From f0cc2c73b78d5a87fae0e0822f18b8b49725c181 Mon Sep 17 00:00:00 2001 From: Rick Rongen Date: Fri, 6 Aug 2021 20:46:44 +0200 Subject: [PATCH] feat: initial paging setup --- CMakeLists.txt | 2 +- boot/boot.S | 24 +++++--- boot/bootpaging.S | 51 ++++++++++++++++ include/myke/mem/paging.h | 8 --- include/myke/mem/pm.h | 19 ++++++ include/myke/mem/pmm.h | 19 ------ include/myke/mem/vmm.h | 13 +++++ kernel/mem/malloc.c | 6 +- kernel/mem/mem.c | 4 +- kernel/mem/paging.c | 59 ------------------- kernel/mem/{pmm.c => pm.c} | 18 ++++-- kernel/mem/vmm.S | 6 ++ kernel/mem/vmm.c | 116 +++++++++++++++++++++++++++++++++++++ kernel/tasks/task.c | 4 +- linker.ld | 22 +++++-- 15 files changed, 257 insertions(+), 114 deletions(-) create mode 100644 boot/bootpaging.S delete mode 100644 include/myke/mem/paging.h create mode 100644 include/myke/mem/pm.h delete mode 100644 include/myke/mem/pmm.h create mode 100644 include/myke/mem/vmm.h delete mode 100644 kernel/mem/paging.c rename kernel/mem/{pmm.c => pm.c} (86%) create mode 100644 kernel/mem/vmm.S create mode 100644 kernel/mem/vmm.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a12ff8..691b2f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,7 @@ set(CMAKE_C_STANDARD 99) FILE(GLOB_RECURSE kernel_src kernel/**.c) FILE(GLOB_RECURSE kernel_asm kernel/**.S) -FILE(GLOB_RECURSE boot_asm boot/boot.S) +FILE(GLOB_RECURSE boot_asm boot/boot.S boot/bootpaging.S) add_compile_definitions(__kernel__) diff --git a/boot/boot.S b/boot/boot.S index 276dee5..83b2f9f 100644 --- a/boot/boot.S +++ b/boot/boot.S @@ -19,7 +19,7 @@ forced to be within the first 8 KiB of the kernel file. .long CHECKSUM #.include "gdt.S" -.section .data +.section .boot.data, "aw", @progbits gdt: .quad 0x0000000000000000 .quad 0x00CF9A000000FFFF @@ -42,7 +42,7 @@ System V ABI standard and de-facto extensions. The compiler will assume the stack is properly aligned and failure to align the stack will result in undefined behavior. */ -.section .bss +.section .boot.bss, "aw", @nobits .align 16 stack_bottom: .skip 16384 # 16 KiB @@ -53,7 +53,10 @@ The linker script specifies _start as the entry point to the kernel and the bootloader will jump to this position once the kernel has been loaded. It doesn't make sense to return from this function as the bootloader is gone. */ -.section .text +.section .boot.text, "ax", @progbits + +.extern _boot_paging + .global _start .type _start, @function _start: @@ -77,6 +80,8 @@ _start: */ mov $stack_top, %esp + call _boot_paging + // store multiboot on stack pushl 0 pushl %eax // MB code @@ -101,6 +106,13 @@ _start: movw %ax,%gs ljmp $0x8, $continue_boot +/* +Set the size of the _start symbol to the current location '.' minus its start. +This is useful when debugging or when you implement call tracing. +*/ +.size _start, . - _start + +.section .text continue_boot: sti /* @@ -129,9 +141,3 @@ continue_boot: cli 1: #hlt jmp 1b - -/* -Set the size of the _start symbol to the current location '.' minus its start. -This is useful when debugging or when you implement call tracing. -*/ -.size _start, . - _start diff --git a/boot/bootpaging.S b/boot/bootpaging.S new file mode 100644 index 0000000..569ebaf --- /dev/null +++ b/boot/bootpaging.S @@ -0,0 +1,51 @@ +.code32 +.extern boot_paging_directory +.extern boot_primary_table + +.section .boot.text, "ax", @progbits +.global _boot_paging +.type _boot_paging, @function +_boot_paging: + push %ebp + mov %esp, %ebp + pusha + # Physical address of paging table 1 + # movl $(boot_primary_table - 0xC0000000), %edi + movl $(boot_primary_table - 0xC0000000), %edi + movl $0, %esi + movl $1024, %ecx + cmpl %esi, %ecx +_boot_paging_loop: + # stop when after kernel's last address + # cmpl $(_kernel_end -0xC0000000), %esi + cmpl $(_kernel_end), %esi + jge _boot_paging_after_kernel + + # make address present and writable + movl %esi, %edx + orl $0x003, %edx + movl %edx, (%edi) + + # next address + addl $4096, %esi + addl $4, %edi + loop _boot_paging_loop + +_boot_paging_after_kernel: + # write to directory + movl $(boot_primary_table - 0xC0000000 + 0x003), boot_paging_directory - 0xC0000000 + 0 + movl $(boot_primary_table - 0xC0000000 + 0x003), boot_paging_directory - 0xC0000000 + 768 * 4 + + # write paging directory to cr3 + movl $(boot_paging_directory - 0xC0000000), %ecx + movl %ecx, %cr3 + + # enable paging and write protect in cr0 + movl %cr0, %ecx + orl $0x80010000, %ecx + movl %ecx, %cr0 + +_boot_pagin_end: + popa + pop %ebp + ret \ No newline at end of file diff --git a/include/myke/mem/paging.h b/include/myke/mem/paging.h deleted file mode 100644 index 1a5f3a6..0000000 --- a/include/myke/mem/paging.h +++ /dev/null @@ -1,8 +0,0 @@ -// -// Created by rick on 21-02-21. -// - -#ifndef NEW_KERNEL_PAGING_H -#define NEW_KERNEL_PAGING_H - -#endif //NEW_KERNEL_PAGING_H diff --git a/include/myke/mem/pm.h b/include/myke/mem/pm.h new file mode 100644 index 0000000..8d8e8bd --- /dev/null +++ b/include/myke/mem/pm.h @@ -0,0 +1,19 @@ +// +// Created by rick on 23-02-21. +// + +#ifndef NEW_KERNEL_PM_H +#define NEW_KERNEL_PM_H + +#include + +// 4k blocks +#define PAGE_SIZE 4096 + +void pm_init(void *start_addr, size_t size); + +void *pm_get_pages(uint32_t num_pages); + +void pm_free_pages(void *page, uint32_t num_pages); + +#endif //NEW_KERNEL_PM_H diff --git a/include/myke/mem/pmm.h b/include/myke/mem/pmm.h deleted file mode 100644 index b5643ff..0000000 --- a/include/myke/mem/pmm.h +++ /dev/null @@ -1,19 +0,0 @@ -// -// Created by rick on 23-02-21. -// - -#ifndef NEW_KERNEL_PMM_H -#define NEW_KERNEL_PMM_H - -#include - -// 4k blocks -#define PAGE_SIZE 4096 - -void pmm_init(void *start_addr, size_t size); - -void *pmm_get_pages(uint32_t num_pages); - -void pmm_free_pages(void *page, uint32_t num_pages); - -#endif //NEW_KERNEL_PMM_H diff --git a/include/myke/mem/vmm.h b/include/myke/mem/vmm.h new file mode 100644 index 0000000..c4cbd71 --- /dev/null +++ b/include/myke/mem/vmm.h @@ -0,0 +1,13 @@ +// +// Created by rick on 21-02-21. +// + +#ifndef NEW_KERNEL_VMM_H +#define NEW_KERNEL_VMM_H +#include + +void vmm_assign_page(void *paddr, void *vaddr, size_t number_of_pages); + +void vmm_unassign_page(void *vaddr, size_t number_of_pages); + +#endif //NEW_KERNEL_VMM_H diff --git a/kernel/mem/malloc.c b/kernel/mem/malloc.c index d59caaa..5ad56fb 100644 --- a/kernel/mem/malloc.c +++ b/kernel/mem/malloc.c @@ -6,7 +6,7 @@ #include #include -#include +#include // retrieved from https://github.com/blanham/liballoc @@ -41,7 +41,7 @@ int liballoc_unlock() { * \return A pointer to the allocated memory. */ void *liballoc_alloc(size_t size) { - return pmm_get_pages(size); + return pm_get_pages(size); } /** This frees previously allocated memory. The void* parameter passed @@ -53,7 +53,7 @@ void *liballoc_alloc(size_t size) { * \return 0 if the memory was successfully freed. */ int liballoc_free(void *addr, size_t size) { - pmm_free_pages(addr, size); + pm_free_pages(addr, size); return 0; } diff --git a/kernel/mem/mem.c b/kernel/mem/mem.c index 7a6fcbc..8a194ea 100644 --- a/kernel/mem/mem.c +++ b/kernel/mem/mem.c @@ -8,7 +8,7 @@ #include #include -#include +#include #define MEMMAP_ENTRIES 16 @@ -84,7 +84,7 @@ void mmap_init_multiboot(struct multiboot_mmap_entry *entries, uint32_t count) { continue; // skip for now } - pmm_init((void *) entry->address, entry->length); + pm_init((void *) entry->address, entry->length); entry->type = MMAP_TYPE_PAGING; } } diff --git a/kernel/mem/paging.c b/kernel/mem/paging.c deleted file mode 100644 index fecd022..0000000 --- a/kernel/mem/paging.c +++ /dev/null @@ -1,59 +0,0 @@ -// -// Created by rick on 21-02-21. -// - -#include -#include -#include - -#include - -#define TABLE_ADDR_MASK 0xFFFFF000 -#define DIRECTORY_SIZE 1024 - -const uint32_t x = TABLE_ADDR_MASK; -typedef struct { - union { - struct { - bool present: 1; - bool read_write: 1; - bool user_mode: 1; - bool write_through: 1; - bool cache_disabled: 1; - bool accessed: 1; - char ignored: 1; - bool page_size: 1; - bool global: 1; // ignored - uint8_t avail: 3; - } att_packed; - uint32_t addr; - }; -} att_packed page_directory_entry; - -typedef struct { - union { - struct { - bool present: 1; - bool read_write: 1; - bool user_supervisor: 1; - bool write_through: 1; - bool cache_disabled: 1; - bool accessed: 1; - bool dirty: 1; - char ignored: 1; - bool global: 1; - uint8_t available: 3; - } att_packed; - uint32_t addr; - }; -} att_packed page_table_entry; - -page_directory_entry page_directory[DIRECTORY_SIZE] att_aligned(4096); - -void page_pre_init() { - for (int i = 0; i < DIRECTORY_SIZE; ++i) { - page_directory[i].read_write = true; - page_directory[i].user_mode = false; - page_directory[i].present = false; - } -} \ No newline at end of file diff --git a/kernel/mem/pmm.c b/kernel/mem/pm.c similarity index 86% rename from kernel/mem/pmm.c rename to kernel/mem/pm.c index 97061bd..a92169b 100644 --- a/kernel/mem/pmm.c +++ b/kernel/mem/pm.c @@ -6,7 +6,8 @@ #include #include -#include +#include +#include #define NUM_PAGING_INFOS 16 @@ -39,7 +40,7 @@ typedef struct { uint8_t last_paging_info = 0; paging_info paging_infos[NUM_PAGING_INFOS]; -void pmm_init(void *start_addr, size_t size) { +void pm_init(void *start_addr, size_t size) { uint32_t num_pages = size / PAGE_SIZE; uint32_t bitmap_size = num_pages / 8; uint32_t bitmap_pages = bitmap_size / PAGE_SIZE; @@ -48,6 +49,10 @@ void pmm_init(void *start_addr, size_t size) { paging_infos[last_paging_info].size = size; paging_infos[last_paging_info].flags.present = true; + // map into memory + vmm_assign_page(start_addr, start_addr, bitmap_pages); + + // set state memset(start_addr, BLOCK_FREE, bitmap_pages); for (uint32_t i = 0; i < bitmap_pages; ++i) { bitmap_set((uint8_t *) start_addr, i); @@ -55,7 +60,7 @@ void pmm_init(void *start_addr, size_t size) { last_paging_info++; } -void *pmm_get_pages(uint32_t num_pages) { +void *pm_get_pages(uint32_t num_pages) { for (int i = 0; i < last_paging_info; ++i) { paging_info *info = &paging_infos[i]; if (!info->flags.present) continue; @@ -75,7 +80,9 @@ void *pmm_get_pages(uint32_t num_pages) { for (k = j; k < j + num_pages; ++k) { bitmap_set(info->page_bitmap, k); } - return page_to_addr(info->first_page, j); + void *paddr = page_to_addr(info->first_page, j); + vmm_assign_page(paddr, paddr, num_pages); + return paddr; } } } @@ -83,7 +90,7 @@ void *pmm_get_pages(uint32_t num_pages) { return NULL; } -void pmm_free_pages(void *page, uint32_t num_pages) { +void pm_free_pages(void *page, uint32_t num_pages) { for (int i = 0; i < last_paging_info; ++i) { paging_info *info = &paging_infos[i]; if (!info->flags.present) continue; @@ -95,5 +102,6 @@ void pmm_free_pages(void *page, uint32_t num_pages) { for (uint32_t current_page = first_page; current_page < first_page + num_pages; ++current_page) { bitmap_unset(info->page_bitmap, current_page); } + vmm_unassign_page(page, num_pages); } } diff --git a/kernel/mem/vmm.S b/kernel/mem/vmm.S new file mode 100644 index 0000000..5083f72 --- /dev/null +++ b/kernel/mem/vmm.S @@ -0,0 +1,6 @@ +.code32 +.section .text +.global vmm_get_current_page_directory +vmm_get_current_page_directory: + movl %cr3, %eax + ret \ No newline at end of file diff --git a/kernel/mem/vmm.c b/kernel/mem/vmm.c new file mode 100644 index 0000000..0bdd160 --- /dev/null +++ b/kernel/mem/vmm.c @@ -0,0 +1,116 @@ +// +// Created by rick on 21-02-21. +// + +#include +#include +#include + +#include +#include +#include + +#define TABLE_ADDR_MASK 0xFFFFF000 +#define TABLE_ADDR_IMASK ~TABLE_ADDR_MASK +#define DIRECTORY_SIZE 1024 + +const uint32_t x = TABLE_ADDR_MASK; +typedef struct { + union { + struct { + bool present: 1; + bool read_write: 1; + bool user_mode: 1; + bool write_through: 1; + bool cache_disabled: 1; + bool accessed: 1; + char: 1; + bool page_size: 1; + bool global: 1; // ignored + uint8_t avail: 3; + } att_packed; + uint32_t addr; + }; +} att_packed page_directory_entry; + +typedef struct { + union { + struct { + bool present: 1; + bool read_write: 1; + bool user_supervisor: 1; + bool write_through: 1; + bool cache_disabled: 1; + bool accessed: 1; + bool dirty: 1; + char: 1; + bool global: 1; + uint8_t available: 3; + } att_packed; + uint32_t addr; + }; +} att_packed page_table_entry; + +//#define in_dir(dir, vaddr) ((page_table_entry*)(((page_directory_entry*)((dir)[(vaddr)/4096/1024].addr & TABLE_ADDR_MASK))[(vaddr)/4096%1024])) + +#define dir_index(vaddr) (((uint32_t)(vaddr)) / PAGE_SIZE / DIRECTORY_SIZE) +#define page_index(vaddr) (((uint32_t)(vaddr)) / PAGE_SIZE % DIRECTORY_SIZE) + +page_directory_entry boot_paging_directory[DIRECTORY_SIZE] att_aligned(4096); +page_table_entry boot_primary_table[DIRECTORY_SIZE] att_aligned(4096); + +extern page_directory_entry *vmm_get_current_page_directory(); + +void vmm_assign_page_one(uint32_t paddr, uint32_t vaddr) { + page_directory_entry *curr = vmm_get_current_page_directory(); + + page_directory_entry *dentry = &curr[dir_index(vaddr)]; + if (!dentry->present || (dentry->addr & TABLE_ADDR_MASK) == 0) { + // todo allocate table + k_panics("need to allocate new table"); + } + + page_table_entry *first_entry = (page_table_entry *) (dentry->addr & TABLE_ADDR_MASK); + page_table_entry *pentry = &first_entry[page_index(vaddr)]; + if (pentry->present) { + // todo already present + k_panics("virtual address already in use"); + } + + pentry->addr = (pentry->addr & TABLE_ADDR_IMASK) | paddr; + pentry->present = true; + pentry->read_write = true; +} + +void vmm_unassign_page_one(uint32_t vaddr) { + page_directory_entry *curr = vmm_get_current_page_directory(); + + page_directory_entry *dentry = &curr[dir_index(vaddr)]; + if (!dentry->present || (dentry->addr & TABLE_ADDR_MASK) == 0) { + // todo allocate table + k_panics("need to allocate new table"); + } + + page_table_entry *first_entry = (page_table_entry *) (dentry->addr & TABLE_ADDR_MASK); + page_table_entry *pentry = &first_entry[page_index(vaddr)]; + if (!pentry->present) { + // todo already present + k_panics("virtual address not in use"); + } + + pentry->present = false; + pentry->read_write = false; + pentry->addr = pentry->addr & TABLE_ADDR_IMASK; +} + +void vmm_assign_page(void *paddr, void *vaddr, size_t number_of_pages) { + for (size_t i = 0; i < number_of_pages; ++i) { + vmm_assign_page_one(((uint32_t) paddr) + PAGE_SIZE * i, ((uint32_t) vaddr) + PAGE_SIZE * i); + } +} + +void vmm_unassign_page(void *vaddr, size_t number_of_pages) { + for (size_t i = 0; i < number_of_pages; ++i) { + vmm_unassign_page_one(((uint32_t) vaddr) + PAGE_SIZE * i); + } +} diff --git a/kernel/tasks/task.c b/kernel/tasks/task.c index 0f86613..59aa4b9 100644 --- a/kernel/tasks/task.c +++ b/kernel/tasks/task.c @@ -9,7 +9,7 @@ #include #include -#include +#include #include #define stack_end(task) ((task)->stack + ((task)->stack_page_count * PAGE_SIZE)) @@ -204,7 +204,7 @@ task_t *task_create(task_entrypoint entrypoint, void *entry_data) { new_task->tid = last_tid++; new_task->state = TASK_STATE_RUNNABLE; - new_task->stack = pmm_get_pages(16); + new_task->stack = pm_get_pages(16); new_task->stack_page_count = 16; // todo check for null diff --git a/linker.ld b/linker.ld index c8a9eca..1b91007 100644 --- a/linker.ld +++ b/linker.ld @@ -10,24 +10,34 @@ SECTIONS loaded at by the bootloader. */ . = 1M; _kernel_start = .; + .boot.data ALIGN(4K) : { + *(.multiboot) + *(.boot.data) + } + .boot.text ALIGN(4K) : { + *(.boot.text) + } + .boot.bss ALIGN(4K) : { + *(.boot.bss) + } + . += 0xC0000000; /* First put the multiboot header, as it is required to be put very early early in the image or the bootloader won't recognize the file format. Next we'll put the .text section. */ - .text BLOCK(4K) : ALIGN(4K) + .text ALIGN(4K) : AT(ADDR(.text)-0xC0000000) { - *(.multiboot) *(.text) } /* Read-only data. */ - .rodata BLOCK(4K) : ALIGN(4K) + .rodata ALIGN(4K) : AT(ADDR(.rodata)-0xC0000000) { *(.rodata) } /* Read-write data (initialized) */ - .data BLOCK(4K) : ALIGN(4K) + .data ALIGN(4K) : AT(ADDR(.data)-0xC0000000) { *(.data) . = ALIGN(16); @@ -41,12 +51,12 @@ SECTIONS } /* Read-write data (uninitialized) and stack */ - .bss BLOCK(4K) : ALIGN(4K) + .bss ALIGN(4K) : AT(ADDR(.bss)-0xC0000000) { *(COMMON) *(.bss) } - _kernel_end = .; + _kernel_end = . - 0xC0000000; /DISCARD/ : { *(.eh_frame);