summaryrefslogtreecommitdiff
path: root/kc/core/memory.c
diff options
context:
space:
mode:
Diffstat (limited to 'kc/core/memory.c')
-rw-r--r--kc/core/memory.c641
1 files changed, 0 insertions, 641 deletions
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);
-}
-