diff options
| author | Ada Christine <adachristine18@gmail.com> | 2022-02-24 01:58:07 +0000 |
|---|---|---|
| committer | Ada Christine <adachristine18@gmail.com> | 2022-02-24 01:58:07 +0000 |
| commit | 1923e47fd296399180eaf19b990d5e951d71ccd2 (patch) | |
| tree | 2c61fa3d29321c3cf8e31a6ca1b1edcc053cb8f1 /kc/core | |
| parent | 4fa48a2a7814c2f7d3992533313df283c3aa2652 (diff) | |
many changes
- boot time memory allocation has changed. the fractal page map has been
replaced by a single self-mapped page giving 2MiB of ready virtual
space to play with without needing a fully-initialized memory manager. this
scheme may be repeated for other purposes
- the page stack has now been totally overhauled to make use of sparse
allocation and all of the ugly init_*() procedures are gone. long live
the new interface. also reference counting is a thing now. sort of.
- found a bug in vmt_init_node where insertion did not use the root node as
the parent in the case of no predecessor node. whoops.
Diffstat (limited to 'kc/core')
| -rw-r--r-- | kc/core/Makefile | 5 | ||||
| -rw-r--r-- | kc/core/kcc_memory.c | 2 | ||||
| -rw-r--r-- | kc/core/memory.c | 641 | ||||
| -rw-r--r-- | kc/core/memory.h | 45 | ||||
| -rw-r--r-- | kc/core/memory/memory.c | 628 | ||||
| -rw-r--r-- | kc/core/memory/page_early.c | 95 | ||||
| -rw-r--r-- | kc/core/memory/page_early.h | 8 | ||||
| -rw-r--r-- | kc/core/memory/page_stack.c | 215 | ||||
| -rw-r--r-- | kc/core/memory/page_stack.h | 18 | ||||
| -rw-r--r-- | kc/core/task.c | 4 | ||||
| -rw-r--r-- | kc/core/vm_object.h | 9 | ||||
| -rw-r--r-- | kc/core/vm_tree.c | 55 | ||||
| -rw-r--r-- | kc/core/vm_tree.h | 10 |
13 files changed, 1078 insertions, 657 deletions
diff --git a/kc/core/Makefile b/kc/core/Makefile index 6651ca6..9b8983a 100644 --- a/kc/core/Makefile +++ b/kc/core/Makefile @@ -1,7 +1,7 @@ include ../../defaults.mk include ../defaults.mk -VPATH := ../../lib ../ cpu/ +VPATH := ../../lib ../ cpu/ memory/ TARGET := kernel.os .DEFAULT: $(TARGET) @@ -13,7 +13,8 @@ SOBJS := GOBJS := entry_x86_64.o reloc_x86_64.o kprint.o serial.o port.o mmu.o \ kc_main.o memory.o memset.o memcpy.o memmove.o memcmp.o cpu.o \ vm_tree.o exceptions.o panic.o msr.o task.o cpu_task.o irq.o \ - kcc_memory.o string.o pic8259.o pic8259_isr.o pit8253.o + kcc_memory.o string.o pic8259.o pic8259_isr.o pit8253.o \ + page_early.o page_stack.o IOBJS := # __attribute__((interrupt)) requires -mgeneral-regs-only diff --git a/kc/core/kcc_memory.c b/kc/core/kcc_memory.c index 1f3a813..e1d2382 100644 --- a/kc/core/kcc_memory.c +++ b/kc/core/kcc_memory.c @@ -5,7 +5,7 @@ KC_EXPORT kc_phys_addr kcc_page_alloc(void) { - return page_alloc(); + return page_alloc(PAGE_ALLOC_ANY); } KC_EXPORT diff --git a/kc/core/memory.c b/kc/core/memory.c deleted file mode 100644 index bef6cd2..0000000 --- a/kc/core/memory.c +++ /dev/null @@ -1,641 +0,0 @@ -#include "memory.h" -#include "kprint.h" -#include "panic.h" -#include "vm_tree.h" -#include "vm_object.h" -#include "cpu/mmu.h" - -#include <stdint.h> - -#include <kc.h> - -/* kernel virtual space guarantees - * - * the loader must set up the address space as follows - * 1. kernel virtual space is 2GiB in size on 2GiB alignment. - * 2. mappings begin at +0x7fc00000. this mapping space is sparse and its - * own mapping tables begin at +0x7fffe000. - * 3. There is at least 64KiB of mapped pages following the kc_image_end symbol. - */ - -#define kpm1_index(v) (((uint64_t)v >> pte_index_bits(1)) & 0x7ffff) -#define kpm2_index(v) (((uint64_t)v >> pte_index_bits(2)) & 0x3ff) - -#define align_next(v, a) (((uint64_t)v + a - 1) & ~(a - 1)) - -static int core_image_handler(uint32_t code, void *address); -static int core_vmobject_handler(uint32_t code, void *address); - -struct page -{ - uint32_t next: 31; - uint32_t used: 1; -}; - -static struct page * const page_array = (struct page *)0xffffffd800000000; -static int first_free_page_index = -1; -static size_t page_array_entries = 0; -static size_t free_pages = 0; - -// the temporary mapping place. never use this permanently. -static void *const vm_temp = (void *)0xffffffffffa00000; -static uint64_t *const kernel_pm1 = (uint64_t *)0xffffffffffc00000; -static uint64_t *const kernel_pm2 = (uint64_t *)0xffffffffffffe000; - -static uint64_t *get_kernel_pm1e(void *vaddr); -static uint64_t *get_kernel_pm2e(void *vaddr); -static void *page_map_at(void *vaddr, phys_addr_t paddr, enum page_map_flags flags); - -static struct vm_tree core_vm_tree; - -static struct vm_tree_node core_image_node; -static struct vm_tree_node core_vmobject_node; -static struct vm_tree_node core_pagemaps_node; - -static struct vm_object core_image_object = {.type = TRANSLATION_VM_OBJECT}; -static struct vm_object core_vmobject = {.type = ANONYMOUS_VM_OBJECT}; - -static size_t vm_object_space_size = 0x1000000; // 16MiB to start. -static void *vm_next_free = NULL; - -static struct memory_range init_grab_pages( - struct memory_range *ranges, - int count, - size_t size) -{ - struct memory_range request = {SYSTEM_MEMORY, - 0, - align_next(size, page_size(1))}; - - for (int i = 0; i < count; i++) - { - if (ranges[i].type != AVAILABLE_MEMORY || - ranges[i].base < (1 << 20)) // leave pages below 1MiB alone. - { - continue; - } - else if (ranges[i].size > request.size) - { - request.base = ranges[i].base; - ranges[i].base += request.size; - ranges[i].size -= request.size; - return request; - } - } - - request.type = INVALID_MEMORY; - return request; -} - -static size_t init_get_max_paddr(struct memory_range *ranges, int count) -{ - phys_addr_t max_paddr = 0; - - // get the highest usable physical address in all memory ranges. - for (int i = 0; i < count; i++) - { - if (ranges[i].type != AVAILABLE_MEMORY) - { - continue; - } - - if ((ranges[i].base + ranges[i].size - 1) > max_paddr) - { - max_paddr = ranges[i].base + ranges[i].size - 1; - } - } - - return max_paddr; -} - -static phys_addr_t get_kernel_pm4_phys(void) -{ - return mmu_get_map(); -} - -static phys_addr_t get_kernel_pm3_phys(void) -{ - phys_addr_t pm3_phys; - // make a temporary mapping to read pm4 - uint64_t *pm4 = page_map_at(vm_temp, - get_kernel_pm4_phys(), - CONTENT_RODATA|SIZE_2M); - - // pm3_phys is in pm4. - pm3_phys = page_address(pm4[pte_index(&kc_image_base, 4)], 1); - // never leave a temporary mapping - page_unmap(pm4); - - return pm3_phys; -} - -static void init_map_page_array_pm2(struct memory_range *pm2_pages) -{ - // now we need to write the physical address of each pm2 page for the - // page_array in the kernel's pm3. - uint64_t *pm3 = page_map_at(vm_temp, - get_kernel_pm3_phys(), - CONTENT_RWDATA|SIZE_2M); - - // the first index is NOT zero! - for (size_t i = 0; i < (pm2_pages->size / PAGE_SIZE); i++) - { - pm3[pte_index(page_array, 3) + i] = ( - pm2_pages->base + i * - PAGE_SIZE)|PAGE_NX|PAGE_WR|PAGE_PR; - } - // never forget to unmap temporary mappings. - page_unmap(pm3); -} - -static void init_create_page_array_map(struct memory_range *pages, - struct memory_range *maps) -{ - // indices for page_array always start at 0 - size_t entry_count = pages->size / PAGE_SIZE; - - // every 512 entries we need to re-map and clean. - for (size_t i = 0; i < entry_count; i += PAGE_TABLE_INDEX_MASK) - { - uint64_t *pm1 = page_map_at( - vm_temp, - maps->base + i * PAGE_SIZE, - CONTENT_RWDATA|SIZE_2M); - memset(pm1, 0, PAGE_SIZE); - for (size_t j = 0; - (j < PAGE_TABLE_INDEX_MASK) && ((j + i) < entry_count); - j++) - { - pm1[j] = (pages->base + (i + j) * PAGE_SIZE)| - PAGE_NX|PAGE_WR|PAGE_PR; - } - - page_unmap(pm1); - } -} - -static void init_set_memory_range(struct memory_range *range) -{ - for (size_t i = 0; i < range->size / PAGE_SIZE; i++) - { - if (range->base / PAGE_SIZE + i > page_array_entries) - { - return; - } - - if (range->type == AVAILABLE_MEMORY) - { - page_free(range->base + PAGE_SIZE * i); - } - else - { - struct page *page = &page_array[range->base / PAGE_SIZE + i]; - page->used = 1; - } - } -} - -static void init_populate_page_array(struct memory_range *ranges, int count) -{ - for (int i = 0; i < count; i++) - { - init_set_memory_range(&ranges[i]); - } -} - -static void init_create_page_array(struct memory_range *ranges, int count) -{ - // the highest actual physical memory address. - size_t max_paddr = init_get_max_paddr(ranges, count); - page_array_entries = max_paddr / page_size(1); - size_t page_array_size = page_array_entries * sizeof(struct page); - - // the physical pages that will contain page_array - struct memory_range pa_pages = init_grab_pages(ranges, count, page_array_size); - - // the number of page tables needed to map pa_pages - // this will be 1 on systems with less than 2GB of memory. - size_t pm1_count = page_count(pa_pages.size, 2); - struct memory_range pm1_pages = init_grab_pages(ranges, - count, - pm1_count * page_size(1)); - - init_create_page_array_map(&pa_pages, &pm1_pages); - - // the number of page directories needed to map pa_pages - // this will be 1 on systems with less than 1TB of memory. - // who the fuck has 1TB of memory lol - size_t pm2_count = page_count(pa_pages.size, 3); - struct memory_range pm2_pages = init_grab_pages(ranges, - count, - pm2_count * page_size(1)); - - init_create_page_array_map(&pm1_pages, &pm2_pages); - - // ok here goes - init_map_page_array_pm2(&pm2_pages); - - memset(page_array, 0, page_array_size); - - init_populate_page_array(ranges, count); - init_populate_page_array(&pa_pages, 1); - init_populate_page_array(&pm1_pages, 1); - init_populate_page_array(&pm2_pages, 1); -} - -static void *page_map_at(void *vaddr, - phys_addr_t paddr, - enum page_map_flags flags) -{ - uint64_t entry = PAGE_PR; - - switch (flags & CONTENT_MASK) - { - case CONTENT_RODATA: - entry |= PAGE_NX; - break; - case CONTENT_RWDATA: - entry |= PAGE_NX|PAGE_WR; - break; - default: - break; - } - - size_t offset; - uint64_t *pte; - - switch (flags & SIZE_MASK) - { - case SIZE_2M: - entry |= page_address(paddr, 2)|PAGE_LG; - offset = page_offset(paddr, 2); - pte = get_kernel_pm2e(vaddr); - break; - case SIZE_4K: - entry |= page_address(paddr, 1); - offset = page_offset(paddr, 1); - pte = get_kernel_pm1e(vaddr); - break; - default: - offset = 0; - pte = NULL; - } - - if (!pte) - { - return NULL; - } - - *pte = entry; - return (char *)vaddr + offset; -} - -static void init_vm_node( - struct vm_tree_node *node, - struct vm_object *object, - void *base, - void *head) -{ - memset(node, 0, sizeof(*node)); - node->key = - (struct vm_tree_key) - { - (uintptr_t)base, - (size_t)head - (size_t)base - }; - - if (!vmt_search_key(&core_vm_tree, &node->key)) - { - kputs("inserting node\n"); - struct vm_tree_node *p = vmn_predecessor_key( - core_vm_tree.root, - &node->key); - vmt_insert( - &core_vm_tree, - node, - p, - vmn_child_direction(node, p)); - node->object = object; - } - else - { - kputs("fatal: attempt to insert overlapping vm node\n"); - PANIC(GENERAL_PANIC); - } -} - -void memory_init(void) -{ - struct kc_boot_data *boot_data = get_boot_data(); - - init_create_page_array( - boot_data->phys_memory_map.base, - boot_data->phys_memory_map.entries); - - void * object_space_head = (void *)page_align( - boot_data->object_space.size + - (uintptr_t)boot_data->object_space.base, 1); - - kputs("static vm node 1\n"); - init_vm_node( - &core_image_node, - &core_image_object, - &kc_image_base, - object_space_head); - core_image_node.object->handler = core_image_handler; - - kputs("static vm node 2\n"); - init_vm_node( - &core_vmobject_node, - &core_vmobject, - object_space_head, - (char *)object_space_head + vm_object_space_size); - core_vmobject_node.object->handler = core_vmobject_handler; - - kputs("static vm node 3\n"); - init_vm_node(&core_pagemaps_node, NULL, vm_temp, (void *)-1); - vm_next_free = (char *)object_space_head + vm_object_space_size; -} - -void *page_map(phys_addr_t paddr, enum page_map_flags flags) -{ - (void)paddr; - (void)flags; - return NULL; -} - -void page_unmap(void *vaddr) -{ - uint64_t *pte = get_kernel_pm2e(vaddr); - - if (pte && !(*pte & PAGE_LG)) - { - pte = get_kernel_pm1e(vaddr); - } - - if (pte) - { - *pte = 0; - } - mmu_invalidate(vaddr); -} - -phys_addr_t page_alloc(void) -{ - int index = first_free_page_index; - - if (index > 0) - { - page_array[index].used = 1; - first_free_page_index = page_array[index].next; - free_pages--; - } - - return (phys_addr_t)index * page_size(1); -} - -void page_free(phys_addr_t paddr) -{ - int index = paddr / page_size(1); - - if ((size_t)index > page_array_entries) - { - return; - } - - page_array[index].used = 0; - page_array[index].next = first_free_page_index; - first_free_page_index = index; - free_pages++; -} - -struct heap_header -{ - size_t size; - struct heap_header *next; -}; - -static struct heap_header *heap_root = (void *)-1; - -void *heap_alloc(size_t size) -{ - // simple first-fit allocator, allocates downward from the head - // of the first block of sufficient size - // TODO: join heap blocks if there is not one of sufficient size - void *block = NULL; - - // first attempt at allocation - if ((void *)-1 == heap_root) - { - heap_root = NULL; - struct heap_header *header = (void *)core_vmobject_node.key.address; - header->size = core_vmobject_node.key.size - sizeof(header->size); - heap_free((char *)header + sizeof(*header)); - } - - struct heap_header *header = heap_root; - size = align_next(size, sizeof(*header)); - - while (header) - { - if (header->size > size) - { - break; - } - header = header->next; - } - - if (header) - { - header->size -= size + sizeof(*header); - header = (struct heap_header *)((char *)header + header->size); - header->size = size; - header->next = NULL; - block = (char *)header + sizeof(*header); - } - - return block; -} - -void heap_free(void *block) -{ - struct heap_header *header = (void *) - ((char *)block - sizeof(*header)); - - header->next = heap_root; - heap_root = header; -} - -void *memory_alloc(size_t size) -{ - // allocations larger than page-size should just get an anonymous vm_object - if (size < 4096) - { - return heap_alloc(size); - } - else - { - return vm_alloc(size); - } -} - -void memory_free(void *block) -{ - // a little complicated to implement - // - // 1. find the vm_object that owns the block - // a. if the vm_object is a heap, call heap_free() - // b. if the vm_object is an anonymous vm_area, call vm_free(). - // c. if the object is any other kind issue a bug warning and do nothing - (void)block; -} - -void *vm_alloc_at(void *address, size_t size) -{ - // TODO: all of the vm_tree code is a bit of a mess. needs to be cleaned - // up and streamlined. - struct vm_tree_key key = {(uintptr_t)address, size}; - - // 1. check that there's a gap at the given location - struct vm_tree_node *node = vmt_search_key( &core_vm_tree, &key); - - if (!node) - { - node = heap_alloc(sizeof(*node)); - init_vm_node(node, &core_vmobject, address, (char *)address + size); - } - else - { - return NULL; - } - - return address; -} - -void *vm_alloc(size_t size) -{ - // TODO: gap-finding algorithm for failed allocations - void *address = vm_alloc_at(vm_next_free, size); - - if (address) - { - vm_next_free = (char *)address + size; - return address; - } - - return NULL; -} - -static uint64_t *get_kernel_pm1e(void *vaddr) -{ - return &kernel_pm1[kpm1_index(vaddr)]; -} - -static uint64_t *get_kernel_pm2e(void *vaddr) -{ - return &kernel_pm2[kpm2_index(vaddr)]; -} - -int anonymous_page_handler(uint32_t code, void *address) -{ - kputs("anonymous space fault\n"); - if (code & PAGE_PR) - { - kputs("can't fault a present page\n"); - // there's no reason a protection violation should happen - // in anonymous space - PANIC(UNHANDLED_FAULT); - } - // kernel page mappings are built different - if (address >= (void *)&kc_image_base) - { - uint64_t *pm2e = get_kernel_pm2e(address); - - if (!page_address(*pm2e, 1)) - { - // create a page table and install it - phys_addr_t pm1_phys = page_alloc(); - if (pm1_phys) - { - uint64_t *pm1; - pm1 = page_map_at(vm_temp, pm1_phys, CONTENT_RWDATA|SIZE_2M); - // zero the page - memset(pm1, 0, PAGE_SIZE); - // put the table where it goes - *pm2e = page_address(pm1_phys, 1)|PAGE_WR|PAGE_PR; - page_unmap(pm1); - } - else - { - PANIC(OUT_OF_MEMORY); - } - } - - uint64_t *pm1e = get_kernel_pm1e(address); - - if (pm1e) - { - phys_addr_t page_phys = page_alloc(); - if (page_phys) - { - *pm1e = page_address(page_phys, 1)|PAGE_WR|PAGE_PR; - // clear a potentially dirty page. - memset((void *)page_address(address, 1), 0, page_size(1)); - } - } - } - - return 0; -} - -int page_fault_handler(uint8_t vector, uint32_t code) -{ - (void)vector; - void *address; - __asm__ volatile ("movq %%cr2, %0" : "=r"(address)); - kprintf("page fault code=%d, pfla=%#lx\n", code, address); - struct vm_object *o = vmt_get_object(&core_vm_tree, address); - int result = 1; - if (o) - { - kputs("found memory manager object\n"); - if (o->handler) - { - result = o->handler(code, address); - } - if (result) - { - PANIC(UNHANDLED_FAULT); - } - } - else - { - kputs("didn't find memory object\n"); - PANIC(UNHANDLED_FAULT); - } - - return 0; -} - -int general_protection_handler(uint8_t vector, uint32_t code) -{ - //TODO: implement #gp handler - (void)vector; - (void)code; - kputs("general protection violation\n"); - PANIC(UNHANDLED_FAULT); - return 0; -} - -static int core_image_handler(uint32_t code, void *address) -{ - (void) code; - (void) address; - return 1; -} - -static int core_vmobject_handler(uint32_t code, void *address) -{ - return anonymous_page_handler(code, address); -} - diff --git a/kc/core/memory.h b/kc/core/memory.h index 526d8ca..ea86912 100644 --- a/kc/core/memory.h +++ b/kc/core/memory.h @@ -1,11 +1,15 @@ #pragma once -#include <lib.h> - #include <kernel/entry.h> #include <kernel/memory/paging.h> #include <kernel/memory/range.h> +#include <lib.h> + +#include <core/memory.h> + +#include "vm_tree.h" + typedef int (*memory_space_handler_func)(uint32_t code, void *vaddr); enum page_map_flags @@ -21,20 +25,53 @@ enum page_map_flags SIZE_MASK = 0xc, }; +enum page_alloc_flags +{ + PAGE_ALLOC_NONE, + PAGE_ALLOC_LOW, + PAGE_ALLOC_CONV, + PAGE_ALLOC_HIGH, + PAGE_ALLOC_ANY +}; + +enum vm_alloc_flags +{ + VM_ALLOC_ANY = 0, + VM_ALLOC_CACHE = 1, + VM_ALLOC_CORE = 2, + VM_ALLOC_TYPE_MASK = 3, + + VM_ALLOC_IMMEDIATE = 4, + VM_ALLOC_ACTION_MASK = 4, + + VM_ALLOC_ANONYMOUS = 8, + VM_ALLOC_DIRECT = 16, + VM_ALLOC_TRANSLATE = 24, + VM_ALLOC_MECHANISM_MASK = 24, +}; + void memory_init(void); +void page_set_present(kc_phys_addr page); +void page_set_allocated(kc_phys_addr page); +void page_set_free(kc_phys_addr page); + void *page_map(phys_addr_t paddr, enum page_map_flags flags); void page_unmap(void *vaddr); -phys_addr_t page_alloc(void); +phys_addr_t page_alloc(enum page_alloc_flags flags); void page_free(phys_addr_t paddr); void *heap_alloc(size_t size); void heap_free(void *block); -void *vm_alloc(size_t size); +void *vm_alloc(size_t size, enum vm_alloc_flags flags); void vm_free(void *block); void *memory_alloc(size_t size); void memory_free(void *block); +struct vm_tree *vm_get_tree(void); + +int anonymous_page_handler(struct vm_tree_node *, uint32_t, void *); + diff --git a/kc/core/memory/memory.c b/kc/core/memory/memory.c new file mode 100644 index 0000000..1a33629 --- /dev/null +++ b/kc/core/memory/memory.c @@ -0,0 +1,628 @@ +/* kernel virtual space guarantees + * the loader must set up the address space as follows + * 1. kernel virtual space is 2GiB in size on 2GiB alignment. + * 2. the very top of address space must have a single self-mapped + * that manages the top 2MiB of space. + * 3. that top 2MiB of space contains a bump-allocated buffer of some size + * recorded in the boot data structures + */ + +#include "page_early.h" +#include "page_stack.h" + +#include "memory.h" +#include "kprint.h" +#include "panic.h" +#include "vm_object.h" +#include "cpu/mmu.h" + +#include <stdint.h> + +#include <kc.h> +#include <core/memory.h> + +#define align_next(x, a) (x + a - 1) & ~(a - 1) + +static void *temp_page_map(kc_phys_addr paddr, enum page_map_flags flags); +static void temp_page_unmap(void *address); + +static void *page_map_at( + void *vaddr, + phys_addr_t paddr, + enum page_map_flags flags); + +enum vm_core_state_items +{ + KERNEL_VM_STATE, + HEAP_VM_STATE, + STACK_VM_STATE, + TEMPS_VM_STATE +}; + +struct vm_core_static_state +{ + struct vm_tree_node node; + struct vm_object *object; +}; + +static struct vm_core_state +{ + struct vm_tree tree; + struct vm_object global_null; + struct vm_object global_anonymous; + struct vm_object global_direct; + struct vm_object global_translate; + struct vm_core_static_state statics[4]; + kc_phys_addr zero_page; + void *first_free; +} vm_state = { + {0}, + {NULL_VM_OBJECT, NULL}, + {ANONYMOUS_VM_OBJECT, anonymous_page_handler}, + {DIRECT_VM_OBJECT, NULL}, + {TRANSLATION_VM_OBJECT, NULL}, + { + {{0}, &vm_state.global_null}, + {{0}, &vm_state.global_anonymous}, + {{0}, &vm_state.global_anonymous}, + {{0}, &vm_state.global_null}, + }, + 0, + NULL +}; + +static struct vm_temp_state +{ + uint64_t *table; + int first_free; +} +temp_state; + +struct vm_tree *vm_get_tree(void) +{ + return &vm_state.tree; +} + +void page_init(void) +{ + kprintf("initializing page frame allocator\n"); + page_early_init(); + page_stack_init(); +} + +void page_init_final(void) +{ + kprintf("finishing page frame allocator initialization\n"); + page_early_final(); +} + + +void page_set_present(kc_phys_addr page) +{ + page_stack_set_present(page); +} + +int page_get_present(kc_phys_addr page) +{ + return page_stack_get_present(page); +} + +void page_set_allocated(kc_phys_addr page) +{ + page_stack_set_allocated(page); +} + +void page_set_free(kc_phys_addr page) +{ + page_stack_set_free(page); +} + +kc_phys_addr page_alloc(enum page_alloc_flags type) +{ + kc_phys_addr paddr = 0; + if ((paddr = page_stack_alloc(type)) > 0) + { + return paddr; + } + else + { + return page_early_alloc(type); + } +} + +void page_free(kc_phys_addr page) +{ + page_stack_free(page); +} + +static void *map_tableset(void *vaddr, uint64_t *tables[4]) +{ + uint64_t current_phys; + uint64_t *current_pte; + // pml4 is guaranteed to be present. no checks necessary. + // + + current_phys = page_address(mmu_get_map(), 1); + tables[3] = temp_page_map(current_phys, CONTENT_RWDATA); + int n = 3; + + while (n) + { + if (tables[n]) + { + current_pte = &tables[n][pte_index(vaddr, n+1)]; + + if (!page_address(*current_pte, 1)) + { + current_phys = page_alloc(PAGE_ALLOC_CONV); + if (current_phys) + { + tables[n-1] = temp_page_map(current_phys, CONTENT_RWDATA); + memset(tables[n-1], 0, page_size(1)); + *current_pte = current_phys |= PAGE_NX|PAGE_WR|PAGE_PR; + } + else + { + kprintf("error: failed allocating memory for page table\n"); + PANIC(OUT_OF_MEMORY); + } + } + else + { + current_phys = page_address(*current_pte, 1); + tables[n-1] = temp_page_map(current_phys, CONTENT_RWDATA); + } + } + n--; + } + + if (tables[0]) + { + current_pte = &tables[0][pte_index(vaddr, 1)]; + } + + return vaddr; +} + +static void *page_map_at( + void *vaddr, + phys_addr_t paddr, + enum page_map_flags flags) +{ + // TODO: add checks to prevent attempts to map reserved addreses + // + uint64_t *mapset[4] = {NULL}; + + if (vaddr != map_tableset(vaddr, mapset)) + { + PANIC(GENERAL_PANIC); + } + + uint64_t entry = PAGE_PR; + + switch (flags & CONTENT_MASK) + { + case CONTENT_RODATA: + entry |= PAGE_NX; + break; + case CONTENT_RWDATA: + entry |= PAGE_NX|PAGE_WR; + break; + default: + break; + } + + size_t offset; + + switch (flags & SIZE_MASK) + { + case SIZE_1G: + offset = page_offset(paddr, 3); + break; + case SIZE_2M: + offset = page_offset(paddr, 2); + break; + case 0: + case SIZE_4K: + offset = page_offset(paddr, 1); + break; + default: + vaddr = NULL; + } + if (vaddr) + { + mapset[0][pte_index(vaddr, 1)] = page_address(paddr, 1) | entry; + } + + for (int i = 0; i < 4; i++) + { + if (mapset[i]) + { + temp_page_unmap(mapset[i]); + } + } + + return (char *)vaddr + offset; +} + +#define VM_HEAP_SIZE 16 * page_size(2) + +static void *temp_page_alloc(void) +{ + int index = temp_state.first_free; + + // do not allocate the 511th index!! + if (index > -1 && index < 511) + { + temp_state.first_free = temp_state.table[index] >> 1; + temp_state.table[index] = 0; + } + else + { + return NULL; + } + + return (void *)-((512ULL - index) << 12); +} + +static void temp_page_free(void *address) +{ + int index = pte_index(address, 1); + + if (index > -1 && index < 511) + { + temp_state.table[index] = temp_state.first_free << 1; + temp_state.first_free = index; + } +} + +static void *temp_page_map(kc_phys_addr paddr, enum page_map_flags flags) +{ + uint64_t entry = PAGE_PR; + size_t offset = 0; + + switch (flags & SIZE_MASK) + { + case 0: + case SIZE_4K: + offset = page_offset(paddr, 1); + break; + default: + return NULL; + } + + switch (flags & CONTENT_MASK) + { + case CONTENT_RODATA: + entry |= PAGE_NX; + break; + case CONTENT_RWDATA: + entry |= PAGE_NX|PAGE_WR; + break; + default: + break; + } + + unsigned char *vaddr = temp_page_alloc(); + + if (vaddr) + { + vaddr += offset; + temp_state.table[pte_index(vaddr, 1)] = page_address(paddr, 1) | entry; + } + + return vaddr; +} + +static void temp_page_unmap(void *vaddr) +{ + temp_page_free(vaddr); + mmu_invalidate(vaddr); +} + +static void vm_init(void) +{ + kprintf("initializing vm state\n"); + struct kc_boot_data *boot_data = get_boot_data(); + struct vm_core_static_state *states = &vm_state.statics[0]; + + // initialize the static vm node entries + struct + { + unsigned char *base; + unsigned char *head; + } vm_ranges[] = { + {&kc_image_base, &kc_image_end}, + {&kc_image_end, &kc_image_end + VM_HEAP_SIZE}, + { + boot_data->buffer.base - page_size(1) * 2, + boot_data->buffer.base - page_size(1) + }, + { + boot_data->buffer.current, + boot_data->buffer.current + boot_data->buffer.max_size, + } + }; + + vm_state.first_free = &kc_image_end; + + for (int i = 0; i <= TEMPS_VM_STATE; i++) + { + vmt_init_node + ( + vm_get_tree(), + &states[i].node, + states[i].object, + (void *)vm_ranges[i].base, + (void *)vm_ranges[i].head + ); + } + + // init the temporary mappings table state + temp_state.table = (uint64_t *)-page_size(1); + temp_state.first_free = -1; + for (int index = 0; index < 512; index++) + { + // i love these array-based linked list stacks + if (!(temp_state.table[index] & PAGE_PR)) + { + temp_page_free((void *)-((512ULL - index) << 12)); + } + } + + page_init(); + + vm_state.zero_page = page_alloc(PAGE_ALLOC_CONV); + if (!vm_state.zero_page) + { + kprintf("failed allocating for zero page\n"); + PANIC(GENERAL_PANIC); + } + + void *zero_temp = temp_page_map(vm_state.zero_page, CONTENT_RWDATA); + + if (!zero_temp) + { + kprintf("failed mapping zero page for initialization\n"); + PANIC(GENERAL_PANIC); + } + + // make the zero page live up to its name; + memset(zero_temp, 0, page_size(1)); + temp_page_unmap(zero_temp); + + page_init_final(); +} + +void memory_init(void) +{ + vm_init(); +} + +void *page_map(phys_addr_t page, enum page_map_flags flags) +{ + void *vm_page = vm_alloc(4096, VM_ALLOC_TRANSLATE); + if (vm_page) + { + page_map_at(vm_page, page, flags); + } + return NULL; +} + +void page_unmap(void *vaddr) +{ + (void)vaddr; +} + +struct heap_header +{ + size_t size; + struct heap_header *next; +}; + +static struct heap_header *heap_root = (void *)-1ULL; + +void *heap_alloc(size_t size) +{ + // simple first-fit allocator, allocates downward from the head + // of the first block of sufficient size + // TODO: join heap blocks if there is not one of sufficient size + void *block = NULL; + struct heap_header *header; + + // first attempt at allocation + if ((void *)-1ULL == heap_root) + { + struct vm_tree_node *heap_node = &vm_state.statics[HEAP_VM_STATE].node; + kprintf("initializing heap at %#lx of %zu bytes\n", + heap_node->key.address, heap_node->key.size); + heap_root = NULL; + header = (struct heap_header *)heap_node->key.address; + header->size = heap_node->key.size - sizeof(header->size); + heap_free((char *)header + sizeof(*header)); + } + else + { + header = heap_root; + } + size = align_next(size, sizeof(*header)); + + while (header) + { + if (header->size > size) + { + break; + } + header = header->next; + } + + if (header) + { + header->size -= size + sizeof(*header); + header = (struct heap_header *)((char *)header + header->size); + header->size = size; + header->next = NULL; + block = (char *)header + sizeof(*header); + } + + return block; +} + +void heap_free(void *block) +{ + struct heap_header *header = (void *) + ((char *)block - sizeof(*header)); + + header->next = heap_root; + heap_root = header; +} + +void *memory_alloc(size_t size) +{ + // allocations larger than page-size should just get an anonymous vm_object + if (size < 4096) + { + return heap_alloc(size); + } + else + { + return vm_alloc(size, VM_ALLOC_ANY|VM_ALLOC_ANONYMOUS); + } +} + +void memory_free(void *block) +{ + // a little complicated to implement + // + // 1. find the vm_object that owns the block + // a. if the vm_object is a heap, call heap_free() + // b. if the vm_object is an anonymous vm_area, call vm_free(). + // c. if the object is any other kind issue a bug warning and do nothing + (void)block; +} + +void *vm_alloc_at(void *address, size_t size, enum vm_alloc_flags flags) +{ + struct vm_tree_key key = {(uintptr_t)address, size}; + struct vm_tree_node *node; + struct vm_object *object; + + switch (flags & VM_ALLOC_MECHANISM_MASK) + { + case VM_ALLOC_ANONYMOUS: + object = &vm_state.global_anonymous; + break; + case VM_ALLOC_DIRECT: + object = &vm_state.global_direct; + break; + case VM_ALLOC_TRANSLATE: + object = &vm_state.global_translate; + break; + default: + return NULL; + } + + if (!vmt_search_key(vm_get_tree(), &key) && + (node = heap_alloc(sizeof(*node)))) + { + vmt_init_node( + vm_get_tree(), + node, + object, + address, + (void *)((char *)address + size)); + } + + return address; +} + +void *vm_alloc(size_t size, enum vm_alloc_flags flags) +{ + (void)flags; + // TODO implement a proper allocator here rather than this bump allocator. + char *address = vm_state.first_free; + + if (address == vm_alloc_at(address, size, flags)) + { + vm_state.first_free = address + size; + return address; + } + + return NULL; +} + +int anonymous_page_handler( + struct vm_tree_node *node, + uint32_t code, + void *address) +{ + (void)node; + + if (!(code & 1)) + { + // map the zero page read-only to the address + page_map_at( + address, + vm_state.zero_page, + CONTENT_RODATA|SIZE_4K); + } + + if ((code & 1) && (code & 2)) // page fault write violation on present page + { + mmu_invalidate(address); + kc_phys_addr paddr = page_alloc(PAGE_ALLOC_CONV); + + if (!paddr) + { + kprintf("got zero from page_alloc :|\n"); + PANIC(OUT_OF_MEMORY); + } + page_map_at( + address, + page_alloc(PAGE_ALLOC_CONV), + CONTENT_RWDATA|SIZE_4K); + // NULL out the whole page + // TODO: thread to clean dirty pages. + memset((void *)page_address(address, 1), 0, page_size(1)); + } + + return 0; +} + +int page_fault_handler(uint8_t vector, uint32_t code) +{ + (void)vector; + + void *address; + __asm__ volatile ("movq %%cr2, %0" : "=r"(address)); + + struct vm_tree_key key = {(uintptr_t)address, sizeof(uint64_t)}; + struct vm_tree_node *node = vmt_search_key(vm_get_tree(), &key); + + if (!node) + { + kprintf("error: page fault in unmanaged address %#lx\n", + address); + PANIC(UNHANDLED_FAULT); + } + + if (!node->object->handler) + { + kprintf("error: vm object at %p has no fault handler\n"); + PANIC(UNHANDLED_FAULT); + } + + return node->object->handler(node, code, address); +} + +int general_protection_handler(uint8_t vector, uint32_t code) +{ + //TODO: implement #gp handler + (void)vector; + (void)code; + + kputs("general protection violation\n"); + PANIC(UNHANDLED_FAULT); + return 0; +} + diff --git a/kc/core/memory/page_early.c b/kc/core/memory/page_early.c new file mode 100644 index 0000000..7f1d0eb --- /dev/null +++ b/kc/core/memory/page_early.c @@ -0,0 +1,95 @@ +#include "page_early.h" +#include "kprint.h" +#include "panic.h" + +static struct page_early_state +{ + struct memory_range *first; + struct memory_range *current; + struct memory_range *last; +} +early_state; + +void page_early_init(void) +{ + kprintf("initializing early page frame allocator\n"); + struct kc_boot_data *boot_data = get_boot_data(); + + early_state.first = &boot_data->memory.entries[0]; + early_state.last = &boot_data->memory.entries[boot_data->memory.count]; + early_state.current = early_state.first; +} + +void page_early_final(void) +{ + if (early_state.first) + { + kprintf("finalizing early page allocator\n"); + for (struct memory_range *current = early_state.first; + current < early_state.last; + current++) + { + while (current->size >= page_size(1)) + { + current->size -= page_size(1); + + enum memory_range_type type = current->type; + kc_phys_addr page = current->base + current->size; + // all pages are gonna be set allocated first + // to initialize the tracking structure at the other side + // and make setting the page free as simple as + // calling page_free(); + page_set_allocated(page); + + switch (type) + { + case RESERVED_MEMORY: + case SYSTEM_MEMORY: + page_set_present(page); + break; + case AVAILABLE_MEMORY: + page_set_present(page); + page_free(page); + break; + case FIRMWARE_MEMORY: + case MMIO_MEMORY: + break; + default: + break; + } + } + } + + early_state = (struct page_early_state){NULL, NULL, NULL}; + } +} + +kc_phys_addr page_early_alloc(enum page_alloc_flags type) +{ + // TODO: support low/conv/high allocations in early_alloc. + // currently we only have conventional allocations enforced + // by the conditions of the scanning loop + (void)type; + + while(early_state.current) + { + if ((early_state.current->type == AVAILABLE_MEMORY) && + (early_state.current->base > 0x10000) && + (early_state.current->size > page_size(1))) + { + early_state.current->size -= page_size(1); + return early_state.current->base + early_state.current->size; + } + + if (early_state.current == early_state.last) + { + early_state.current = NULL; + break; + } + early_state.current++; + } + + kprintf("error: early allocator has run out of memory\n"); + PANIC(OUT_OF_MEMORY); +} + diff --git a/kc/core/memory/page_early.h b/kc/core/memory/page_early.h new file mode 100644 index 0000000..3cd5b4e --- /dev/null +++ b/kc/core/memory/page_early.h @@ -0,0 +1,8 @@ +#pragma once + +#include "memory.h" + +void page_early_init(void); +void page_early_final(void); +kc_phys_addr page_early_alloc(enum page_alloc_flags type); + diff --git a/kc/core/memory/page_stack.c b/kc/core/memory/page_stack.c new file mode 100644 index 0000000..92c5cff --- /dev/null +++ b/kc/core/memory/page_stack.c @@ -0,0 +1,215 @@ +#include "page_stack.h" +#include "vm_tree.h" +#include "vm_object.h" + +#include <kc.h> + +#define PAGE_STACK_CACHE_SIZE (1ULL << 32) +#define page_stack_index(x) (x / page_size(1)) +#define page_stack_address(x) (x * page_size(1)) + +struct page +{ + uint32_t present: 1; // a physical page is at this location + uint32_t allocated: 1; // this physical page has been taken + int32_t next_refs; // allocated = 0: the index of the next page in the list + // allocated = 1: the number of references this page has +}; + +static struct page_stack_state +{ + struct vm_tree_node node; + struct vm_object object; + struct page *stack; + int32_t free_count[3]; + int32_t total_count[3]; + int32_t first_free[3]; +} +stack_state = { + {0}, + {ANONYMOUS_VM_OBJECT, anonymous_page_handler}, + NULL, + {0,0,0}, + {0,0,0}, + {-1,-1,-1} +}; + +static enum page_alloc_flags stack_type(kc_phys_addr page); +static void stack_push(kc_phys_addr page); +static kc_phys_addr stack_pop(enum page_alloc_flags type); + +void page_stack_init(void) +{ + vmt_init_node( + vm_get_tree(), + &stack_state.node, + &stack_state.object, + &kc_image_base - PAGE_STACK_CACHE_SIZE, + &kc_image_base); + + stack_state.stack = (struct page *)stack_state.node.key.address; +} + +kc_phys_addr page_stack_alloc(enum page_alloc_flags type) +{ + kc_phys_addr page = stack_pop(type); + if (page) + { + page_stack_inc_ref(page); + } + return page; +} + +void page_stack_free(kc_phys_addr page) +{ + // immediately release a reference + int referents = page_stack_dec_ref(page); + + // freeing a page only makes sense for present physical pages + if (!page_stack_get_present(page)) + { + return; + } + + // only actually free the page if it has no more references + if (referents == 0) + { + page_stack_set_free(page); + stack_push(page); + } +} + +int page_stack_get_present(kc_phys_addr page) +{ + unsigned index = page_stack_index(page); + return stack_state.stack[index].present; +} + +void page_stack_set_present(kc_phys_addr page) +{ + unsigned index = page_stack_index(page); + stack_state.stack[index].present = 1; +} + +void page_stack_set_allocated(kc_phys_addr page) +{ + unsigned index = page_stack_index(page); + stack_state.stack[index].allocated = 1; + stack_state.stack[index].next_refs = 1; +} + +void page_stack_set_free(kc_phys_addr page) +{ + unsigned index = page_stack_index(page); + stack_state.stack[index].allocated = 0; + stack_state.stack[index].next_refs = -1; +} + +int page_stack_inc_ref(kc_phys_addr page) +{ + unsigned index = page_stack_index(page); + // taking a reference only makes sense for an allocated page + if (stack_state.stack[index].allocated) + { + return ++stack_state.stack[index].next_refs; + } + + return -1; +} + +int page_stack_dec_ref(kc_phys_addr page) +{ + unsigned index = page_stack_index(page); + // releasing a reference only makes sense for an allocated page + if (stack_state.stack[index].allocated) + { + // releasing a reference only makes sense if the refcount is > 0 + if (stack_state.stack[index].next_refs > 0) + { + stack_state.stack[index].next_refs--; + } + return stack_state.stack[index].next_refs; + } + + return -1; +} + +static enum page_alloc_flags stack_type(kc_phys_addr page) +{ + // the page stack type only makes sense for physical pages + if (!page_stack_get_present(page)) + { + return PAGE_ALLOC_NONE; + } + + // pages above 4GiB physical are high memory + if (page > -1U) + { + return PAGE_ALLOC_HIGH; + } + + // pages below 1MiB are low memory + else if (page < 0x100000) + { + return PAGE_ALLOC_LOW; + } + + // all other pages are conventional memory + return PAGE_ALLOC_CONV; +} + +static void stack_push(kc_phys_addr page) +{ + enum page_alloc_flags type = stack_type(page); + + // stack push only makes sense for pages in a physical stack + if ((type < PAGE_ALLOC_LOW) || type > PAGE_ALLOC_HIGH) + { + return; + } + + unsigned index = page_stack_index(page); + stack_state.stack[index].next_refs = stack_state.first_free[type - 1]; + stack_state.first_free[type - 1] = index; + stack_state.free_count[type - 1]++; +} + +static kc_phys_addr stack_pop(enum page_alloc_flags type) +{ + int pop_stack_index = -1; + int pop_stack_last = -1; + + // pops only make sense for pages with a stack type + if ((type < PAGE_ALLOC_LOW) || (type > PAGE_ALLOC_HIGH)) + { + return 0; + } + + // do allocations in a high-to-low order if we're allocating any + if (type == PAGE_ALLOC_ANY) + { + pop_stack_index = PAGE_ALLOC_HIGH; + pop_stack_last = PAGE_ALLOC_LOW; + } + else + { + pop_stack_index = type; + pop_stack_last = type; + } + + for (; pop_stack_last <= pop_stack_index; --pop_stack_index) + { + if (stack_state.first_free[pop_stack_index - 1] > 0) + { + unsigned index = stack_state.first_free[pop_stack_index-1]; + stack_state.first_free[pop_stack_index-1] = + stack_state.stack[index].next_refs; + page_stack_set_allocated(page_stack_address(index)); + stack_state.free_count[pop_stack_index - 1]--; + return page_stack_address(index); + } + } + + return 0; +} + diff --git a/kc/core/memory/page_stack.h b/kc/core/memory/page_stack.h new file mode 100644 index 0000000..92d7222 --- /dev/null +++ b/kc/core/memory/page_stack.h @@ -0,0 +1,18 @@ +#pragma once + +#include "memory.h" + +void page_stack_init(void); + +kc_phys_addr page_stack_alloc(enum page_alloc_flags type); +void page_stack_free(kc_phys_addr page); + +int page_stack_get_present(kc_phys_addr page); +void page_stack_set_present(kc_phys_addr page); + +void page_stack_set_allocated(kc_phys_addr page); +void page_stack_set_free(kc_phys_addr page); + +int page_stack_inc_ref(kc_phys_addr page); +int page_stack_dec_ref(kc_phys_addr page); + diff --git a/kc/core/task.c b/kc/core/task.c index f9a8fc8..e43dbe0 100644 --- a/kc/core/task.c +++ b/kc/core/task.c @@ -73,7 +73,7 @@ noreturn void task_init(void) // XXX: unfuck this mess at some point // XXX: make this also not demand-allocated or it's gonna fail - char *kernel_rsp0 = vm_alloc(4096); + char *kernel_rsp0 = vm_alloc(4096, VM_ALLOC_ANY); kernel_rsp0[1] = 0; *get_tss_rsp0() = (uintptr_t)kernel_rsp0 + 4096; @@ -169,7 +169,7 @@ void update_time(void) static struct kc_thread *create_thread(void (*thread_f)(void)) { - char *task_bottom = vm_alloc(16384); + char *task_bottom = vm_alloc(16384, VM_ALLOC_ANY); struct kc_thread *thread = (struct kc_thread *)(task_bottom + 16384 - sizeof(*thread)); diff --git a/kc/core/vm_object.h b/kc/core/vm_object.h index d1de145..c8e93f8 100644 --- a/kc/core/vm_object.h +++ b/kc/core/vm_object.h @@ -1,6 +1,11 @@ #pragma once -typedef int (*vm_object_handler_func)(uint32_t code, void *address); +#include "vm_tree.h" + +typedef int (*vm_object_handler_func)( + struct vm_tree_node *node, + uint32_t code, + void *address); enum vm_object_type { @@ -8,8 +13,6 @@ enum vm_object_type DIRECT_VM_OBJECT, // a 1:1 physical-to-virtual mapping TRANSLATION_VM_OBJECT, // a mapping between a physical and virtual address range ANONYMOUS_VM_OBJECT, // a mapping that has no definite physical location - KC_IMAGE_VM_OBJECT, // a kernel component image - KC_HEAP_VM_OBJECT, // a kernel component heap // TODO: more to come }; diff --git a/kc/core/vm_tree.c b/kc/core/vm_tree.c index 7218d90..cca32bb 100644 --- a/kc/core/vm_tree.c +++ b/kc/core/vm_tree.c @@ -28,10 +28,11 @@ // The full text of the CC-BY-SA 3.0 license can be found here: // https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License -#include <stddef.h> -#include <stdint.h> - #include "vm_tree.h" +#include "panic.h" +#include "kprint.h" + +#include <lib.h> #define assert(expr) @@ -66,6 +67,52 @@ static int compare_key(struct vm_tree_key const * const k1, return compare(k1->address, k1->size, k2->address, k2->size); } +void vmt_init_node( + struct vm_tree *tree, + struct vm_tree_node *node, + struct vm_object *object, + void *base, + void *head) +{ + memset(node, 0, sizeof(*node)); + node->key = + (struct vm_tree_key) + { + (uintptr_t)base, + (uintptr_t)head - (uintptr_t)base + }; + + struct vm_tree_node *ek = NULL; + if (!(ek = vmt_search_key(tree, &node->key))) + { + struct vm_tree_node *p = vmn_predecessor_key( + tree->root, + &node->key); + if (!p) + { + // the tree root should be in place of a missing predecessor + p = tree->root; + } + vmt_insert( + tree, + node, + p, + vmn_child_direction(node, p)); + node->object = object; + } + else + { + kprintf("fatal: attempt to insert overlapping vm node\n" + "node 1: %p: %p @ %zu bytes\n" + "node 2: %p: %p @ %zu bytes\n", + node->key.address, node->key.size, + ek->key.address, ek->key.size); + + PANIC(GENERAL_PANIC); + } + +} + static struct vm_tree_node* RotateDirRoot( struct vm_tree* T, // red–black tree struct vm_tree_node* P, // root of subtree (may be the root of T) @@ -236,7 +283,7 @@ enum vm_tree_direction vmn_child_direction( struct vm_tree_node *p ) { - if (p && compare_key(&p->key, &n->key) > 0) + if (p && (compare_key(&p->key, &n->key) > 0)) { return LEFT; } diff --git a/kc/core/vm_tree.h b/kc/core/vm_tree.h index b2a64e2..a1d40c4 100644 --- a/kc/core/vm_tree.h +++ b/kc/core/vm_tree.h @@ -30,6 +30,9 @@ // The full text of the CC-BY-SA 3.0 license can be found here: // https://en.wikipedia.org/wiki/Wikipedia:Text_of_Creative_Commons_Attribution-ShareAlike_3.0_Unported_License +#include <stdint.h> +#include <stddef.h> + enum vm_tree_direction { LEFT, @@ -77,6 +80,13 @@ enum vm_tree_direction vmn_child_direction( struct vm_tree_node *n, struct vm_tree_node *p); +void vmt_init_node( + struct vm_tree *tree, + struct vm_tree_node *node, + struct vm_object *object, + void *base, + void *head); + void vmt_delete(struct vm_tree*, struct vm_tree_node*); struct vm_tree_node *vmt_search_key(struct vm_tree *, struct vm_tree_key *); |
