[kernel] Pass objects not handles to syscall impls

This commit contains a couple large, interdependent changes:

- In preparation for capability checking, the _syscall_verify_*
  functions now load most handles passed in, and verify that they exist
  and are of the correct type. Lists and out-handles are not converted
  to objects.
- Also in preparation for capability checking, the internal
  representation of handles has changed. j6_handle_t is now 32 bits, and
  a new j6_cap_t (also 32 bits) is added. Handles of a process are now a
  util::map<j6_handle_t, handle> where handle is a new struct containing
  the id, capabilities, and object pointer.
- The kernel object definition DSL gained a few changes to support auto
  generating the handle -> object conversion in the _syscall_verify_*
  functions, mostly knowing the object type, and an optional "cname"
  attribute on objects where their names differ from C++ code.
  (Specifically vma/vm_area)
- Kernel object code and other code under kernel/objects is now in a new
  obj:: namespace, because fuck you <cstdlib> for putting "system" in
  the global namespace. Why even have that header then?
- Kernel object types constructed with the construct_handle helper now
  have a creation_caps static member to declare what capabilities a
  newly created object's handle should have.
This commit is contained in:
Justin C. Miller
2022-01-17 23:23:04 -08:00
parent e0246df26b
commit 1d30322820
50 changed files with 492 additions and 300 deletions

View File

@@ -4,29 +4,27 @@
#include "objects/channel.h"
#include "syscalls/helpers.h"
using namespace obj;
namespace syscalls {
j6_status_t
channel_create(j6_handle_t *handle)
channel_create(j6_handle_t *self)
{
construct_handle<channel>(handle);
construct_handle<channel>(self);
return j6_status_ok;
}
j6_status_t
channel_send(j6_handle_t handle, size_t *len, void *data)
channel_send(channel *self, size_t *len, void *data)
{
channel *c = get_handle<channel>(handle);
if (!c) return j6_err_invalid_arg;
return c->enqueue(len, data);
return self->enqueue(len, data);
}
j6_status_t
channel_receive(j6_handle_t handle, size_t *len, void *data)
channel_receive(channel *self, size_t *len, void *data)
{
channel *c = get_handle<channel>(handle);
if (!c) return j6_err_invalid_arg;
return c->dequeue(len, data);
return self->dequeue(len, data);
}
} // namespace syscalls

View File

