diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..8aa8a46a9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +target +*/target +*.img +.vscode +rootfs +.gdbinit +initramfs.cpio +*.dtb +*.txt +.idea +initramfs/prog diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 000000000..c48479853 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,48 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aarch64-cpu" +version = "9.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287" +dependencies = [ + "tock-registers", +] + +[[package]] +name = "bootloader" +version = "0.1.0" +dependencies = [ + "aarch64-cpu", + "driver", +] + +[[package]] +name = "driver" +version = "0.1.0" +dependencies = [ + "aarch64-cpu", +] + +[[package]] +name = "fdt" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784a4df722dc6267a04af36895398f59d21d07dce47232adf31ec0ff2fa45e67" + +[[package]] +name = "kernel" +version = "0.1.0" +dependencies = [ + "aarch64-cpu", + "driver", + "fdt", +] + +[[package]] +name = "tock-registers" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 000000000..5cc67823b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "driver", + "kernel", + "bootloader", +] + diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..27525e411 --- /dev/null +++ b/Makefile @@ -0,0 +1,104 @@ +BSP = rpi3 + +TARGET = aarch64-unknown-none-softfloat + +KERNEL_BIN = kernel8.img +BOOTLOADER_BIN = bootloader.img + +include ./utils/format.mk +include ./utils/os.mk + +# QEMU +QEMU_BINARY = qemu-system-aarch64 +QEMU_MACHINE_TYPE = raspi3b +QEMU_RELEASE_ARGS = -display none -serial null -serial stdio +QEMU_DISPLAY_ARGS = -display gtk -serial null -serial stdio +QEMU_DEBUG_ARGS = -display none -S -s -serial null -serial stdio +QEMU_TTY_ARGS = -display none -serial null -serial pty +QEMU_TTY_DEBUG_ARGS = -display none -S -s -serial null -serial pty +EXEC_QEMU = $(QEMU_BINARY) -M $(QEMU_MACHINE_TYPE) + +QEMU_DTB_PATH = $(shell pwd)/bcm2710-rpi-3-b-plus.dtb + +OBJDUMP_BINARY = aarch64-none-elf-objdump +NM_BINARY = aarch64-none-elf-mn +READELF_BINARY = aarch64-none-elf-readelf +RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 + +KERNEL_PATH = $(shell pwd)/kernel +BOOTLOADER_PATH = $(shell pwd)/bootloader +INITRD_PATH = $(shell pwd)/initramfs.cpio + +KERNEL_ELF = target/$(TARGET)/release/kernel +BOOTLOADER_ELF = target/$(TARGET)/release/bootloader + +USER_PROG_IMG = userprog/prog/target/prog +USER_PROG = userprog/prog + +OBJCOPY_CMD = rust-objcopy \ + --strip-all \ + -O binary + +.PHONY: all doc qemu clippy clean readelf objdump nm check + +all: $(KERNEL_BIN) $(BOOTLOADER_BIN) + +$(KERNEL_BIN): kernel_elf + $(call color_header, "Generating stripped binary") + $(OBJCOPY_CMD) $(KERNEL_ELF) $(KERNEL_BIN) + $(call color_progress_prefix, "Name") + @echo $(KERNEL_BIN) + $(call color_progress_prefix, "Size") + $(call disk_usage_KiB, $(KERNEL_BIN)) + +$(BOOTLOADER_BIN): bootloader_elf + $(call color_header, "Generating stripped binary") + $(OBJCOPY_CMD) $(BOOTLOADER_ELF) $(BOOTLOADER_BIN) + $(call color_progress_prefix, "Name") + @echo $(BOOTLOADER_BIN) + $(call color_progress_prefix, "Size") + $(call disk_usage_KiB, $(BOOTLOADER_BIN)) + +kernel_elf: + make -C $(KERNEL_PATH) all + +bootloader_elf: + make -C $(BOOTLOADER_PATH) all + +kernel_gtk: $(KERNEL_BIN) cpio + $(call color_header, "Launching QEMU") + $(EXEC_QEMU) $(QEMU_DISPLAY_ARGS) -initrd $(INITRD_PATH) -dtb $(QEMU_DTB_PATH) -kernel $(KERNEL_BIN) + +kernel_qemu: $(KERNEL_BIN) cpio + $(call color_header, "Launching QEMU") + $(EXEC_QEMU) $(QEMU_RELEASE_ARGS) -initrd $(INITRD_PATH) -dtb $(QEMU_DTB_PATH) -kernel $(KERNEL_BIN) + + + +kernel_gdb: $(KERNEL_BIN) cpio + $(call color_header, "Launching QEMU in background") + $(EXEC_QEMU) $(QEMU_DEBUG_ARGS) -initrd $(INITRD_PATH) -dtb $(QEMU_DTB_PATH) -kernel $(KERNEL_BIN) + +bootloader_qemu:$(BOOTLOADER_BIN) $(KERNEL_BIN) cpio + $(call color_header, "Launching QEMU") + $(EXEC_QEMU) $(QEMU_TTY_ARGS) -dtb $(QEMU_DTB_PATH) -kernel $(BOOTLOADER_BIN) -initrd $(INITRD_PATH) + +bootloader_gdb: $(BOOTLOADER_BIN) $(KERNEL_BIN) cpio + $(call color_header, "Launching QEMU in background") + $(EXEC_QEMU) $(QEMU_TTY_DEBUG_ARGS) $(QEMU_DTB_PATH) -kernel $(BOOTLOADER_BIN) -initrd $(INITRD_PATH) + +$(USER_PROG_IMG): + make -C $(USER_PROG) all + +cpio: initramfs/* $(USER_PROG_IMG) + $(call color_header, "Creating initramfs") + cp $(USER_PROG_IMG) initramfs/ + cd initramfs && find . | cpio -H newc -o > ../initramfs.cpio + +clean: + make -C $(KERNEL_PATH) clean + make -C $(KERNEL_PATH) clean + -rm -r target + -rm $(KERNEL_BIN) + -rm $(BOOTLOADER_BIN) + -rm $(INITRD_PATH) diff --git a/README.md b/README.md new file mode 100644 index 000000000..7df1525a4 --- /dev/null +++ b/README.md @@ -0,0 +1,39 @@ + +# OSC2024 + +| Github Account | Student ID | Name | +|----------------|------------|---------------| +| cfmc30 | 109350078 | Erh-Tai Huang | + +## Requirements + +- Install Rust + ``` + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + source $HOME/.cargo/env + ``` + +- Tool Chain + ``` + cargo install cargo-binutils rustfilt + ``` + +- Others are specified in [rust-toolchain.toml](rust-toolchain.toml) which will be configured by cargo. + +## Things that can be improved + +- The structure of the project +- Rust code but with C style +- String with fixed size + +## Build + +``` +make +``` + +## Test With QEMU + +``` +make kernel_qemu +``` diff --git a/bootloader/Cargo.lock b/bootloader/Cargo.lock new file mode 100644 index 000000000..531d09f14 --- /dev/null +++ b/bootloader/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aarch64-cpu" +version = "9.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287" +dependencies = [ + "tock-registers", +] + +[[package]] +name = "lab1" +version = "0.1.0" +dependencies = [ + "aarch64-cpu", +] + +[[package]] +name = "tock-registers" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" diff --git a/bootloader/Cargo.toml b/bootloader/Cargo.toml new file mode 100644 index 000000000..88d388523 --- /dev/null +++ b/bootloader/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "bootloader" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.release] +lto = true + + +[features] +default = [] +bsp_rpi3 = [] + +# specify bin +[[bin]] +name = "bootloader" +path = "src/main.rs" + +[dependencies] +driver = { path = "../driver" } + + +# Platform specific dependencies +[target.'cfg(target_arch = "aarch64")'.dependencies] +aarch64-cpu = {version = "9.x.x"} + diff --git a/bootloader/Makefile b/bootloader/Makefile new file mode 100644 index 000000000..b85fa988f --- /dev/null +++ b/bootloader/Makefile @@ -0,0 +1,64 @@ +BSP = rpi3 + +TARGET = aarch64-unknown-none-softfloat +BL_BIN = bootloader.img + +OBJDUMP_BINARY = aarch64-none-elf-objdump +NM_BINARY = aarch64-none-elf-mn +READELF_BINARY = aarch64-none-elf-readelf + +LD_SCRIPT_PATH = $(shell pwd)/src/bsp/raspberrypi +RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 + +export LD_SCRIPT_PATH + +BL_MANIFEST = Cargo.toml +BL_LINKER_SCRIPT = bootloader.ld +LAST_BUILD_CONFIG = target/$(TARGET).build_config + +BL_ELF = ../target/$(TARGET)/release/bootloader + +# parses cargo's dep-info file. +BL_ELF_DEPS = $(filter-out %: ,$(file < $(BL_ELF).d)) $(BL_MANIFEST) $(LAST_BUILD_CONFIG) + + +# command building blocks + +RUSTFLAGS = $(RUSTC_MISC_ARGS) \ + -C link-arg=--library-path=$(LD_SCRIPT_PATH) \ + -C link-arg=--script=$(BL_LINKER_SCRIPT) + + +#RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) \ +# -D warnings \ +# -D missing_docs + +RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) +# -D warnings \ +# -D missing_docs + +FEATURES = --features bsp_$(BSP) +COMPILER_ARGS = --target=$(TARGET) \ + $(FEATURES) \ + --release + +RUSTC_CMD = cargo rustc $(COMPILER_ARGS) --bin bootloader +CLIPPY_CMD = cargo clippy $(COMPILER_ARGS) + + +.PHONY: all doc qemu clippy clean readelf objdump nm check + +all: $(BL_ELF) + +$(LAST_BUILD_CONFIG): + @rm -f target/*.build_config + @mkdir -p target + @touch $(LAST_BUILD_CONFIG) + +$(BL_ELF): $(BL_ELF_DEPS) + RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(RUSTC_CMD) + +clean: + -rm -r target + -rm $(BL_BIN) + diff --git a/bootloader/README.md b/bootloader/README.md new file mode 100644 index 000000000..50c5d4048 --- /dev/null +++ b/bootloader/README.md @@ -0,0 +1,39 @@ + +# OSC2024 + +| Github Account | Student ID | Name | +|----------------|------------|---------------| +| cfmc30 | 109350078 | Erh-Tai Huang | + +## Requirements + +- Install Rust + ``` + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh + source $HOME/.cargo/env + ``` + +- Tool Chain + ``` + cargo install cargo-binutils rustfilt + ``` + +- Others are specified in [rust-toolchain.toml](rust-toolchain.toml) which will be configured by cargo. + +## Things that can be improved + +- The structure of the project +- Rust code but with C style +- String with fixed size + +## Build + +``` +make +``` + +## Test With QEMU + +``` +make qemu +``` diff --git a/bootloader/build.rs b/bootloader/build.rs new file mode 100644 index 000000000..a6234085a --- /dev/null +++ b/bootloader/build.rs @@ -0,0 +1,21 @@ +use std::{env, fs, process}; + +fn main() { + let ld_script_path = match env::var("LD_SCRIPT_PATH") { + Ok(var) => var, + _ => process::exit(0), + }; + + let files = fs::read_dir(ld_script_path).unwrap(); + files + .filter_map(Result::ok) + .filter(|d| { + if let Some(e) = d.path().extension() { + e == "ld" + } + else { + false + } + }) + .for_each(|f| println!("cargo:rerun-if-changed={}", f.path().display())); +} diff --git a/bootloader/rust-toolchain.toml b/bootloader/rust-toolchain.toml new file mode 100644 index 000000000..a554c0a98 --- /dev/null +++ b/bootloader/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2022-10-13" +components = ["rust-src", "llvm-tools-preview", "rustfmt"] +targets = ["aarch64-unknown-none-softfloat"] diff --git a/bootloader/src/_arch/aarch64/cpu.rs b/bootloader/src/_arch/aarch64/cpu.rs new file mode 100644 index 000000000..8b5266023 --- /dev/null +++ b/bootloader/src/_arch/aarch64/cpu.rs @@ -0,0 +1,24 @@ +use aarch64_cpu::asm; + +// public code +/// pause execution on the core + + +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe(); + } +} + +/* +const int PM_RSTC = 0x2010001c; +const int PM_WDOG = 0x20100024; +const int PM_PASSWORD = 0x5a000000; +const int PM_RSTC_WRCFG_FULL_RESET = 0x00000020; + +PUT32(PM_WDOG, PM_PASSWORD | 1); // timeout = 1/16th of a second? (whatever) +PUT32(PM_RSTC, PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET); +*/ + + diff --git a/bootloader/src/_arch/aarch64/cpu/boot.rs b/bootloader/src/_arch/aarch64/cpu/boot.rs new file mode 100644 index 000000000..885ea465d --- /dev/null +++ b/bootloader/src/_arch/aarch64/cpu/boot.rs @@ -0,0 +1,18 @@ + +use core::arch::global_asm; +use core::arch::asm; + +use driver::uart; + +global_asm!( + include_str!("boot.s") , + CONST_CORE_ID_MASK = const 0b11 +); + +// mod uart; + +#[no_mangle] +pub unsafe fn _start_rust(){ + crate::bootloader(); + uart::uart_write_str("Bootloader finished\r\n"); +} diff --git a/bootloader/src/_arch/aarch64/cpu/boot.s b/bootloader/src/_arch/aarch64/cpu/boot.s new file mode 100644 index 000000000..a27fb4827 --- /dev/null +++ b/bootloader/src/_arch/aarch64/cpu/boot.s @@ -0,0 +1,83 @@ + +.macro ADR_REL register, symbol + adrp \register, \symbol + add \register, \register, #:lo12:\symbol +.endm + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- +.section .text._start + +//------------------------------------------------------------------------------ +// fn _start() +//------------------------------------------------------------------------------ +_start: + // Save the address of the device tree blob. + ; ldr x1, =__dtb_addr + ; str x0, [x1] + + // Only proceed on the boot core. Park it otherwise. + mrs x0, MPIDR_EL1 + and x0, x0, {CONST_CORE_ID_MASK} + ldr x1, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs + cmp x0, x1 + b.ne .L_parking_loop + + // get addresses + ldr x0, =0x60000 + ADR_REL x1, __relo_start + ADR_REL x2, __relo_end + +.L_relo_loop: + cmp x1, x2 + b.eq .L_relo_end + // load data from x1 + ldr x3, [x1] + // store into [x0] + str x3, [x0] + // move to next address + add x1, x1, #8 + add x0, x0, #8 + b .L_relo_loop + +.L_relo_end: + ldr x0, =.L_no_relo + br x0 + +.L_no_relo: + // Initialize DRAM. + ADR_REL x0, __bss_start + ADR_REL x1, __bss_end_exclusive + +.L_bss_init_loop: + cmp x0, x1 + b.eq .L_prepare_rust + stp xzr, xzr, [x0], #16 + b .L_bss_init_loop + + + // Prepare the jump to Rust code. +.L_prepare_rust: + // Set the stack pointer. + mov sp, 0x60000 + + // Jump to Rust code. + bl _start_rust + + // restore the address of the device tree blob. + ; ldr x1, =__dtb_addr + ; ldr x0, [x1] + + // jump to 0x80000 + ldr x1, =0x80000 + br x1 + + // Infinitely wait for events (aka "park the core"). +.L_parking_loop: + wfe + b .L_parking_loop + +.size _start, . - _start +.type _start, function +.global _start diff --git a/bootloader/src/_arch/aarch64/cpu/boot_bak.s b/bootloader/src/_arch/aarch64/cpu/boot_bak.s new file mode 100644 index 000000000..453dcd1f5 --- /dev/null +++ b/bootloader/src/_arch/aarch64/cpu/boot_bak.s @@ -0,0 +1,55 @@ + +.macro ADR_REL register, symbol + adrp \register, \symbol + add \register, \register, #:lo12:\symbol +.endm + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- +.section .text._start + +//------------------------------------------------------------------------------ +// fn _start() +//------------------------------------------------------------------------------ +_start: + // Only proceed on the boot core. Park it otherwise. + mrs x0, MPIDR_EL1 + and x0, x0, {CONST_CORE_ID_MASK} + ldr x1, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs + cmp x0, x1 + b.ne .L_parking_loop + + // If execution reaches here, it is the boot core. + + // Initialize DRAM. + ADR_REL x0, __bss_start + ADR_REL x1, __bss_end_exclusive + +.L_bss_init_loop: + cmp x0, x1 + b.eq .L_prepare_rust + stp xzr, xzr, [x0], #16 + b .L_bss_init_loop + + + // Prepare the jump to Rust code. +.L_prepare_rust: + // Set the stack pointer. + mov sp, 0x60000 + + // Jump to Rust code. + bl _start_rust + + // jump to 0x80000 + ldr x0, =0x80000 + br x0 + + // Infinitely wait for events (aka "park the core"). +.L_parking_loop: + wfe + b .L_parking_loop + +.size _start, . - _start +.type _start, function +.global _start diff --git a/bootloader/src/bsp.rs b/bootloader/src/bsp.rs new file mode 100644 index 000000000..a0751c7ec --- /dev/null +++ b/bootloader/src/bsp.rs @@ -0,0 +1,3 @@ +mod raspberrypi; +pub use raspberrypi::*; + diff --git a/bootloader/src/bsp/raspberrypi.rs b/bootloader/src/bsp/raspberrypi.rs new file mode 100644 index 000000000..3bfb62f3d --- /dev/null +++ b/bootloader/src/bsp/raspberrypi.rs @@ -0,0 +1 @@ +pub mod cpu; diff --git a/bootloader/src/bsp/raspberrypi/bootloader.ld b/bootloader/src/bsp/raspberrypi/bootloader.ld new file mode 100644 index 000000000..6e6eb21fc --- /dev/null +++ b/bootloader/src/bsp/raspberrypi/bootloader.ld @@ -0,0 +1,37 @@ + +__rpi_phys_dram_start_addr = 0; + +__rpi_phys_binary_load_addr = 0x60000; + + +ENTRY(__rpi_phys_binary_load_addr) + + +SECTIONS +{ + . = __rpi_phys_binary_load_addr; + __relo_start = .; + .text : + { + KEEP(*(.text._start)) + *(.text._start_arguments) /* Constants (or statics in Rust speak) read by _start(). */ + *(.text._start_rust) /* The Rust entry point */ + *(.text*) /* Everything else */ + } :segment_code + + .rodata : ALIGN(16) { *(.rodata*) } :segment_code + .data : ALIGN(16) { *(.data*) } + . = ALIGN(16); + __relo_end = .; + + + .bss (NOLOAD): ALIGN(16) + { + __bss_start = .; + *(.bss*); + . = ALIGN(16); + __bss_end_exclusive = .; + } + __dtb_addr = .; + +} \ No newline at end of file diff --git a/bootloader/src/bsp/raspberrypi/cpu.rs b/bootloader/src/bsp/raspberrypi/cpu.rs new file mode 100644 index 000000000..e899e2c00 --- /dev/null +++ b/bootloader/src/bsp/raspberrypi/cpu.rs @@ -0,0 +1,7 @@ + +// boot core id +#[no_mangle] +#[link_section = ".text._start_arguments"] +pub static BOOT_CORE_ID: u64 = 0; + + diff --git a/bootloader/src/cpu.rs b/bootloader/src/cpu.rs new file mode 100644 index 000000000..c029d9b00 --- /dev/null +++ b/bootloader/src/cpu.rs @@ -0,0 +1,8 @@ +#[cfg(target_arch = "aarch64")] +#[path = "_arch/aarch64/cpu.rs"] +mod arch_cpu; + +mod boot; + +pub use arch_cpu::wait_forever; + diff --git a/bootloader/src/cpu/boot.rs b/bootloader/src/cpu/boot.rs new file mode 100644 index 000000000..b244f2185 --- /dev/null +++ b/bootloader/src/cpu/boot.rs @@ -0,0 +1,3 @@ +#[cfg(target_arch = "aarch64")] +#[path = "../_arch/aarch64/cpu/boot.rs"] +mod arch_boot; diff --git a/bootloader/src/main.rs b/bootloader/src/main.rs new file mode 100644 index 000000000..bd9ab3452 --- /dev/null +++ b/bootloader/src/main.rs @@ -0,0 +1,62 @@ +#![feature(asm_const)] +#![no_main] +#![no_std] + +mod bsp; +mod cpu; +mod panic_wait; + + +use core::{ + ptr::{read_volatile, write_volatile}, + usize, +}; +use driver::uart; + +const KERNEL_LOAD_ADDR: u64 = 0x80000; + + +// load the kernel from uart +#[no_mangle] +unsafe fn bootloader(){ + // initialize uart + uart::init_uart(false); + uart::uart_write_str("Bootloader started\r\n"); + + // print current pc value + let mut pc: u64; + core::arch::asm!("adr {}, .", out(reg) pc); + uart::uart_write_str("Current PC: "); + uart::print_hex(pc as u32); + uart::uart_write_str("\r\n"); + + + // read kernel size + let mut kernel_size: u32 = 0; + + // read kernel size (4 bytes) from uart + uart::read(&mut kernel_size as *mut u32 as *mut u8 , 4); + uart::uart_write_str("Kernel size: "); + uart::print_hex(kernel_size); + uart::uart_write_str("\r\n"); + + // start to read kernel + let mut kernel_addr: u64 = KERNEL_LOAD_ADDR; + let mut read_size: u32 = 0; + while read_size < kernel_size { + let mut read_buf: [u8; 128] = [0; 128]; + let mut read_len: usize = 128; + if kernel_size - read_size < 128 { + read_len = (kernel_size - read_size) as usize; + } + uart::read(read_buf.as_mut_ptr(), read_len); + for i in 0..read_len { + write_volatile(kernel_addr as *mut u8, read_buf[i]); + kernel_addr += 1; + } + read_size += read_len as u32; + } + + // jump to kernel + uart::uart_write_str("Jump to kernel\r\n"); +} diff --git a/bootloader/src/panic_wait.rs b/bootloader/src/panic_wait.rs new file mode 100644 index 000000000..c297b7a74 --- /dev/null +++ b/bootloader/src/panic_wait.rs @@ -0,0 +1,7 @@ +use crate::cpu; +use core::panic::PanicInfo; + +#[panic_handler] +fn panic(_info: &PanicInfo) -> ! { + cpu::wait_forever() +} diff --git a/driver/Cargo.lock b/driver/Cargo.lock new file mode 100644 index 000000000..531d09f14 --- /dev/null +++ b/driver/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aarch64-cpu" +version = "9.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287" +dependencies = [ + "tock-registers", +] + +[[package]] +name = "lab1" +version = "0.1.0" +dependencies = [ + "aarch64-cpu", +] + +[[package]] +name = "tock-registers" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" diff --git a/driver/Cargo.toml b/driver/Cargo.toml new file mode 100644 index 000000000..3c7f6d9f7 --- /dev/null +++ b/driver/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "driver" +version = "0.1.0" +edition = "2021" + +[profile.release] +lto = true + +[features] +default = [] +bsp_rpi3 = [] + +[dependencies] + +# Platform specific dependencies +aarch64-cpu = {version = "9.x.x"} + +[target.'cfg(target_arch = "aarch64")'.dependencies] diff --git a/driver/src/addr_loader.rs b/driver/src/addr_loader.rs new file mode 100644 index 000000000..ef2ac0c19 --- /dev/null +++ b/driver/src/addr_loader.rs @@ -0,0 +1,16 @@ + +pub fn load_dtb_addr() -> *mut u8 { + let dtb_addr: *mut u64; + unsafe { + core::arch::asm!("ldr {}, =__dtb_addr", out(reg) dtb_addr); + (*dtb_addr) as *mut u8 + } +} + +pub fn usr_load_prog_base() -> *mut u8 { + let addr: *mut u64; + unsafe { + core::arch::asm!("ldr {}, =__usr_prog_start", out(reg) addr); + addr as *mut u8 + } +} diff --git a/driver/src/lib.rs b/driver/src/lib.rs new file mode 100644 index 000000000..b75ff74db --- /dev/null +++ b/driver/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(dead_code)] +#![no_std] +pub mod uart; +pub mod mailbox; +pub mod addr_loader; \ No newline at end of file diff --git a/driver/src/mailbox.rs b/driver/src/mailbox.rs new file mode 100644 index 000000000..ab54a62f2 --- /dev/null +++ b/driver/src/mailbox.rs @@ -0,0 +1,86 @@ +const MMIO_BASE: u32 = 0x3f000000; +const MAILBOX_BASE: u32 = MMIO_BASE + 0xb880; + +const MAILBOX_READ: u32 = MAILBOX_BASE; +const MAILBOX_STATUS: u32 = MAILBOX_BASE + 0x18; +const MAILBOX_WRITE: u32 = MAILBOX_BASE + 0x20; + +const MAILBOX_EMPTY: u32 = 0x40000000; +const MAILBOX_FULL: u32 = 0x80000000; + +use core::ptr::{read_volatile, write_volatile}; + +#[no_mangle] +#[inline(never)] +pub fn mailbox_call(ch:u32, mailbox: *const u32) { + // check if the mailbox is full + unsafe { + let mut status: u32; + loop { + status = read_volatile(MAILBOX_STATUS as *const u32); + if (status & MAILBOX_FULL) == 0 { + // mailbox is not full, we can write the message + break; + } + } + // Combine the message address (upper 28 bits) with channel number (lower 4 bits) + let channel = ch; + let message_address = mailbox as u32; + let combined_address = (message_address & 0xFFFFFFF0) | (channel & 0xF); + write_volatile(MAILBOX_WRITE as *mut u32, combined_address as u32); + + // check if the mailbox is empty + loop { + status = read_volatile(MAILBOX_STATUS as *const u32); + if (status & MAILBOX_EMPTY) == 0 { + // mailbox is not empty, we can read the message + break; + } + } + if read_volatile(MAILBOX_READ as *const u32) != (message_address as u32) {} + } +} + + + +const GET_BOARD_REVISION: u32 = 0x00010002; +const REQUEST_CODE: u32 = 0x00000000; +const REQUEST_SUCCEED: u32 = 0x80000000; +const REQUEST_FAILED: u32 = 0x80000001; +const TAG_REQUEST_CODE: u32 = 0x00000000; +const END_TAG: u32 = 0x00000000; + +pub fn get_board_revisioin() -> u32 { + let mut mailbox: [u32; 32] = [0; 32]; + let idx_offset: usize = (0x10 - mailbox.as_ptr() as u32 & 0xf) as usize; + + mailbox[0 + idx_offset] = 7 * 4; + mailbox[1 + idx_offset] = REQUEST_CODE; + mailbox[2 + idx_offset] = GET_BOARD_REVISION; + mailbox[3 + idx_offset] = 4; + mailbox[4 + idx_offset] = TAG_REQUEST_CODE; + mailbox[5 + idx_offset] = 0; + mailbox[6 + idx_offset] = END_TAG; + + mailbox_call(8,(mailbox[idx_offset..(idx_offset + 7)]).as_ptr()); + mailbox[5] +} + +// Get ARM memory base address and size using mailbox +const GET_ARM_MEMORY: u32 = 0x00010005; +pub fn get_arm_memory() -> (u32, u32) { + let mut mailbox: [u32; 32] = [0; 32]; + let idx_offset: usize = (0x10 - mailbox.as_ptr() as u32 & 0xf) as usize; + + mailbox[0 + idx_offset] = 8 * 4; + mailbox[1 + idx_offset] = REQUEST_CODE; + mailbox[2 + idx_offset] = GET_ARM_MEMORY; + mailbox[3 + idx_offset] = 8; + mailbox[4 + idx_offset] = TAG_REQUEST_CODE; + mailbox[5 + idx_offset] = 0; + mailbox[6 + idx_offset] = 0; + mailbox[7 + idx_offset] = END_TAG; + + mailbox_call(8, mailbox[idx_offset..(idx_offset + 8)].as_ptr()); + (mailbox[5], mailbox[6]) +} diff --git a/driver/src/uart.rs b/driver/src/uart.rs new file mode 100644 index 000000000..18f1cc0ef --- /dev/null +++ b/driver/src/uart.rs @@ -0,0 +1,404 @@ +use core::arch::asm; +use core::u32; +use core::{ + ptr::{read_volatile, write_volatile}, + usize, +}; + +pub const AUXENB: u32 = 0x3F215004; +pub const AUX_MU_CNTL_REG: u32 = 0x3F215060; +pub const AUX_MU_IER_REG: u32 = 0x3F215044; +pub const AUX_MU_LCR_REG: u32 = 0x3F21504C; +pub const AUX_MU_MCR_REG: u32 = 0x3F215050; +pub const AUX_MU_BAUD_REG: u32 = 0x3F215068; +pub const AUX_MU_IIR_REG: u32 = 0x3F215048; +pub const AUX_MU_IO_REG: u32 = 0x3F215040; +pub const AUX_MU_LSR_REG: u32 = 0x3F215054; + +const GPFSEL_BASE: u32 = 0x3F200000; +const GPFSEL0: u32 = 0x3F200000; +const GPFSEL1: u32 = 0x3F200004; + +const GPPUD: u32 = 0x3F200094; +const GPPUDCLK0: u32 = 0x3F200098; +const GPPUDCLK1: u32 = 0x3F20009C; + +pub fn is_buf_empty() -> bool { + unsafe { READ_RING_BUFFER.is_empty() } +} + +struct RingBuffer { + buffer: [u8; 1024], + head: usize, + tail: usize, +} + +impl RingBuffer { + pub fn new() -> RingBuffer { + RingBuffer { + buffer: [0; 1024], + head: 0, + tail: 0, + } + } + + pub fn write(&mut self, c: u8) { + self.buffer[self.head] = c; + self.head = (self.head + 1) % 1024; + } + + pub fn read(&mut self) -> Option { + if self.head == self.tail { + None + } else { + let c = self.buffer[self.tail]; + self.tail = (self.tail + 1) % 1024; + Some(c) + } + } + + pub fn is_empty(&self) -> bool { + self.head == self.tail + } + + pub fn is_full(&self) -> bool { + (self.head + 1) % 1024 == self.tail + } +} + +static mut READ_RING_BUFFER: RingBuffer = RingBuffer { + buffer: [0; 1024], + head: 0, + tail: 0, +}; + +static mut WRITE_RING_BUFFER: RingBuffer = RingBuffer { + buffer: [0; 1024], + head: 0, + tail: 0, +}; + +#[no_mangle] +#[inline(never)] +pub fn push_read_buf(c: u8) { + unsafe { + READ_RING_BUFFER.write(c); + } +} + +#[no_mangle] +#[inline(never)] +pub fn pop_read_buf() -> Option { + unsafe { READ_RING_BUFFER.read() } +} + +#[no_mangle] +#[inline(never)] +pub fn pop_write_buf() -> Option { + unsafe { WRITE_RING_BUFFER.read() } +} + +#[no_mangle] +#[inline(never)] +pub fn push_write_buf(c: u8) { + unsafe { + WRITE_RING_BUFFER.write(c); + } +} + +#[no_mangle] +#[inline(never)] +pub fn async_getline(s: &mut [u8; 128], is_echo: bool) -> &str { + let mut ptr: usize = 0; + loop { + if let Some(i) = pop_read_buf() { + if is_echo { + write_u8_buf(i as u8); + flush(); + } + if i == 13 { + write_u8_buf(10); + break; + } + s[ptr] = i as u8; + ptr = ptr + 1; + } + } + core::str::from_utf8(&s[0..ptr]).unwrap() +} + +pub fn flush() { + write_int(true); +} + +pub fn write_u8_buf(c: u8) { + unsafe { + WRITE_RING_BUFFER.write(c); + } +} + +#[no_mangle] +#[inline(never)] +pub fn write_int(enable: bool) { + unsafe { + // enable interrupt + if enable { + write_volatile(AUX_MU_IER_REG as *mut u32, 0b11); + } else { + write_volatile(AUX_MU_IER_REG as *mut u32, 0b01); + } + } +} + +#[no_mangle] +#[inline(never)] +pub fn send_write_buf() { + loop { + if let Some(s) = pop_write_buf() { + unsafe { write_u8(s) }; + } else { + break; + } + } + write_int(false); +} +// Initialize the UART +#[no_mangle] +#[inline(never)] +pub fn init_uart(int: bool) { + unsafe { + // configure GPFSEL1 register to set FSEL14 FSEL15 to ALT5 + let fsel = read_volatile(GPFSEL1 as *mut u32); + let fsel_mask = !(0b111111 << 12); + let fsel_set = 0b010010 << 12; + write_volatile(GPFSEL1 as *mut u32, (fsel & fsel_mask) | fsel_set); + + // configure pull up/down register to disable GPIO pull up/down + let pud = 0b0; + write_volatile(GPPUD as *mut u32, pud); + + // wait 150 cycles + uart_nops(); + + // configure pull up/down clock register to disable GPIO pull up/down + let pudclk0 = !(0b11 << 14); + write_volatile(GPPUDCLK0 as *mut u32, pudclk0); + let pudclk1 = 0; + write_volatile(GPPUDCLK1 as *mut u32, pudclk1); + // wait 150 cycles + uart_nops(); + + // Write to GPPUD to remove the control signal + write_volatile(GPPUD as *mut u32, 0); + // Write to GPPUDCLK0 to remove the clock + write_volatile(GPPUDCLK0 as *mut u32, 0); + + // write some word to uart to initialize it + // Set AUXENB register to enable mini UART + write_volatile(AUXENB as *mut u32, 1); + + // Set AUX_MU_CNTL_REG to 0 + write_volatile(AUX_MU_CNTL_REG as *mut u32, 0); + if int { + // Set AUX_MU_IER_REG to 1 (enable receive interrupt) + write_volatile(AUX_MU_IER_REG as *mut u32, 0b1); + const IRQS1_ADDR: *mut u32 = 0x3f00b210 as *mut u32; + const IRQS2_ADDR: *mut u32 = 0x3f00b214 as *mut u32; + // Set IRQs1 register to enable mini UART interrupt + write_volatile(IRQS1_ADDR, 1 << 29); + // write_volatile(IRQS2_ADDR, 1 << 25); + } else { + // Set AUX_MU_IER_REG to 0 (disable interrupts) + write_volatile(AUX_MU_IER_REG as *mut u32, 0); + } + + // Set AUX_MU_LCR_REG to 3 + write_volatile(AUX_MU_LCR_REG as *mut u32, 3); + // Set AUX_MU_MCR_REG to 0 + write_volatile(AUX_MU_MCR_REG as *mut u32, 0); + // Set AUX_MU_BAUD_REG to 270 + write_volatile(AUX_MU_BAUD_REG as *mut u32, 270); + // Set AUX_MU_IIR_REG to 6 + write_volatile(AUX_MU_IIR_REG as *mut u32, 6); + // Set AUX_MU_CNTL_REG to 3 + write_volatile(AUX_MU_CNTL_REG as *mut u32, 3); + READ_RING_BUFFER.head = 0; + READ_RING_BUFFER.tail = 0; + WRITE_RING_BUFFER.head = 0; + WRITE_RING_BUFFER.tail = 0; + } +} +pub struct Uart; + +use core::fmt::{self, Write}; + +pub struct UartWriter; +pub struct UartWriterPolling; + +// core::fmt::write needs a mutable reference to the writer +// we create a UartWriter which is implemented for Write trait with write_str method +// write_str method writes the formatted string to the buffer +impl Write for UartWriter { + fn write_str(&mut self, s: &str) -> fmt::Result { + for i in s.bytes() { + write_u8_buf(i); + } + write_int(true); + Ok(()) + } + fn write_fmt(&mut self, args: core::fmt::Arguments) -> core::fmt::Result { + core::fmt::write(&mut UartWriter, args).unwrap(); + Ok(()) + } +} + +impl Write for UartWriterPolling { + fn write_str(&mut self, s: &str) -> fmt::Result { + for i in s.bytes() { + unsafe { write_u8(i) }; + } + Ok(()) + } + fn write_fmt(&mut self, args: core::fmt::Arguments) -> core::fmt::Result { + core::fmt::write(&mut UartWriterPolling, args).unwrap(); + Ok(()) + } +} + +impl UartWriterPolling { + pub fn new() -> UartWriterPolling { + UartWriterPolling + } +} + +impl UartWriter { + pub fn new() -> UartWriter { + UartWriter + } +} + +impl Uart { + pub fn new() -> Uart { + Uart + } +} + +#[no_mangle] +#[inline(never)] +pub fn uart_write_str(s: &str) { + for i in s.bytes() { + unsafe { write_u8(i) }; + } +} + +#[no_mangle] +pub fn getline(s: &mut [u8; 128], is_echo: bool) -> &str { + let mut ptr: usize = 0; + unsafe { + loop { + let c = read_u8(); + match c { + Some(i) => { + if is_echo { + write_u8(i as u8); + } + if i == 13 { + write_u8(10); + break; + } + s[ptr] = i as u8; + ptr = ptr + 1; + } + None => {} + } + } + } + core::str::from_utf8(&s[0..ptr]).unwrap() +} + +#[no_mangle] +pub unsafe fn uart_nops() { + for _ in 0..150 { + asm!("nop"); + } +} + +// Function to print something using the UART +#[no_mangle] +pub unsafe fn write_u8(s: u8) { + loop { + if (read_volatile(AUX_MU_LSR_REG as *mut u32) & 0b100000) != 0 { + break; + } + } + write_volatile(AUX_MU_IO_REG as *mut u8, s as u8); +} + +#[no_mangle] +pub unsafe fn read_u8() -> Option { + let lsr: u32 = read_volatile(AUX_MU_LSR_REG as *mut u32) & 0b1; + if lsr != 0 { + Some(read_volatile(AUX_MU_IO_REG as *mut u8)) + } else { + None + } +} + +pub unsafe fn read(s: *mut u8, len: usize) { + let mut ptr: usize = 0; + while ptr < len { + let c = read_u8(); + match c { + Some(i) => { + write_volatile(s.add(ptr), i as u8); + ptr = ptr + 1; + } + None => {} + } + } +} + +// print u32 in hex +#[no_mangle] +pub fn print_hex(n: u32) { + let mut buf: [u8; 8] = [0; 8]; + let mut ptr: usize = 0; + let mut num: u32 = n; + for _ in 0..8 { + let rem: u8 = (num % 16) as u8; + if rem < 10 { + buf[ptr] = (rem + b'0') as u8; + } else { + buf[ptr] = (rem - 10 + b'A') as u8; + } + ptr = ptr + 1; + num = num / 16; + } + for i in buf.iter().take(8).rev() { + unsafe { + write_u8(*i); + } + } +} + +pub fn strncmp(s1: &str, s2: &str, n: usize) -> bool { + let mut i = 0; + while i < n { + if s1.as_bytes()[i] != s2.as_bytes()[i] { + return false; + } + i = i + 1; + } + true +} + +pub fn reboot() { + const PM_PASSWORD: u32 = 0x5a000000; + const PM_RSTC: u32 = 0x3F10001c; + const PM_WDOG: u32 = 0x3F100024; + const PM_RSTC_WRCFG_FULL_RESET: u32 = 0x00000020; + unsafe { + write_volatile(PM_WDOG as *mut u32, PM_PASSWORD | 100); + write_volatile(PM_RSTC as *mut u32, PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET); + } +} diff --git a/initramfs/KAF.txt b/initramfs/KAF.txt new file mode 100644 index 000000000..4a34cdad9 --- /dev/null +++ b/initramfs/KAF.txt @@ -0,0 +1 @@ +KAF is very kawaii \ No newline at end of file diff --git a/initramfs/c.txt b/initramfs/c.txt new file mode 100644 index 000000000..01e8bba17 --- /dev/null +++ b/initramfs/c.txt @@ -0,0 +1 @@ +I love C. \ No newline at end of file diff --git a/initramfs/catGift.txt b/initramfs/catGift.txt new file mode 100644 index 000000000..3cba9a32c --- /dev/null +++ b/initramfs/catGift.txt @@ -0,0 +1,38 @@ +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift +catGift diff --git a/initramfs/file1.txt b/initramfs/file1.txt new file mode 100644 index 000000000..b062f1906 --- /dev/null +++ b/initramfs/file1.txt @@ -0,0 +1 @@ +This is file1. \ No newline at end of file diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock new file mode 100644 index 000000000..531d09f14 --- /dev/null +++ b/kernel/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aarch64-cpu" +version = "9.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287" +dependencies = [ + "tock-registers", +] + +[[package]] +name = "lab1" +version = "0.1.0" +dependencies = [ + "aarch64-cpu", +] + +[[package]] +name = "tock-registers" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml new file mode 100644 index 000000000..5cc53086d --- /dev/null +++ b/kernel/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "kernel" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[profile.release] +lto = true + + +[features] +default = [] +bsp_rpi3 = [] + +# specify bin +[[bin]] +name = "kernel" +path = "src/main.rs" + +[dependencies] +driver = { path = "../driver" } +fdt = "0.1.5" + +# Platform specific dependencies +[target.'cfg(target_arch = "aarch64")'.dependencies] +aarch64-cpu = {version = "9.x.x"} + diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100644 index 000000000..7b19ef7c7 --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,70 @@ + +BSP = rpi3 + +TARGET = aarch64-unknown-none-softfloat +KERNEL_BIN = kernel8.img +QEMU_BINARY = qemu-system-aarch64 +QEMU_MACHINE_TYPE = raspi3b +QEMU_RELEASE_ARGS = -display none -serial null -serial stdio +QEMU_DEBUG_ARGS = -display none -S -s -serial null -serial stdio +OBJDUMP_BINARY = aarch64-none-elf-objdump +NM_BINARY = aarch64-none-elf-mn +READELF_BINARY = aarch64-none-elf-readelf +LD_SCRIPT_PATH = $(shell pwd)/src/bsp/raspberrypi +RUSTC_MISC_ARGS = -C target-cpu=cortex-a53 + +export LD_SCRIPT_PATH + +KERNEL_MANIFEST = Cargo.toml +KERNEL_LINKER_SCRIPT = kernel.ld +LAST_BUILD_CONFIG = target/$(TARGET).build_config + +KERNEL_ELF = ../target/$(TARGET)/release/kernel + +# parses cargo's dep-info file. +KERNEL_ELF_DEPS = $(filter-out %: ,$(file < $(KERNEL_ELF).d)) $(KERNEL_MANIFEST) $(LAST_BUILD_CONFIG) + + +# command building blocks + +RUSTFLAGS = $(RUSTC_MISC_ARGS) \ + -C link-arg=--library-path=$(LD_SCRIPT_PATH) \ + -C link-arg=--script=$(KERNEL_LINKER_SCRIPT) + + +#RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) \ +# -D warnings \ +# -D missing_docs + +RUSTFLAGS_PEDANTIC = $(RUSTFLAGS) +# -D warnings \ +# -D missing_docs + +FEATURES = --features bsp_$(BSP) +COMPILER_ARGS = --target=$(TARGET) \ + $(FEATURES) \ + --release + +RUSTC_CMD = cargo rustc $(COMPILER_ARGS) --bin kernel +CLIPPY_CMD = cargo clippy $(COMPILER_ARGS) + + +.PHONY: all doc qemu clippy clean readelf objdump nm check + +all: $(KERNEL_ELF) + +$(LAST_BUILD_CONFIG): + @rm -f target/*.build_config + @mkdir -p target + @touch $(LAST_BUILD_CONFIG) + +$(KERNEL_ELF): $(KERNEL_ELF_DEPS) + RUSTFLAGS="$(RUSTFLAGS_PEDANTIC)" $(RUSTC_CMD) + +# -- -C panic=abort -C panic=abort -C opt-level=3 + + +clean: + -rm -r target + -rm $(KERNEL_BIN) + diff --git a/kernel/README.md b/kernel/README.md new file mode 100644 index 000000000..89689ef91 --- /dev/null +++ b/kernel/README.md @@ -0,0 +1,10 @@ + +# Kernel + +## New Feature + +- Allocator + Todo: Heap management + +- Macro `println!` + Need well implenmented allocator diff --git a/kernel/build.rs b/kernel/build.rs new file mode 100644 index 000000000..a6234085a --- /dev/null +++ b/kernel/build.rs @@ -0,0 +1,21 @@ +use std::{env, fs, process}; + +fn main() { + let ld_script_path = match env::var("LD_SCRIPT_PATH") { + Ok(var) => var, + _ => process::exit(0), + }; + + let files = fs::read_dir(ld_script_path).unwrap(); + files + .filter_map(Result::ok) + .filter(|d| { + if let Some(e) = d.path().extension() { + e == "ld" + } + else { + false + } + }) + .for_each(|f| println!("cargo:rerun-if-changed={}", f.path().display())); +} diff --git a/kernel/rust-toolchain.toml b/kernel/rust-toolchain.toml new file mode 100644 index 000000000..a554c0a98 --- /dev/null +++ b/kernel/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly-2022-10-13" +components = ["rust-src", "llvm-tools-preview", "rustfmt"] +targets = ["aarch64-unknown-none-softfloat"] diff --git a/kernel/src/_arch/aarch64/cpu.rs b/kernel/src/_arch/aarch64/cpu.rs new file mode 100644 index 000000000..ae3f3946d --- /dev/null +++ b/kernel/src/_arch/aarch64/cpu.rs @@ -0,0 +1,11 @@ +use aarch64_cpu::asm; + +// public code +/// pause execution on the core + +#[inline(always)] +pub fn wait_forever() -> ! { + loop { + asm::wfe(); + } +} diff --git a/kernel/src/_arch/aarch64/cpu/boot.rs b/kernel/src/_arch/aarch64/cpu/boot.rs new file mode 100644 index 000000000..8670a9dbd --- /dev/null +++ b/kernel/src/_arch/aarch64/cpu/boot.rs @@ -0,0 +1,14 @@ +use core::arch::global_asm; + +global_asm!( + include_str!("boot.s") , + CONST_CORE_ID_MASK = const 0b11 +); + + +// mod uart; + +#[no_mangle] +pub unsafe fn _start_rust() { + crate::kernel_init() +} diff --git a/kernel/src/_arch/aarch64/cpu/boot.s b/kernel/src/_arch/aarch64/cpu/boot.s new file mode 100644 index 000000000..3de19d149 --- /dev/null +++ b/kernel/src/_arch/aarch64/cpu/boot.s @@ -0,0 +1,75 @@ + +.macro ADR_REL register, symbol + adrp \register, \symbol + add \register, \register, #:lo12:\symbol +.endm + +//-------------------------------------------------------------------------------------------------- +// Public Code +//-------------------------------------------------------------------------------------------------- +.section .text._start + +//------------------------------------------------------------------------------ +// fn _start() +//------------------------------------------------------------------------------ +_start: + // Only proceed on the boot core. Park it otherwise. + // save dtb address (x0) to uar + ADR_REL x1, __dtb_addr + str x0, [x1] + + mrs x0, MPIDR_EL1 + and x0, x0, {CONST_CORE_ID_MASK} + ldr x1, BOOT_CORE_ID // provided by bsp/__board_name__/cpu.rs + cmp x0, x1 + b.ne .L_parking_loop + + mov x0, 1 + msr cntp_ctl_el0, x0 // enable + mrs x0, cntfrq_el0 + //msr cntp_tval_el0, x0 // set expired time + // mov x0, 2 + +set_exception_vector_table: + adr x0, exception_vector_table + msr vbar_el1, x0 + + // If execution reaches here, it is the boot core. + bl from_el2_to_el1 + + // Initialize DRAM. + ADR_REL x0, __bss_start + ADR_REL x1, __bss_end_exclusive + +.L_bss_init_loop: + cmp x0, x1 + b.eq .L_prepare_rust + stp xzr, xzr, [x0], #16 + b .L_bss_init_loop + + + // Prepare the jump to Rust code. +.L_prepare_rust: + // Set the stack pointer. + mov sp, 0x80000 + + // Jump to Rust code. + b _start_rust + + // Infinitely wait for events (aka "park the core"). +.L_parking_loop: + wfe + b .L_parking_loop + +from_el2_to_el1: + mov x0, (1 << 31) // EL1 uses aarch64 + msr hcr_el2, x0 + // mov x0, 0x3c5 // EL1h (SPSel = 1) with interrupt disabled + mov x0, 0x345 // EL1h (SPSel = 0) with interrupt enabled + msr spsr_el2, x0 + msr elr_el2, lr + eret // return to EL1 + +.size _start, . - _start +.type _start, function +.global _start diff --git a/kernel/src/bsp.rs b/kernel/src/bsp.rs new file mode 100644 index 000000000..a0751c7ec --- /dev/null +++ b/kernel/src/bsp.rs @@ -0,0 +1,3 @@ +mod raspberrypi; +pub use raspberrypi::*; + diff --git a/kernel/src/bsp/raspberrypi.rs b/kernel/src/bsp/raspberrypi.rs new file mode 100644 index 000000000..3bfb62f3d --- /dev/null +++ b/kernel/src/bsp/raspberrypi.rs @@ -0,0 +1 @@ +pub mod cpu; diff --git a/kernel/src/bsp/raspberrypi/cpu.rs b/kernel/src/bsp/raspberrypi/cpu.rs new file mode 100644 index 000000000..e899e2c00 --- /dev/null +++ b/kernel/src/bsp/raspberrypi/cpu.rs @@ -0,0 +1,7 @@ + +// boot core id +#[no_mangle] +#[link_section = ".text._start_arguments"] +pub static BOOT_CORE_ID: u64 = 0; + + diff --git a/kernel/src/bsp/raspberrypi/kernel.ld b/kernel/src/bsp/raspberrypi/kernel.ld new file mode 100644 index 000000000..c66d93532 --- /dev/null +++ b/kernel/src/bsp/raspberrypi/kernel.ld @@ -0,0 +1,39 @@ + +__rpi_phys_dram_start_addr = 0; + +__rpi_phys_binary_load_addr = 0x80000; + +ENTRY(__rpi_phys_binary_load_addr) + + +SECTIONS +{ + . = __rpi_phys_binary_load_addr; + .text : + { + KEEP(*(.text._start)) + *(.text._start_arguments) /* Constants (or statics in Rust speak) read by _start(). */ + *(.text._start_rust) /* The Rust entry point */ + *(.text*) /* Everything else */ + } :segment_code + + .rodata : ALIGN(8) { *(.rodata*) } :segment_code + + .data : { *(.data*) } + + .bss (NOLOAD): ALIGN(16) + { + __bss_start = .; + *(.bss*); + . = ALIGN(16); + __bss_end_exclusive = .; + } + . = ALIGN(8); + .usr_prog(NOLOAD): { + __usr_prog_start = .; + . = . + 0x1000; + __usr_prog_end = .; + } + __dtb_addr = .; + +} diff --git a/kernel/src/cpu.rs b/kernel/src/cpu.rs new file mode 100644 index 000000000..bed0bb09c --- /dev/null +++ b/kernel/src/cpu.rs @@ -0,0 +1,7 @@ +#[path = "_arch/aarch64/cpu.rs"] +mod arch_cpu; + +mod boot; + +pub use arch_cpu::wait_forever; + diff --git a/kernel/src/cpu/boot.rs b/kernel/src/cpu/boot.rs new file mode 100644 index 000000000..b244f2185 --- /dev/null +++ b/kernel/src/cpu/boot.rs @@ -0,0 +1,3 @@ +#[cfg(target_arch = "aarch64")] +#[path = "../_arch/aarch64/cpu/boot.rs"] +mod arch_boot; diff --git a/kernel/src/fdt.rs b/kernel/src/fdt.rs new file mode 100644 index 000000000..759aa694d --- /dev/null +++ b/kernel/src/fdt.rs @@ -0,0 +1,227 @@ +// A parser parse device tree; + +use alloc::vec::Vec; + +#[repr(C, packed)] +pub struct FdtHeader { + pub magic: u32, + pub totalsize: u32, + pub off_dt_struct: u32, + pub off_dt_strings: u32, + pub off_mem_rsvmap: u32, + pub version: u32, + pub last_comp_version: u32, + pub boot_cpuid_phys: u32, + pub size_dt_strings: u32, + pub size_dt_struct: u32, +} + +pub struct FdtReserveEntry { + address: u64, + size: u64, +} +impl FdtReserveEntry { + pub fn get_address(&self) -> u64 { + self.address + } + pub fn get_size(&self) -> u64 { + self.size + } +} + +#[repr(C, packed)] +struct FdtProp { + len: u32, + nameoff: u32, +} + +pub struct DtbParser { + pub header: *mut FdtHeader, + pub magic: u32, + pub totalsize: u32, + pub off_dt_struct: u32, + pub off_dt_strings: u32, + pub off_mem_rsvmap: u32, + pub version: u32, + pub last_comp_version: u32, + pub boot_cpuid_phys: u32, + pub size_dt_strings: u32, + pub size_dt_struct: u32, + pub fdt_reserve_entries: Vec, +} + +impl DtbParser { + pub fn new(dtb: *const u8) -> DtbParser { + let h: *mut FdtHeader = dtb as *mut FdtHeader; + unsafe { + DtbParser { + header: dtb as *mut FdtHeader, + magic: (*h).magic.swap_bytes(), + totalsize: (*h).totalsize.swap_bytes(), + off_dt_struct: (*h).off_dt_struct.swap_bytes(), + off_dt_strings: (*h).off_dt_strings.swap_bytes(), + off_mem_rsvmap: (*h).off_mem_rsvmap.swap_bytes(), + version: (*h).version.swap_bytes(), + last_comp_version: (*h).last_comp_version.swap_bytes(), + boot_cpuid_phys: (*h).boot_cpuid_phys.swap_bytes(), + size_dt_strings: (*h).size_dt_strings.swap_bytes(), + size_dt_struct: (*h).size_dt_struct.swap_bytes(), + fdt_reserve_entries: mem_rsvmap_paser(dtb, (*h).off_mem_rsvmap), + } + } + } + + pub fn get_fdt_reserve_entries(&self) -> &Vec { + &self.fdt_reserve_entries + } + // parse structure block + pub fn parse_struct_block(&self) { + let mut offset = self.off_dt_struct as usize; + let h: *mut u8 = self.header as *mut u8; + unsafe { + loop { + crate::println!("Offset: {:#x}", offset); + let token = (*(h.add(offset) as *const u32)).swap_bytes(); + match token { + FDT_BEGIN_NODE => { + crate::println!("Node: "); + offset += 4; + let name = c_str_to_str(h.add(offset)); + crate::println!("\tName: /{}", name); + crate::println!("\tName Length: {}", name.len()); + offset += name.len() + 1; + } + FDT_END_NODE => { + crate::println!("End Node"); + offset += 4; + } + FDT_PROP => { + offset += 4; + let prop = h.add(offset as usize) as *const FdtProp; + let len = (*prop).len.swap_bytes() as usize; + let nameoff = (*prop).nameoff.swap_bytes() as usize; + let name = h.add(nameoff + self.off_dt_strings as usize); + // print name of property + crate::println!("Property: "); + crate::println!("\tLength: {}", len); + crate::println!("\tName: {}", c_str_to_str(name)); + // print value + let value = h.add(offset + 8); + crate::println!("\tValue:"); + crate::print!("\t\t"); + for i in 0..len { + crate::print!("{:02x} ", *value.add(i)); + if i % 16 == 15 { + crate::println!(); + crate::print!("\t\t"); + } + } + crate::println!(); + crate::print!("\t\t"); + for i in 0..len { + let val = *value.add(i); + if val >= 32 && val <= 126 { + crate::print!("{}", val as char); + } else { + crate::print!("."); + } + } + crate::println!(); + offset += 8 + len; + } + FDT_NOP => { + crate::println!("NOP"); + offset += 4; + } + FDT_END => { + crate::println!("End"); + break; + } + _ => { + crate::println!("Unknown token: {:#x}", token); + break; + } + } + crate::println!(); + // next token + offset = (offset + 3) & !3; + } + } + } + + pub fn traverse(&self, callback: fn(&str, *const u8, usize) -> Option) -> Option { + let mut offset = self.off_dt_struct as usize; + let h: *mut u8 = self.header as *mut u8; + unsafe { + loop { + let token = (*(h.add(offset) as *const u32)).swap_bytes(); + match token { + FDT_BEGIN_NODE => { + offset += 4; + let name = c_str_to_str(h.add(offset)); + offset += name.len() + 1; + } + FDT_END_NODE => { + offset += 4; + } + FDT_PROP => { + offset += 4; + let prop = h.add(offset as usize) as *const FdtProp; + let len = (*prop).len.swap_bytes() as usize; + let nameoff = (*prop).nameoff.swap_bytes() as usize; + let name = h.add(nameoff + self.off_dt_strings as usize); + let value = h.add(offset + 8); + if let Some(ret) = callback(c_str_to_str(name), value, len) { + return Some(ret); + } + offset += 8 + len; + } + FDT_NOP => { + offset += 4; + } + FDT_END => { + break None; + } + _ => { + crate::println!("Unknown token: {:#x}", token); + break None; + } + } + // next token + offset = (offset + 3) & !3; + } + } + } + + pub fn get_dtb_size(&self) -> u32 { + return self.totalsize; + } +} + +fn mem_rsvmap_paser(dtb: *const u8, off_mem_rsvmap: u32) -> Vec { + let h: *mut FdtHeader = dtb as *mut FdtHeader; + let mut entries = Vec::new(); + let mut offset = off_mem_rsvmap.swap_bytes(); + unsafe { + loop { + let address = *(dtb.add(offset as usize) as *const u64); + let size = (*(dtb.add(offset as usize + 8) as *const u64)).swap_bytes(); + if address == 0 && size == 0 { + break; + } + entries.push(FdtReserveEntry { address, size }); + offset += 16; + } + } + entries +} + +const FDT_BEGIN_NODE: u32 = 0x00000001; +const FDT_END_NODE: u32 = 0x00000002; +const FDT_PROP: u32 = 0x00000003; +const FDT_NOP: u32 = 0x00000004; +const FDT_END: u32 = 0x00000009; + +fn c_str_to_str(s: *const u8) -> &'static str { + unsafe { core::str::from_utf8_unchecked(core::ffi::CStr::from_ptr(s as *const i8).to_bytes()) } +} diff --git a/kernel/src/fs/cpio.rs b/kernel/src/fs/cpio.rs new file mode 100644 index 000000000..3ec3badec --- /dev/null +++ b/kernel/src/fs/cpio.rs @@ -0,0 +1,188 @@ +extern crate alloc; + +use alloc::string::String; + +// a paraser to read cpio archive +#[repr(C, packed)] +struct CpioNewcHeader { + c_magic: [u8; 6], + c_ino: [u8; 8], + c_mode: [u8; 8], + c_uid: [u8; 8], + c_gid: [u8; 8], + c_nlink: [u8; 8], + c_mtime: [u8; 8], + c_filesize: [u8; 8], + c_devmajor: [u8; 8], + c_devminor: [u8; 8], + c_rdevmajor: [u8; 8], + c_rdevminor: [u8; 8], + c_namesize: [u8; 8], + c_check: [u8; 8], +} + +pub struct CpioHandler { + cpio_start: *mut CpioNewcHeader, +} + +impl CpioHandler { + pub fn new(cpio_start: *mut u8) -> CpioHandler { + CpioHandler { + cpio_start: cpio_start as *mut CpioNewcHeader, + } + } + + pub fn get_files(&self) -> CpioIterator { + CpioIterator::new(&CpioHeaderWrapper::new(self.cpio_start)) + } +} + +pub struct CpioIterator { + cur_header: CpioHeaderWrapper, +} + +impl CpioIterator { + fn new(cpio_header: &CpioHeaderWrapper) -> CpioIterator { + CpioIterator { + cur_header: CpioHeaderWrapper::new(cpio_header.header), + } + } +} + +impl Iterator for CpioIterator { + type Item = CpioFile; + + fn next(&mut self) -> Option { + if self.cur_header.get_file_name() == "TRAILER!!!" { + return None; + } + let file_name = self.cur_header.get_file_name(); + let file_size = self.cur_header.get_file_size(); + let file_content = self.cur_header.get_current_file_content_ptr(); + let file = CpioFile::new(String::from(file_name), file_content, file_size); + self.cur_header = self.cur_header.get_next_file(); + Some(file) + } + +} + +struct CpioHeaderWrapper { + header: *mut CpioNewcHeader, +} + +impl CpioHeaderWrapper { + pub fn new(header: *mut CpioNewcHeader) -> CpioHeaderWrapper { + CpioHeaderWrapper { + header, + } + } + + pub fn get_file_name(&self) -> &str { + let namesize = unsafe {hex_to_u64(&(*self.header).c_namesize)}; + let name_ptr = (self.header as u64 + core::mem::size_of::() as u64) as *mut u8; + unsafe { + core::str::from_utf8_unchecked(core::slice::from_raw_parts( + name_ptr, + (namesize - 1) as usize, + )) + } + } + pub fn get_file_size(&self) -> usize { + let filesize = unsafe {hex_to_u64(&(*self.header).c_filesize)}; + filesize as usize + } + pub fn get_next_file(&mut self) -> CpioHeaderWrapper { + let mut offset = core::mem::size_of::() as usize; + let namesize = unsafe {hex_to_u64(&(*self.header).c_namesize)} as usize; + offset += namesize; + if offset % 4 != 0 { + offset += 4 - offset % 4; + } + let filesize = unsafe {hex_to_u64(&(*self.header).c_filesize)} as usize; + offset += filesize; + if offset % 4 != 0 { + offset += 4 - offset % 4; + } + self.header = ((self.header as u64) + offset as u64) as *mut CpioNewcHeader; + CpioHeaderWrapper::new(self.header) + } + + pub fn get_current_file_header(&self) -> *mut CpioNewcHeader{ + self.header + } + + pub fn get_current_file_name_len(&self) -> usize { + let namesize = unsafe {hex_to_u64(&(*self.header).c_namesize)}; + namesize as usize + } + + pub fn get_current_file_content_ptr(&self) -> *const u8 { + let cur_pos = self.header as *mut u8; + let name_size = self.get_current_file_name_len(); + let mut offset = (core::mem::size_of::()+ name_size) as u64 + cur_pos as u64 ; + if offset % 4 != 0 { + offset += 4 - offset % 4; + } + let file_ptr = offset as *mut u8; + file_ptr + } + + pub fn get_current_file_size(&self) -> usize { + (unsafe {hex_to_u64(&(*self.header).c_filesize)} as usize) + } +} + +pub struct CpioFile { + name: String, + content: *const u8, + size: usize, + pos: usize, +} + +impl CpioFile { + pub fn new(name: String, content: *const u8, size: usize) -> CpioFile { + CpioFile { + name, + content, + size, + pos: 0, + } + } + + pub fn read(&mut self, max_size: usize) -> &[u8] { + let mut len = max_size; + if self.pos + len > self.size { + len = self.size - self.pos; + } + let slice = unsafe {core::slice::from_raw_parts(self.content.add(self.pos), len)}; + self.pos += len; + slice + } + + pub fn get_name(&self) -> &str { + &self.name + } + + pub fn get_size(&self) -> usize { + self.size + } + +} + +fn hex_to_u64(hex: &[u8; 8]) -> u64 { + let mut result: u64 = 0; + for i in 0..8 { + result = result << 4; + let c = hex[i]; + if c >= b'0' && c <= b'9' { + result += (c - b'0') as u64; + } else if c >= b'a' && c <= b'f' { + result += (c - b'a' + 10) as u64; + } else if c >= b'A' && c <= b'F' { + result += (c - b'A' + 10) as u64; + } else { + break; + } + } + result +} diff --git a/kernel/src/fs/cpiofs.rs b/kernel/src/fs/cpiofs.rs new file mode 100644 index 000000000..b21c1365c --- /dev/null +++ b/kernel/src/fs/cpiofs.rs @@ -0,0 +1,345 @@ +extern crate alloc; + +use alloc::string::{String, ToString}; +use super::vfs::{FileOperations, FileSystemOperations, VnodeOperations}; +use core::ptr::NonNull; +use alloc::vec::Vec; +use alloc::boxed::Box; + +// a paraser to read cpio archive +#[repr(C, packed)] +struct CpioNewcHeader { + c_magic: [u8; 6], + c_ino: [u8; 8], + c_mode: [u8; 8], + c_uid: [u8; 8], + c_gid: [u8; 8], + c_nlink: [u8; 8], + c_mtime: [u8; 8], + c_filesize: [u8; 8], + c_devmajor: [u8; 8], + c_devminor: [u8; 8], + c_rdevmajor: [u8; 8], + c_rdevminor: [u8; 8], + c_namesize: [u8; 8], + c_check: [u8; 8], +} + +pub struct CpioFS { + name: String, + cpio_start: *mut CpioNewcHeader, + root_vnode: NonNull, +} + +impl CpioFS { + pub fn new(name: &str, cpio_start: *mut u8) -> CpioFS { + CpioFS { + name: name.to_string(), + cpio_start: cpio_start as *mut CpioNewcHeader, + root_vnode: CpioVnode::new_root("".to_string(), None, cpio_start), + } + } + + pub fn get_files(&self) -> CpioIterator { + CpioIterator::new(&CpioHeaderWrapper::new(self.cpio_start)) + } +} + +impl FileSystemOperations for CpioFS { + fn mount(&self) -> NonNull { + self.root_vnode + } + fn get_name(&self) -> String { + self.name.clone() + } +} + +pub struct CpioIterator { + cur_header: CpioHeaderWrapper, +} + +impl CpioIterator { + fn new(cpio_header: &CpioHeaderWrapper) -> CpioIterator { + CpioIterator { + cur_header: CpioHeaderWrapper::new(cpio_header.header), + } + } +} + +impl Iterator for CpioIterator { + type Item = CpioFileInternal; + + fn next(&mut self) -> Option { + if self.cur_header.get_file_name() == "TRAILER!!!" { + return None; + } + let file_name = self.cur_header.get_file_name(); + let file_size = self.cur_header.get_file_size(); + let file_content = self.cur_header.get_current_file_content_ptr(); + let file = CpioFileInternal::new(String::from(file_name), file_content, file_size); + self.cur_header = self.cur_header.get_next_file(); + Some(file) + } +} + +pub struct CpioVnode { + name: String, + parent: Option>, + vnode_type: CpioVnodeType, + mount_vnode: Option>, +} + +enum CpioVnodeType { + File(CpioFileInternal), + Dir(Vec>), +} + +impl CpioVnode { + pub fn new_root(name: String, parent: Option>, cpio_start:*mut u8) -> NonNull { + let b = Box::new( + CpioVnode { + name, + parent: None, + vnode_type: CpioVnodeType::Dir(Vec::new()), + mount_vnode: None, + }); + let mut vnode = NonNull::new(Box::into_raw(b)).unwrap(); + let children = CpioIterator::new(&CpioHeaderWrapper::new(cpio_start as *mut CpioNewcHeader)).map(|file| { + let vnode = Box::new(CpioVnode { + name: file.get_name().to_string(), + parent: Some(vnode), + vnode_type: CpioVnodeType::File(file), + mount_vnode: None, + }); + NonNull::new(Box::into_raw(vnode)).unwrap() + }); + unsafe {vnode.as_mut()}.vnode_type = CpioVnodeType::Dir(children.collect()); + vnode + } +} + +impl VnodeOperations for CpioVnode { + fn get_name(&self) -> String { + self.name.clone() + } + + fn get_parent(&self) -> Option> { + return self.parent; + } + + + fn list_dir(&self) -> Option>> { + match &self.vnode_type { + CpioVnodeType::Dir(children) => { + Some(children.iter().map(|child| { + NonNull::new(child.as_ptr() as *mut dyn VnodeOperations).unwrap() + }).collect()) + } + _ => None, + } + } + + fn lookup(&self, path_vec: &Vec) -> Option> { + if let Some(mount) = self.mount_vnode { + let mount = unsafe {mount.as_ref()}; + return mount.lookup(path_vec); + } + if path_vec.len() == 0 { + return Some(NonNull::from(self)); + } + let name = &path_vec[0]; + match &self.vnode_type { + CpioVnodeType::Dir(children) => { + for child in children.iter() { + let child = unsafe {child.as_ref()}; + if child.get_name() == *name { + return child.lookup(&path_vec[1..].to_vec()); + } + } + return None; + } + _ => return None, + } + } + + fn mkdir(&mut self, file_name: &str) -> Result, String> { + if let Some(mut mount) = self.mount_vnode { + let mount = unsafe {mount.as_mut()}; + return mount.mkdir(file_name); + } + unimplemented!("cpiofs: unsupported operation mkdir") + } + + fn mkfile(&mut self, file_name: &str) -> Result, String> { + if let Some(mut mount) = self.mount_vnode { + let mount = unsafe {mount.as_mut()}; + return mount.mkfile(file_name); + } + unimplemented!("cpiofs: unsupported operation mkdir") + } + fn mount(&mut self, fs: NonNull) { + self.mount_vnode = Some(unsafe {fs.as_ref()}.mount()); + } + + fn open(&self) -> Result, String> { + match &self.vnode_type { + CpioVnodeType::File(file) => { + let file = Box::new(CpioFile { + CpioFileInternal: file.clone(), + read_pos: 0, + }); + Ok(NonNull::new(Box::into_raw(file) as *mut dyn FileOperations).unwrap()) + } + _ => Err("cpiofs: cannot open a directory".to_string()), + } + } + + fn umount(&mut self) { + unimplemented!("umount") + } + + +} + + +struct CpioHeaderWrapper { + header: *mut CpioNewcHeader, +} + +impl CpioHeaderWrapper { + pub fn new(header: *mut CpioNewcHeader) -> CpioHeaderWrapper { + CpioHeaderWrapper { + header, + } + } + + pub fn get_file_name(&self) -> &str { + let namesize = unsafe {hex_to_u64(&(*self.header).c_namesize)}; + let name_ptr = (self.header as u64 + core::mem::size_of::() as u64) as *mut u8; + unsafe { + core::str::from_utf8_unchecked(core::slice::from_raw_parts( + name_ptr, + (namesize - 1) as usize, + )) + } + } + pub fn get_file_size(&self) -> usize { + let filesize = unsafe {hex_to_u64(&(*self.header).c_filesize)}; + filesize as usize + } + pub fn get_next_file(&mut self) -> CpioHeaderWrapper { + let mut offset = core::mem::size_of::() as usize; + let namesize = unsafe {hex_to_u64(&(*self.header).c_namesize)} as usize; + offset += namesize; + if offset % 4 != 0 { + offset += 4 - offset % 4; + } + let filesize = unsafe {hex_to_u64(&(*self.header).c_filesize)} as usize; + offset += filesize; + if offset % 4 != 0 { + offset += 4 - offset % 4; + } + self.header = ((self.header as u64) + offset as u64) as *mut CpioNewcHeader; + CpioHeaderWrapper::new(self.header) + } + + pub fn get_current_file_header(&self) -> *mut CpioNewcHeader{ + self.header + } + + pub fn get_current_file_name_len(&self) -> usize { + let namesize = unsafe {hex_to_u64(&(*self.header).c_namesize)}; + namesize as usize + } + + pub fn get_current_file_content_ptr(&self) -> *const u8 { + let cur_pos = self.header as *mut u8; + let name_size = self.get_current_file_name_len(); + let mut offset = (core::mem::size_of::()+ name_size) as u64 + cur_pos as u64 ; + if offset % 4 != 0 { + offset += 4 - offset % 4; + } + let file_ptr = offset as *mut u8; + file_ptr + } + + pub fn get_current_file_size(&self) -> usize { + (unsafe {hex_to_u64(&(*self.header).c_filesize)} as usize) + } +} + +#[derive(Clone)] +pub struct CpioFileInternal { + name: String, + content: *const u8, + size: usize, +} + + +impl CpioFileInternal { + pub fn new(name: String, content: *const u8, size: usize) -> CpioFileInternal { + CpioFileInternal { + name, + content, + size, + } + } + + pub fn get_name(&self) -> &str { + &self.name + } + + pub fn get_size(&self) -> usize { + self.size + } +} + +pub struct CpioFile { + CpioFileInternal: CpioFileInternal, + read_pos: usize, +} + +impl FileOperations for CpioFile { + fn read(&mut self, len: usize) -> Result, String> { + if self.read_pos == self.CpioFileInternal.size { + return Err("read: EOF".to_string()); + } + let read_len = core::cmp::min(len, self.CpioFileInternal.size - self.read_pos); + let read_data = unsafe { + Vec::from_raw_parts(self.CpioFileInternal.content.add(self.read_pos) as *mut u8, read_len, read_len) + }; + self.read_pos += read_len; + Ok(read_data) + } + fn write(&mut self, buf: &Vec) -> Result { + Err("write: not supported".to_string()) + } + fn close(&mut self) { + // do nothing + } + fn seek(&mut self, offset: usize) -> Result { + if self.read_pos + offset > self.CpioFileInternal.size { + return Err("file out of range.".to_string()); + } + self.read_pos = self.read_pos + offset; + Ok(offset) + } +} + +fn hex_to_u64(hex: &[u8; 8]) -> u64 { + let mut result: u64 = 0; + for i in 0..8 { + result = result << 4; + let c = hex[i]; + if c >= b'0' && c <= b'9' { + result += (c - b'0') as u64; + } else if c >= b'a' && c <= b'f' { + result += (c - b'a' + 10) as u64; + } else if c >= b'A' && c <= b'F' { + result += (c - b'A' + 10) as u64; + } else { + break; + } + } + result +} diff --git a/kernel/src/fs/devfs.rs b/kernel/src/fs/devfs.rs new file mode 100644 index 000000000..3f21e9a57 --- /dev/null +++ b/kernel/src/fs/devfs.rs @@ -0,0 +1,211 @@ +use crate::println; + +use super::vfs::{FileOperations, FileSystemOperations, VnodeOperations}; +use alloc::boxed::Box; +use alloc::string::ToString; +use alloc::vec; +use alloc::vec::Vec; +use alloc::{ffi::NulError, string::String}; +use core::ptr::NonNull; + +use driver::uart; + +pub struct Devfs { + name: String, + vnode: NonNull, +} + +impl Devfs { + pub fn new(name: &str) -> Self { + let root = Box::new(DevfsVnode::new_root()); + let root = Box::into_raw(root); + let root = core::ptr::NonNull::new(root).unwrap(); + Self { + name: name.to_string(), + vnode: root, + } + } + +} + +impl FileSystemOperations for Devfs { + fn mount(&self) -> NonNull { + self.vnode + } + + fn get_name(&self) -> String { + self.name.clone() + } +} + +enum DevfsVnodeType { + Uart, + Framebuffer, + Dir(Vec>), +} + +pub struct DevfsVnode { + name: String, + mount: Option>, + parent: Option>, + node_type: DevfsVnodeType, + self_ref: Option>, +} + +impl DevfsVnode { + pub fn new_root() -> Self { + let uart = Box::new(DevfsVnode { + name: "uart".to_string(), + mount: None, + parent: None, + node_type: DevfsVnodeType::Uart, + self_ref: None, + }); + let uart = Box::into_raw(uart); + let mut uart = core::ptr::NonNull::new(uart).unwrap(); + unsafe {uart.as_mut()}.self_ref = Some(uart); + let framebuffer = Box::new(DevfsVnode { + name: "framebuffer".to_string(), + mount: None, + parent: None, + node_type: DevfsVnodeType::Framebuffer, + self_ref: None, + }); + + let framebuffer = Box::into_raw(framebuffer); + let mut framebuffer = core::ptr::NonNull::new(framebuffer).unwrap(); + + unsafe {framebuffer.as_mut()}.self_ref = Some(framebuffer); + + Self { + name: "".to_string(), + mount: None, + parent: None, + node_type: DevfsVnodeType::Dir(vec![uart, framebuffer]), + self_ref: None, + } + } +} + +impl VnodeOperations for DevfsVnode { + fn get_name(&self) -> String { + self.name.clone() + } + + fn get_parent(&self) -> Option> { + self.parent + } + + fn lookup(&self, path_vec: &Vec) -> Option> { + if let Some(mount) = self.mount { + let mount = unsafe { mount.as_ref() }; + return mount.lookup(path_vec); + } + + if path_vec.len() == 0 { + return Some(self.self_ref.unwrap()); + } + let mut path_vec = path_vec.clone(); + let name = path_vec.remove(0); + match &self.node_type { + DevfsVnodeType::Dir(vnodes) => { + for vnode in vnodes { + let vnode = unsafe { vnode.as_ref() }; + if vnode.get_name() == name { + return vnode.lookup(&path_vec); + } + } + None + } + _ => None, + } + } + + fn list_dir(&self) -> Option>> { + match &self.node_type { + DevfsVnodeType::Dir(children) => { + let mut vnodes: Vec> = Vec::new(); + for vnode in children.iter() { + vnodes.push(NonNull::from(*vnode)); + } + Some(vnodes) + } + _ => None, + } + } + + fn mount(&mut self, fs: NonNull) { + self.mount = Some(unsafe { fs.as_ref() }.mount()); + } + + fn mkdir(&mut self, file_name: &str) -> Result, String> { + Err("devfs: cannot mkdir".to_string()) + } + + fn mkfile(&mut self, file_name: &str) -> Result, String> { + Err("devfs: cannot mkfile".to_string()) + } + + fn open(&self) -> Result, String> { + let file = Box::new(DevFile { + vnode: self.self_ref.unwrap(), + }); + let file = Box::into_raw(file); + let file = core::ptr::NonNull::new(file).unwrap(); + Ok(file) + } + + fn umount(&mut self) { + unimplemented!("devfs umount") + } +} + +pub struct DevFile { + vnode: NonNull, +} + +impl FileOperations for DevFile { + fn read(&mut self, len: usize) -> Result, String> { + let vnode = unsafe { self.vnode.as_ref() }; + match vnode.node_type { + DevfsVnodeType::Uart => { + let mut buf = Vec::new(); + while buf.len() < len { + let c = unsafe {uart::read_u8()}; + match c { + Some(c) => buf.push(c), + None => {} + } + } + Ok(buf) + } + DevfsVnodeType::Framebuffer => Err("devfs: cannot read framebuffer".to_string()), + DevfsVnodeType::Dir(_) => Err("devfs: cannot read dir".to_string()), + } + } + + fn write(&mut self, buf: &Vec) -> Result { + let vnode = unsafe { self.vnode.as_ref() }; + match vnode.node_type { + DevfsVnodeType::Uart => { + for i in buf.iter() { + uart::push_write_buf(*i); + } + uart::flush(); + Ok(buf.len()) + } + DevfsVnodeType::Framebuffer => { + unimplemented!("devfs write framebuffer") + } + DevfsVnodeType::Dir(_) => Err("devfs: cannot write dir".to_string()), + } + } + + fn close(&mut self) { + unimplemented!("devfs close") + } + + fn seek(&mut self, offset: usize) -> Result { + unimplemented!("devfs seek") + } +} diff --git a/kernel/src/fs/fs.org b/kernel/src/fs/fs.org new file mode 100644 index 000000000..f6bd80296 --- /dev/null +++ b/kernel/src/fs/fs.org @@ -0,0 +1 @@ +* VFS diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs new file mode 100644 index 000000000..8bcf56a14 --- /dev/null +++ b/kernel/src/fs/mod.rs @@ -0,0 +1,6 @@ +pub mod cpio; +pub mod vfs; +pub mod utils; +pub mod tmpfs; +pub mod cpiofs; +pub mod devfs; diff --git a/kernel/src/fs/tmpfs.rs b/kernel/src/fs/tmpfs.rs new file mode 100644 index 000000000..1977f7cc6 --- /dev/null +++ b/kernel/src/fs/tmpfs.rs @@ -0,0 +1,286 @@ +use crate::println; + +use super::utils::clean_path; +use super::vfs::{FileOperations, FileSystemOperations, VnodeOperations}; + +use alloc::ffi::NulError; +use alloc::format; +use alloc::string::String; +use alloc::vec::Vec; +use core::any::Any; +use core::borrow::BorrowMut; +use core::cmp::min; +use core::ptr::NonNull; + +use alloc::boxed::Box; +use alloc::string::ToString; + +use alloc::vec; + +const PAGE_SIZE: usize = 4096; + +pub struct Tmpfs { + name: String, + root: NonNull, +} + +impl Tmpfs { + pub fn new(name: &str) -> Self { + let root = Box::new(TmpfsVnode { + name: "".to_string(), + mount: None, + parent: None, + node_type: TmpfsVnodeType::Dir(Vec::new()), + self_ref: None, + file_size: 0, + }); + let root = Box::into_raw(root); + let mut root = NonNull::new(root).unwrap(); + unsafe { root.as_mut() }.self_ref = Some(root); + Tmpfs { + name: name.to_string(), + root: root, + } + } +} + +impl FileSystemOperations for Tmpfs { + fn mount(&self) -> NonNull { + self.root + } + fn get_name(&self) -> String { + self.name.clone() + } +} + +enum TmpfsVnodeType { + File(FileAddressManager), + Dir(Vec>), +} + +struct FileAddressManager { + addr: *mut u8, + len: usize, +} + +impl FileAddressManager { + fn alloc(page_num: usize) -> Self { + let mut page_alloc = unsafe { &mut crate::PAGE_ALLOC }; + let addr = page_alloc.page_alloc(page_num); + FileAddressManager { + addr: addr, + len: page_num * PAGE_SIZE, + } + } + + fn extend_and_copy(&mut self, page_num: usize) { + let mut page_alloc = unsafe { &mut crate::PAGE_ALLOC }; + let new_addr = page_alloc.page_alloc(self.len / PAGE_SIZE + page_num); + let new_len = self.len + page_num * PAGE_SIZE; + unsafe { + core::ptr::copy(self.addr, new_addr, self.len); + } + self.len = new_len; + self.addr = new_addr; + // dealloc old addr + } +} + +struct TmpfsVnode { + name: String, + parent: Option>, + mount: Option>, + node_type: TmpfsVnodeType, + file_size: usize, + self_ref: Option>, +} + +struct TmpfsFile { + vnode: NonNull, + f_read_pos: usize, + f_write_pos: usize, +} + +impl FileOperations for TmpfsFile { + fn read(&mut self, len: usize) -> Result, String> { + let vnode = unsafe { self.vnode.as_ref() }; + match &vnode.node_type { + TmpfsVnodeType::File(file_addr_mgr) => { + if self.f_read_pos == vnode.file_size { + return Err("read: EOF".to_string()); + } + let read_len = min(len, vnode.file_size - self.f_read_pos); + let read_data = Ok(unsafe { + Vec::from_raw_parts(file_addr_mgr.addr.add(self.f_read_pos), read_len, read_len) + }); + self.f_read_pos += read_len; + + read_data + } + _ => Err(format!("{} is not a file", vnode.name)), + } + } + + fn write(&mut self, buf: &Vec) -> Result { + let vnode = unsafe { self.vnode.as_mut() }; + + match &mut vnode.node_type { + TmpfsVnodeType::File(file_addr_mgr) => { + if vnode.file_size + buf.len() > file_addr_mgr.len { + file_addr_mgr.extend_and_copy(1); + } + unsafe { + core::ptr::copy( + buf.as_ptr(), + file_addr_mgr.addr.add(self.f_write_pos), + buf.len(), + ); + } + vnode.file_size += buf.len(); + Ok(buf.len()) + } + TmpfsVnodeType::Dir(_) => Err(format!("write: {} is not a file", vnode.name)), + } + } + + fn seek(&mut self, offset: usize) -> Result { + let vnode = unsafe { self.vnode.as_ref() }; + match &vnode.node_type { + TmpfsVnodeType::File(_) => { + if self.f_read_pos + offset > vnode.file_size { + Err(format!("file out of range.")) + } else { + self.f_read_pos = self.f_read_pos + offset; + Ok(offset) + } + } + TmpfsVnodeType::Dir(_) => Err(format!("{} is not a file.", vnode.name)), + } + } + + fn close(&mut self) {} +} + +impl TmpfsVnode { + fn add_child( + &mut self, + vnode: NonNull, + ) -> Result, String> { + match &mut self.node_type { + TmpfsVnodeType::Dir(children) => { + children.push(vnode); + Ok(vnode) + } + _ => Err(format!("{} is not a directory", self.name)), + } + } +} + +impl VnodeOperations for TmpfsVnode { + fn list_dir(&self) -> Option>> { + match &self.node_type { + TmpfsVnodeType::Dir(children) => { + let mut vnodes: Vec> = Vec::new(); + for child in children.iter() { + vnodes.push(NonNull::from(*child)); + } + Some(vnodes) + } + _ => None, + } + } + + fn get_name(&self) -> String { + self.name.clone() + } + + fn lookup(&self, path: &Vec) -> Option> { + match self.mount { + Some(mount) => { + if path.len() == 0 { + return Some(self.mount.unwrap()); + } + let name = &path[0]; + let mount = unsafe { mount.as_ref() }; + return mount.lookup(path); + } + None => { + if path.len() == 0 { + return Some(self.self_ref.unwrap()); + } + let name = &path[0]; + match &self.node_type { + TmpfsVnodeType::Dir(children) => { + for child in children.iter() { + let child = unsafe { child.as_ref() }; + if child.get_name() == *name { + return child.lookup(&path[1..].to_vec()); + } + } + return None; + } + _ => return None, + } + }, + }; + } + + fn mkfile(&mut self, file_name: &str) -> Result, String> { + let name = file_name; + + let file_addr_mgr = FileAddressManager::alloc(1); + + let vnode = Box::new(TmpfsVnode { + name: name.to_string(), + parent: Some(self.self_ref.unwrap()), + mount: None, + node_type: TmpfsVnodeType::File(file_addr_mgr), + self_ref: None, + file_size: 0, + }); + let vnode = Box::into_raw(vnode); + let mut vnode = NonNull::new(vnode).unwrap(); + unsafe { vnode.as_mut() }.self_ref = Some(vnode); + self.add_child(vnode) + } + + fn mkdir(&mut self, file_name: &str) -> Result, String> { + let name = file_name; + let vnode = Box::new(TmpfsVnode { + name: name.to_string(), + parent: Some(self.self_ref.unwrap()), + mount: None, + node_type: TmpfsVnodeType::Dir(Vec::new()), + self_ref: None, + file_size: 0, + }); + let vnode = Box::into_raw(vnode); + let mut vnode = NonNull::new(vnode).unwrap(); + unsafe { vnode.as_mut() }.self_ref = Some(vnode); + self.add_child(vnode) + } + + fn umount(&mut self) { + unimplemented!("TmpfsVnode::umount()"); + } + + fn mount(&mut self, fs: NonNull) { + self.mount = Some(unsafe { fs.as_ref().mount() }); + } + + fn get_parent(&self) -> Option> { + self.parent + } + + fn open(&self) -> Result, String> { + let file = TmpfsFile { + vnode: self.self_ref.unwrap(), + f_read_pos: 0, + f_write_pos: 0, + }; + // println!("open: file_size: {}", self.file_size); + let file = Box::new(file); + let file = Box::into_raw(file) as *mut dyn FileOperations; + Ok(NonNull::new(file).unwrap()) + } +} diff --git a/kernel/src/fs/utils.rs b/kernel/src/fs/utils.rs new file mode 100644 index 000000000..b6310823a --- /dev/null +++ b/kernel/src/fs/utils.rs @@ -0,0 +1,26 @@ +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec::Vec; +// remove duplicate / in path and . and .. +pub fn clean_path(path: &str) -> String { + path.split("/").filter(|name| *name != "").collect::>().join("/") +} + +pub fn split_path(path: &str) -> Vec { + path.split("/").filter(|name| *name != "").map(|s| s.to_string()).collect::>() +} + +pub fn path_parser(path: &str) -> String { + + let path_vec = split_path(&path); + let mut path_stack: Vec = Vec::new(); + for name in path_vec { + if name == ".." { + path_stack.pop(); + } else if name != "." { + + path_stack.push(name.to_string()); + } + } + path_stack.join("/") +} diff --git a/kernel/src/fs/vfs.rs b/kernel/src/fs/vfs.rs new file mode 100644 index 000000000..b921e4e4e --- /dev/null +++ b/kernel/src/fs/vfs.rs @@ -0,0 +1,242 @@ +use crate::println; + +use super::utils::{split_path}; + +use alloc::format; +use core::ptr::NonNull; + +use alloc::{string::String, vec::Vec}; + +use alloc::string::ToString; + +pub struct VFS { + mount: NonNull, + open_files: Vec, +} + +impl VFS { + pub fn new(fs: NonNull) -> Self { + VFS { + mount: unsafe {fs.as_ref()}.mount(), + open_files: Vec::new(), + } + } + + pub fn mount(&mut self, fs: NonNull, path: &str) -> Result<(), String> { + let vnode = self.lookup(path); + + match &vnode { + Some(mut vnode) => { + let vnode = unsafe { vnode.as_mut() }; + vnode.mount(fs); + Ok(()) + } + None => { + Err(format!("mount: cannot find {}", path)) + } + } + + } + + pub fn lookup(&self, path: &str) -> Option> { + let path_vec = split_path(path); + let vnode = unsafe { self.mount.as_ref() }; + vnode.lookup(&path_vec) + } + + pub fn open(&mut self, path: &str, is_create: bool) -> Result { + let vnode = self.lookup(path); + match vnode { + Some(vnode) => { + let vnode = unsafe { vnode.as_ref() }; + match vnode.open() { + Ok(file) => Ok(File { file }), + Err(e) => Err(e), + } + } + None => { + if is_create{ + let path_vec = split_path(path); + let parent_path_vec = path_vec[0..path_vec.len() - 1].to_vec(); + + let mount = unsafe { self.mount.as_mut() }; + // create file + let mut parent_vnode = match mount.lookup(&parent_path_vec) { + Some(parent_node) => parent_node, + None => { + let parent_path = parent_path_vec.join("/"); + return Err(format!("open: No such file or directory: {}", parent_path).to_string()); + } + }; + let file_name = &path_vec[path_vec.len() - 1]; + let new_vnode = unsafe { parent_vnode.as_mut() }.mkfile(&file_name); + match new_vnode { + Ok(new_vnode) => { + let new_vnode = unsafe { new_vnode.as_ref() }; + match new_vnode.open() { + Ok(file) => Ok(File { file }), + Err(e) => Err(e), + } + } + Err(e) => { + return Err(e); + } + } + } + else{ + return Err(format!( + "open: cannot access '{}': No such file or directory", + path + )); + } + + } + } + } + + pub fn ls(&self, path: &str) -> Result, String> { + let vnode = self.lookup(path); + match vnode { + Some(vnode) => { + let vnode = unsafe { vnode.as_ref() }; + match vnode.list_dir() { + Some(vnodes) => Ok(vnodes + .iter() + .map(|vnode| { + let vnode = unsafe { vnode.as_ref() }; + vnode.get_name() + }) + .collect()), + None => { + return Err(format!("ls: {} is not dir", path)); + } + } + } + None => { + return Err(format!( + "ls: cannot access '{}': No such file or directory", + path + )); + } + } + } + + + + pub fn read_fd(&self, file: &File, buf: &mut [u8], len: usize) -> usize { + unimplemented!("VFS::read()") + } + + pub fn write_fd(&self, file: &File, buf: &[u8], len: usize) -> usize { + unimplemented!("VFS::write()") + } + + pub fn close_fd(&self, file: &File) { + unimplemented!("VFS::close()") + } + + pub fn mkdir(&mut self, path: &str) -> Result<(), String> { + let path_vec = split_path(path); + // println!("mkdir: {:?}", path_vec); + let vnode = unsafe { self.mount.as_mut().lookup(&path_vec) }; + match vnode { + Some(_) => { + return Err(format!( + "mkdir: cannot create directory '{}': File exists", + path + )); + } + None => { + let vnode = unsafe { self.mount.as_mut() }; + let parent_path_vec = path_vec[0..path_vec.len() - 1].to_vec(); + let parent = vnode.lookup(&parent_path_vec); + let file_name = &path_vec[path_vec.len() - 1]; + match parent { + Some(mut parent) => { + let parent = unsafe { parent.as_mut() }; + parent.mkdir(&file_name); + } + None => { + return Err(format!( + "mkdir: cannot create directory '{}': No such file or directory", + path + )); + } + } + } + } + Ok(()) + } + + pub fn umount(&mut self, path: &str) { + let vnode = self.lookup(path); + match vnode { + Some(mut vnode) => { + let vnode = unsafe { vnode.as_mut() }; + vnode.umount(); + } + None => { + println!("umount: cannot find {}", path); + } + } + } +} + +pub struct File { + file: NonNull, +} + +impl File { + pub fn read(&mut self, len: usize) -> Result, String> { + let file = unsafe { self.file.as_mut() }; + file.read(len) + } + + pub fn write(&mut self, buf: &Vec) -> Result { + let file = unsafe { self.file.as_mut() }; + file.write(buf) + } + + pub fn seek(&mut self, offset: usize, whence: i64) -> Result { + let file = unsafe { self.file.as_mut() }; + if whence == 0 { + file.seek(offset) + } + else { + unimplemented!("seek: whence != 0") + } + } + + pub fn close(&mut self) { + let file = unsafe { self.file.as_mut() }; + file.close(); + } +} + +pub trait FileSystemOperations { + fn mount(&self) -> NonNull; + fn get_name(&self) -> String; +} + +pub trait VnodeOperations { + fn lookup(&self, path_vec: &Vec) -> Option>; + fn mkfile(&mut self, file_name: &str) -> Result, String>; + fn mkdir(&mut self, file_name: &str) -> Result, String>; + + // replace current vnode with new vnode + fn mount(&mut self, fs: NonNull); + // recover vnode + fn umount(&mut self); + fn get_parent(&self) -> Option>; + fn get_name(&self) -> String; + fn list_dir(&self) -> Option>>; + + fn open(&self) -> Result, String>; +} + +pub trait FileOperations { + fn read(&mut self, len: usize) -> Result, String>; + fn write(&mut self, buf: &Vec) -> Result; + fn seek(&mut self, offset: usize) -> Result; + fn close(&mut self); +} diff --git a/kernel/src/interrupt/interrupt.s b/kernel/src/interrupt/interrupt.s new file mode 100644 index 000000000..0bbbac843 --- /dev/null +++ b/kernel/src/interrupt/interrupt.s @@ -0,0 +1,121 @@ +// save general registers to stack + +.macro save_all + sub sp, sp, 32 * 8 + + stp x0, x1, [sp ,16 * 0] + stp x2, x3, [sp ,16 * 1] + stp x4, x5, [sp ,16 * 2] + stp x6, x7, [sp ,16 * 3] + stp x8, x9, [sp ,16 * 4] + stp x10, x11, [sp ,16 * 5] + stp x12, x13, [sp ,16 * 6] + stp x14, x15, [sp ,16 * 7] + stp x16, x17, [sp ,16 * 8] + stp x18, x19, [sp ,16 * 9] + stp x20, x21, [sp ,16 * 10] + stp x22, x23, [sp ,16 * 11] + stp x24, x25, [sp ,16 * 12] + stp x26, x27, [sp ,16 * 13] + stp x28, x29, [sp ,16 * 14] + str x30, [sp, 16 * 15] + mov x0, sp +.endm + +// load general registers from stack +.macro load_all + ldp x0, x1, [sp ,16 * 0] + ldp x2, x3, [sp ,16 * 1] + ldp x4, x5, [sp ,16 * 2] + ldp x6, x7, [sp ,16 * 3] + ldp x8, x9, [sp ,16 * 4] + ldp x10, x11, [sp ,16 * 5] + ldp x12, x13, [sp ,16 * 6] + ldp x14, x15, [sp ,16 * 7] + ldp x16, x17, [sp ,16 * 8] + ldp x18, x19, [sp ,16 * 9] + ldp x20, x21, [sp ,16 * 10] + ldp x22, x23, [sp ,16 * 11] + ldp x24, x25, [sp ,16 * 12] + ldp x26, x27, [sp ,16 * 13] + ldp x28, x29, [sp ,16 * 14] + ldr x30, [sp, 16 * 15] + add sp, sp, 32 * 8 +.endm + +.section .text.exception + +serr_handler: + save_all + bl rust_serr_handler + load_all + eret + +irq_handler: + save_all + bl rust_irq_handler + load_all + eret + +frq_handler: + save_all + bl rust_frq_handler + load_all + eret + +core_timer_handler: + save_all + bl rust_core_timer_handler + load_all + mrs x0, cntfrq_el0 + lsl x0, x0, #1 + msr cntp_tval_el0, x0 + eret + +sync_handler: + save_all + bl rust_sync_handler + load_all + eret + +.align 11 // vector table should be aligned to 0x800 +.global exception_vector_table +exception_vector_table: + b sync_handler + .align 7 + b irq_handler + .align 7 + b frq_handler + .align 7 + b serr_handler + .align 7 + + b sync_handler + .align 7 + b irq_handler + .align 7 + b frq_handler + .align 7 + b serr_handler + .align 7 + + b sync_handler + .align 7 + b irq_handler + .align 7 + b frq_handler + .align 7 + b serr_handler + .align 7 + + b sync_handler + .align 7 + b irq_handler + .align 7 + b frq_handler + .align 7 + b serr_handler + .align 7 + +exception_entry: + nop \ No newline at end of file diff --git a/kernel/src/interrupt/mod.rs b/kernel/src/interrupt/mod.rs new file mode 100644 index 000000000..79db266fb --- /dev/null +++ b/kernel/src/interrupt/mod.rs @@ -0,0 +1,408 @@ +pub mod timer; + +mod system_call; + +use core::arch::global_asm; + +use crate::process::{IOType, ProcessState, Registers}; +use crate::{print, print_polling, println, println_polling}; + +use alloc::string::ToString; +use driver::mailbox; +use driver::uart; + +use crate::fs::utils::path_parser; + +use core::{panic, ptr::read_volatile}; + +global_asm!(include_str!("interrupt.s")); + +#[no_mangle] +#[inline(never)] +pub fn rust_frq_handler() { + println!("FRQ interrupt!"); +} + +#[no_mangle] +#[inline(never)] +pub fn int_boink() { + unsafe { core::arch::asm!("nop") }; +} + +pub fn c_str_to_str(c_str: *const u8) -> &'static str { + let mut len = 0; + while unsafe { *c_str.add(len) } != 0 { + len += 1; + } + let c_str_slice = unsafe { core::slice::from_raw_parts(c_str, len) }; + let str = core::str::from_utf8(c_str_slice).unwrap(); + str +} + +fn do_file_syscall(system_call_number: u64, sp: *mut u64) { + let process_scheduler = unsafe { crate::PROCESS_SCHEDULER.as_mut().unwrap() }; + let pid = process_scheduler.get_current_pid(); + let process = process_scheduler.get_process(pid).unwrap(); + let vfs = unsafe { crate::GLOBAL_VFS.as_mut().unwrap() }; + + match system_call_number { + 11 => { + // int open(const char *pathname, int flags); + unsafe { + let pathname = process.registers.x0 as *const u8; + let mut pathname_len = 0; + while *pathname.add(pathname_len) != 0 { + pathname_len += 1; + } + let pathname = core::slice::from_raw_parts(pathname, pathname_len); + let pathname_str_relative = core::str::from_utf8(pathname).unwrap(); + let flags = process.registers.x1 as u32; + let pathname_str = + path_parser(&(process.current_dir.clone() + "/" + pathname_str_relative)); + // println_polling!("file_syscall open: /{}, flags: {}", pathname_str, flags); + let file = vfs.open(&pathname_str, flags != 0); + match file { + Ok(file) => { + let fd = process.open_files.len() as u64; + process.open_files.push(file); + // println_polling!("fd: {}", fd); + process.registers.x0 = fd; + } + Err(e) => { + println_polling!("open error: {}", e); + process.registers.x0 = -1i64 as u64; + } + } + // println_polling!("open done"); + } + } + 12 => { + // println_polling!("close"); + } + 13 => { + // long write(int fd, const void *buf, unsigned long count); + unsafe { + let fd = process.registers.x0 as usize; + let buf = process.registers.x1 as *const u8; + let count = process.registers.x2 as usize; + let buf = core::slice::from_raw_parts(buf, count).to_vec(); + + let file = process.open_files.get_mut(fd).unwrap(); + let write_len = file.write(&buf); + // println_polling!("file_syscall: write"); + match write_len { + Ok(write_len) => { + process.registers.x0 = write_len as u64; + } + Err(e) => { + println_polling!("write error: {}", e); + process.registers.x0 = -1i64 as u64; + } + } + } + } + 14 => { + // long read(int fd, void *buf, unsigned long count); + unsafe { + let fd = process.registers.x0 as usize; + let buf = process.registers.x1 as *mut u8; + let count = process.registers.x2 as usize; + // println_polling!("file_syscall: read fd {}", fd); + let file = process.open_files.get_mut(fd); + if file.is_none() { + process.registers.x0 = -1i64 as u64; + return; + } + let file = file.unwrap(); + let read_data = file.read(count); + match read_data { + Ok(read_data) => { + unsafe { + core::ptr::copy(read_data.as_ptr(), buf, read_data.len()); + } + process.registers.x0 = read_data.len() as u64; + } + Err(e) => { + println_polling!("read error: {}", e); + process.registers.x0 = -1i64 as u64; + } + } + } + } + 15 => { + // you can ignore mode, since there is no access control + // int mkdir(const char *pathname, unsigned mode); + // println_polling!("file_syscall: mkdir"); + unsafe { + let pathname = c_str_to_str(process.registers.x0 as *const u8); + let pathname = process.current_dir.clone() + "/" + path_parser(pathname).as_str(); + // println_polling!("mkdir: {}", pathname); + let result = vfs.mkdir(&pathname); + match result { + Ok(_) => { + process.registers.x0 = 0; + } + Err(e) => { + println_polling!("mkdir error: {}", e); + process.registers.x0 = -1i64 as u64; + } + } + } + } + 16 => { + // you can ignore arguments other than target (where to mount) and filesystem (fs name) + // int mount(const char *src, const char *target, const char *filesystem, unsigned long flags, const void *data); + + unsafe { + let target = c_str_to_str(process.registers.x1 as *const u8); + let filesystem = c_str_to_str(process.registers.x2 as *const u8); + // println_polling!("mount: {} {}", target, filesystem); + let fss = unsafe { &crate::FSS }; + // println_polling!("file_syscall: mount {} {}", target, filesystem); + let fs = fss.iter().find(|f| { + let f = unsafe { f.as_ref() }; + f.get_name() == filesystem + }); + if fs.is_none() { + println_polling!("mount error: fs {} not found", filesystem); + process.registers.x0 = -1i64 as u64; + return; + } + let result = vfs.mount(*fs.unwrap(), target); + match result { + Ok(_) => { + process.registers.x0 = 0; + } + Err(e) => { + println_polling!("mount error: {}", e); + process.registers.x0 = -1i64 as u64; + } + } + } + } + 17 => { + // int chdir(const char *path); + unsafe { + let path = c_str_to_str(process.registers.x0 as *const u8); + // println_polling!("chdir: {}", path); + process.current_dir = path_parser(path); + process.registers.x0 = 0; + } + } + 18 => { + // you only need to implement seek set + // # define SEEK_SET 0 + // long lseek64(int fd, long offset, int whence); + unsafe { + let fd = process.registers.x0 as usize; + let offset = process.registers.x1 as usize; + let whence = process.registers.x2 as i64; + let file = process.open_files.get_mut(fd).unwrap(); + let result = file.seek(offset, whence); + match result { + Ok(offset) => { + process.registers.x0 = offset as u64; + } + Err(e) => { + println_polling!("lseek64 error: {}", e); + process.registers.x0 = -1i64 as u64; + } + } + } + + } + _ => { + panic!("沒看過,加油") + } + } +} + +fn do_syscall(ec: u64, sp: *mut u64) { + match ec { + 0b010101 => { + // println_polling!("svc call from user program"); + // check the system call number + let process_scheduler = unsafe { crate::PROCESS_SCHEDULER.as_mut().unwrap() }; + process_scheduler.ksp = sp; + let mut registers: *mut Registers = sp as *mut Registers; + let system_call_number = unsafe { (*registers).x8 }; + match system_call_number { + 0 => { + // get pid + println_polling!("get pid"); + let current_pid = process_scheduler.get_current_pid(); + unsafe { + (*registers).x0 = current_pid as u64; + } + } + 1 => { + // uart read + // println_polling!("uart read"); + process_scheduler + .save_current_process(ProcessState::Waiting(IOType::UartRead), sp); + process_scheduler.next_process(); + } + 2 => { + // uart write + // println_polling!("uart write"); + unsafe { + let buf: *mut u8 = (*registers).x0 as *mut u8; + let buf_len: usize = (*registers).x1 as usize; + let write_array = core::slice::from_raw_parts(buf, buf_len); + for i in write_array { + uart::write_u8(*i); + } + } + } + 3 => { + // exec + unsafe { + let prog_name: *const u8 = (*registers).x0 as *const u8; + // from c string to rust string + let mut prog_name_len = 0; + while *prog_name.add(prog_name_len) != 0 { + prog_name_len += 1; + } + let prog_name_array = core::slice::from_raw_parts(prog_name, prog_name_len); + let prog_name_str = core::str::from_utf8(prog_name_array).unwrap(); + println_polling!("exec: {}", prog_name_str); + let cpio_handler = crate::CPIO_HANDLER.as_mut().unwrap(); + let mut file = cpio_handler + .get_files() + .find(|file| file.get_name() == prog_name_str) + .unwrap(); + let file_size = file.get_size(); + let file_data = file.read(file_size); + let process = process_scheduler + .get_process(process_scheduler.get_current_pid()) + .unwrap(); + process.exec(file_data); + process_scheduler.next_process(); + } + } + 4 => { + // fork + int_boink(); + process_scheduler.save_current_process(ProcessState::Running, sp); + + let new_pid = process_scheduler.fork(); + + // let current process is the parent process + let current_pid = process_scheduler.get_current_pid(); + let mut parent_process = process_scheduler.get_process(current_pid).unwrap(); + parent_process.registers.x0 = new_pid as u64; + + unsafe { + (*registers).x0 = new_pid as u64; + } + } + 5 => { + // exit + process_scheduler.terminate(process_scheduler.get_current_pid()); + } + 6 => { + // mbox call + let ch = unsafe { (*registers).x0 as u32 }; + let mailbox = unsafe { (*registers).x1 as *const u32 }; + // print_polling!("mbox call: ch: {}, mailbox: {:?}", ch, mailbox); + // println_polling!("mbox call: ch: {}, mailbox: {:?}", ch, mailbox); + mailbox::mailbox_call(ch, mailbox); + unsafe { + (*registers).x0 = 1 as u64; + } + } + 7 => { + // kill + let pid = unsafe { (*registers).x0 as usize }; + process_scheduler.terminate(pid); + } + _ => { + process_scheduler.save_current_process(ProcessState::Ready, sp); + do_file_syscall(system_call_number, sp); + let next_pid = + process_scheduler.get_next_ready(process_scheduler.get_current_pid()); + + // run the next process + process_scheduler.set_next_process(next_pid); + } + } + } + _ => { + panic!("Unknown synchronous exception"); + } + } +} + +#[no_mangle] +#[inline(never)] +pub extern "C" fn rust_sync_handler(sp: *mut u64) { + // check the interrupt source + let esr_el1: u64; + let spsr_el1: u64; + let elr_el1: u64; + unsafe { + core::arch::asm!("mrs {0}, spsr_el1", out(reg) spsr_el1); + core::arch::asm!("mrs {0}, elr_el1", out(reg) elr_el1); + core::arch::asm!("mrs {0}, esr_el1", out(reg) esr_el1); + } + // check the interrupt source + let ec = (esr_el1 >> 26) & 0b111111; + do_syscall(ec, sp); +} + +#[no_mangle] +#[inline(never)] +pub fn rust_core_timer_handler(sp: *mut u64) { + crate::timer::timer_handler(sp); +} + +#[no_mangle] +fn rust_serr_handler() { + // print the content of spsr_el1, elr_el1, and esr_el1 + println!("SErr happened!"); +} + +#[no_mangle] +fn rust_irq_handler(sp: *mut u64) { + // read IRQ basic pending register + let irq_basic_pending: u32; + const IRQ_BASE: *mut u64 = 0x3F00B000 as *mut u64; + const IRQ_BASIC_PENDING: *mut u32 = 0x3F00B200 as *mut u32; + const IRQ_PENDING_1: *mut u32 = 0x3F00B204 as *mut u32; + + let irq_pending_1: u32; + let irq_source: u32; + const CORE0_IRQ_SOURCE: u64 = 0x40000060; + unsafe { + irq_pending_1 = read_volatile(IRQ_PENDING_1 as *mut u32); + irq_source = read_volatile(CORE0_IRQ_SOURCE as *mut u32); + irq_basic_pending = read_volatile(IRQ_BASIC_PENDING); + } + // println!("IRQ basic pending register: {:#b}", irq_basic_pending); + + if irq_source & 0b10 != 0 { + rust_core_timer_handler(sp); + } + // there is a pending interrupt in the pending register 1 + if irq_basic_pending & (0b1 << 8) != 0 { + // interrupt from GPU + let process_scheduler = unsafe { crate::PROCESS_SCHEDULER.as_mut().unwrap() }; + + let iir = unsafe { read_volatile(uart::AUX_MU_IIR_REG as *mut u32) }; + if iir & 0b10 != 0 { + // Transmit holding register empty + uart::send_write_buf(); + } else if iir & 0b100 != 0 { + if let Some(u) = unsafe { uart::read_u8() } { + uart::push_read_buf(u); + }; + if process_scheduler.is_running() { + if let Some(current_process) = + process_scheduler.get_waiting_process(IOType::UartRead) + { + current_process.do_uart_read(); + } + } + } + } +} diff --git a/kernel/src/interrupt/system_call.rs b/kernel/src/interrupt/system_call.rs new file mode 100644 index 000000000..e69de29bb diff --git a/kernel/src/interrupt/timer.rs b/kernel/src/interrupt/timer.rs new file mode 100644 index 000000000..079e53c29 --- /dev/null +++ b/kernel/src/interrupt/timer.rs @@ -0,0 +1,156 @@ +use crate::println; + +use alloc::collections::BinaryHeap; +use alloc::string::String; +use core::{panic, ptr::write_volatile}; + + +struct TimerEntry { + pub target_time: u64, + pub callback: fn(String, *mut u64), + pub message: String, +} + +// Ord +impl core::cmp::Ord for TimerEntry { + fn cmp(&self, other: &Self) -> core::cmp::Ordering { + self.target_time.cmp(&other.target_time).reverse() + } +} + +// PartialOrd +impl core::cmp::PartialOrd for TimerEntry { + fn partial_cmp(&self, other: &Self) -> Option { + Some(self.cmp(other)) + } +} + +// PartialEq +impl core::cmp::PartialEq for TimerEntry { + fn eq(&self, other: &Self) -> bool { + self.target_time == other.target_time + } +} + +// Eq +impl core::cmp::Eq for TimerEntry {} + +static mut TIMER_ENTRY_QUEUE: Option> = None; + +pub fn add_timer(callback: fn(String, *mut u64), time_ms: u64, message: String) { + let current_time = get_current_time(); + let time_duration = ms_to_tick(time_ms); + let target_time = time_duration + current_time; + let entry: TimerEntry = TimerEntry { + target_time: target_time, // the time to trigger the timer + callback: callback, + message: message, + }; + unsafe { + if TIMER_ENTRY_QUEUE.is_none() { + TIMER_ENTRY_QUEUE = Some(BinaryHeap::new()); + } + if let Some(cur_ent) = TIMER_ENTRY_QUEUE.as_mut().unwrap().peek() { + if cur_ent.target_time > target_time { + set_timer_interrupt(target_time); + } + } + else { + // if the queue is empty + set_timer_interrupt(target_time); + } + + TIMER_ENTRY_QUEUE.as_mut().unwrap().push(entry); + } +} + +#[no_mangle] +#[inline(never)] +fn timer_boink() { + unsafe { core::arch::asm!("nop") }; +} + +pub fn init_timer() { + unsafe { + let mut tmp: u64; + core::arch::asm!("mrs {0}, cntkctl_el1", out(reg) tmp); + tmp |= 1; + core::arch::asm!("msr cntkctl_el1, {0}", in(reg) tmp); + } +} + +pub fn timer_handler(sp : *mut u64) { + if let Some(queue) = unsafe {TIMER_ENTRY_QUEUE.as_mut()} { + if let Some(entry) = queue.pop() { + // execute the callback function + (entry.callback)(entry.message.clone(), sp); + if let Some(next_entry) = queue.peek() { + set_timer_interrupt(next_entry.target_time); + } else { + // queue is empty + disable_timer_interrupt(); + } + } else { + panic!("No timer entry!"); + } + + } +} + +pub fn get_timer_freq() -> u64 { + let mut cntfrq_el0: u64; + unsafe { + core::arch::asm!("mrs {0}, cntfrq_el0", out(reg) cntfrq_el0); + } + cntfrq_el0 +} + +pub fn get_current_time() -> u64 { + let mut cntpct_el0: u64; + unsafe { + core::arch::asm!("mrs {0}, cntpct_el0", out(reg) cntpct_el0); + } + cntpct_el0 +} + +pub fn sec_to_tick(sec: u64) -> u64 { + sec * get_timer_freq() +} + +pub fn ms_to_tick(ms: u64) -> u64 { + ms * get_timer_freq() / 1000 +} + +fn timer_callback(message: String) { + let current_time = get_current_time() / get_timer_freq(); + println!("You have a timer after boot {}s, message: {}", current_time, message); +} + +fn set_timer_interrupt(target_time: u64) { + unsafe { + let addr: *mut u32 = 0x40000040 as *mut u32; + let basic_irq_enable: *mut u32 = 0x3F00B218 as *mut u32; + core::arch::asm!("msr cntp_cval_el0, {0}", in(reg) target_time); + // println!( + // "Debug: Set timer interrupt at {} tic, freq: {}, current {} tic", + // target_time, + // get_timer_freq(), + // get_current_time() + // ); + // enable timer interrupt + // write_volatile(basic_irq_enable, 0b1); + // enable the timer + write_volatile(addr, 0x2); + } +} + +fn disable_timer_interrupt() { + unsafe { + let addr: *mut u32 = 0x40000040 as *mut u32; + let basic_irq_enable: *mut u32 = 0x3F00B218 as *mut u32; + // enable timer interrupt + // write_volatile(basic_irq_enable, 0b0); + // disable the timer + write_volatile(addr, 0x0); + } +} diff --git a/kernel/src/kernel_thread/mod.rs b/kernel/src/kernel_thread/mod.rs new file mode 100644 index 000000000..c33079a63 --- /dev/null +++ b/kernel/src/kernel_thread/mod.rs @@ -0,0 +1,177 @@ +use alloc::vec::Vec; +use core::arch::{asm, global_asm}; + +use crate::println; + +global_asm!(include_str!("switch.s")); + +pub static mut SCHEDULER: Scheduler = Scheduler { + run_queue: Vec::new(), + used_thread_id: 0, +}; + +pub struct Thread { + // The stack pointer of the thread + pub sp: usize, + pub thread_id: usize, + // The function to run + pub task: fn(), + // array for callee-saved registers + pub registers: [usize; 16], + pub is_running: bool, +} + +#[repr(C)] +struct Registers {} + +impl Thread { + pub fn new(task: fn()) -> Self { + // allocate a memory for the thread + Self { + sp: 0, + thread_id: 0, + task, + registers: [0; 16], + is_running: false, + } + } + + pub fn run(&mut self) { + self.is_running = true; + set_current_tpidr_el1(self.registers.as_ptr() as u64); + (self.task)(); + } + + pub fn get_thread_id(&self) -> usize { + self.thread_id + } +} + +pub struct Scheduler { + pub run_queue: Vec, + used_thread_id: usize, +} + +fn get_current_tpidr_el1() -> u64 { + let tpidr_el1: u64; + unsafe { + asm!("mrs {}, tpidr_el1", out(reg) tpidr_el1); + } + tpidr_el1 +} + +fn set_current_tpidr_el1(tpidr_el1: u64) { + unsafe { + asm!("msr tpidr_el1, {}", in(reg) tpidr_el1); + } +} +#[no_mangle] +#[inline(never)] +fn schedule_boink() { + unsafe {asm!("nop");} +} +#[no_mangle] +#[inline(never)] +pub fn schedule() { + // get current thread id from tpidr_el1 + unsafe { + asm!( + " + // save the callee-saved registers + mrs x0, tpidr_el1 + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + ", out("x0") _, out("x9") _); + } + let cur_tpidr_el1 = get_current_tpidr_el1(); + let scheduler = unsafe { &mut SCHEDULER }; + let current_thread_pos = scheduler + .run_queue + .iter() + .position(|thread| thread.registers.as_ptr() == cur_tpidr_el1 as *const usize) + .unwrap(); + + let current_thread = scheduler.run_queue.get(current_thread_pos).unwrap(); + let scheduler = unsafe { &mut SCHEDULER }; + + let next_thread_pos = (current_thread_pos + 1) % scheduler.run_queue.len(); + // determine the next thread to run + let next_thread = scheduler.run_queue.get_mut(next_thread_pos).unwrap(); + let next_thread_regs = next_thread.registers.as_ptr(); + + println!("From thread {} to thread {}", current_thread.get_thread_id(), next_thread.get_thread_id()); + println!("current_regs: {:p}, next_regs: {:p}", cur_tpidr_el1 as *const usize, next_thread_regs); + schedule_boink(); + set_current_tpidr_el1(next_thread_regs as u64); + if next_thread.is_running { + unsafe { + asm!( + " + ldp x19, x20, [{0}, 16 * 0] + ldp x21, x22, [{0}, 16 * 1] + ldp x23, x24, [{0}, 16 * 2] + ldp x25, x26, [{0}, 16 * 3] + ldp x27, x28, [{0}, 16 * 4] + ldp fp, lr, [{0}, 16 * 5] + ldr x9, [{0}, 16 * 6] + mov sp, x9 + ret + " + , + in(reg) next_thread_regs, out("x9") _); + } + } + else { + next_thread.run(); + } +} +#[macro_export] +macro_rules! exit_thread { + ($return_value:ident) => { + let cur_thread = kernel_thread::get_scheduler().get_current_thread(); + println!("Thread {} exited", cur_thread.get_thread_id()); + cur_thread.is_running = false; + return $return_value; + }; + () => { + let cur_thread = kernel_thread::get_scheduler().get_current_thread(); + println!("Thread {} exited", cur_thread.get_thread_id()); + cur_thread.is_running = false; + return; + }; +} + +pub fn get_scheduler() -> &'static mut Scheduler { + unsafe { &mut SCHEDULER } +} + +impl Scheduler { + // if function called schedule, change to another thread. + pub fn add_task(&mut self, task: fn()) { + let mut thread = Thread::new(task); + thread.thread_id = self.used_thread_id; + self.used_thread_id += 1; + self.run_queue.push(thread); + } + pub fn start(&mut self) { + self.run_queue.first_mut().unwrap().run(); + } + + pub fn get_current_thread(&mut self) -> &mut Thread { + let cur_tpidr_el1 = get_current_tpidr_el1(); + self.run_queue + .iter_mut() + .find(|thread| thread.registers.as_ptr() == cur_tpidr_el1 as *const usize) + .unwrap() + } + + pub fn rm_task(&mut self, task_id: usize) { + self.run_queue.remove(task_id); + } +} diff --git a/kernel/src/kernel_thread/switch.s b/kernel/src/kernel_thread/switch.s new file mode 100644 index 000000000..6957693f0 --- /dev/null +++ b/kernel/src/kernel_thread/switch.s @@ -0,0 +1,26 @@ +.global switch_to +switch_to: + // save the callee-saved registers + stp x19, x20, [x0, 16 * 0] + stp x21, x22, [x0, 16 * 1] + stp x23, x24, [x0, 16 * 2] + stp x25, x26, [x0, 16 * 3] + stp x27, x28, [x0, 16 * 4] + stp fp, lr, [x0, 16 * 5] + mov x9, sp + str x9, [x0, 16 * 6] + + ldp x19, x20, [x1, 16 * 0] + ldp x21, x22, [x1, 16 * 1] + ldp x23, x24, [x1, 16 * 2] + ldp x25, x26, [x1, 16 * 3] + ldp x27, x28, [x1, 16 * 4] + ldp fp, lr, [x1, 16 * 5] + ldr x9, [x1, 16 * 6] + mov sp, x9 + ret + +.global get_current +get_current: + mrs x0, tpidr_el1 + ret diff --git a/kernel/src/main.rs b/kernel/src/main.rs new file mode 100644 index 000000000..5ee03f30e --- /dev/null +++ b/kernel/src/main.rs @@ -0,0 +1,563 @@ +#![feature(asm_const)] +#![no_main] +#![no_std] +#![feature(default_alloc_error_handler)] +#![feature(format_args_nl)] +#![feature(int_log)] +#![feature(linked_list_cursors)] + +mod bsp; +mod cpu; +mod fdt; +mod panic_wait; +mod print; +mod fs; +mod memory; +mod kernel_thread; +mod process; +mod interrupt; + +use alloc::borrow::ToOwned; +use alloc::format; +use driver::uart::async_getline; +use fs::vfs::VFS; +use interrupt::timer; + +extern crate alloc; + +use core::alloc::GlobalAlloc; +use core::fmt::write; +use core::panic; +use alloc::string::String; +use alloc::string::ToString; +use alloc::vec::Vec; +use alloc::collections::BinaryHeap; +use alloc::boxed::Box; +use core::alloc::Layout; +use core::ptr::NonNull; +use fs::cpio::CpioHandler; +use fs::vfs::{FileSystemOperations}; + +use driver::mailbox; +use driver::uart; +use driver::addr_loader; +use kernel_thread::Thread; +use memory::{dynamic_memory_allocator, simple_alloc::SimpleAllocator}; + +#[global_allocator] +static mut ALLOCATOR: SimpleAllocator = SimpleAllocator::new(); +static mut PAGE_ALLOC: dynamic_memory_allocator::DynamicMemoryAllocator = dynamic_memory_allocator::DynamicMemoryAllocator::new(); + +static mut GLOBAL_VFS: Option = None; +static mut FSS: Vec> = Vec::new(); + +static mut CPIO_HANDLER: Option = None; + +static mut PROCESS_SCHEDULER: Option = None; + +fn get_initrd_addr(name: &str, data: *const u8, len: usize) -> Option { + if name == "linux,initrd-start" { + unsafe { Some((*(data as *const u32)).swap_bytes()) } + } else { + None + } +} + +fn foo(){ + let scheduler = kernel_thread::get_scheduler(); + for i in 0..10 { + let cur_thread = scheduler.get_current_thread(); + println!("Thread id: {}, i: {}", cur_thread.get_thread_id(), i); + kernel_thread::schedule(); + } + exit_thread!(); +} + +#[no_mangle] +#[inline(never)] +fn exec(addr: extern "C" fn() -> !) { + // set spsr_el1 to 0x3c0 and elr_el1 to the program’s start address. + // set the user pro gram’s stack pointer to a proper position by setting sp_el0. + // issue eret to return to the user code. + unsafe { + core::arch::asm!(" + msr spsr_el1, {k} + msr elr_el1, {a} + msr sp_el0, {s} + eret + ", k = in(reg) 0x3c0 as u64, a = in(reg) addr as u64, s = in(reg) 0x60000 as u64) ; + }; + println!(""); +} + +#[no_mangle] +#[inline(never)] +fn boink() { + unsafe{core::arch::asm!("nop")}; +} + + + +fn timer_callback(message: String, _: *mut u64) { + let current_time = timer::get_current_time() / timer::get_timer_freq(); + println!("You have a timer after boot {}s, message: {}", current_time, message); +} + +fn fs_open_shell(vfs: &mut VFS, path: &str) { + let mut file = vfs.open(&path, true); + match file { + Ok(mut file) => { + println!("File: {} opened.", path); + loop { + print!("file: {}>> ", path); + let mut in_buf: [u8; 128] = [0; 128]; + let inp = alloc::string::String::from(uart::async_getline(&mut in_buf, true)); + let cmd = inp.trim().split(' ').collect::>(); + match cmd[0] { + "read" => { + let size = if cmd.len() < 2 { + 32 + } else { + cmd[1].parse().unwrap() + }; + let read_buf = file.read(32); + match read_buf { + Ok(buf) => { + let s = String::from_utf8(buf).unwrap(); + println!("{}", s); + } + Err(e) => { + println!("{}", e); + } + } + } + "write" => { + + let mut read_buf :[u8; 128] = [0; 128]; + let read_line = async_getline(&mut read_buf, true); + let mut write_buf = Vec::new(); + let bytes = read_line.as_bytes(); + write_buf.extend_from_slice(bytes); + file.write(&write_buf); + } + "close" => { + // vfs.close(&file); + break; + } + "" => {} + _ => { + println!("Command not found"); + } + } + } + } + Err(e) => { + println!("{}", e); + } + } +} + +fn fs_shell(idle_proc_code: &[u8]) { + let mut vfs = unsafe {GLOBAL_VFS.as_mut().unwrap()}; + let mut working_dir = String::from(""); + let mut fss = unsafe {&mut FSS}; + loop { + print!("{}>> ", &working_dir); + let mut in_buf: [u8; 128] = [0; 128]; + let inp = alloc::string::String::from(uart::async_getline(&mut in_buf, true)); + let cmd = inp.trim().split(' ').collect::>(); + match cmd[0] { + "help" => { + println!("ls : list files in directory\r\nmkdir : create directory\r\nopen : open file\r\ncat : read and display file\r\nexit : exit shell"); + } + "ls" => { + let path = if cmd.len() < 2 { + working_dir.to_owned() + } else { + working_dir.to_owned() + "/" + cmd[1] + }; + let ls = vfs.ls(&path); + match ls { + Ok(files) => { + for file in files { + println!("{}", file); + } + } + Err(e) => { + println!("{}", e); + } + } + } + "mkdir" => { + if cmd.len() < 2 { + println!("Usage: mkdir "); + continue; + } + let path = working_dir.to_owned() + "/" + cmd[1]; + let res = vfs.mkdir(&path); + match res { + Ok(_) => {} + Err(e) => { + println!("{}", e); + } + } + } + "cd" => { + unimplemented!("cd"); + } + "open" => { + if cmd.len() < 2 { + println!("Usage: open "); + continue; + } + let path = working_dir.to_owned() + "/" + cmd[1]; + + fs_open_shell(&mut vfs, &path); + } + "cat" => { + if cmd.len() < 2 { + println!("Usage: cat "); + continue; + } + let path = working_dir.to_owned() + "/" + cmd[1]; + let mut file = vfs.open(&path, false); + if let Err(e) = file { + println!("{}", e); + continue; + } + let mut file = file.unwrap(); + while let Ok(buf) = file.read(2) { + let s = String::from_utf8(buf).unwrap(); + print!("{}", s); + } + println!(""); + file.close(); + } + "rmdir" => { + unimplemented!("rmdir"); + } + "mkfs" => { + if cmd.len() < 2 { + println!("Usage: mkfs "); + continue; + } + let b = Box::new(fs::tmpfs::Tmpfs::new(cmd[1])); + fss.push(NonNull::new(Box::into_raw(b)).unwrap()); + } + "lsfs" => { + for fs in fss.iter() { + let fs = unsafe {fs.as_ref()}; + println!("{}", fs.get_name()); + } + } + "mount" => + { + if cmd.len() < 3 { + println!("Usage: mount "); + continue; + } + let fs = cmd[1]; + let path = cmd[2]; + let fs = fss.iter().find(|f| { + let f = unsafe {f.as_ref()}; + f.get_name() == fs + }); + if let Some(fs) = fs { + vfs.mount(*fs, path); + } else { + println!("Filesystem not found"); + } + } + "umount" => { + if cmd.len() < 2 { + println!("Usage: umount "); + continue; + } + let path = cmd[1]; + vfs.umount(path); + } + "exec" => { + if cmd.len() < 2 { + println!("Usage: exec "); + continue; + } + let file = vfs.open(cmd[1], false); + match file { + Ok(mut file) => { + let mut data = Vec::new(); + while let Ok(buf) = file.read(1024) { + data.extend_from_slice(&buf); + } + let process_scheduler = unsafe {PROCESS_SCHEDULER.as_mut().unwrap()}; + let idle_pid = process_scheduler.create_process(&idle_proc_code); + println!("Idle process created with pid: {}", idle_pid); + + let pid = process_scheduler.create_process(&data); + println!("Process created with pid: {}", pid); + if process_scheduler.is_running() == false{ + process_scheduler.start(); + } + } + Err(e) => { + println!("{}", e); + } + } + } + "exit" => { + break; + } + _ => { + println!("Command not found"); + } + } + + } +} + +fn init_fs(initrd_start: *mut u8) { + // init vfs + let mut fss = unsafe {&mut FSS}; + + let b = Box::new(fs::tmpfs::Tmpfs::new("rootfs")); + fss.push(NonNull::new(Box::into_raw(b)).unwrap()); + + let b = Box::new(fs::tmpfs::Tmpfs::new("tmpfs")); + fss.push(NonNull::new(Box::into_raw(b)).unwrap()); + + let b = Box::new(fs::cpiofs::CpioFS::new("initramfs", initrd_start)); + fss.push(NonNull::new(Box::into_raw(b)).unwrap()); + + let b = Box::new(fs::devfs::Devfs::new("devfs")); + fss.push(NonNull::new(Box::into_raw(b)).unwrap()); + + unsafe {GLOBAL_VFS = Some(VFS::new(fss[0]));} + + +} + +#[no_mangle] +fn kernel_init() -> ! { + let dtb_addr = addr_loader::load_dtb_addr(); + let dtb_parser = fdt::DtbParser::new(dtb_addr); + // init process scheduler + unsafe { + PROCESS_SCHEDULER = Some(process::ProcessScheduler::new()); + } + // init uart + uart::init_uart(true); + timer::init_timer(); + println_polling!("Kernel started"); + + // find initrd value by dtb traverse + + let initrd_start: *mut u8; + if let Some(addr) = dtb_parser.traverse(get_initrd_addr) { + initrd_start = addr as *mut u8; + println_polling!("Initrd start address: {:#x}", initrd_start as u64) + } else { + initrd_start = 0 as *mut u8; + } + unsafe { + CPIO_HANDLER = Some(CpioHandler::new(initrd_start as *mut u8)); + } + let cpio_handler = unsafe {CPIO_HANDLER.as_mut().unwrap()}; + + // load symbol address usr_load_prog_base + + + init_fs(initrd_start as *mut u8); + + + + let mut in_buf: [u8; 128] = [0; 128]; + + let page_alloc = unsafe {&mut PAGE_ALLOC}; + page_alloc.init(0x00000000, 0x3C000000 , 4096); + + page_alloc.reserve_addr(0x0, 0x1000); + page_alloc.reserve_addr(0x60000, 0x80000); // stack + page_alloc.reserve_addr(0x80000, 0x100000); // code + page_alloc.reserve_addr(initrd_start as usize, initrd_start as usize + 4096); //initrd + page_alloc.reserve_addr(dtb_addr as usize, ((dtb_addr as usize + dtb_parser.get_dtb_size() as usize) + (4096 - 1)) & !(4096 - 1)); // dtb + + // let scheduler = kernel_thread::get_scheduler(); + let mut idle_proc_code = cpio_handler.get_files().find(|f| { + f.get_name() == "prog"} + ).unwrap(); + + let idle_proc_data = { + let idle_proc_size = idle_proc_code.get_size(); + println!("Idle process size: {}", idle_proc_size); + idle_proc_code.read(idle_proc_size) + }; + + loop { + print!("meow>> "); + let inp = alloc::string::String::from(uart::async_getline(&mut in_buf, true)); + let cmd = inp.trim().split(' ').collect::>(); + // split the input string + match cmd[0] { + "hello" => println!("Hello World!"), + "help" => println!("help : print this help menu\r\nhello : print Hello World!\r\nreboot : reboot the device"), + "reboot" => { + uart::reboot(); + break; + } + "ls" => { + for file in cpio_handler.get_files() { + println!("{}, size: {}", file.get_name(), file.get_size()); + } + } + "cat" => { + if cmd.len() < 2 { + println!("Usage: cat "); + continue; + } + let mut file = cpio_handler.get_files().find(|f| f.get_name() == cmd[1]); + if let Some(mut f) = file { + println!("File size: {}", f.get_size()); + loop { + let data = f.read(32); + if data.len() == 0 { + break; + } + for i in data { + unsafe { + uart::write_u8(*i); + } + } + } + } else { + println!("File not found"); + } + } + "dtb" => { + println!("Start address: {:#x}", dtb_addr as u64); + println!("dtb load address: {:#x}", dtb_addr as u64); + println!("dtb pos: {:#x}", unsafe {*(dtb_addr)}); + dtb_parser.parse_struct_block(); + } + "mailbox" => { + println!("Revision: {:#x}", mailbox::get_board_revisioin()); + let (base, size) = mailbox::get_arm_memory(); + println!("ARM memory base address: {:#x}", base); + println!("ARM memory size: {:#x}", size); + } + "exec" => { + if cmd.len() < 2 { + println!("Usage: exec "); + continue; + } + let file = cpio_handler.get_files().find(|f| { + f.get_name() == cmd[1]} + ); + + + if let Some(mut f) = file { + let file_size = f.get_size(); + println!("File size: {}", file_size); + let data = f.read(file_size); + let process_scheduler = unsafe {PROCESS_SCHEDULER.as_mut().unwrap()}; + let idle_pid = process_scheduler.create_process(&idle_proc_data); + println!("Idle process created with pid: {}", idle_pid); + let pid = process_scheduler.create_process(&data); + + println!("Process created with pid: {}", pid); + if process_scheduler.is_running() == false{ + process_scheduler.start(); + } + } else { + println!("File not found"); + } + } + "setTimeout" => { + if cmd.len() < 3 { + println!("Usage: setTimeout