diff --git a/include/socks/kext.h b/include/socks/kext.h index 3c0919e..5b0e42b 100644 --- a/include/socks/kext.h +++ b/include/socks/kext.h @@ -3,9 +3,11 @@ #include #include +#include #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,36 +17,38 @@ #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__ \ - }; \ - static struct kext_info __section(".kextinfo") __used __KEXT_INFO_VARNAME() = { \ - __KEXT_INFO_FLAGS, \ - ident, \ - online, \ - offline, \ - __KEXT_INFO_DEPNAME(), \ +#define DEFINE_KEXT(ident, online, offline, ...) \ + static const char *__KEXT_INFO_DEPNAME()[] = { \ + __VA_ARGS__, NULL \ + }; \ + static struct kext_info __section(".kextinfo") __aligned(__KEXT_INFO_ALIGNMENT) __used __KEXT_INFO_VARNAME() = { \ + __KEXT_INFO_FLAGS, \ + ident, \ + online, \ + offline, \ + __KEXT_INFO_DEPNAME(), \ } #else -#define DEFINE_KEXT(ident, online, offline, ...) \ - static const char *__kext_deps[] = { \ - __VA_ARGS__ \ - }; \ - static struct kext_info __section(".kextinfo") __used __kext_info = { \ - .k_flags = __KEXT_INFO_FLAGS, \ - .k_ident = ident, \ - .k_online = online, \ - .k_offline = offline, \ - .k_dependencies = __kext_deps, \ +#define DEFINE_KEXT(ident, online, offline, ...) \ + static const char *__KEXT_INFO_DEPNAME()[] = { \ + __VA_ARGS__, NULL \ + }; \ + 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_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 diff --git a/init/main.c b/init/main.c index 6e698c0..1a3b2de 100644 --- a/init/main.c +++ b/init/main.c @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -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()); diff --git a/kxld/internal.c b/kxld/internal.c index 2c577ea..6f553ec 100644 --- a/kxld/internal.c +++ b/kxld/internal.c @@ -1,36 +1,165 @@ #include #include #include +#include #include -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; } diff --git a/kxld/kext.c b/kxld/kext.c new file mode 100644 index 0000000..8d9a07f --- /dev/null +++ b/kxld/kext.c @@ -0,0 +1,176 @@ +#include +#include +#include +#include +#include +#include + +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; +}