blob: e153d6a1554b062fb43a8753254ac21b0146c044 [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SRC_DEVICES_TESTING_FAKE_OBJECT_INCLUDE_LIB_FAKE_OBJECT_OBJECT_H_
#define SRC_DEVICES_TESTING_FAKE_OBJECT_INCLUDE_LIB_FAKE_OBJECT_OBJECT_H_
#include <lib/zx/status.h>
#include <limits.h>
#include <stdio.h>
#include <zircon/compiler.h>
#include <zircon/errors.h>
#include <zircon/types.h>
#include <vector>
#include <fbl/auto_lock.h>
#include <fbl/ref_counted.h>
#include <fbl/ref_ptr.h>
#define FAKE_OBJECT_TRACE 0
#if FAKE_OBJECT_TRACE
#define ftracef(...) \
{ \
printf("fake-object %16.16s: ", __func__); \
printf(__VA_ARGS__); \
}
#else
#define ftracef(...) ;
#endif
namespace fake_object {
enum class HandleType : uint32_t {
BASE, // A non-derived object, used for tests and assertions.
BTI,
MSI,
PMT,
RESOURCE,
CUSTOM, // For local tests that are not providing a fake-* lib
};
class Object : public fbl::RefCounted<Object> {
public:
// For each object-related syscall we stub out a fake-specific version that
// can be implemented within the derived fake objects. syscall symbols defined
// in the fake-object source will route to the fake impl or real impl
// depending on the handle's validity.
virtual zx_status_t get_child(zx_handle_t /* handle */, uint64_t /* koid */,
zx_rights_t /* rights */, zx_handle_t* /* out */) {
return ZX_ERR_NOT_SUPPORTED;
}
virtual zx_status_t get_info(zx_handle_t /* handle */, uint32_t /* topic */, void* /* buffer */,
size_t /* buffer_size */, size_t* /* actual_count */,
size_t* /* aval_count */) {
return ZX_ERR_NOT_SUPPORTED;
}
virtual zx_status_t get_property(zx_handle_t /* handle */, uint32_t /* property */,
void* /* value */, size_t /* value_size */) {
return ZX_ERR_NOT_SUPPORTED;
}
virtual zx_status_t set_profile(zx_handle_t /* handle */, zx_handle_t /* profile */,
uint32_t /* options */) {
return ZX_ERR_NOT_SUPPORTED;
}
virtual zx_status_t set_property(zx_handle_t /* handle */, uint32_t /* property */,
const void* /* value */, size_t /* value_size */) {
return ZX_ERR_NOT_SUPPORTED;
}
virtual zx_status_t signal(zx_handle_t /* handle */, uint32_t /* clear_mask */,
uint32_t /* set_mask */) {
return ZX_ERR_NOT_SUPPORTED;
}
virtual zx_status_t signal_peer(zx_handle_t /* handle */, uint32_t /* clear_mask */,
uint32_t /* set_mask */) {
return ZX_ERR_NOT_SUPPORTED;
}
virtual zx_status_t wait_one(zx_handle_t /* handle */, zx_signals_t /* signals */,
zx_time_t /* deadline */, zx_signals_t* /* observed */) {
return ZX_ERR_NOT_SUPPORTED;
}
// |zx_object_wait_many| is omitted because we would need to define what it means to wait on
// both real objects and fake objects at the same time due to it taking a handle table parameter.
virtual zx_status_t wait_async(zx_handle_t /* handle */, zx_handle_t /* port */,
uint64_t /* key */, zx_signals_t /* signals */,
uint32_t /* options */) {
return ZX_ERR_NOT_SUPPORTED;
}
virtual ~Object() = default;
// For the purposes of tests we only need to ensure the koid is unique to the object.
zx_koid_t get_koid() const { return reinterpret_cast<zx_koid_t>(this); }
virtual HandleType type() const { return HandleType::BASE; }
};
class HandleTable {
public:
HandleTable() = default;
~HandleTable() = default;
HandleTable(const HandleTable&) = delete;
HandleTable& operator=(const HandleTable&) = delete;
HandleTable(HandleTable&&) = delete;
HandleTable& operator=(HandleTable&&) = delete;
// A valid fake handle does not have the reserved bits of a real handle set, and
// has a non-zero value after shifting. This ensures that they will not overlap
// with real handles, and that ZX_HANDLE_INVALID is not a valid fake handle.
static bool IsValidFakeHandle(zx_handle_t handle) {
return ((handle & ZX_HANDLE_FIXED_BITS_MASK) == 0) && (handle & ~ZX_HANDLE_FIXED_BITS_MASK);
}
__EXPORT
zx::status<fbl::RefPtr<Object>> Get(zx_handle_t handle) __TA_EXCLUDES(lock_) {
fbl::AutoLock guard(&lock_);
return GetLocked(handle);
}
zx::status<> Remove(zx_handle_t handle) __TA_EXCLUDES(lock_);
zx::status<zx_handle_t> Add(fbl::RefPtr<Object> obj) __TA_EXCLUDES(lock_);
void Clear() __TA_EXCLUDES(lock_);
// Walks the handle table and calls |cb| on each handle that matches the
// provided |type|. Stops walking the table when |cb| returns false.
//
// |cb| must NOT attempt to acquire the lock, so this method is not suitable
// for internal methods.
template <typename ObjectCallback>
void ForEach(HandleType type, const ObjectCallback cb) __TA_EXCLUDES(lock_) {
fbl::AutoLock lock(&lock_);
for (const auto& obj : handles_) {
if (obj && obj->type() == type) {
if (!std::forward<const ObjectCallback>(cb)(obj.get())) {
break;
}
}
}
}
void Dump() __TA_EXCLUDES(lock_);
// We use the overall size of the vector to calculate new indices
// so to determine the occupied size we have to verify each element.
size_t size() __TA_EXCLUDES(lock_) {
fbl::AutoLock lock(&lock_);
return size_locked();
}
private:
__EXPORT
zx::status<fbl::RefPtr<Object>> GetLocked(zx_handle_t handle) __TA_REQUIRES(lock_);
size_t size_locked() __TA_REQUIRES(lock_) {
size_t s = 0;
for (auto& h : handles_) {
if (h) {
s++;
}
}
return s;
}
// The first |ZX_HANDLE_FIXED_BITS_MASK| bits of real handle values are
// required to be |1|. This requirement is defined both in the public handle
// documentation and in ProcessDispatcher's implementation.
static constexpr size_t FakeHandleShiftBitsCount() {
size_t bits = 0;
uint32_t val = ZX_HANDLE_FIXED_BITS_MASK;
while (val) {
val >>= 1;
bits++;
}
return bits;
}
// Fake handle values start at (1 << 2) which maps to 0 in the handle table.
static zx::status<size_t> HandleToIndex(zx_handle_t handle) {
if (!IsValidFakeHandle(handle)) {
return zx::error(ZX_ERR_BAD_HANDLE);
}
return zx::ok((handle >> FakeHandleShiftBitsCount()) - 1);
}
static zx_handle_t IndexToHandle(size_t idx) {
return static_cast<zx_handle_t>((idx + 1) << FakeHandleShiftBitsCount());
}
fbl::Mutex lock_;
std::vector<fbl::RefPtr<Object>> handles_ __TA_GUARDED(lock_);
};
HandleTable& FakeHandleTable();
// Creates a base object for testing handle methods.
zx::status<zx_handle_t> fake_object_create();
zx::status<zx_koid_t> fake_object_get_koid(zx_handle_t);
void* FindRealSyscall(const char* name);
} // namespace fake_object
#define REAL_SYSCALL(name) \
([]() { \
static const auto real_syscall = \
reinterpret_cast<decltype(name)*>(fake_object::FindRealSyscall("_" #name)); \
return real_syscall; \
}())
#endif // SRC_DEVICES_TESTING_FAKE_OBJECT_INCLUDE_LIB_FAKE_OBJECT_OBJECT_H_