kxld: implement internal kext dependency resolution and initialisation

This commit is contained in:
2023-04-09 16:43:03 +01:00
parent 076a15e47b
commit eb998860a5
4 changed files with 358 additions and 30 deletions

View File

@@ -3,9 +3,11 @@
#include <socks/status.h>
#include <socks/compiler.h>
#include <socks/btree.h>
#define KERNEL_KEXT_ID "net.doorstuck.socks-kernel"
#define KEXT_IDENT_MAX 80
#define KEXT_NO_DEPENDENCIES NULL
#define __KEXT_INFO_VARNAME_2(a, b) a ## b
#define __KEXT_INFO_VARNAME_1(a, b) __KEXT_INFO_VARNAME_2(a, b)
@@ -15,19 +17,21 @@
#define __KEXT_INFO_VARNAME() __KEXT_INFO_VARNAME_1(__kext_info, __LINE__)
#define __KEXT_INFO_DEPNAME() __KEXT_INFO_VARNAME_1(__kext_deps, __LINE__)
#define __KEXT_INFO_FLAGS KEXT_INTERNAL
#define __KEXT_INFO_ALIGNMENT 0x80
#else
#define __KEXT_INFO_LINKAGE
#define __KEXT_INFO_VARNAME() __kext_info
#define __KEXT_INFO_DEPNAME() __kext_deps
#define __KEXT_INFO_FLAGS KEXT_NONE
#define __KEXT_INFO_ALIGNMENT 0x80
#endif
#ifdef __cplusplus
#define DEFINE_KEXT(ident, online, offline, ...) \
static const char *__KEXT_INFO_DEPNAME()[] = { \
__VA_ARGS__ \
__VA_ARGS__, NULL \
}; \
static struct kext_info __section(".kextinfo") __used __KEXT_INFO_VARNAME() = { \
static struct kext_info __section(".kextinfo") __aligned(__KEXT_INFO_ALIGNMENT) __used __KEXT_INFO_VARNAME() = { \
__KEXT_INFO_FLAGS, \
ident, \
online, \
@@ -36,15 +40,15 @@
}
#else
#define DEFINE_KEXT(ident, online, offline, ...) \
static const char *__kext_deps[] = { \
__VA_ARGS__ \
static const char *__KEXT_INFO_DEPNAME()[] = { \
__VA_ARGS__, NULL \
}; \
static struct kext_info __section(".kextinfo") __used __kext_info = { \
static struct kext_info __section(".kextinfo") __aligned(__KEXT_INFO_ALIGNMENT) __used __KEXT_INFO_VARNAME() = { \
.k_flags = __KEXT_INFO_FLAGS, \
.k_ident = ident, \
.k_online = online, \
.k_offline = offline, \
.k_dependencies = __kext_deps, \
.k_dependencies = __KEXT_INFO_DEPNAME(), \
}
#endif
@@ -67,6 +71,8 @@ struct kext_info {
struct kext {
enum kext_flags k_flags;
char k_ident[KEXT_IDENT_MAX];
uint64_t k_ident_hash;
btree_node_t k_node;
kern_status_t(*k_online)(struct kext *);
kern_status_t(*k_offline)(struct kext *);
@@ -78,4 +84,11 @@ struct kext {
extern kern_status_t scan_internal_kexts(void);
extern kern_status_t bring_internal_kexts_online(void);
extern kern_status_t kext_cache_init(void);
extern struct kext *kext_alloc(void);
extern void kext_release(struct kext *kext);
extern kern_status_t kext_register(struct kext *kext);
extern struct kext *kext_get_by_id(const char *ident);
extern kern_status_t kext_bring_online(struct kext *kext);
#endif

View File

@@ -1,5 +1,6 @@
#include <stdint.h>
#include <socks/init.h>
#include <socks/panic.h>
#include <socks/test.h>
#include <socks/printk.h>
#include <socks/kext.h>
@@ -21,7 +22,16 @@ void kernel_init(uintptr_t arg)
{
ml_init(arg);
scan_internal_kexts();
kern_status_t status;
status = scan_internal_kexts();
if (status != KERN_OK) {
panic("scan_internal_kexts() failed with code %s", kern_status_string(status));
}
status = bring_internal_kexts_online();
if (status != KERN_OK) {
panic("bring_internal_kexts_online() failed with code %s", kern_status_string(status));
}
printk("kernel_init() running on processor %u", this_cpu());

View File

@@ -1,36 +1,165 @@
#include <socks/kext.h>
#include <socks/printk.h>
#include <socks/vm.h>
#include <socks/util.h>
#include <socks/libc/stdio.h>
static vm_cache_t kext_cache = { .c_obj_size = sizeof(struct kext) };
static struct kext *self = NULL;
extern btree_t kext_tree;
extern char __kexts_start[];
extern char __kexts_end[];
static kern_status_t collect_dependencies(struct kext *kext, struct kext_info *info)
{
if (!info->k_dependencies) {
return KERN_OK;
}
unsigned int i;
for (i = 0; info->k_dependencies[i]; i++);
kext->k_nr_dependencies = i;
if (!kext->k_nr_dependencies) {
return KERN_OK;
}
kext->k_dependencies = kmalloc(kext->k_nr_dependencies * sizeof (struct kext *), VM_NORMAL);
if (!kext->k_dependencies) {
kext_release(kext);
return KERN_NO_MEMORY;
}
for (i = 0; info->k_dependencies[i]; i++) {
struct kext *dep = kext_get_by_id(info->k_dependencies[i]);
if (!dep) {
kfree(kext->k_dependencies);
printk("kxld: internal kext has unresolved dependency:");
printk("kxld: * kext '%s'", kext->k_ident);
printk("kxld: depends on unknown kext '%s'", info->k_dependencies[i]);
return KERN_NO_ENTRY;
}
kext->k_dependencies[i] = dep;
}
return KERN_OK;
}
static kern_status_t create_kext_from_info(struct kext_info *info, struct kext **out)
{
struct kext *kext = kext_alloc();
if (!kext) {
return KERN_NO_MEMORY;
}
strncpy(kext->k_ident, info->k_ident, sizeof kext->k_ident);
kext->k_ident[sizeof kext->k_ident - 1] = 0;
kext->k_ident_hash = hash_string(kext->k_ident);
kext->k_flags = KEXT_INTERNAL;
kext->k_online = info->k_online;
kext->k_offline = info->k_offline;
kext->k_dependencies = NULL;
kext->k_nr_dependencies = 0;
if (info->k_dependencies) {
unsigned int i;
for (i = 0; info->k_dependencies[i]; i++);
kext->k_nr_dependencies = i;
}
*out = kext;
return KERN_OK;
}
kern_status_t register_internal_kexts(void)
{
struct kext_info *cur = (struct kext_info *)__kexts_start;
struct kext_info *end = (struct kext_info *)__kexts_end;
while (cur < end) {
struct kext *kext;
kern_status_t status = create_kext_from_info(cur, &kext);
if (status != KERN_OK) {
return status;
}
status = kext_register(kext);
if (status != KERN_OK) {
kext_release(kext);
return status;
}
cur = (struct kext_info *)((char *)cur + __KEXT_INFO_ALIGNMENT);
}
return KERN_OK;
}
kern_status_t resolve_internal_dependencies(void)
{
struct kext_info *cur = (struct kext_info *)__kexts_start;
struct kext_info *end = (struct kext_info *)__kexts_end;
while (cur < end) {
struct kext *kext = kext_get_by_id(cur->k_ident);
if (!kext) {
return KERN_NO_ENTRY;
}
kern_status_t status = collect_dependencies(kext, cur);
kext_release(kext);
if (status != KERN_OK) {
return status;
}
cur = (struct kext_info *)((char *)cur + __KEXT_INFO_ALIGNMENT);
}
return KERN_OK;
}
kern_status_t scan_internal_kexts(void)
{
vm_cache_init(&kext_cache);
kext_cache_init();
self = kext_alloc();
if (!self) {
return KERN_NO_MEMORY;
}
self = vm_cache_alloc(&kext_cache, 0);
snprintf(self->k_ident, sizeof self->k_ident, "%s", KERNEL_KEXT_ID);
self->k_flags = KEXT_INTERNAL | KEXT_ONLINE;
self->k_nr_dependencies = 0;
self->k_dependencies = NULL;
struct kext_info *cur = (struct kext_info *)__kexts_start;
struct kext_info *end = (struct kext_info *)__kexts_end;
while (cur < end) {
printk("kext: found internal kext '%s'", cur->k_ident);
cur++;
kern_status_t status = register_internal_kexts();
if (status != KERN_OK) {
return status;
}
return KERN_OK;
status = resolve_internal_dependencies();
return status;
}
kern_status_t bring_internal_kexts_online(void)
{
btree_node_t *cur = btree_first(&kext_tree);
while (cur) {
struct kext *kext = BTREE_CONTAINER(struct kext, k_node, cur);
kern_status_t status = kext_bring_online(kext);
if (status != KERN_OK) {
return status;
}
cur = btree_next(cur);
}
return KERN_OK;
}

176
kxld/kext.c Normal file
View File

@@ -0,0 +1,176 @@
#include <socks/kext.h>
#include <socks/btree.h>
#include <socks/vm.h>
#include <socks/util.h>
#include <socks/object.h>
#include <stddef.h>
static spin_lock_t kext_tree_lock = SPIN_LOCK_INIT;
btree_t kext_tree;
static kern_status_t kext_query_name(object_t *obj, char out[OBJECT_NAME_MAX])
{
struct kext *kext = object_data(obj);
strncpy(out, kext->k_ident, OBJECT_NAME_MAX - 1);
out[OBJECT_NAME_MAX - 1] = 0;
return KERN_OK;
}
static kern_status_t kext_destroy(object_t *obj)
{
struct kext *kext = object_data(obj);
if (kext->k_dependencies) {
kfree(kext->k_dependencies);
}
return KERN_OK;
}
static object_type_t kext_type = {
.ob_name = "kext",
.ob_size = sizeof(struct kext),
.ob_ops = {
.query_name = kext_query_name,
},
};
static struct kext *kext_get(const char *ident)
{
uint64_t ident_hash = hash_string(ident);
btree_node_t *cur = kext_tree.b_root;
while (cur) {
struct kext *cur_node = BTREE_CONTAINER(struct kext, k_node, cur);
if (ident_hash > cur_node->k_ident_hash) {
cur = btree_right(cur);
} else if (ident_hash < cur_node->k_ident_hash) {
cur = btree_left(cur);
} else if (!strcmp(cur_node->k_ident, ident)) {
return cur_node;
}
}
return NULL;
}
static void kext_add(struct kext *kext)
{
if (!kext_tree.b_root) {
kext_tree.b_root = &kext->k_node;
btree_insert_fixup(&kext_tree, &kext->k_node);
return;
}
btree_node_t *cur = kext_tree.b_root;
while (1) {
struct kext *cur_node = BTREE_CONTAINER(struct kext, k_node, cur);
btree_node_t *next = NULL;
if (kext->k_ident_hash > cur_node->k_ident_hash) {
next = btree_right(cur);
if (!next) {
btree_put_right(cur, &kext->k_node);
break;
}
} else if (kext->k_ident_hash < cur_node->k_ident_hash) {
next = btree_left(cur);
if (!next) {
btree_put_left(cur, &kext->k_node);
break;
}
} else {
return;
}
cur = next;
}
btree_insert_fixup(&kext_tree, &kext->k_node);
}
static void kext_remove(struct kext *kext)
{
btree_delete(&kext_tree, &kext->k_node);
}
struct kext *kext_get_by_id(const char *ident)
{
unsigned long flags;
spin_lock_irqsave(&kext_tree_lock, &flags);
struct kext *kext = kext_get(ident);
if (kext) {
object_t *kext_obj = object_header(kext);
object_ref(kext_obj);
}
spin_unlock_irqrestore(&kext_tree_lock, flags);
return kext;
}
kern_status_t kext_cache_init(void)
{
object_type_register(&kext_type);
return KERN_OK;
}
struct kext *kext_alloc(void)
{
object_t *kext_obj = object_create(&kext_type);
if (!kext_obj) {
return NULL;
}
return object_data(kext_obj);
}
void kext_release(struct kext *kext)
{
object_deref(object_header(kext));
}
kern_status_t kext_register(struct kext *kext)
{
unsigned long flags;
spin_lock_irqsave(&kext_tree_lock, &flags);
struct kext *n = kext_get(kext->k_ident);
if (n) {
spin_unlock_irqrestore(&kext_tree_lock, flags);
return KERN_NAME_EXISTS;
}
object_ref(object_header(kext));
kext_add(kext);
spin_unlock_irqrestore(&kext_tree_lock, flags);
return KERN_OK;
}
kern_status_t kext_bring_online(struct kext *kext)
{
if (kext->k_flags & KEXT_ONLINE) {
return KERN_OK;
}
kern_status_t status;
for (unsigned int i = 0; i < kext->k_nr_dependencies; i++) {
status = kext_bring_online(kext->k_dependencies[i]);
if (status != KERN_OK) {
return status;
}
}
if (kext->k_online) {
status = kext->k_online(kext);
if (status != KERN_OK) {
return status;
}
}
kext->k_flags |= KEXT_ONLINE;
return KERN_OK;
}