Add initial pass of syscall API kobjects

This commit is contained in:
Justin C. Miller
2019-06-30 19:36:46 -07:00
parent b3f88bbe02
commit 19cd01ef8d
12 changed files with 379 additions and 2 deletions

View File

@@ -34,6 +34,8 @@ modules:
- src/kernel/main.cpp
- src/kernel/memory_bootstrap.cpp
- src/kernel/msr.cpp
- src/kernel/objects/handle.cpp
- src/kernel/objects/kobject.cpp
- src/kernel/page_manager.cpp
- src/kernel/pci.cpp
- src/kernel/process.cpp

16
src/include/j6/errors.h Normal file
View File

@@ -0,0 +1,16 @@
#pragma once
/// \file errors.h
/// Collection of constants for the j6_status_t type
#define j6_status_error 0x8000000000000000
#define j6_err(x) ((x) | j6_status_error)
#define j6_is_err(x) (((x) & j6_status_error) == j6_status_error)
#define j6_status_ok 0x0000
#define j6_status_destroyed 0x1001
#define j6_err_nyi j6_err(0x0001)
#define j6_err_unexpected j6_err(0x0002)
#define j6_err_invalid_arg j6_err(0x0003)

18
src/include/j6/signals.h Normal file
View File

@@ -0,0 +1,18 @@
#pragma once
/// \file signals.h
/// Collection of constants for the j6_signal_t type
// Signals 0-7 are common to all types
#define j6_signal_no_handles (1 << 0)
// Signals 8-15 are user-defined signals
#define j6_signal_user0 (1 << 8)
#define j6_signal_user1 (1 << 9)
#define j6_signal_user2 (1 << 10)
#define j6_signal_user3 (1 << 11)
#define j6_signal_user4 (1 << 12)
#define j6_signal_user5 (1 << 13)
#define j6_signal_user6 (1 << 14)
#define j6_signal_user7 (1 << 15)
// All other signals are type-specific

20
src/include/j6/types.h Normal file
View File

@@ -0,0 +1,20 @@
#pragma once
/// \file types.h
/// Basic kernel types exposed to userspace
#include <stdint.h>
/// All interactable kernel objects have a uniqe kernel object id
typedef uint64_t j6_koid_t;
/// Syscalls return status as this type
typedef uint32_t j6_status_t;
/// Handles are references and capabilities to other objects
typedef uint32_t j6_handle_t;
/// Some objects have signals, which are a bitmap of 64 possible signals
typedef uint64_t j6_signal_t;
/// The rights of a handle/capability are a bitmap of 64 possible rights
typedef uint64_t j6_rights_t;

View File

@@ -8,3 +8,4 @@ LOG(task, info);
LOG(boot, debug);
LOG(syscall,debug);
LOG(vmem, debug);
LOG(objs, debug);

View File

@@ -1,6 +1,8 @@
#include <stddef.h>
#include <stdint.h>
#include "j6/signals.h"
#include "initrd/initrd.h"
#include "kutil/assert.h"
#include "kutil/heap_allocator.h"
@@ -16,6 +18,8 @@
#include "kernel_args.h"
#include "kernel_memory.h"
#include "log.h"
#include "objects/event.h"
#include "objects/handle.h"
#include "page_manager.h"
#include "scheduler.h"
#include "serial.h"
@@ -30,6 +34,26 @@ extern void __kernel_assert(const char *, unsigned, const char *);
extern kutil::heap_allocator g_kernel_heap;
class test_observer :
public kobject::observer
{
public:
test_observer(const char *name) : m_name(name) {}
virtual bool on_signals_changed(
kobject *obj,
j6_signal_t s,
j6_signal_t ds,
j6_status_t result)
{
log::info(logs::objs, " %s: Signals %016lx changed, object %p, result %016lx",
m_name, ds, obj, result);
return false;
}
const char *m_name;
};
void
init_console()
{
@@ -143,5 +167,20 @@ kernel_main(kernel_args *header)
sched->load_process(f.name(), f.data(), f.size());
}
log::info(logs::objs, "Testing object system:");
test_observer obs1("event");
test_observer obs2("no handles");
{
event e;
e.register_signal_observer(&obs1, j6_signal_user0);
e.register_signal_observer(&obs2, j6_signal_no_handles);
e.assert_signal(j6_signal_user0);
handle h(1, 0, &e);
}
sched->start();
}

View File

@@ -0,0 +1,15 @@
#pragma once
/// \file event.h
/// Definition of event kobject types
#include "objects/kobject.h"
class event :
public kobject
{
public:
static constexpr type type_id = type::event;
event() :
kobject(type_id) {}
};

View File

@@ -0,0 +1,26 @@
#include "objects/handle.h"
handle::handle(handle&& other) :
m_owner(other.m_owner),
m_object(other.m_object),
m_rights(other.m_rights)
{
other.m_owner = 0;
other.m_object = nullptr;
other.m_rights = 0;
}
handle::handle(j6_koid_t owner, j6_rights_t rights, kobject *obj) :
m_owner(owner),
m_object(obj),
m_rights(rights)
{
if (m_object)
m_object->handle_retain();
}
handle::~handle()
{
if (m_object)
m_object->handle_release();
}

View File

@@ -0,0 +1,30 @@
#pragma once
/// \file handle.h
/// Defines types for user handles to kernel objects
#include "j6/types.h"
#include "objects/kobject.h"
class handle
{
public:
/// Move constructor. Takes ownership of the object from other.
handle(handle&& other);
/// Constructor.
/// \arg owner koid of the process that has this handle
/// \arg rights access rights this handle has over the object
/// \arg obj the object held
handle(j6_koid_t owner, j6_rights_t rights, kobject *obj);
~handle();
handle() = delete;
handle(const handle &other) = delete;
handle & operator=(const handle& other) = delete;
private:
j6_koid_t m_owner;
kobject *m_object;
j6_rights_t m_rights;
};

