From 1ecc2feb01a9f6391dec47a1243c1eab409dd33f Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Sun, 26 May 2024 23:07:13 +0800 Subject: [PATCH 01/14] feat: kernel with mmu enabled & executed in kernel space --- Cargo.toml | 2 +- kernel/linker.ld | 5 ++- kernel/src/exception/handlers/svc.rs | 3 +- kernel/src/kernel.S | 64 ++++++++++++++++++++++++---- kernel/src/kernel.rs | 7 ++- kernel/src/main.rs | 2 +- stdio/src/lib.rs | 8 ++-- 7 files changed, 72 insertions(+), 19 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 79a799ce4..d86c26dca 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ resolver = "2" [profile.release] debug = false -strip = true +strip = false opt-level = 'z' codegen-units = 1 lto = true diff --git a/kernel/linker.ld b/kernel/linker.ld index 44f5ac13f..6cc2cba1a 100644 --- a/kernel/linker.ld +++ b/kernel/linker.ld @@ -1,9 +1,10 @@ ENTRY(_start) SECTIONS { - . = 0x80000; + . = 0xffff000000000000; + . += 0x80000; .text : { - KEEP(*(.text._start_kernel)) + KEEP(*(.text._start_section)) } .rodata : { *(.rodata*) diff --git a/kernel/src/exception/handlers/svc.rs b/kernel/src/exception/handlers/svc.rs index 9f16cc217..254e1897c 100644 --- a/kernel/src/exception/handlers/svc.rs +++ b/kernel/src/exception/handlers/svc.rs @@ -87,10 +87,11 @@ unsafe fn el1_interrupt(sp: u64) { unsafe fn syscall_handler(sp: u64) { let syscall = Syscall::new(sp); + println!("Syscall {:?}", syscall); assert!(trap_frame::TRAP_FRAME.is_some()); match syscall.idx { 0 => { - println!("Syscall get_pid"); + // println!("Syscall get_pid"); let pid = crate::syscall::get_pid(); trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = pid; } diff --git a/kernel/src/kernel.S b/kernel/src/kernel.S index fae5769a3..a7c0f7a87 100644 --- a/kernel/src/kernel.S +++ b/kernel/src/kernel.S @@ -1,8 +1,33 @@ -.section ".text._start_kernel" +.section ".text._start_section" .global _start +.equ TCR_CONFIG_REGION_48bit, (((64 - 48) << 0) | ((64 - 48) << 16)); +.equ TCR_CONFIG_4KB, ((0x0 << 14) | (0x2 << 30)); +.equ TCR_CONFIG_DEFAULT, (TCR_CONFIG_REGION_48bit | TCR_CONFIG_4KB); + +.equ MAIR_DEVICE_nGnRnE, 0b00000000 +.equ MAIR_NORMAL_NOCACHE, 0b01000100 +.equ MAIR_IDX_DEVICE_nGnRnE, 0 +.equ MAIR_IDX_NORMAL_NOCACHE, 1 +.equ MAIR_CONFIG_DEFAULT, (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) + +.equ PD_TABLE, 0b11 +.equ PD_BLOCK, 0b01 +.equ PD_ACCESS, (1 << 10) +.equ BOOT_PGD_ATTR, PD_TABLE +.equ BOOT_PUD_ATTR, (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) + _start: + mrs x0, CurrentEL + and x0, x0, #0xc + cmp x0, #0b1000 + bne _loop + + bl from_el2_to_el1 + bl set_mmu + + // set stack before our code ldr x1, =_start mov sp, x1 @@ -14,15 +39,7 @@ _start: str xzr, [x1], #8 sub w2, w2, #1 cbnz w2, 3b - _start_kernel: - mrs x0, CurrentEL - and x0, x0, #0xc - cmp x0, #0b1000 - bne _loop - - bl from_el2_to_el1 - adr x0, exception_vector_table msr vbar_el1, x0 @@ -38,5 +55,34 @@ from_el2_to_el1: msr sp_el1, x0 eret // return to EL1 +set_mmu: + ldr x0, =TCR_CONFIG_DEFAULT + msr tcr_el1, x0 + + ldr x0, =MAIR_CONFIG_DEFAULT + msr mair_el1, x0 + + mov x0, 0x0000 // PGD's page frame at 0x0 + mov x1, 0x1000 // PUD's page frame at 0x1000 + + ldr x2, =BOOT_PGD_ATTR + orr x2, x1, x2 // combine the physical address of next level page with attribute. + str x2, [x0] + + ldr x2, =BOOT_PUD_ATTR + mov x3, 0x00000000 + orr x3, x2, x3 + str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD + mov x3, 0x40000000 + orr x3, x2, x3 + str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD + + msr ttbr0_el1, x0 // load PGD to the bottom translation-based register. + + mrs x2, sctlr_el1 + orr x2 , x2, 1 + msr sctlr_el1, x2 // enable MMU, cache remains disabled + ret + _loop: b _loop diff --git a/kernel/src/kernel.rs b/kernel/src/kernel.rs index 5a5a21eca..9115936b9 100644 --- a/kernel/src/kernel.rs +++ b/kernel/src/kernel.rs @@ -1,4 +1,4 @@ -use core::arch::global_asm; +use core::arch::{asm, global_asm}; use driver::uart::init; use stdio::println; @@ -8,5 +8,10 @@ global_asm!(include_str!("kernel.S")); extern "C" fn _start_rust() { init(); println!("Kernel starting main..."); + let sp: u64; + unsafe { + asm!("mov {}, sp", out(reg) sp); + } + println!("Stack pointer: {:#x}", sp); crate::main(); } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 3b068b9bc..8b8ab4fd6 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -25,7 +25,7 @@ pub static mut INITRAMFS_ADDR: u32 = 0; fn main() -> ! { boot(); println!("Kernel booted successfully!"); - commands::execute(b"exec syscall.img"); + // commands::execute(b"exec syscall.img"); kernel_shell(); } diff --git a/stdio/src/lib.rs b/stdio/src/lib.rs index f260f2458..6955d5680 100644 --- a/stdio/src/lib.rs +++ b/stdio/src/lib.rs @@ -4,8 +4,8 @@ pub mod macros; use driver::uart; pub fn send(c: u8) { - // uart::send(c); - uart::send_async(c); + uart::send(c); + // uart::send_async(c); } fn _recv() -> u8 { @@ -17,8 +17,8 @@ fn _recv() -> u8 { } pub fn recv() -> u8 { - // let c = uart::recv(); - let c = _recv(); + let c = uart::recv(); + // let c = _recv(); match c { b'\r' | b'\n' => { write(b"\r\n"); From 9211b8a8af59366e48b794141ea045ca6119354e Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Mon, 27 May 2024 01:01:04 +0800 Subject: [PATCH 02/14] fix: skip reserved memory block (0x0000~0x1000) --- Makefile | 1 + initramfs/rprog.img | Bin 1108 -> 532 bytes initramfs/vm.img | Bin 0 -> 247488 bytes kernel/src/allocator/buddy.rs | 1 - kernel/src/exception/handlers/svc.rs | 14 ++++++++-- kernel/src/kernel.S | 11 ++++++-- kernel/src/main.rs | 7 +++++ program/src/main.rs | 40 +++++++-------------------- 8 files changed, 38 insertions(+), 36 deletions(-) create mode 100644 initramfs/vm.img diff --git a/Makefile b/Makefile index cc046ba80..05217e0f0 100644 --- a/Makefile +++ b/Makefile @@ -92,6 +92,7 @@ run: all debug: all size $(CPROG) $(RPROG) $(OBJDUMP) -D $(KERNEL_ELF) > $(BUILD_DIR)/kernel.S $(OBJDUMP) -D $(BOOTLOADER_ELF) > $(BUILD_DIR)/bootloader.S + $(OBJDUMP) -D $(RPROG_ELF) > $(BUILD_DIR)/rprog.S debug-qemu: $(QEMU) -M raspi3b \ diff --git a/initramfs/rprog.img b/initramfs/rprog.img index 3e1f5042ebbc4bc52d6060e4f383db6b1d97d7bf..89fdeb0e7089718625cce37a76f023c1ed8e6ac1 100755 GIT binary patch delta 257 zcmcb@F@?qH|6zuU|NNO&zF?NuVq{>r@|l@omAnG;)dpS$fkuXgAX^58DL}bZFO?Z4 z{{PRgaxx>s7I{a8j}2@LAz6G5lZyXMwqurJygWIQS=+>cks-t*Gt=ThF~dYfpk_^0 zhKnzX87w`3Y>?shtPC3&|NjqB0P-IK4HX9(KAD3_QD2$C@&V8Sn7S1}`3FFGke+}3 zPAh?IEucdu9$=2YrVrBN!?;EE^8f$S{{wM=r;9D4G_$n;0|Nt41B1!G|Nk{6^RkF5 ZNC0^t%mNhQ0ZDoA8BAuH#{y)50RX)lQDXoA delta 803 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
LaIRxxGkGu2Nc^r*p2nQQ3R%~CR5{JU3A)Ai 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 Y|NVQD{zsGk@BjU0zWVPb{hucNe;je&3jhEB literal 0 HcmV?d00001 diff --git a/kernel/src/allocator/buddy.rs b/kernel/src/allocator/buddy.rs index db8b79ffa..8138def24 100644 --- a/kernel/src/allocator/buddy.rs +++ b/kernel/src/allocator/buddy.rs @@ -79,7 +79,6 @@ impl BuddyAllocator { } } println!("Free list: {:?}", BUDDY_SYSTEM.free_list); - BUDDY_SYSTEM.initialized = true; } pub unsafe fn print_info(&self) { diff --git a/kernel/src/exception/handlers/svc.rs b/kernel/src/exception/handlers/svc.rs index 254e1897c..a7b99b16c 100644 --- a/kernel/src/exception/handlers/svc.rs +++ b/kernel/src/exception/handlers/svc.rs @@ -1,9 +1,9 @@ use crate::exception::trap_frame; -use core::arch::asm; +use core::{arch::asm, fmt::Debug}; use stdio::{debug, println}; #[repr(C)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy)] struct Syscall { arg0: u64, arg1: u64, @@ -42,6 +42,16 @@ impl Syscall { } } +impl Debug for Syscall { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + write!( + f, + "Syscall {{ idx: 0x{:x}, arg0: 0x{:x}, arg1: 0x{:x}, arg2: 0x{:x}, arg3: 0x{:x}, arg4: 0x{:x}, arg5: 0x{:x}, arg6: 0x{:x}, arg7: 0x{:x} }}", + self.idx, self.arg0, self.arg1, self.arg2, self.arg3, self.arg4, self.arg5, self.arg6, self.arg7 + ) + } +} + #[no_mangle] unsafe fn svc_handler(eidx: u64, sp: u64) { trap_frame::TRAP_FRAME = Some(trap_frame::TrapFrame::new(sp)); diff --git a/kernel/src/kernel.S b/kernel/src/kernel.S index a7c0f7a87..17654ad16 100644 --- a/kernel/src/kernel.S +++ b/kernel/src/kernel.S @@ -10,7 +10,9 @@ .equ MAIR_NORMAL_NOCACHE, 0b01000100 .equ MAIR_IDX_DEVICE_nGnRnE, 0 .equ MAIR_IDX_NORMAL_NOCACHE, 1 -.equ MAIR_CONFIG_DEFAULT, (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) | (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) +.equ MAIR_CONFIG_PART1, (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) +.equ MAIR_CONFIG_PART2, (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) +.equ MAIR_CONFIG_DEFAULT, (MAIR_CONFIG_PART1 | MAIR_CONFIG_PART2) .equ PD_TABLE, 0b11 .equ PD_BLOCK, 0b01 @@ -62,8 +64,8 @@ set_mmu: ldr x0, =MAIR_CONFIG_DEFAULT msr mair_el1, x0 - mov x0, 0x0000 // PGD's page frame at 0x0 - mov x1, 0x1000 // PUD's page frame at 0x1000 + mov x0, 0x1000 // PGD's page frame at 0x0 + mov x1, 0x2000 // PUD's page frame at 0x1000 ldr x2, =BOOT_PGD_ATTR orr x2, x1, x2 // combine the physical address of next level page with attribute. @@ -78,10 +80,13 @@ set_mmu: str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD msr ttbr0_el1, x0 // load PGD to the bottom translation-based register. + msr ttbr1_el1, x0 // load PGD to the bottom translation-based register. + isb mrs x2, sctlr_el1 orr x2 , x2, 1 msr sctlr_el1, x2 // enable MMU, cache remains disabled + isb ret _loop: diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 8b8ab4fd6..da39a64b9 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -74,9 +74,16 @@ fn buddy_init() { unsafe { BUDDY_SYSTEM.print_info(); } + unsafe { BUDDY_SYSTEM.initialized = true } + // allocator::utils::toggle_dynamic_verbose(); } fn buddy_reserve_memory() { + unsafe { + BUDDY_SYSTEM.reserve_by_addr_range(0x1000, 0x1_0000); + // BUDDY_SYSTEM.reserve_by_addr_range(0x2000, 0x3000); + } + let rsv_mem = dtb::get_reserved_memory(); for (addr, size) in rsv_mem { unsafe { diff --git a/program/src/main.rs b/program/src/main.rs index 07b67ef37..fda068ea2 100644 --- a/program/src/main.rs +++ b/program/src/main.rs @@ -28,11 +28,20 @@ fn delay(n: u64) { } #[start] fn main(_: isize, _: *const *const u8) -> isize { - thread_test(); + basic_test(); syscall::exit(0); return 0; } +#[allow(dead_code)] +fn basic_test() { + // println("Hello, world!"); + let pid = syscall::get_pid(); + print("PID="); + print_hex(pid); + println(""); +} + #[allow(dead_code)] fn mailbox_test() { println("Printing mailbox info..."); @@ -165,35 +174,6 @@ fn fork_test() { } } -// void fork_test(){ -// printf("\nFork Test, pid %d\n", get_pid()); -// int cnt = 1; -// int ret = 0; -// if ((ret = fork()) == 0) { // child -// long long cur_sp; -// asm volatile("mov %0, sp" : "=r"(cur_sp)); -// printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); -// ++cnt; - -// if ((ret = fork()) != 0){ -// asm volatile("mov %0, sp" : "=r"(cur_sp)); -// printf("first child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); -// } -// else{ -// while (cnt < 5) { -// asm volatile("mov %0, sp" : "=r"(cur_sp)); -// printf("second child pid: %d, cnt: %d, ptr: %x, sp : %x\n", get_pid(), cnt, &cnt, cur_sp); -// delay(1000000); -// ++cnt; -// } -// } -// exit(); -// } -// else { -// printf("parent here, pid %d, child %d\n", get_pid(), ret); -// } -// } - #[repr(C, align(16))] pub struct MailBox { buffer: [u32; 36], From 705c13da516bd7ebccdb349750f14eeca8cce185 Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Mon, 27 May 2024 01:49:13 +0800 Subject: [PATCH 03/14] fix: kernel shell strip command --- kernel/src/commands/exec.rs | 9 +++++++-- kernel/src/commands/mod.rs | 25 +++++++++++++++---------- kernel/src/kernel.S | 4 ++-- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/kernel/src/commands/exec.rs b/kernel/src/commands/exec.rs index 97493426b..3f2505a74 100644 --- a/kernel/src/commands/exec.rs +++ b/kernel/src/commands/exec.rs @@ -20,6 +20,11 @@ pub fn exec(args: Vec) { ); } } - assert!(!scheduler::get().ready_queue.is_empty()); - scheduler::get().run_threads(); + + if scheduler::get().ready_queue.is_empty() { + println!("No threads to run!"); + return; + } else { + scheduler::get().run_threads(); + } } diff --git a/kernel/src/commands/mod.rs b/kernel/src/commands/mod.rs index 427f48392..1f64c06f6 100644 --- a/kernel/src/commands/mod.rs +++ b/kernel/src/commands/mod.rs @@ -14,30 +14,35 @@ use alloc::string::ToString; use alloc::vec::Vec; pub fn execute(command: &[u8]) { + let command = match command.iter().position(|&c| c == 0) { + Some(i) => &command[..i], + None => command, + }; let args: Vec = core::str::from_utf8(command) .unwrap() .split_whitespace() .map(|s| s.to_string()) .collect(); - if command.starts_with(b"\x00") { + println!("Executing command: {:?}", args); + if args.is_empty() { return; - } else if command.starts_with(b"hello") { + } else if args[0] == "hello" { hello::exec(); - } else if command.starts_with(b"help") { + } else if args[0] == "help" { help::exec(); - } else if command.starts_with(b"reboot") { + } else if args[0] == "reboot" { reboot::exec(); - } else if command.starts_with(b"ls") { + } else if args[0] == "ls" { ls::exec(); - } else if command.starts_with(b"cat") { + } else if args[0] == "cat" { cat::exec(&command); - } else if command.starts_with(b"exec") { + } else if args[0] == "exec" { exec::exec(args); - } else if command.starts_with(b"echo") { + } else if args[0] == "echo" { echo::exec(&command); - } else if command.starts_with(b"setTimeOut") { + } else if args[0] == "setTimeOut" { set_time_out::exec(&command); - } else if command.starts_with(b"buddy") { + } else if args[0] == "buddy" { buddy::exec(); } else { println!( diff --git a/kernel/src/kernel.S b/kernel/src/kernel.S index 17654ad16..1cbdaaf67 100644 --- a/kernel/src/kernel.S +++ b/kernel/src/kernel.S @@ -29,7 +29,6 @@ _start: bl from_el2_to_el1 bl set_mmu - // set stack before our code ldr x1, =_start mov sp, x1 @@ -45,7 +44,8 @@ _start_kernel: adr x0, exception_vector_table msr vbar_el1, x0 - b _start_rust + ldr x0, =_start_rust + br x0 from_el2_to_el1: mov x0, (1 << 31) // EL1 uses aarch64 From 69a6539aa70377e8466a4f2808ad2e800948d90e Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Mon, 27 May 2024 02:06:06 +0800 Subject: [PATCH 04/14] fix: use 4KB page frame for both kernel space and user space --- kernel/src/dtb/mod.rs | 4 +++- kernel/src/kernel.S | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/kernel/src/dtb/mod.rs b/kernel/src/dtb/mod.rs index 24e636226..f9dac85cc 100644 --- a/kernel/src/dtb/mod.rs +++ b/kernel/src/dtb/mod.rs @@ -7,6 +7,8 @@ mod utils; use alloc::vec::Vec; use stdio::println; +const DTB_ADDRERSS: u64 = 0x6_f000; + pub fn load_dtb() -> dt::Dt { let (dtb_addr, header) = get_dtb_addr(); let strings_addr = dtb_addr + header.off_dt_strings; @@ -17,7 +19,7 @@ pub fn load_dtb() -> dt::Dt { } pub fn get_dtb_addr() -> (u32, fdt::FdtHeader) { - let dtb_addr = 0x6f000 as *const u8; + let dtb_addr = DTB_ADDRERSS as *const u8; println!("DTB address: {:p}", dtb_addr); let dtb_addr = unsafe { core::ptr::read_volatile(dtb_addr as *const u32) }; println!("DTB address: {:#x}", dtb_addr); diff --git a/kernel/src/kernel.S b/kernel/src/kernel.S index 1cbdaaf67..caa58a435 100644 --- a/kernel/src/kernel.S +++ b/kernel/src/kernel.S @@ -3,7 +3,7 @@ .global _start .equ TCR_CONFIG_REGION_48bit, (((64 - 48) << 0) | ((64 - 48) << 16)); -.equ TCR_CONFIG_4KB, ((0x0 << 14) | (0x2 << 30)); +.equ TCR_CONFIG_4KB, ((0b00 << 14) | (0b00 << 30)); .equ TCR_CONFIG_DEFAULT, (TCR_CONFIG_REGION_48bit | TCR_CONFIG_4KB); .equ MAIR_DEVICE_nGnRnE, 0b00000000 From 21b72fe2c95f978a15a9095c7aef2be746c8d5ee Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Thu, 30 May 2024 16:54:37 +0800 Subject: [PATCH 05/14] feat: enable mmu in rust --- kernel/src/commands/exec.rs | 5 +- kernel/src/exception/handlers/svc.rs | 22 +++++++- kernel/src/kernel.S | 50 ------------------ kernel/src/main.rs | 3 +- kernel/src/mmu.rs | 76 ++++++++++++++++++++++++++++ kernel/src/scheduler.rs | 3 +- 6 files changed, 103 insertions(+), 56 deletions(-) create mode 100644 kernel/src/mmu.rs diff --git a/kernel/src/commands/exec.rs b/kernel/src/commands/exec.rs index 3f2505a74..546bbbb73 100644 --- a/kernel/src/commands/exec.rs +++ b/kernel/src/commands/exec.rs @@ -5,7 +5,7 @@ use alloc::vec::Vec; use filesystem::cpio::CpioArchive; use stdio::println; -pub fn exec(args: Vec) { +pub fn exec(args: Vec) -> ! { println!("Executing exec command with args: {:?}", args); let rootfs = CpioArchive::load(unsafe { INITRAMFS_ADDR } as *const u8); for filename in args.iter().skip(1) { @@ -22,8 +22,7 @@ pub fn exec(args: Vec) { } if scheduler::get().ready_queue.is_empty() { - println!("No threads to run!"); - return; + panic!("No threads to run!"); } else { scheduler::get().run_threads(); } diff --git a/kernel/src/exception/handlers/svc.rs b/kernel/src/exception/handlers/svc.rs index a7b99b16c..44f7713e2 100644 --- a/kernel/src/exception/handlers/svc.rs +++ b/kernel/src/exception/handlers/svc.rs @@ -61,7 +61,27 @@ unsafe fn svc_handler(eidx: u64, sp: u64) { // debug!("Syscall {:?}", syscall); match eidx { 4 => el1_interrupt(sp), - 8 => syscall_handler(sp), + 8 => { + let esr_el1: u64; + let elr_el1: u64; + let current_el: u64; + let far_el1: u64; + asm!( + "mrs {0}, esr_el1", + "mrs {1}, elr_el1", + "mrs {2}, CurrentEL", + "mrs {3}, far_el1", + out(reg) esr_el1, + out(reg) elr_el1, + out(reg) current_el, + out(reg) far_el1, + ); + debug!("ESR_EL1: 0x{:x}", esr_el1); + debug!("ELR_EL1: 0x{:x}", elr_el1); + debug!("CurrentEL: 0x{:x}", current_el); + debug!("FAR_EL1: 0x{:x}", far_el1); + syscall_handler(sp) + } _ => { println!("Exception {}", eidx); println!("Unknown exception"); diff --git a/kernel/src/kernel.S b/kernel/src/kernel.S index caa58a435..2e369ae30 100644 --- a/kernel/src/kernel.S +++ b/kernel/src/kernel.S @@ -2,24 +2,6 @@ .global _start -.equ TCR_CONFIG_REGION_48bit, (((64 - 48) << 0) | ((64 - 48) << 16)); -.equ TCR_CONFIG_4KB, ((0b00 << 14) | (0b00 << 30)); -.equ TCR_CONFIG_DEFAULT, (TCR_CONFIG_REGION_48bit | TCR_CONFIG_4KB); - -.equ MAIR_DEVICE_nGnRnE, 0b00000000 -.equ MAIR_NORMAL_NOCACHE, 0b01000100 -.equ MAIR_IDX_DEVICE_nGnRnE, 0 -.equ MAIR_IDX_NORMAL_NOCACHE, 1 -.equ MAIR_CONFIG_PART1, (MAIR_DEVICE_nGnRnE << (MAIR_IDX_DEVICE_nGnRnE * 8)) -.equ MAIR_CONFIG_PART2, (MAIR_NORMAL_NOCACHE << (MAIR_IDX_NORMAL_NOCACHE * 8)) -.equ MAIR_CONFIG_DEFAULT, (MAIR_CONFIG_PART1 | MAIR_CONFIG_PART2) - -.equ PD_TABLE, 0b11 -.equ PD_BLOCK, 0b01 -.equ PD_ACCESS, (1 << 10) -.equ BOOT_PGD_ATTR, PD_TABLE -.equ BOOT_PUD_ATTR, (PD_ACCESS | (MAIR_IDX_DEVICE_nGnRnE << 2) | PD_BLOCK) - _start: mrs x0, CurrentEL and x0, x0, #0xc @@ -57,37 +39,5 @@ from_el2_to_el1: msr sp_el1, x0 eret // return to EL1 -set_mmu: - ldr x0, =TCR_CONFIG_DEFAULT - msr tcr_el1, x0 - - ldr x0, =MAIR_CONFIG_DEFAULT - msr mair_el1, x0 - - mov x0, 0x1000 // PGD's page frame at 0x0 - mov x1, 0x2000 // PUD's page frame at 0x1000 - - ldr x2, =BOOT_PGD_ATTR - orr x2, x1, x2 // combine the physical address of next level page with attribute. - str x2, [x0] - - ldr x2, =BOOT_PUD_ATTR - mov x3, 0x00000000 - orr x3, x2, x3 - str x3, [x1] // 1st 1GB mapped by the 1st entry of PUD - mov x3, 0x40000000 - orr x3, x2, x3 - str x3, [x1, 8] // 2nd 1GB mapped by the 2nd entry of PUD - - msr ttbr0_el1, x0 // load PGD to the bottom translation-based register. - msr ttbr1_el1, x0 // load PGD to the bottom translation-based register. - isb - - mrs x2, sctlr_el1 - orr x2 , x2, 1 - msr sctlr_el1, x2 // enable MMU, cache remains disabled - isb - ret - _loop: b _loop diff --git a/kernel/src/main.rs b/kernel/src/main.rs index da39a64b9..f419bf91e 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -11,6 +11,7 @@ mod commands; mod dtb; mod exception; mod kernel; +mod mmu; mod panic; mod scheduler; mod syscall; @@ -25,7 +26,7 @@ pub static mut INITRAMFS_ADDR: u32 = 0; fn main() -> ! { boot(); println!("Kernel booted successfully!"); - // commands::execute(b"exec syscall.img"); + commands::execute(b"exec rprog.img"); kernel_shell(); } diff --git a/kernel/src/mmu.rs b/kernel/src/mmu.rs new file mode 100644 index 000000000..a92857405 --- /dev/null +++ b/kernel/src/mmu.rs @@ -0,0 +1,76 @@ +use core::arch::asm; + +const TCR_CONFIG_REGION_48BIT: u64 = (64 - 48) << 0 | (64 - 48) << 16; +const TCR_CONFIG_REGION_4KB: u64 = 0b00 << 14 | 0b00 << 30; +const TCR_CONFIG_DEFAULT: u64 = TCR_CONFIG_REGION_48BIT | TCR_CONFIG_REGION_4KB; + +const MAIR_DEVICE_NG_NR_NE: u8 = 0b00000000; +const MAIR_NORMAL_NC: u8 = 0b01000100; +const MAIR_DEVICE_NG_NR_NE_IDX: u8 = 0; +const MAIR_NORMAL_NC_IDX: u8 = 1; +const MAIR_CONFIG_DEFAULT: u64 = (MAIR_DEVICE_NG_NR_NE as u64) << (MAIR_DEVICE_NG_NR_NE_IDX * 8) + | (MAIR_NORMAL_NC as u64) << (MAIR_NORMAL_NC_IDX * 8); + +const L0_ADDR: u64 = 0x1000; +const L1_ADDR: u64 = 0x2000; +const L2_ADDR: u64 = 0x3000; +const L3_ADDR: u64 = 0x4000; + +const PD_TABLE: u32 = 0b11; +const PD_BLOCK: u32 = 0b01; +const PD_ACCESS: u32 = 1 << 10; + +#[no_mangle] +pub unsafe extern "C" fn set_mmu() { + asm!( + "msr tcr_el1, {0}", + "msr mair_el1, {1}", + in(reg) TCR_CONFIG_DEFAULT, + in(reg) MAIR_CONFIG_DEFAULT, + ); + + // Set up PGD + // 0b0000_0000_AAAA_AAAA_ABBB_BBBB_BBCC_CCCC_CCCD_DDDD_DDDD_XXXX_XXXX_XXXX + // 0000_0000_0 + *(L0_ADDR as *mut u64) = L1_ADDR | PD_TABLE as u64; + + // Set up PUD + // 0b0000_0000_AAAA_AAAA_ABBB_BBBB_BBCC_CCCC_CCCD_DDDD_DDDD_XXXX_XXXX_XXXX + // 000_0000_00 + *(L1_ADDR as *mut u64) = L2_ADDR | PD_TABLE as u64; + // *(L1_ADDR as *mut u64) = + // 0x0000_0000 as u64 | PD_ACCESS as u64 | (MAIR_NORMAL_NC_IDX as u64) << 2 | PD_BLOCK as u64; + // *(L1_ADDR.wrapping_add(8) as *mut u64) = PD_ACCESS as u64 + // | PD_ACCESS as u64 + // | (MAIR_DEVICE_NG_NR_NE_IDX as u64) << 2 + // | PD_BLOCK as u64; + + // Set up PMD + // 0b0000_0000_AAAA_AAAA_ABBB_BBBB_BBCC_CCCC_CCCD_DDDD_DDDD_XXXX_XXXX_XXXX + // 00_0000_000 + for i in (0x0000_0000 / (1 << 12) / (1 << 9))..(0x3C00_0000 / (1 << 12) / (1 << 9)) { + let addr = L2_ADDR + i * 8; + let attr: u64 = PD_ACCESS as u64 | (MAIR_NORMAL_NC_IDX as u64) << 2 | PD_BLOCK as u64; + *(addr as *mut u64) = attr | (i * (1 << 12) * (1 << 9)); + } + for i in (0x3C00_0000 / (1 << 12) / (1 << 9))..(0x4000_0000 / (1 << 12) / (1 << 9)) { + let addr = L2_ADDR + i * 8; + let attr: u64 = PD_ACCESS as u64 | (MAIR_DEVICE_NG_NR_NE_IDX as u64) << 2 | PD_BLOCK as u64; + *(addr as *mut u64) = attr | i * (1 << 12) * (1 << 9); + } + + asm!( + "msr ttbr0_el1, {l0}", + "msr ttbr1_el1, {l0}", + "isb", + l0 = in(reg) L0_ADDR, + ); + + asm!( + "mrs {0}, sctlr_el1", + "orr {0}, {0}, #1", + "msr sctlr_el1, {0}", + "isb", + out(reg) _, + ) +} diff --git a/kernel/src/scheduler.rs b/kernel/src/scheduler.rs index 568919e63..111c35910 100644 --- a/kernel/src/scheduler.rs +++ b/kernel/src/scheduler.rs @@ -93,7 +93,7 @@ impl Scheduler { ); } - pub fn run_threads(&mut self) { + pub fn run_threads(&mut self) -> ! { self.sched_timer(); assert!(self.current.is_none()); assert!(!self.ready_queue.is_empty()); @@ -123,6 +123,7 @@ impl Scheduler { "eret", in(reg) entry, in(reg) sp, + options(noreturn), ); } } From 44ea8e5e7ee503972ae657db180d7f34092e53a5 Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Thu, 30 May 2024 16:59:11 +0800 Subject: [PATCH 06/14] feat: map device memory --- kernel/src/mmu.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/kernel/src/mmu.rs b/kernel/src/mmu.rs index a92857405..dec487b71 100644 --- a/kernel/src/mmu.rs +++ b/kernel/src/mmu.rs @@ -38,12 +38,10 @@ pub unsafe extern "C" fn set_mmu() { // 0b0000_0000_AAAA_AAAA_ABBB_BBBB_BBCC_CCCC_CCCD_DDDD_DDDD_XXXX_XXXX_XXXX // 000_0000_00 *(L1_ADDR as *mut u64) = L2_ADDR | PD_TABLE as u64; - // *(L1_ADDR as *mut u64) = - // 0x0000_0000 as u64 | PD_ACCESS as u64 | (MAIR_NORMAL_NC_IDX as u64) << 2 | PD_BLOCK as u64; - // *(L1_ADDR.wrapping_add(8) as *mut u64) = PD_ACCESS as u64 - // | PD_ACCESS as u64 - // | (MAIR_DEVICE_NG_NR_NE_IDX as u64) << 2 - // | PD_BLOCK as u64; + *(L1_ADDR.wrapping_add(8) as *mut u64) = 0x4000_0000 as u64 + | PD_ACCESS as u64 + | (MAIR_DEVICE_NG_NR_NE_IDX as u64) << 2 + | PD_BLOCK as u64; // Set up PMD // 0b0000_0000_AAAA_AAAA_ABBB_BBBB_BBCC_CCCC_CCCD_DDDD_DDDD_XXXX_XXXX_XXXX From 469f91d53c424885a24eabd86bfafeaa8f019c72 Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Wed, 12 Jun 2024 16:54:19 +0800 Subject: [PATCH 07/14] feat: mmu exec user thread --- kernel/src/commands/exec.rs | 2 +- kernel/src/exception/context_switch.S | 16 ++++- kernel/src/exception/exception_table.S | 6 +- kernel/src/exception/handlers/mod.rs | 1 + kernel/src/exception/handlers/page.rs | 21 ++++++ kernel/src/exception/handlers/svc.rs | 48 ++++++------- kernel/src/exception/mod.rs | 2 +- kernel/src/kernel.S | 4 ++ kernel/src/mmu.rs | 32 ++++++--- kernel/src/mmu/config.rs | 2 + kernel/src/mmu/entry.rs | 62 +++++++++++++++++ kernel/src/mmu/page_table.rs | 93 ++++++++++++++++++++++++++ kernel/src/mmu/vm.rs | 72 ++++++++++++++++++++ kernel/src/scheduler.rs | 30 +++++---- kernel/src/thread.rs | 91 +++++++++++++++++-------- kernel/src/thread/cpu.rs | 16 ++++- 16 files changed, 407 insertions(+), 91 deletions(-) create mode 100644 kernel/src/exception/handlers/page.rs create mode 100644 kernel/src/mmu/config.rs create mode 100644 kernel/src/mmu/entry.rs create mode 100644 kernel/src/mmu/page_table.rs create mode 100644 kernel/src/mmu/vm.rs diff --git a/kernel/src/commands/exec.rs b/kernel/src/commands/exec.rs index 546bbbb73..01e21d1bf 100644 --- a/kernel/src/commands/exec.rs +++ b/kernel/src/commands/exec.rs @@ -12,7 +12,7 @@ pub fn exec(args: Vec) -> ! { let filename = filename.as_bytes(); if let Some(data) = rootfs.get_file(core::str::from_utf8(filename).unwrap()) { let program = scheduler::alloc_prog(data); - scheduler::get().create_thread(program); + scheduler::get().create_thread(program.0, program.1); } else { println!( "File not found: {}", diff --git a/kernel/src/exception/context_switch.S b/kernel/src/exception/context_switch.S index ade2cbd1d..98b8d54cf 100644 --- a/kernel/src/exception/context_switch.S +++ b/kernel/src/exception/context_switch.S @@ -20,17 +20,29 @@ mrs x0, elr_el1 mrs x1, sp_el0 mrs x2, spsr_el1 + mrs x3, ttbr0_el1 stp x0, x1, [sp, 16 * 16] - str x2, [sp, 16 * 17] + stp x2, x3, [sp, 16 * 17] + dsb ish + mov x0, 0x1000 + msr ttbr0_el1, x0 + tlbi vmalle1is + dsb ish + isb .endm // load general registers from stack .macro load_all ldp x0, x1, [sp, 16 * 16] - ldr x2, [sp, 16 * 17] + ldp x2, x3, [sp, 16 * 17] msr elr_el1, x0 msr sp_el0, x1 msr spsr_el1, x2 + dsb ish + msr ttbr0_el1, x3 + tlbi vmalle1is + dsb ish + isb ldp x0, x1, [sp ,16 * 0] ldp x2, x3, [sp ,16 * 1] ldp x4, x5, [sp ,16 * 2] diff --git a/kernel/src/exception/exception_table.S b/kernel/src/exception/exception_table.S index f81138b4c..349c31f72 100644 --- a/kernel/src/exception/exception_table.S +++ b/kernel/src/exception/exception_table.S @@ -21,13 +21,13 @@ exception_vector_table: EXCEPTION_WITH_TYPE 1, unknown_exception_handler .align 7 - EXCEPTION_WITH_TYPE 2, unknown_exception_handler + EXCEPTION_WITH_TYPE 2, lower_exception_handler .align 7 EXCEPTION_WITH_TYPE 3, unknown_exception_handler .align 7 - EXCEPTION_WITH_TYPE 4, svc_handler + EXCEPTION_WITH_TYPE 4, lower_exception_handler .align 7 EXCEPTION_WITH_TYPE 5, irq_handler @@ -39,7 +39,7 @@ exception_vector_table: EXCEPTION_WITH_TYPE 7, unknown_exception_handler .align 7 - EXCEPTION_WITH_TYPE 8, svc_handler + EXCEPTION_WITH_TYPE 8, lower_exception_handler .align 7 EXCEPTION_WITH_TYPE 9, irq_handler diff --git a/kernel/src/exception/handlers/mod.rs b/kernel/src/exception/handlers/mod.rs index bd50bfbe8..35a6ad9fa 100644 --- a/kernel/src/exception/handlers/mod.rs +++ b/kernel/src/exception/handlers/mod.rs @@ -1,2 +1,3 @@ mod irq; +mod page; mod svc; diff --git a/kernel/src/exception/handlers/page.rs b/kernel/src/exception/handlers/page.rs new file mode 100644 index 000000000..b5d8f57d5 --- /dev/null +++ b/kernel/src/exception/handlers/page.rs @@ -0,0 +1,21 @@ +use core::arch::asm; +use stdio::println; + +pub unsafe fn page_fault() { + let esr_el1: u64; + asm!( + "mrs {0}, esr_el1", + out(reg) esr_el1, + ); + let ec = esr_el1 >> 26; + let far_el1: u64; + asm!( + "mrs {0}, far_el1", + out(reg) far_el1, + ); + println!("Page fault"); + println!("Exception Class: 0b{:06b}", ec); + println!("ESR_EL1: 0x{:x}", esr_el1); + println!("FAR_EL1: 0x{:x}", far_el1); + panic!("Page fault"); +} diff --git a/kernel/src/exception/handlers/svc.rs b/kernel/src/exception/handlers/svc.rs index 44f7713e2..b0f2127ba 100644 --- a/kernel/src/exception/handlers/svc.rs +++ b/kernel/src/exception/handlers/svc.rs @@ -1,3 +1,4 @@ +use super::page::page_fault; use crate::exception::trap_frame; use core::{arch::asm, fmt::Debug}; use stdio::{debug, println}; @@ -53,40 +54,31 @@ impl Debug for Syscall { } #[no_mangle] -unsafe fn svc_handler(eidx: u64, sp: u64) { - trap_frame::TRAP_FRAME = Some(trap_frame::TrapFrame::new(sp)); - // let syscall = Syscall::new(sp); - // println!("Exception {}", eidx); - // debug!("Syscall idx: {}", syscall.idx); - // debug!("Syscall {:?}", syscall); - match eidx { - 4 => el1_interrupt(sp), - 8 => { - let esr_el1: u64; - let elr_el1: u64; - let current_el: u64; - let far_el1: u64; - asm!( - "mrs {0}, esr_el1", - "mrs {1}, elr_el1", - "mrs {2}, CurrentEL", - "mrs {3}, far_el1", - out(reg) esr_el1, - out(reg) elr_el1, - out(reg) current_el, - out(reg) far_el1, - ); - debug!("ESR_EL1: 0x{:x}", esr_el1); - debug!("ELR_EL1: 0x{:x}", elr_el1); - debug!("CurrentEL: 0x{:x}", current_el); - debug!("FAR_EL1: 0x{:x}", far_el1); - syscall_handler(sp) +unsafe fn lower_exception_handler(eidx: u64, sp: u64) { + let esr_el1: u64; + asm!( + "mrs {0}, esr_el1", + out(reg) esr_el1, + ); + let ec = esr_el1 >> 26; + match ec { + 0b010101 => svc_handler(sp), + 0b001110 => { + panic!("Illegal Execution state."); } + 0b100000 => page_fault(), _ => { println!("Exception {}", eidx); println!("Unknown exception"); + println!("ec: 0b{:06b}", ec); + el1_interrupt(sp); } } +} + +unsafe fn svc_handler(sp: u64) { + trap_frame::TRAP_FRAME = Some(trap_frame::TrapFrame::new(sp)); + syscall_handler(sp); trap_frame::TRAP_FRAME.unwrap().restore(); trap_frame::TRAP_FRAME = None; } diff --git a/kernel/src/exception/mod.rs b/kernel/src/exception/mod.rs index 48a844d59..75a3153c6 100644 --- a/kernel/src/exception/mod.rs +++ b/kernel/src/exception/mod.rs @@ -20,5 +20,5 @@ unsafe fn unknown_exception_handler(eidx: u64) { ); debug!("ESR_EL1: 0x{:x}", esr_el1); debug!("ELR_EL1: 0x{:x}", elr_el1); - panic!("Unknown exception {}", eidx); + panic!("Unknown exception handler {}", eidx); } diff --git a/kernel/src/kernel.S b/kernel/src/kernel.S index 2e369ae30..bef88f49e 100644 --- a/kernel/src/kernel.S +++ b/kernel/src/kernel.S @@ -11,6 +11,9 @@ _start: bl from_el2_to_el1 bl set_mmu + ldr x0, =_boot_rest + br x0 +_boot_rest: // set stack before our code ldr x1, =_start mov sp, x1 @@ -22,6 +25,7 @@ _start: str xzr, [x1], #8 sub w2, w2, #1 cbnz w2, 3b + _start_kernel: adr x0, exception_vector_table msr vbar_el1, x0 diff --git a/kernel/src/mmu.rs b/kernel/src/mmu.rs index dec487b71..576c88047 100644 --- a/kernel/src/mmu.rs +++ b/kernel/src/mmu.rs @@ -1,8 +1,12 @@ use core::arch::asm; +mod config; +mod entry; +mod page_table; +pub mod vm; const TCR_CONFIG_REGION_48BIT: u64 = (64 - 48) << 0 | (64 - 48) << 16; const TCR_CONFIG_REGION_4KB: u64 = 0b00 << 14 | 0b00 << 30; -const TCR_CONFIG_DEFAULT: u64 = TCR_CONFIG_REGION_48BIT | TCR_CONFIG_REGION_4KB; +const TCR_CONFIG_DEFAULT: u64 = TCR_CONFIG_REGION_48BIT | TCR_CONFIG_REGION_4KB | 0b101u64 << 32; const MAIR_DEVICE_NG_NR_NE: u8 = 0b00000000; const MAIR_NORMAL_NC: u8 = 0b01000100; @@ -14,14 +18,17 @@ const MAIR_CONFIG_DEFAULT: u64 = (MAIR_DEVICE_NG_NR_NE as u64) << (MAIR_DEVICE_N const L0_ADDR: u64 = 0x1000; const L1_ADDR: u64 = 0x2000; const L2_ADDR: u64 = 0x3000; -const L3_ADDR: u64 = 0x4000; const PD_TABLE: u32 = 0b11; const PD_BLOCK: u32 = 0b01; +const PD_PAGE: u32 = 0b11; const PD_ACCESS: u32 = 1 << 10; +const AP_RW_EL0: usize = 0b01 << 6; +const AP_RO_EL0: usize = 0b11 << 6; + #[no_mangle] -pub unsafe extern "C" fn set_mmu() { +unsafe extern "C" fn set_mmu() { asm!( "msr tcr_el1, {0}", "msr mair_el1, {1}", @@ -38,25 +45,26 @@ pub unsafe extern "C" fn set_mmu() { // 0b0000_0000_AAAA_AAAA_ABBB_BBBB_BBCC_CCCC_CCCD_DDDD_DDDD_XXXX_XXXX_XXXX // 000_0000_00 *(L1_ADDR as *mut u64) = L2_ADDR | PD_TABLE as u64; - *(L1_ADDR.wrapping_add(8) as *mut u64) = 0x4000_0000 as u64 - | PD_ACCESS as u64 - | (MAIR_DEVICE_NG_NR_NE_IDX as u64) << 2 - | PD_BLOCK as u64; // Set up PMD // 0b0000_0000_AAAA_AAAA_ABBB_BBBB_BBCC_CCCC_CCCD_DDDD_DDDD_XXXX_XXXX_XXXX // 00_0000_000 - for i in (0x0000_0000 / (1 << 12) / (1 << 9))..(0x3C00_0000 / (1 << 12) / (1 << 9)) { + for i in (0x0000_0000 / (1 << 9) / (1 << 12))..(0x3C00_0000 / (1 << 9) / (1 << 12)) { let addr = L2_ADDR + i * 8; let attr: u64 = PD_ACCESS as u64 | (MAIR_NORMAL_NC_IDX as u64) << 2 | PD_BLOCK as u64; - *(addr as *mut u64) = attr | (i * (1 << 12) * (1 << 9)); + *(addr as *mut u64) = attr | (i * (1 << 9) * (1 << 12)); } - for i in (0x3C00_0000 / (1 << 12) / (1 << 9))..(0x4000_0000 / (1 << 12) / (1 << 9)) { + for i in (0x3C00_0000 / (1 << 9) / (1 << 12))..(0x4000_0000 / (1 << 9) / (1 << 12)) { let addr = L2_ADDR + i * 8; let attr: u64 = PD_ACCESS as u64 | (MAIR_DEVICE_NG_NR_NE_IDX as u64) << 2 | PD_BLOCK as u64; - *(addr as *mut u64) = attr | i * (1 << 12) * (1 << 9); + *(addr as *mut u64) = attr | i * (1 << 9) * (1 << 12); } + *(L1_ADDR.wrapping_add(8) as *mut u64) = 0x4000_0000 as u64 + | PD_ACCESS as u64 + | (MAIR_DEVICE_NG_NR_NE_IDX as u64) << 2 + | PD_BLOCK as u64; + asm!( "msr ttbr0_el1, {l0}", "msr ttbr1_el1, {l0}", @@ -71,4 +79,6 @@ pub unsafe extern "C" fn set_mmu() { "isb", out(reg) _, ) + // 0x00c50838 + // 0b0000_0000_1100_0101_0000_1000_0011_1000 } diff --git a/kernel/src/mmu/config.rs b/kernel/src/mmu/config.rs new file mode 100644 index 000000000..2a4e8bbe0 --- /dev/null +++ b/kernel/src/mmu/config.rs @@ -0,0 +1,2 @@ +// pub const PAGE_SIZE: usize = 4096; +pub const ENTRY_COUNT: usize = 512; diff --git a/kernel/src/mmu/entry.rs b/kernel/src/mmu/entry.rs new file mode 100644 index 000000000..2b229dd97 --- /dev/null +++ b/kernel/src/mmu/entry.rs @@ -0,0 +1,62 @@ +use core::fmt::Debug; + +use super::page_table::PageTable; +use alloc::boxed::Box; +use stdio::print; + +#[derive(Clone)] +pub enum Entry { + None, + PdBlock((*mut u64, u64)), + PdTable(Box), +} + +impl Entry { + pub fn new() -> Self { + Entry::None + } + + pub fn is_valid(&self) -> bool { + match self { + Entry::None => false, + _ => true, + } + } + + pub fn set_addr(&mut self, addr: u32) { + match self { + Entry::PdBlock((saddr, pg)) => { + *pg = (*pg & 0xfff) | (addr as u64); + unsafe { **saddr = *pg } + } + _ => panic!("set_addr: not a PdBlock"), + } + } + + pub fn set_flag(&mut self, flag: u32) { + match self { + Entry::PdBlock((saddr, pg)) => { + *pg = (*pg & !0xfff) | (flag as u64); + unsafe { **saddr = *pg } + } + _ => panic!("set_flag: not a PdBlock"), + } + } + + pub fn get_addr(&self) -> *mut u8 { + match self { + Entry::PdBlock((_, pg)) => (*pg & 0xffff_ffff_f000) as *mut u8, + _ => panic!("get_addr: not a PdBlock"), + } + } +} + +impl Debug for Entry { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Entry::None => write!(f, "None"), + Entry::PdBlock((_, pg)) => write!(f, "PdBlock(0x{:x})", pg), + Entry::PdTable(tbl) => write!(f, "PdTable 0x{:x}", tbl.addr), + } + } +} diff --git a/kernel/src/mmu/page_table.rs b/kernel/src/mmu/page_table.rs new file mode 100644 index 000000000..444141835 --- /dev/null +++ b/kernel/src/mmu/page_table.rs @@ -0,0 +1,93 @@ +use super::config::ENTRY_COUNT; +use super::entry::Entry; +use alloc::alloc::alloc; +use alloc::boxed::Box; +use alloc::vec; +use alloc::vec::Vec; +use core::alloc::Layout; + +use crate::mmu::MAIR_NORMAL_NC_IDX; +use crate::mmu::PD_ACCESS; +use crate::mmu::PD_PAGE; +use crate::mmu::PD_TABLE; + +#[derive(Debug, Clone)] +pub struct PageTable { + entries: Vec, + pub addr: u64, +} + +impl PageTable { + pub fn new() -> Self { + let addr = unsafe { alloc(Layout::from_size_align(0x1000, 0x1000).unwrap()) as u64 }; + unsafe { + let mut p = addr as *mut u8; + for _ in 0..0x1000 { + *p = 0; + p = p.add(1); + } + } + PageTable { + entries: vec![Entry::new(); ENTRY_COUNT], + addr, + } + } + + pub fn get_page(&mut self, addr: u64, level: u64) -> &mut Entry { + let idx = ((addr >> (12 + 9 * (3 - level))) & 0x1ff) as usize; + if level == 3 { + if !self.exists(idx as usize) { + self.set_entry( + idx as usize, + Entry::PdBlock(((self.addr + (idx * 8) as u64) as *mut u64, 0)), + ); + } + return self.get_entry(idx as usize); + } + if !self.exists(idx as usize) { + let pg = PageTable::new(); + self.set_entry(idx as usize, Entry::PdTable(Box::new(pg))); + if let Entry::PdTable(pt) = self.get_entry(idx as usize) { + return pt.get_page(addr, level + 1); + } + panic!("get_page: not a PdTable"); + } + let entry = self.get_entry(idx as usize); + if let Entry::PdTable(pt) = entry { + return pt.get_page(addr, level + 1); + } + panic!("get_page: not a PdTable"); + } + + pub fn get_entry(&mut self, idx: usize) -> &mut Entry { + &mut self.entries[idx] + } + + pub fn set_entry(&mut self, idx: usize, entry: Entry) { + assert!(idx < ENTRY_COUNT); + assert!(self.entries[idx].is_valid() == false); + self.entries[idx] = entry.clone(); + match entry { + Entry::PdTable(pt) => { + let entry_addr = self.addr + (idx * 8) as u64; + unsafe { + let entry = entry_addr as *mut u64; + *entry = + pt.addr | (PD_TABLE | PD_ACCESS | (MAIR_NORMAL_NC_IDX << 2) as u32) as u64; + } + } + Entry::PdBlock((saddr, addr)) => { + assert!(addr == 0); + unsafe { + let entry = saddr as *mut u64; + *entry = addr | (PD_PAGE | PD_ACCESS | (MAIR_NORMAL_NC_IDX << 2) as u32) as u64; + } + } + _ => {} + } + } + + pub fn exists(&self, idx: usize) -> bool { + self.entries[idx].is_valid() + } +} diff --git a/kernel/src/mmu/vm.rs b/kernel/src/mmu/vm.rs new file mode 100644 index 000000000..de7a2e07e --- /dev/null +++ b/kernel/src/mmu/vm.rs @@ -0,0 +1,72 @@ +use super::entry::Entry; +use super::page_table::PageTable; +use alloc::alloc::alloc; +use core::alloc::Layout; +use stdio::println; + +#[derive(Debug, Clone)] +pub struct VirtualMemory { + root: PageTable, +} + +impl VirtualMemory { + pub fn new() -> Self { + VirtualMemory { + root: PageTable::new(), + } + } + + pub fn get_l0_addr(&self) -> *mut u8 { + self.root.addr as *mut u8 + } + + fn get_page(&mut self, addr: u64) -> &mut Entry { + self.root.get_page(addr, 0) + } + + pub fn mmap(&mut self, addr: u64, size: usize, flag: u32) -> *mut u8 { + let flag = flag & 0xfff; + let page = self.get_page(addr); + let mem = + unsafe { alloc(Layout::from_size_align(size as usize, 0x1000).unwrap()) as *mut u8 }; + println!( + "mmap: addr: 0x{:x}, size: 0x{:x}, mem: 0x{:x}", + addr, size, mem as u64 + ); + page.set_addr(mem as u32); + page.set_flag(flag); + return addr as *mut u8; + } + + pub fn get_phys(&mut self, addr: u64) -> *mut u8 { + let page = self.get_page(addr); + page.get_addr() + } + pub fn dump(&self) { + println!("VirtualMemory:"); + println!(" root: 0x{:x}", self.root.addr); + unsafe { dump(self.root.addr as *mut u64, 0) } + } +} + +unsafe fn dump(addr: *mut u64, level: u8) { + println!(" addr: 0x{:x}", addr as u64); + for i in 0..512 { + let p = addr.add(i); + if *p & 0b1 == 1 { + println!(" [{:03}] = 0x{:016x}", i, *p); + } + } + for i in 0..512 { + let p = addr.add(i); + if *p & 0b1 == 1 { + if *p & 0b11 == 0b11 { + if level < 3 { + dump((*p & !0xfff) as *mut u64, level + 1); + } else { + println!(" [{:03}] = 0x{:016x}", i, *p); + } + } + } + } +} diff --git a/kernel/src/scheduler.rs b/kernel/src/scheduler.rs index 111c35910..5842997f2 100644 --- a/kernel/src/scheduler.rs +++ b/kernel/src/scheduler.rs @@ -1,5 +1,7 @@ use crate::exception::trap_frame::TRAP_FRAME; use crate::thread::Thread; +use alloc::alloc::alloc; +use alloc::alloc::Layout; use alloc::boxed::Box; use alloc::collections::VecDeque; use alloc::string::String; @@ -7,6 +9,7 @@ use alloc::vec::Vec; use core::arch::asm; use core::time::Duration; use stdio::println; + pub struct Scheduler { pub current: Option, pub threads: Vec>>, @@ -74,8 +77,9 @@ impl Scheduler { // println!("Switching from {} to {}", current, next); } - pub fn create_thread(&mut self, entry: extern "C" fn()) { - let thread = Box::new(Thread::new(0x2000, entry)); + pub fn create_thread(&mut self, entry: *mut u8, len: usize) { + let thread = Box::new(Thread::new(0x1000, entry, len)); + println!("Creating thread"); let tid = self.add_thread(thread); println!("Created thread {}", tid); self.ready_queue.push_back(tid); @@ -105,11 +109,8 @@ impl Scheduler { assert!(thread.id == next); let entry = thread.entry; let sp = thread.stack as usize + thread.stack_size; + thread.vm.dump(); unsafe { - // uint64_t tmp; - // asm volatile("mrs %0, cntkctl_el1" : "=r"(tmp)); - // tmp |= 1; - // asm volatile("msr cntkctl_el1, %0" : : "r"(tmp)); asm!( "mrs {0}, cntkctl_el1", "orr {0}, {0}, #1", @@ -120,9 +121,15 @@ impl Scheduler { "msr spsr_el1, xzr", "msr elr_el1, {0}", "msr sp_el0, {1}", + "dsb ish", + "msr ttbr0_el1, {2}", + "tlbi vmalle1is", + "dsb ish", + "isb", "eret", in(reg) entry, in(reg) sp, + in(reg) thread.vm.get_l0_addr(), options(noreturn), ); } @@ -134,7 +141,7 @@ impl Scheduler { filesystem::cpio::CpioArchive::load(unsafe { crate::INITRAMFS_ADDR } as *const u8); if let Some(data) = program.get_file(name.as_str()) { let program = alloc_prog(data); - let new_thread = Box::new(Thread::new(0x2000, program)); + let new_thread = Box::new(Thread::new(0x2000, program.0, program.1)); self.threads[current] = Some(new_thread); self.ready_queue.push_back(current); let next = self.restore_next(); @@ -194,10 +201,7 @@ pub fn init() { } } -use alloc::alloc::alloc; -use alloc::alloc::Layout; - -pub fn alloc_prog(data: &[u8]) -> extern "C" fn() { +pub fn alloc_prog(data: &[u8]) -> (*mut u8, usize) { let program_entry = unsafe { alloc(Layout::from_size_align(data.len(), 0x1000).unwrap()) }; unsafe { core::ptr::write_bytes(program_entry, 0, data.len()); @@ -209,9 +213,9 @@ pub fn alloc_prog(data: &[u8]) -> extern "C" fn() { ); println!("Program size: 0x{:x} bytes", data.len()); println!("Program entry: {:p}", program_entry); - let program_entry: extern "C" fn() = unsafe { core::mem::transmute(program_entry) }; + // let program_entry: extern "C" fn() = unsafe { core::mem::transmute(program_entry) }; unsafe { core::ptr::copy(data.as_ptr(), program_entry as *mut u8, data.len()); } - program_entry + (program_entry, data.len()) } diff --git a/kernel/src/thread.rs b/kernel/src/thread.rs index f82968858..a7dabed98 100644 --- a/kernel/src/thread.rs +++ b/kernel/src/thread.rs @@ -1,7 +1,9 @@ pub mod cpu; pub mod state; -use alloc::alloc::{alloc, Layout}; +use crate::mmu::vm::VirtualMemory; +// use alloc::alloc::alloc; +// use core::alloc::Layout; use stdio::println; #[repr(C)] @@ -11,54 +13,85 @@ pub struct Thread { pub state: state::State, pub stack: *mut u8, pub stack_size: usize, - pub entry: extern "C" fn(), + pub entry: *mut u8, pub cpu_state: cpu::State, + pub vm: VirtualMemory, } impl Thread { - pub fn new(stack_size: usize, entry: extern "C" fn()) -> Self { - let stack = - unsafe { alloc(Layout::from_size_align(stack_size, 0x100).unwrap()) as *mut u8 }; - let cpu_state = cpu::State::new(stack, stack_size, entry); + pub fn new(stack_size: usize, entry: *mut u8, len: usize) -> Self { + let mut vm = VirtualMemory::new(); + // let stack = + // unsafe { alloc(Layout::from_size_align(stack_size, 0x100).unwrap()) as *mut u8 }; + let stack = vm.mmap(0x7fff_ffff_b000, stack_size, 0b0100_0100_0111) as *mut u8; println!( "Stack: {:x}-{:x}", stack as usize, stack as usize + stack_size ); + let pc = vm.mmap(0x0000_0000_0000, len, 0b0100_1100_0111); + let phy = vm.get_phys(pc as u64); + println!("Physical address: 0x{:x}", phy as usize); + println!("entry: 0x{:x}, len: 0x{:x}", entry as usize, len); + vm.dump(); + + unsafe { + core::ptr::copy(entry, phy as *mut u8, len); + } + // unsafe { + // for i in 0..len { + // let val = core::ptr::read(entry.add(i)); + // core::ptr::write(phy.add(i), val); + // println!("0x{:x} = 0x{:x}", phy as usize + i, val); + // } + // } + assert!(stack == 0x7fff_ffff_b000 as *mut u8); + let cpu_state = cpu::State::new(stack, stack_size, entry, vm.get_l0_addr()); + println!( + "Stack: {:x}-{:x}", + stack as usize, + stack as usize + stack_size + ); + println!("entry: 0x{:x}, len: 0x{:x}", entry as usize, len); Thread { id: 0xC8763, state: state::State::Ready, stack, stack_size, - entry, + entry: pc, cpu_state, + vm, } } } impl Clone for Thread { fn clone(&self) -> Self { - let stack = - unsafe { alloc(Layout::from_size_align(self.stack_size, 0x100).unwrap()) as *mut u8 }; - unsafe { - core::ptr::copy(self.stack, stack, self.stack_size); - } - let mut cpu_state = self.cpu_state.clone(); - cpu_state.sp = (cpu_state.sp as usize - self.stack as usize + stack as usize) as u64; - println!( - "Cloning thread 0x{:x} to 0x{:x}", - self.id, 0xdeadbeaf as u32 - ); - println!( - "Stack: {:x}-{:x}", - stack as usize, - stack as usize + self.stack_size - ); - Thread { - id: 0xdeadbeaf, - stack, - cpu_state, - ..*self - } + // let stack = + // unsafe { alloc(Layout::from_size_align(self.stack_size, 0x100).unwrap()) as *mut u8 }; + panic!("Cloning thread 0x{:x}", self.id); + // let mut vm = self.vm.clone(); + // let stack = vm.mmap(0x7fff_ffff_0000, self.stack_size) as *mut u8; + // unsafe { + // core::ptr::copy(self.stack, stack, self.stack_size); + // } + // let mut cpu_state = self.cpu_state.clone(); + // cpu_state.sp = (cpu_state.sp as usize - self.stack as usize + stack as usize) as u64; + // println!( + // "Cloning thread 0x{:x} to 0x{:x}", + // self.id, 0xdeadbeaf as u32 + // ); + // println!( + // "Stack: {:x}-{:x}", + // stack as usize, + // stack as usize + self.stack_size + // ); + // Thread { + // id: 0xdeadbeaf, + // stack, + // cpu_state, + // vm, + // ..*self + // } } } diff --git a/kernel/src/thread/cpu.rs b/kernel/src/thread/cpu.rs index d43a39fc3..cca2c65ad 100644 --- a/kernel/src/thread/cpu.rs +++ b/kernel/src/thread/cpu.rs @@ -9,10 +9,11 @@ pub struct State { pub pc: u64, pub sp: u64, spsr: u64, + pub l0: u64, } impl State { - pub fn new(stack: *mut u8, stack_size: usize, entry: extern "C" fn()) -> Self { + pub fn new(stack: *mut u8, stack_size: usize, entry: *mut u8, vm: *mut u8) -> Self { let sp = stack as u64 + stack_size as u64; let x = [0; 31]; // x[30] = entry as u64; @@ -22,6 +23,7 @@ impl State { pc: entry as u64, sp, spsr, + l0: vm as u64, } } @@ -33,13 +35,20 @@ impl State { let pc = unsafe { read_volatile((addr + 32 * 8) as *const u64) }; let sp = unsafe { read_volatile((addr + 33 * 8) as *const u64) }; let spsr = unsafe { read_volatile((addr + 34 * 8) as *const u64) }; + let l0 = unsafe { read_volatile((addr + 35 * 8) as *const u64) }; // println!("spsr: 0x{:x}", spsr); if spsr & 0xfff != 0x200 { stdio::println!("SPSR is not 0x200 0x{:x}", spsr); } // assert!((spsr & 0xfff) == 0x0, "SPSR is not zero 0x{:x}", spsr); // spsr = 0x0; - State { x, pc, sp, spsr } + State { + x, + pc, + sp, + spsr, + l0, + } } pub fn store(&self, addr: u64) { @@ -48,7 +57,8 @@ impl State { } unsafe { write_volatile((addr + 32 * 8) as *mut u64, self.pc) }; unsafe { write_volatile((addr + 33 * 8) as *mut u64, self.sp) }; - unsafe { write_volatile((addr + 34 * 8) as *mut u64, 0x200) }; + unsafe { write_volatile((addr + 34 * 8) as *mut u64, self.spsr) }; + unsafe { write_volatile((addr + 35 * 8) as *mut u64, self.l0) }; } } From 535a19a1d2ad3fb0300d6df273663ef00268b08c Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Thu, 13 Jun 2024 11:28:21 +0800 Subject: [PATCH 08/14] feat: exec in vm.img & fork without mapping gpu --- kernel/src/allocator/buddy.rs | 2 +- kernel/src/allocator/dynamic.rs | 10 +-- kernel/src/exception/context_switch.S | 2 - kernel/src/exception/exception_table.S | 2 +- kernel/src/exception/handlers/page.rs | 7 +++ kernel/src/exception/handlers/svc.rs | 26 +++++--- kernel/src/main.rs | 4 +- kernel/src/mmu/entry.rs | 20 +++++- kernel/src/mmu/page_table.rs | 68 +++++++++++++++++++-- kernel/src/mmu/vm.rs | 59 ++++++++++++++---- kernel/src/scheduler.rs | 10 +-- kernel/src/thread.rs | 84 +++++++++++--------------- kernel/src/thread/cpu.rs | 3 - 13 files changed, 204 insertions(+), 93 deletions(-) diff --git a/kernel/src/allocator/buddy.rs b/kernel/src/allocator/buddy.rs index 8138def24..7b1723877 100644 --- a/kernel/src/allocator/buddy.rs +++ b/kernel/src/allocator/buddy.rs @@ -114,7 +114,7 @@ impl BuddyAllocator { if align < FRAME_SIZE { layer = 0; } else { - while (1 << layer) < align { + while (1 << layer) * FRAME_SIZE < align { layer += 1; } } diff --git a/kernel/src/allocator/dynamic.rs b/kernel/src/allocator/dynamic.rs index eff0911c5..7d89e37b4 100644 --- a/kernel/src/allocator/dynamic.rs +++ b/kernel/src/allocator/dynamic.rs @@ -113,10 +113,12 @@ unsafe impl GlobalAlloc for DynamicAllocator { return BumpAllocator.dealloc(ptr, layout); } if layout.size() > FRAME_SIZE { - debug!( - "Deallocating 0x{:x} with size > {}", - ptr as usize, FRAME_SIZE - ); + if DYNAMIC_ALLOCATOR.verbose { + debug!( + "Deallocating 0x{:x} with size > {}", + ptr as usize, FRAME_SIZE + ); + } return BUDDY_SYSTEM.dealloc(ptr, layout); } if DYNAMIC_ALLOCATOR.verbose { diff --git a/kernel/src/exception/context_switch.S b/kernel/src/exception/context_switch.S index 98b8d54cf..310ba4a89 100644 --- a/kernel/src/exception/context_switch.S +++ b/kernel/src/exception/context_switch.S @@ -23,7 +23,6 @@ mrs x3, ttbr0_el1 stp x0, x1, [sp, 16 * 16] stp x2, x3, [sp, 16 * 17] - dsb ish mov x0, 0x1000 msr ttbr0_el1, x0 tlbi vmalle1is @@ -38,7 +37,6 @@ msr elr_el1, x0 msr sp_el0, x1 msr spsr_el1, x2 - dsb ish msr ttbr0_el1, x3 tlbi vmalle1is dsb ish diff --git a/kernel/src/exception/exception_table.S b/kernel/src/exception/exception_table.S index 349c31f72..70aa26c56 100644 --- a/kernel/src/exception/exception_table.S +++ b/kernel/src/exception/exception_table.S @@ -21,7 +21,7 @@ exception_vector_table: EXCEPTION_WITH_TYPE 1, unknown_exception_handler .align 7 - EXCEPTION_WITH_TYPE 2, lower_exception_handler + EXCEPTION_WITH_TYPE 2, unknown_exception_handler .align 7 EXCEPTION_WITH_TYPE 3, unknown_exception_handler diff --git a/kernel/src/exception/handlers/page.rs b/kernel/src/exception/handlers/page.rs index b5d8f57d5..6635617d1 100644 --- a/kernel/src/exception/handlers/page.rs +++ b/kernel/src/exception/handlers/page.rs @@ -13,9 +13,16 @@ pub unsafe fn page_fault() { "mrs {0}, far_el1", out(reg) far_el1, ); + let elr_el1: u64; + asm!( + "mrs {0}, elr_el1", + out(reg) elr_el1, + ); + println!("Page fault"); println!("Exception Class: 0b{:06b}", ec); println!("ESR_EL1: 0x{:x}", esr_el1); + println!("ELR_EL1: 0x{:x}", elr_el1); println!("FAR_EL1: 0x{:x}", far_el1); panic!("Page fault"); } diff --git a/kernel/src/exception/handlers/svc.rs b/kernel/src/exception/handlers/svc.rs index b0f2127ba..d04896d3e 100644 --- a/kernel/src/exception/handlers/svc.rs +++ b/kernel/src/exception/handlers/svc.rs @@ -1,5 +1,5 @@ use super::page::page_fault; -use crate::exception::trap_frame; +use crate::{exception::trap_frame, mmu::vm::VirtualMemory}; use core::{arch::asm, fmt::Debug}; use stdio::{debug, println}; @@ -61,12 +61,14 @@ unsafe fn lower_exception_handler(eidx: u64, sp: u64) { out(reg) esr_el1, ); let ec = esr_el1 >> 26; + // println!("Exception {}", eidx); + // println!("Exception Class: 0b{:06b}", ec); match ec { 0b010101 => svc_handler(sp), 0b001110 => { panic!("Illegal Execution state."); } - 0b100000 => page_fault(), + 0b100000 | 0b100001 | 0b100100 => page_fault(), _ => { println!("Exception {}", eidx); println!("Unknown exception"); @@ -109,17 +111,20 @@ unsafe fn el1_interrupt(sp: u64) { unsafe fn syscall_handler(sp: u64) { let syscall = Syscall::new(sp); - println!("Syscall {:?}", syscall); + // println!("{:?}", syscall); assert!(trap_frame::TRAP_FRAME.is_some()); + let mut vm = VirtualMemory::load(trap_frame::TRAP_FRAME.as_ref().unwrap().state.l0); + // vm.dump(); match syscall.idx { 0 => { - // println!("Syscall get_pid"); + println!("Syscall get_pid"); let pid = crate::syscall::get_pid(); trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = pid; } 1 => { // println!("Syscall read"); let buf = syscall.arg0 as *mut u8; + let buf = vm.get_phys(buf as u64); let size = syscall.arg1 as usize; let read = crate::syscall::read(buf, size); assert!(size == 1); @@ -132,34 +137,37 @@ unsafe fn syscall_handler(sp: u64) { 2 => { // println!("Syscall write"); let buf = syscall.arg0 as *const u8; + let buf = vm.get_phys(buf as u64); let size = syscall.arg1 as usize; let written = crate::syscall::write(buf, size); trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = written as u64; } 3 => { - // println!("Syscall exec"); + println!("Syscall exec"); let name = syscall.arg0 as *const u8; + let name = vm.get_phys(name as u64); let ret = crate::syscall::exec(name); trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = ret; } 4 => { - // println!("Syscall fork"); + println!("Syscall fork"); let pid = crate::syscall::fork(); trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = pid; } 5 => { - // println!("Syscall exit"); + println!("Syscall exit"); crate::syscall::exit(syscall.arg0); } 6 => { - // println!("Syscall mbox_call"); + println!("Syscall mbox_call"); let channel = syscall.arg0 as u8; let mbox = syscall.arg1 as *mut u32; + let mbox = vm.get_phys(mbox as u64) as *mut u32; let ret = crate::syscall::mbox_call(channel, mbox); trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = ret as u64; } 7 => { - // println!("Syscall kill"); + println!("Syscall kill"); let pid = syscall.arg0; crate::syscall::kill(pid); } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index f419bf91e..d7f430f6f 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -26,7 +26,7 @@ pub static mut INITRAMFS_ADDR: u32 = 0; fn main() -> ! { boot(); println!("Kernel booted successfully!"); - commands::execute(b"exec rprog.img"); + commands::execute(b"exec vm.img"); kernel_shell(); } @@ -71,7 +71,7 @@ fn buddy_init() { BUDDY_SYSTEM.init(); } buddy_reserve_memory(); - allocator::utils::toggle_bump_verbose(); + // allocator::utils::toggle_bump_verbose(); unsafe { BUDDY_SYSTEM.print_info(); } diff --git a/kernel/src/mmu/entry.rs b/kernel/src/mmu/entry.rs index 2b229dd97..5a0ef829a 100644 --- a/kernel/src/mmu/entry.rs +++ b/kernel/src/mmu/entry.rs @@ -1,10 +1,10 @@ use core::fmt::Debug; use super::page_table::PageTable; +use alloc::alloc::alloc; use alloc::boxed::Box; -use stdio::print; +use core::alloc::Layout; -#[derive(Clone)] pub enum Entry { None, PdBlock((*mut u64, u64)), @@ -60,3 +60,19 @@ impl Debug for Entry { } } } + +impl Clone for Entry { + fn clone(&self) -> Self { + match self { + Entry::None => Entry::None, + Entry::PdBlock((_, pg)) => { + let blk = unsafe { alloc(Layout::from_size_align(0x1000, 0x1000).unwrap()) }; + unsafe { + core::ptr::copy((*pg & !0xfff) as *mut u8, blk, 0x1000); + } + Entry::PdBlock((0 as *mut u64, blk as u64 | *pg as u64 & 0xfff)) + } + Entry::PdTable(tbl) => Entry::PdTable(Box::new(*tbl.clone())), + } + } +} diff --git a/kernel/src/mmu/page_table.rs b/kernel/src/mmu/page_table.rs index 444141835..b5e0581d7 100644 --- a/kernel/src/mmu/page_table.rs +++ b/kernel/src/mmu/page_table.rs @@ -11,7 +11,7 @@ use crate::mmu::PD_ACCESS; use crate::mmu::PD_PAGE; use crate::mmu::PD_TABLE; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct PageTable { entries: Vec, pub addr: u64, @@ -33,6 +33,24 @@ impl PageTable { } } + pub fn load(addr: u64, level: u64) -> Self { + // println!("PageTable::load: 0x{:x}", addr); + let mut entries = vec![Entry::new(); ENTRY_COUNT]; + for i in 0..ENTRY_COUNT { + let entry_addr = addr + (i * 8) as u64; + let entry = unsafe { *(entry_addr as *const u64) }; + if entry & 0b1 == 1 { + if level < 3 { + let pt = Box::new(PageTable::load(entry & 0xffff_ffff_ffff_f000, level + 1)); + entries[i] = Entry::PdTable(pt); + } else { + entries[i] = Entry::PdBlock((entry_addr as *mut u64, entry)); + } + } + } + PageTable { entries, addr } + } + pub fn get_page(&mut self, addr: u64, level: u64) -> &mut Entry { let idx = ((addr >> (12 + 9 * (3 - level))) & 0x1ff) as usize; if level == 3 { @@ -66,8 +84,8 @@ impl PageTable { pub fn set_entry(&mut self, idx: usize, entry: Entry) { assert!(idx < ENTRY_COUNT); assert!(self.entries[idx].is_valid() == false); - self.entries[idx] = entry.clone(); - match entry { + self.entries[idx] = entry; + match &self.entries[idx] { Entry::PdTable(pt) => { let entry_addr = self.addr + (idx * 8) as u64; unsafe { @@ -77,9 +95,9 @@ impl PageTable { } } Entry::PdBlock((saddr, addr)) => { - assert!(addr == 0); + assert!(*addr == 0); unsafe { - let entry = saddr as *mut u64; + let entry = *saddr as *mut u64; *entry = addr | (PD_PAGE | PD_ACCESS | (MAIR_NORMAL_NC_IDX << 2) as u32) as u64; } } @@ -91,3 +109,43 @@ impl PageTable { self.entries[idx].is_valid() } } + +impl Clone for PageTable { + fn clone(&self) -> Self { + let addr = unsafe { alloc(Layout::from_size_align(0x1000, 0x1000).unwrap()) as u64 }; + unsafe { + let mut p = addr as *mut u8; + for _ in 0..0x1000 { + *p = 0; + p = p.add(1); + } + }; + let entries = self.entries.clone(); + for i in 0..ENTRY_COUNT { + if self.entries[i].is_valid() { + let entry = self.entries[i].clone(); + match entry { + Entry::PdBlock((saddr, pg)) => { + assert!(saddr as u64 == 0); + let entry_addr = addr + (i * 8) as u64; + unsafe { + let entry = entry_addr as *mut u64; + *entry = pg; + } + } + Entry::PdTable(pt) => { + let pt = pt.clone(); + let entry_addr = addr + (i * 8) as u64; + unsafe { + let entry = entry_addr as *mut u64; + *entry = pt.addr + | (PD_TABLE | PD_ACCESS | (MAIR_NORMAL_NC_IDX << 2) as u32) as u64; + } + } + _ => {} + } + } + } + PageTable { entries, addr } + } +} diff --git a/kernel/src/mmu/vm.rs b/kernel/src/mmu/vm.rs index de7a2e07e..24f975aee 100644 --- a/kernel/src/mmu/vm.rs +++ b/kernel/src/mmu/vm.rs @@ -4,7 +4,7 @@ use alloc::alloc::alloc; use core::alloc::Layout; use stdio::println; -#[derive(Debug, Clone)] +#[derive(Debug)] pub struct VirtualMemory { root: PageTable, } @@ -16,6 +16,13 @@ impl VirtualMemory { } } + pub fn load(addr: u64) -> Self { + // println!("VirtualMemory::load: 0x{:x}", addr); + VirtualMemory { + root: PageTable::load(addr, 0), + } + } + pub fn get_l0_addr(&self) -> *mut u8 { self.root.addr as *mut u8 } @@ -24,23 +31,43 @@ impl VirtualMemory { self.root.get_page(addr, 0) } - pub fn mmap(&mut self, addr: u64, size: usize, flag: u32) -> *mut u8 { - let flag = flag & 0xfff; - let page = self.get_page(addr); - let mem = - unsafe { alloc(Layout::from_size_align(size as usize, 0x1000).unwrap()) as *mut u8 }; + pub fn map_pa(&mut self, addr: u64, pa: u64, size: usize, flag: u32) -> *mut u8 { println!( - "mmap: addr: 0x{:x}, size: 0x{:x}, mem: 0x{:x}", - addr, size, mem as u64 + "map_pa: 0x{:x} -> 0x{:x}, size: 0x{:x}, flag: 0x{:x}", + addr, pa, size, flag ); - page.set_addr(mem as u32); - page.set_flag(flag); + let flag = flag & 0xfff; + for i in (0..size).step_by(0x1000) { + println!("map page: 0x{:x} -> 0x{:x}", addr + i as u64, pa + i as u64); + let page = self.get_page(addr + i as u64); + page.set_addr((pa + i as u64) as u32); + page.set_flag(flag); + } + println!("map_pa: 0x{:x} -> 0x{:x}", addr, pa); + return addr as *mut u8; + } + + pub fn mmap(&mut self, addr: u64, size: usize, flag: u32) -> *mut u8 { + println!("mmap: 0x{:x}, size: 0x{:x}, flag: 0x{:x}", addr, size, flag); + let flag = flag & 0xfff; + let mem = unsafe { alloc(Layout::from_size_align(size, 0x1000).unwrap()) as *mut u8 }; + for i in (0..size).step_by(0x1000) { + println!( + "map page: 0x{:x} -> 0x{:x}", + addr + i as u64, + mem as u64 + i as u64 + ); + let page = self.get_page(addr + i as u64); + page.set_addr((mem as u64 + i as u64) as u32); + page.set_flag(flag); + } + println!("mmap: 0x{:x} -> 0x{:x}", addr, mem as usize); return addr as *mut u8; } pub fn get_phys(&mut self, addr: u64) -> *mut u8 { let page = self.get_page(addr); - page.get_addr() + (page.get_addr() as u64 | addr & 0xfff) as *mut u8 } pub fn dump(&self) { println!("VirtualMemory:"); @@ -53,7 +80,7 @@ unsafe fn dump(addr: *mut u64, level: u8) { println!(" addr: 0x{:x}", addr as u64); for i in 0..512 { let p = addr.add(i); - if *p & 0b1 == 1 { + if *p & 0b1 == 1 && level < 3 { println!(" [{:03}] = 0x{:016x}", i, *p); } } @@ -70,3 +97,11 @@ unsafe fn dump(addr: *mut u64, level: u8) { } } } + +impl Clone for VirtualMemory { + fn clone(&self) -> Self { + VirtualMemory { + root: self.root.clone(), + } + } +} diff --git a/kernel/src/scheduler.rs b/kernel/src/scheduler.rs index 5842997f2..b508e228d 100644 --- a/kernel/src/scheduler.rs +++ b/kernel/src/scheduler.rs @@ -18,6 +18,8 @@ pub struct Scheduler { pub wait_queue: VecDeque, } +const STACK_SIZE: usize = 0x4000; + impl Scheduler { fn new() -> Self { Scheduler { @@ -78,7 +80,7 @@ impl Scheduler { } pub fn create_thread(&mut self, entry: *mut u8, len: usize) { - let thread = Box::new(Thread::new(0x1000, entry, len)); + let thread = Box::new(Thread::new(STACK_SIZE, entry, len)); println!("Creating thread"); let tid = self.add_thread(thread); println!("Created thread {}", tid); @@ -107,7 +109,7 @@ impl Scheduler { println!("Switching to {}", next); let thread = self.threads[next].as_ref().unwrap(); assert!(thread.id == next); - let entry = thread.entry; + let pc = thread.cpu_state.pc; let sp = thread.stack as usize + thread.stack_size; thread.vm.dump(); unsafe { @@ -127,7 +129,7 @@ impl Scheduler { "dsb ish", "isb", "eret", - in(reg) entry, + in(reg) pc, in(reg) sp, in(reg) thread.vm.get_l0_addr(), options(noreturn), @@ -141,7 +143,7 @@ impl Scheduler { filesystem::cpio::CpioArchive::load(unsafe { crate::INITRAMFS_ADDR } as *const u8); if let Some(data) = program.get_file(name.as_str()) { let program = alloc_prog(data); - let new_thread = Box::new(Thread::new(0x2000, program.0, program.1)); + let new_thread = Box::new(Thread::new(STACK_SIZE, program.0, program.1)); self.threads[current] = Some(new_thread); self.ready_queue.push_back(current); let next = self.restore_next(); diff --git a/kernel/src/thread.rs b/kernel/src/thread.rs index a7dabed98..4848bcad7 100644 --- a/kernel/src/thread.rs +++ b/kernel/src/thread.rs @@ -2,8 +2,8 @@ pub mod cpu; pub mod state; use crate::mmu::vm::VirtualMemory; -// use alloc::alloc::alloc; -// use core::alloc::Layout; +use alloc::alloc::alloc; +use core::alloc::Layout; use stdio::println; #[repr(C)] @@ -14,6 +14,7 @@ pub struct Thread { pub stack: *mut u8, pub stack_size: usize, pub entry: *mut u8, + pub len: usize, pub cpu_state: cpu::State, pub vm: VirtualMemory, } @@ -21,44 +22,28 @@ pub struct Thread { impl Thread { pub fn new(stack_size: usize, entry: *mut u8, len: usize) -> Self { let mut vm = VirtualMemory::new(); - // let stack = - // unsafe { alloc(Layout::from_size_align(stack_size, 0x100).unwrap()) as *mut u8 }; - let stack = vm.mmap(0x7fff_ffff_b000, stack_size, 0b0100_0100_0111) as *mut u8; + let stack = vm.mmap(0xffff_ffff_b000, stack_size, 0b0100_0100_0111) as *mut u8; println!( "Stack: {:x}-{:x}", stack as usize, stack as usize + stack_size ); - let pc = vm.mmap(0x0000_0000_0000, len, 0b0100_1100_0111); - let phy = vm.get_phys(pc as u64); - println!("Physical address: 0x{:x}", phy as usize); - println!("entry: 0x{:x}, len: 0x{:x}", entry as usize, len); - vm.dump(); - - unsafe { - core::ptr::copy(entry, phy as *mut u8, len); - } - // unsafe { - // for i in 0..len { - // let val = core::ptr::read(entry.add(i)); - // core::ptr::write(phy.add(i), val); - // println!("0x{:x} = 0x{:x}", phy as usize + i, val); - // } - // } - assert!(stack == 0x7fff_ffff_b000 as *mut u8); - let cpu_state = cpu::State::new(stack, stack_size, entry, vm.get_l0_addr()); + let pc = vm.map_pa(0x0000_0000_0000, entry as u64, len, 0b0100_1100_0111); + assert!(stack == 0xffff_ffff_b000 as *mut u8); + let cpu_state = cpu::State::new(stack, stack_size, pc, vm.get_l0_addr()); println!( "Stack: {:x}-{:x}", stack as usize, stack as usize + stack_size ); - println!("entry: 0x{:x}, len: 0x{:x}", entry as usize, len); + println!("pc: {:x}", pc as usize); Thread { id: 0xC8763, state: state::State::Ready, stack, stack_size, - entry: pc, + entry, + len, cpu_state, vm, } @@ -67,31 +52,34 @@ impl Thread { impl Clone for Thread { fn clone(&self) -> Self { - // let stack = - // unsafe { alloc(Layout::from_size_align(self.stack_size, 0x100).unwrap()) as *mut u8 }; - panic!("Cloning thread 0x{:x}", self.id); - // let mut vm = self.vm.clone(); - // let stack = vm.mmap(0x7fff_ffff_0000, self.stack_size) as *mut u8; + let mut vm = VirtualMemory::new(); + let stack = vm.mmap(0x7fff_ffff_b000, self.stack_size, 0b0100_0100_0111) as *mut u8; + // unsafe { // core::ptr::copy(self.stack, stack, self.stack_size); // } - // let mut cpu_state = self.cpu_state.clone(); - // cpu_state.sp = (cpu_state.sp as usize - self.stack as usize + stack as usize) as u64; - // println!( - // "Cloning thread 0x{:x} to 0x{:x}", - // self.id, 0xdeadbeaf as u32 - // ); - // println!( - // "Stack: {:x}-{:x}", - // stack as usize, - // stack as usize + self.stack_size - // ); - // Thread { - // id: 0xdeadbeaf, - // stack, - // cpu_state, - // vm, - // ..*self - // } + let cpu_state = self.cpu_state.clone(); + vm.map_pa( + 0x0000_0000_0000, + self.entry as u64, + self.len, + 0b0100_1100_0111, + ); + println!( + "Cloning thread 0x{:x} to 0x{:x}", + self.id, 0xdeadbeaf as u32 + ); + println!( + "Stack: {:x}-{:x}", + stack as usize, + stack as usize + self.stack_size + ); + Thread { + id: 0xdeadbeaf, + stack, + cpu_state, + vm, + ..*self + } } } diff --git a/kernel/src/thread/cpu.rs b/kernel/src/thread/cpu.rs index cca2c65ad..b35f479e5 100644 --- a/kernel/src/thread/cpu.rs +++ b/kernel/src/thread/cpu.rs @@ -37,9 +37,6 @@ impl State { let spsr = unsafe { read_volatile((addr + 34 * 8) as *const u64) }; let l0 = unsafe { read_volatile((addr + 35 * 8) as *const u64) }; // println!("spsr: 0x{:x}", spsr); - if spsr & 0xfff != 0x200 { - stdio::println!("SPSR is not 0x200 0x{:x}", spsr); - } // assert!((spsr & 0xfff) == 0x0, "SPSR is not zero 0x{:x}", spsr); // spsr = 0x0; State { From 5e9f1aa29a612b1de029eaf7fdeda45d55e31447 Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Thu, 13 Jun 2024 17:12:55 +0800 Subject: [PATCH 09/14] feat: mmu fork with device memory passthrough --- kernel/src/mmu/entry.rs | 17 +---------------- kernel/src/mmu/page_table.rs | 32 +++++++++++++++++++++++++------- kernel/src/mmu/vm.rs | 19 +++++++++---------- kernel/src/scheduler.rs | 2 +- kernel/src/thread.rs | 27 ++++++++++++++++++++------- kernel/src/thread/cpu.rs | 6 +++++- 6 files changed, 61 insertions(+), 42 deletions(-) diff --git a/kernel/src/mmu/entry.rs b/kernel/src/mmu/entry.rs index 5a0ef829a..2510d9437 100644 --- a/kernel/src/mmu/entry.rs +++ b/kernel/src/mmu/entry.rs @@ -5,6 +5,7 @@ use alloc::alloc::alloc; use alloc::boxed::Box; use core::alloc::Layout; +#[derive(Clone)] pub enum Entry { None, PdBlock((*mut u64, u64)), @@ -60,19 +61,3 @@ impl Debug for Entry { } } } - -impl Clone for Entry { - fn clone(&self) -> Self { - match self { - Entry::None => Entry::None, - Entry::PdBlock((_, pg)) => { - let blk = unsafe { alloc(Layout::from_size_align(0x1000, 0x1000).unwrap()) }; - unsafe { - core::ptr::copy((*pg & !0xfff) as *mut u8, blk, 0x1000); - } - Entry::PdBlock((0 as *mut u64, blk as u64 | *pg as u64 & 0xfff)) - } - Entry::PdTable(tbl) => Entry::PdTable(Box::new(*tbl.clone())), - } - } -} diff --git a/kernel/src/mmu/page_table.rs b/kernel/src/mmu/page_table.rs index b5e0581d7..3dd1af65b 100644 --- a/kernel/src/mmu/page_table.rs +++ b/kernel/src/mmu/page_table.rs @@ -51,7 +51,25 @@ impl PageTable { PageTable { entries, addr } } - pub fn get_page(&mut self, addr: u64, level: u64) -> &mut Entry { + pub fn get_entry(&self, idx: usize) -> &Entry { + &self.entries[idx] + } + + pub fn get_page(&self, addr: u64, level: u64) -> &Entry { + let idx = ((addr >> (12 + 9 * (3 - level))) & 0x1ff) as usize; + if level == 3 { + return self.get_entry(idx); + } + if !self.exists(idx) { + return &Entry::None; + } + if let Entry::PdTable(pt) = self.get_entry(idx) { + return pt.get_page(addr, level + 1); + } + &Entry::None + } + + pub fn create_page(&mut self, addr: u64, level: u64) -> &mut Entry { let idx = ((addr >> (12 + 9 * (3 - level))) & 0x1ff) as usize; if level == 3 { if !self.exists(idx as usize) { @@ -60,24 +78,24 @@ impl PageTable { Entry::PdBlock(((self.addr + (idx * 8) as u64) as *mut u64, 0)), ); } - return self.get_entry(idx as usize); + return self.get_entry_mut(idx as usize); } if !self.exists(idx as usize) { let pg = PageTable::new(); self.set_entry(idx as usize, Entry::PdTable(Box::new(pg))); - if let Entry::PdTable(pt) = self.get_entry(idx as usize) { - return pt.get_page(addr, level + 1); + if let Entry::PdTable(pt) = self.get_entry_mut(idx as usize) { + return pt.create_page(addr, level + 1); } panic!("get_page: not a PdTable"); } - let entry = self.get_entry(idx as usize); + let entry = self.get_entry_mut(idx as usize); if let Entry::PdTable(pt) = entry { - return pt.get_page(addr, level + 1); + return pt.create_page(addr, level + 1); } panic!("get_page: not a PdTable"); } - pub fn get_entry(&mut self, idx: usize) -> &mut Entry { + pub fn get_entry_mut(&mut self, idx: usize) -> &mut Entry { &mut self.entries[idx] } diff --git a/kernel/src/mmu/vm.rs b/kernel/src/mmu/vm.rs index 24f975aee..7ebf256b3 100644 --- a/kernel/src/mmu/vm.rs +++ b/kernel/src/mmu/vm.rs @@ -27,10 +27,14 @@ impl VirtualMemory { self.root.addr as *mut u8 } - fn get_page(&mut self, addr: u64) -> &mut Entry { + fn get_page(&self, addr: u64) -> &Entry { self.root.get_page(addr, 0) } + fn create_page(&mut self, addr: u64) -> &mut Entry { + self.root.create_page(addr, 0) + } + pub fn map_pa(&mut self, addr: u64, pa: u64, size: usize, flag: u32) -> *mut u8 { println!( "map_pa: 0x{:x} -> 0x{:x}, size: 0x{:x}, flag: 0x{:x}", @@ -38,8 +42,7 @@ impl VirtualMemory { ); let flag = flag & 0xfff; for i in (0..size).step_by(0x1000) { - println!("map page: 0x{:x} -> 0x{:x}", addr + i as u64, pa + i as u64); - let page = self.get_page(addr + i as u64); + let page = self.create_page(addr + i as u64); page.set_addr((pa + i as u64) as u32); page.set_flag(flag); } @@ -52,12 +55,7 @@ impl VirtualMemory { let flag = flag & 0xfff; let mem = unsafe { alloc(Layout::from_size_align(size, 0x1000).unwrap()) as *mut u8 }; for i in (0..size).step_by(0x1000) { - println!( - "map page: 0x{:x} -> 0x{:x}", - addr + i as u64, - mem as u64 + i as u64 - ); - let page = self.get_page(addr + i as u64); + let page = self.create_page(addr + i as u64); page.set_addr((mem as u64 + i as u64) as u32); page.set_flag(flag); } @@ -65,10 +63,11 @@ impl VirtualMemory { return addr as *mut u8; } - pub fn get_phys(&mut self, addr: u64) -> *mut u8 { + pub fn get_phys(&self, addr: u64) -> *mut u8 { let page = self.get_page(addr); (page.get_addr() as u64 | addr & 0xfff) as *mut u8 } + pub fn dump(&self) { println!("VirtualMemory:"); println!(" root: 0x{:x}", self.root.addr); diff --git a/kernel/src/scheduler.rs b/kernel/src/scheduler.rs index b508e228d..6a1795fec 100644 --- a/kernel/src/scheduler.rs +++ b/kernel/src/scheduler.rs @@ -111,7 +111,7 @@ impl Scheduler { assert!(thread.id == next); let pc = thread.cpu_state.pc; let sp = thread.stack as usize + thread.stack_size; - thread.vm.dump(); + println!("{:?}", thread.cpu_state); unsafe { asm!( "mrs {0}, cntkctl_el1", diff --git a/kernel/src/thread.rs b/kernel/src/thread.rs index 4848bcad7..4dd9ad917 100644 --- a/kernel/src/thread.rs +++ b/kernel/src/thread.rs @@ -2,8 +2,6 @@ pub mod cpu; pub mod state; use crate::mmu::vm::VirtualMemory; -use alloc::alloc::alloc; -use core::alloc::Layout; use stdio::println; #[repr(C)] @@ -29,6 +27,7 @@ impl Thread { stack as usize + stack_size ); let pc = vm.map_pa(0x0000_0000_0000, entry as u64, len, 0b0100_1100_0111); + // vm.map_pa(0x3C00_0000, 0x3C00_0000, 0x400_0000, 0b0100_0100_0111); assert!(stack == 0xffff_ffff_b000 as *mut u8); let cpu_state = cpu::State::new(stack, stack_size, pc, vm.get_l0_addr()); println!( @@ -52,19 +51,32 @@ impl Thread { impl Clone for Thread { fn clone(&self) -> Self { + // self.vm.dump(); let mut vm = VirtualMemory::new(); - let stack = vm.mmap(0x7fff_ffff_b000, self.stack_size, 0b0100_0100_0111) as *mut u8; + let stack = vm.mmap(0xffff_ffff_b000, self.stack_size, 0b0100_0100_0111) as *mut u8; - // unsafe { - // core::ptr::copy(self.stack, stack, self.stack_size); - // } - let cpu_state = self.cpu_state.clone(); + let src = self.vm.get_phys(self.stack as u64); + let dst = vm.get_phys(stack as u64); + println!( + "Cloning stack 0x{:x} to 0x{:x}, size: 0x{:x}", + src as usize, dst as usize, self.stack_size + ); + unsafe { + core::ptr::copy_nonoverlapping( + self.vm.get_phys(self.stack as u64), + vm.get_phys(stack as u64), + self.stack_size, + ); + } + let mut cpu_state = self.cpu_state.clone(); + cpu_state.l0 = vm.get_l0_addr() as u64; vm.map_pa( 0x0000_0000_0000, self.entry as u64, self.len, 0b0100_1100_0111, ); + vm.map_pa(0x3C00_0000, 0x3C00_0000, 0x400_0000, 0b0100_0100_0111); println!( "Cloning thread 0x{:x} to 0x{:x}", self.id, 0xdeadbeaf as u32 @@ -74,6 +86,7 @@ impl Clone for Thread { stack as usize, stack as usize + self.stack_size ); + // vm.dump(); Thread { id: 0xdeadbeaf, stack, diff --git a/kernel/src/thread/cpu.rs b/kernel/src/thread/cpu.rs index b35f479e5..58473a788 100644 --- a/kernel/src/thread/cpu.rs +++ b/kernel/src/thread/cpu.rs @@ -65,6 +65,10 @@ impl Debug for State { for i in 0..31 { write!(f, "0x{:x}, ", self.x[i])?; } - write!(f, "], pc: 0x{:x}, sp: 0x{:x} }}", self.pc, self.sp) + write!( + f, + "], pc: 0x{:x}, sp: 0x{:x}, spsr: 0x{:x}, l0: 0x{:x} }}", + self.pc, self.sp, self.spsr, self.l0 + ) } } From 2c1ceec3a9b6c6c318a0533e34d8646a1f86dc32 Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Thu, 13 Jun 2024 17:29:32 +0800 Subject: [PATCH 10/14] refactor: seperate config flags --- kernel/src/exception/handlers/svc.rs | 5 +--- kernel/src/main.rs | 1 - kernel/src/mmu.rs | 34 +++++++++------------------- kernel/src/mmu/config.rs | 34 ++++++++++++++++++++++++++++ kernel/src/mmu/page_table.rs | 8 +++---- kernel/src/thread.rs | 29 ++++++++---------------- stdio/src/lib.rs | 8 +++---- 7 files changed, 63 insertions(+), 56 deletions(-) diff --git a/kernel/src/exception/handlers/svc.rs b/kernel/src/exception/handlers/svc.rs index d04896d3e..c40743d42 100644 --- a/kernel/src/exception/handlers/svc.rs +++ b/kernel/src/exception/handlers/svc.rs @@ -61,8 +61,6 @@ unsafe fn lower_exception_handler(eidx: u64, sp: u64) { out(reg) esr_el1, ); let ec = esr_el1 >> 26; - // println!("Exception {}", eidx); - // println!("Exception Class: 0b{:06b}", ec); match ec { 0b010101 => svc_handler(sp), 0b001110 => { @@ -113,8 +111,7 @@ unsafe fn syscall_handler(sp: u64) { let syscall = Syscall::new(sp); // println!("{:?}", syscall); assert!(trap_frame::TRAP_FRAME.is_some()); - let mut vm = VirtualMemory::load(trap_frame::TRAP_FRAME.as_ref().unwrap().state.l0); - // vm.dump(); + let vm = VirtualMemory::load(trap_frame::TRAP_FRAME.as_ref().unwrap().state.l0); match syscall.idx { 0 => { println!("Syscall get_pid"); diff --git a/kernel/src/main.rs b/kernel/src/main.rs index d7f430f6f..482297525 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -82,7 +82,6 @@ fn buddy_init() { fn buddy_reserve_memory() { unsafe { BUDDY_SYSTEM.reserve_by_addr_range(0x1000, 0x1_0000); - // BUDDY_SYSTEM.reserve_by_addr_range(0x2000, 0x3000); } let rsv_mem = dtb::get_reserved_memory(); diff --git a/kernel/src/mmu.rs b/kernel/src/mmu.rs index 576c88047..e78c768d6 100644 --- a/kernel/src/mmu.rs +++ b/kernel/src/mmu.rs @@ -1,31 +1,19 @@ use core::arch::asm; -mod config; +pub mod config; mod entry; mod page_table; pub mod vm; -const TCR_CONFIG_REGION_48BIT: u64 = (64 - 48) << 0 | (64 - 48) << 16; -const TCR_CONFIG_REGION_4KB: u64 = 0b00 << 14 | 0b00 << 30; -const TCR_CONFIG_DEFAULT: u64 = TCR_CONFIG_REGION_48BIT | TCR_CONFIG_REGION_4KB | 0b101u64 << 32; - -const MAIR_DEVICE_NG_NR_NE: u8 = 0b00000000; -const MAIR_NORMAL_NC: u8 = 0b01000100; -const MAIR_DEVICE_NG_NR_NE_IDX: u8 = 0; -const MAIR_NORMAL_NC_IDX: u8 = 1; -const MAIR_CONFIG_DEFAULT: u64 = (MAIR_DEVICE_NG_NR_NE as u64) << (MAIR_DEVICE_NG_NR_NE_IDX * 8) - | (MAIR_NORMAL_NC as u64) << (MAIR_NORMAL_NC_IDX * 8); - -const L0_ADDR: u64 = 0x1000; -const L1_ADDR: u64 = 0x2000; -const L2_ADDR: u64 = 0x3000; - -const PD_TABLE: u32 = 0b11; -const PD_BLOCK: u32 = 0b01; -const PD_PAGE: u32 = 0b11; -const PD_ACCESS: u32 = 1 << 10; - -const AP_RW_EL0: usize = 0b01 << 6; -const AP_RO_EL0: usize = 0b11 << 6; +use crate::mmu::config::L0_ADDR; +use crate::mmu::config::L1_ADDR; +use crate::mmu::config::L2_ADDR; +use crate::mmu::config::MAIR_CONFIG_DEFAULT; +use crate::mmu::config::MAIR_DEVICE_NG_NR_NE_IDX; +use crate::mmu::config::MAIR_NORMAL_NC_IDX; +use crate::mmu::config::PD_ACCESS; +use crate::mmu::config::PD_BLOCK; +use crate::mmu::config::PD_TABLE; +use crate::mmu::config::TCR_CONFIG_DEFAULT; #[no_mangle] unsafe extern "C" fn set_mmu() { diff --git a/kernel/src/mmu/config.rs b/kernel/src/mmu/config.rs index 2a4e8bbe0..fb6ef3cbc 100644 --- a/kernel/src/mmu/config.rs +++ b/kernel/src/mmu/config.rs @@ -1,2 +1,36 @@ // pub const PAGE_SIZE: usize = 4096; pub const ENTRY_COUNT: usize = 512; + +pub const TCR_CONFIG_REGION_48BIT: u64 = (64 - 48) << 0 | (64 - 48) << 16; +pub const TCR_CONFIG_REGION_4KB: u64 = 0b00 << 14 | 0b00 << 30; +pub const TCR_CONFIG_DEFAULT: u64 = + TCR_CONFIG_REGION_48BIT | TCR_CONFIG_REGION_4KB | 0b101u64 << 32; + +pub const MAIR_DEVICE_NG_NR_NE: u8 = 0b00000000; +pub const MAIR_NORMAL_NC: u8 = 0b01000100; +pub const MAIR_DEVICE_NG_NR_NE_IDX: u8 = 0; +pub const MAIR_NORMAL_NC_IDX: u8 = 1; +pub const MAIR_CONFIG_DEFAULT: u64 = (MAIR_DEVICE_NG_NR_NE as u64) + << (MAIR_DEVICE_NG_NR_NE_IDX * 8) + | (MAIR_NORMAL_NC as u64) << (MAIR_NORMAL_NC_IDX * 8); + +pub const L0_ADDR: u64 = 0x1000; +pub const L1_ADDR: u64 = 0x2000; +pub const L2_ADDR: u64 = 0x3000; + +pub const PD_TABLE: u32 = 0b11; +pub const PD_BLOCK: u32 = 0b01; +pub const PD_PAGE: u32 = 0b11; +pub const PD_ACCESS: u32 = 1 << 10; + +pub const AP_RW_EL0: usize = 0b01 << 6; +pub const AP_RO_EL0: usize = 0b11 << 6; + +pub const STACK_CONFIG: u32 = + PD_ACCESS | AP_RW_EL0 as u32 | (MAIR_NORMAL_NC_IDX as u32) << 2 as u32 | PD_PAGE as u32; + +pub const TEXT_CONFIG: u32 = + PD_ACCESS | AP_RO_EL0 as u32 | (MAIR_NORMAL_NC_IDX as u32) << 2 as u32 | PD_PAGE as u32; + +pub const GPU_CONFIG: u32 = + PD_ACCESS | AP_RW_EL0 as u32 | (MAIR_DEVICE_NG_NR_NE_IDX as u32) << 2 as u32 | PD_PAGE as u32; diff --git a/kernel/src/mmu/page_table.rs b/kernel/src/mmu/page_table.rs index 3dd1af65b..68d12c36a 100644 --- a/kernel/src/mmu/page_table.rs +++ b/kernel/src/mmu/page_table.rs @@ -6,10 +6,10 @@ use alloc::vec; use alloc::vec::Vec; use core::alloc::Layout; -use crate::mmu::MAIR_NORMAL_NC_IDX; -use crate::mmu::PD_ACCESS; -use crate::mmu::PD_PAGE; -use crate::mmu::PD_TABLE; +use crate::mmu::config::MAIR_NORMAL_NC_IDX; +use crate::mmu::config::PD_ACCESS; +use crate::mmu::config::PD_PAGE; +use crate::mmu::config::PD_TABLE; #[derive(Debug)] pub struct PageTable { diff --git a/kernel/src/thread.rs b/kernel/src/thread.rs index 4dd9ad917..1eae73d22 100644 --- a/kernel/src/thread.rs +++ b/kernel/src/thread.rs @@ -1,6 +1,9 @@ pub mod cpu; pub mod state; +use crate::mmu::config::GPU_CONFIG; +use crate::mmu::config::STACK_CONFIG; +use crate::mmu::config::TEXT_CONFIG; use crate::mmu::vm::VirtualMemory; use stdio::println; @@ -20,14 +23,14 @@ pub struct Thread { impl Thread { pub fn new(stack_size: usize, entry: *mut u8, len: usize) -> Self { let mut vm = VirtualMemory::new(); - let stack = vm.mmap(0xffff_ffff_b000, stack_size, 0b0100_0100_0111) as *mut u8; + let stack = vm.mmap(0xffff_ffff_b000, stack_size, STACK_CONFIG) as *mut u8; println!( "Stack: {:x}-{:x}", stack as usize, stack as usize + stack_size ); - let pc = vm.map_pa(0x0000_0000_0000, entry as u64, len, 0b0100_1100_0111); - // vm.map_pa(0x3C00_0000, 0x3C00_0000, 0x400_0000, 0b0100_0100_0111); + let pc = vm.map_pa(0x0000_0000_0000, entry as u64, len, TEXT_CONFIG); + vm.map_pa(0x3C00_0000, 0x3C00_0000, 0x400_0000, GPU_CONFIG); assert!(stack == 0xffff_ffff_b000 as *mut u8); let cpu_state = cpu::State::new(stack, stack_size, pc, vm.get_l0_addr()); println!( @@ -51,16 +54,8 @@ impl Thread { impl Clone for Thread { fn clone(&self) -> Self { - // self.vm.dump(); let mut vm = VirtualMemory::new(); - let stack = vm.mmap(0xffff_ffff_b000, self.stack_size, 0b0100_0100_0111) as *mut u8; - - let src = self.vm.get_phys(self.stack as u64); - let dst = vm.get_phys(stack as u64); - println!( - "Cloning stack 0x{:x} to 0x{:x}, size: 0x{:x}", - src as usize, dst as usize, self.stack_size - ); + let stack = vm.mmap(0xffff_ffff_b000, self.stack_size, STACK_CONFIG) as *mut u8; unsafe { core::ptr::copy_nonoverlapping( self.vm.get_phys(self.stack as u64), @@ -70,13 +65,8 @@ impl Clone for Thread { } let mut cpu_state = self.cpu_state.clone(); cpu_state.l0 = vm.get_l0_addr() as u64; - vm.map_pa( - 0x0000_0000_0000, - self.entry as u64, - self.len, - 0b0100_1100_0111, - ); - vm.map_pa(0x3C00_0000, 0x3C00_0000, 0x400_0000, 0b0100_0100_0111); + vm.map_pa(0x0000_0000_0000, self.entry as u64, self.len, TEXT_CONFIG); + vm.map_pa(0x3C00_0000, 0x3C00_0000, 0x400_0000, GPU_CONFIG); println!( "Cloning thread 0x{:x} to 0x{:x}", self.id, 0xdeadbeaf as u32 @@ -86,7 +76,6 @@ impl Clone for Thread { stack as usize, stack as usize + self.stack_size ); - // vm.dump(); Thread { id: 0xdeadbeaf, stack, diff --git a/stdio/src/lib.rs b/stdio/src/lib.rs index 6955d5680..f260f2458 100644 --- a/stdio/src/lib.rs +++ b/stdio/src/lib.rs @@ -4,8 +4,8 @@ pub mod macros; use driver::uart; pub fn send(c: u8) { - uart::send(c); - // uart::send_async(c); + // uart::send(c); + uart::send_async(c); } fn _recv() -> u8 { @@ -17,8 +17,8 @@ fn _recv() -> u8 { } pub fn recv() -> u8 { - let c = uart::recv(); - // let c = _recv(); + // let c = uart::recv(); + let c = _recv(); match c { b'\r' | b'\n' => { write(b"\r\n"); From 29e9b93e288a52f3e7e218fbcfb41e7088f834c1 Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Thu, 13 Jun 2024 17:31:21 +0800 Subject: [PATCH 11/14] refactor: allow dead_code for vm dump --- kernel/src/mmu/entry.rs | 2 -- kernel/src/mmu/vm.rs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/src/mmu/entry.rs b/kernel/src/mmu/entry.rs index 2510d9437..a9bd7896b 100644 --- a/kernel/src/mmu/entry.rs +++ b/kernel/src/mmu/entry.rs @@ -1,9 +1,7 @@ use core::fmt::Debug; use super::page_table::PageTable; -use alloc::alloc::alloc; use alloc::boxed::Box; -use core::alloc::Layout; #[derive(Clone)] pub enum Entry { diff --git a/kernel/src/mmu/vm.rs b/kernel/src/mmu/vm.rs index 7ebf256b3..5fe01271e 100644 --- a/kernel/src/mmu/vm.rs +++ b/kernel/src/mmu/vm.rs @@ -68,6 +68,7 @@ impl VirtualMemory { (page.get_addr() as u64 | addr & 0xfff) as *mut u8 } + #[allow(dead_code)] pub fn dump(&self) { println!("VirtualMemory:"); println!(" root: 0x{:x}", self.root.addr); @@ -75,6 +76,7 @@ impl VirtualMemory { } } +#[allow(dead_code)] unsafe fn dump(addr: *mut u64, level: u8) { println!(" addr: 0x{:x}", addr as u64); for i in 0..512 { From 483a3070b9bfe7ab3be7f7289160a07fae8330a5 Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Thu, 13 Jun 2024 17:41:38 +0800 Subject: [PATCH 12/14] chore: clean --- Cargo.toml | 2 +- Makefile | 1 - kernel/src/exception/handlers/svc.rs | 13 ++++++------- kernel/src/main.rs | 3 --- 4 files changed, 7 insertions(+), 12 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d86c26dca..79a799ce4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,7 +7,7 @@ resolver = "2" [profile.release] debug = false -strip = false +strip = true opt-level = 'z' codegen-units = 1 lto = true diff --git a/Makefile b/Makefile index 05217e0f0..cc046ba80 100644 --- a/Makefile +++ b/Makefile @@ -92,7 +92,6 @@ run: all debug: all size $(CPROG) $(RPROG) $(OBJDUMP) -D $(KERNEL_ELF) > $(BUILD_DIR)/kernel.S $(OBJDUMP) -D $(BOOTLOADER_ELF) > $(BUILD_DIR)/bootloader.S - $(OBJDUMP) -D $(RPROG_ELF) > $(BUILD_DIR)/rprog.S debug-qemu: $(QEMU) -M raspi3b \ diff --git a/kernel/src/exception/handlers/svc.rs b/kernel/src/exception/handlers/svc.rs index c40743d42..e284d5bca 100644 --- a/kernel/src/exception/handlers/svc.rs +++ b/kernel/src/exception/handlers/svc.rs @@ -109,12 +109,11 @@ unsafe fn el1_interrupt(sp: u64) { unsafe fn syscall_handler(sp: u64) { let syscall = Syscall::new(sp); - // println!("{:?}", syscall); assert!(trap_frame::TRAP_FRAME.is_some()); let vm = VirtualMemory::load(trap_frame::TRAP_FRAME.as_ref().unwrap().state.l0); match syscall.idx { 0 => { - println!("Syscall get_pid"); + // println!("Syscall get_pid"); let pid = crate::syscall::get_pid(); trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = pid; } @@ -140,23 +139,23 @@ unsafe fn syscall_handler(sp: u64) { trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = written as u64; } 3 => { - println!("Syscall exec"); + // println!("Syscall exec"); let name = syscall.arg0 as *const u8; let name = vm.get_phys(name as u64); let ret = crate::syscall::exec(name); trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = ret; } 4 => { - println!("Syscall fork"); + // println!("Syscall fork"); let pid = crate::syscall::fork(); trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = pid; } 5 => { - println!("Syscall exit"); + // println!("Syscall exit"); crate::syscall::exit(syscall.arg0); } 6 => { - println!("Syscall mbox_call"); + // println!("Syscall mbox_call"); let channel = syscall.arg0 as u8; let mbox = syscall.arg1 as *mut u32; let mbox = vm.get_phys(mbox as u64) as *mut u32; @@ -164,7 +163,7 @@ unsafe fn syscall_handler(sp: u64) { trap_frame::TRAP_FRAME.as_mut().unwrap().state.x[0] = ret as u64; } 7 => { - println!("Syscall kill"); + // println!("Syscall kill"); let pid = syscall.arg0; crate::syscall::kill(pid); } diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 482297525..dd401f02b 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -71,12 +71,9 @@ fn buddy_init() { BUDDY_SYSTEM.init(); } buddy_reserve_memory(); - // allocator::utils::toggle_bump_verbose(); unsafe { BUDDY_SYSTEM.print_info(); } - unsafe { BUDDY_SYSTEM.initialized = true } - // allocator::utils::toggle_dynamic_verbose(); } fn buddy_reserve_memory() { From 5e0e19615583d2f721f67d04f5bfbf0028da3f7c Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Fri, 14 Jun 2024 11:48:00 +0800 Subject: [PATCH 13/14] fix: buddy init & uart --- kernel/src/commands/echo.rs | 16 ---------------- kernel/src/commands/mod.rs | 3 --- kernel/src/main.rs | 4 +++- stdio/src/lib.rs | 8 ++++---- 4 files changed, 7 insertions(+), 24 deletions(-) delete mode 100644 kernel/src/commands/echo.rs diff --git a/kernel/src/commands/echo.rs b/kernel/src/commands/echo.rs deleted file mode 100644 index fa1aecad6..000000000 --- a/kernel/src/commands/echo.rs +++ /dev/null @@ -1,16 +0,0 @@ -use stdio::println; - -pub fn exec(command: &[u8]) { - for c in &command[5..] { - driver::uart::send_async(*c); - } - println!("Echoed: {}", core::str::from_utf8(&command[5..]).unwrap()); - loop { - if let Some(c) = driver::uart::recv_async() { - println!("Received: {} (0x{:x})", c as char, c); - if c == b'\n' { - break; - } - } - } -} diff --git a/kernel/src/commands/mod.rs b/kernel/src/commands/mod.rs index 1f64c06f6..72bc1f173 100644 --- a/kernel/src/commands/mod.rs +++ b/kernel/src/commands/mod.rs @@ -1,6 +1,5 @@ mod buddy; mod cat; -mod echo; mod exec; mod hello; mod help; @@ -38,8 +37,6 @@ pub fn execute(command: &[u8]) { cat::exec(&command); } else if args[0] == "exec" { exec::exec(args); - } else if args[0] == "echo" { - echo::exec(&command); } else if args[0] == "setTimeOut" { set_time_out::exec(&command); } else if args[0] == "buddy" { diff --git a/kernel/src/main.rs b/kernel/src/main.rs index dd401f02b..58ee9b0aa 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -17,6 +17,7 @@ mod scheduler; mod syscall; mod thread; mod timer; +mod vfs; use allocator::buddy::BUDDY_SYSTEM; use stdio::{debug, gets, print, println}; @@ -26,7 +27,7 @@ pub static mut INITRAMFS_ADDR: u32 = 0; fn main() -> ! { boot(); println!("Kernel booted successfully!"); - commands::execute(b"exec vm.img"); + // commands::execute(b"exec vm.img"); kernel_shell(); } @@ -72,6 +73,7 @@ fn buddy_init() { } buddy_reserve_memory(); unsafe { + BUDDY_SYSTEM.initialized = true; BUDDY_SYSTEM.print_info(); } } diff --git a/stdio/src/lib.rs b/stdio/src/lib.rs index f260f2458..6955d5680 100644 --- a/stdio/src/lib.rs +++ b/stdio/src/lib.rs @@ -4,8 +4,8 @@ pub mod macros; use driver::uart; pub fn send(c: u8) { - // uart::send(c); - uart::send_async(c); + uart::send(c); + // uart::send_async(c); } fn _recv() -> u8 { @@ -17,8 +17,8 @@ fn _recv() -> u8 { } pub fn recv() -> u8 { - // let c = uart::recv(); - let c = _recv(); + let c = uart::recv(); + // let c = _recv(); match c { b'\r' | b'\n' => { write(b"\r\n"); From 98b6710dcf836e9bc8dad54efa06c786d753325b Mon Sep 17 00:00:00 2001 From: LolaInTa Chi Date: Thu, 20 Jun 2024 19:45:13 +0800 Subject: [PATCH 14/14] fix: remove unused mod --- kernel/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/kernel/src/main.rs b/kernel/src/main.rs index 58ee9b0aa..6fbf507bb 100644 --- a/kernel/src/main.rs +++ b/kernel/src/main.rs @@ -17,7 +17,6 @@ mod scheduler; mod syscall; mod thread; mod timer; -mod vfs; use allocator::buddy::BUDDY_SYSTEM; use stdio::{debug, gets, print, println};