@@ -5,62 +5,61 @@
#include "objects/endpoint.h"
#include "syscalls/helpers.h"
using namespace obj;
namespace syscalls {
j6_status_t
endpoint_create(j6_handle_t *handle)
endpoint_create(j6_handle_t *self)
{
construct_handle<endpoint>(handle);
construct_handle<endpoint>(self);
return j6_status_ok;
}
j6_status_t
endpoint_send(j6_handle_t handle, uint64_t tag, const void * data, size_t data_len)
endpoint_send(endpoint *self, uint64_t tag, const void * data, size_t data_len)
{
if (tag & j6_tag_system_flag)
return j6_err_invalid_arg;
endpoint *e = get_handle<endpoint>(handle);
if (!e) return j6_err_invalid_arg;
return e->send(tag, data, data_len);
return self->send(tag, data, data_len);
}
j6_status_t
endpoint_receive(j6_handle_t handle, uint64_t * tag, void * data, size_t * data_len, uint64_t timeout)
endpoint_receive(endpoint *self, uint64_t * tag, void * data, size_t * data_len, uint64_t timeout)
{
// Data is marked optional, but we need the length, and if length > 0,
// data is not optional.
if (!data_len || (*data_len && !data))
return j6_err_invalid_arg;
endpoint *e = get_handle<endpoint>(handle);
if (!e) return j6_err_invalid_arg;
// Use local variables instead of the passed-in pointers, since
// they may get filled in when the sender is running, which means
// a different user VM space would be active.
j6_tag_t out_tag = j6_tag_invalid;
size_t out_len = *data_len;
j6_status_t s = e->receive(&out_tag, data, &out_len, timeout);
j6_status_t s = self->receive(&out_tag, data, &out_len, timeout);
*tag = out_tag;
*data_len = out_len;
return s;
}
j6_status_t
endpoint_sendrecv(j6_handle_t handle, uint64_t * tag, void * data, size_t * data_len, uint64_t timeout)
endpoint_sendrecv(endpoint *self, uint64_t * tag, void * data, size_t * data_len, uint64_t timeout)
{
if (*tag & j6_tag_system_flag)
return j6_err_invalid_arg;
endpoint *e = get_handle<endpoint>(handle);
if (!e) return j6_err_invalid_arg;
j6_status_t status = e->send(*tag, data, *data_len);
j6_status_t status = self->send(*tag, data, *data_len);
if (status != j6_status_ok)
return status;
// Use local variables instead of the passed-in pointers, since
// they may get filled in when the sender is running, which means
// a different user VM space would be active.
j6_tag_t out_tag = j6_tag_invalid;
size_t out_len = *data_len;
j6_status_t s = e->receive(&out_tag, data, &out_len, timeout);
j6_status_t s = self->receive(&out_tag, data, &out_len, timeout);
*tag = out_tag;
*data_len = out_len;
return s;

View File

@@ -10,38 +10,41 @@
namespace syscalls {
template <typename T, typename... Args>
T * construct_handle(j6_handle_t *handle, Args... args)
T * construct_handle(j6_handle_t *id, Args... args)
{
process &p = process::current();
obj::process &p = obj::process::current();
T *o = new T {args...};
*handle = p.add_handle(o);
*id = p.add_handle(o, T::creation_caps);
return o;
}
template <typename T>
T * get_handle(j6_handle_t handle)
obj::handle * get_handle(j6_handle_t id)
{
process &p = process::current();
kobject *o = p.lookup_handle(handle);
if (!o || o->get_type() != T::type)
obj::process &p = obj::process::current();
obj::handle *h = p.lookup_handle(id);
if (!h || h->type() != T::type)
return nullptr;
return static_cast<T*>(o);
return h;
}
template <>
inline kobject * get_handle<kobject>(j6_handle_t handle)
inline obj::handle * get_handle<obj::kobject>(j6_handle_t id)
{
process &p = process::current();
return p.lookup_handle(handle);
obj::process &p = obj::process::current();
return p.lookup_handle(id);
}
template <typename T>
T * remove_handle(j6_handle_t handle)
T * remove_handle(j6_handle_t id)
{
T *o = get_handle<T>(handle);
if (o) {
process &p = process::current();
p.remove_handle(handle);
obj::handle *h = get_handle<T>(id);
T *o = nullptr;
if (h) {
o = h->object;
obj::process &p = obj::process::current();
p.remove_handle(id);
}
return o;
}

View File

@@ -8,34 +8,28 @@
#include "objects/thread.h"
#include "syscalls/helpers.h"
using namespace obj;
namespace syscalls {
j6_status_t
kobject_koid(j6_handle_t handle, j6_koid_t *koid)
kobject_koid(kobject *self, j6_koid_t *koid)
{
kobject *obj = get_handle<kobject>(handle);
if (!obj)
return j6_err_invalid_arg;
*koid = obj->koid();
*koid = self->koid();
return j6_status_ok;
}
j6_status_t
kobject_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs)
kobject_wait(kobject *self, j6_signal_t mask, j6_signal_t *sigs)
{
kobject *obj = get_handle<kobject>(handle);
if (!obj)
return j6_err_invalid_arg;
j6_signal_t current = obj->signals();
j6_signal_t current = self->signals();
if ((current & mask) != 0) {
*sigs = current;
return j6_status_ok;
}
thread &th = thread::current();
obj->add_blocked_thread(&th);
self->add_blocked_thread(&th);
th.wait_on_signals(mask);
j6_status_t result = th.get_wait_result();
@@ -46,23 +40,24 @@ kobject_wait(j6_handle_t handle, j6_signal_t mask, j6_signal_t *sigs)
}
j6_status_t
kobject_wait_many(j6_handle_t * handles, size_t handles_count, uint64_t mask, j6_handle_t * handle, uint64_t * signals)
kobject_wait_many(j6_handle_t * handles, size_t handles_count, uint64_t mask, j6_handle_t * woken, uint64_t * signals)
{
util::vector<kobject*> objects {uint32_t(handles_count)};
for (unsigned i = 0; i < handles_count; ++i) {
j6_handle_t h = handles[i];
if (h == j6_handle_invalid)
j6_handle_t hid = handles[i];
if (hid == j6_handle_invalid)
continue;
kobject *obj = get_handle<kobject>(h);
if (!obj)
handle *h = get_handle<kobject>(hid);
if (!h || !h->object)
return j6_err_invalid_arg;
kobject *obj = h->object;
j6_signal_t current = obj->signals();
if ((current & mask) != 0) {
*signals = current;
*handle = h;
*woken = hid;
return j6_status_ok;
}
@@ -79,43 +74,35 @@ kobject_wait_many(j6_handle_t * handles, size_t handles_count, uint64_t mask, j6
if (result != j6_status_ok)
return result;
*handle = j6_handle_invalid;
*woken = j6_handle_invalid;
*signals = th.get_wait_data();
j6_koid_t koid = th.get_wait_object();
for (unsigned i = 0; i < handles_count; ++i) {
if (koid == objects[i]->koid())
*handle = handles[i];
*woken = handles[i];
else
objects[i]->remove_blocked_thread(&th);
}
kassert(*handle != j6_handle_invalid,
kassert(*woken != j6_handle_invalid,
"Somehow woke on a handle that was not waited on");
return j6_status_ok;
}
j6_status_t
kobject_signal(j6_handle_t handle, j6_signal_t signals)
kobject_signal(kobject *self, j6_signal_t signals)
{
if ((signals & j6_signal_user_mask) != signals)
return j6_err_invalid_arg;
kobject *obj = get_handle<kobject>(handle);
if (!obj)
return j6_err_invalid_arg;
obj->assert_signal(signals);
self->assert_signal(signals);
return j6_status_ok;
}
j6_status_t
kobject_close(j6_handle_t handle)
kobject_close(kobject *self)
{
kobject *obj = get_handle<kobject>(handle);
if (!obj)
return j6_err_invalid_arg;
obj->close();
self->close();
return j6_status_ok;
}

View File

@@ -5,41 +5,25 @@
#include "objects/process.h"
#include "syscalls/helpers.h"
using namespace obj;
namespace syscalls {
j6_status_t
process_create(j6_handle_t *handle)
process_create(j6_handle_t *self)
{
process *child = construct_handle<process>(handle);
log::debug(logs::task, "Process %llx created", child->koid());
process *p = construct_handle<process>(self);
log::debug(logs::task, "Process %llx created", p->koid());
return j6_status_ok;
}
j6_status_t
process_start(j6_handle_t handle, uintptr_t entrypoint, j6_handle_t * handles, size_t handles_count)
process_kill(process *self)
{
process &p = process::current();
process *c = get_handle<process>(handle);
if (handles_count && !handles)
return j6_err_invalid_arg;
for (size_t i = 0; i < handles_count; ++i) {
kobject *o = p.lookup_handle(handles[i]);
if (o) c->add_handle(o);
}
return j6_err_nyi;
}
j6_status_t
process_kill(j6_handle_t handle)
{
process &p = process::current();
process *c = get_handle<process>(handle);
if (!c) return j6_err_invalid_arg;
log::debug(logs::task, "Process %llx killed by process %llx", c->koid(), p.koid());
c->exit(-1u);
log::debug(logs::task, "Process %llx killed by process %llx", self->koid(), p.koid());
self->exit(-1u);
return j6_status_ok;
}
@@ -57,16 +41,13 @@ process_exit(int32_t status)
}
j6_status_t
process_give_handle(j6_handle_t handle, j6_handle_t sender, j6_handle_t *receiver)
process_give_handle(process *self, j6_handle_t target, j6_handle_t *received)
{
process *dest = get_handle<process>(handle);
if (!dest) return j6_err_invalid_arg;
handle *target_handle = get_handle<kobject>(target);
j6_handle_t out = self->add_handle(target_handle->object, target_handle->caps);
kobject *o = get_handle<kobject>(sender);
j6_handle_t out = dest->add_handle(o);
if (receiver)
*receiver = out;
if (received)
*received = out;
return j6_status_ok;
}

View File

@@ -14,8 +14,12 @@
extern log::logger &g_logger;
using namespace obj;
namespace syscalls {
using system = class ::system;
j6_status_t
log(const char *message)
{
@@ -33,7 +37,7 @@ noop()
}
j6_status_t
system_get_log(j6_handle_t sys, void *buffer, size_t *buffer_len)
system_get_log(system *self, void *buffer, size_t *buffer_len)
{
// Buffer is marked optional, but we need the length, and if length > 0,
// buffer is not optional.
@@ -43,25 +47,22 @@ system_get_log(j6_handle_t sys, void *buffer, size_t *buffer_len)
size_t orig_size = *buffer_len;
*buffer_len = g_logger.get_entry(buffer, *buffer_len);
if (!g_logger.has_log())
system::get().deassert_signal(j6_signal_system_has_log);
self->deassert_signal(j6_signal_system_has_log);
return (*buffer_len > orig_size) ? j6_err_insufficient : j6_status_ok;
}
j6_status_t
system_bind_irq(j6_handle_t sys, j6_handle_t endp, unsigned irq)
system_bind_irq(system *self, endpoint *endp, unsigned irq)
{
endpoint *e = get_handle<endpoint>(endp);
if (!e) return j6_err_invalid_arg;
if (device_manager::get().bind_irq(irq, e))
if (device_manager::get().bind_irq(irq, endp))
return j6_status_ok;
return j6_err_invalid_arg;
}
j6_status_t
system_map_phys(j6_handle_t handle, j6_handle_t * area, uintptr_t phys, size_t size, uint32_t flags)
system_map_phys(system *self, j6_handle_t * area, uintptr_t phys, size_t size, uint32_t flags)
{
// TODO: check to see if frames are already used? How would that collide with
// the bootloader's allocated pages already being marked used?
@@ -75,7 +76,7 @@ system_map_phys(j6_handle_t handle, j6_handle_t * area, uintptr_t phys, size_t s
}
j6_status_t
system_request_iopl(j6_handle_t handle, unsigned iopl)
system_request_iopl(system *self, unsigned iopl)
{
if (iopl != 0 && iopl != 3)
return j6_err_invalid_arg;

View File

@@ -6,25 +6,24 @@
#include "objects/thread.h"
#include "syscalls/helpers.h"
using namespace obj;
namespace syscalls {
j6_status_t
thread_create(j6_handle_t *handle, j6_handle_t proc, uintptr_t stack_top, uintptr_t entrypoint)
thread_create(j6_handle_t *self, process *proc, uintptr_t stack_top, uintptr_t entrypoint)
{
thread &parent_th = thread::current();
process &parent_pr = parent_th.parent();
process *owner = get_handle<process>(proc);
if (!owner) return j6_err_invalid_arg;
thread *child = owner->create_thread(stack_top);
thread *child = proc->create_thread(stack_top);
child->add_thunk_user(entrypoint);
*handle = child->self_handle();
*self = child->self_handle();
child->clear_state(thread::state::loading);
child->set_state(thread::state::ready);
log::debug(logs::task, "Thread %llx:%llx spawned new thread %llx:%llx",
parent_pr.koid(), parent_th.koid(), owner->koid(), child->koid());
parent_pr.koid(), parent_th.koid(), proc->koid(), child->koid());
return j6_status_ok;
}
@@ -41,14 +40,10 @@ thread_exit(int32_t status)
}
j6_status_t
thread_kill(j6_handle_t handle)
thread_kill(thread *self)
{
thread *th = get_handle<thread>(handle);
if (!th)
return j6_err_invalid_arg;
log::debug(logs::task, "Killing thread %llx", th->koid());
th->exit(-1);
log::debug(logs::task, "Killing thread %llx", self->koid());
self->exit(-1);
return j6_status_ok;
}

View File

@@ -8,58 +8,45 @@
#include "syscalls/helpers.h"
#include "vm_space.h"
using namespace obj;
namespace syscalls {
j6_status_t
vma_create(j6_handle_t *handle, size_t size, uint32_t flags)
vma_create(j6_handle_t *self, size_t size, uint32_t flags)
{
vm_flags f = vm_flags::user_mask & flags;
construct_handle<vm_area_open>(handle, size, f);
construct_handle<vm_area_open>(self, size, f);
return j6_status_ok;
}
j6_status_t
vma_create_map(j6_handle_t *handle, size_t size, uintptr_t base, uint32_t flags)
vma_create_map(j6_handle_t *self, size_t size, uintptr_t base, uint32_t flags)
{
vm_flags f = vm_flags::user_mask & flags;
vm_area *a = construct_handle<vm_area_open>(handle, size, f);
vm_area *a = construct_handle<vm_area_open>(self, size, f);
process::current().space().add(base, a);
return j6_status_ok;
}
j6_status_t
vma_map(j6_handle_t handle, j6_handle_t proc, uintptr_t base)
vma_map(vm_area *self, process *proc, uintptr_t base)
{
vm_area *a = get_handle<vm_area>(handle);
if (!a) return j6_err_invalid_arg;
process *p = get_handle<process>(proc);
if (!p) return j6_err_invalid_arg;
p->space().add(base, a);
proc->space().add(base, self);
return j6_status_ok;
}
j6_status_t
vma_unmap(j6_handle_t handle, j6_handle_t proc)
vma_unmap(vm_area *self, process *proc)
{
vm_area *a = get_handle<vm_area>(handle);
if (!a) return j6_err_invalid_arg;
process *p = get_handle<process>(proc);
if (!p) return j6_err_invalid_arg;
p->space().remove(a);
proc->space().remove(self);
return j6_status_ok;
}
j6_status_t
vma_resize(j6_handle_t handle, size_t *size)
vma_resize(vm_area *self, size_t *size)
{
vm_area *a = get_handle<vm_area>(handle);
if (!a) return j6_err_invalid_arg;
*size = a->resize(*size);
*size = self->resize(*size);
return j6_status_ok;
}