From 4736cfcb4e641de832a1bb67d25625a049f542f6 Mon Sep 17 00:00:00 2001 From: Paul Pan Date: Wed, 6 Sep 2023 22:51:24 +0800 Subject: [PATCH] init --- .cargo/config.toml | 2 + .gitignore | 1 + Cargo.lock | 91 ++++++++++++++++++++++++++++ Cargo.toml | 26 ++++++++ build.rs | 23 +++++++ rust-toolchain.toml | 2 + src/arch/io.rs | 72 ++++++++++++++++++++++ src/arch/lowlevel.rs | 18 ++++++ src/arch/mod.rs | 10 +++ src/arch/riscv/board/virt/mod.rs | 3 + src/arch/riscv/board/virt/printer.rs | 17 ++++++ src/arch/riscv/boot/linker64.ld | 52 ++++++++++++++++ src/arch/riscv/entry.rs | 43 +++++++++++++ src/arch/riscv/io.rs | 10 +++ src/arch/riscv/lowlevel.rs | 37 +++++++++++ src/arch/riscv/mod.rs | 7 +++ src/entry.rs | 30 +++++++++ src/lang.rs | 21 +++++++ src/lib.rs | 17 ++++++ src/logging.rs | 31 ++++++++++ src/main.rs | 6 ++ 21 files changed, 519 insertions(+) create mode 100644 .cargo/config.toml create mode 100644 .gitignore create mode 100644 Cargo.lock create mode 100644 Cargo.toml create mode 100644 build.rs create mode 100644 rust-toolchain.toml create mode 100644 src/arch/io.rs create mode 100644 src/arch/lowlevel.rs create mode 100644 src/arch/mod.rs create mode 100644 src/arch/riscv/board/virt/mod.rs create mode 100644 src/arch/riscv/board/virt/printer.rs create mode 100644 src/arch/riscv/boot/linker64.ld create mode 100644 src/arch/riscv/entry.rs create mode 100644 src/arch/riscv/io.rs create mode 100644 src/arch/riscv/lowlevel.rs create mode 100644 src/arch/riscv/mod.rs create mode 100644 src/entry.rs create mode 100644 src/lang.rs create mode 100644 src/lib.rs create mode 100644 src/logging.rs create mode 100644 src/main.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..86e09f1 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "riscv64imac-unknown-none-elf" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f00a32b --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,91 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "bit_field" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "log" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" + +[[package]] +name = "raw-cpuid" +version = "10.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c297679cb867470fa8c9f67dbba74a78d78e3e98d7cf2b08d6d71540f797332" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustversion" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" + +[[package]] +name = "sbi-rt" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c113c53291db8ac141e01f43224ed488b8d6001ab66737b82e04695a43a42b7" +dependencies = [ + "sbi-spec", +] + +[[package]] +name = "sbi-spec" +version = "0.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d4027cf9bb591a9fd0fc0e283be6165c5abe96cb73e9f0e24738c227f425377" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "tiny_os" +version = "0.1.0" +dependencies = [ + "log", + "sbi-rt", + "uart_16550", +] + +[[package]] +name = "uart_16550" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dc00444796f6c71f47c85397a35e9c4dbf9901902ac02386940d178e2b78687" +dependencies = [ + "bitflags", + "rustversion", + "x86", +] + +[[package]] +name = "x86" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2781db97787217ad2a2845c396a5efe286f87467a5810836db6d74926e94a385" +dependencies = [ + "bit_field", + "bitflags", + "raw-cpuid", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..1cad101 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "tiny_os" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +default = ["arch_riscv64", "board_virt"] +arch_riscv64 = [] +arch_arm = [] +arch_x86 = [] +board_default = [] +board_virt = [] +board_thead = [] + +[profile.dev] +panic = "abort" + +[profile.release] +panic = "abort" + +[dependencies] +sbi-rt = { version = "0.0.2", features = ["legacy"] } +uart_16550 = "0.3" +log = "0.4" diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..85e7a04 --- /dev/null +++ b/build.rs @@ -0,0 +1,23 @@ +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + + struct TargetConfiguration { + target: &'static str, + lds: &'static str, + } + + const TARGET_CONFIGURATIONS: &[TargetConfiguration] = &[TargetConfiguration { + target: "riscv64", + lds: "src/arch/riscv/boot/linker64.ld", + }]; + + let target = std::env::var("TARGET").unwrap(); + for cfg in TARGET_CONFIGURATIONS { + if target.starts_with(cfg.target) { + println!("cargo:rustc-link-arg=-T{}", cfg.lds); + return; + } + } + + panic!("Unsupported target: {}", target); +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/src/arch/io.rs b/src/arch/io.rs new file mode 100644 index 0000000..caa84a5 --- /dev/null +++ b/src/arch/io.rs @@ -0,0 +1,72 @@ +use core::fmt::Write; + +pub struct RawConsole; + +pub trait Printer: Write { + fn put_char(c: char); + + #[inline] + fn put_str(s: &str) { + for c in s.chars() { + Self::put_char(c); + } + } + + #[inline] + fn put_line(s: &str) { + Self::put_str(s); + Self::put_char('\n'); + } + + fn put_num(mut num: usize) { + if num == 0 { + Self::put_char('0'); + return; + } + + let digits = "0123456789".as_bytes(); + let mut buf = [0u8; 10]; + let mut i = 0; + + while num != 0 { + buf[i] = digits[num % 10]; + num /= 10; + i += 1; + } + + buf[..i] + .iter() + .rev() + .for_each(|c| Self::put_char(*c as char)); + } + + fn put_hex(mut num: usize) { + if num == 0 { + Self::put_str("0x0"); + return; + } + + let alphabet = "0123456789abcdef".as_bytes(); + let mut buf = [0u8; 8]; + let mut i = 0; + + while num != 0 { + buf[i] = alphabet[num % 16]; + num /= 16; + i += 1; + } + + Self::put_str("0x"); + buf[..i] + .iter() + .rev() + .for_each(|c| Self::put_char(*c as char)); + } +} + +impl Write for RawConsole { + fn write_str(&mut self, s: &str) -> core::fmt::Result { + Self::put_str(s); + Ok(()) + } +} diff --git a/src/arch/lowlevel.rs b/src/arch/lowlevel.rs new file mode 100644 index 0000000..37c233e --- /dev/null +++ b/src/arch/lowlevel.rs @@ -0,0 +1,18 @@ +pub struct Hardware; + +pub trait LowLevel { + fn halt() {} + + fn shutdown(_failure: bool) -> ! { + Self::halt(); + + #[allow(clippy::empty_loop)] + loop {} + } + + fn reset(_failure: bool) -> ! { + panic!("Reset is not implemented for this architecture"); + } + + fn disable_interrupt() {} +} diff --git a/src/arch/mod.rs b/src/arch/mod.rs new file mode 100644 index 0000000..2ad9760 --- /dev/null +++ b/src/arch/mod.rs @@ -0,0 +1,10 @@ +// Basic IO +pub mod io; + +// Low Level +pub mod lowlevel; + +// Arch Level +#[cfg(feature = "arch_riscv64")] +#[path = "riscv/mod.rs"] +mod riscv; diff --git a/src/arch/riscv/board/virt/mod.rs b/src/arch/riscv/board/virt/mod.rs new file mode 100644 index 0000000..545cbe0 --- /dev/null +++ b/src/arch/riscv/board/virt/mod.rs @@ -0,0 +1,3 @@ +pub const UART0_BASE: usize = 0x1000_0000; + +pub mod printer; diff --git a/src/arch/riscv/board/virt/printer.rs b/src/arch/riscv/board/virt/printer.rs new file mode 100644 index 0000000..8b98120 --- /dev/null +++ b/src/arch/riscv/board/virt/printer.rs @@ -0,0 +1,17 @@ +use crate::arch::io::{Printer, RawConsole}; + +impl Printer for RawConsole { + #[inline] + fn put_char(c: char) { + let uart = super::UART0_BASE as *mut char; + unsafe { uart.write_volatile(c) } + } + + #[inline] + fn put_str(s: &str) { + let uart = super::UART0_BASE as *mut char; + for c in s.chars() { + unsafe { uart.write_volatile(c) } + } + } +} diff --git a/src/arch/riscv/boot/linker64.ld b/src/arch/riscv/boot/linker64.ld new file mode 100644 index 0000000..1c24a78 --- /dev/null +++ b/src/arch/riscv/boot/linker64.ld @@ -0,0 +1,52 @@ +OUTPUT_ARCH(riscv) +ENTRY(_start) + +BASE_ADDRESS = 0x80200000; + +MEMORY { + DRAM : ORIGIN = BASE_ADDRESS, LENGTH = 16M +} + +SECTIONS { + . = BASE_ADDRESS; + + .text : { + __text_start = .; + *(.text.entry) + *(.text .text.*) + __text_end = .; + } > DRAM + + .rodata : { + . = ALIGN(8); + __rodata_start = .; + *(.rodata .rodata.*) + *(.srodata .srodata.*) + __rodata_end = .; + } > DRAM + + .data : { + . = ALIGN(8); + __data_start = .; + *(.data .data.*) + *(.sdata .sdata.*) + __data_end = .; + } > DRAM + + .stack : { + . = ALIGN(8); + *(.boot_stack) + } > DRAM + + .bss : { + . = ALIGN(8); + __bss_start = .; + *(.bss .bss.*) + *(.sbss .sbss.*) + __bss_end = .; + } > DRAM + + /DISCARD/ : { + *(.eh_frame) + } +} diff --git a/src/arch/riscv/entry.rs b/src/arch/riscv/entry.rs new file mode 100644 index 0000000..854bb68 --- /dev/null +++ b/src/arch/riscv/entry.rs @@ -0,0 +1,43 @@ +#[naked] +#[no_mangle] +#[link_section = ".text.entry"] +unsafe extern "C" fn _start(hart_id: usize, device_tree_addr: usize) -> ! { + // simplest starter + // no stack here + + const STACK_SIZE: usize = 4096 * 16; + #[link_section = ".boot_stack"] + static mut STACK: [u8; STACK_SIZE] = [0u8; STACK_SIZE]; + + core::arch::asm!( + "la sp, {stack} + {stack_size}", + "j {main}", + stack_size = const STACK_SIZE, + stack = sym STACK, + main = sym pre_main, + options(noreturn), + ) +} + +extern "C" fn pre_main(hart_id: usize, device_tree_addr: usize) -> ! { + unsafe { zero_bss() } + + // TODO: initialize page table + + crate::entry::rust_main(hart_id, device_tree_addr); +} + +extern "C" { + static mut __bss_start: u8; + static mut __bss_end: u8; +} + +unsafe fn zero_bss() { + let mut cur = &mut __bss_start as *mut u8; + let end = &mut __bss_end as *mut u8; + + while cur < end { + cur.write_volatile(0); + cur = cur.add(1); + } +} diff --git a/src/arch/riscv/io.rs b/src/arch/riscv/io.rs new file mode 100644 index 0000000..41f1697 --- /dev/null +++ b/src/arch/riscv/io.rs @@ -0,0 +1,10 @@ +#[allow(unused_imports)] +use crate::arch::io::{Printer, RawConsole}; + +#[cfg(feature = "board_default")] +impl Printer for RawConsole { + fn put_char(c: char) { + #[allow(deprecated)] + sbi_rt::legacy::console_putchar(c as usize); + } +} diff --git a/src/arch/riscv/lowlevel.rs b/src/arch/riscv/lowlevel.rs new file mode 100644 index 0000000..2f7b90a --- /dev/null +++ b/src/arch/riscv/lowlevel.rs @@ -0,0 +1,37 @@ +use crate::arch::lowlevel::{Hardware, LowLevel}; +use log::error; + +impl LowLevel for Hardware { + #[inline] + fn halt() { + unsafe { core::arch::asm!("wfi") } + } + + #[inline] + fn shutdown(failure: bool) -> ! { + Self::disable_interrupt(); + + if failure { + sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::SystemFailure); + } else { + sbi_rt::system_reset(sbi_rt::Shutdown, sbi_rt::NoReason); + } + + loop { + // in case system_reset failed + Self::halt() + } + } + + #[inline] + fn reset(failure: bool) -> ! { + if failure { + sbi_rt::system_reset(sbi_rt::ColdReboot, sbi_rt::SystemFailure); + } else { + sbi_rt::system_reset(sbi_rt::WarmReboot, sbi_rt::NoReason); + } + + error!("[riscv/lowlevel] system_reset failed, shutdown instead"); + Self::shutdown(true); + } +} diff --git a/src/arch/riscv/mod.rs b/src/arch/riscv/mod.rs new file mode 100644 index 0000000..ed61d40 --- /dev/null +++ b/src/arch/riscv/mod.rs @@ -0,0 +1,7 @@ +#[cfg(feature = "board_virt")] +#[path = "board/virt/mod.rs"] +mod board; + +pub mod entry; +pub mod io; +pub mod lowlevel; diff --git a/src/entry.rs b/src/entry.rs new file mode 100644 index 0000000..6f2db9d --- /dev/null +++ b/src/entry.rs @@ -0,0 +1,30 @@ +use crate::arch::lowlevel::{Hardware, LowLevel}; +use log::{error, info}; + +pub extern "C" fn rust_main(hart_id: usize, device_tree_addr: usize) -> ! { + crate::logging::init(); + + info!("[rust_main] Kernel Started"); + + info!("Hello World!"); + info!("hart_id = {}", hart_id); + info!("device_tree_addr = {:#x}", device_tree_addr); + + // TODO: setup and start scheduler + + error!("[rust_main] Should not reach here! Maybe scheduler is not working?"); + Hardware::shutdown(true); +} + +pub fn print_hello() { + const SERIAL_PORT_BASE_ADDRESS: usize = 0x1000_0000; + const HELLO: &[u8] = b"Hello World!\n"; + + use uart_16550::MmioSerialPort; + let mut serial_port = unsafe { MmioSerialPort::new(SERIAL_PORT_BASE_ADDRESS) }; + serial_port.init(); + + for &byte in HELLO { + serial_port.send(byte); + } +} diff --git a/src/lang.rs b/src/lang.rs new file mode 100644 index 0000000..d37b724 --- /dev/null +++ b/src/lang.rs @@ -0,0 +1,21 @@ +use crate::arch::lowlevel::{Hardware, LowLevel}; +use core::panic::PanicInfo; +use log::error; + +#[panic_handler] +fn panic(info: &PanicInfo) -> ! { + error!("[lang] Kernel panic!"); + + if let Some(location) = info.location() { + error!( + "[lang] Panicked: [{}:{}] {}", + location.file(), + location.line(), + info.message().unwrap(), + ); + } else { + error!("[lang] Panicked: {}", info.message().unwrap()); + } + + Hardware::shutdown(true); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..72ce742 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,17 @@ +#![no_std] +#![feature(asm_const)] +#![feature(naked_functions)] +#![feature(panic_info_message)] +#![feature(fmt_internals)] + +// arch +pub mod arch; + +// rust language runtime +pub mod lang; + +// entrypoint +pub mod entry; + +// logging +pub mod logging; diff --git a/src/logging.rs b/src/logging.rs new file mode 100644 index 0000000..2e7a82c --- /dev/null +++ b/src/logging.rs @@ -0,0 +1,31 @@ +use crate::arch::io::RawConsole; +use core::fmt::Write; +use log::{self, LevelFilter, Log, Metadata, Record}; + +struct SimpleLogger; + +impl Log for SimpleLogger { + fn enabled(&self, _metadata: &Metadata) -> bool { + true + } + + fn log(&self, record: &Record) { + if !self.enabled(record.metadata()) { + return; + } + + RawConsole + .write_fmt(format_args!("[{}] {}\n", record.level(), record.args())) + .unwrap(); + } + + fn flush(&self) {} +} + +pub fn init() { + static LOGGER: SimpleLogger = SimpleLogger; + log::set_logger(&LOGGER).unwrap(); + + // TODO: get log level from boot env + log::set_max_level(LevelFilter::Trace); +} diff --git a/src/main.rs b/src/main.rs new file mode 100644 index 0000000..7a84ed8 --- /dev/null +++ b/src/main.rs @@ -0,0 +1,6 @@ +#![no_std] +#![no_main] + +#[allow(unused_imports)] +#[allow(clippy::single_component_path_imports)] +use tiny_os;