diff --git a/modules.yaml b/modules.yaml index 607a824..f707d2a 100644 --- a/modules.yaml +++ b/modules.yaml @@ -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 diff --git a/src/include/j6/errors.h b/src/include/j6/errors.h new file mode 100644 index 0000000..1e1b012 --- /dev/null +++ b/src/include/j6/errors.h @@ -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) + diff --git a/src/include/j6/signals.h b/src/include/j6/signals.h new file mode 100644 index 0000000..0f27c2f --- /dev/null +++ b/src/include/j6/signals.h @@ -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 diff --git a/src/include/j6/types.h b/src/include/j6/types.h new file mode 100644 index 0000000..ccff446 --- /dev/null +++ b/src/include/j6/types.h @@ -0,0 +1,20 @@ +#pragma once +/// \file types.h +/// Basic kernel types exposed to userspace + +#include + +/// 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; diff --git a/src/include/log_areas.inc b/src/include/log_areas.inc index c63ce77..15f8787 100644 --- a/src/include/log_areas.inc +++ b/src/include/log_areas.inc @@ -8,3 +8,4 @@ LOG(task, info); LOG(boot, debug); LOG(syscall,debug); LOG(vmem, debug); +LOG(objs, debug); diff --git a/src/kernel/main.cpp b/src/kernel/main.cpp index a2a1516..574a41c 100644 --- a/src/kernel/main.cpp +++ b/src/kernel/main.cpp @@ -1,6 +1,8 @@ #include #include +#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(); } diff --git a/src/kernel/objects/event.h b/src/kernel/objects/event.h new file mode 100644 index 0000000..fe5ff64 --- /dev/null +++ b/src/kernel/objects/event.h @@ -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) {} +}; diff --git a/src/kernel/objects/handle.cpp b/src/kernel/objects/handle.cpp new file mode 100644 index 0000000..37991a7 --- /dev/null +++ b/src/kernel/objects/handle.cpp @@ -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(); +} diff --git a/src/kernel/objects/handle.h b/src/kernel/objects/handle.h new file mode 100644 index 0000000..c8a77a7 --- /dev/null +++ b/src/kernel/objects/handle.h @@ -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; +}; diff --git a/src/kernel/objects/kobject.cpp b/src/kernel/objects/kobject.cpp new file mode 100644 index 0000000..64bc8af --- /dev/null +++ b/src/kernel/objects/kobject.cpp @@ -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(t) << 48) | next_koid++; +} + +kobject::type +kobject::koid_type(j6_koid_t koid) +{ + return static_cast((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 ® = 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 ® : 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 ® = 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); +} diff --git a/src/kernel/objects/kobject.h b/src/kernel/objects/kobject.h new file mode 100644 index 0000000..d8a8b39 --- /dev/null +++ b/src/kernel/objects/kobject.h @@ -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 m_observers; +}; diff --git a/src/libraries/kutil/include/kutil/vector.h b/src/libraries/kutil/include/kutil/vector.h index e963afc..8522cd8 100644 --- a/src/libraries/kutil/include/kutil/vector.h +++ b/src/libraries/kutil/include/kutil/vector.h @@ -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;