From 85006411bde3800c51357d4eafe955431f918448 Mon Sep 17 00:00:00 2001 From: Max Wash Date: Thu, 19 Feb 2026 19:13:44 +0000 Subject: [PATCH] kernel: add header files --- arch/x86_64/include/kernel/machine/cpu.h | 77 ++++ arch/x86_64/include/kernel/machine/hwlock.h | 22 + arch/x86_64/include/kernel/machine/init.h | 28 ++ arch/x86_64/include/kernel/machine/irq.h | 5 + arch/x86_64/include/kernel/machine/panic.h | 12 + arch/x86_64/include/kernel/machine/pmap.h | 11 + arch/x86_64/include/kernel/machine/thread.h | 31 ++ arch/x86_64/include/kernel/machine/vm.h | 30 ++ include/kernel/arg.h | 14 + include/kernel/bitmap.h | 35 ++ include/kernel/bsp.h | 38 ++ include/kernel/btree.h | 475 ++++++++++++++++++++ include/kernel/channel.h | 53 +++ include/kernel/clock.h | 27 ++ include/kernel/compiler.h | 41 ++ include/kernel/console.h | 56 +++ include/kernel/cpu.h | 65 +++ include/kernel/fb.h | 49 ++ include/kernel/flags.h | 11 + include/kernel/handle.h | 65 +++ include/kernel/init.h | 41 ++ include/kernel/input.h | 184 ++++++++ include/kernel/iovec.h | 23 + include/kernel/locks.h | 25 ++ include/kernel/memblock.h | 345 ++++++++++++++ include/kernel/msg.h | 30 ++ include/kernel/object.h | 95 ++++ include/kernel/panic.h | 13 + include/kernel/percpu.h | 36 ++ include/kernel/pmap.h | 81 ++++ include/kernel/port.h | 39 ++ include/kernel/printk.h | 25 ++ include/kernel/queue.h | 62 +++ include/kernel/ringbuffer.h | 34 ++ include/kernel/sched.h | 277 ++++++++++++ include/kernel/syscall.h | 183 ++++++++ include/kernel/test.h | 14 + include/kernel/types.h | 18 + include/kernel/util.h | 70 +++ include/kernel/vm-object.h | 91 ++++ include/kernel/vm-region.h | 146 ++++++ include/kernel/vm.h | 358 +++++++++++++++ 42 files changed, 3335 insertions(+) create mode 100644 arch/x86_64/include/kernel/machine/cpu.h create mode 100644 arch/x86_64/include/kernel/machine/hwlock.h create mode 100644 arch/x86_64/include/kernel/machine/init.h create mode 100644 arch/x86_64/include/kernel/machine/irq.h create mode 100644 arch/x86_64/include/kernel/machine/panic.h create mode 100644 arch/x86_64/include/kernel/machine/pmap.h create mode 100644 arch/x86_64/include/kernel/machine/thread.h create mode 100644 arch/x86_64/include/kernel/machine/vm.h create mode 100644 include/kernel/arg.h create mode 100644 include/kernel/bitmap.h create mode 100644 include/kernel/bsp.h create mode 100644 include/kernel/btree.h create mode 100644 include/kernel/channel.h create mode 100644 include/kernel/clock.h create mode 100644 include/kernel/compiler.h create mode 100644 include/kernel/console.h create mode 100644 include/kernel/cpu.h create mode 100644 include/kernel/fb.h create mode 100644 include/kernel/flags.h create mode 100644 include/kernel/handle.h create mode 100644 include/kernel/init.h create mode 100644 include/kernel/input.h create mode 100644 include/kernel/iovec.h create mode 100644 include/kernel/locks.h create mode 100644 include/kernel/memblock.h create mode 100644 include/kernel/msg.h create mode 100644 include/kernel/object.h create mode 100644 include/kernel/panic.h create mode 100644 include/kernel/percpu.h create mode 100644 include/kernel/pmap.h create mode 100644 include/kernel/port.h create mode 100644 include/kernel/printk.h create mode 100644 include/kernel/queue.h create mode 100644 include/kernel/ringbuffer.h create mode 100644 include/kernel/sched.h create mode 100644 include/kernel/syscall.h create mode 100644 include/kernel/test.h create mode 100644 include/kernel/types.h create mode 100644 include/kernel/util.h create mode 100644 include/kernel/vm-object.h create mode 100644 include/kernel/vm-region.h create mode 100644 include/kernel/vm.h diff --git a/arch/x86_64/include/kernel/machine/cpu.h b/arch/x86_64/include/kernel/machine/cpu.h new file mode 100644 index 0000000..bbd2792 --- /dev/null +++ b/arch/x86_64/include/kernel/machine/cpu.h @@ -0,0 +1,77 @@ +#ifndef KERNEL_X86_64_CPU_H_ +#define KERNEL_X86_64_CPU_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define ML_BIG_ENDIAN 0 + +#define ml_cpu_block_get_id(p) ((p)->c_cpu_id) +#define ml_cpu_block_get_data(p) ((p)->c_data) + +#if 0 +#define ml_read_sp(sp, bp) \ + asm volatile("mov %%rsp, %0" : "=r"(sp)); \ + asm volatile("mov %%rbp, %0" : "=r"(bp)); +#endif + +struct cpu_data; + +typedef struct ml_cpu_block { + struct ml_cpu_block *c_this; + + struct gdt c_gdt; + struct gdt_ptr c_gdt_ptr; + + struct tss c_tss; + struct tss_ptr c_tss_ptr; + + struct idt_ptr c_idt_ptr; + unsigned int c_cpu_id; + + struct cpu_data *c_data; +} ml_cpu_block; + +struct ml_int_context { + uint64_t rip, cs, rflags, rsp, ss; +}; + +struct ml_cpu_context { + uint64_t r15, r14, r13, r12, r11, r10, r9, r8; + uint64_t rdi, rsi, rbp, unused_rsp, rbx, rdx, rcx, rax; + uint64_t int_no, err_no; + uint64_t rip, cs, rflags, rsp, ss; +} __packed; + +#define ml_cpu_pause() __asm__ __volatile__("hlt") +#define ml_cpu_relax() __asm__ __volatile__("pause") + +#define ml_int_disable() __asm__ __volatile__("cli") +#define ml_int_enable() __asm__ __volatile__("sti") + +extern int ml_init_bootcpu(void); + +extern int ml_cpu_block_init(ml_cpu_block *p); +extern int ml_cpu_block_use(ml_cpu_block *p); + +extern virt_addr_t ml_cpu_block_get_ustack(ml_cpu_block *p); +extern virt_addr_t ml_cpu_block_get_kstack(ml_cpu_block *p); + +extern void ml_cpu_block_set_ustack(ml_cpu_block *p, virt_addr_t sp); +extern void ml_cpu_block_set_kstack(ml_cpu_block *p, virt_addr_t sp); + +/* defined in cpu_ctrl.S */ +extern void ml_halt_cpu(void); +extern ml_cpu_block *ml_this_cpu(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/arch/x86_64/include/kernel/machine/hwlock.h b/arch/x86_64/include/kernel/machine/hwlock.h new file mode 100644 index 0000000..355e82c --- /dev/null +++ b/arch/x86_64/include/kernel/machine/hwlock.h @@ -0,0 +1,22 @@ +#ifndef KERNEL_X86_64_HWLOCK_H_ +#define KERNEL_X86_64_HWLOCK_H_ + +#define ML_HWLOCK_INIT (0) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int ml_hwlock_t; + +extern void ml_hwlock_lock(ml_hwlock_t *lck); +extern void ml_hwlock_unlock(ml_hwlock_t *lck); + +extern void ml_hwlock_lock_irqsave(ml_hwlock_t *lck, unsigned long *flags); +extern void ml_hwlock_unlock_irqrestore(ml_hwlock_t *lck, unsigned long flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/arch/x86_64/include/kernel/machine/init.h b/arch/x86_64/include/kernel/machine/init.h new file mode 100644 index 0000000..719ee10 --- /dev/null +++ b/arch/x86_64/include/kernel/machine/init.h @@ -0,0 +1,28 @@ +#ifndef KERNEL_X86_64_INIT_H_ +#define KERNEL_X86_64_INIT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define __X2(x) #x +#define __X(x) __X2(x) + +#define __define_initcall(fn, id) \ + static initcall_t __initcall_##fn##id __used __section( \ + ".initcall" __X(id) ".init") \ + = (fn) + +extern int ml_init(uintptr_t arg); + +extern const struct framebuffer_varinfo *bootfb_varinfo(void); +extern const struct framebuffer_fixedinfo *bootfb_fixedinfo(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/arch/x86_64/include/kernel/machine/irq.h b/arch/x86_64/include/kernel/machine/irq.h new file mode 100644 index 0000000..3eb9922 --- /dev/null +++ b/arch/x86_64/include/kernel/machine/irq.h @@ -0,0 +1,5 @@ +#ifndef KERNEL_X86_64_IRQ_H_ +#define KERNEL_X86_64_IRQ_H_ + + +#endif diff --git a/arch/x86_64/include/kernel/machine/panic.h b/arch/x86_64/include/kernel/machine/panic.h new file mode 100644 index 0000000..d16be93 --- /dev/null +++ b/arch/x86_64/include/kernel/machine/panic.h @@ -0,0 +1,12 @@ +#ifndef KERNEL_X86_64_PANIC_H_ +#define KERNEL_X86_64_PANIC_H_ + +#include + +struct ml_cpu_context; + +extern void ml_print_cpu_state(struct ml_cpu_context *ctx); +extern void ml_print_stack_trace(uintptr_t ip); +extern void ml_print_stack_trace_irq(struct ml_cpu_context *ctx); + +#endif diff --git a/arch/x86_64/include/kernel/machine/pmap.h b/arch/x86_64/include/kernel/machine/pmap.h new file mode 100644 index 0000000..bb1dfd6 --- /dev/null +++ b/arch/x86_64/include/kernel/machine/pmap.h @@ -0,0 +1,11 @@ +#ifndef KERNEL_X86_64_PMAP_H_ +#define KERNEL_X86_64_PMAP_H_ + +#include + +#define ML_PMAP_INVALID ((uintptr_t)-1) + +typedef pml4t_ptr_t ml_pmap_t; +typedef uint64_t ml_pfn_t; + +#endif diff --git a/arch/x86_64/include/kernel/machine/thread.h b/arch/x86_64/include/kernel/machine/thread.h new file mode 100644 index 0000000..2b963af --- /dev/null +++ b/arch/x86_64/include/kernel/machine/thread.h @@ -0,0 +1,31 @@ +#ifndef KERNEL_X86_64_THREAD_H_ +#define KERNEL_X86_64_THREAD_H_ + +#include + +struct ml_cpu_context; + +/* switch from one thread to another. the stack of the `to` thread must have + * been prepared in one of two ways: + * 1) a previous call to ml_thread_switch where it was the `from` thread. + * 2) a call to ml_thread_prepare_kernel_context + * the switch occurs entirely with kernel mode. a further return from an + * interrupt context is then used to return to usermode if necessary. + */ +extern void ml_thread_switch(struct thread *from, struct thread *to); + +/* perform the initial transition to userspace. the stack must be prepared using + * ml_thread_prepare_user_context before this function can be used */ +extern void ml_thread_switch_user(void); +/* prepare the stack so that ml_thread_switch can jump to the specified IP/SP */ +extern void ml_thread_prepare_kernel_context(uintptr_t ip, uintptr_t *sp); +/* prepare the stack so that ml_thread_switch_user can jump to usermode + * with the specified IP/user SP */ +extern kern_status_t ml_thread_prepare_user_context( + virt_addr_t ip, + virt_addr_t user_sp, + virt_addr_t *kernel_sp, + const uintptr_t *args, + size_t nr_args); + +#endif diff --git a/arch/x86_64/include/kernel/machine/vm.h b/arch/x86_64/include/kernel/machine/vm.h new file mode 100644 index 0000000..01c2a8d --- /dev/null +++ b/arch/x86_64/include/kernel/machine/vm.h @@ -0,0 +1,30 @@ +#ifndef KERNEL_X86_64_VM_H_ +#define KERNEL_X86_64_VM_H_ + +/* kernel higher-half base virtual address. */ +#define VM_KERNEL_VOFFSET 0xFFFFFFFF80000000 + +/* direct page-mapping region. + NOTE that these are the maximum bounds of this region. + the actual size depends on the amount of physical + memory present. */ +#define VM_PAGEMAP_BASE 0xFFFF888000000000 +#define VM_PAGEMAP_LIMIT 0xFFFFC87FFFFFFFFF + +#define VM_PAGE_SIZE 0x1000 +#define VM_PAGE_MASK (VM_PAGE_SIZE - 1) +#define VM_PAGE_SHIFT 12 + +#define VM_PAGE_MIN_ORDER VM_PAGE_4K +#define VM_PAGE_MAX_ORDER VM_PAGE_8M + +#define VM_ZONE_MIN VM_ZONE_DMA +#define VM_ZONE_MAX VM_ZONE_NORMAL + +#define VM_USER_BASE 0x0000000000100000 +#define VM_USER_LIMIT 0x00007fffffffffff + +#define VM_KERNEL_BASE 0XFFFF800000000000 +#define VM_KERNEL_LIMIT 0XFFFFFFFFFFFFFFFF + +#endif diff --git a/include/kernel/arg.h b/include/kernel/arg.h new file mode 100644 index 0000000..6b0cdde --- /dev/null +++ b/include/kernel/arg.h @@ -0,0 +1,14 @@ +#ifndef KERNEL_ARG_H_ +#define KERNEL_ARG_H_ + +#include +#include + +#define CMDLINE_MAX 4096 + +extern kern_status_t parse_cmdline(const char *cmdline); + +extern const char *arg_value(const char *arg_name); +extern bool arg_is_set(const char *arg_name); + +#endif diff --git a/include/kernel/bitmap.h b/include/kernel/bitmap.h new file mode 100644 index 0000000..2216846 --- /dev/null +++ b/include/kernel/bitmap.h @@ -0,0 +1,35 @@ +#ifndef KERNEL_BITMAP_H_ +#define KERNEL_BITMAP_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BITS_PER_WORD (8 * sizeof(unsigned long)) +#define __DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) +#define BITMAP_WORDS(nbits) __DIV_ROUND_UP(nbits, BITS_PER_WORD) +#define BITMAP_NPOS ((unsigned int)-1) + +#define DECLARE_BITMAP(name, nbits) unsigned long name[BITMAP_WORDS(nbits)] + +extern void bitmap_zero(unsigned long *map, unsigned long nbits); +extern void bitmap_fill(unsigned long *map, unsigned long nbits); +extern void bitmap_set(unsigned long *map, unsigned long bit); +extern void bitmap_clear(unsigned long *map, unsigned long bit); +extern bool bitmap_check(unsigned long *map, unsigned long bit); + +extern unsigned int bitmap_count_set(unsigned long *map, unsigned long nbits); +extern unsigned int bitmap_count_clear(unsigned long *map, unsigned long nbits); + +extern unsigned int bitmap_highest_set(unsigned long *map, unsigned long nbits); +extern unsigned int bitmap_highest_clear(unsigned long *map, unsigned long nbits); +extern unsigned int bitmap_lowest_set(unsigned long *map, unsigned long nbits); +extern unsigned int bitmap_lowest_clear(unsigned long *map, unsigned long nbits); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/bsp.h b/include/kernel/bsp.h new file mode 100644 index 0000000..f22e6d9 --- /dev/null +++ b/include/kernel/bsp.h @@ -0,0 +1,38 @@ +#ifndef KERNEL_BSP_H_ +#define KERNEL_BSP_H_ + +#include +#include +#include +#include +#include + +#define BSP_MAGIC 0xcafebabe + +struct task; + +struct bsp_trailer { + /* these fields are stored in big endian in the package itself */ + uint32_t bsp_magic; + uint64_t bsp_fs_offset; + uint32_t bsp_fs_len; + uint64_t bsp_exec_offset; + uint32_t bsp_exec_len; + uint64_t bsp_text_faddr, bsp_text_vaddr, bsp_text_size; + uint64_t bsp_data_faddr, bsp_data_vaddr, bsp_data_size; + uint64_t bsp_exec_entry; +} __packed; + +struct bsp { + /* the values in this struct are stored in host byte order */ + struct bsp_trailer bsp_trailer; + struct vm_object *bsp_vmo; +}; + +extern void bsp_set_location(const struct boot_module *mod); +extern void bsp_get_location(struct boot_module *out); + +extern kern_status_t bsp_load(struct bsp *bsp, const struct boot_module *mod); +extern kern_status_t bsp_launch_async(struct bsp *bsp, struct task *task); + +#endif diff --git a/include/kernel/btree.h b/include/kernel/btree.h new file mode 100644 index 0000000..af835e3 --- /dev/null +++ b/include/kernel/btree.h @@ -0,0 +1,475 @@ +/* + The Clear BSD License + + Copyright (c) 2023 Max Wash + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted (subject to the limitations in the disclaimer + below) provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + */ + +#ifndef KERNEL_BTREE_H_ +#define KERNEL_BTREE_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* if your custom structure contains a struct btree_node (i.e. it can be part of + a btree), you can use this macro to convert a struct btree_node* to a + your_type* + + @param t the name of your custom type (something that can be passed to + offsetof) + @param m the name of the struct btree_node member variable within your custom + type. + @param v the struct btree_node pointer that you wish to convert. if this is + NULL, NULL will be returned. +*/ +#define BTREE_CONTAINER(t, m, v) \ + ((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0)) + +/* defines a simple node insertion function. + this function assumes that your nodes have simple integer keys that can be + compared with the usual operators. + + EXAMPLE: + if you have a tree node type like this: + + struct my_tree_node { + int key; + struct btree_node base; + } + + You would use the following call to generate an insert function for a tree + with this node type: + + BTREE_DEFINE_SIMPLE_INSERT(struct my_tree_node, base, key, + my_tree_node_insert); + + Which would emit a function defined like: + + static void my_tree_node_insert(struct btree *tree, struct my_tree_node + *node); + + @param node_type your custom tree node type. usually a structure that + contains a struct btree_node member. + @param container_node_member the name of the struct btree_node member + variable within your custom type. + @param container_key_member the name of the key member variable within your + custom type. + @param function_name the name of the function to generate. +*/ +#define BTREE_DEFINE_SIMPLE_INSERT( \ + node_type, \ + container_node_member, \ + container_key_member, \ + function_name) \ + void function_name(struct btree *tree, node_type *node) \ + { \ + if (!tree->b_root) { \ + tree->b_root = &node->container_node_member; \ + btree_insert_fixup( \ + tree, \ + &node->container_node_member); \ + return; \ + } \ + \ + struct btree_node *cur = tree->b_root; \ + while (1) { \ + node_type *cur_node = BTREE_CONTAINER( \ + node_type, \ + container_node_member, \ + cur); \ + struct btree_node *next = NULL; \ + \ + if (node->container_key_member \ + > cur_node->container_key_member) { \ + next = btree_right(cur); \ + \ + if (!next) { \ + btree_put_right( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else if ( \ + node->container_key_member \ + < cur_node->container_key_member) { \ + next = btree_left(cur); \ + \ + if (!next) { \ + btree_put_left( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else { \ + return; \ + } \ + \ + cur = next; \ + } \ + \ + btree_insert_fixup(tree, &node->container_node_member); \ + } + +/* defines a node insertion function. + this function should be used for trees with complex node keys that cannot be + directly compared. a comparator for your keys must be supplied. + + EXAMPLE: + if you have a tree node type like this: + + struct my_tree_node { + complex_key_t key; + struct btree_node base; + } + + You would need to define a comparator function or macro with the following + signature: + + int my_comparator(struct my_tree_node *a, struct my_tree_node *b); + + Which implements the following: + + return -1 if a < b + return 0 if a == b + return 1 if a > b + + You would use the following call to generate an insert function for a tree + with this node type: + + BTREE_DEFINE_INSERT(struct my_tree_node, base, key, my_tree_node_insert, + my_comparator); + + Which would emit a function defined like: + + static void my_tree_node_insert(struct btree *tree, struct my_tree_node + *node); + + @param node_type your custom tree node type. usually a structure that + contains a struct btree_node member. + @param container_node_member the name of the struct btree_node member + variable within your custom type. + @param container_key_member the name of the key member variable within your + custom type. + @param function_name the name of the function to generate. + @param comparator the name of a comparator function or functional-macro that + conforms to the requirements listed above. +*/ +#define BTREE_DEFINE_INSERT( \ + node_type, \ + container_node_member, \ + container_key_member, \ + function_name, \ + comparator) \ + void function_name(struct btree *tree, node_type *node) \ + { \ + if (!tree->b_root) { \ + tree->b_root = &node->container_node_member; \ + btree_insert_fixup( \ + tree, \ + &node->container_node_member); \ + return; \ + } \ + \ + struct btree_node *cur = tree->b_root; \ + while (1) { \ + node_type *cur_node = BTREE_CONTAINER( \ + node_type, \ + container_node_member, \ + cur); \ + struct btree_node *next = NULL; \ + int cmp = comparator(node, cur_node); \ + \ + if (cmp == 1) { \ + next = btree_right(cur); \ + \ + if (!next) { \ + btree_put_right( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else if (cmp == -1) { \ + next = btree_left(cur); \ + \ + if (!next) { \ + btree_put_left( \ + cur, \ + &node->container_node_member); \ + break; \ + } \ + } else { \ + return; \ + } \ + \ + cur = next; \ + } \ + \ + btree_insert_fixup(tree, &node->container_node_member); \ + } + +/* defines a simple tree search function. + this function assumes that your nodes have simple integer keys that can be + compared with the usual operators. + + EXAMPLE: + if you have a tree node type like this: + + struct my_tree_node { + int key; + struct btree_node base; + } + + You would use the following call to generate a search function for a tree + with this node type: + + BTREE_DEFINE_SIMPLE_GET(struct my_tree_node, int, base, key, + my_tree_node_get); + + Which would emit a function defined like: + + static struct my_tree_node *my_tree_node_get(struct btree *tree, int key); + + @param node_type your custom tree node type. usually a structure that + contains a struct btree_node member. + @param key_type the type name of the key embedded in your custom tree node + type. this type must be compatible with the builtin comparison operators. + @param container_node_member the name of the struct btree_node member + variable within your custom type. + @param container_key_member the name of the key member variable within your + custom type. + @param function_name the name of the function to generate. +*/ +#define BTREE_DEFINE_SIMPLE_GET( \ + node_type, \ + key_type, \ + container_node_member, \ + container_key_member, \ + function_name) \ + node_type *function_name(struct btree *tree, key_type key) \ + { \ + struct btree_node *cur = tree->b_root; \ + while (cur) { \ + node_type *cur_node = BTREE_CONTAINER( \ + node_type, \ + container_node_member, \ + cur); \ + if (key > cur_node->container_key_member) { \ + cur = btree_right(cur); \ + } else if (key < cur_node->container_key_member) { \ + cur = btree_left(cur); \ + } else { \ + return cur_node; \ + } \ + } \ + \ + return NULL; \ + } + +/* perform an in-order traversal of a binary tree + + If you have a tree defined like: + + struct btree my_tree; + + with nodes defined like: + + struct my_tree_node { + int key; + struct btree_node base; + } + + and you want to do something like: + + foreach (struct my_tree_node *node : my_tree) { ... } + + you should use this: + + btree_foreach (struct my_tree_node, node, &my_tree, base) { ... } + + @param iter_type the type name of the iterator variable. this should be the + tree's node type, and shouldn't be a pointer. + @param iter_name the name of the iterator variable. + @param tree_name a pointer to the tree to traverse. + @param node_member the name of the struct btree_node member variable within + the tree node type. +*/ +#define btree_foreach(iter_type, iter_name, tree_name, node_member) \ + for (iter_type *iter_name = BTREE_CONTAINER( \ + iter_type, \ + node_member, \ + btree_first(tree_name)); \ + iter_name; \ + iter_name = BTREE_CONTAINER( \ + iter_type, \ + node_member, \ + btree_next(&((iter_name)->node_member)))) + +/* perform an reverse in-order traversal of a binary tree + + If you have a tree defined like: + + struct btree my_tree; + + with nodes defined like: + + struct my_tree_node { + int key; + struct btree_node base; + } + + and you want to do something like: + + foreach (struct my_tree_node *node : reverse(my_tree)) { ... } + + you should use this: + + btree_foreach_r (struct my_tree_node, node, &my_tree, base) { ... } + + @param iter_type the type name of the iterator variable. this should be the + tree's node type, and shouldn't be a pointer. + @param iter_name the name of the iterator variable. + @param tree_name a pointer to the tree to traverse. + @param node_member the name of the struct btree_node member variable within + the tree node type. +*/ +#define btree_foreach_r(iter_type, iter_name, tree_name, node_member) \ + for (iter_type *iter_name \ + = BTREE_CONTAINER(iter_type, node_member, btree_last(tree_name)); \ + iter_name; \ + iter_name = BTREE_CONTAINER( \ + iter_type, \ + node_member, \ + btree_prev(&((iter_name)->node_member)))) + +/* binary tree nodes. this *cannot* be used directly. you need to define a + custom node type that contains a member variable of type struct btree_node. + + you would then use the supplied macros to define functions to manipulate your + custom binary tree. +*/ +struct btree_node { + struct btree_node *b_parent, *b_left, *b_right; + unsigned short b_height; +}; + +/* binary tree. unlike struct btree_node, you can define variables of type + * struct btree. */ +struct btree { + struct btree_node *b_root; +}; + +/* re-balance a binary tree after an insertion operation. + + NOTE that, if you define an insertion function using BTREE_DEFINE_INSERT or + similar, this function will automatically called for you. + + @param tree the tree to re-balance. + @param node the node that was just inserted into the tree. +*/ +extern void btree_insert_fixup(struct btree *tree, struct btree_node *node); + +/* delete a node from a binary tree and re-balance the tree afterwards. + + @param tree the tree to delete from + @param node the node to delete. +*/ +extern void btree_delete(struct btree *tree, struct btree_node *node); + +/* get the first node in a binary tree. + + this will be the node with the smallest key (i.e. the node that is + furthest-left from the root) +*/ +extern struct btree_node *btree_first(struct btree *tree); + +/* get the last node in a binary tree. + + this will be the node with the largest key (i.e. the node that is + furthest-right from the root) +*/ +extern struct btree_node *btree_last(struct btree *tree); +/* for any binary tree node, this function returns the node with the + * next-largest key value */ +extern struct btree_node *btree_next(struct btree_node *node); +/* for any binary tree node, this function returns the node with the + * next-smallest key value */ +extern struct btree_node *btree_prev(struct btree_node *node); + +static inline bool btree_empty(const struct btree *tree) +{ + return tree->b_root == NULL; +} + +/* sets `child` as the immediate left-child of `parent` */ +static inline void btree_put_left( + struct btree_node *parent, + struct btree_node *child) +{ + parent->b_left = child; + child->b_parent = parent; +} + +/* sets `child` as the immediate right-child of `parent` */ +static inline void btree_put_right( + struct btree_node *parent, + struct btree_node *child) +{ + parent->b_right = child; + child->b_parent = parent; +} + +/* get the immediate left-child of `node` */ +static inline struct btree_node *btree_left(struct btree_node *node) +{ + return node->b_left; +} + +/* get the immediate right-child of `node` */ +static inline struct btree_node *btree_right(struct btree_node *node) +{ + return node->b_right; +} + +/* get the immediate parent of `node` */ +static inline struct btree_node *btree_parent(struct btree_node *node) +{ + return node->b_parent; +} + +/* get the height of `node`. + + the height of a node is defined as the length of the longest path + between the node and a leaf node. + + this count includes the node itself, so the height of a leaf node will be 1. +*/ +static inline unsigned short btree_height(struct btree_node *node) +{ + return node->b_height; +} + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/channel.h b/include/kernel/channel.h new file mode 100644 index 0000000..83314cb --- /dev/null +++ b/include/kernel/channel.h @@ -0,0 +1,53 @@ +#ifndef KERNEL_CHANNEL_H_ +#define KERNEL_CHANNEL_H_ + +#include +#include + +struct kmsg; + +struct channel { + struct object c_base; + unsigned int c_id; + struct waitqueue c_wq; + struct btree c_msg; + struct btree_node c_node; +}; + +extern kern_status_t channel_type_init(void); + +extern struct channel *channel_create(void); + +extern kern_status_t channel_enqueue_msg( + struct channel *channel, + struct kmsg *msg); + +extern kern_status_t channel_recv_msg( + struct channel *channel, + struct msg *out_msg, + msgid_t *out_id, + unsigned long *irq_flags); +extern kern_status_t channel_reply_msg( + struct channel *channel, + msgid_t id, + const struct msg *resp, + unsigned long *irq_flags); + +extern kern_status_t channel_read_msg( + struct channel *channel, + msgid_t msg, + size_t offset, + void *buf, + size_t len, + size_t *nr_read); +extern kern_status_t channel_write_msg( + struct channel *channel, + msgid_t msg, + size_t offset, + const void *buf, + size_t len, + size_t *nr_written); + +DEFINE_OBJECT_LOCK_FUNCTION(channel, c_base) + +#endif diff --git a/include/kernel/clock.h b/include/kernel/clock.h new file mode 100644 index 0000000..bcb3b88 --- /dev/null +++ b/include/kernel/clock.h @@ -0,0 +1,27 @@ +#ifndef KERNEL_CLOCK_H_ +#define KERNEL_CLOCK_H_ + +#include + +#ifdef __cplusplus +extern "C" { +#endif + + +#define HZ (clock_hz()) + +typedef uint64_t clock_ticks_t; + +extern volatile clock_ticks_t clock_ticks; + +extern clock_ticks_t clock_hz(void); + +extern void clock_calibrate(clock_ticks_t hz); +extern void clock_advance(clock_ticks_t ticks); +extern void clock_wait(clock_ticks_t ticks); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/compiler.h b/include/kernel/compiler.h new file mode 100644 index 0000000..30d0847 --- /dev/null +++ b/include/kernel/compiler.h @@ -0,0 +1,41 @@ +#ifndef KERNEL_COMPILER_H_ +#define KERNEL_COMPILER_H_ + +#ifdef __cplusplus +template +T read_once(const volatile T *ptr) +{ + return *ptr; +} + +template +void write_once(volatile T *ptr, T value) +{ + *ptr = value; +} +#else + +#define READ_ONCE(p) (*((const volatile typeof(p) *)&(p))) +#define WRITE_ONCE(p, v) *(volatile typeof(p) *)&(p) = (v) + +#endif + +#undef __used +#define __used __attribute__((__used__)) + +#undef __noreturn +#define __noreturn __attribute__((__noreturn__)) + +#undef __packed +#define __packed __attribute__((__packed__)) + +#undef __section +#define __section(name) __attribute__((__section__(name))) + +#undef __aligned +#define __aligned(x) __attribute__((__aligned__(x))) + +#undef __weak +#define __weak __attribute__((__weak__)) + +#endif diff --git a/include/kernel/console.h b/include/kernel/console.h new file mode 100644 index 0000000..347f416 --- /dev/null +++ b/include/kernel/console.h @@ -0,0 +1,56 @@ +#ifndef KERNEL_CONSOLE_H_ +#define KERNEL_CONSOLE_H_ + +/* The console system + + Consoles are like simplified TTYs. Their purpose is to serve as an output + sink for messages printed using printk. + + a struct console could be used to represent a serial port, UART port, or even + a text-based framebuffer display. Anything where the job of displaying + or sending text can be abstracted to a simple write() call. + + A secondary purpose of consoles is to allow input. For example, a console + representing a serial port may allow both sending AND receiving over the + port. +*/ +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum console_flags { + /* console is only used during the boot process. the console + will be automatically de-registered when the first + non-boot console is registered */ + CON_BOOT = 0x01u, +}; + +struct console { + char c_name[16]; + enum console_flags c_flags; + spin_lock_t c_lock; + + void (*c_write)(struct console *, const char *, unsigned int); + int (*c_read)(struct console *, char *, unsigned int); + + struct queue_entry c_list; +}; + +extern kern_status_t console_register(struct console *con); +extern kern_status_t console_unregister(struct console *con); + +extern struct queue *get_consoles(unsigned long *flags); +extern void put_consoles(struct queue *consoles, unsigned long flags); + +extern void console_write(struct console *con, const char *s, unsigned int len); +extern int console_read(struct console *con, char *s, unsigned int len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/cpu.h b/include/kernel/cpu.h new file mode 100644 index 0000000..47c7d39 --- /dev/null +++ b/include/kernel/cpu.h @@ -0,0 +1,65 @@ +#ifndef KERNEL_CPU_H_ +#define KERNEL_CPU_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum cpu_flags { + CPU_ONLINE = 0x01u, +}; + +struct cpu_data { + enum cpu_flags c_flags; + unsigned int c_id; + int c_preempt_count; + + struct runqueue c_rq; + struct workqueue c_wq; + struct queue c_timers; +}; + +/* maximum number of processor cores that the kernel can support. + TODO move to build config option */ +#define CPU_MAX 128 + +#define this_cpu() (ml_cpu_block_get_id(ml_this_cpu())) + +extern struct cpu_data *get_this_cpu(void); +extern struct cpu_data *get_cpu(unsigned int id); +extern void put_cpu(struct cpu_data *cpu); + +extern bool cpu_is_available(unsigned int cpu_id); +extern bool cpu_is_online(unsigned int cpu_id); + +extern void cpu_set_available(unsigned int cpu_id); +extern void cpu_set_online(unsigned int cpu_id); + +extern unsigned int cpu_nr_available(void); +extern unsigned int cpu_nr_online(void); + +extern cycles_t get_cycles(void); +static inline cycles_t cycles_diff(cycles_t then, cycles_t now) +{ + return now >= then ? now - then : (CYCLES_MAX - then) + now; +} + +#define irq_enable() ml_int_enable() +#define irq_disable() ml_int_disable() + +extern void preempt_disable(void); +extern void preempt_enable(void); +extern int preempt_count(void); + +extern unsigned int cpu_get_highest_available(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/fb.h b/include/kernel/fb.h new file mode 100644 index 0000000..2a2a034 --- /dev/null +++ b/include/kernel/fb.h @@ -0,0 +1,49 @@ +#ifndef KERNEL_FB_H_ +#define KERNEL_FB_H_ + +#include + +enum framebuffer_flags { + FB_MODE_RGB = 0x01u, + FB_MODE_VGATEXT = 0x02u, + FB_MODE_PALETTE = 0x04u, +}; + +struct framebuffer_bitfield { + uint32_t b_offset; + uint16_t b_length; +}; + +struct framebuffer_varinfo { + enum framebuffer_flags fb_flags; + + uint32_t fb_xres; + uint32_t fb_yres; + uint32_t fb_bpp; + uint32_t fb_stride; + + union { + struct { + uint32_t fb_xcells; + uint32_t fb_ycells; + }; + + struct { + struct framebuffer_bitfield fb_red; + struct framebuffer_bitfield fb_green; + struct framebuffer_bitfield fb_blue; + struct framebuffer_bitfield fb_alpha; + }; + + struct { + uintptr_t fb_palette_addr; + uint16_t fb_palette_nr_colours; + }; + }; +}; + +struct framebuffer_fixedinfo { + uint64_t fb_baseptr; +}; + +#endif diff --git a/include/kernel/flags.h b/include/kernel/flags.h new file mode 100644 index 0000000..9056c0c --- /dev/null +++ b/include/kernel/flags.h @@ -0,0 +1,11 @@ +#ifndef KERNEL_FLAGS_H_ +#define KERNEL_FLAGS_H_ + +#include + +typedef enum { + S_NORMAL = 0x00u, + S_NOBLOCK = 0x01u, +} mango_flags_t; + +#endif diff --git a/include/kernel/handle.h b/include/kernel/handle.h new file mode 100644 index 0000000..1c12aae --- /dev/null +++ b/include/kernel/handle.h @@ -0,0 +1,65 @@ +#ifndef KERNEL_HANDLE_H_ +#define KERNEL_HANDLE_H_ + +#include +#include +#include +#include + +/* subtract 32 bytes to account for the handle bitmap */ +#define HANDLES_PER_TABLE ((4096 - 32) / sizeof(struct handle)) +#define REFS_PER_TABLE ((4096 - 64) / sizeof(struct handle_table *)) + +typedef uint32_t kern_handle_t; + +typedef uintptr_t handle_flags_t; + +struct task; +struct object; +struct handle_list; + +struct handle { + struct object *h_object; + handle_flags_t h_flags; +}; + +struct handle_table { + union { + struct { + /* bitmap tracks which bits in t_handle_list are + * allocated */ + DECLARE_BITMAP(t_handle_map, HANDLES_PER_TABLE); + struct handle t_handle_list[HANDLES_PER_TABLE]; + } t_handles; + + struct { + /* bitmap tracks which sub-tables are fully-allocated */ + DECLARE_BITMAP(t_subtable_map, REFS_PER_TABLE); + struct handle_table *t_subtable_list[REFS_PER_TABLE]; + } t_subtables; + }; +}; + +extern struct handle_table *handle_table_create(void); +extern void handle_table_destroy(struct handle_table *tab); + +extern kern_status_t handle_table_alloc_handle( + struct handle_table *tab, + struct handle **out_slot, + kern_handle_t *out_handle); +extern kern_status_t handle_table_free_handle( + struct handle_table *tab, + kern_handle_t handle); +extern struct handle *handle_table_get_handle( + struct handle_table *tab, + kern_handle_t handle); + +extern kern_status_t handle_list_transfer( + struct handle_table *dest, + struct handle_list *dest_list, + size_t dest_list_count, + struct handle_table *src, + const struct handle_list *src_list, + size_t src_list_count); + +#endif diff --git a/include/kernel/init.h b/include/kernel/init.h new file mode 100644 index 0000000..23ec32a --- /dev/null +++ b/include/kernel/init.h @@ -0,0 +1,41 @@ +#ifndef KERNEL_INIT_H_ +#define KERNEL_INIT_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef int (*initcall_t)(void); + +#define INITLEVEL_EARLY 0 +#define INITLEVEL_CORE 1 +#define INITLEVEL_POSTCORE 2 +#define INITLEVEL_ARCH 3 +#define INITLEVEL_SUBSYS 4 +#define INITLEVEL_ROOTFS 5 +#define INITLEVEL_DEVICE 6 +#define INITLEVEL_LATE 7 +#define INITLEVEL_TESTS 8 + +#define early_initcall(fn) __define_initcall(fn, INITLEVEL_EARLY) +#define core_initcall(fn) __define_initcall(fn, INITLEVEL_CORE) +#define postcore_initcall(fn) __define_initcall(fn, INITLEVEL_POSTCORE) +#define arch_initcall(fn) __define_initcall(fn, INITLEVEL_ARCH) +#define subsys_initcall(fn) __define_initcall(fn, INITLEVEL_SUBSYS) +#define rootfs_initcall(fn) __define_initcall(fn, INITLEVEL_ROOTFS) +#define device_initcall(fn) __define_initcall(fn, INITLEVEL_DEVICE) +#define late_initcall(fn) __define_initcall(fn, INITLEVEL_LATE) +#define test_initcall(fn) __define_initcall(fn, INITLEVEL_TESTS) + +extern void print_kernel_banner(void); +extern int do_initcalls(void); +extern int start_initlevel(int level); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/input.h b/include/kernel/input.h new file mode 100644 index 0000000..fb41be3 --- /dev/null +++ b/include/kernel/input.h @@ -0,0 +1,184 @@ +#ifndef KERNEL_INPUT_H_ +#define KERNEL_INPUT_H_ + +#include +#include +#include + +enum input_event_hook_flags { + INPUT_HOOK_SQUASH_EVENT = 0x01u, +}; + +struct device; + +enum input_event_type { + INPUT_TYPE_UNKNOWN = 0x00u, + INPUT_TYPE_KEY = 0x01u, + INPUT_TYPE_MOTION = 0x02u, +}; + +enum input_event_motion_type { + INPUT_MOTION_TYPE_MOUSE = 0x01u, + INPUT_MOTION_TYPE_SCROLL = 0x02u, +}; + +enum input_keycode { + KEY_UNKNOWN = 0x00u, + KEY_A = 0x01u, + KEY_B = 0x02u, + KEY_C = 0x03u, + KEY_D = 0x04u, + KEY_E = 0x05u, + KEY_F = 0x06u, + KEY_G = 0x07u, + KEY_H = 0x08u, + KEY_I = 0x09u, + KEY_J = 0x0Au, + KEY_K = 0x0Bu, + KEY_L = 0x0Cu, + KEY_M = 0x0Du, + KEY_N = 0x0Eu, + KEY_O = 0x0Fu, + KEY_P = 0x10u, + KEY_Q = 0x11u, + KEY_R = 0x12u, + KEY_S = 0x13u, + KEY_T = 0x14u, + KEY_U = 0x15u, + KEY_V = 0x16u, + KEY_W = 0x17u, + KEY_X = 0x18u, + KEY_Y = 0x19u, + KEY_Z = 0x1Au, + KEY_KEY_1 = 0x1Bu, + KEY_KEY_2 = 0x1Cu, + KEY_KEY_3 = 0x1Du, + KEY_KEY_4 = 0x1Eu, + KEY_KEY_5 = 0x1Fu, + KEY_KEY_6 = 0x20u, + KEY_KEY_7 = 0x21u, + KEY_KEY_8 = 0x22u, + KEY_KEY_9 = 0x23u, + KEY_KEY_0 = 0x24u, + KEY_ENTER = 0x25u, + KEY_ESCAPE = 0x26u, + KEY_BACKSPACE = 0x27u, + KEY_TAB = 0x28u, + KEY_SPACE = 0x29u, + KEY_MINUS = 0x2Au, + KEY_EQUALS = 0x2Bu, + KEY_LEFT_BRACE = 0x2Cu, + KEY_RIGHT_BRACE = 0x2Du, + KEY_BACKSLASH = 0x2Eu, + KEY_NON_US_HASH = 0x2Fu, + KEY_SEMICOLON = 0x30u, + KEY_APOSTROPHE = 0x31u, + KEY_GRAVE_ACCENT = 0x32u, + KEY_COMMA = 0x33u, + KEY_DOT = 0x34u, + KEY_SLASH = 0x35u, + KEY_CAPS_LOCK = 0x36u, + KEY_F1 = 0x37u, + KEY_F2 = 0x38u, + KEY_F3 = 0x39u, + KEY_F4 = 0x3Au, + KEY_F5 = 0x3Bu, + KEY_F6 = 0x3Cu, + KEY_F7 = 0x3Du, + KEY_F8 = 0x3Eu, + KEY_F9 = 0x3Fu, + KEY_F10 = 0x40u, + KEY_F11 = 0x41u, + KEY_F12 = 0x42u, + KEY_PRINT_SCREEN = 0x43u, + KEY_SCROLL_LOCK = 0x44u, + KEY_PAUSE = 0x45u, + KEY_INSERT = 0x46u, + KEY_HOME = 0x47u, + KEY_PAGE_UP = 0x48u, + KEY_DELETE = 0x49u, + KEY_END = 0x4Au, + KEY_PAGE_DOWN = 0x4Bu, + KEY_RIGHT = 0x4Cu, + KEY_LEFT = 0x4Du, + KEY_DOWN = 0x4Eu, + KEY_UP = 0x4Fu, + KEY_NUM_LOCK = 0x50u, + KEY_KEYPAD_SLASH = 0x51u, + KEY_KEYPAD_ASTERISK = 0x52u, + KEY_KEYPAD_MINUS = 0x53u, + KEY_KEYPAD_PLUS = 0x54u, + KEY_KEYPAD_ENTER = 0x55u, + KEY_KEYPAD_1 = 0x56u, + KEY_KEYPAD_2 = 0x57u, + KEY_KEYPAD_3 = 0x58u, + KEY_KEYPAD_4 = 0x59u, + KEY_KEYPAD_5 = 0x5Au, + KEY_KEYPAD_6 = 0x5Bu, + KEY_KEYPAD_7 = 0x5Cu, + KEY_KEYPAD_8 = 0x5Du, + KEY_KEYPAD_9 = 0x5Eu, + KEY_KEYPAD_0 = 0x5Fu, + KEY_KEYPAD_DOT = 0x60u, + KEY_NON_US_BACKSLASH = 0x61u, + KEY_KEYPAD_EQUALS = 0x62u, + KEY_MENU = 0x63u, + KEY_LEFT_CTRL = 0x64u, + KEY_LEFT_SHIFT = 0x65u, + KEY_LEFT_ALT = 0x66u, + KEY_LEFT_META = 0x67u, + KEY_RIGHT_CTRL = 0x68u, + KEY_RIGHT_SHIFT = 0x69u, + KEY_RIGHT_ALT = 0x6Au, + KEY_RIGHT_META = 0x6Bu, + KEY_MEDIA_MUTE = 0x6Cu, + KEY_MEDIA_VOLUME_INCREMENT = 0x6Du, + KEY_MEDIA_VOLUME_DECREMENT = 0x6Eu, +}; + +enum input_key_state { + INPUT_KEYSTATE_DOWN = 0x00u, + INPUT_KEYSTATE_UP = 0x01u, +}; + +enum input_button { + INPUT_BUTTON_MOUSE_LEFT = 0x00u, + INPUT_BUTTON_MOUSE_MIDDLE = 0x01u, + INPUT_BUTTON_MOUSE_RIGHT = 0x02u, + INPUT_BUTTON_MOUSE_BACK = 0x03u, + INPUT_BUTTON_MOUSE_FORWARD = 0x04u, +}; + +enum input_button_state { + INPUT_BUTTON_DOWN = 0x00u, + INPUT_BUTTON_UP = 0x01u, +}; + +struct input_event { + enum input_event_type ev_type; + union { + struct { + enum input_event_motion_type type; + int16_t movement_x; + int16_t movement_y; + } ev_motion; + + struct { + enum input_button button; + enum input_button_state state; + } ev_button; + + struct { + enum input_keycode key; + enum input_key_state state; + } ev_key; + }; +}; + +struct input_event_hook { + void(*hook_callback)(struct device *, struct input_event *, enum input_event_hook_flags *, void *); + void *hook_arg; + struct queue_entry hook_head; +}; + +#endif diff --git a/include/kernel/iovec.h b/include/kernel/iovec.h new file mode 100644 index 0000000..f6c9649 --- /dev/null +++ b/include/kernel/iovec.h @@ -0,0 +1,23 @@ +#ifndef KERNEL_IOVEC_H_ +#define KERNEL_IOVEC_H_ + +#include +#include + +struct iovec_iterator { + const struct iovec *it_vecs; + size_t it_nr_vecs; + size_t it_vec_ptr; + + virt_addr_t it_base; + size_t it_len; +}; + +extern void iovec_iterator_begin( + struct iovec_iterator *it, + const struct iovec *vecs, + size_t nr_vecs); + +extern void iovec_iterator_seek(struct iovec_iterator *it, size_t nr_bytes); + +#endif diff --git a/include/kernel/locks.h b/include/kernel/locks.h new file mode 100644 index 0000000..8ff7098 --- /dev/null +++ b/include/kernel/locks.h @@ -0,0 +1,25 @@ +#ifndef KERNEL_LOCKS_H_ +#define KERNEL_LOCKS_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef __aligned(8) ml_hwlock_t spin_lock_t; + +#define SPIN_LOCK_INIT ML_HWLOCK_INIT + +#define spin_lock(lck) ml_hwlock_lock(lck); +#define spin_unlock(lck) ml_hwlock_unlock(lck); + +#define spin_lock_irqsave(lck, flags) ml_hwlock_lock_irqsave(lck, flags); +#define spin_unlock_irqrestore(lck, flags) ml_hwlock_unlock_irqrestore(lck, flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/memblock.h b/include/kernel/memblock.h new file mode 100644 index 0000000..08f9597 --- /dev/null +++ b/include/kernel/memblock.h @@ -0,0 +1,345 @@ +/* + The Clear BSD License + + Copyright (c) 2023 Max Wash + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted (subject to the limitations in the disclaimer + below) provided that the following conditions are met: + + - Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + - Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from this + software without specific prior written permission. + */ +#ifndef KERNEL_MEMBLOCK_H_ +#define KERNEL_MEMBLOCK_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MEMBLOCK_INIT_MEMORY_REGION_COUNT 128 +#define MEMBLOCK_INIT_RESERVED_REGION_COUNT 128 + +#define __for_each_mem_range(i, type_a, type_b, p_start, p_end) \ + for ((i)->__idx = 0, \ + __next_memory_region(i, type_a, type_b, p_start, p_end); \ + (i)->__idx != ULLONG_MAX; \ + __next_memory_region(i, type_a, type_b, p_start, p_end)) + +/* iterate through all memory regions known to memblock. + + this consists of all regions that have been registered + with memblock using memblock_add(). + + this iteration can be optionally constrained to a given region. + + @param i the iterator. this should be a pointer of type struct memblock_iter. + for each iteration, this structure will be filled with details about + the current memory region. + @param p_start the lower bound of the memory region to iterate through. + if you don't want to use a lower bound, pass 0. + @param p_end the upper bound of the memory region to iterate through. + if you don't want to use an upper bound, pass UINTPTR_MAX. + + EXAMPLE: to iterate through all memory regions (with no bounds): + + struct memblock_iter it; + for_each_mem_region (&it, 0x0, UINTPTR_MAX) { ... } + + + EXAMPLE: to iterate through all memory regions between physical + addresses 0x40000 and 0x80000: + + struct memblock_iter it; + for_each_mem_region (&it, 0x40000, 0x80000) { ... } +*/ +#define for_each_mem_range(i, p_start, p_end) \ + __for_each_mem_range(i, &memblock.memory, NULL, p_start, p_end) + +/* iterate through all memory regions reserved using memblock. + + this consists of all regions that have been registered + with memblock using memblock_reserve(). + + this iteration can be optionally constrained to a given region. + + @param i the iterator. this should be a pointer of type struct memblock_iter. + for each iteration, this structure will be filled with details about + the current memory region. + @param p_start the lower bound of the memory region to iterate through. + if you don't want to use a lower bound, pass 0. + @param p_end the upper bound of the memory region to iterate through. + if you don't want to use an upper bound, pass UINTPTR_MAX. + + EXAMPLE: to iterate through all reserved memory regions (with no bounds): + + struct memblock_iter it; + for_each_reserved_mem_region (&it, 0x0, UINTPTR_MAX) { ... } + + + EXAMPLE: to iterate through all reserved memory regions between physical + addresses 0x40000 and 0x80000: + + struct memblock_iter it; + for_each_reserved_mem_region (&it, 0x40000, 0x80000) { ... } +*/ +#define for_each_reserved_mem_range(i, p_start, p_end) \ + __for_each_mem_range(i, &memblock.reserved, NULL, p_start, p_end) + +/* iterate through all memory regions known by memblock to be free. + + this consists of all regions BETWEEN those regions that have been + registered using memblock_reserve(), bounded within the memory + regions added using memblock_add(). + + this iteration can be optionally constrained to a given region. + + @param i the iterator. this should be a pointer of type struct memblock_iter. + for each iteration, this structure will be filled with details about + the current memory region. + @param p_start the lower bound of the memory region to iterate through. + if you don't want to use a lower bound, pass 0. + @param p_end the upper bound of the memory region to iterate through. + if you don't want to use an upper bound, pass UINTPTR_MAX. + + EXAMPLE: if you have added the following memory regions to + memblock using memblock_add(): + + - 0x00000 -> 0x05fff + - 0x08000 -> 0x1ffff + + ...and you have reserved the following memory regions using + memblock_reserve(): + + - 0x01000 -> 0x04fff + - 0x09000 -> 0x0ffff + + the following call: + + struct memblock_iter it; + for_each_free_mem_range (&it, 0x0, UINTPTR_MAX) { ... } + + would iterate through the following sequence of free memory ranges: + + - 0x00000 -> 0x00fff + - 0x05000 -> 0x05fff + - 0x08000 -> 0x08fff + - 0x10000 -> 0x1ffff +*/ +#define for_each_free_mem_range(i, p_start, p_end) \ + __for_each_mem_range( \ + i, \ + &memblock.memory, \ + &memblock.reserved, \ + p_start, \ + p_end) + +typedef uint64_t memblock_index_t; + +enum memblock_region_status { + /* Used in memblock.memory regions, indicates that the memory region + * exists */ + MEMBLOCK_MEMORY = 0, + /* Used in memblock.reserved regions, indicates that the memory region + * was reserved by a call to memblock_alloc() */ + MEMBLOCK_ALLOC, + /* Used in memblock.reserved regions, indicates that the memory region + * was reserved by a call to memblock_reserve() */ + MEMBLOCK_RESERVED, +}; + +struct memblock_region { + /* the status of the memory region (free, reserved, allocated, etc) */ + enum memblock_region_status status; + /* the address of the first byte that makes up the region */ + phys_addr_t base; + /* the address of the last byte that makes up the region */ + phys_addr_t limit; +}; + +/* buffer of memblock regions, all of which are the same type + (memory, reserved, etc) */ +struct memblock_type { + struct memblock_region *regions; + unsigned int count; + unsigned int max; + const char *name; +}; + +struct memblock { + /* bounds of the memory region that can be used by memblock_alloc() + both of these are virtual addresses */ + uintptr_t m_alloc_start, m_alloc_end; + /* memblock assumes that all memory in the alloc zone is contiguously + mapped (if paging is enabled). m_voffset is the offset that needs to + be added to a given physical address to get the corresponding virtual + address */ + uintptr_t m_voffset; + + struct memblock_type memory; + struct memblock_type reserved; +}; + +struct memblock_iter { + memblock_index_t __idx; + phys_addr_t it_base; + phys_addr_t it_limit; + enum memblock_region_status it_status; +}; + +/* global memblock state. */ +extern struct memblock memblock; + +extern int __next_mem_range(struct memblock_iter *it); + +/* initialise the global memblock state. + this function must be called before any other memblock functions can be used. + + this function sets the bounds of the heap area. memory allocation requests + using memblock_alloc() will be constrained to this zone. + + memblock assumes that all physical memory in the system is mapped to + an area in virtual memory, such that converting a physical address to + a valid virtual address can be done by simply applying an offset. + + @param alloc_start the virtual address of the start of the heap area. + @param alloc_end the virtual address of the end of the heap area. + @param voffset the offset between the physical address of a given page and + its corresponding virtual address. +*/ +extern int memblock_init( + uintptr_t alloc_start, + uintptr_t alloc_end, + uintptr_t voffset); + +/* add a region of memory to memblock. + + this function is used to define regions of memory that are accessible, but + says nothing about the STATE of the given memory. + + all memory is free by default. once a region of memory is added, + memblock_reserve() can be used to mark the memory as reserved. + + @param base the physical address of the start of the memory region to add. + @oaram size the size of the memory region to add in bytes. +*/ +extern int memblock_add(phys_addr_t base, size_t size); +/* mark a region of memory as reserved. + + this function can only operate on regions of memory that have been previously + registered with memblock using memblock_add(). + + reserved memory will not be used by memblock_alloc(), and will remain + reserved when the vm_page memory map is initialised. + + @param base the physical address of the start of the memory region to + reserve. + @oaram size the size of the memory region to reserve in bytes. +*/ +extern int memblock_reserve(phys_addr_t base, size_t size); + +/* allocate a block of memory, returning a virtual address. + + this function selects the first available region of memory that satisfies + the requested allocation size, marks `size` bytes of this region as reserved, + and returns the virtual address of the region. + + when looking for a suitable region of memory, this function searches the + intersection of the following memory zones: + - the regions of memory added with memblock_alloc(). + - the region of memory specified as the heap bounds during the call + to memblock_init(). + and excludes the following regions: + - the regions of memory marked as reserved by memblock_reserve() and + previous calls to memblock_alloc() + + @param size the size of the buffer to allocate in bytes. + @param align the alignment to use. for example, an alignment of 4096 + will result in the returned pointer being a multiple + of 4096. this must be a power of 2. +*/ +extern void *memblock_alloc(size_t size, phys_addr_t align); + +/* allocate a block of memory, returning a physical address. + + this function selects the first available region of memory that satisfies + the requested allocation size, marks `size` bytes of this region as reserved, + and returns the virtual address of the region. + + when looking for a suitable region of memory, this function searches the + intersection of the following memory zones: + - the regions of memory added with memblock_alloc(). + - the region of memory specified as the heap bounds during the call + to memblock_init(). + and excludes the following regions: + - the regions of memory marked as reserved by memblock_reserve() and + previous calls to memblock_alloc() + + @param size the size of the buffer to allocate in bytes. + @param align the alignment to use. for example, an alignment of 4096 + will result in the returned pointer being a multiple + of 4096. this must be a power of 2. +*/ +extern phys_addr_t memblock_alloc_phys(size_t size, phys_addr_t align); + +/* free a block of memory using its virtual address. + + due to limitations in memblock (as it is meant to be a simple, + early-boot allocator), you must specify the size of the memory + region you intend to free. + + @param addr the virtual address of the region to free. + @param size the size of the region to free in bytes. +*/ +extern int memblock_free(void *addr, size_t size); + +/* free a block of memory using its physical address. + + due to limitations in memblock (as it is meant to be a simple, + early-boot allocator), you must specify the size of the memory + region you intend to free. + + @param addr the physical address of the region to free. + @param size the size of the region to free in bytes. +*/ +extern int memblock_free_phys(phys_addr_t addr, size_t size); + +/* convert a virtual pointer returned by memblock + to a physical address. + + @param p the pointer to convert. +*/ +extern phys_addr_t memblock_virt_to_phys(const void *p); + +/* convert a physical address returned by memblock + to a virtual pointer. + + @param p the address to convert. +*/ +extern void *memblock_phys_to_virt(phys_addr_t p); + +extern void __next_memory_region( + struct memblock_iter *it, + struct memblock_type *type_a, + struct memblock_type *type_b, + phys_addr_t start, + phys_addr_t end); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/msg.h b/include/kernel/msg.h new file mode 100644 index 0000000..910a997 --- /dev/null +++ b/include/kernel/msg.h @@ -0,0 +1,30 @@ +#ifndef KERNEL_MSG_H_ +#define KERNEL_MSG_H_ + +#include +#include +#include +#include + +struct port; +struct thread; + +enum kmsg_status { + KMSG_WAIT_RECEIVE, + KMSG_WAIT_REPLY, + KMSG_REPLY_SENT, +}; + +struct kmsg { + spin_lock_t msg_lock; + enum kmsg_status msg_status; + struct btree_node msg_node; + msgid_t msg_id; + kern_status_t msg_result; + struct port *msg_sender_port; + struct thread *msg_sender_thread; + const struct msg *msg_req; + struct msg *msg_resp; +}; + +#endif diff --git a/include/kernel/object.h b/include/kernel/object.h new file mode 100644 index 0000000..4f053de --- /dev/null +++ b/include/kernel/object.h @@ -0,0 +1,95 @@ +#ifndef KERNEL_OBJECT_H_ +#define KERNEL_OBJECT_H_ + +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEFINE_OBJECT_LOCK_FUNCTION(object_name, base) \ + static inline void object_name##_lock(struct object_name *p) \ + { \ + object_lock(&p->base); \ + } \ + static inline void object_name##_unlock(struct object_name *p) \ + { \ + object_unlock(&p->base); \ + } \ + static inline void object_name##_lock_irqsave( \ + struct object_name *p, \ + unsigned long *flags) \ + { \ + object_lock_irqsave(&p->base, flags); \ + } \ + static inline void object_name##_unlock_irqrestore( \ + struct object_name *p, \ + unsigned long flags) \ + { \ + object_unlock_irqrestore(&p->base, flags); \ + } + +#define OBJECT_MAGIC 0xBADDCAFE +#define OBJECT_NAME_MAX 64 +#define OBJECT_PATH_MAX 256 + +#define OBJECT_CAST(to_type, to_type_member, p) \ + ((to_type *)((uintptr_t)p) - offsetof(to_type, to_type_member)) +#define OBJECT_C_CAST(c_type, c_type_member, obj_type, objp) \ + OBJECT_IS_TYPE(objp, obj_type) \ + ? OBJECT_CAST(c_type, c_type_member, (objp)) : NULL +#define OBJECT_IS_TYPE(obj, type_ptr) ((obj)->ob_type == (type_ptr)) + +struct object; +struct object_attrib; + +enum object_type_flags { + OBJTYPE_INIT = 0x01u, +}; + +struct object_ops { + kern_status_t (*destroy)(struct object *obj); +}; + +struct object_type { + enum object_type_flags ob_flags; + char ob_name[32]; + unsigned int ob_size; + unsigned int ob_header_offset; + struct vm_cache ob_cache; + struct queue_entry ob_list; + struct object_ops ob_ops; +}; + +struct object { + uint32_t ob_magic; + struct object_type *ob_type; + spin_lock_t ob_lock; + unsigned int ob_refcount; + unsigned int ob_handles; + struct queue_entry ob_list; +} __aligned(sizeof(long)); + +extern kern_status_t object_bootstrap(void); +extern kern_status_t object_type_register(struct object_type *p); +extern kern_status_t object_type_unregister(struct object_type *p); + +extern struct object *object_create(struct object_type *type); +extern struct object *object_ref(struct object *obj); +extern void object_unref(struct object *obj); +extern void object_add_handle(struct object *obj); +extern void object_remove_handle(struct object *obj); +extern void object_lock(struct object *obj); +extern void object_unlock(struct object *obj); +extern void object_lock_irqsave(struct object *obj, unsigned long *flags); +extern void object_unlock_irqrestore(struct object *obj, unsigned long flags); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/panic.h b/include/kernel/panic.h new file mode 100644 index 0000000..fba0325 --- /dev/null +++ b/include/kernel/panic.h @@ -0,0 +1,13 @@ +#ifndef KERNEL_PANIC_H_ +#define KERNEL_PANIC_H_ + +#include + +struct ml_cpu_context; + +#define panic(...) panic_irq(NULL, __VA_ARGS__) + +extern void __noreturn +panic_irq(struct ml_cpu_context *ctx, const char *fmt, ...); + +#endif diff --git a/include/kernel/percpu.h b/include/kernel/percpu.h new file mode 100644 index 0000000..afff2f2 --- /dev/null +++ b/include/kernel/percpu.h @@ -0,0 +1,36 @@ +#ifndef KERNEL_PERCPU_H_ +#define KERNEL_PERCPU_H_ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define DEFINE_PERCPU_VAR(type, name) \ + __section(".data.percpu") type name + +#define percpu_get(var) \ + __extension__({ \ + preempt_disable(); \ + __percpu_get(this_cpu(), var); \ + }) + +#define percpu_get_from(cpu, var) \ + __extension__({ \ + preempt_disable(); \ + __percpu_get(cpu, var); \ + }) + +#define percpu_put(var) preempt_enable(); + +extern kern_status_t init_per_cpu_areas(void); +extern void *__percpu_get(unsigned int cpu, void *var); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/pmap.h b/include/kernel/pmap.h new file mode 100644 index 0000000..77e3079 --- /dev/null +++ b/include/kernel/pmap.h @@ -0,0 +1,81 @@ +#ifndef KERNEL_PMAP_H_ +#define KERNEL_PMAP_H_ + +/* all the functions declared in this file are defined in arch/xyz/pmap.c */ + +#include +#include +#include +#include + +#define PMAP_INVALID ML_PMAP_INVALID +#define PFN(x) ((x) >> VM_PAGE_SHIFT) + +#ifdef __cplusplus +extern "C" { +#endif + +typedef ml_pmap_t pmap_t; +typedef ml_pfn_t pfn_t; + +enum pmap_fault_flags { + /* if set, the faulting page is present, and the fault is + * protection-related. + * if clear, the faulting page is missing, and the + * fault is due to the missing page. + */ + PMAP_FAULT_PRESENT = 0x01u, + /* if set, the faulting page was accessed from user mode. + * if clear, the faulting page was accessed from kernel mode. + */ + PMAP_FAULT_USER = 0x02u, + /* if set, the fault was caused by a write operation. + * if clear, the faulting page was caused by a read operation. + */ + PMAP_FAULT_WRITE = 0x04u, + /* if set, the fault was caused while fetching an instruction from the + * faulting page. + */ + PMAP_FAULT_IFETCH = 0x08u, + /* if set, the fault was caused by misconfigured page tables */ + PMAP_FAULT_BADCFG = 0x10u, +}; + +enum pmap_flags { + PMAP_NORMAL = 0x00u, + PMAP_HUGEPAGE = 0x01u, +}; + +extern void pmap_bootstrap(void); +extern pmap_t get_kernel_pmap(void); + +extern pmap_t pmap_create(void); +extern void pmap_destroy(pmap_t pmap); +extern void pmap_switch(pmap_t pmap); + +extern kern_status_t pmap_handle_fault( + virt_addr_t fault_addr, + enum pmap_fault_flags flags); + +extern kern_status_t pmap_add( + pmap_t pmap, + virt_addr_t p, + pfn_t pfn, + vm_prot_t prot, + enum pmap_flags flags); +extern kern_status_t pmap_add_block( + pmap_t pmap, + virt_addr_t p, + pfn_t pfn, + size_t len, + vm_prot_t prot, + enum pmap_flags flags); + +extern kern_status_t pmap_remove(pmap_t pmap, virt_addr_t p); +extern kern_status_t pmap_remove_range(pmap_t pmap, virt_addr_t p, size_t len); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/port.h b/include/kernel/port.h new file mode 100644 index 0000000..a167afa --- /dev/null +++ b/include/kernel/port.h @@ -0,0 +1,39 @@ +#ifndef KERNEL_PORT_H_ +#define KERNEL_PORT_H_ + +#include +#include + +enum port_status { + /* port is not connected */ + PORT_OFFLINE = 0, + /* port is connected and ready to send messages */ + PORT_READY, + /* port has sent a message, and is waiting for the remote to receive it + */ + PORT_SEND_BLOCKED, + /* port has sent a message, and the remote has received it. waiting for + * the remote to reply. */ + PORT_REPLY_BLOCKED, +}; + +struct port { + struct object p_base; + enum port_status p_status; + struct channel *p_remote; +}; + +extern kern_status_t port_type_init(void); +extern struct port *port_cast(struct object *obj); + +extern struct port *port_create(void); + +extern kern_status_t port_connect(struct port *port, struct channel *remote); +extern kern_status_t port_send_msg( + struct port *port, + const struct msg *req, + struct msg *resp); + +DEFINE_OBJECT_LOCK_FUNCTION(port, p_base) + +#endif diff --git a/include/kernel/printk.h b/include/kernel/printk.h new file mode 100644 index 0000000..904c39a --- /dev/null +++ b/include/kernel/printk.h @@ -0,0 +1,25 @@ +#ifndef KERNEL_PRINTK_H_ +#define KERNEL_PRINTK_H_ + +#include + +#undef TRACE + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef TRACE +#define tracek(...) printk(__VA_ARGS__) +#else +#define tracek(...) +#endif + +extern void early_printk_init(struct console *con); +extern int printk(const char *format, ...); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/queue.h b/include/kernel/queue.h new file mode 100644 index 0000000..3097793 --- /dev/null +++ b/include/kernel/queue.h @@ -0,0 +1,62 @@ +#ifndef KERNEL_QUEUE_H_ +#define KERNEL_QUEUE_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define QUEUE_CONTAINER(t, m, v) ((void *)((v) ? (uintptr_t)(v) - (offsetof(t, m)) : 0)) + +#define QUEUE_INIT ((struct queue){ .q_first = NULL, .q_last = NULL }) +#define QUEUE_ENTRY_INIT ((struct queue_entry){ .qe_next = NULL, .qe_prev = NULL }) + +#define queue_foreach(iter_type, iter_name, queue_name, node_member) \ + for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER(iter_type, node_member, queue_first(queue_name)); \ + iter_name; \ + iter_name = (iter_type *)QUEUE_CONTAINER(iter_type, node_member, queue_next(&((iter_name)->node_member)))) + +#define queue_foreach_r(iter_type, iter_name, queue_name, node_member) \ + for (iter_type *iter_name = (iter_type *)QUEUE_CONTAINER(iter_type, node_member, queue_last(queue_name)); \ + iter_name; \ + iter_name = (iter_type *)QUEUE_CONTAINER(iter_type, node_member, queue_prev(&((iter_name)->node_member)))) + +struct queue_entry { + struct queue_entry *qe_next; + struct queue_entry *qe_prev; +}; + +struct queue { + struct queue_entry *q_first; + struct queue_entry *q_last; +}; + +static inline void queue_init(struct queue *q) { memset(q, 0x00, sizeof *q); } +static inline bool queue_empty(struct queue *q) { return q->q_first == NULL; } + +static inline struct queue_entry *queue_first(struct queue *q) { return q->q_first; } +static inline struct queue_entry *queue_last(struct queue *q) { return q->q_last; } +static inline struct queue_entry *queue_next(struct queue_entry *entry) { return entry->qe_next; } +static inline struct queue_entry *queue_prev(struct queue_entry *entry) { return entry->qe_prev; } + +extern size_t queue_length(struct queue *q); + +extern void queue_insert_before(struct queue *q, struct queue_entry *entry, struct queue_entry *before); +extern void queue_insert_after(struct queue *q, struct queue_entry *entry, struct queue_entry *after); + +extern void queue_push_front(struct queue *q, struct queue_entry *entry); +extern void queue_push_back(struct queue *q, struct queue_entry *entry); + +extern struct queue_entry *queue_pop_front(struct queue *q); +extern struct queue_entry *queue_pop_back(struct queue *q); + +extern void queue_delete(struct queue *q, struct queue_entry *entry); +extern void queue_delete_all(struct queue *q); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/ringbuffer.h b/include/kernel/ringbuffer.h new file mode 100644 index 0000000..9c362c0 --- /dev/null +++ b/include/kernel/ringbuffer.h @@ -0,0 +1,34 @@ +#ifndef KERNEL_RINGBUFFER_H_ +#define KERNEL_RINGBUFFER_H_ + +#include +#include + +struct ringbuffer { + unsigned char *r_buffer; + size_t r_write_ptr; + size_t r_read_ptr; + size_t r_size; + spin_lock_t r_lock; + struct waitqueue r_wait_readers; + struct waitqueue r_wait_writers; +}; + +extern struct ringbuffer *ringbuffer_create(size_t size); +extern void ringbuffer_destroy(struct ringbuffer *buf); + +extern kern_status_t ringbuffer_init(struct ringbuffer *buf, size_t size); +extern kern_status_t ringbuffer_deinit(struct ringbuffer *buf); + +extern size_t ringbuffer_unread(struct ringbuffer *buf); +extern size_t ringbuffer_avail(struct ringbuffer *buf); +extern size_t ringbuffer_read(struct ringbuffer *buf, size_t size, void *buffer, mango_flags_t flags); +extern size_t ringbuffer_write(struct ringbuffer *buf, size_t size, const void *buffer, mango_flags_t flags); + +/* TODO */ +//extern size_t ringbuffer_peek(struct ringbuffer *buf, size_t at, size_t size, void *buffer); +//extern size_t ringbuffer_skip(struct ringbuffer *buf, size_t count); + +extern int ringbuffer_write_would_block(struct ringbuffer *buf); + +#endif diff --git a/include/kernel/sched.h b/include/kernel/sched.h new file mode 100644 index 0000000..f2f78ac --- /dev/null +++ b/include/kernel/sched.h @@ -0,0 +1,277 @@ +#ifndef KERNEL_SCHED_H_ +#define KERNEL_SCHED_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define TASK_NAME_MAX 64 +#define PRIO_MAX 32 +#define PID_MAX 99999 +#define THREAD_KSTACK_ORDER VM_PAGE_4K +#define THREAD_MAX 65536 + +#define wait_event(wq, cond) \ + ({ \ + struct thread *self = current_thread(); \ + struct wait_item waiter; \ + wait_item_init(&waiter, self); \ + for (;;) { \ + thread_wait_begin(&waiter, wq); \ + if (cond) { \ + break; \ + } \ + schedule(SCHED_NORMAL); \ + } \ + thread_wait_end(&waiter, wq); \ + }) + +#ifdef __cplusplus +extern "C" { +#endif + +struct channel; +struct runqueue; +struct work_item; + +enum task_state { + TASK_RUNNING, + TASK_STOPPED, +}; + +enum thread_state { + THREAD_READY = 1, + THREAD_SLEEPING = 2, + THREAD_STOPPED = 3, +}; + +enum thread_flags { + THREAD_F_NEED_RESCHED = 0x01u, + THREAD_F_NO_PREEMPT = 0x02u, +}; + +enum sched_priority { + PRIO_IDLE = 4, + PRIO_SUBNORMAL = 6, + PRIO_NORMAL = 10, + PRIO_SUPERNORMAL = 14, + PRIO_HIGH = 18, + PRIO_REALTIME = 24, +}; + +enum sched_mode { + /* used when calling from non-interrupt context. + threads that aren't in state THREAD_READY are + removed from the runqueue. */ + SCHED_NORMAL = 0, + /* used when calling from interrupt context. + threads that aren't in state THREAD_READY are + still added to the runqueue. */ + SCHED_IRQ = 1, +}; + +struct task { + struct object t_base; + + struct task *t_parent; + long t_id; + enum task_state t_state; + char t_name[TASK_NAME_MAX]; + + pmap_t t_pmap; + struct vm_region *t_address_space; + struct handle_table *t_handles; + struct btree b_channels; + + struct btree_node t_tasklist; + struct queue_entry t_child_entry; + + size_t t_next_thread_id; + struct queue t_threads; + struct queue t_children; +}; + +struct thread { + struct object thr_base; + + enum thread_state tr_state; + enum thread_flags tr_flags; + struct task *tr_parent; + + unsigned int tr_id; + unsigned int tr_prio; + + cycles_t tr_charge_period_start; + cycles_t tr_quantum_cycles, tr_quantum_target; + cycles_t tr_total_cycles; + + virt_addr_t tr_ip, tr_sp, tr_bp; + virt_addr_t tr_cpu_user_sp, tr_cpu_kernel_sp; + + struct runqueue *tr_rq; + struct kmsg tr_msg; + + struct queue_entry tr_parent_entry; + struct queue_entry tr_rqentry; + + struct vm_page *tr_kstack; + struct vm_object *tr_ustack; +}; + +struct runqueue { + struct queue rq_queues[PRIO_MAX]; + uint32_t rq_readybits; + spin_lock_t rq_lock; + unsigned int rq_nthreads; + + struct thread *rq_cur, *rq_idle; +}; + +struct timer { + struct queue_entry t_entry; + struct cpu_data *t_cpu; + struct thread *t_owner; + unsigned long t_expiry; + void (*t_callback)(struct timer *); +}; + +struct wait_item { + struct thread *w_thread; + struct queue_entry w_entry; +}; + +struct waitqueue { + struct queue wq_waiters; + spin_lock_t wq_lock; +}; + +typedef void (*work_func_t)(struct work_item *); + +struct work_item { + void *w_data; + work_func_t w_func; + struct queue_entry w_head; +}; + +struct worker_pool { + struct thread **wp_workers; + size_t wp_nworkers; +}; + +struct workqueue { + spin_lock_t wq_lock; + struct queue wq_queue; /* list of struct work_item */ +}; + +extern kern_status_t sched_init(void); +extern void schedule(enum sched_mode mode); +extern void preempt_disable(void); +extern void preempt_enable(void); + +extern void rq_init(struct runqueue *rq); +extern struct thread *rq_dequeue(struct runqueue *rq); +extern void rq_enqueue(struct runqueue *rq, struct thread *thr); +static inline void rq_lock(struct runqueue *rq, unsigned long *flags) +{ + spin_lock_irqsave(&rq->rq_lock, flags); +} +static inline void rq_unlock(struct runqueue *rq, unsigned long flags) +{ + spin_unlock_irqrestore(&rq->rq_lock, flags); +} +extern void rq_remove_thread(struct runqueue *rq, struct thread *thr); +extern struct runqueue *cpu_rq(unsigned int cpu); + +extern struct task *task_alloc(void); +extern struct task *task_cast(struct object *obj); +extern struct task *task_create(const char *name, size_t name_len); +static inline struct task *task_ref(struct task *task) +{ + return OBJECT_CAST(struct task, t_base, object_ref(&task->t_base)); +} +static inline void task_unref(struct task *task) +{ + object_unref(&task->t_base); +} +extern kern_status_t task_add_child(struct task *parent, struct task *child); +extern kern_status_t task_add_channel( + struct task *task, + struct channel *channel, + unsigned int id); +extern struct channel *task_get_channel(struct task *task, unsigned int id); +extern struct task *task_from_tid(tid_t id); +extern kern_status_t task_open_handle( + struct task *task, + struct object *obj, + handle_flags_t flags, + kern_handle_t *out); +extern kern_status_t task_resolve_handle( + struct task *task, + kern_handle_t handle, + struct object **out_obj, + handle_flags_t *out_flags); +extern kern_status_t task_close_handle(struct task *task, kern_handle_t handle); +extern struct thread *task_create_thread(struct task *parent); +extern struct task *kernel_task(void); +extern struct task *idle_task(void); +extern cycles_t default_quantum(void); + +extern bool need_resched(void); +extern struct task *current_task(void); +extern struct thread *current_thread(void); + +extern struct runqueue *select_rq_for_thread(struct thread *thr); +extern void schedule_thread_on_cpu(struct thread *thr); + +extern void start_charge_period(void); +extern void end_charge_period(void); + +DEFINE_OBJECT_LOCK_FUNCTION(task, t_base) + +extern struct thread *thread_alloc(void); +extern struct thread *thread_cast(struct object *obj); +extern kern_status_t thread_init_kernel(struct thread *thr, virt_addr_t ip); +extern kern_status_t thread_init_user( + struct thread *thr, + virt_addr_t ip, + virt_addr_t sp, + const uintptr_t *args, + size_t nr_args); +extern int thread_priority(struct thread *thr); +extern void thread_awaken(struct thread *thr); +extern void idle(void); +extern struct thread *create_kernel_thread(void (*fn)(void)); +extern struct thread *create_idle_thread(void); + +extern void add_timer(struct timer *timer); +extern void remove_timer(struct timer *timer); +extern unsigned long schedule_timeout(unsigned long clock_ticks); +extern unsigned long milli_sleep(unsigned long ms); +extern void sleep_forever(void); + +extern void wait_item_init(struct wait_item *item, struct thread *thr); +extern void thread_wait_begin(struct wait_item *waiter, struct waitqueue *q); +extern void thread_wait_end(struct wait_item *waiter, struct waitqueue *q); +extern void wait_on_queue(struct waitqueue *q); +extern void wakeup_queue(struct waitqueue *q); +extern void wakeup_one(struct waitqueue *q); + +extern void work_item_init(work_func_t func, void *data, struct work_item *out); +extern void workqueue_init(struct workqueue *wq); +extern struct worker_pool *worker_pool_create(size_t nworkers); +extern struct worker_pool *global_worker_pool(void); +extern bool schedule_work_on(struct workqueue *wq, struct work_item *work); +extern bool schedule_work(struct work_item *work); + +extern void wake_workers(struct workqueue *wq, struct worker_pool *pool); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/syscall.h b/include/kernel/syscall.h new file mode 100644 index 0000000..b7dd0ad --- /dev/null +++ b/include/kernel/syscall.h @@ -0,0 +1,183 @@ +#ifndef KERNEL_SYSCALL_H_ +#define KERNEL_SYSCALL_H_ + +#include +#include +#include +#include + +#define validate_access(task, ptr, len, flags) \ + vm_region_validate_access( \ + task->t_address_space, \ + (virt_addr_t)ptr, \ + len, \ + flags | VM_PROT_USER) +#define validate_access_r(task, ptr, len) \ + validate_access(task, ptr, len, VM_PROT_READ | VM_PROT_USER) +#define validate_access_w(task, ptr, len) \ + validate_access(task, ptr, len, VM_PROT_WRITE | VM_PROT_USER) +#define validate_access_rw(task, ptr, len) \ + validate_access( \ + task, \ + ptr, \ + len, \ + VM_PROT_READ | VM_PROT_WRITE | VM_PROT_USER) + +extern kern_status_t sys_task_exit(int status); +extern kern_status_t sys_task_create( + kern_handle_t parent_handle, + const char *name, + size_t name_len, + kern_handle_t *out_task, + kern_handle_t *out_address_space); +extern kern_status_t sys_task_create_thread( + kern_handle_t task, + virt_addr_t ip, + virt_addr_t sp, + uintptr_t *args, + size_t nr_args, + kern_handle_t *out_thread); + +extern kern_status_t sys_thread_start(kern_handle_t thread); + +extern kern_status_t sys_vm_object_create( + const char *name, + size_t name_len, + size_t data_len, + vm_prot_t prot, + kern_handle_t *out); +extern kern_status_t sys_vm_object_read( + kern_handle_t object, + void *dst, + off_t offset, + size_t count, + size_t *nr_read); +extern kern_status_t sys_vm_object_write( + kern_handle_t object, + const void *src, + off_t offset, + size_t count, + size_t *nr_written); +extern kern_status_t sys_vm_object_copy( + kern_handle_t dst, + off_t dst_offset, + kern_handle_t src, + off_t src_offset, + size_t count, + size_t *nr_copied); + +extern kern_status_t sys_vm_region_create( + kern_handle_t parent, + const char *name, + size_t name_len, + off_t offset, + size_t region_len, + vm_prot_t prot, + kern_handle_t *out, + virt_addr_t *out_base_address); +extern kern_status_t sys_vm_region_read( + kern_handle_t region, + void *dst, + off_t offset, + size_t count, + size_t *nr_read); +extern kern_status_t sys_vm_region_write( + kern_handle_t region, + const void *src, + off_t offset, + size_t count, + size_t *nr_read); +extern kern_status_t sys_vm_region_map_absolute( + kern_handle_t region, + virt_addr_t map_address, + kern_handle_t object, + off_t object_offset, + size_t length, + vm_prot_t prot, + virt_addr_t *out_base_address); +extern kern_status_t sys_vm_region_map_relative( + kern_handle_t region, + off_t region_offset, + kern_handle_t object, + off_t object_offset, + size_t length, + vm_prot_t prot, + virt_addr_t *out_base_address); +extern kern_status_t sys_vm_region_unmap_absolute( + kern_handle_t region, + virt_addr_t address, + size_t length); +extern kern_status_t sys_vm_region_unmap_relative( + kern_handle_t region, + off_t offset, + size_t length); + +extern kern_status_t sys_kern_log(const char *s); +extern kern_status_t sys_kern_handle_close(kern_handle_t handle); +extern kern_status_t sys_kern_config_get( + kern_config_key_t key, + void *ptr, + size_t len); +extern kern_status_t sys_kern_config_set( + kern_config_key_t key, + const void *ptr, + size_t len); + +extern kern_status_t sys_channel_create( + unsigned int id, + channel_flags_t flags, + kern_handle_t *out); +extern kern_status_t sys_port_create(kern_handle_t *out); +extern kern_status_t sys_port_connect( + kern_handle_t port, + tid_t task_id, + unsigned int channel_id); +extern kern_status_t sys_port_disconnect(kern_handle_t port); + +extern kern_status_t sys_msg_send( + kern_handle_t port, + msg_flags_t flags, + const struct msg *req, + struct msg *resp); + +extern kern_status_t sys_msg_recv( + kern_handle_t channel, + msg_flags_t flags, + msgid_t *out_id, + struct msg *out_msg); + +extern kern_status_t sys_msg_reply( + kern_handle_t channel, + msg_flags_t flags, + msgid_t id, + const struct msg *reply); + +extern kern_status_t sys_msg_read( + kern_handle_t channel, + msgid_t id, + size_t offset, + struct iovec *out, + size_t nr_out); +extern kern_status_t sys_msg_read_handles( + kern_handle_t channel, + msgid_t id, + size_t offset, + struct handle_list *out, + size_t nr_out); + +extern kern_status_t sys_msg_write( + kern_handle_t channel, + msgid_t id, + size_t offset, + const struct iovec *in, + size_t nr_in); +extern kern_status_t sys_msg_write_handles( + kern_handle_t channel, + msgid_t id, + size_t offset, + const struct handle_list *in, + size_t nr_in); + +extern virt_addr_t syscall_get_function(unsigned int sysid); + +#endif diff --git a/include/kernel/test.h b/include/kernel/test.h new file mode 100644 index 0000000..3638956 --- /dev/null +++ b/include/kernel/test.h @@ -0,0 +1,14 @@ +#ifndef KERNEL_TEST_H_ +#define KERNEL_TEST_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +extern int run_all_tests(void); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/types.h b/include/kernel/types.h new file mode 100644 index 0000000..f72685a --- /dev/null +++ b/include/kernel/types.h @@ -0,0 +1,18 @@ +#ifndef KERNEL_TYPES_H_ +#define KERNEL_TYPES_H_ + +#include +#include +#include + +#define CYCLES_MAX UINT64_MAX + +typedef uint64_t cycles_t; +typedef uint64_t sectors_t; + +struct boot_module { + phys_addr_t mod_base; + size_t mod_size; +}; + +#endif diff --git a/include/kernel/util.h b/include/kernel/util.h new file mode 100644 index 0000000..4ffed8b --- /dev/null +++ b/include/kernel/util.h @@ -0,0 +1,70 @@ +#ifndef KERNEL_UTIL_H_ +#define KERNEL_UTIL_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) +#define CLAMP(x, lo, hi) (MIN(MAX(x, lo), hi)) + +extern uint64_t hash_string(const char *s); +extern void data_size_to_string(size_t value, char *out, size_t outsz); +static inline bool power_of_2(size_t x) +{ + return (x > 0 && (x & (x - 1)) == 0); +} +static inline unsigned long long div64_pow2( + unsigned long long x, + unsigned long long y) +{ + return x >> (__builtin_ctz(y)); +} + +static inline unsigned long long absdiff64( + unsigned long long x, + unsigned long long y) +{ + return x < y ? y - x : x - y; +} + +extern int16_t host_to_big_i16(int16_t v); +extern int16_t host_to_little_i16(int16_t v); +extern int16_t big_to_host_i16(int16_t v); +extern int16_t little_to_host_i16(int16_t v); +extern uint16_t host_to_big_u16(uint16_t v); +extern uint16_t host_to_little_u16(uint16_t v); +extern uint16_t big_to_host_u16(uint16_t v); +extern uint16_t little_to_host_u16(uint16_t v); + +extern int32_t host_to_big_i32(int32_t v); +extern int32_t host_to_little_i32(int32_t v); +extern int32_t big_to_host_i32(int32_t v); +extern int32_t little_to_host_i32(int32_t v); +extern uint32_t host_to_big_u32(uint32_t v); +extern uint32_t host_to_little_u32(uint32_t v); +extern uint32_t big_to_host_u32(uint32_t v); +extern uint32_t little_to_host_u32(uint32_t v); + +extern int64_t host_to_big_i64(int64_t v); +extern int64_t host_to_little_i64(int64_t v); +extern int64_t big_to_host_i64(int64_t v); +extern int64_t little_to_host_i64(int64_t v); +extern uint64_t host_to_big_u64(uint64_t v); +extern uint64_t host_to_little_u64(uint64_t v); +extern uint64_t big_to_host_u64(uint64_t v); +extern uint64_t little_to_host_u64(uint64_t v); + +extern bool fill_random(void *buffer, unsigned int size); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/include/kernel/vm-object.h b/include/kernel/vm-object.h new file mode 100644 index 0000000..093ebf9 --- /dev/null +++ b/include/kernel/vm-object.h @@ -0,0 +1,91 @@ +#ifndef KERNEL_VM_OBJECT_H_ +#define KERNEL_VM_OBJECT_H_ + +#include +#include + +#define VM_OBJECT_NAME_MAX 64 + +enum vm_object_flags { + /* the memory behind this vm-object wasn't allocated by us, and + * therefore shouldn't be freed by us */ + VMO_IN_PLACE = 0x01u, +}; + +struct vm_object { + struct object vo_base; + + char vo_name[VM_OBJECT_NAME_MAX]; + enum vm_object_flags vo_flags; + + /* queue of struct vm_region_mapping */ + struct queue vo_mappings; + + /* memory protection flags. mappings of this vm_object can only use + * a subset of the flags set in this mask. */ + vm_prot_t vo_prot; + + /* btree of vm_pages that have been allocated to this vm_object. + * vm_page->p_vmo_offset and the size of each page is the bst key. */ + struct btree vo_pages; + /* total length of the vm_object in bytes. */ + size_t vo_size; +}; + +extern kern_status_t vm_object_type_init(void); +extern struct vm_object *vm_object_cast(struct object *obj); + +/* create a vm_object with the specified length in bytes and protection flags. + * the length will be automatically rounded up to the nearest vm_object page + * order size. the actual page frames themselves won't be allocated until + * they are mapped and accessed. */ +extern struct vm_object *vm_object_create( + const char *name, + size_t name_len, + size_t data_len, + vm_prot_t prot); + +/* create a vm_object that represents the specified range of physical memory. + * the length will be automatically rounded up to the nearest vm_object page + * order size. + * NOTE this function assumes that the physical memory has already been + * reserved, and is not in use by any other kernel component. */ +extern struct vm_object *vm_object_create_in_place( + const char *name, + size_t name_len, + phys_addr_t base, + size_t data_len, + vm_prot_t prot); + +extern struct vm_page *vm_object_get_page( + const struct vm_object *vo, + off_t offset); + +extern struct vm_page *vm_object_alloc_page( + struct vm_object *vo, + off_t offset, + enum vm_page_order size); + +extern kern_status_t vm_object_read( + struct vm_object *vo, + void *dst, + off_t offset, + size_t count, + size_t *nr_read); +extern kern_status_t vm_object_write( + struct vm_object *vo, + const void *dst, + off_t offset, + size_t count, + size_t *nr_written); +extern kern_status_t vm_object_copy( + struct vm_object *dst, + off_t dst_offset, + struct vm_object *src, + off_t src_offset, + size_t count, + size_t *nr_copied); + +DEFINE_OBJECT_LOCK_FUNCTION(vm_object, vo_base) + +#endif diff --git a/include/kernel/vm-region.h b/include/kernel/vm-region.h new file mode 100644 index 0000000..e2cbfe1 --- /dev/null +++ b/include/kernel/vm-region.h @@ -0,0 +1,146 @@ +#ifndef KERNEL_VM_REGION_H_ +#define KERNEL_VM_REGION_H_ + +#include +#include +#include + +#define VM_REGION_NAME_MAX 64 +#define VM_REGION_COPY_ALL ((size_t)-1) + +struct vm_region; +struct vm_object; + +enum vm_region_entry_type { + VM_REGION_ENTRY_NONE = 0, + VM_REGION_ENTRY_REGION, + VM_REGION_ENTRY_MAPPING, +}; + +struct vm_region_entry { + struct btree_node e_node; + struct vm_region_entry *e_parent; + enum vm_region_entry_type e_type; + /* offset in bytes of this entry within its immediate parent. */ + off_t e_offset; + /* size of the entry in bytes */ + size_t e_size; +}; + +struct vm_region_mapping { + struct vm_region_entry m_entry; + struct vm_object *m_object; + + /* used to link to vm_object->vo_mappings */ + struct queue_entry m_object_entry; + + vm_prot_t m_prot; + /* offset in bytes to the start of the object data that was mapped */ + off_t m_object_offset; +}; + +struct vm_region { + struct object vr_base; + struct vm_region_entry vr_entry; + + char vr_name[VM_REGION_NAME_MAX]; + + /* btree of struct vm_region_entry. + * sibling entries cannot overlap each other, and child entries must + * be entirely contained within their immediate parent entry. */ + struct btree vr_entries; + + /* memory protection restriction mask. + * any mapping in this region, or any of its children, cannot use + * protection flags that are not set in this mask. + * for example, if VM_PROT_EXEC is /not/ set here, no mapping + * can be created in this region or any child region with VM_PROT_EXEC + * set. */ + vm_prot_t vr_prot; + + /* the physical address space in which mappings in this region (and + * its children) are created */ + pmap_t vr_pmap; +}; + +extern kern_status_t vm_region_type_init(void); +extern struct vm_region *vm_region_cast(struct object *obj); + +/* create a new vm-region, optionally within a parent region. + * `offset` is the byte offset within the parent region where the new region + * should start. + * if no parent is specified, `offset` is the absolute virtual address of the + * start of the region. + * in both cases, `len` is the length of the new region in bytes. */ +extern kern_status_t vm_region_create( + struct vm_region *parent, + const char *name, + size_t name_len, + off_t offset, + size_t region_len, + vm_prot_t prot, + struct vm_region **out); + +/* map a vm-object into a vm-region. + * [region_offset,length] must fall within exactly one region, and cannot span + * multiple sibling regions. + * if [region_offset,length] falls within a child region, the map operation + * will be transparently redirected to the relevant region. + * `prot` must be allowed both by the region into which the mapping is being + * created AND the vm-object being mapped. */ +extern kern_status_t vm_region_map_object( + struct vm_region *region, + off_t region_offset, + struct vm_object *object, + off_t object_offset, + size_t length, + vm_prot_t prot, + virt_addr_t *out); + +extern kern_status_t vm_region_unmap( + struct vm_region *region, + off_t region_offset, + size_t length); + +extern bool vm_region_validate_access( + struct vm_region *region, + off_t offset, + size_t len, + vm_prot_t prot); + +/* find the mapping corresponding to the given virtual address, and page-in the + * necessary vm_page to allow the memory access to succeed. if the relevant + * vm-object page hasn't been allocated yet, it will be allocated here. */ +extern kern_status_t vm_region_demand_map( + struct vm_region *region, + virt_addr_t addr, + enum pmap_fault_flags flags); + +/* get the absolute base virtual address of a region within its + * parent/ancestors. */ +extern virt_addr_t vm_region_get_base_address(const struct vm_region *region); + +extern void vm_region_dump(struct vm_region *region); + +extern kern_status_t vm_region_memmove( + struct vm_region *dest_region, + virt_addr_t dest_ptr, + struct vm_region *src_region, + virt_addr_t src_ptr, + size_t count, + size_t *nr_moved); + +extern kern_status_t vm_region_memmove_v( + struct vm_region *dest_region, + size_t dest_offset, + struct iovec *dest, + size_t nr_dest, + struct vm_region *src_region, + size_t src_offset, + const struct iovec *src, + size_t nr_src, + size_t bytes_to_move); + +DEFINE_OBJECT_LOCK_FUNCTION(vm_region, vr_base) + +#endif diff --git a/include/kernel/vm.h b/include/kernel/vm.h new file mode 100644 index 0000000..c4aed17 --- /dev/null +++ b/include/kernel/vm.h @@ -0,0 +1,358 @@ +#ifndef KERNEL_VM_H_ +#define KERNEL_VM_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct bcache; + +/* maximum number of NUMA nodes */ +#define VM_MAX_NODES 64 +/* maximum number of memory zones per node */ +#define VM_MAX_ZONES (VM_ZONE_MAX + 1) +/* maximum number of supported page orders */ +#define VM_MAX_PAGE_ORDERS (VM_PAGE_MAX_ORDER + 1) +/* maximum number of sparse memory sectors */ +#define VM_MAX_SECTORS 8192 + +/* maximum number of disk sectors that can be stored in a single + page. AKA the number of bits in the sector bitmap. + used by the block cache */ +#define VM_MAX_SECTORS_PER_PAGE 32 + +#define VM_CHECK_ALIGN(p, mask) ((((p) & (mask)) == (p)) ? 1 : 0) + +#define VM_CACHE_INITIALISED(c) ((c)->c_obj_count != 0) +#define VM_PAGE_IS_FREE(pg) \ + (((pg)->p_flags & (VM_PAGE_RESERVED | VM_PAGE_ALLOC)) == 0) + +#define vm_page_foreach(pg, i) \ + for (struct vm_page *i = (pg); i; i = vm_page_get_next_tail(i)) + +typedef phys_addr_t vm_alignment_t; +typedef unsigned int vm_node_id_t; + +enum vm_model { + VM_MODEL_FLAT = 1, + VM_MODEL_SPARSE, +}; + +enum vm_flags { + VM_NORMAL = 0x00u, + VM_GET_DMA = 0x01u, +}; + +enum vm_zone_id { + /* NOTE that these are used as indices into the node_zones array in + vm/zone.c they need to be continuous, and must start at 0! + + not all of these zones are implemented for every architecture. */ + VM_ZONE_DMA = 0u, + VM_ZONE_NORMAL = 1u, + VM_ZONE_HIGHMEM = 2u, +}; + +enum vm_page_order { + VM_PAGE_4K = 0u, + VM_PAGE_8K, + VM_PAGE_16K, + VM_PAGE_32K, + VM_PAGE_64K, + VM_PAGE_128K, + VM_PAGE_256K, + VM_PAGE_512K, + VM_PAGE_1M, + VM_PAGE_2M, + VM_PAGE_4M, + VM_PAGE_8M, + VM_PAGE_16M, + VM_PAGE_32M, + VM_PAGE_64M, + VM_PAGE_128M, + + /* struct vm_page only has 4 bits to store the page order with. + the maximum order that can be stored in 4 bits is 15 (VM_PAGE_128M) + to use any of the page orders listed here, this field + will have to be expanded. */ + VM_PAGE_256M, + VM_PAGE_512M, + VM_PAGE_1G, + VM_PAGE_2G, + VM_PAGE_4G, + VM_PAGE_8G, + VM_PAGE_16G, + VM_PAGE_32G, + VM_PAGE_64G, +}; + +enum vm_page_flags { + /* page is reserved (probably by a call to memblock_reserve()) and + cannot be returned by any allocation function */ + VM_PAGE_RESERVED = 0x01u, + /* page has been allocated by a zone's buddy allocator, and is in-use */ + VM_PAGE_ALLOC = 0x02u, + /* page is the first page of a huge-page */ + VM_PAGE_HEAD = 0x04u, + /* page is part of a huge-page */ + VM_PAGE_HUGE = 0x08u, + /* page is holding cached data from secondary storage, and can be freed + * if necessary (and not dirty). */ + VM_PAGE_CACHE = 0x10u, +}; + +enum vm_memory_region_status { + VM_REGION_FREE = 0x01u, + VM_REGION_RESERVED = 0x02u, +}; + +enum vm_cache_flags { + VM_CACHE_OFFSLAB = 0x01u, + VM_CACHE_DMA = 0x02u +}; + +struct vm_zone_descriptor { + enum vm_zone_id zd_id; + vm_node_id_t zd_node; + const char zd_name[32]; + phys_addr_t zd_base; + phys_addr_t zd_limit; +}; + +struct vm_zone { + struct vm_zone_descriptor z_info; + spin_lock_t z_lock; + + struct queue z_free_pages[VM_MAX_PAGE_ORDERS]; + unsigned long z_size; +}; + +struct vm_pg_data { + struct vm_zone pg_zones[VM_MAX_ZONES]; +}; + +struct vm_cache { + const char *c_name; + enum vm_cache_flags c_flags; + struct queue_entry c_list; + + struct queue c_slabs_full; + struct queue c_slabs_partial; + struct queue c_slabs_empty; + + spin_lock_t c_lock; + + /* number of objects that can be stored in a single slab */ + unsigned int c_obj_count; + /* the size of object kept in the cache */ + unsigned int c_obj_size; + /* combined size of struct vm_slab and the freelist */ + unsigned int c_hdr_size; + /* power of 2 alignment for objects returned from the cache */ + unsigned int c_align; + /* offset from one object to the next in a slab. + this may be different from c_obj_size depending + on the alignment settings for this cache. */ + unsigned int c_stride; + /* size of page used for slabs */ + unsigned int c_page_order; +}; + +struct vm_slab { + struct vm_cache *s_cache; + /* queue entry for struct vm_cache.c_slabs_* */ + struct queue_entry s_list; + /* pointer to the first object slot. */ + void *s_objects; + /* the number of objects allocated on the slab. */ + unsigned int s_obj_allocated; + /* the index of the next free object. + if s_free is equal to FREELIST_END (defined in vm/cache.c) + there are no free slots left in the slab. */ + unsigned int s_free; + /* list of free object slots. + when allocating: + - s_free should be set to the value of s_freelist[s_free] + when freeing: + - s_free should be set to the index of the object being freed. + - s_freelist[s_free] should be set to the previous value of s_free. + this is commented as it as flexible arrays are not supported in c++. + */ + // unsigned int s_freelist[]; +}; + +struct vm_page { + /* order of the page block that this page belongs too */ + uint32_t p_order : 4; + /* the id of the NUMA node that this page belongs to */ + uint32_t p_node : 6; + /* the id of the memory zone that this page belongs to */ + uint32_t p_zone : 3; + /* vm_page_flags_t bitfields. */ + uint32_t p_sector : 11; + /* some unused bits */ + uint32_t p_reserved : 8; + + uint32_t p_flags; + + /* owner-specific pointer */ + union { + struct vm_slab *p_slab; + struct bcache *p_bcache; + void *p_priv0; + }; + + /* multi-purpose list/tree entry. + the owner of the page can decide what to do with this. + some examples: + - the buddy allocator uses this to maintain its per-zone free-page + lists. + - vm_object uses it to maintain a btree of allocated pages keyed + by offset/size. + - the block cache uses this to maintain a tree of pages keyed by + block number. + */ + union { + struct queue_entry p_list; + struct btree_node p_bnode; + + /* btree_node contains three pointers, so provide three + pointer-sized integers for use if p_bnode isn't needed. */ + uintptr_t priv1[3]; + }; + + union { + /* used by bcache when sector size is < page size. bitmap of + * present/missing sectors */ + DECLARE_BITMAP(p_blockbits, VM_MAX_SECTORS_PER_PAGE); + uint32_t p_priv2; + }; + + union { + /* sector address, used by bcache */ + sectors_t p_blockid; + /* offset of this page within the vm_object it is a part of */ + off_t p_vmo_offset; + + uint32_t p_priv3[2]; + }; +} __aligned(2 * sizeof(unsigned long)); + +/* represents a sector of memory, containing its own array of vm_pages. + this struct is used under the sparse memory model, instead of the + global vm_page array */ +struct vm_sector { + /* sector size. this must be a power of 2. + all sectors in the system have the same size. */ + enum vm_page_order s_size; + /* PFN of the first page contained in s_pages. + to find the PFN of any page contained within s_pages, + simply add its offset within the array to s_first_pfn */ + size_t s_first_pfn; + /* array of pages contained in this sector */ + struct vm_page *s_pages; +}; + +extern kern_status_t vm_bootstrap( + const struct vm_zone_descriptor *zones, + size_t nr_zones); +extern enum vm_model vm_memory_model(void); +extern void vm_set_memory_model(enum vm_model model); + +extern struct vm_pg_data *vm_pg_data_get(vm_node_id_t node); + +extern phys_addr_t vm_virt_to_phys(const void *p); +extern void *vm_phys_to_virt(phys_addr_t p); + +extern size_t vm_page_order_to_bytes(enum vm_page_order order); +extern size_t vm_page_order_to_pages(enum vm_page_order order); +extern vm_alignment_t vm_page_order_to_alignment(enum vm_page_order order); +extern void vm_page_init_array(void); +extern struct vm_page *vm_page_get(phys_addr_t addr); +extern phys_addr_t vm_page_get_paddr(struct vm_page *pg); +extern struct vm_zone *vm_page_get_zone(struct vm_page *pg); +extern void *vm_page_get_vaddr(struct vm_page *pg); +extern size_t vm_page_get_pfn(struct vm_page *pg); +static inline size_t vm_page_get_size_bytes(const struct vm_page *pg) +{ + return vm_page_order_to_bytes(pg->p_order); +} +extern struct vm_page *vm_page_alloc( + enum vm_page_order order, + enum vm_flags flags); +extern void vm_page_free(struct vm_page *pg); + +extern int vm_page_split( + struct vm_page *pg, + struct vm_page **a, + struct vm_page **b); +extern struct vm_page *vm_page_merge(struct vm_page *a, struct vm_page *b); +extern struct vm_page *vm_page_get_buddy(struct vm_page *pg); +extern struct vm_page *vm_page_get_next_tail(struct vm_page *pg); + +extern size_t vm_bytes_to_pages(size_t bytes); + +extern void vm_zone_init( + struct vm_zone *z, + const struct vm_zone_descriptor *zone_info); +extern struct vm_page *vm_zone_alloc_page( + struct vm_zone *z, + enum vm_page_order order, + enum vm_flags flags); +extern void vm_zone_free_page(struct vm_zone *z, struct vm_page *pg); + +extern struct vm_cache *vm_cache_create( + const char *name, + size_t objsz, + enum vm_cache_flags flags); +extern void vm_cache_init(struct vm_cache *cache); +extern void vm_cache_destroy(struct vm_cache *cache); +extern void *vm_cache_alloc(struct vm_cache *cache, enum vm_flags flags); +extern void vm_cache_free(struct vm_cache *cache, void *p); + +extern void kmalloc_init(void); +extern void *kmalloc(size_t count, enum vm_flags flags); +extern void *kzalloc(size_t count, enum vm_flags flags); +extern void kfree(void *p); + +/* Flat memory model functions */ +extern void vm_flat_init(void); +extern struct vm_page *vm_page_get_flat(phys_addr_t addr); +extern size_t vm_page_get_pfn_flat(struct vm_page *pg); + +/* Sparse memory model functions */ +extern void vm_sparse_init(void); +extern struct vm_page *vm_page_get_sparse(phys_addr_t addr); +extern size_t vm_page_get_pfn_sparse(struct vm_page *pg); + +#ifdef __cplusplus +} +#endif + +#ifdef __cplusplus +inline void *operator new(size_t count, void *p) +{ + return p; +} + +#define kmalloc_object(objtype, flags, ...) \ + __extension__({ \ + void *p = kmalloc(sizeof(objtype), flags); \ + if (p) { \ + new (p) objtype(__VA_ARGS__); \ + } \ + (objtype *)p; \ + }) + +#endif + +#endif