From 0b517ad3502c8fd659a94620fbbe489a483afa1f Mon Sep 17 00:00:00 2001 From: "debian@utm" Date: Mon, 4 Nov 2024 09:09:12 -0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E5=A4=8D=E7=8E=B0=E7=AC=AC?= =?UTF-8?q?=E4=B8=80=E7=AB=A0=E5=AE=9E=E9=AA=8C=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- os/Cargo.toml | 1 + os/Makefile | 75 ++++++++++++++++++++++++++++++++++++++++ os/src/boards/qemu.rs | 79 +++++++++++++++++++++++++++++++++++++++++++ os/src/console.rs | 34 +++++++++++++++++++ os/src/lang_items.rs | 16 +++++++-- os/src/logging.rs | 45 ++++++++++++++++++++++++ os/src/main.rs | 39 ++++++++++++++++++++- os/src/sbi.rs | 8 +++++ 8 files changed, 294 insertions(+), 3 deletions(-) create mode 100644 os/Makefile create mode 100644 os/src/boards/qemu.rs create mode 100644 os/src/console.rs create mode 100644 os/src/logging.rs diff --git a/os/Cargo.toml b/os/Cargo.toml index aad338f4..0e0ef800 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2021" [dependencies] +log = "0.4" \ No newline at end of file diff --git a/os/Makefile b/os/Makefile new file mode 100644 index 00000000..9b541863 --- /dev/null +++ b/os/Makefile @@ -0,0 +1,75 @@ +# Building +TARGET := riscv64gc-unknown-none-elf +MODE := release +KERNEL_ELF := target/$(TARGET)/$(MODE)/os +KERNEL_BIN := $(KERNEL_ELF).bin +DISASM_TMP := target/$(TARGET)/$(MODE)/asm + +# Building mode argument +ifeq ($(MODE), release) + MODE_ARG := --release +endif + +# BOARD +BOARD := qemu +SBI ?= rustsbi +BOOTLOADER := ../bootloader/$(SBI)-$(BOARD).bin + +# KERNEL ENTRY +KERNEL_ENTRY_PA := 0x80200000 + +# Binutils +OBJDUMP := rust-objdump --arch-name=riscv64 +OBJCOPY := rust-objcopy --binary-architecture=riscv64 + +# Disassembly +DISASM ?= -x + +build: env $(KERNEL_BIN) + +env: + (rustup target list | grep "riscv64gc-unknown-none-elf (installed)") || rustup target add $(TARGET) + cargo install cargo-binutils + rustup component add rust-src + rustup component add llvm-tools-preview + +$(KERNEL_BIN): kernel + @$(OBJCOPY) $(KERNEL_ELF) --strip-all -O binary $@ + +kernel: + @echo Platform: $(BOARD) + @cargo build $(MODE_ARG) + +clean: + @cargo clean + +disasm: kernel + @$(OBJDUMP) $(DISASM) $(KERNEL_ELF) | less + +disasm-vim: kernel + @$(OBJDUMP) $(DISASM) $(KERNEL_ELF) > $(DISASM_TMP) + @vim $(DISASM_TMP) + @rm $(DISASM_TMP) + +run: run-inner + +run-inner: build + @qemu-system-riscv64 \ + -machine virt \ + -nographic \ + -bios $(BOOTLOADER) \ + -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) + +debug: build + @tmux new-session -d \ + "qemu-system-riscv64 -machine virt -nographic -bios $(BOOTLOADER) -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) -s -S" && \ + tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \ + tmux -2 attach-session -d + +gdbserver: build + @qemu-system-riscv64 -machine virt -nographic -bios $(BOOTLOADER) -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY_PA) -s -S + +gdbclient: + @riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234' + +.PHONY: build env kernel clean disasm disasm-vim run-inner gdbserver gdbclient \ No newline at end of file diff --git a/os/src/boards/qemu.rs b/os/src/boards/qemu.rs new file mode 100644 index 00000000..08a4a8fd --- /dev/null +++ b/os/src/boards/qemu.rs @@ -0,0 +1,79 @@ +//ref:: https://github.com/andre-richter/qemu-exit +use core::arch::asm; + +const EXIT_SUCCESS: u32 = 0x5555; // Equals `exit(0)`. qemu successful exit + +const EXIT_FAILURE_FLAG: u32 = 0x3333; +const EXIT_FAILURE: u32 = exit_code_encode(1); // Equals `exit(1)`. qemu failed exit +const EXIT_RESET: u32 = 0x7777; // qemu reset + +pub trait QEMUExit { + /// Exit with specified return code. + /// + /// Note: For `X86`, code is binary-OR'ed with `0x1` inside QEMU. + fn exit(&self, code: u32) -> !; + + /// Exit QEMU using `EXIT_SUCCESS`, aka `0`, if possible. + /// + /// Note: Not possible for `X86`. + fn exit_success(&self) -> !; + + /// Exit QEMU using `EXIT_FAILURE`, aka `1`. + fn exit_failure(&self) -> !; +} + +/// RISCV64 configuration +pub struct RISCV64 { + /// Address of the sifive_test mapped device. + addr: u64, +} + +/// Encode the exit code using EXIT_FAILURE_FLAG. +const fn exit_code_encode(code: u32) -> u32 { + (code << 16) | EXIT_FAILURE_FLAG +} + +impl RISCV64 { + /// Create an instance. + pub const fn new(addr: u64) -> Self { + RISCV64 { addr } + } +} + +impl QEMUExit for RISCV64 { + /// Exit qemu with specified exit code. + fn exit(&self, code: u32) -> ! { + // If code is not a special value, we need to encode it with EXIT_FAILURE_FLAG. + let code_new = match code { + EXIT_SUCCESS | EXIT_FAILURE | EXIT_RESET => code, + _ => exit_code_encode(code), + }; + + unsafe { + asm!( + "sw {0}, 0({1})", + in(reg)code_new, in(reg)self.addr + ); + + // For the case that the QEMU exit attempt did not work, transition into an infinite + // loop. Calling `panic!()` here is unfeasible, since there is a good chance + // this function here is the last expression in the `panic!()` handler + // itself. This prevents a possible infinite loop. + loop { + asm!("wfi", options(nomem, nostack)); + } + } + } + + fn exit_success(&self) -> ! { + self.exit(EXIT_SUCCESS); + } + + fn exit_failure(&self) -> ! { + self.exit(EXIT_FAILURE); + } +} + +const VIRT_TEST: u64 = 0x100000; + +pub const QEMU_EXIT_HANDLE: RISCV64 = RISCV64::new(VIRT_TEST); \ No newline at end of file diff --git a/os/src/console.rs b/os/src/console.rs new file mode 100644 index 00000000..f7d13ac8 --- /dev/null +++ b/os/src/console.rs @@ -0,0 +1,34 @@ +//! SBI console driver, for text output +use crate::sbi::console_putchar; +use core::fmt::{self, Write}; + +struct Stdout; + +impl Write for Stdout { + fn write_str(&mut self, s: &str) -> fmt::Result { + for c in s.chars() { + console_putchar(c as usize); + } + Ok(()) + } +} + +pub fn print(args: fmt::Arguments) { + Stdout.write_fmt(args).unwrap(); +} + +/// Print! to the host console using the format string and arguments. +#[macro_export] +macro_rules! print { + ($fmt: literal $(, $($arg: tt)+)?) => { + $crate::console::print(format_args!($fmt $(, $($arg)+)?)) + } +} + +/// Println! to the host console using the format string and arguments. +#[macro_export] +macro_rules! println { + ($fmt: literal $(, $($arg: tt)+)?) => { + $crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?)) + } +} diff --git a/os/src/lang_items.rs b/os/src/lang_items.rs index c03b9538..d4b09322 100644 --- a/os/src/lang_items.rs +++ b/os/src/lang_items.rs @@ -1,6 +1,18 @@ use core::panic::PanicInfo; +use crate::sbi::shutdown; #[panic_handler] -fn panic(_info: &PanicInfo) -> ! { - loop {} +/// panic handler +fn panic(info: &PanicInfo) -> ! { + if let Some(location) = info.location() { + println!( + "[kernel] Panicked at {}:{} {}", + location.file(), + location.line(), + info.message().unwrap() + ); + } else { + println!("[kernel] Panicked: {}", info.message().unwrap()); + } + shutdown() } diff --git a/os/src/logging.rs b/os/src/logging.rs new file mode 100644 index 00000000..e9cc3554 --- /dev/null +++ b/os/src/logging.rs @@ -0,0 +1,45 @@ +//! Global logger + +use log::{Level, LevelFilter, Log, Metadata, Record}; + +/// a simple logger +struct SimpleLogger; + +impl Log for SimpleLogger { + fn enabled(&self, _metadata: &Metadata) -> bool { + true + } + fn log(&self, record: &Record) { + if !self.enabled(record.metadata()) { + return; + } + let color = match record.level() { + Level::Error => 31, // Red + Level::Warn => 93, // BrightYellow + Level::Info => 34, // Blue + Level::Debug => 32, // Green + Level::Trace => 90, // BrightBlack + }; + println!( + "\u{1B}[{}m[{:>5}] {}\u{1B}[0m", + color, + record.level(), + record.args(), + ); + } + fn flush(&self) {} +} + +/// initiate logger +pub fn init() { + static LOGGER: SimpleLogger = SimpleLogger; + log::set_logger(&LOGGER).unwrap(); + log::set_max_level(match option_env!("LOG") { + Some("ERROR") => LevelFilter::Error, + Some("WARN") => LevelFilter::Warn, + Some("INFO") => LevelFilter::Info, + Some("DEBUG") => LevelFilter::Debug, + Some("TRACE") => LevelFilter::Trace, + _ => LevelFilter::Off, + }); +} \ No newline at end of file diff --git a/os/src/main.rs b/os/src/main.rs index c9ff6ba4..fac3ef5c 100644 --- a/os/src/main.rs +++ b/os/src/main.rs @@ -1,12 +1,17 @@ #![no_std] #![no_main] - +#![feature(panic_info_message)] // use core::fmt; // use core::fmt::Write; +#[macro_use] +mod console; mod lang_items; +mod logging; +use log::*; core::arch::global_asm!(include_str!("entry.asm")); mod sbi; use sbi::shutdown; + fn clear_bss(){ extern "C"{ fn sbss(); @@ -18,7 +23,39 @@ fn clear_bss(){ } #[no_mangle] pub fn rust_main()->!{ + extern "C" { + fn stext(); // begin addr of text segment + fn etext(); // end addr of text segment + fn srodata(); // start addr of Read-Only data segment + fn erodata(); // end addr of Read-Only data ssegment + fn sdata(); // start addr of data segment + fn edata(); // end addr of data segment + fn sbss(); // start addr of BSS segment + fn ebss(); // end addr of BSS segment + fn boot_stack(); // stack lower bound + fn boot_stack_top(); // stack top + } clear_bss(); + logging::init(); + println!("[kernel] Hello, world!"); + trace!( + "[kernel] .text [{:#x}, {:#x})", + stext as usize, + etext as usize + ); + debug!( + "[kernel] .rodata [{:#x}, {:#x})", + srodata as usize, erodata as usize + ); + info!( + "[kernel] .data [{:#x}, {:#x})", + sdata as usize, edata as usize + ); + warn!( + "[kernel] boot_stack top=bottom={:#x}, lower_bound={:#x}", + boot_stack_top as usize, boot_stack as usize + ); + error!("[kernel] .bss [{:#x}, {:#x})", sbss as usize, ebss as usize); shutdown(); } // // println!("Hello, world!"); diff --git a/os/src/sbi.rs b/os/src/sbi.rs index 7b7eb62f..903b756e 100644 --- a/os/src/sbi.rs +++ b/os/src/sbi.rs @@ -2,6 +2,7 @@ use core::arch::asm; +const SBI_CONSOLE_PUTCHAR: usize = 1; /// general sbi call #[inline(always)] @@ -20,9 +21,16 @@ fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize { ret } +/// use sbi call to putchar in console (qemu uart handler) +pub fn console_putchar(c: usize) { + sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0); +} + const SBI_SHUTDOWN:usize=8; +/// use sbi call to shutdown the kernel pub fn shutdown() -> ! { sbi_call(SBI_SHUTDOWN,0,0,0); panic!("It should shutdown!"); + } \ No newline at end of file