View File

@@ -0,0 +1,98 @@
#include "j6/errors.h"
#include "j6/signals.h"
#include "j6/types.h"
#include "kutil/heap_allocator.h"
#include "objects/kobject.h"
extern kutil::heap_allocator g_kernel_heap;
// TODO: per-cpu this?
static j6_koid_t next_koid;
kobject::kobject(type t, j6_signal_t signals) :
m_koid(koid_generate(t)),
m_signals(signals),
m_observers(g_kernel_heap),
m_handle_count(0)
{}
kobject::~kobject()
{
notify_signal_observers(0, j6_status_destroyed);
}
j6_koid_t
kobject::koid_generate(type t)
{
return (static_cast<uint64_t>(t) << 48) | next_koid++;
}
kobject::type
kobject::koid_type(j6_koid_t koid)
{
return static_cast<type>((koid >> 48) & 0xffffull);
}
void
kobject::assert_signal(j6_signal_t s)
{
m_signals |= s;
notify_signal_observers(s);
}
void
kobject::deassert_signal(j6_signal_t s)
{
m_signals &= ~s;
notify_signal_observers(s);
}
void
kobject::register_signal_observer(observer *object, j6_signal_t s)
{
m_observers.emplace(object, s);
}
void
kobject::deregister_signal_observer(observer *object)
{
for (size_t i = 0; i < m_observers.count(); ++i) {
auto &reg = m_observers[i];
if (reg.object != object) continue;
reg = m_observers[m_observers.count() - 1];
m_observers.remove();
break;
}
}
void
kobject::notify_signal_observers(j6_signal_t mask, j6_status_t result)
{
for (auto &reg : m_observers) {
if (mask == 0 || (reg.signals & mask) != 0) {
if (!reg.object->on_signals_changed(this, m_signals, mask, result))
reg.object = nullptr;
}
}
// Compact the observer list
long last = m_observers.count() - 1;
while (m_observers[last].object == nullptr && last >= 0) last--;
for (long i = 0; i < long(m_observers.count()) && i < last; ++i) {
auto &reg = m_observers[i];
if (reg.object != nullptr) continue;
reg = m_observers[last--];
while (m_observers[last].object == nullptr && last >= i) last--;
}
m_observers.set_size(last + 1);
}
void
kobject::on_no_handles()
{
assert_signal(j6_signal_no_handles);
}

View File

@@ -0,0 +1,110 @@
#pragma once
/// \file kobject.h
/// Definition of base type for user-interactable kernel objects
#include "j6/errors.h"
#include "j6/types.h"
#include "kutil/vector.h"
/// Base type for all user-interactable kernel objects
class kobject
{
public:
/// Types of kernel objects.
enum class type : uint16_t
{
none,
event,
eventpair,
vms,
vmo,
job,
process,
thread,
};
kobject(type t, j6_signal_t signals = 0ull);
virtual ~kobject();
/// Generate a new koid for a given type
/// \arg t The object type
/// \returns A new unique koid
static j6_koid_t koid_generate(type t);
/// Get the kobject type from a given koid
/// \arg koid An existing koid
/// \returns The object type for the koid
static type koid_type(j6_koid_t koid);
/// Set the given signals active on this object
/// \arg s The set of signals to assert
void assert_signal(j6_signal_t s);
/// Clear the given signals on this object
/// \arg s The set of signals to deassert
void deassert_signal(j6_signal_t s);
class observer
{
public:
/// Callback for when signals change.
/// \arg obj The object triggering the callback
/// \arg s The current state of obj's signals
/// \arg ds Which signals caused the callback
/// \arg result Status code for this notification
/// \returns True if this object wants to keep watching signals
virtual bool on_signals_changed(
kobject *obj,
j6_signal_t s,
j6_signal_t ds,
j6_status_t result) = 0;
};
/// Register a signal observer
/// \arg object The observer
/// \arg s The signals the object wants notifiations for
void register_signal_observer(observer *object, j6_signal_t s);
/// Deegister a signal observer
/// \arg object The observer
void deregister_signal_observer(observer *object);
/// Increment the handle refcount
inline void handle_retain() { ++m_handle_count; }
/// Decrement the handle refcount
inline void handle_release() {
if (--m_handle_count == 0) on_no_handles();
}
protected:
kobject() = delete;
kobject(const kobject &other) = delete;
kobject(const kobject &&other) = delete;
/// Notifiy observers of this object
/// \arg mask The signals that triggered this call. If 0, notify all observers.
/// \arg result The result to pass to the observers
void notify_signal_observers(j6_signal_t mask, j6_status_t result = j6_status_ok);
/// Interface for subclasses to handle when all handles are closed. Subclasses
/// should either call the base version, or assert j6_signal_no_handles.
virtual void on_no_handles();
j6_koid_t m_koid;
j6_signal_t m_signals;
uint16_t m_handle_count;
struct observer_registration
{
observer_registration(observer* o = nullptr, j6_signal_t s = 0) :
object(o), signals(s) {}
observer *object;
j6_signal_t signals;
};
kutil::vector<observer_registration> m_observers;
};

View File

@@ -117,12 +117,14 @@ public:
m_elements[m_size].~T();
}
/// Set the size of the array. Any new items are default
/// constructed. The array is realloced if needed.
/// Set the size of the array. Any new items are default constructed.
/// Any items past the end are deleted. The array is realloced if needed.
/// \arg size The new size
void set_size(size_t size)
{
ensure_capacity(size);
for (size_t i = size; i < m_size; ++i)
m_elements[i].~T();
for (size_t i = m_size; i < size; ++i)
new (&m_elements[i]) T;
m_size = size;