From c36e1b5e5e069ce1735dcc2a94aa45ba72ea2441 Mon Sep 17 00:00:00 2001 From: Richard Qin Date: Tue, 9 Dec 2025 00:57:38 +0800 Subject: feat: Initial PMM implementation Introduce the foundational Physical Memory Manager (PMM) and restructure the project for modularity. Implemented basic PMM and related memory management headers (mm/pmm.h, mm/vm.h). Updated Makefile and build process to support new modular structure. --- LICENSE | 21 ++++++++++ Makefile | 74 ++++++++++++++-------------------- README.md | 0 build/kernel.elf | Bin 6616 -> 19104 bytes build/src/boot/entry.d | 1 - build/src/boot/entry.o | Bin 2024 -> 1656 bytes build/src/drivers/uart.d | 2 - build/src/drivers/uart.o | Bin 2688 -> 2688 bytes build/src/kernel/main.d | 1 - build/src/kernel/main.o | Bin 3344 -> 7752 bytes build/src/kernel/mm/pmm.o | Bin 0 -> 5128 bytes build/src/kernel/mm/vm.o | Bin 0 -> 5472 bytes include/kernel/mm.h | 21 ++++++++++ include/kernel/pmm.h | 13 ++++++ kernel.cc | 69 -------------------------------- link.ld | 39 ++++++++++++++++++ linker.ld | 47 ---------------------- src/boot/entry.S | 46 +++++++++++++-------- src/kernel/main.cc | 56 ++++++++++++++++++++++++-- src/kernel/mm/pmm.cc | 71 ++++++++++++++++++++++++++++++++ src/kernel/mm/vm.cc | 100 ++++++++++++++++++++++++++++++++++++++++++++++ 21 files changed, 376 insertions(+), 185 deletions(-) create mode 100644 LICENSE create mode 100644 README.md delete mode 100644 build/src/boot/entry.d delete mode 100644 build/src/drivers/uart.d delete mode 100644 build/src/kernel/main.d create mode 100644 build/src/kernel/mm/pmm.o create mode 100644 build/src/kernel/mm/vm.o create mode 100644 include/kernel/mm.h create mode 100644 include/kernel/pmm.h delete mode 100644 kernel.cc create mode 100644 link.ld delete mode 100644 linker.ld create mode 100644 src/kernel/mm/pmm.cc create mode 100644 src/kernel/mm/vm.cc diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..f7d2b7c --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Richard Qin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/Makefile b/Makefile index 4d80143..23a8620 100644 --- a/Makefile +++ b/Makefile @@ -1,61 +1,45 @@ -TOOLCHAIN := riscv64-linux-gnu -CC := $(TOOLCHAIN)-gcc -CXX := $(TOOLCHAIN)-g++ -LD := $(TOOLCHAIN)-ld -OBJCOPY := $(TOOLCHAIN)-objcopy +TOOLCHAIN_PREFIX := riscv64-linux-gnu- +CC := $(TOOLCHAIN_PREFIX)gcc +CXX := $(TOOLCHAIN_PREFIX)g++ +LD := $(TOOLCHAIN_PREFIX)ld +OBJCOPY := $(TOOLCHAIN_PREFIX)objcopy -TARGET_ELF := build/kernel.elf -TARGET_BIN := build/kernel.bin +BUILD_DIR := build +SRC_DIR := src -ARCH_FLAGS := -march=rv64gc -mabi=lp64 -mcmodel=medany -CFLAGS := -Wall -Wextra -O2 -ffreestanding -nostdlib -Iinclude $(ARCH_FLAGS) -CXXFLAGS := $(CFLAGS) -fno-exceptions -fno-rtti -fno-use-cxa-atexit +CFLAGS := -Wall -Wextra -O2 -ffreestanding -nostdlib -mcmodel=medany -Iinclude +CXXFLAGS := $(CFLAGS) -fno-exceptions -fno-rtti -fno-use-cxa-atexit -SRC_DIRS := src -SRCS_ASM := $(shell find $(SRC_DIRS) -name "*.S") -SRCS_CXX := $(shell find $(SRC_DIRS) -name "*.cc") +LINKER_SCRIPT := link.ld -OBJS := $(SRCS_ASM:%.S=build/%.o) $(SRCS_CXX:%.cc=build/%.o) +SRCS_S := $(shell find $(SRC_DIR) -name "*.S") +SRCS_CXX := $(shell find $(SRC_DIR) -name "*.cc") +OBJS := $(SRCS_S:%.S=$(BUILD_DIR)/%.o) $(SRCS_CXX:%.cc=$(BUILD_DIR)/%.o) -DEPS := $(OBJS:.o=.d) +TARGET := $(BUILD_DIR)/kernel.elf +.PHONY: all clean run -LINKER_SCRIPT := linker.ld +all: $(TARGET) -QEMU := qemu-system-riscv64 -QEMU_FLAGS := -machine virt -m 128M -nographic -bios none -kernel $(TARGET_ELF) - - -.PHONY: all clean run debug directories - -all: directories $(TARGET_ELF) - -$(TARGET_ELF): $(OBJS) $(LINKER_SCRIPT) - @echo "Linking $@" +$(TARGET): $(OBJS) $(LINKER_SCRIPT) + @echo " LD $@" @$(LD) -T $(LINKER_SCRIPT) -o $@ $(OBJS) -build/%.o: %.S +$(BUILD_DIR)/%.o: %.S @mkdir -p $(dir $@) - @echo "Compiling ASM $<" - @$(CC) $(CFLAGS) -MMD -c $< -o $@ + @echo " AS $<" + @$(CC) $(CFLAGS) -c $< -o $@ -build/%.o: %.cc +$(BUILD_DIR)/%.o: %.cc @mkdir -p $(dir $@) - @echo "Compiling C++ $<" - @$(CXX) $(CXXFLAGS) -MMD -c $< -o $@ + @echo " CXX $<" + @$(CXX) $(CXXFLAGS) -c $< -o $@ -directories: - @mkdir -p build - -run: all - @echo "Starting QEMU..." - $(QEMU) $(QEMU_FLAGS) - -debug: all - @echo "Starting QEMU in debug mode (connect with gdb-multiarch)..." - $(QEMU) $(QEMU_FLAGS) -S -s +run: $(TARGET) + @echo " QEMU Running..." + @qemu-system-riscv64 -machine virt -nographic -bios none \ + -kernel $(TARGET) -m 128M clean: - @rm -rf build - --include $(DEPS) \ No newline at end of file + rm -rf $(BUILD_DIR) \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/build/kernel.elf b/build/kernel.elf index e57b2af..6e2868c 100755 Binary files a/build/kernel.elf and b/build/kernel.elf differ diff --git a/build/src/boot/entry.d b/build/src/boot/entry.d deleted file mode 100644 index db71800..0000000 --- a/build/src/boot/entry.d +++ /dev/null @@ -1 +0,0 @@ -build/src/boot/entry.o: src/boot/entry.S diff --git a/build/src/boot/entry.o b/build/src/boot/entry.o index 5244030..586b6e4 100644 Binary files a/build/src/boot/entry.o and b/build/src/boot/entry.o differ diff --git a/build/src/drivers/uart.d b/build/src/drivers/uart.d deleted file mode 100644 index e6ff9b6..0000000 --- a/build/src/drivers/uart.d +++ /dev/null @@ -1,2 +0,0 @@ -build/src/drivers/uart.o: src/drivers/uart.cc include/drivers/uart.h \ - include/common/types.h diff --git a/build/src/drivers/uart.o b/build/src/drivers/uart.o index 9283f15..6f45d67 100644 Binary files a/build/src/drivers/uart.o and b/build/src/drivers/uart.o differ diff --git a/build/src/kernel/main.d b/build/src/kernel/main.d deleted file mode 100644 index a31a83b..0000000 --- a/build/src/kernel/main.d +++ /dev/null @@ -1 +0,0 @@ -build/src/kernel/main.o: src/kernel/main.cc include/drivers/uart.h diff --git a/build/src/kernel/main.o b/build/src/kernel/main.o index bf89688..5188b34 100644 Binary files a/build/src/kernel/main.o and b/build/src/kernel/main.o differ diff --git a/build/src/kernel/mm/pmm.o b/build/src/kernel/mm/pmm.o new file mode 100644 index 0000000..7065343 Binary files /dev/null and b/build/src/kernel/mm/pmm.o differ diff --git a/build/src/kernel/mm/vm.o b/build/src/kernel/mm/vm.o new file mode 100644 index 0000000..5a94eec Binary files /dev/null and b/build/src/kernel/mm/vm.o differ diff --git a/include/kernel/mm.h b/include/kernel/mm.h new file mode 100644 index 0000000..0f7c73d --- /dev/null +++ b/include/kernel/mm.h @@ -0,0 +1,21 @@ +#pragma once +#include "common/types.h" + +namespace VM +{ + typedef uint64_t* pagetable_t; + typedef uint64_t pte_t; + typedef uint64_t uint64; + + #define PTE_V (1L << 0) + #define PTE_R (1L << 1) + #define PTE_W (1L << 2) + #define PTE_X (1L << 3) + #define PTE_U (1L << 4) + + #define SATP_SV39 (8L << 60) + #define MAKE_SATP(pagetable) (SATP_SV39 | (((uint64)pagetable) >> 12)) + + void kvminit(); + void kvminithart(); +} \ No newline at end of file diff --git a/include/kernel/pmm.h b/include/kernel/pmm.h new file mode 100644 index 0000000..3328450 --- /dev/null +++ b/include/kernel/pmm.h @@ -0,0 +1,13 @@ +#pragma once +#include "common/types.h" + +#define PHYSTOP (0x80000000 + 128 * 1024 * 1024) +#define PGSIZE 4096 + +namespace PMM{ + void init(); + + void* alloc_page(); + + void free_page(void* pa); +} \ No newline at end of file diff --git a/kernel.cc b/kernel.cc deleted file mode 100644 index f9cacba..0000000 --- a/kernel.cc +++ /dev/null @@ -1,69 +0,0 @@ -extern "C" void kernel_main(); - -#define UART_ADDR 0x10000000UL - -#define THR_OFFSET 0x0 -#define LSR_OFFSET 0x5 -#define FCR_OFFSET 0x2 -#define LCR_OFFSET 0x3 -#define DLL_OFFSET 0x0 -#define DLH_OFFSET 0x1 - -static void -write_byte(unsigned long addr, char byte) -{ - *((volatile char *)addr) = byte; -} - -static int -is_tx_empty() -{ - return *((volatile char *)(UART_ADDR + LSR_OFFSET)) & (1 << 5); -} - -void uart_init() -{ - write_byte(UART_ADDR + LCR_OFFSET, 0x80); - - write_byte(UART_ADDR + DLL_OFFSET, 0x01); - write_byte(UART_ADDR + DLH_OFFSET, 0x00); - - write_byte(UART_ADDR + LCR_OFFSET, 0x03); - - write_byte(UART_ADDR + FCR_OFFSET, 0x07); -} - -void put_char(char c) -{ - if (c == '\n') - { - put_char('\r'); - } - - while (!is_tx_empty()) - { - } - write_byte(UART_ADDR + THR_OFFSET, c); -} - -void print_str(const char *s) -{ - while (*s != '\0') - { - put_char(*s++); - } -} - -void kernel_main() -{ - uart_init(); - - print_str("Modern DOS 1.0\n"); - print_str("Hello OS! Running on RISC-V 64.\n"); - print_str("Hello Ziyi Yang! Love from RUC"); - - while (1) - { - - } -} \ No newline at end of file diff --git a/link.ld b/link.ld new file mode 100644 index 0000000..239d244 --- /dev/null +++ b/link.ld @@ -0,0 +1,39 @@ +OUTPUT_ARCH( "riscv" ) +ENTRY(_start) + +SECTIONS +{ + . = 0x80000000; + + .text : { + PROVIDE(text_start = .); + *(.text.entry) + *(.text .text.*) + . = ALIGN(4096); + PROVIDE(text_end = .); + } + + .rodata : { + PROVIDE(rodata_start = .); + *(.rodata .rodata.*) + . = ALIGN(4096); + PROVIDE(rodata_end = .); + } + + .data : { + PROVIDE(data_start = .); + *(.data .data.*) + . = ALIGN(4096); + PROVIDE(data_end = .); + } + + .bss : { + PROVIDE(bss_start = .); + *(.bss .bss.*) + *(.sbss .sbss.*) + . = ALIGN(4096); + PROVIDE(bss_end = .); + } + + PROVIDE(kernel_end = .); +} \ No newline at end of file diff --git a/linker.ld b/linker.ld deleted file mode 100644 index 0b01e98..0000000 --- a/linker.ld +++ /dev/null @@ -1,47 +0,0 @@ -OUTPUT_ARCH( "riscv" ) -ENTRY(_start) - -MEMORY -{ - ram (wxa) : ORIGIN = 0x80000000, LENGTH = 128M -} - -SECTIONS -{ - . = 0x80000000; - - .text : { - PROVIDE(_text_start = .); - *(.text.entry) - *(.text .text.*) - PROVIDE(_text_end = .); - } > ram - - .rodata : { - . = ALIGN(16); - PROVIDE(_rodata_start = .); - *(.rodata .rodata.*) - PROVIDE(_rodata_end = .); - } > ram - - .data : { - . = ALIGN(16); - PROVIDE(_data_start = .); - *(.data .data.*) - *(.sdata .sdata.*) - PROVIDE(_data_end = .); - } > ram - - .bss : { - . = ALIGN(16); - PROVIDE(_bss_start = .); - *(.bss .bss.*) - *(.sbss .sbss.*) - PROVIDE(_bss_end = .); - } > ram - - . = ALIGN(4096); - PROVIDE(_stack_top = . + 0x8000); - . = . + 0x8000; - PROVIDE(_heap_start = .); -} \ No newline at end of file diff --git a/src/boot/entry.S b/src/boot/entry.S index 1a6f6b8..0ca3b29 100644 --- a/src/boot/entry.S +++ b/src/boot/entry.S @@ -1,28 +1,40 @@ .section .text.entry -.globl _start +.global _start _start: csrr t0, mhartid - bnez t0, .L_park_harts + bnez t0, .L_park - la sp, _stack_top + li t0, 0x1fffffffffffff + csrw pmpaddr0, t0 + li t0, 0xf + csrw pmpcfg0, t0 - la a0, _bss_start - la a1, _bss_end - bge a0, a1, .L_call_main + li t0, 0 + li t1, (1 << 11) | (1 << 7) + csrw mstatus, t1 -.L_clear_bss: - sd zero, (a0) - addi a0, a0, 8 - blt a0, a1, .L_clear_bss + csrw satp, zero -.L_call_main: - call kernel_main + li t0, 0xffff + csrw medeleg, t0 + csrw mideleg, t0 + + la t0, kernel_main + csrw mepc, t0 -.L_infinite_loop: - wfi - j .L_infinite_loop + la sp, stack_top + + mret -.L_park_harts: +.L_park: wfi - j .L_park_harts \ No newline at end of file + j .L_park + + +.section .bss +.align 4 +.global stack_top +stack_base: + .space 4096 * 4 +stack_top: \ No newline at end of file diff --git a/src/kernel/main.cc b/src/kernel/main.cc index 49adf2b..beda9dc 100644 --- a/src/kernel/main.cc +++ b/src/kernel/main.cc @@ -1,17 +1,67 @@ #include "drivers/uart.h" +#include "kernel/pmm.h" +#include "kernel/mm.h" + +void print_hex(uint64_t val) +{ + char buf[17]; + buf[16] = 0; + const char *digits = "0123456789ABCDEF"; + for (int i = 15; i >= 0; i--) + { + buf[i] = digits[val & 0xf]; + val >>= 4; + } + Drivers::uart_puts("0x"); + Drivers::uart_puts(buf); +} extern "C" void kernel_main() { Drivers::uart_init(); + Drivers::uart_puts("\n[Modern DOS Kernel] Booted in S-Mode.\n"); + Drivers::uart_puts("2025 Richard Qin\n"); + Drivers::uart_puts("\n"); + + Drivers::uart_puts("[Kernel] Initializing PMM...\n"); + PMM::init(); + Drivers::uart_puts("[Kernel] PMM initialized.\n"); + Drivers::uart_puts("[Kernel] Testing alloc_page...\n"); + void *p1 = PMM::alloc_page(); + void *p2 = PMM::alloc_page(); + + Drivers::uart_puts("Allocated Page 1: "); + print_hex((uint64_t)p1); Drivers::uart_puts("\n"); - Drivers::uart_puts("Modern DOS v1.0 [RISC-V 64]\n"); - Drivers::uart_puts("2025 Richard Qin\n"); + + Drivers::uart_puts("Allocated Page 2: "); + print_hex((uint64_t)p2); Drivers::uart_puts("\n"); - Drivers::uart_puts("A:\\> "); + Drivers::uart_puts("[Kernel] Freeing Page 1...\n"); + PMM::free_page(p1); + + void *p3 = PMM::alloc_page(); + Drivers::uart_puts("Allocated Page 3: "); + print_hex((uint64_t)p3); + Drivers::uart_puts("\n"); + + if (p3 == p1) + { + Drivers::uart_puts("[Test Passed] Reuse freed memory successfully.\n"); + } + else + { + Drivers::uart_puts("[Test Failed] Memory leak or logic error.\n"); + } + + VM::kvminit(); + Drivers::uart_puts("[Boot] Kernel page table created.\n"); + VM::kvminithart(); + Drivers::uart_puts("[Boot] MMU ENABLED! We are now using virtual addresses.\n"); while (1) { diff --git a/src/kernel/mm/pmm.cc b/src/kernel/mm/pmm.cc new file mode 100644 index 0000000..9b68fcd --- /dev/null +++ b/src/kernel/mm/pmm.cc @@ -0,0 +1,71 @@ +#include "kernel/pmm.h" +#include "drivers/uart.h" + +extern "C" char kernel_end[]; + +namespace PMM +{ + struct Run + { + struct Run *next; + }; + + struct + { + struct Run *freelist; + } kmem; + + inline uint64_t PGROUNDUP(uint64_t a) + { + return (a + PGSIZE - 1) & ~(PGSIZE - 1); + } + + void init() + { + uint64_t start = PGROUNDUP((uint64_t)kernel_end); + + Drivers::uart_puts("PMM: Initializing from"); + + for (uint64_t p = start; p + PGSIZE <= PHYSTOP; p += PGSIZE) + { + free_page((void *)p); + } + } + + void free_page(void* pa) + { + struct Run *r; + if (((uint64_t)pa % PGSIZE) != 0 || (uint64_t)pa < (uint64_t)kernel_end || (uint64_t)pa >= PHYSTOP) + { + Drivers::uart_puts("PMM: Error - Invalid free address\n"); + return; + } + r = (struct Run*)pa; + + r->next = kmem.freelist; + kmem.freelist = r; + + } + + void* alloc_page() + { + struct Run* r; + + r = kmem.freelist; + if (r) + { + kmem.freelist = r->next; + + uint64_t *p = (uint64_t *)r; + for (int i = 0; i < PGSIZE / 8; i++) + { + p[i] = 0; + } + } + else + { + Drivers::uart_puts("PMM: Out of memory!\n"); + } + return (void *)r; + } +} \ No newline at end of file diff --git a/src/kernel/mm/vm.cc b/src/kernel/mm/vm.cc new file mode 100644 index 0000000..3c14d40 --- /dev/null +++ b/src/kernel/mm/vm.cc @@ -0,0 +1,100 @@ +#include "kernel/mm.h" +#include "kernel/pmm.h" +#include "drivers/uart.h" +#include "common/types.h" + +extern "C" char text_start[]; +extern "C" char text_end[]; +extern "C" char rodata_start[]; +extern "C" char rodata_end[]; +extern "C" char data_start[]; +extern "C" char kernel_end[]; + +namespace VM +{ + pagetable_t kernel_pagetable; + + #define PX(level, va) ((((uint64)va) >> (12 + 9 * (level))) & 0x1FF) + #define PA2PTE(pa) ((((uint64)pa) >> 12) << 10) + #define PTE2PA(pte) (((pte) >> 10) << 12) + + pte_t *walk(pagetable_t pagetable, uint64 va, int alloc) + { + for (int level = 2; level > 0; level--) + { + pte_t *pte = &pagetable[PX(level, va)]; + + if (*pte & PTE_V) + { + pagetable = (pagetable_t)PTE2PA(*pte); + } + else + { + if (!alloc) + return 0; + + pagetable = (pagetable_t)PMM::alloc_page(); + if (!pagetable) + return 0; + + *pte = PA2PTE(pagetable) | PTE_V; + } + } + return &pagetable[PX(0, va)]; + } + + int mappages(pagetable_t pagetable, uint64 va, uint64 size, uint64 pa, int perm) + { + uint64 a, last; + pte_t *pte; + + a = (va / PGSIZE) * PGSIZE; + last = ((va + size - 1) / PGSIZE) * PGSIZE; + + for(;;) + { + if((pte = walk(pagetable, a, 1)) == 0) + return -1; + + if (*pte & PTE_V) + { + Drivers::uart_puts("Remap error\n"); + return -1; + } + + *pte = PA2PTE(pa) | perm | PTE_V; + + if (a == last) + break; + a += PGSIZE; + pa += PGSIZE; + } + return 0; + } + + void kvminit() + { + kernel_pagetable = (pagetable_t)PMM::alloc_page(); + + mappages(kernel_pagetable, 0x10000000, 0x10000, 0x10000000, PTE_R | PTE_W); + + mappages(kernel_pagetable, (uint64)text_start, (uint64)text_end - (uint64)text_start, + (uint64)text_start, PTE_R | PTE_X); + + mappages(kernel_pagetable, (uint64)data_start, PHYSTOP - (uint64)data_start, + (uint64)data_start, PTE_R | PTE_W); + + mappages(kernel_pagetable, (uint64)rodata_start, (uint64)rodata_end - (uint64)rodata_start, + (uint64)rodata_start, PTE_R); + + } + void kvminithart() + { + asm volatile("sfence.vma zero, zero"); + + uint64 satp_val = MAKE_SATP(kernel_pagetable); + asm volatile("csrw satp, %0" : : "r"(satp_val)); + + asm volatile("sfence.vma zero, zero"); + } +} \ No newline at end of file -- cgit v1.2.3