diff --git a/.gitignore b/.gitignore index 446ff27..4b4a5a8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,8 @@ build target /kernel/src/arch/x86_64/interrupt/vector.asm +*.gen.s +*.dtb Cargo.lock !kernel/Cargo.lock @@ -11,3 +13,5 @@ Cargo.lock # for eclipse .project + +.vscode diff --git a/.travis.yml b/.travis.yml index e7ecc38..9e99092 100644 --- a/.travis.yml +++ b/.travis.yml @@ -28,6 +28,7 @@ env: - ARCH="riscv32" - ARCH="x86_64" - ARCH="aarch64" + - ARCH="mipsel" OPTS="board=malta" matrix: allow_failures: @@ -45,10 +46,10 @@ install: if [ $TRAVIS_OS_NAME = linux ]; then export FILE="gcc-arm-8.2-2018.11-x86_64-aarch64-elf"; wget https://developer.arm.com/-/media/Files/downloads/gnu-a/8.2-2018.11/$FILE.tar.xz; - tar -xvf $FILE.tar.xz; + tar -xf $FILE.tar.xz; export PATH=$PATH:$PWD/$FILE/bin; wget https://musl.cc/aarch64-linux-musl-cross.tgz; - tar -xvf aarch64-linux-musl-cross.tgz; + tar -xf aarch64-linux-musl-cross.tgz; export PATH=$PATH:$PWD/aarch64-linux-musl-cross/bin; elif [ $TRAVIS_OS_NAME = osx ]; then brew tap SergioBenitez/osxct; @@ -60,16 +61,16 @@ install: sudo apt update; sudo apt install linux-headers-$(uname -r); wget https://musl.cc/x86_64-linux-musl-cross.tgz; - tar -xvf x86_64-linux-musl-cross.tgz; + tar -xf x86_64-linux-musl-cross.tgz; export PATH=$PATH:$PWD/x86_64-linux-musl-cross/bin; fi; fi - if [ $TRAVIS_OS_NAME = linux ]; then - wget https://download.qemu.org/qemu-3.1.0.tar.xz && tar xvJf qemu-3.1.0.tar.xz > /dev/null && cd qemu-3.1.0 && ./configure --target-list=$ARCH-softmmu && make && cd ..; + wget https://download.qemu.org/qemu-3.1.0.tar.xz && tar xJf qemu-3.1.0.tar.xz > /dev/null && cd qemu-3.1.0 && ./configure --target-list=$ARCH-softmmu && make && cd ..; export PATH=$PATH:$PWD/qemu-3.1.0/$ARCH-softmmu:$PWD/qemu-3.1.0; sudo apt update; - sudo apt install libfuse-dev; + sudo apt install libfuse-dev device-tree-compiler -y; fi - if [ $ARCH = riscv32 ]; then @@ -77,7 +78,7 @@ install: sudo apt update; sudo apt install linux-headers-$(uname -r); wget https://musl.cc/riscv32-linux-musl-cross.tgz; - tar -xvf riscv32-linux-musl-cross.tgz; + tar -xf riscv32-linux-musl-cross.tgz; export PATH=$PATH:$PWD/riscv32-linux-musl-cross/bin; fi; fi @@ -86,10 +87,19 @@ install: sudo apt update; sudo apt install linux-headers-$(uname -r); wget https://musl.cc/riscv64-linux-musl-cross.tgz; - tar -xvf riscv64-linux-musl-cross.tgz; + tar -xf riscv64-linux-musl-cross.tgz; export PATH=$PATH:$PWD/riscv64-linux-musl-cross/bin; fi; fi + - if [ $ARCH = mipsel ]; then + if [ $TRAVIS_OS_NAME = linux ]; then + sudo apt update; + sudo apt install linux-headers-$(uname -r); + wget https://musl.cc/mipsel-linux-musln32-cross.tgz; + tar -xf mipsel-linux-musln32-cross.tgz; + export PATH=$PATH:$PWD/mipsel-linux-musln32-cross/bin; + fi; + fi before_script: diff --git a/bootloader/Makefile b/bootloader/Makefile index e675023..2e51b7d 100644 --- a/bootloader/Makefile +++ b/bootloader/Makefile @@ -13,6 +13,8 @@ else ifeq ($(arch), riscv32) prefix := riscv64-unknown-elf- else ifeq ($(arch), riscv64) prefix := riscv64-unknown-elf- +else ifeq ($(arch), mipsel) +prefix := mipsel-linux-gnu- else ifeq ($(arch), aarch64) prefix ?= aarch64-none-elf- ifeq (,$(shell which $(prefix)ld)) @@ -30,6 +32,7 @@ strip := $(prefix)strip export CC = $(cc) export PAYLOAD = $(payload) +export DTB = $(dtb) build_args := --target=targets/$(arch).json ifeq ($(mode), release) diff --git a/bootloader/build.rs b/bootloader/build.rs index 958e977..3a91fca 100644 --- a/bootloader/build.rs +++ b/bootloader/build.rs @@ -10,14 +10,22 @@ fn main() { } } +/// include payload and dtb in sections of asm fn gen_payload_asm() -> Result { let out_dir = std::env::var("OUT_DIR").unwrap(); let payload = std::env::var("PAYLOAD").unwrap(); + let dtb = std::env::var("DTB").unwrap(); if !Path::new(&payload).is_file() { panic!("Kernel payload `{}` not found", payload) } + let mut has_dtb = true; + + if !Path::new(&dtb).is_file() { + has_dtb = false; + } + let file_path = Path::new(&out_dir).join("payload.S"); let mut f = File::create(&file_path).unwrap(); @@ -31,10 +39,24 @@ fn gen_payload_asm() -> Result { _kernel_payload_start: .incbin "{}" _kernel_payload_end: -"#, payload)?; + "#, payload)?; println!("cargo:rerun-if-changed={}", payload); println!("cargo:rerun-if-env-changed=PAYLOAD"); + if has_dtb { + write!(f, r#" + .section .dtb,"a" + .align 12 + .global _dtb_start, _dtb_end +_dtb_start: + .incbin "{}" +_dtb_end: + "#, dtb)?; + println!("{:x?} {:x?}", dtb, file_path); + println!("cargo:rerun-if-changed={}", dtb); + println!("cargo:rerun-if-env-changed=DTB"); + } + Ok(file_path) } diff --git a/bootloader/src/arch/mipsel/boot.S b/bootloader/src/arch/mipsel/boot.S new file mode 100644 index 0000000..ffcf6da --- /dev/null +++ b/bootloader/src/arch/mipsel/boot.S @@ -0,0 +1,20 @@ +.section .text.boot +.globl _start + +_start: + # read cpu affinity, start core 0, halt the rest + mfc0 $8, $15, 1 + beqz $8, setup + andi $8, $8, 0x3ff # use bits 11 ~ 0 + +halt: + # core affinity != 0, halt it + b halt + nop + +setup: + # put the bootstack at 8MB offset of physical mem + la $29, 0x80800000 # $sp + la $28, _gp # $gp + b boot_main + nop \ No newline at end of file diff --git a/bootloader/src/arch/mipsel/boot.ld b/bootloader/src/arch/mipsel/boot.ld new file mode 100644 index 0000000..f62b631 --- /dev/null +++ b/bootloader/src/arch/mipsel/boot.ld @@ -0,0 +1,41 @@ +ENTRY(_start) + +SECTIONS { + + /* MIPS entry point after cold reset */ + . = 0xBFC00000; + + .text : { + KEEP(*(.text.boot)) /* from boot.S */ + *(.text .text.* .gnu.linkonce.t*) + . = ALIGN(4K); + } + + .rodata : { + *(.rodata .rodata.* .gnu.linkonce.r*) + . = ALIGN(4K); + } + + .data : { + *(.data .data.* .gnu.linkonce.d*) + . = ALIGN(4K); + } + + .bss : { + _sbss = .; + *(.bss .bss.*) + *(COMMON) + . = ALIGN(4K); + _ebss = .; + } + + .payload : { + *(.payload) + } + + .dtb : { + *(.dtb) + } + + /DISCARD/ : { *(.comment) *(.gnu*) *(.note*) *(.eh_frame*) } +} diff --git a/bootloader/src/arch/mipsel/mod.rs b/bootloader/src/arch/mipsel/mod.rs new file mode 100644 index 0000000..14237b6 --- /dev/null +++ b/bootloader/src/arch/mipsel/mod.rs @@ -0,0 +1,34 @@ +use core::ptr; +use fixedvec::FixedVec; +use xmas_elf::program::{ProgramHeader32, Type}; + +const KERNEL_OFFSET: u32 = 0x80000000; + +global_asm!(include_str!("boot.S")); + +pub fn copy_kernel(kernel_start: usize, segments: &FixedVec) { + // reverse program headers to avoid overlapping in memory copying + let mut space = alloc_stack!([ProgramHeader32; 32]); + let mut rev_segments = FixedVec::new(&mut space); + for i in (0..segments.len()).rev() { + rev_segments.push(segments[i]).unwrap(); + } + + for segment in &rev_segments { + if segment.get_type() != Ok(Type::Load) { + continue; + } + let virt_addr = segment.virtual_addr; + let offset = segment.offset; + let file_size = segment.file_size; + let mem_size = segment.mem_size; + + unsafe { + let src = (kernel_start as u32 + offset) as *const u8; + let dst = virt_addr.wrapping_sub(KERNEL_OFFSET) as *mut u8; + ptr::copy(src, dst, file_size as usize); + ptr::write_bytes(dst.offset(file_size as isize), 0, (mem_size - file_size) as usize); + } + } + +} diff --git a/bootloader/src/main.rs b/bootloader/src/main.rs index 88a6ac7..c886029 100644 --- a/bootloader/src/main.rs +++ b/bootloader/src/main.rs @@ -12,13 +12,18 @@ use core::slice; use fixedvec::FixedVec; use xmas_elf::{ header, - program::{ProgramHeader, ProgramHeader64}, + program::{ProgramHeader, ProgramHeader32, ProgramHeader64}, ElfFile, }; #[cfg(target_arch = "aarch64")] #[path = "arch/aarch64/mod.rs"] pub mod arch; + +#[cfg(target_arch = "mips")] +#[path = "arch/mipsel/mod.rs"] +pub mod arch; + pub mod lang_items; extern "C" { @@ -26,8 +31,15 @@ extern "C" { fn _kernel_payload_end(); } +#[cfg(target_arch = "mips")] +extern "C" { + fn _dtb_start(); + fn _dtb_end(); +} + /// The entry point of bootloader -#[no_mangle] // don't mangle the name of this function +#[cfg(target_arch = "aarch64")] +#[no_mangle] pub extern "C" fn boot_main() -> ! { let kernel_size = _kernel_payload_end as usize - _kernel_payload_start as usize; let kernel = unsafe { slice::from_raw_parts(_kernel_payload_start as *const u8, kernel_size) }; @@ -54,3 +66,33 @@ pub extern "C" fn boot_main() -> ! { loop {} } + + +#[cfg(target_arch = "mips")] +#[no_mangle] +pub extern "C" fn boot_main() -> ! { + let kernel_size = _kernel_payload_end as usize - _kernel_payload_start as usize; + let kernel = unsafe { slice::from_raw_parts(_kernel_payload_start as *const u8, kernel_size) }; + let kernel_elf = ElfFile::new(kernel).unwrap(); + header::sanity_check(&kernel_elf).unwrap(); + + let mut preallocated_space = alloc_stack!([ProgramHeader32; 32]); + let mut segments = FixedVec::new(&mut preallocated_space); + + for program_header in kernel_elf.program_iter() { + match program_header { + ProgramHeader::Ph32(header) => segments + .push(*header) + .expect("does not support more than 32 program segments"), + ProgramHeader::Ph64(_) => panic!("does not support 64 bit elf files"), + } + } + + let entry = kernel_elf.header.pt2.entry_point() as u32; + let kernel_main: extern "C" fn(dtb: usize) = unsafe { transmute(entry) }; + + arch::copy_kernel(_kernel_payload_start as usize, &segments); + kernel_main(_dtb_start as usize); + + loop {} +} diff --git a/bootloader/targets/mipsel.json b/bootloader/targets/mipsel.json new file mode 100644 index 0000000..d343fe4 --- /dev/null +++ b/bootloader/targets/mipsel.json @@ -0,0 +1,36 @@ +{ + "arch": "mips", + "cpu": "mips32r2", + "llvm-target": "mipsel-unknown-none", + "data-layout": "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64", + "target-endian": "little", + "target-pointer-width": "32", + "target-c-int-width": "32", + "os": "none", + "features": "+mips32r2", + "max-atomic-width": "32", + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "pre-link-args": { + "ld.lld": [ + "-Tsrc/arch/mipsel/boot.ld" + ] + }, + "executables": true, + "panic-strategy": "abort", + "relocation-model": "static", + "abi-blacklist": [ + "cdecl", + "stdcall", + "fastcall", + "vectorcall", + "thiscall", + "aapcs", + "win64", + "sysv64", + "ptx-kernel", + "msp430-interrupt", + "x86-interrupt" + ], + "eliminate-frame-pointer": false +} diff --git a/kernel/Cargo.lock b/kernel/Cargo.lock index b17ca55..28447a3 100644 --- a/kernel/Cargo.lock +++ b/kernel/Cargo.lock @@ -115,7 +115,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cc" -version = "1.0.32" +version = "1.0.31" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -234,6 +234,15 @@ name = "managed" version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "mips" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "bit_field 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nodrop" version = "0.1.13" @@ -249,6 +258,26 @@ name = "os_bootinfo" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "paste" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro-hack 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "paste-impl" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro-hack 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pc-keyboard" version = "0.5.0" @@ -262,6 +291,24 @@ dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "proc-macro-hack" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "proc-macro2" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pulldown-cmark" version = "0.0.3" @@ -270,6 +317,14 @@ dependencies = [ "getopts 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "quote" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rand" version = "0.4.6" @@ -301,7 +356,7 @@ version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_version 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -318,14 +373,16 @@ dependencies = [ "bitvec 0.11.0 (git+https://github.com/myrrlyn/bitvec.git)", "bootloader 0.4.0 (git+https://github.com/rcore-os/bootloader)", "buddy_system_allocator 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", "console-traits 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "device_tree 1.0.3 (git+https://github.com/rcore-os/device_tree-rs)", "heapless 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "isomorphic_drivers 0.1.0 (git+https://github.com/rcore-os/isomorphic_drivers)", "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "mips 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "once 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "pc-keyboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "pci 0.0.1 (git+https://github.com/rcore-os/pci-rs)", "raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -369,7 +426,7 @@ dependencies = [ [[package]] name = "rcore-thread" version = "0.1.0" -source = "git+https://github.com/rcore-os/rcore-thread#765ea6c7b97de4b154fd8b9b8b4678deb7d7c93b" +source = "git+https://github.com/rcore-os/rcore-thread#7236bfd2e2bde673773214739695bb2925a77ae5" dependencies = [ "deque 0.3.2 (git+https://github.com/rcore-os/deque.git?branch=no_std)", "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -467,6 +524,16 @@ name = "static_assertions" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "syn" +version = "0.15.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "tempdir" version = "0.3.7" @@ -500,6 +567,11 @@ name = "unicode-width" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicode-xid" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "usize_conversions" version = "0.2.0" @@ -609,7 +681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bootloader 0.4.0 (git+https://github.com/rcore-os/bootloader)" = "" "checksum buddy_system_allocator 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2ed828f1e227d6e32b998d6375b67fd63ac5389d50b23f258ce151d22b6cc595" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" -"checksum cc 1.0.32 (registry+https://github.com/rust-lang/crates.io-index)" = "ad0daef304fa0b4238f5f7ed7178774b43b06f6a9b6509f6642bef4ff1f7b9b2" +"checksum cc 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "c9ce8bb087aacff865633f0bd5aeaed910fe2fe55b55f4739527f2e023a2e53d" "checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4" "checksum console-traits 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f711b3d1d5c3f7ae7d6428901c0f3e5d5f5c800fcfac86bf0252e96373a2cec6" "checksum deque 0.3.2 (git+https://github.com/rcore-os/deque.git?branch=no_std)" = "" @@ -627,12 +699,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum managed 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fdcec5e97041c7f0f1c5b7d93f12e57293c831c646f4cc7a5db59460c7ea8de6" +"checksum mips 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4cbf449a63e4db77af9f662d6b42068c0925e779a3a7c70ad02f191cf1e6c802" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" "checksum once 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "931fb7a4cf34610cf6cbe58d52a8ca5ef4c726d4e2e178abd0dc13a6551c6d73" "checksum os_bootinfo 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "66481dbeb5e773e7bd85b63cd6042c30786f834338288c5ec4f3742673db360a" +"checksum paste 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1f4a4a1c555c6505821f9d58b8779d0f630a6b7e4e1be24ba718610acf01fa79" +"checksum paste-impl 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "26e796e623b8b257215f27e6c80a5478856cae305f5b59810ff9acdaa34570e6" "checksum pc-keyboard 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c48392db76c4e9a69e0b3be356c5f97ebb7b14413c5e4fd0af4755dbf86e2fce" "checksum pci 0.0.1 (git+https://github.com/rcore-os/pci-rs)" = "" +"checksum proc-macro-hack 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3e90aa19cd73dedc2d0e1e8407473f073d735fef0ab521438de6da8ee449ab66" +"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" "checksum pulldown-cmark 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8361e81576d2e02643b04950e487ec172b687180da65c731c03cf336784e6c07" +"checksum quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "cdd8e04bd9c52e0342b406469d494fcb033be4bdbe5c606016defbb1681411e1" "checksum rand 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" "checksum rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" "checksum rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d0e7a549d590831370895ab7ba4ea0c1b6b011d106b5ff2da6eee112615e6dc0" @@ -652,11 +730,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum spin 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "ceac490aa12c567115b40b7b7fceca03a6c9d53d5defea066123debc83c5dc1f" "checksum spin 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44363f6f51401c34e7be73db0db371c04705d35efbe9f7d6082e03a921a32c55" "checksum static_assertions 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "389ce475f424f267dbed6479cbd8f126c5e1afb053b0acdaa019c74305fc65d1" +"checksum syn 0.15.30 (registry+https://github.com/rust-lang/crates.io-index)" = "66c8865bf5a7cbb662d8b011950060b3c8743dca141b054bf7195b20d314d8e2" "checksum tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "15f2b5fb00ccdf689e0149d1b1b3c03fead81c2b37735d812fa8bddbbf41b6d8" "checksum tock-registers 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3a385d94f3f62e60445a0adb9ff8d9621faa272234530d4c0f848ec98f88e316" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum uart_16550 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "269f953d8de3226f7c065c589c7b4a3e83d10a419c7c3b5e2e0f197e6acc966e" "checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" +"checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum usize_conversions 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f70329e2cbe45d6c97a5112daad40c34cd9a4e18edb5a2a18fefeb584d8d25e5" "checksum ux 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "88dfeb711b61ce620c0cb6fd9f8e3e678622f0c971da2a63c4b3e25e88ed012f" "checksum volatile 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6af0edf5b4faacc31fc51159244d78d65ec580f021afcef7bd53c04aeabc7f29" diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index 3cf9eee..5d95f9c 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -13,7 +13,9 @@ authors = [ "chenqiuhao ", "maoyuchaxue ", "Jiajie Chen ", - "chyyuu " + "chyyuu ", + "Shengqi Chen ", + "Yuhao Zhou " ] [features] @@ -24,6 +26,11 @@ board_u540 = ["sv39", "link_user"] nographic = [] board_raspi3 = ["bcm2837", "link_user"] raspi3_use_generic_timer = ["bcm2837/use_generic_timer"] +# for qemu machine +board_malta = ["link_user"] +board_mipssim = ["link_user"] +# for thinpad +board_thinpad = ["link_user"] # Hard link user program link_user = [] # Run cmdline instead of user shell, useful for automatic testing @@ -71,6 +78,10 @@ riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } aarch64 = { git = "https://github.com/rcore-os/aarch64" } bcm2837 = { git = "https://github.com/rcore-os/bcm2837", optional = true } +[target.'cfg(target_arch = "mips")'.dependencies] +mips = "^0.2.0" +paste = "0.1" + [package.metadata.bootimage] default-target = "targets/x86_64.json" output = "target/x86_64/bootimage.bin" diff --git a/kernel/Makefile b/kernel/Makefile index 3543c70..e6592a5 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -15,8 +15,8 @@ # make clean Clean # # Options: -# arch = x86_64 | riscv32 | riscv64 | aarch64 -# d = int | in_asm | ... QEMU debug info +# arch = x86_64 | riscv32 | riscv64 | aarch64 | mipsel +# d = int | in_asm | ... QEMU debug info # mode = debug | release # LOG = off | error | warn | info | debug | trace # SFSIMG = SFS image path of user programs @@ -50,16 +50,37 @@ bootloader := $(bootloader_dir)/target/$(target)/$(mode)/rcore-bootloader bbl_path := $(PWD)/../riscv-pk user_dir := ../user + ### export environments ### -export ARCH = $(arch) -export BOARD = $(board) -export SMP = $(smp) + +ifeq ($(arch), $(filter $(arch), aarch64 mipsel)) +#link user img, so use original image +export SFSIMG = $(user_dir)/build/$(arch).img +else export SFSIMG = $(user_dir)/build/$(arch).qcow2 +endif + ifeq ($(arch), aarch64) board := raspi3 -export SFSIMG = $(user_dir)/build/$(arch).img +need_bootloader := true +endif + +# currently only mipsel architecture needs DTB linked to the kernel +ifeq ($(arch), mipsel) +dtb := src/arch/$(arch)/board/$(board)/device.dtb +endif + +# mipssim does not support SMP +ifeq ($(board), mipssim) +smp := 1 endif +export ARCH = $(arch) +export BOARD = $(board) +export SMP = $(smp) +export DTB = $(dtb) + + ### qemu options ### qemu_opts := \ -smp cores=$(smp) @@ -119,6 +140,20 @@ ifneq ($(u_boot), ) qemu_opts += \ -sd $(bootloader) endif + +else ifeq ($(arch), mipsel) +ifeq ($(board), malta) +qemu_opts += \ + -machine $(board) -device VGA \ + -serial null -serial null -serial mon:stdio \ + -kernel $(kernel_img) +endif +ifeq ($(board), mipssim) +qemu_opts += \ + -machine $(board) \ + -serial mon:stdio \ + -kernel $(kernel_img) +endif endif ifdef d @@ -172,6 +207,8 @@ else ifeq ($(arch), riscv32) prefix := riscv64-unknown-elf- else ifeq ($(arch), riscv64) prefix := riscv64-unknown-elf- +else ifeq ($(arch), mipsel) +prefix ?= mipsel-linux-musln32- else ifeq ($(arch), aarch64) prefix ?= aarch64-none-elf- ifeq (,$(shell which $(prefix)ld)) @@ -186,9 +223,12 @@ cc := $(prefix)gcc as := $(prefix)as gdb := $(prefix)gdb strip := $(prefix)strip +dtc := dtc export CC = $(cc) -.PHONY: all clean build asm doc debug kernel sfsimg install run justrun runnet justrunnet runui justrunui runtest justruntest +hostcc := gcc + +.PHONY: all clean build asm doc debug kernel sfsimg install run justrun runnet justrunnet runui justrunui runtest justruntest all: kernel @@ -235,8 +275,17 @@ header: sym: @$(objdump) -t $(kernel) | less +### device tree process ### + +%.dtb: %.dts + @echo Generating device tree file $@ + @$(dtc) -I dts -O dtb -o $@ $< + @rm -rf src/arch/${arch}/boot/dtb.gen.s + +### bootloader and kernel image ### + $(bootloader): $(kernel) -ifeq ($(arch), aarch64) +ifeq ($(need_bootloader), true) @echo Building $(arch) bootloader @$(strip) $(kernel) -o $(kernel)_stripped @cd $(bootloader_dir) && make arch=$(arch) mode=$(mode) payload=../kernel/$(kernel)_stripped @@ -272,9 +321,12 @@ ifneq ($(u_boot), ) else @$(objcopy) $(bootloader) --strip-all -O binary $@ endif +else ifeq ($(arch), mipsel) +# qemu-system-mipsel accepts ELF file only, so objcopy is not needed + @$(strip) $(kernel) -o $@ endif -kernel: +kernel: $(dtb) @echo Building $(arch) kernel ifeq ($(arch), x86_64) @bootimage build $(build_args) @@ -292,6 +344,11 @@ else ifeq ($(arch), riscv64) @cargo xbuild $(build_args) else ifeq ($(arch), aarch64) @cargo xbuild $(build_args) +else ifeq ($(arch), mipsel) + @for file in context entry trap ; do \ + $(hostcc) -E src/arch/$(arch)/boot/$${file}.S -o src/arch/$(arch)/boot/$${file}.gen.s ; \ + done + @cargo xbuild $(build_args) endif diff --git a/kernel/build.rs b/kernel/build.rs index 57c7ee7..8e1440f 100644 --- a/kernel/build.rs +++ b/kernel/build.rs @@ -2,6 +2,7 @@ extern crate cc; use std::fs::File; use std::io::{Result, Write}; +use std::path::Path; fn main() { println!("cargo:rerun-if-env-changed=LOG"); @@ -16,6 +17,9 @@ fn main() { } "riscv32" => {} "riscv64" => {} + "mipsel" => { + gen_dtb_asm(&arch, &board).unwrap(); + } "aarch64" => {} _ => panic!("Unknown arch {}", arch), } @@ -44,3 +48,32 @@ fn gen_vector_asm() -> Result<()> { } Ok(()) } + +fn gen_dtb_asm(arch: &String, _board: &String) -> Result<()> { + let dtb = std::env::var("DTB").unwrap(); + + if !Path::new(&dtb).is_file() { + panic!("DTB `{}` not found", dtb) + } + + let mut f = File::create(format!("src/arch/{}/boot/dtb.gen.s", arch)).unwrap(); + + println!("cargo:rerun-if-changed={}", dtb); + println!("cargo:rerun-if-env-changed=DTB"); + + writeln!(f, "# generated by build.rs - do not edit")?; + write!( + f, + r#" + .section .dtb,"a" + .align 12 + .global _dtb_start, _dtb_end +_dtb_start: + .incbin "{}" +_dtb_end: + "#, + dtb + )?; + + Ok(()) +} diff --git a/kernel/src/arch/aarch64/board/raspi3/mod.rs b/kernel/src/arch/aarch64/board/raspi3/mod.rs index 713c725..1cc6696 100644 --- a/kernel/src/arch/aarch64/board/raspi3/mod.rs +++ b/kernel/src/arch/aarch64/board/raspi3/mod.rs @@ -1,14 +1,18 @@ //! Raspberry PI 3 Model B/B+ +use alloc::string::String; use bcm2837::atags::Atags; use once::*; +#[path = "../../../../drivers/gpu/fb.rs"] pub mod fb; pub mod irq; pub mod mailbox; pub mod serial; pub mod timer; +use fb::{ColorConfig, FramebufferInfo, FramebufferResult}; + pub const IO_REMAP_BASE: usize = bcm2837::consts::IO_BASE; pub const IO_REMAP_END: usize = bcm2837::consts::KERNEL_OFFSET + 0x4000_1000; @@ -41,3 +45,42 @@ pub fn probe_memory() -> Option<(usize, usize)> { } None } + +pub fn probe_fb_info(width: u32, height: u32, depth: u32) -> FramebufferResult { + let (width, height) = if width == 0 || height == 0 { + mailbox::framebuffer_get_physical_size()? + } else { + (width, height) + }; + + let depth = if depth == 0 { + mailbox::framebuffer_get_depth()? + } else { + depth + }; + + let info = mailbox::framebuffer_alloc(width, height, depth)?; + + if info.bus_addr == 0 || info.screen_size == 0 { + Err(format!("mailbox call returned an invalid address/size"))?; + } + if info.pitch == 0 || info.pitch != info.xres * info.depth / 8 { + Err(format!( + "mailbox call returned an invalid pitch value {}", + info.pitch + ))?; + } + + use crate::arch::memory; + let paddr = info.bus_addr & !0xC0000000; + let vaddr = memory::ioremap(paddr as usize, info.screen_size as usize, "fb"); + if vaddr == 0 { + Err(format!( + "cannot remap memory range [{:#x?}..{:#x?}]", + paddr, + paddr + info.screen_size + ))?; + } + + Ok((info, fb::ColorConfig::BGRA8888, vaddr)) +} diff --git a/kernel/src/arch/aarch64/driver/mod.rs b/kernel/src/arch/aarch64/driver/mod.rs index 9c29662..a100c2b 100644 --- a/kernel/src/arch/aarch64/driver/mod.rs +++ b/kernel/src/arch/aarch64/driver/mod.rs @@ -5,6 +5,7 @@ use once::*; pub use self::board::fb; pub use self::board::serial; +#[path = "../../../drivers/console/mod.rs"] pub mod console; /// Initialize ARM64 common drivers diff --git a/kernel/src/arch/mipsel/board/malta/consts.rs b/kernel/src/arch/mipsel/board/malta/consts.rs new file mode 100644 index 0000000..73ff966 --- /dev/null +++ b/kernel/src/arch/mipsel/board/malta/consts.rs @@ -0,0 +1,3 @@ +/// board specific constants +pub const MEMORY_END: usize = 0x8800_0000; +pub const KERNEL_HEAP_SIZE: usize = 0x00a0_0000; diff --git a/kernel/src/arch/mipsel/board/malta/device.dts b/kernel/src/arch/mipsel/board/malta/device.dts new file mode 100644 index 0000000..bbe1e66 --- /dev/null +++ b/kernel/src/arch/mipsel/board/malta/device.dts @@ -0,0 +1,51 @@ +/dts-v1/; + + +/ { + model = "qemu malta"; + compatible = "qemu,malta"; + #address-cells = <1>; + #size-cells = <1>; + + chosen { + stdio = &uart2; + bootargs = "rust/sh"; + }; + + aliases { }; + + cpu_intc: interrupt-controller { + compatible = "mti,cpu-interrupt-controller"; + interrupt-controller; + #interrupt-cells = <1>; + }; + + main_memory: memory@0 { + device_type = "memory"; + reg = <0x00000000 0x10000000>; + }; + + uart0: serial@b80003f8 { + compatible = "ns16550a"; + reg = <0xb80003f8 0x8>; + clock-frequency = <1843200>; + }; + + uart2: serial@bf000900 { + compatible = "ns16550a"; + reg = <0xbf000900 0x40>; + reg-shift = <3>; + clock-frequency = <1843200>; + /* The CBUS UART is attached to the MIPS CPU INT2 pin, ie interrupt 4 */ + interrupt-parent = <&cpu_intc>; + interrupts = <4>; + }; + + nor0: flash@be000000 { + compatible = "cfi-flash"; + reg = <0xbe000000 0x00400000>; + }; + + // TODO: add graphics and ethernet adapter + +}; diff --git a/kernel/src/arch/mipsel/board/malta/mod.rs b/kernel/src/arch/mipsel/board/malta/mod.rs new file mode 100644 index 0000000..0fbaa33 --- /dev/null +++ b/kernel/src/arch/mipsel/board/malta/mod.rs @@ -0,0 +1,54 @@ +use crate::drivers::bus::pci; +use alloc::string::String; +use mips::registers::cp0; +use once::*; + +#[path = "../../../../drivers/console/mod.rs"] +pub mod console; +pub mod consts; +#[path = "../../../../drivers/gpu/fb.rs"] +pub mod fb; +#[path = "../../../../drivers/serial/ti_16c550c.rs"] +pub mod serial; +#[path = "../../../../drivers/gpu/qemu_stdvga.rs"] +pub mod vga; + +use fb::FramebufferInfo; + +/// Initialize serial port first +pub fn init_serial_early() { + assert_has_not_been_called!("board::init must be called only once"); + // initialize serial driver + serial::init(0xbf000900); + // Enable serial interrupt + unsafe { + let mut status = cp0::status::read(); + status.enable_hard_int2(); + cp0::status::write(status); + } + println!("Hello QEMU Malta!"); +} + +/// Initialize other board drivers +pub fn init_driver() { + // TODO: add possibly more drivers + vga::init(0xbbe00000, 0xb2050000, 800, 600); + pci::init(); + fb::init(); +} + +pub fn probe_fb_info(_width: u32, _height: u32, _depth: u32) -> fb::FramebufferResult { + let fb_info = FramebufferInfo { + xres: 800, + yres: 600, + xres_virtual: 800, + yres_virtual: 600, + xoffset: 0, + yoffset: 0, + depth: 8, + pitch: 800, + bus_addr: 0xb0000000, + screen_size: 800 * 600, + }; + Ok((fb_info, fb::ColorConfig::VgaPalette, 0xb0000000)) +} diff --git a/kernel/src/arch/mipsel/board/mipssim/consts.rs b/kernel/src/arch/mipsel/board/mipssim/consts.rs new file mode 100644 index 0000000..73ff966 --- /dev/null +++ b/kernel/src/arch/mipsel/board/mipssim/consts.rs @@ -0,0 +1,3 @@ +/// board specific constants +pub const MEMORY_END: usize = 0x8800_0000; +pub const KERNEL_HEAP_SIZE: usize = 0x00a0_0000; diff --git a/kernel/src/arch/mipsel/board/mipssim/device.dts b/kernel/src/arch/mipsel/board/mipssim/device.dts new file mode 100644 index 0000000..2e99f7b --- /dev/null +++ b/kernel/src/arch/mipsel/board/mipssim/device.dts @@ -0,0 +1,36 @@ +/dts-v1/; + + +/ { + model = "qemu mipssim"; + compatible = "qemu,mipssim"; + #address-cells = <1>; + #size-cells = <1>; + + chosen { + stdio = &uart0; + }; + + aliases { }; + + cpu_intc: interrupt-controller { + compatible = "mti,cpu-interrupt-controller"; + interrupt-controller; + #interrupt-cells = <1>; + }; + + main_memory: memory@0 { + device_type = "memory"; + reg = <0x00000000 0x10000000>; + }; + + uart0: serial@bfd003f8 { + compatible = "ns16550a"; + reg = <0xbfd003f8 0x8>; + clock-frequency = <1843200>; + /* attached to the MIPS CPU INT2 pin, ie interrupt 4 */ + interrupt-parent = <&cpu_intc>; + interrupts = <4>; + }; + +}; diff --git a/kernel/src/arch/mipsel/board/mipssim/mod.rs b/kernel/src/arch/mipsel/board/mipssim/mod.rs new file mode 100644 index 0000000..97eb465 --- /dev/null +++ b/kernel/src/arch/mipsel/board/mipssim/mod.rs @@ -0,0 +1,27 @@ +use alloc::string::String; +use once::*; + +#[path = "../../../../drivers/console/mod.rs"] +pub mod console; +pub mod consts; +#[path = "../../../../drivers/gpu/fb.rs"] +pub mod fb; +#[path = "../../../../drivers/serial/16550_reg.rs"] +pub mod serial; + +/// Initialize serial port first +pub fn init_serial_early() { + assert_has_not_been_called!("board::init must be called only once"); + serial::init(0xbfd003f8); + println!("Hello QEMU MIPSSIM!"); +} + +/// Initialize other board drivers +pub fn init_driver() { + // TODO: add possibly more drivers + // timer::init(); +} + +pub fn probe_fb_info(_width: u32, _height: u32, _depth: u32) -> fb::FramebufferResult { + Err(String::from("Framebuffer not usable on mipssim board")) +} diff --git a/kernel/src/arch/mipsel/board/thinpad/consts.rs b/kernel/src/arch/mipsel/board/thinpad/consts.rs new file mode 100644 index 0000000..b59309a --- /dev/null +++ b/kernel/src/arch/mipsel/board/thinpad/consts.rs @@ -0,0 +1,3 @@ +/// board specific constants +pub const MEMORY_END: usize = 0x8080_0000; +pub const KERNEL_HEAP_SIZE: usize = 0x0020_0000; diff --git a/kernel/src/arch/mipsel/board/thinpad/device.dts b/kernel/src/arch/mipsel/board/thinpad/device.dts new file mode 100644 index 0000000..5a771c9 --- /dev/null +++ b/kernel/src/arch/mipsel/board/thinpad/device.dts @@ -0,0 +1,85 @@ +/dts-v1/; + + +/ { + model = "thinpad trivialmips"; + compatible = "tsinghua,thinpad"; + #address-cells = <1>; + #size-cells = <1>; + + chosen { + stdio = &uart; + bootargs = ""; + }; + + aliases { }; + + cpu_intc: interrupt-controller { + compatible = "mti,cpu-interrupt-controller"; + interrupt-controller; + #interrupt-cells = <1>; + }; + + memory: memory@80000000 { + device_type = "memory"; + reg = <0x80000000 0x00800000>; + }; + + bus: trivial_bus@a0000000 { + compatible = "thinpad,bus"; + reg = <0xa0000000 0x800000>; + #address-cells = <1>; + #size-cells = <1>; + ranges; + + flash: flash@a1000000 { + compatible = "cfi-flash"; + reg = <0xa1000000 0x00800000>; + }; + + framebuffer: framebuffer@a2000000 { + compatible = "thinpad,framebuffer"; + reg = <0xa2000000 0x75300 + 0xa2075300 0x4>; + }; + + uart: serial@a3000000 { + compatible = "thinpad,uart"; + reg = <0xa3000000 0x1 + 0xa3000004 0x1>; + clock-frequency = <115200>; + interrupt-parent = <&cpu_intc>; + interrupts = <1>; + }; + + timer: gpio@a4000000 { + compatible = "thinpad,timer"; + reg = <0xa400000 0x8>; + }; + + eth: ethernet@a5000000 { + compatible = "davicom,dm9000"; + reg = <0xa5000000 0x2 + 0xa5000004 0x2>; + interrupt-parent = <&cpu_intc>; + interrupts = <2>; + davicom,no-eeprom; + mac-address = [00 0a 2d 98 01 29]; + }; + + gpio: gpio@a6000000 { + compatible = "thinpad,gpio"; + reg = <0xa6000000 0x2 + 0xa6000004 0x2 + 0xa6000008 0x2>; + reg-io-width = <2>; + }; + + usb: usb@a7000000 { + compatible = "cypress,sl811"; + reg = <0xa7000000 0x1 + 0xa7000004 0x1>; + }; + }; + +}; diff --git a/kernel/src/arch/mipsel/board/thinpad/mod.rs b/kernel/src/arch/mipsel/board/thinpad/mod.rs new file mode 100644 index 0000000..dae1f92 --- /dev/null +++ b/kernel/src/arch/mipsel/board/thinpad/mod.rs @@ -0,0 +1,42 @@ +use alloc::string::String; +use once::*; + +#[path = "../../../../drivers/console/mod.rs"] +pub mod console; +pub mod consts; +#[path = "../../../../drivers/gpu/fb.rs"] +pub mod fb; +#[path = "../../../../drivers/serial/simple_uart.rs"] +pub mod serial; + +use fb::FramebufferInfo; +use fb::FramebufferResult; + +/// Initialize serial port first +pub fn init_serial_early() { + assert_has_not_been_called!("board::init must be called only once"); + serial::init(0xa3000000); + println!("Hello ThinPad!"); +} + +/// Initialize other board drivers +pub fn init_driver() { + // TODO: add possibly more drivers + // timer::init(); +} + +pub fn probe_fb_info(width: u32, height: u32, depth: u32) -> FramebufferResult { + let fb_info = FramebufferInfo { + xres: 800, + yres: 600, + xres_virtual: 800, + yres_virtual: 600, + xoffset: 0, + yoffset: 0, + depth: 8, + pitch: 800, + bus_addr: 0xa2000000, + screen_size: 800 * 600, + }; + Ok((fb_info, fb::ColorConfig::RGB332, 0xa2000000)) +} diff --git a/kernel/src/arch/mipsel/boot/context.S b/kernel/src/arch/mipsel/boot/context.S new file mode 100644 index 0000000..534ede5 --- /dev/null +++ b/kernel/src/arch/mipsel/boot/context.S @@ -0,0 +1,59 @@ +#include "regdef.h" + +.set noat +.set noreorder + +.section .text.context +.globl switch_context +.extern _root_page_table_ptr +.extern _cur_kstack_ptr + +switch_context: + // save from's registers + addi sp, sp, (-4*14) + sw sp, 0(a0) + sw ra, 0(sp) + sw s0, 2*4(sp) + sw s1, 3*4(sp) + sw s2, 4*4(sp) + sw s3, 5*4(sp) + sw s4, 6*4(sp) + sw s5, 7*4(sp) + sw s6, 8*4(sp) + sw s7, 9*4(sp) + sw s8, 10*4(sp) + sw gp, 11*4(sp) + // sw ra, 12*4(sp) + // sw sp, 13*4(sp) + + // save page table address + la s0, _root_page_table_ptr + lw s1, 0(s0) + sw s1, 4(sp) + + // restore to's registers + lw sp, 0(a1) + lw s1, 4(sp) + sw s1, 0(s0) + + // restore kstack ptr + // la s0, _cur_kstack_ptr + // addi s1, sp, 4 * 14 + // sw s1, 0(s0) + + lw ra, 0(sp) + lw s0, 2*4(sp) + lw s1, 3*4(sp) + lw s2, 4*4(sp) + lw s3, 5*4(sp) + lw s4, 6*4(sp) + lw s5, 7*4(sp) + lw s6, 8*4(sp) + lw s7, 9*4(sp) + lw s8, 10*4(sp) + lw gp, 11*4(sp) + addi sp, sp, (4*14) + + sw zero, 0(a1) + jr ra + nop diff --git a/kernel/src/arch/mipsel/boot/entry.S b/kernel/src/arch/mipsel/boot/entry.S new file mode 100644 index 0000000..e72c353 --- /dev/null +++ b/kernel/src/arch/mipsel/boot/entry.S @@ -0,0 +1,40 @@ +#include "regdef.h" + + .set noreorder + .section .text.entry + .globl _start + .extern _root_page_table_buffer + .extern _cur_kstack_ptr + +_start: + # setup stack and gp + la sp, bootstacktop + la gp, _gp + + la t0, _cur_kstack_ptr + la t1, _root_page_table_buffer + sw t1, 0(t0) + + # set ebase + la t0, trap_entry + mfc0 t1, $15, 1 # C0_EBASE + or t1, t1, t0 + mtc0 t1, $15, 1 + + # exit bootstrap mode + mfc0 t0, $12 # C0_STATUS + li t1, 0xFFBFFFFF # set BEV (bit 22) to 0 + and t0, t0, t1 + mtc0 t0, $12 + + # directly jump to main function + jal rust_main + nop + + .section .bss.stack + .align 12 #PGSHIFT + .global bootstack +bootstack: + .space 4096 * 16 * 8 + .global bootstacktop +bootstacktop: diff --git a/kernel/src/arch/mipsel/boot/linker.ld b/kernel/src/arch/mipsel/boot/linker.ld new file mode 100644 index 0000000..420430d --- /dev/null +++ b/kernel/src/arch/mipsel/boot/linker.ld @@ -0,0 +1,50 @@ +/* Simple linker script for the ucore kernel. + See the GNU ld 'info' manual ("info ld") to learn the syntax. */ + +OUTPUT_ARCH(riscv) +ENTRY(_start) + +BASE_ADDRESS = 0x80100000; + +SECTIONS +{ + + . = BASE_ADDRESS; + start = .; + + .text : { + stext = .; + *(.text.entry) + . = ALIGN(4K); + *(.text.ebase) + *(.text .text.*) + . = ALIGN(4K); + etext = .; + } + + .rodata : { + srodata = .; + *(.rodata .rodata.*) + *(.dtb) + . = ALIGN(4K); + erodata = .; + } + + .data : { + sdata = .; + *(.data .data.*) + edata = .; + } + + .stack : { + *(.bss.stack) + } + + .bss : { + sbss = .; + *(.bss .bss.*) + ebss = .; + } + + PROVIDE(end = .); +} diff --git a/kernel/src/arch/mipsel/boot/regdef.h b/kernel/src/arch/mipsel/boot/regdef.h new file mode 100644 index 0000000..14721cb --- /dev/null +++ b/kernel/src/arch/mipsel/boot/regdef.h @@ -0,0 +1,55 @@ +/* + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 1985 MIPS Computer Systems, Inc. + * Copyright (C) 1994, 95, 99, 2003 by Ralf Baechle + * Copyright (C) 1990 - 1992, 1999 Silicon Graphics, Inc. + */ +#ifndef _ASM_REGDEF_H +#define _ASM_REGDEF_H + + + +/* + * Symbolic register names for 32 bit ABI + */ +#define zero $0 /* wired zero */ +#define AT $1 /* assembler temp - uppercase because of ".set at" */ +#define v0 $2 /* return value */ +#define v1 $3 +#define a0 $4 /* argument registers */ +#define a1 $5 +#define a2 $6 +#define a3 $7 +#define t0 $8 /* caller saved */ +#define t1 $9 +#define t2 $10 +#define t3 $11 +#define t4 $12 +#define t5 $13 +#define t6 $14 +#define t7 $15 +#define s0 $16 /* callee saved */ +#define s1 $17 +#define s2 $18 +#define s3 $19 +#define s4 $20 +#define s5 $21 +#define s6 $22 +#define s7 $23 +#define t8 $24 /* caller saved */ +#define t9 $25 +#define jp $25 /* PIC jump register */ +#define k0 $26 /* kernel scratch */ +#define k1 $27 +#define gp $28 /* global pointer */ +#define sp $29 /* stack pointer */ +#define fp $30 /* frame pointer */ +#define s8 $30 /* same like fp! */ +#define ra $31 /* return address */ + + + +#endif /* _ASM_REGDEF_H */ diff --git a/kernel/src/arch/mipsel/boot/trap.S b/kernel/src/arch/mipsel/boot/trap.S new file mode 100644 index 0000000..2d40343 --- /dev/null +++ b/kernel/src/arch/mipsel/boot/trap.S @@ -0,0 +1,179 @@ +#include "regdef.h" + + .set noat + .set noreorder + .section .text.ebase + .globl trap_entry + +.org 0x0 +trap_entry: +# +0x000: TLB-miss vector + b general_trap_vec + +# +0x180: general vector +.org 0x180 +general_trap_vec: + move k1, sp # save stack pointer to k1 + mfc0 k0, $12 # read cp0.status + andi k0, k0, 0x10 # extract cp0.status.ksu + beq k0, zero, trap_from_kernel + nop # delayslot + +trap_from_user: + # load kstack, we can use k0 to store something +# la k0, kernel_stack +# la sp, kernel_stack_top + la k0, _cur_kstack_ptr + lw sp, 0(k0) + +trap_from_kernel: + /* + * k0 is damaged + * k1 = old stack pointer + * sp = kernel stack */ + + # allocate 38 words for trapframe + 4 extra words + addiu sp, sp, -168 + + # save general registers + sw ra, 160(sp) + sw fp, 156(sp) + sw k1, 152(sp) # k1 = old sp + sw gp, 148(sp) + sw k1, 144(sp) # real k1 is damaged + sw k0, 140(sp) # real k0 is damaged + sw t9, 136(sp) + sw t8, 132(sp) + sw s7, 128(sp) + sw s6, 124(sp) + sw s5, 120(sp) + sw s4, 116(sp) + sw s3, 112(sp) + sw s2, 108(sp) + sw s1, 104(sp) + sw s0, 100(sp) + sw t7, 96(sp) + sw t6, 92(sp) + sw t5, 88(sp) + sw t4, 84(sp) + sw t3, 80(sp) + sw t2, 76(sp) + sw t1, 72(sp) + sw t0, 68(sp) + sw a3, 64(sp) + sw a2, 60(sp) + sw a1, 56(sp) + sw a0, 52(sp) + sw v1, 48(sp) + sw v0, 44(sp) + sw AT, 40(sp) + nop + + # save hi/lo + mflo t1 + sw t1, 36(sp) + mfhi t0 + sw t0, 32(sp) + + # save special registers + mfc0 t0, $8 # cp0.vaddr + sw t0, 28(sp) + + mfc0 t1, $14 # cp0.epc + sw t1, 24(sp) + + mfc0 t0, $13 # cp0.cause + sw t0, 20(sp) + + mfc0 t1, $12 # cp0.status + sw t1, 16(sp) + + # support nested interrupt + la t0, ~0x1b # reset status.ksu, status.exl, status.ie + and t1, t1, t0 + mtc0 t1, $12 # cp0.status + + # prepare to call rust_trap + ori a0, sp, 0 /* set argument (trapframe) */ + jal rust_trap + nop + + .globl trap_return +trap_return: + # restore special registers + lw t1, 16(sp) + ori t1, t1, 0x2 # status.exl + nop + mtc0 t1, $12 # cp0.status + + lw k0, 24(sp) + mtc0 k0, $14 # cp0.epc + + lw t0, 32(sp) + mthi t0 + lw t1, 36(sp) + mtlo t1 + + # restore general registers + lw AT, 40(sp) + lw v0, 44(sp) + lw v1, 48(sp) + lw a0, 52(sp) + lw a1, 56(sp) + lw a2, 60(sp) + lw a3, 64(sp) + lw t0, 68(sp) + lw t1, 72(sp) + lw t2, 76(sp) + lw t3, 80(sp) + lw t4, 84(sp) + lw t5, 88(sp) + lw t6, 92(sp) + lw t7, 96(sp) + lw s0, 100(sp) + lw s1, 104(sp) + lw s2, 108(sp) + lw s3, 112(sp) + lw s4, 116(sp) + lw s5, 120(sp) + lw s6, 124(sp) + lw s7, 128(sp) + lw t8, 132(sp) + lw t9, 136(sp) + + # lw k0, 140(sp) + # lw k1, 144(sp) + lw gp, 148(sp) + lw fp, 156(sp) + lw ra, 160(sp) + + // save kernel stack + la k0, _cur_kstack_ptr + addiu k1, sp, 168 + sw k1, 0(k0) + nop + + // restore stack + lw sp, 152(sp) + + eret + nop + + .section .bss.stack + .align 12 #PGSHIFT + .global kernel_stack +kernel_stack: + .space 1024 * 16 # 16KB for kernel stack + .global kernel_stack_top +kernel_stack_top: + + .align 12 #PGSHIFT + .global _root_page_table_buffer +_root_page_table_buffer: + .space 1024 * 64 # 64KB + .global _root_page_table_ptr +_root_page_table_ptr: + .space 4 # 4bytes + .global _cur_kstack_ptr +_cur_kstack_ptr: + .space 4 # 4bytes diff --git a/kernel/src/arch/mipsel/compiler_rt.rs b/kernel/src/arch/mipsel/compiler_rt.rs new file mode 100644 index 0000000..04ed3c3 --- /dev/null +++ b/kernel/src/arch/mipsel/compiler_rt.rs @@ -0,0 +1,8 @@ +//! Workaround for missing compiler-builtin symbols +//! +//! [atomic](http://llvm.org/docs/Atomics.html#libcalls-atomic) + +#[no_mangle] +pub extern "C" fn abort() { + panic!("abort"); +} diff --git a/kernel/src/arch/mipsel/consts.rs b/kernel/src/arch/mipsel/consts.rs new file mode 100644 index 0000000..ff2ef0c --- /dev/null +++ b/kernel/src/arch/mipsel/consts.rs @@ -0,0 +1,13 @@ +/// Platform specific constants +/// +pub use super::board::consts::*; + +pub const KERNEL_OFFSET: usize = 0x80100000; + +pub const MEMORY_OFFSET: usize = 0x8000_0000; + +pub const USER_STACK_OFFSET: usize = 0x80000000 - USER_STACK_SIZE; +pub const USER_STACK_SIZE: usize = 0x10000; +pub const USER32_STACK_OFFSET: usize = 0x80000000 - USER_STACK_SIZE; + +pub const MAX_DTB_SIZE: usize = 0x2000; diff --git a/kernel/src/arch/mipsel/context.rs b/kernel/src/arch/mipsel/context.rs new file mode 100644 index 0000000..85bc56b --- /dev/null +++ b/kernel/src/arch/mipsel/context.rs @@ -0,0 +1,273 @@ +use mips::registers::cp0; +use mips::tlb; + +/// Saved registers on a trap. +#[derive(Clone)] +#[repr(C)] +pub struct TrapFrame { + /// unused 16 bytes + pub unused: [usize; 4], + /// CP0 status register + pub status: cp0::status::Status, + /// CP0 cause register + pub cause: cp0::cause::Cause, + /// CP0 EPC register + pub epc: usize, + /// CP0 vaddr register + pub vaddr: usize, + /// HI/LO registers + pub hi: usize, + pub lo: usize, + /// General registers + pub at: usize, + pub v0: usize, + pub v1: usize, + pub a0: usize, + pub a1: usize, + pub a2: usize, + pub a3: usize, + pub t0: usize, + pub t1: usize, + pub t2: usize, + pub t3: usize, + pub t4: usize, + pub t5: usize, + pub t6: usize, + pub t7: usize, + pub s0: usize, + pub s1: usize, + pub s2: usize, + pub s3: usize, + pub s4: usize, + pub s5: usize, + pub s6: usize, + pub s7: usize, + pub t8: usize, + pub t9: usize, + pub k0: usize, + pub k1: usize, + pub gp: usize, + pub sp: usize, + pub fp: usize, + pub ra: usize, + /// Reserve space for hartid + pub _hartid: usize, +} + +impl TrapFrame { + /// Constructs TrapFrame for a new kernel thread. + /// + /// The new thread starts at function `entry` with an usize argument `arg`. + /// The stack pointer will be set to `sp`. + fn new_kernel_thread(entry: extern "C" fn(usize) -> !, arg: usize, sp: usize) -> Self { + use core::mem::zeroed; + let mut tf: Self = unsafe { zeroed() }; + tf.a0 = arg; + tf.sp = sp; + tf.epc = entry as usize; + tf.status = cp0::status::read(); + tf.status.set_kernel_mode(); + tf.status.set_ie(); + tf.status.set_exl(); + tf + } + + /// Constructs TrapFrame for a new user thread. + /// + /// The new thread starts at `entry_addr`. + /// The stack pointer will be set to `sp`. + fn new_user_thread(entry_addr: usize, sp: usize) -> Self { + use core::mem::zeroed; + let mut tf: Self = unsafe { zeroed() }; + tf.sp = sp; + tf.epc = entry_addr; + tf.status = cp0::status::read(); + tf.status.set_user_mode(); + tf.status.set_ie(); + tf.status.set_exl(); + tf + } +} + +use core::fmt::{Debug, Error, Formatter}; +impl Debug for TrapFrame { + fn fmt(&self, f: &mut Formatter) -> Result<(), Error> { + f.debug_struct("TrapFrame") + .field("status", &self.status.bits) + .field("epc", &self.epc) + .field("cause", &self.cause.bits) + .field("vaddr", &self.vaddr) + .finish() + } +} + +/// Kernel stack contents for a new thread +#[derive(Debug)] +#[repr(C)] +pub struct InitStack { + context: ContextData, + tf: TrapFrame, +} + +impl InitStack { + /// Push the InitStack on the stack and transfer to a Context. + unsafe fn push_at(self, stack_top: usize) -> Context { + let ptr = (stack_top as *mut Self).sub(1); //real kernel stack top + *ptr = self; + Context { sp: ptr as usize } + } +} + +extern "C" { + fn trap_return(); +} + +/// Saved registers for kernel context switches. +#[derive(Debug, Default)] +#[repr(C)] +struct ContextData { + /// Return address + ra: usize, + /// Page table token + satp: usize, + /// Callee-saved registers + s: [usize; 12], +} + +impl ContextData { + fn new(satp: usize) -> Self { + ContextData { + ra: trap_return as usize, + satp, + ..ContextData::default() + } + } +} + +/// Context of a kernel thread. +#[derive(Debug)] +#[repr(C)] +pub struct Context { + /// The stack pointer of the suspended thread. + /// A `ContextData` is stored here. + sp: usize, +} + +impl Context { + /// Switch to another kernel thread. + /// + /// Push all callee-saved registers at the current kernel stack. + /// Store current sp, switch to target. + /// Pop all callee-saved registers, then return to the target. + #[inline(always)] + pub unsafe fn switch(&mut self, target: &mut Self) { + extern "C" { + fn switch_context(src: *mut Context, dst: *mut Context); + } + + tlb::clear_all_tlb(); + switch_context(self as *mut Context, target as *mut Context); + } + + /// Constructs a null Context for the current running thread. + pub unsafe fn null() -> Self { + Context { sp: 0 } + } + + /// Constructs Context for a new kernel thread. + /// + /// The new thread starts at function `entry` with an usize argument `arg`. + /// The stack pointer will be set to `kstack_top`. + /// The SATP register will be set to `satp`. + pub unsafe fn new_kernel_thread( + entry: extern "C" fn(usize) -> !, + arg: usize, + kstack_top: usize, + satp: usize, + ) -> Self { + info!( + "New kernel thread @ {:x}, stack = {:x}", + entry as usize, kstack_top + ); + + InitStack { + context: ContextData::new(satp), + tf: TrapFrame::new_kernel_thread(entry, arg, kstack_top), + } + .push_at(kstack_top) + } + + /// Constructs Context for a new user thread. + /// + /// The new thread starts at `entry_addr`. + /// The stack pointer of user and kernel mode will be set to `ustack_top`, `kstack_top`. + /// The SATP register will be set to `satp`. + pub unsafe fn new_user_thread( + entry_addr: usize, + ustack_top: usize, + kstack_top: usize, + _is32: bool, + satp: usize, + ) -> Self { + info!( + "New user thread @ {:x}, stack = {:x}", + entry_addr, kstack_top + ); + + InitStack { + context: ContextData::new(satp), + tf: TrapFrame::new_user_thread(entry_addr, ustack_top), + } + .push_at(kstack_top) + } + + /// Fork a user process and get the new Context. + /// + /// The stack pointer in kernel mode will be set to `kstack_top`. + /// The SATP register will be set to `satp`. + /// All the other registers are same as the original. + pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, satp: usize) -> Self { + InitStack { + context: ContextData::new(satp), + tf: { + let mut tf = tf.clone(); + // fork function's ret value, the new process is 0 + tf.v0 = 0; + tf + }, + } + .push_at(kstack_top) + } + + /// Fork a user thread and get the new Context. + /// + /// The stack pointer in kernel mode will be set to `kstack_top`. + /// The SATP register will be set to `satp`. + /// The new user stack will be set to `ustack_top`. + /// The new thread pointer will be set to `tls`. + /// All the other registers are same as the original. + pub unsafe fn new_clone( + tf: &TrapFrame, + ustack_top: usize, + kstack_top: usize, + satp: usize, + tls: usize, + ) -> Self { + InitStack { + context: ContextData::new(satp), + tf: { + let mut tf = tf.clone(); + tf.sp = ustack_top; // sp + tf.v1 = tls; + tf.v0 = 0; // return value + tf + }, + } + .push_at(kstack_top) + } + + /// Used for getting the init TrapFrame of a new user context in `sys_exec`. + pub unsafe fn get_init_tf(&self) -> TrapFrame { + (*(self.sp as *const InitStack)).tf.clone() + } +} diff --git a/kernel/src/arch/mipsel/cpu.rs b/kernel/src/arch/mipsel/cpu.rs new file mode 100644 index 0000000..932744a --- /dev/null +++ b/kernel/src/arch/mipsel/cpu.rs @@ -0,0 +1,30 @@ +use crate::consts::MAX_CPU_NUM; +use core::ptr::{read_volatile, write_volatile}; +use mips::registers::cp0; + +static mut STARTED: [bool; MAX_CPU_NUM] = [false; MAX_CPU_NUM]; + +pub fn id() -> usize { + (cp0::ebase::read_u32() as usize) & 0x3ff +} + +pub unsafe fn has_started(cpu_id: usize) -> bool { + read_volatile(&STARTED[cpu_id]) +} + +pub unsafe fn start_others(hart_mask: usize) { + for cpu_id in 0..MAX_CPU_NUM { + if (hart_mask >> cpu_id) & 1 != 0 { + write_volatile(&mut STARTED[cpu_id], true); + } + } +} + +pub fn halt() { + /* nothing to do */ +} + +pub unsafe fn exit_in_qemu(error_code: u8) -> ! { + /* nothing to do */ + loop {} +} diff --git a/kernel/src/arch/mipsel/driver/mod.rs b/kernel/src/arch/mipsel/driver/mod.rs new file mode 100644 index 0000000..0cf6ad0 --- /dev/null +++ b/kernel/src/arch/mipsel/driver/mod.rs @@ -0,0 +1,16 @@ +//! mipsel drivers + +use super::board; +use once::*; + +pub use self::board::fb; +pub use self::board::serial; +#[path = "../../../drivers/console/mod.rs"] +pub mod console; + +/// Initialize common drivers +pub fn init() { + assert_has_not_been_called!("driver::init must be called only once"); + board::init_driver(); + console::init(); +} diff --git a/kernel/src/arch/mipsel/interrupt.rs b/kernel/src/arch/mipsel/interrupt.rs new file mode 100644 index 0000000..bc60547 --- /dev/null +++ b/kernel/src/arch/mipsel/interrupt.rs @@ -0,0 +1,175 @@ +pub use self::context::*; +use crate::arch::paging::get_root_page_table_ptr; +use crate::drivers::DRIVERS; +use log::*; +use mips::addr::*; +use mips::interrupts; +use mips::paging::{ + PageTable as MIPSPageTable, PageTableEntry, PageTableFlags as EF, TwoLevelPageTable, +}; +use mips::registers::cp0; +use mips::tlb; + +#[path = "context.rs"] +mod context; + +/// Initialize interrupt +pub fn init() { + extern "C" { + fn trap_entry(); + } + unsafe { + // Set the exception vector address + cp0::ebase::write_u32(trap_entry as u32); + println!("Set ebase = {:x}", trap_entry as u32); + + let mut status = cp0::status::read(); + // Enable IPI + status.enable_soft_int0(); + status.enable_soft_int1(); + // Enable clock interrupt + status.enable_hard_int5(); + + cp0::status::write(status); + } + info!("interrupt: init end"); +} + +/// Enable interrupt +#[inline] +pub unsafe fn enable() { + interrupts::enable(); +} + +/// Disable interrupt and return current interrupt status +#[inline] +pub unsafe fn disable_and_store() -> usize { + let e = cp0::status::read_u32() & 1; + interrupts::disable(); + e as usize +} + +/// Enable interrupt if `flags` != 0 +#[inline] +pub unsafe fn restore(flags: usize) { + if flags != 0 { + enable(); + } +} + +/// Dispatch and handle interrupt. +/// +/// This function is called from `trap.asm`. +#[no_mangle] +pub extern "C" fn rust_trap(tf: &mut TrapFrame) { + use cp0::cause::Exception as E; + trace!("Exception @ CPU{}: {:?} ", 0, tf.cause.cause()); + match tf.cause.cause() { + E::Interrupt => interrupt_dispatcher(tf), + E::Syscall => syscall(tf), + E::TLBModification => page_fault(tf), + E::TLBLoadMiss => page_fault(tf), + E::TLBStoreMiss => page_fault(tf), + _ => { + error!("Unhandled Exception @ CPU{}: {:?} ", 0, tf.cause.cause()); + crate::trap::error(tf) + } + } + trace!("Interrupt end"); +} + +fn interrupt_dispatcher(tf: &mut TrapFrame) { + let pint = tf.cause.pending_interrupt(); + trace!(" Interrupt {:08b} ", pint); + if (pint & 0b100_000_00) != 0 { + timer(); + } else if (pint & 0b011_111_00) != 0 { + external(); + } else { + ipi(); + } +} + +fn external() { + // true means handled, false otherwise + let handlers = [try_process_serial, try_process_drivers]; + for handler in handlers.iter() { + if handler() == true { + break; + } + } +} + +fn try_process_serial() -> bool { + match super::io::getchar_option() { + Some(ch) => { + trace!("Get char {} from serial", ch); + crate::trap::serial(ch); + true + } + None => false, + } +} + +fn try_process_drivers() -> bool { + // TODO + for driver in DRIVERS.read().iter() { + if driver.try_handle_interrupt(None) == true { + return true; + } + } + return false; +} + +fn ipi() { + debug!("IPI"); + cp0::cause::reset_soft_int0(); + cp0::cause::reset_soft_int1(); +} + +fn timer() { + super::timer::set_next(); + crate::trap::timer(); +} + +fn syscall(tf: &mut TrapFrame) { + tf.epc += 4; // Must before syscall, because of fork. + let arguments = [tf.a0, tf.a1, tf.a2, tf.a3, tf.t0, tf.t1]; + trace!("MIPS syscall {} invoked with {:?}", tf.v0, arguments); + + let ret = crate::syscall::syscall(tf.v0, arguments, tf) as isize; + // comply with mips n32 abi, always return a positive value + // https://git.musl-libc.org/cgit/musl/tree/arch/mipsn32/syscall_arch.h + if (ret < 0) { + tf.v0 = (-ret) as usize; + tf.a3 = 1; + } else { + tf.v0 = ret as usize; + tf.a3 = 0; + } +} + +fn page_fault(tf: &mut TrapFrame) { + // TODO: set access/dirty bit + let addr = tf.vaddr; + trace!("\nEXCEPTION: Page Fault @ {:#x}", addr); + + let virt_addr = VirtAddr::new(addr); + let root_table = unsafe { &mut *(get_root_page_table_ptr() as *mut MIPSPageTable) }; + let tlb_result = root_table.lookup(addr); + match tlb_result { + Ok(tlb_entry) => { + trace!( + "PhysAddr = {:x}/{:x}", + tlb_entry.entry_lo0.get_pfn() << 12, + tlb_entry.entry_lo1.get_pfn() << 12 + ); + tlb::write_tlb_random(tlb_entry) + } + Err(()) => { + if !crate::memory::handle_page_fault(addr) { + crate::trap::error(tf); + } + } + } +} diff --git a/kernel/src/arch/mipsel/io.rs b/kernel/src/arch/mipsel/io.rs new file mode 100644 index 0000000..e5be3e5 --- /dev/null +++ b/kernel/src/arch/mipsel/io.rs @@ -0,0 +1,25 @@ +//! Input/output for mipsel. + +use super::driver::console::CONSOLE; +use super::driver::serial::*; +use core::fmt::{Arguments, Write}; + +pub fn getchar() -> char { + unsafe { SERIAL_PORT.force_unlock() } + SERIAL_PORT.lock().getchar() +} + +pub fn getchar_option() -> Option { + unsafe { SERIAL_PORT.force_unlock() } + SERIAL_PORT.lock().getchar_option() +} + +pub fn putfmt(fmt: Arguments) { + unsafe { SERIAL_PORT.force_unlock() } + SERIAL_PORT.lock().write_fmt(fmt).unwrap(); + + unsafe { CONSOLE.force_unlock() } + if let Some(console) = CONSOLE.lock().as_mut() { + console.write_fmt(fmt).unwrap(); + } +} diff --git a/kernel/src/arch/mipsel/memory.rs b/kernel/src/arch/mipsel/memory.rs new file mode 100644 index 0000000..43c99f0 --- /dev/null +++ b/kernel/src/arch/mipsel/memory.rs @@ -0,0 +1,77 @@ +use crate::arch::paging::*; +use crate::consts::{KERNEL_OFFSET, MEMORY_END, MEMORY_OFFSET}; +use crate::memory::{init_heap, Linear, MemoryAttr, MemorySet, FRAME_ALLOCATOR}; +use core::mem; +use log::*; +use rcore_memory::PAGE_SIZE; + +/// Initialize the memory management module +pub fn init() { + // initialize heap and Frame allocator + init_frame_allocator(); + init_heap(); + + set_root_page_table_ptr(0xFFFF_FFFF); + extern "C" { + fn _root_page_table_buffer(); + fn _root_page_table_ptr(); + } + + println!("_root_page_table_ptr {:x}", _root_page_table_ptr as usize); +} + +pub fn init_other() { + // TODO: init other CPU cores +} + +fn init_frame_allocator() { + use bitmap_allocator::BitAlloc; + use core::ops::Range; + + let mut ba = FRAME_ALLOCATOR.lock(); + let range = to_range( + (end as usize) - KERNEL_OFFSET + MEMORY_OFFSET + PAGE_SIZE, + MEMORY_END, + ); + ba.insert(range); + + info!("frame allocator: init end"); + + /// Transform memory area `[start, end)` to integer range for `FrameAllocator` + fn to_range(start: usize, end: usize) -> Range { + let page_start = (start - MEMORY_OFFSET) / PAGE_SIZE; + let page_end = (end - MEMORY_OFFSET - 1) / PAGE_SIZE + 1; + assert!(page_start < page_end, "illegal range for frame allocator"); + page_start..page_end + } +} + +// First core stores its SATP here. +// Other cores load it later. +static mut SATP: usize = 0; + +pub unsafe fn clear_bss() { + let start = sbss as usize; + let end = ebss as usize; + let step = core::mem::size_of::(); + for i in (start..end).step_by(step) { + (i as *mut usize).write(0); + } +} + +// Symbols provided by linker script +#[allow(dead_code)] +extern "C" { + fn stext(); + fn etext(); + fn sdata(); + fn edata(); + fn srodata(); + fn erodata(); + fn sbss(); + fn ebss(); + fn start(); + fn end(); + fn bootstack(); + fn bootstacktop(); +} diff --git a/kernel/src/arch/mipsel/mod.rs b/kernel/src/arch/mipsel/mod.rs new file mode 100644 index 0000000..74103db --- /dev/null +++ b/kernel/src/arch/mipsel/mod.rs @@ -0,0 +1,83 @@ +pub mod compiler_rt; +pub mod consts; +pub mod cpu; +pub mod driver; +pub mod interrupt; +pub mod io; +pub mod memory; +pub mod paging; +pub mod rand; +pub mod syscall; +pub mod timer; + +use log::*; +use mips::registers::cp0; + +#[cfg(feature = "board_malta")] +#[path = "board/malta/mod.rs"] +pub mod board; + +#[cfg(feature = "board_thinpad")] +#[path = "board/thinpad/mod.rs"] +pub mod board; + +#[cfg(feature = "board_mipssim")] +#[path = "board/mipssim/mod.rs"] +pub mod board; + +extern "C" { + fn _dtb_start(); + fn _dtb_end(); +} + +#[no_mangle] +pub extern "C" fn rust_main() -> ! { + // unsafe { cpu::set_cpu_id(hartid); } + + let ebase = cp0::ebase::read_u32(); + let cpu_id = ebase & 0x3ff; + let dtb_start = _dtb_start as usize; + + if cpu_id != BOOT_CPU_ID { + // TODO: run others_main on other CPU + // while unsafe { !cpu::has_started(hartid) } { } + // println!("Hello RISCV! in hart {}, dtb @ {:#x}", hartid, dtb); + // others_main(); + loop {} + } + + unsafe { + memory::clear_bss(); + } + + board::init_serial_early(); + crate::logging::init(); + + interrupt::init(); + memory::init(); + timer::init(); + driver::init(); + + println!("Hello MIPS 32 from CPU {}, dtb @ {:#x}", cpu_id, dtb_start); + + crate::drivers::init(dtb_start); + crate::process::init(); + + // TODO: start other CPU + // unsafe { cpu::start_others(hart_mask); } + crate::kmain(); +} + +fn others_main() -> ! { + interrupt::init(); + memory::init_other(); + timer::init(); + crate::kmain(); +} + +const BOOT_CPU_ID: u32 = 0; + +global_asm!(include_str!("boot/context.gen.s")); +global_asm!(include_str!("boot/entry.gen.s")); +global_asm!(include_str!("boot/trap.gen.s")); +global_asm!(include_str!("boot/dtb.gen.s")); diff --git a/kernel/src/arch/mipsel/paging.rs b/kernel/src/arch/mipsel/paging.rs new file mode 100644 index 0000000..a9aa72b --- /dev/null +++ b/kernel/src/arch/mipsel/paging.rs @@ -0,0 +1,232 @@ +// Depends on kernel +use crate::memory::{active_table, alloc_frame, dealloc_frame}; +use log::*; +use mips::addr::*; +use mips::paging::{FrameAllocator, FrameDeallocator}; +use mips::paging::{ + Mapper, PageTable as MIPSPageTable, PageTableEntry, PageTableFlags as EF, TwoLevelPageTable, +}; +use mips::tlb::*; +use rcore_memory::paging::*; + +pub struct ActivePageTable(TwoLevelPageTable<'static>, PageEntry); + +/// PageTableEntry: the contents of this entry. +/// Page: this entry is the pte of page `Page`. +pub struct PageEntry(&'static mut PageTableEntry, Page); + +impl PageTable for ActivePageTable { + fn map(&mut self, addr: usize, target: usize) -> &mut Entry { + // map the 4K `page` to the 4K `frame` with `flags` + let flags = EF::VALID | EF::WRITABLE | EF::CACHEABLE; + let page = Page::of_addr(VirtAddr::new(addr)); + let frame = Frame::of_addr(PhysAddr::new(target)); + // map the page to the frame using FrameAllocatorForRiscv + // we may need frame allocator to alloc frame for new page table(first/second) + self.0 + .map_to(page, frame, flags, &mut FrameAllocatorForRiscv) + .unwrap() + .flush(); + self.get_entry(addr).expect("fail to get entry") + } + + fn unmap(&mut self, addr: usize) { + let page = Page::of_addr(VirtAddr::new(addr)); + let (_, flush) = self.0.unmap(page).unwrap(); + flush.flush(); + } + + fn get_entry(&mut self, vaddr: usize) -> Option<&mut Entry> { + let page = Page::of_addr(VirtAddr::new(vaddr)); + if let Ok(e) = self.0.ref_entry(page.clone()) { + let e = unsafe { &mut *(e as *mut PageTableEntry) }; + self.1 = PageEntry(e, page); + Some(&mut self.1 as &mut Entry) + } else { + None + } + } +} + +extern "C" { + fn _root_page_table_buffer(); + fn _root_page_table_ptr(); +} + +pub fn set_root_page_table_ptr(ptr: usize) { + unsafe { + clear_all_tlb(); + *(_root_page_table_ptr as *mut usize) = ptr; + } +} + +pub fn get_root_page_table_ptr() -> usize { + unsafe { *(_root_page_table_ptr as *mut usize) } +} + +pub fn root_page_table_buffer() -> &'static mut MIPSPageTable { + unsafe { &mut *(_root_page_table_ptr as *mut MIPSPageTable) } +} + +impl PageTableExt for ActivePageTable {} + +/// The virtual address of root page table + +impl ActivePageTable { + pub unsafe fn new() -> Self { + ActivePageTable( + TwoLevelPageTable::new(root_page_table_buffer()), + ::core::mem::uninitialized(), + ) + } +} + +/// implementation for the Entry trait in /crate/memory/src/paging/mod.rs +impl Entry for PageEntry { + fn update(&mut self) { + unsafe { + clear_all_tlb(); + } + } + fn accessed(&self) -> bool { + self.0.flags().contains(EF::ACCESSED) + } + fn dirty(&self) -> bool { + self.0.flags().contains(EF::DIRTY) + } + fn writable(&self) -> bool { + self.0.flags().contains(EF::WRITABLE) + } + fn present(&self) -> bool { + self.0.flags().contains(EF::VALID) + } + fn clear_accessed(&mut self) { + self.0.flags_mut().remove(EF::ACCESSED); + } + fn clear_dirty(&mut self) { + self.0.flags_mut().remove(EF::DIRTY); + } + fn set_writable(&mut self, value: bool) { + self.0.flags_mut().set(EF::WRITABLE, value); + } + fn set_present(&mut self, value: bool) { + self.0.flags_mut().set(EF::VALID, value); + } + fn target(&self) -> usize { + self.0.addr().as_usize() + } + fn set_target(&mut self, target: usize) { + let flags = self.0.flags(); + let frame = Frame::of_addr(PhysAddr::new(target)); + self.0.set(frame, flags); + } + fn writable_shared(&self) -> bool { + false + } + fn readonly_shared(&self) -> bool { + false + } + fn set_shared(&mut self, writable: bool) {} + fn clear_shared(&mut self) {} + fn swapped(&self) -> bool { + self.0.flags().contains(EF::RESERVED1) + } + fn set_swapped(&mut self, value: bool) { + self.0.flags_mut().set(EF::RESERVED1, value); + } + fn user(&self) -> bool { + true + } + fn set_user(&mut self, value: bool) {} + fn execute(&self) -> bool { + true + } + fn set_execute(&mut self, value: bool) {} + fn mmio(&self) -> u8 { + 0 + } + fn set_mmio(&mut self, _value: u8) {} +} + +#[derive(Debug)] +pub struct InactivePageTable0 { + root_frame: Frame, +} + +impl InactivePageTable for InactivePageTable0 { + type Active = ActivePageTable; + + fn new_bare() -> Self { + let target = alloc_frame().expect("failed to allocate frame"); + let frame = Frame::of_addr(PhysAddr::new(target)); + + let table = unsafe { &mut *(target as *mut MIPSPageTable) }; + + table.zero(); + InactivePageTable0 { root_frame: frame } + } + + fn map_kernel(&mut self) { + /* nothing to do */ + } + + fn token(&self) -> usize { + self.root_frame.to_kernel_unmapped().as_usize() + } + + unsafe fn set_token(token: usize) { + set_root_page_table_ptr(token); + } + + fn active_token() -> usize { + get_root_page_table_ptr() + } + + fn flush_tlb() { + unsafe { + clear_all_tlb(); + } + } + + fn edit(&mut self, f: impl FnOnce(&mut Self::Active) -> T) -> T { + let pt: *mut MIPSPageTable = unsafe { self.token() as *mut MIPSPageTable }; + + unsafe { + clear_all_tlb(); + } + + let mut active = unsafe { + ActivePageTable( + TwoLevelPageTable::new(&mut *pt), + ::core::mem::uninitialized(), + ) + }; + + let ret = f(&mut active); + + unsafe { + clear_all_tlb(); + } + ret + } +} + +impl Drop for InactivePageTable0 { + fn drop(&mut self) { + dealloc_frame(self.root_frame.start_address().as_usize()); + } +} + +struct FrameAllocatorForRiscv; + +impl FrameAllocator for FrameAllocatorForRiscv { + fn alloc(&mut self) -> Option { + alloc_frame().map(|addr| Frame::of_addr(PhysAddr::new(addr))) + } +} + +impl FrameDeallocator for FrameAllocatorForRiscv { + fn dealloc(&mut self, frame: Frame) { + dealloc_frame(frame.start_address().as_usize()); + } +} diff --git a/kernel/src/arch/mipsel/rand.rs b/kernel/src/arch/mipsel/rand.rs new file mode 100644 index 0000000..f2f1e2c --- /dev/null +++ b/kernel/src/arch/mipsel/rand.rs @@ -0,0 +1,3 @@ +pub fn rand() -> u64 { + return 0; +} diff --git a/kernel/src/arch/mipsel/syscall.rs b/kernel/src/arch/mipsel/syscall.rs new file mode 100644 index 0000000..7e1144f --- /dev/null +++ b/kernel/src/arch/mipsel/syscall.rs @@ -0,0 +1,391 @@ +//! MIPS N32 ABI syscall ids +//! Reference: https://git.linux-mips.org/cgit/ralf/linux.git/tree/arch/mips/include/uapi/asm/unistd.h + +extern crate paste; + +pub const MIPS_SYSCALL_OFFSET: usize = 4000; + +macro_rules! define_syscall { + ($name: ident, $id: expr) => { + paste::item! { + pub const [] : usize = MIPS_SYSCALL_OFFSET + $id; + } + }; +} + +define_syscall!(SYSCALL, 0); +define_syscall!(EXIT, 1); +define_syscall!(FORK, 2); +define_syscall!(READ, 3); +define_syscall!(WRITE, 4); +define_syscall!(OPEN, 5); +define_syscall!(CLOSE, 6); +define_syscall!(WAITPID, 7); +define_syscall!(CREAT, 8); +define_syscall!(LINK, 9); +define_syscall!(UNLINK, 10); +define_syscall!(EXECVE, 11); +define_syscall!(CHDIR, 12); +define_syscall!(TIME, 13); +define_syscall!(MKNOD, 14); +define_syscall!(CHMOD, 15); +define_syscall!(LCHOWN, 16); +define_syscall!(BREAK, 17); +define_syscall!(UNUSED18, 18); +define_syscall!(LSEEK, 19); +define_syscall!(GETPID, 20); +define_syscall!(MOUNT, 21); +define_syscall!(UMOUNT, 22); +define_syscall!(SETUID, 23); +define_syscall!(GETUID, 24); +define_syscall!(STIME, 25); +define_syscall!(PTRACE, 26); +define_syscall!(ALARM, 27); +define_syscall!(UNUSED28, 28); +define_syscall!(PAUSE, 29); +define_syscall!(UTIME, 30); +define_syscall!(STTY, 31); +define_syscall!(GTTY, 32); +define_syscall!(ACCESS, 33); +define_syscall!(NICE, 34); +define_syscall!(FTIME, 35); +define_syscall!(SYNC, 36); +define_syscall!(KILL, 37); +define_syscall!(RENAME, 38); +define_syscall!(MKDIR, 39); +define_syscall!(RMDIR, 40); +define_syscall!(DUP, 41); +define_syscall!(PIPE, 42); +define_syscall!(TIMES, 43); +define_syscall!(PROF, 44); +define_syscall!(BRK, 45); +define_syscall!(SETGID, 46); +define_syscall!(GETGID, 47); +define_syscall!(SIGNAL, 48); +define_syscall!(GETEUID, 49); +define_syscall!(GETEGID, 50); +define_syscall!(ACCT, 51); +define_syscall!(UMOUNT2, 52); +define_syscall!(LOCK, 53); +define_syscall!(IOCTL, 54); +define_syscall!(FCNTL, 55); +define_syscall!(MPX, 56); +define_syscall!(SETPGID, 57); +define_syscall!(ULIMIT, 58); +define_syscall!(UNUSED59, 59); +define_syscall!(UMASK, 60); +define_syscall!(CHROOT, 61); +define_syscall!(USTAT, 62); +define_syscall!(DUP2, 63); +define_syscall!(GETPPID, 64); +define_syscall!(GETPGRP, 65); +define_syscall!(SETSID, 66); +define_syscall!(SIGACTION, 67); +define_syscall!(SGETMASK, 68); +define_syscall!(SSETMASK, 69); +define_syscall!(SETREUID, 70); +define_syscall!(SETREGID, 71); +define_syscall!(SIGSUSPEND, 72); +define_syscall!(SIGPENDING, 73); +define_syscall!(SETHOSTNAME, 74); +define_syscall!(SETRLIMIT, 75); +define_syscall!(GETRLIMIT, 76); +define_syscall!(GETRUSAGE, 77); +define_syscall!(GETTIMEOFDAY, 78); +define_syscall!(SETTIMEOFDAY, 79); +define_syscall!(GETGROUPS, 80); +define_syscall!(SETGROUPS, 81); +define_syscall!(RESERVED82, 82); +define_syscall!(SYMLINK, 83); +define_syscall!(UNUSED84, 84); +define_syscall!(READLINK, 85); +define_syscall!(USELIB, 86); +define_syscall!(SWAPON, 87); +define_syscall!(REBOOT, 88); +define_syscall!(READDIR, 89); +define_syscall!(MMAP, 90); +define_syscall!(MUNMAP, 91); +define_syscall!(TRUNCATE, 92); +define_syscall!(FTRUNCATE, 93); +define_syscall!(FCHMOD, 94); +define_syscall!(FCHOWN, 95); +define_syscall!(GETPRIORITY, 96); +define_syscall!(SETPRIORITY, 97); +define_syscall!(PROFIL, 98); +define_syscall!(STATFS, 99); +define_syscall!(FSTATFS, 100); +define_syscall!(IOPERM, 101); +define_syscall!(SOCKETCALL, 102); +define_syscall!(SYSLOG, 103); +define_syscall!(SETITIMER, 104); +define_syscall!(GETITIMER, 105); +define_syscall!(STAT, 106); +define_syscall!(LSTAT, 107); +define_syscall!(FSTAT, 108); +define_syscall!(UNUSED109, 109); +define_syscall!(IOPL, 110); +define_syscall!(VHANGUP, 111); +define_syscall!(IDLE, 112); +define_syscall!(VM86, 113); +define_syscall!(WAIT4, 114); +define_syscall!(SWAPOFF, 115); +define_syscall!(SYSINFO, 116); +define_syscall!(IPC, 117); +define_syscall!(FSYNC, 118); +define_syscall!(SIGRETURN, 119); +define_syscall!(CLONE, 120); +define_syscall!(SETDOMAINNAME, 121); +define_syscall!(UNAME, 122); +define_syscall!(MODIFY_LDT, 123); +define_syscall!(ADJTIMEX, 124); +define_syscall!(MPROTECT, 125); +define_syscall!(SIGPROCMASK, 126); +define_syscall!(CREATE_MODULE, 127); +define_syscall!(INIT_MODULE, 128); +define_syscall!(DELETE_MODULE, 129); +define_syscall!(GET_KERNEL_SYMS, 130); +define_syscall!(QUOTACTL, 131); +define_syscall!(GETPGID, 132); +define_syscall!(FCHDIR, 133); +define_syscall!(BDFLUSH, 134); +define_syscall!(SYSFS, 135); +define_syscall!(PERSONALITY, 136); +define_syscall!(AFS_SYSCALL, 137); +define_syscall!(SETFSUID, 138); +define_syscall!(SETFSGID, 139); +define_syscall!(_LLSEEK, 140); +define_syscall!(GETDENTS, 141); +define_syscall!(_NEWSELECT, 142); +define_syscall!(FLOCK, 143); +define_syscall!(MSYNC, 144); +define_syscall!(READV, 145); +define_syscall!(WRITEV, 146); +define_syscall!(CACHEFLUSH, 147); +define_syscall!(CACHECTL, 148); +define_syscall!(SYSMIPS, 149); +define_syscall!(UNUSED150, 150); +define_syscall!(GETSID, 151); +define_syscall!(FDATASYNC, 152); +define_syscall!(_SYSCTL, 153); +define_syscall!(MLOCK, 154); +define_syscall!(MUNLOCK, 155); +define_syscall!(MLOCKALL, 156); +define_syscall!(MUNLOCKALL, 157); +define_syscall!(SCHED_SETPARAM, 158); +define_syscall!(SCHED_GETPARAM, 159); +define_syscall!(SCHED_SETSCHEDULER, 160); +define_syscall!(SCHED_GETSCHEDULER, 161); +define_syscall!(SCHED_YIELD, 162); +define_syscall!(SCHED_GET_PRIORITY_MAX, 163); +define_syscall!(SCHED_GET_PRIORITY_MIN, 164); +define_syscall!(SCHED_RR_GET_INTERVAL, 165); +define_syscall!(NANOSLEEP, 166); +define_syscall!(MREMAP, 167); +define_syscall!(ACCEPT, 168); +define_syscall!(BIND, 169); +define_syscall!(CONNECT, 170); +define_syscall!(GETPEERNAME, 171); +define_syscall!(GETSOCKNAME, 172); +define_syscall!(GETSOCKOPT, 173); +define_syscall!(LISTEN, 174); +define_syscall!(RECV, 175); +define_syscall!(RECVFROM, 176); +define_syscall!(RECVMSG, 177); +define_syscall!(SEND, 178); +define_syscall!(SENDMSG, 179); +define_syscall!(SENDTO, 180); +define_syscall!(SETSOCKOPT, 181); +define_syscall!(SHUTDOWN, 182); +define_syscall!(SOCKET, 183); +define_syscall!(SOCKETPAIR, 184); +define_syscall!(SETRESUID, 185); +define_syscall!(GETRESUID, 186); +define_syscall!(QUERY_MODULE, 187); +define_syscall!(POLL, 188); +define_syscall!(NFSSERVCTL, 189); +define_syscall!(SETRESGID, 190); +define_syscall!(GETRESGID, 191); +define_syscall!(PRCTL, 192); +define_syscall!(RT_SIGRETURN, 193); +define_syscall!(RT_SIGACTION, 194); +define_syscall!(RT_SIGPROCMASK, 195); +define_syscall!(RT_SIGPENDING, 196); +define_syscall!(RT_SIGTIMEDWAIT, 197); +define_syscall!(RT_SIGQUEUEINFO, 198); +define_syscall!(RT_SIGSUSPEND, 199); +define_syscall!(PREAD64, 200); +define_syscall!(PWRITE64, 201); +define_syscall!(CHOWN, 202); +define_syscall!(GETCWD, 203); +define_syscall!(CAPGET, 204); +define_syscall!(CAPSET, 205); +define_syscall!(SIGALTSTACK, 206); +define_syscall!(SENDFILE, 207); +define_syscall!(GETPMSG, 208); +define_syscall!(PUTPMSG, 209); +define_syscall!(MMAP2, 210); +define_syscall!(TRUNCATE64, 211); +define_syscall!(FTRUNCATE64, 212); +define_syscall!(STAT64, 213); +define_syscall!(LSTAT64, 214); +define_syscall!(FSTAT64, 215); +define_syscall!(PIVOT_ROOT, 216); +define_syscall!(MINCORE, 217); +define_syscall!(MADVISE, 218); +define_syscall!(GETDENTS64, 219); +define_syscall!(FCNTL64, 220); +define_syscall!(RESERVED221, 221); +define_syscall!(GETTID, 222); +define_syscall!(READAHEAD, 223); +define_syscall!(SETXATTR, 224); +define_syscall!(LSETXATTR, 225); +define_syscall!(FSETXATTR, 226); +define_syscall!(GETXATTR, 227); +define_syscall!(LGETXATTR, 228); +define_syscall!(FGETXATTR, 229); +define_syscall!(LISTXATTR, 230); +define_syscall!(LLISTXATTR, 231); +define_syscall!(FLISTXATTR, 232); +define_syscall!(REMOVEXATTR, 233); +define_syscall!(LREMOVEXATTR, 234); +define_syscall!(FREMOVEXATTR, 235); +define_syscall!(TKILL, 236); +define_syscall!(SENDFILE64, 237); +define_syscall!(FUTEX, 238); +define_syscall!(SCHED_SETAFFINITY, 239); +define_syscall!(SCHED_GETAFFINITY, 240); +define_syscall!(IO_SETUP, 241); +define_syscall!(IO_DESTROY, 242); +define_syscall!(IO_GETEVENTS, 243); +define_syscall!(IO_SUBMIT, 244); +define_syscall!(IO_CANCEL, 245); +define_syscall!(EXIT_GROUP, 246); +define_syscall!(LOOKUP_DCOOKIE, 247); +define_syscall!(EPOLL_CREATE, 248); +define_syscall!(EPOLL_CTL, 249); +define_syscall!(EPOLL_WAIT, 250); +define_syscall!(REMAP_FILE_PAGES, 251); +define_syscall!(SET_TID_ADDRESS, 252); +define_syscall!(RESTART_SYSCALL, 253); +define_syscall!(FADVISE64, 254); +define_syscall!(STATFS64, 255); +define_syscall!(FSTATFS64, 256); +define_syscall!(TIMER_CREATE, 257); +define_syscall!(TIMER_SETTIME, 258); +define_syscall!(TIMER_GETTIME, 259); +define_syscall!(TIMER_GETOVERRUN, 260); +define_syscall!(TIMER_DELETE, 261); +define_syscall!(CLOCK_SETTIME, 262); +define_syscall!(CLOCK_GETTIME, 263); +define_syscall!(CLOCK_GETRES, 264); +define_syscall!(CLOCK_NANOSLEEP, 265); +define_syscall!(TGKILL, 266); +define_syscall!(UTIMES, 267); +define_syscall!(MBIND, 268); +define_syscall!(GET_MEMPOLICY, 269); +define_syscall!(SET_MEMPOLICY, 270); +define_syscall!(MQ_OPEN, 271); +define_syscall!(MQ_UNLINK, 272); +define_syscall!(MQ_TIMEDSEND, 273); +define_syscall!(MQ_TIMEDRECEIVE, 274); +define_syscall!(MQ_NOTIFY, 275); +define_syscall!(MQ_GETSETATTR, 276); +define_syscall!(VSERVER, 277); +define_syscall!(WAITID, 278); +define_syscall!(SYS_SETALTROOT, 279); +define_syscall!(ADD_KEY, 280); +define_syscall!(REQUEST_KEY, 281); +define_syscall!(KEYCTL, 282); +define_syscall!(SET_THREAD_AREA, 283); +define_syscall!(INOTIFY_INIT, 284); +define_syscall!(INOTIFY_ADD_WATCH, 285); +define_syscall!(INOTIFY_RM_WATCH, 286); +define_syscall!(MIGRATE_PAGES, 287); +define_syscall!(OPENAT, 288); +define_syscall!(MKDIRAT, 289); +define_syscall!(MKNODAT, 290); +define_syscall!(FCHOWNAT, 291); +define_syscall!(FUTIMESAT, 292); +define_syscall!(FSTATAT64, 293); +define_syscall!(UNLINKAT, 294); +define_syscall!(RENAMEAT, 295); +define_syscall!(LINKAT, 296); +define_syscall!(SYMLINKAT, 297); +define_syscall!(READLINKAT, 298); +define_syscall!(FCHMODAT, 299); +define_syscall!(FACCESSAT, 300); +define_syscall!(PSELECT6, 301); +define_syscall!(PPOLL, 302); +define_syscall!(UNSHARE, 303); +define_syscall!(SPLICE, 304); +define_syscall!(SYNC_FILE_RANGE, 305); +define_syscall!(TEE, 306); +define_syscall!(VMSPLICE, 307); +define_syscall!(MOVE_PAGES, 308); +define_syscall!(SET_ROBUST_LIST, 309); +define_syscall!(GET_ROBUST_LIST, 310); +define_syscall!(KEXEC_LOAD, 311); +define_syscall!(GETCPU, 312); +define_syscall!(EPOLL_PWAIT, 313); +define_syscall!(IOPRIO_SET, 314); +define_syscall!(IOPRIO_GET, 315); +define_syscall!(UTIMENSAT, 316); +define_syscall!(SIGNALFD, 317); +define_syscall!(TIMERFD, 318); +define_syscall!(EVENTFD, 319); +define_syscall!(FALLOCATE, 320); +define_syscall!(TIMERFD_CREATE, 321); +define_syscall!(TIMERFD_GETTIME, 322); +define_syscall!(TIMERFD_SETTIME, 323); +define_syscall!(SIGNALFD4, 324); +define_syscall!(EVENTFD2, 325); +define_syscall!(EPOLL_CREATE1, 326); +define_syscall!(DUP3, 327); +define_syscall!(PIPE2, 328); +define_syscall!(INOTIFY_INIT1, 329); +define_syscall!(PREADV, 330); +define_syscall!(PWRITEV, 331); +define_syscall!(RT_TGSIGQUEUEINFO, 332); +define_syscall!(PERF_EVENT_OPEN, 333); +define_syscall!(ACCEPT4, 334); +define_syscall!(RECVMMSG, 335); +define_syscall!(FANOTIFY_INIT, 336); +define_syscall!(FANOTIFY_MARK, 337); +define_syscall!(PRLIMIT64, 338); +define_syscall!(NAME_TO_HANDLE_AT, 339); +define_syscall!(OPEN_BY_HANDLE_AT, 340); +define_syscall!(CLOCK_ADJTIME, 341); +define_syscall!(SYNCFS, 342); +define_syscall!(SENDMMSG, 343); +define_syscall!(SETNS, 344); +define_syscall!(PROCESS_VM_READV, 345); +define_syscall!(PROCESS_VM_WRITEV, 346); +define_syscall!(KCMP, 347); +define_syscall!(FINIT_MODULE, 348); +define_syscall!(SCHED_SETATTR, 349); +define_syscall!(SCHED_GETATTR, 350); +define_syscall!(RENAMEAT2, 351); +define_syscall!(SECCOMP, 352); +define_syscall!(GETRANDOM, 353); +define_syscall!(MEMFD_CREATE, 354); +define_syscall!(BPF, 355); +define_syscall!(EXECVEAT, 356); +define_syscall!(USERFAULTFD, 357); +define_syscall!(MEMBARRIER, 358); +define_syscall!(MLOCK2, 359); +define_syscall!(COPY_FILE_RANGE, 360); +define_syscall!(PREADV2, 361); +define_syscall!(PWRITEV2, 362); +define_syscall!(PKEY_MPROTECT, 363); +define_syscall!(PKEY_ALLOC, 364); +define_syscall!(PKEY_FREE, 365); +define_syscall!(STATX, 366); +define_syscall!(RSEQ, 367); +define_syscall!(IO_PGETEVENTS, 368); + +// non-existent syscalls, will not be called or matched +pub const SYS_NEWFSTATAT: usize = 0; + +// custom temporary syscall +pub const SYS_MAP_PCI_DEVICE: usize = 999; +pub const SYS_GET_PADDR: usize = 998; diff --git a/kernel/src/arch/mipsel/timer.rs b/kernel/src/arch/mipsel/timer.rs new file mode 100644 index 0000000..9057c02 --- /dev/null +++ b/kernel/src/arch/mipsel/timer.rs @@ -0,0 +1,24 @@ +use log::*; +use mips::registers::cp0; + +pub fn read_epoch() -> u64 { + // TODO: support RTC + 0 +} + +/// Enable timer interrupt +pub fn init() { + // Enable supervisor timer interrupt + cp0::status::enable_hard_int5(); // IP(7), timer interrupt + cp0::count::write_u32(0); + set_next(); + info!("timer: init end"); +} + +/// Set the next timer interrupt +pub fn set_next() { + // 100Hz @ QEMU + let timebase = 250000; + cp0::count::write_u32(0); + cp0::compare::write_u32(timebase); +} diff --git a/kernel/src/backtrace.rs b/kernel/src/backtrace.rs index 32f6919..223e99b 100644 --- a/kernel/src/backtrace.rs +++ b/kernel/src/backtrace.rs @@ -22,6 +22,11 @@ pub fn fp() -> usize { unsafe { asm!("mov %rbp, $0" : "=r"(ptr)); } + #[cfg(any(target_arch = "mips"))] + unsafe { + // read $sp + asm!("ori $0, $$29, 0" : "=r"(ptr)); + } ptr } @@ -43,6 +48,11 @@ pub fn lr() -> usize { asm!("movq 8(%rbp), $0" : "=r"(ptr)); } + #[cfg(target_arch = "mips")] + unsafe { + asm!("ori $0, $$31, 0" : "=r"(ptr)); + } + ptr } @@ -52,16 +62,41 @@ pub fn backtrace() { let mut current_pc = lr(); let mut current_fp = fp(); let mut stack_num = 0; + + // adjust sp to the top address of backtrace() function + #[cfg(target_arch = "mips")] + { + let func_base = backtrace as *const isize; + let sp_offset = (*func_base << 16) >> 16; + current_fp = ((current_fp as isize) - sp_offset) as usize; + } + + println!("=== BEGIN rCore stack trace ==="); + while current_pc >= stext as usize && current_pc <= etext as usize && current_fp as usize != 0 { - println!( - "#{} {:#018X} fp {:#018X}", - stack_num, - current_pc - size_of::(), - current_fp - ); + // print current backtrace + match size_of::() { + 4 => { + println!( + "#{:02} PC: {:#010X} FP: {:#010X}", + stack_num, + current_pc - size_of::(), + current_fp + ); + } + _ => { + println!( + "#{:02} PC: {:#018X} FP: {:#018X}", + stack_num, + current_pc - size_of::(), + current_fp + ); + } + } + stack_num = stack_num + 1; #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] { @@ -78,6 +113,53 @@ pub fn backtrace() { current_pc = *(current_fp as *const usize).offset(1); } } + #[cfg(target_arch = "mips")] + { + // the prologue of function is always like: + // main+0: 27bd____ addiu sp, sp, -____ + // main+4: afbf____ sw ra, ____(sp) + let mut code_ptr = current_pc as *const isize; + code_ptr = code_ptr.offset(-1); + + // get the stack size of last function + while (*code_ptr as usize >> 16) != 0x27bd { + code_ptr = code_ptr.offset(-1); + } + let sp_offset = (*code_ptr << 16) >> 16; + trace!( + "Found addiu sp @ {:08X}({:08x}) with sp offset {}", + code_ptr as usize, + *code_ptr, + sp_offset + ); + + // get the return address offset of last function + let mut last_fun_found = false; + while (code_ptr as usize) < current_pc { + if (*code_ptr as usize >> 16) == 0xafbf { + last_fun_found = true; + break; + } + code_ptr = code_ptr.offset(1); + } + if last_fun_found { + // unwind stack + let ra_offset = (*code_ptr << 16) >> 16; + trace!( + "Found sw ra @ {:08X}({:08x}) with ra offset {}", + code_ptr as usize, + *code_ptr, + ra_offset + ); + current_pc = *(((current_fp as isize) + ra_offset) as *const usize); + current_fp = ((current_fp as isize) - sp_offset) as usize; + trace!("New PC {:08X} FP {:08X}", current_pc, current_fp); + continue; + } else { + trace!("No sw ra found, probably due to optimizations."); + break; + } + } #[cfg(target_arch = "x86_64")] { // Kernel stack at 0x0000_57ac_0000_0000 (defined in bootloader crate) @@ -91,5 +173,6 @@ pub fn backtrace() { current_pc = *(current_fp as *const usize).offset(1); } } + println!("=== END rCore stack trace ==="); } } diff --git a/kernel/src/drivers/bus/mod.rs b/kernel/src/drivers/bus/mod.rs index 673e3ba..cee2283 100644 --- a/kernel/src/drivers/bus/mod.rs +++ b/kernel/src/drivers/bus/mod.rs @@ -1,3 +1,3 @@ -#[cfg(target_arch = "x86_64")] +#[cfg(any(target_arch = "x86_64", target_arch = "mips"))] pub mod pci; pub mod virtio_mmio; diff --git a/kernel/src/drivers/bus/pci.rs b/kernel/src/drivers/bus/pci.rs index c0de3bc..2744b2b 100644 --- a/kernel/src/drivers/bus/pci.rs +++ b/kernel/src/drivers/bus/pci.rs @@ -9,7 +9,6 @@ use core::cmp::Ordering; use pci::*; use rcore_memory::{paging::PageTable, PAGE_SIZE}; use spin::Mutex; -use x86_64::instructions::port::Port; const PCI_COMMAND: u16 = 0x04; const PCI_CAP_PTR: u16 = 0x34; @@ -25,6 +24,10 @@ const PCI_CAP_ID_MSI: u8 = 0x05; struct PortOpsImpl; +#[cfg(target_arch = "x86_64")] +use x86_64::instructions::port::Port; + +#[cfg(target_arch = "x86_64")] impl PortOps for PortOpsImpl { unsafe fn read8(&self, port: u16) -> u8 { Port::new(port).read() @@ -46,6 +49,33 @@ impl PortOps for PortOpsImpl { } } +#[cfg(target_arch = "mips")] +use crate::util::{read, write}; +#[cfg(feature = "board_malta")] +const PCI_BASE: usize = 0xbbe00000; + +#[cfg(target_arch = "mips")] +impl PortOps for PortOpsImpl { + unsafe fn read8(&self, port: u16) -> u8 { + read(PCI_BASE + port as usize) + } + unsafe fn read16(&self, port: u16) -> u16 { + read(PCI_BASE + port as usize) + } + unsafe fn read32(&self, port: u16) -> u32 { + read(PCI_BASE + port as usize) + } + unsafe fn write8(&self, port: u16, val: u8) { + write(PCI_BASE + port as usize, val); + } + unsafe fn write16(&self, port: u16, val: u16) { + write(PCI_BASE + port as usize, val); + } + unsafe fn write32(&self, port: u16, val: u32) { + write(PCI_BASE + port as usize, val); + } +} + /// Enable the pci device and its interrupt /// Return assigned MSI interrupt number when applicable unsafe fn enable(loc: Location) -> Option { diff --git a/kernel/src/arch/aarch64/driver/console/color.rs b/kernel/src/drivers/console/color.rs similarity index 56% rename from kernel/src/arch/aarch64/driver/console/color.rs rename to kernel/src/drivers/console/color.rs index 419df50..ed543c1 100644 --- a/kernel/src/arch/aarch64/driver/console/color.rs +++ b/kernel/src/drivers/console/color.rs @@ -2,12 +2,10 @@ use crate::util::color::ConsoleColor; -pub trait FramebufferColor { - /// pack as 16-bit integer - fn pack16(&self) -> u16; +use super::ColorConfig; - /// pack as 32-bit integer - fn pack32(&self) -> u32; +pub trait FramebufferColor { + fn pack32(&self, config: ColorConfig) -> u32; } #[repr(C)] @@ -43,28 +41,32 @@ impl From for RgbColor { impl FramebufferColor for RgbColor { #[inline] - fn pack16(&self) -> u16 { - // BGR565 - ((self.0 as u16 & 0xF8) << 8) | ((self.1 as u16 & 0xFC) << 3) | (self.2 as u16 >> 3) - } - - #[inline] - fn pack32(&self) -> u32 { - // BGRA8888 - // FIXME: qemu and low version RPi use RGBA order for 24/32-bit color depth, - // but RPi3 B+ uses BGRA order for 24/32-bit color depth. - ((self.0 as u32) << 16) | ((self.1 as u32) << 8) | (self.2 as u32) + fn pack32(&self, config: ColorConfig) -> u32 { + match config { + ColorConfig::RGB332 => { + (((self.0 >> 5) << 5) | ((self.1 >> 5) << 2) | (self.2 >> 6)) as u32 + } + ColorConfig::RGB565 => { + (((self.0 as u16 & 0xF8) << 8) + | ((self.1 as u16 & 0xFC) << 3) + | (self.2 as u16 >> 3)) as u32 + } + // FIXME: qemu and low version RPi use RGBA order for 24/32-bit color depth, + // but RPi3 B+ uses BGRA order for 24/32-bit color depth. + ColorConfig::BGRA8888 => { + ((self.0 as u32) << 16) | ((self.1 as u32) << 8) | (self.2 as u32) + } + _ => unimplemented!(), + } } } impl FramebufferColor for ConsoleColor { #[inline] - fn pack16(&self) -> u16 { - RgbColor::from(*self).pack16() - } - - #[inline] - fn pack32(&self) -> u32 { - RgbColor::from(*self).pack32() + fn pack32(&self, config: ColorConfig) -> u32 { + match config { + ColorConfig::VgaPalette => *self as u32, + _ => RgbColor::from(*self).pack32(config), + } } } diff --git a/kernel/src/arch/aarch64/driver/console/fonts/font8x16.rs b/kernel/src/drivers/console/fonts/font8x16.rs similarity index 100% rename from kernel/src/arch/aarch64/driver/console/fonts/font8x16.rs rename to kernel/src/drivers/console/fonts/font8x16.rs diff --git a/kernel/src/arch/aarch64/driver/console/fonts/mod.rs b/kernel/src/drivers/console/fonts/mod.rs similarity index 100% rename from kernel/src/arch/aarch64/driver/console/fonts/mod.rs rename to kernel/src/drivers/console/fonts/mod.rs diff --git a/kernel/src/arch/aarch64/driver/console/mod.rs b/kernel/src/drivers/console/mod.rs similarity index 93% rename from kernel/src/arch/aarch64/driver/console/mod.rs rename to kernel/src/drivers/console/mod.rs index b16ae13..853cce0 100644 --- a/kernel/src/arch/aarch64/driver/console/mod.rs +++ b/kernel/src/drivers/console/mod.rs @@ -10,7 +10,7 @@ use spin::Mutex; use crate::util::escape_parser::{CharacterAttribute, EscapeParser}; -use super::fb::{ColorDepth::*, FramebufferInfo, FRAME_BUFFER}; +use super::fb::{ColorConfig, ColorDepth::*, FramebufferInfo, FRAME_BUFFER}; use self::color::FramebufferColor; use self::fonts::{Font, Font8x16}; @@ -62,13 +62,10 @@ impl ConsoleBuffer { let off_x = col * F::WIDTH; let off_y = row * F::HEIGHT; if let Some(fb) = FRAME_BUFFER.lock().as_mut() { - let (mut foreground, mut background) = match fb.color_depth { - ColorDepth16 => ( - ch.attr.foreground.pack16() as u32, - ch.attr.background.pack16() as u32, - ), - ColorDepth32 => (ch.attr.foreground.pack32(), ch.attr.background.pack32()), - }; + let (mut foreground, mut background) = ( + ch.attr.foreground.pack32(fb.color_config), + ch.attr.background.pack32(fb.color_config), + ); if ch.attr.reverse { core::mem::swap(&mut foreground, &mut background); } diff --git a/kernel/src/drivers/device_tree.rs b/kernel/src/drivers/device_tree.rs index c983e17..656b7e2 100644 --- a/kernel/src/drivers/device_tree.rs +++ b/kernel/src/drivers/device_tree.rs @@ -14,6 +14,7 @@ fn walk_dt_node(dt: &Node) { if compatible == "virtio,mmio" { virtio_probe(dt); } + // TODO: initial other devices (16650, etc.) } if let Ok(bootargs) = dt.prop_str("bootargs") { if bootargs.len() > 0 { diff --git a/kernel/src/arch/aarch64/board/raspi3/fb.rs b/kernel/src/drivers/gpu/fb.rs similarity index 76% rename from kernel/src/arch/aarch64/board/raspi3/fb.rs rename to kernel/src/drivers/gpu/fb.rs index ff3b31f..54d7e10 100644 --- a/kernel/src/arch/aarch64/board/raspi3/fb.rs +++ b/kernel/src/drivers/gpu/fb.rs @@ -1,6 +1,5 @@ //! Framebuffer -use super::mailbox; use alloc::string::String; use core::fmt; use lazy_static::lazy_static; @@ -40,14 +39,28 @@ pub struct FramebufferInfo { #[repr(u32)] #[derive(Debug, Clone, Copy)] pub enum ColorDepth { + ColorDepth8 = 8, ColorDepth16 = 16, ColorDepth32 = 32, } use self::ColorDepth::*; +#[repr(u32)] +#[derive(Debug, Clone, Copy)] +pub enum ColorConfig { + RGB332, + RGB565, + BGRA8888, + VgaPalette, +} +use self::ColorConfig::*; + +pub type FramebufferResult = Result<(FramebufferInfo, ColorConfig, usize), String>; + #[repr(C)] union ColorBuffer { base_addr: usize, + buf8: &'static mut [u8], buf16: &'static mut [u16], buf32: &'static mut [u32], } @@ -56,6 +69,9 @@ impl ColorBuffer { fn new(color_depth: ColorDepth, base_addr: usize, size: usize) -> ColorBuffer { unsafe { match color_depth { + ColorDepth8 => ColorBuffer { + buf8: core::slice::from_raw_parts_mut(base_addr as *mut u8, size), + }, ColorDepth16 => ColorBuffer { buf16: core::slice::from_raw_parts_mut(base_addr as *mut u16, size / 2), }, @@ -66,6 +82,11 @@ impl ColorBuffer { } } + #[inline] + fn read8(&self, index: u32) -> u8 { + unsafe { self.buf8[index as usize] } + } + #[inline] fn read16(&self, index: u32) -> u16 { unsafe { self.buf16[index as usize] } @@ -76,6 +97,11 @@ impl ColorBuffer { unsafe { self.buf32[index as usize] } } + #[inline] + fn write8(&mut self, index: u32, pixel: u8) { + unsafe { self.buf8[index as usize] = pixel } + } + #[inline] fn write16(&mut self, index: u32, pixel: u16) { unsafe { self.buf16[index as usize] = pixel } @@ -91,6 +117,7 @@ impl ColorBuffer { pub struct Framebuffer { pub fb_info: FramebufferInfo, pub color_depth: ColorDepth, + pub color_config: ColorConfig, buf: ColorBuffer, } @@ -99,6 +126,7 @@ impl fmt::Debug for Framebuffer { let mut f = f.debug_struct("Framebuffer"); f.field("fb_info", &self.fb_info); f.field("color_depth", &self.color_depth); + f.field("color_config", &self.color_config); f.field("base_addr", unsafe { &self.buf.base_addr }); f.finish() } @@ -108,49 +136,25 @@ impl Framebuffer { fn new(width: u32, height: u32, depth: u32) -> Result { assert_has_not_been_called!("Framebuffer::new must be called only once"); - let (width, height) = if width == 0 || height == 0 { - mailbox::framebuffer_get_physical_size()? - } else { - (width, height) - }; - let depth = if depth == 0 { - mailbox::framebuffer_get_depth()? - } else { - depth - }; - - let info = mailbox::framebuffer_alloc(width, height, depth)?; - let color_depth = match info.depth { - 16 => ColorDepth16, - 32 => ColorDepth32, - _ => Err(format!("unsupported color depth {}", info.depth))?, - }; - - if info.bus_addr == 0 || info.screen_size == 0 { - Err(format!("mailbox call returned an invalid address/size"))?; - } - if info.pitch == 0 || info.pitch != info.xres * info.depth / 8 { - Err(format!( - "mailbox call returned an invalid pitch value {}", - info.pitch - ))?; - } - - use crate::arch::memory; - let paddr = info.bus_addr & !0xC0000000; - let vaddr = memory::ioremap(paddr as usize, info.screen_size as usize, "fb"); - if vaddr == 0 { - Err(format!( - "cannot remap memory range [{:#x?}..{:#x?}]", - paddr, - paddr + info.screen_size - ))?; + let probed_info = super::probe_fb_info(width, height, depth); + + match probed_info { + Ok((info, config, addr)) => { + let color_depth = match info.depth { + 8 => ColorDepth8, + 16 => ColorDepth16, + 32 => ColorDepth32, + _ => Err(format!("unsupported color depth {}", info.depth))?, + }; + Ok(Framebuffer { + buf: ColorBuffer::new(color_depth, addr, info.screen_size as usize), + color_config: config, + color_depth, + fb_info: info, + }) + } + Err(e) => Err(e)?, } - Ok(Framebuffer { - buf: ColorBuffer::new(color_depth, vaddr, info.screen_size as usize), - color_depth, - fb_info: info, - }) } #[inline] @@ -162,6 +166,7 @@ impl Framebuffer { #[inline] pub fn read(&self, x: u32, y: u32) -> u32 { match self.color_depth { + ColorDepth8 => self.buf.read8(y * self.fb_info.xres + x) as u32, ColorDepth16 => self.buf.read16(y * self.fb_info.xres + x) as u32, ColorDepth32 => self.buf.read32(y * self.fb_info.xres + x), } @@ -171,6 +176,7 @@ impl Framebuffer { #[inline] pub fn write(&mut self, x: u32, y: u32, pixel: u32) { match self.color_depth { + ColorDepth8 => self.buf.write8(y * self.fb_info.xres + x, pixel as u8), ColorDepth16 => self.buf.write16(y * self.fb_info.xres + x, pixel as u16), ColorDepth32 => self.buf.write32(y * self.fb_info.xres + x, pixel), } diff --git a/kernel/src/drivers/gpu/qemu_stdvga.rs b/kernel/src/drivers/gpu/qemu_stdvga.rs new file mode 100644 index 0000000..1515f15 --- /dev/null +++ b/kernel/src/drivers/gpu/qemu_stdvga.rs @@ -0,0 +1,153 @@ +//! driver for qemu stdvga (Cirrus) + +use crate::util::{read, write}; + +const VGA_MMIO_OFFSET: usize = 0x400 - 0x3C0; +const VBE_MMIO_OFFSET: usize = 0x500; + +const VGA_AR_ADDR: u16 = 0x3C0; +const VBE_DISPI_INDEX_XRES: u16 = 0x1; +const VBE_DISPI_INDEX_YRES: u16 = 0x2; +const VBE_DISPI_INDEX_BPP: u16 = 0x3; +const VBE_DISPI_INDEX_ENABLE: u16 = 0x4; +const VBE_DISPI_INDEX_BANK: u16 = 0x5; +const VBE_DISPI_INDEX_VIRT_WIDTH: u16 = 0x6; +const VBE_DISPI_INDEX_VIRT_HEIGHT: u16 = 0x7; +const VBE_DISPI_INDEX_X_OFFSET: u16 = 0x8; +const VBE_DISPI_INDEX_Y_OFFSET: u16 = 0x9; +const VBE_DISPI_INDEX_VIDEO_MEMORY_64K: u16 = 0xa; + +const VGA_AR_PAS: u8 = 0x20; +const VBE_DISPI_ENABLED: u16 = 0x01; +const VBE_DISPI_8BIT_DAC: u16 = 0x20; +const VBE_DISPI_LFB_ENABLED: u16 = 0x40; + +const PCI_COMMAND: u8 = 0x04; +const PCI_COMMAND_IO: u32 = 0x1; +const PCI_COMMAND_MEMORY: u32 = 0x2; +const PCI_COMMAND_MASTER: u32 = 0x4; +const PCI_COMMAND_SPECIAL: u32 = 0x8; +const PCI_COMMAND_SERR: u32 = 0x100; + +fn pci_read_config(pci_base: usize, bus: u8, slot: u8, func: u8, offset: u8) -> u32 { + // write config address + let address = (1 << 31) + | ((bus as u32) << 16) + | ((slot as u32) << 11) + | ((func as u32) << 8) + | (offset as u32); + write(pci_base + 0xcf8, address); + // do the actual work + let value = read(pci_base + 0xcfc); + debug!( + "Read {:08x} from PCI address: {:02x}:{:02x}.{:02x} @ 0x{:02x}", + value, bus, slot, func, offset + ); + value +} + +fn pci_write_config(pci_base: usize, bus: u8, slot: u8, func: u8, offset: u8, value: u32) { + // write config address + let address = (1 << 31) + | ((bus as u32) << 16) + | ((slot as u32) << 11) + | ((func as u32) << 8) + | (offset as u32); + debug!( + "Write {:08x} to PCI address: {:02x}:{:02x}.{:02x} @ 0x{:02x}", + value, bus, slot, func, offset + ); + write(pci_base + 0xcf8, address); + // do the actual work + write(pci_base + 0xcfc, value) +} + +pub fn init(pci_base: usize, vga_base: usize, x_res: u16, y_res: u16) { + debug!( + "PCI Controller Base: {:08x}", + pci_read_config(pci_base, 0x00, 0x00, 0x00, 0x20) + ); + + let controller = pci_read_config(pci_base, 0x00, 0x00, 0x00, PCI_COMMAND); + pci_write_config( + pci_base, + 0x00, + 0x00, + 0x00, + PCI_COMMAND, + controller | PCI_COMMAND_MASTER | PCI_COMMAND_IO | PCI_COMMAND_MEMORY | PCI_COMMAND_SERR, + ); + + let pci_vendor = pci_read_config(pci_base, 0x00, 0x12, 0x00, 0x0); + debug!("VGA PCI Device ID: {:08x}", pci_vendor); + + // enable port and MMIO for vga card + pci_write_config( + pci_base, + 0x00, + 0x12, + 0x00, + PCI_COMMAND, + pci_read_config(pci_base, 0x00, 0x12, 0x00, PCI_COMMAND) | PCI_COMMAND_MEMORY, + ); + // bar 0 + pci_write_config(pci_base, 0x00, 0x12, 0x00, 0x10, 0x10000000); + debug!( + "VGA PCI BAR 0: {:08x}", + pci_read_config(pci_base, 0x00, 0x12, 0x00, 0x10) + ); + // bar 2 + pci_write_config(pci_base, 0x00, 0x12, 0x00, 0x18, 0x12050000); + debug!( + "VGA PCI BAR 2: {:08x}", + pci_read_config(pci_base, 0x00, 0x12, 0x00, 0x18) + ); + + // vga operations + let vga_write_io = |offset: u16, value: u8| { + write(vga_base + VGA_MMIO_OFFSET + (offset as usize), value); + }; + + let vga_read_io = |offset: u16| -> u8 { read(vga_base + VGA_MMIO_OFFSET + (offset as usize)) }; + + let vga_write_vbe = |offset: u16, value: u16| { + write(vga_base + VBE_MMIO_OFFSET + (offset as usize) * 2, value); + }; + + let vga_read_vbe = + |offset: u16| -> u16 { read(vga_base + VBE_MMIO_OFFSET + (offset as usize) * 2) }; + + debug!("VGA Endianess: {:x}", read::(vga_base + 0x604)); + + // unblank vga output + vga_write_io(VGA_AR_ADDR, VGA_AR_PAS); + debug!("VGA AR: {}", vga_read_io(VGA_AR_ADDR)); + + vga_write_vbe(VBE_DISPI_INDEX_ENABLE, 0); + + // set resolution and color depth + vga_write_vbe(VBE_DISPI_INDEX_XRES, x_res); + vga_write_vbe(VBE_DISPI_INDEX_YRES, y_res); + vga_write_vbe(VBE_DISPI_INDEX_VIRT_WIDTH, x_res); + vga_write_vbe(VBE_DISPI_INDEX_VIRT_HEIGHT, y_res); + vga_write_vbe(VBE_DISPI_INDEX_BANK, 0); + vga_write_vbe(VBE_DISPI_INDEX_X_OFFSET, 0); + vga_write_vbe(VBE_DISPI_INDEX_Y_OFFSET, 0); + vga_write_vbe(VBE_DISPI_INDEX_BPP, 8); + debug!( + "VGA Resolution: {}*{}@{}bit", + vga_read_vbe(VBE_DISPI_INDEX_XRES), + vga_read_vbe(VBE_DISPI_INDEX_YRES), + vga_read_vbe(VBE_DISPI_INDEX_BPP) + ); + + // enable vbe + let vbe_enable = vga_read_vbe(VBE_DISPI_INDEX_ENABLE); + vga_write_vbe( + VBE_DISPI_INDEX_ENABLE, + vbe_enable | VBE_DISPI_ENABLED | VBE_DISPI_LFB_ENABLED | VBE_DISPI_8BIT_DAC, + ); + debug!("VBE Status: {:04x}", vga_read_vbe(VBE_DISPI_INDEX_ENABLE)); + + info!("QEMU STDVGA driver initialized @ {:x}", vga_base); +} diff --git a/kernel/src/drivers/mod.rs b/kernel/src/drivers/mod.rs index 05735c1..104a567 100644 --- a/kernel/src/drivers/mod.rs +++ b/kernel/src/drivers/mod.rs @@ -115,7 +115,7 @@ lazy_static! { pub static ref SOCKET_ACTIVITY: Condvar = Condvar::new(); } -#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +#[cfg(any(target_arch = "riscv32", target_arch = "riscv64", target_arch = "mips"))] pub fn init(dtb: usize) { device_tree::init(dtb); } diff --git a/kernel/src/drivers/serial/16550_reg.rs b/kernel/src/drivers/serial/16550_reg.rs new file mode 100644 index 0000000..424b839 --- /dev/null +++ b/kernel/src/drivers/serial/16550_reg.rs @@ -0,0 +1,109 @@ +//! 16550 serial adapter driver for malta board + +#![allow(dead_code)] + +use crate::util::{read, write}; +use core::fmt::{Arguments, Result, Write}; +use spin::Mutex; + +pub struct SerialPort { + base: usize, +} + +impl SerialPort { + fn new() -> SerialPort { + SerialPort { base: 0 } + } + + pub fn init(&mut self, base: usize) { + self.base = base; + // Turn off the FIFO + write(self.base + COM_FCR, 0 as u8); + // Set speed; requires DLAB latch + write(self.base + COM_LCR, COM_LCR_DLAB); + write(self.base + COM_DLL, (115200 / 9600) as u8); + write(self.base + COM_DLM, 0 as u8); + + // 8 data bits, 1 stop bit, parity off; turn off DLAB latch + write(self.base + COM_LCR, COM_LCR_WLEN8 & !COM_LCR_DLAB); + + // No modem controls + write(self.base + COM_MCR, 0 as u8); + // Enable rcv interrupts + write(self.base + COM_IER, COM_IER_RDI); + } + + /// non-blocking version of putchar() + pub fn putchar(&mut self, c: u8) { + write(self.base + COM_TX, c); + } + + /// blocking version of getchar() + pub fn getchar(&mut self) -> char { + loop { + if (read::(self.base + COM_LSR) & COM_LSR_DATA) == 0 { + break; + } + } + let c = read::(self.base + COM_RX); + match c { + 255 => '\0', // null + c => c as char, + } + } + + /// non-blocking version of getchar() + pub fn getchar_option(&mut self) -> Option { + match read::(self.base + COM_LSR) & COM_LSR_DATA { + 0 => None, + _ => Some(read::(self.base + COM_RX) as u8 as char), + } + } + + pub fn putfmt(&mut self, fmt: Arguments) { + self.write_fmt(fmt).unwrap(); + } +} + +impl Write for SerialPort { + fn write_str(&mut self, s: &str) -> Result { + for c in s.bytes() { + if c == 127 { + self.putchar(8); + self.putchar(b' '); + self.putchar(8); + } else { + self.putchar(c); + } + } + Ok(()) + } +} + +const COM_RX: usize = 0; // In: Receive buffer (DLAB=0) +const COM_TX: usize = 0; // Out: Transmit buffer (DLAB=0) +const COM_DLL: usize = 0; // Out: Divisor Latch Low (DLAB=1) +const COM_DLM: usize = 1; // Out: Divisor Latch High (DLAB=1) +const COM_IER: usize = 1; // Out: Interrupt Enable Register +const COM_IER_RDI: u8 = 0x01; // Enable receiver data interrupt +const COM_IIR: usize = 2; // In: Interrupt ID Register +const COM_FCR: usize = 2; // Out: FIFO Control Register +const COM_LCR: usize = 3; // Out: Line Control Register +const COM_LCR_DLAB: u8 = 0x80; // Divisor latch access bit +const COM_LCR_WLEN8: u8 = 0x03; // Wordlength: 8 bits +const COM_MCR: usize = 4; // Out: Modem Control Register +const COM_MCR_RTS: u8 = 0x02; // RTS complement +const COM_MCR_DTR: u8 = 0x01; // DTR complement +const COM_MCR_OUT2: u8 = 0x08; // Out2 complement +const COM_LSR: usize = 5; // In: Line Status Register +const COM_LSR_DATA: u8 = 0x01; // Data available +const COM_LSR_TXRDY: u8 = 0x20; // Transmit buffer avail +const COM_LSR_TSRE: u8 = 0x40; // Transmitter off + +lazy_static! { + pub static ref SERIAL_PORT: Mutex = Mutex::new(SerialPort::new()); +} + +pub fn init(base: usize) { + SERIAL_PORT.lock().init(base); +} diff --git a/kernel/src/drivers/serial/simple_uart.rs b/kernel/src/drivers/serial/simple_uart.rs new file mode 100644 index 0000000..9b34494 --- /dev/null +++ b/kernel/src/drivers/serial/simple_uart.rs @@ -0,0 +1,79 @@ +//! naive serial adapter driver for thinpad + +use crate::util::{read, write}; +use core::fmt::{Arguments, Result, Write}; + +#[derive(Debug, Clone, Copy)] +pub struct SerialPort { + base: usize, +} + +const UART_STATUS: usize = 0x0; +const UART_DATA: usize = 0x4; + +const UART_STATUS_CTS: u8 = 0x1; // clear to send signal +const UART_STATUS_DR: u8 = 0x2; // data ready signal + +impl SerialPort { + pub fn init(&mut self, base: usize) { + self.base = base; + } + + /// non-blocking version of putchar() + pub fn putchar(&mut self, c: u8) { + write(self.base + UART_DATA, c); + } + + /// blocking version of getchar() + pub fn getchar(&mut self) -> char { + loop { + if (read::(self.base + UART_STATUS) & UART_STATUS_DR) == 0 { + break; + } + } + let c = read::(self.base + UART_DATA); + match c { + 255 => '\0', // null + c => c as char, + } + } + + /// non-blocking version of getchar() + pub fn getchar_option(&mut self) -> Option { + match read::(self.base + UART_STATUS) & UART_STATUS_DR { + 0 => None, + _ => Some(read::(self.base + UART_DATA) as u8 as char), + } + } + + pub fn putfmt(&mut self, fmt: Arguments) { + self.write_fmt(fmt).unwrap(); + } + + pub fn lock(&self) -> SerialPort { + self.clone() + } + + pub fn force_unlock(&self) {} +} + +impl Write for SerialPort { + fn write_str(&mut self, s: &str) -> Result { + for c in s.bytes() { + if c == 127 { + self.putchar(8); + self.putchar(b' '); + self.putchar(8); + } else { + self.putchar(c); + } + } + Ok(()) + } +} + +pub static SERIAL_PORT: SerialPort = SerialPort { base: 0 }; + +pub fn init(base: usize) { + SERIAL_PORT.lock().init(base); +} diff --git a/kernel/src/drivers/serial/ti_16c550c.rs b/kernel/src/drivers/serial/ti_16c550c.rs new file mode 100644 index 0000000..4f5b51a --- /dev/null +++ b/kernel/src/drivers/serial/ti_16c550c.rs @@ -0,0 +1,95 @@ +//! TI 16c550c serial adapter driver for malta board + +#![allow(dead_code)] + +use crate::util::{read, write}; +use core::fmt::{Arguments, Result, Write}; +use spin::Mutex; + +pub struct SerialPort { + base: usize, +} + +impl SerialPort { + fn new() -> SerialPort { + SerialPort { base: 0 } + } + + pub fn init(&mut self, base: usize) { + self.base = base; + // Turn off the FIFO + // write(self.base + COM_FCR, 0 as u8); + // Set speed; requires DLAB latch + // write(self.base + COM_LCR, COM_LCR_DLAB); + // write(self.base + COM_DLL, (115200 / 9600) as u8); + // write(self.base + COM_DLM, 0 as u8); + + // 8 data bits, 1 stop bit, parity off; turn off DLAB latch + // write(self.base + COM_LCR, COM_LCR_WLEN8 & !COM_LCR_DLAB); + + // No modem controls + // write(self.base + COM_MCR, 0 as u8); + // Enable rcv interrupts + write(self.base + COM_INT_EN, 0x1); + } + + /// non-blocking version of putchar() + pub fn putchar(&mut self, c: u8) { + write(self.base + COM_TX, c); + } + + /// blocking version of getchar() + pub fn getchar(&mut self) -> char { + loop { + if (read::(self.base + COM_LSR) & 0x01) == 0 { + break; + } + } + let c = read::(self.base + COM_RX); + match c { + 255 => '\0', // null + c => c as char, + } + } + + /// non-blocking version of getchar() + pub fn getchar_option(&mut self) -> Option { + match read::(self.base + COM_LSR) & 0x01 { + 0 => None, + _ => Some(read::(self.base + COM_RX) as u8 as char), + } + } + + pub fn putfmt(&mut self, fmt: Arguments) { + self.write_fmt(fmt).unwrap(); + } +} + +impl Write for SerialPort { + fn write_str(&mut self, s: &str) -> Result { + for c in s.bytes() { + if c == 127 { + self.putchar(8); + self.putchar(b' '); + self.putchar(8); + } else { + self.putchar(c); + } + } + Ok(()) + } +} + +const COM_RX: usize = 0x00; // In: Receive buffer (DLAB=0) +const COM_TX: usize = 0x00; // Out: Transmit buffer (DLAB=0) +const COM_INT_EN: usize = 0x08; // In: Interrupt enable +const COM_INT_ID: usize = 0x10; // Out: Interrupt identification +const COM_LSR: usize = 0x28; // In: Line status register + +lazy_static! { + pub static ref SERIAL_PORT: Mutex = Mutex::new(SerialPort::new()); +} + +pub fn init(base: usize) { + SERIAL_PORT.lock().init(base); +} diff --git a/kernel/src/fs/mod.rs b/kernel/src/fs/mod.rs index 9414c47..1b772a9 100644 --- a/kernel/src/fs/mod.rs +++ b/kernel/src/fs/mod.rs @@ -21,7 +21,7 @@ mod stdio; #[cfg(feature = "link_user")] global_asm!(concat!( r#" - .section .data + .section .data.img .global _user_img_start .global _user_img_end _user_img_start: @@ -54,6 +54,7 @@ lazy_static! { fn _user_img_start(); fn _user_img_end(); } + info!("SFS linked to kernel, from {:08x} to {:08x}", _user_img_start as usize, _user_img_end as usize); Arc::new(unsafe { device::MemBuf::new(_user_img_start, _user_img_end) }) }; diff --git a/kernel/src/lib.rs b/kernel/src/lib.rs index 53a9cc2..779b75f 100644 --- a/kernel/src/lib.rs +++ b/kernel/src/lib.rs @@ -42,6 +42,10 @@ mod trap; #[path = "arch/x86_64/mod.rs"] pub mod arch; +#[cfg(target_arch = "mips")] +#[path = "arch/mipsel/mod.rs"] +pub mod arch; + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] #[path = "arch/riscv32/mod.rs"] pub mod arch; diff --git a/kernel/src/memory.rs b/kernel/src/memory.rs index a58efcd..a631437 100644 --- a/kernel/src/memory.rs +++ b/kernel/src/memory.rs @@ -21,7 +21,7 @@ pub type FrameAlloc = bitmap_allocator::BitAlloc16M; pub type FrameAlloc = bitmap_allocator::BitAlloc4K; // Raspberry Pi 3 has 1G memory -#[cfg(target_arch = "aarch64")] +#[cfg(any(target_arch = "aarch64", target_arch = "mips"))] pub type FrameAlloc = bitmap_allocator::BitAlloc1M; lazy_static! { diff --git a/kernel/src/sync/mutex.rs b/kernel/src/sync/mutex.rs index e319fde..8af1460 100644 --- a/kernel/src/sync/mutex.rs +++ b/kernel/src/sync/mutex.rs @@ -216,7 +216,7 @@ impl MutexSupport for Spin { unsafe { #[cfg(target_arch = "x86_64")] asm!("pause" :::: "volatile"); - #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64", target_arch = "mips"))] asm!("nop" :::: "volatile"); #[cfg(target_arch = "aarch64")] asm!("yield" :::: "volatile"); @@ -254,7 +254,7 @@ impl MutexSupport for SpinNoIrq { unsafe { #[cfg(target_arch = "x86_64")] asm!("pause" :::: "volatile"); - #[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] + #[cfg(any(target_arch = "riscv32", target_arch = "riscv64", target_arch = "mips"))] asm!("nop" :::: "volatile"); #[cfg(target_arch = "aarch64")] asm!("yield" :::: "volatile"); diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index dc2ef48..d74e125 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -44,7 +44,7 @@ pub fn syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> isize { debug!("{}:{}:{} syscall id {} begin", cid, pid, tid, id); } - // use syscall numbers in Linux x86_64 + // use platform-specific syscal numbers // See https://filippo.io/linux-syscall-table/ // And https://fedora.juszkiewicz.com.pl/syscalls.html. let ret = match id { diff --git a/kernel/src/util/mod.rs b/kernel/src/util/mod.rs index 582f101..ee7eb60 100644 --- a/kernel/src/util/mod.rs +++ b/kernel/src/util/mod.rs @@ -1,3 +1,5 @@ +use core::ptr::{read_volatile, write_volatile}; + pub mod color; pub mod escape_parser; @@ -68,3 +70,17 @@ macro_rules! enum_with_unknown { } } } + +#[inline(always)] +pub fn write(addr: usize, content: T) { + let cell = (addr) as *mut T; + unsafe { + write_volatile(cell, content); + } +} + +#[inline(always)] +pub fn read(addr: usize) -> T { + let cell = (addr) as *const T; + unsafe { read_volatile(cell) } +} \ No newline at end of file diff --git a/kernel/targets/mipsel.json b/kernel/targets/mipsel.json new file mode 100644 index 0000000..346e3e8 --- /dev/null +++ b/kernel/targets/mipsel.json @@ -0,0 +1,36 @@ +{ + "arch": "mips", + "cpu": "mips32r2", + "llvm-target": "mipsel-unknown-none", + "data-layout": "e-m:m-p:32:32-i8:8:32-i16:16:32-i64:64-n32-S64", + "target-endian": "little", + "target-pointer-width": "32", + "target-c-int-width": "32", + "os": "none", + "features": "+mips32r2,+soft-float", + "max-atomic-width": "32", + "linker": "rust-lld", + "linker-flavor": "ld.lld", + "pre-link-args": { + "ld.lld": [ + "-Tsrc/arch/mipsel/boot/linker.ld" + ] + }, + "executables": true, + "panic-strategy": "abort", + "relocation-model": "static", + "abi-blacklist": [ + "cdecl", + "stdcall", + "fastcall", + "vectorcall", + "thiscall", + "aapcs", + "win64", + "sysv64", + "ptx-kernel", + "msp430-interrupt", + "x86-interrupt" + ], + "eliminate-frame-pointer": false +} diff --git a/user b/user index b09e4e2..aeea2b5 160000 --- a/user +++ b/user @@ -1 +1 @@ -Subproject commit b09e4e24ad1cfe3b6eae2b40231f389070cdb9e2 +Subproject commit aeea2b569efe90e38b39d3c72edb4bc015837d14