OccupyMars2025 / rCore-Tutorial-v3

Let's write an OS which can run on RISC-V in Rust from scratch!
https://rcore-os.github.io/rCore-Tutorial-Book-v3/index.html
GNU General Public License v3.0
0 stars 0 forks source link

branch ch1 (record my learning process) 2023/6/29 15:00 - 7/3 21:24 #11

Open OccupyMars2025 opened 1 year ago

OccupyMars2025 commented 1 year ago

https://doc.rust-lang.org/cargo/guide/cargo-home.html

You can alter the location of the Cargo home by setting the CARGO_HOME environmental variable. The home crate provides an API for getting this location if you need this information inside your Rust crate. By default, the Cargo home is located in $HOME/.cargo/

chapter 1
        println!(
            "\u{1B}[{}m 0x{:x} (os/src/mm/address.rs impl From<VirtAddr> for VirtPageNum)\u{1B}[0m",
            43,
            v.0
        );
useful colors:  43, 44,  31..38 ,  90..98

Our implemented  "println" seems to be able to execute :  println!("{}", x),   
but cannot execute: println!("{x}")

https://doc.rust-lang.org/cargo/reference/config.html#hierarchical-structure
https://docs.rust-embedded.org/embedonomicon/smallest-no-std.html
https://doc.rust-lang.org/unstable-book/language-features/lang-items.html
https://www.qemu.org/docs/master/system/riscv/virt.html
https://os.phil-opp.com/freestanding-rust-binary/
http://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/3first-instruction-in-kernel1.html#id5
Calling Convention
https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf
Cornell 大学的 CS 3410: Computer System Organization and Programming 课件内容
http://www.cs.cornell.edu/courses/cs3410/2019sp/schedule/slides/10-calling-notes-bw.pdf
https://www.cs.cornell.edu/courses/cs3410/2019sp/schedule/slides/10-calling-notes.pdf
RISC-V 架构上的 C 语言调用规范
http://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/5support-func-call.html#id8
Qemu 模拟器的启动流程
在本书中,我们使用软件 qemu-system-riscv64 来模拟一台 64 位 RISC-V 架构的计算机,它包含CPU 、物理内存以及若干 I/O 外设。它的具体配置(比如 CPU 的核数或是物理内存的大小)均可由用户通过Qemu的执行参数选项来调整。
$tree os
$strace target/debug/os
$ rustc --version --verbose
$ rustc --print target-list | grep riscv
(how to use : riscv64gc-unknown-linux-gnu)
$ rustup target add riscv64gc-unknown-none-elf
cargo install cargo-binutils
rustup component add llvm-tools-preview
file target/riscv64gc-unknown-none-elf/debug/os
rust-readobj -h target/riscv64gc-unknown-none-elf/debug/os
rust-objdump -S target/riscv64gc-unknown-none-elf/debug/os

qemu-system-riscv64 -machine help
qemu-system-riscv64 -device help | less
qemu-system-riscv64 -device loader,help | less

stat bootloader/rustsbi-qemu.bin

cargo build --release
rust-objcopy --strip-all target/riscv64gc-unknown-none-elf/release/os -O binary target/riscv64gc-unknown-none-elf/release/os.bin

先把两个文件加载到 Qemu 的物理内存中:即作把作为 bootloader 的 rustsbi-qemu.bin 加载到物理内存以物理地址 0x80000000 开头的区域上,同时把内核镜像 os.bin 加载到以物理地址 0x80200000 开头的区域上
我们选用的 RustSBI 则是将下一阶段的入口地址预先约定为固定的 0x80200000 ,在 RustSBI 的初始化工作完成之后,它会跳转到该地址并将计算机控制权移交给下一阶段的软件——也即我们的内核镜像。
已初始化数据段保存程序中那些已初始化的全局数据,分为 .rodata 和 .data 两部分.  
未初始化数据段 .bss 保存程序中那些未初始化的全局数据,通常由程序的加载者代为进行零初始化,即将这块区域逐字节清零

Method to compile source code into RISC-V assembly language and run it on x86-64 machine

$ riscv64-unknown-elf-gcc  print_stack_trace_fp_chain.c  -o print_stack_trace_fp_chain -fno-omit-frame-pointer
$ qemu-riscv64 ./print_stack_trace_fp_chain

colorful logging

echo -e "\x1b[31mhello world\x1b[0m"

ESC ascii value is 27 = 0x1b

http://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/7exercise.html#log
https://en.wikipedia.org/wiki/ANSI_escape_code#Colors

reset all attributes with ESC[0m


the SGR parameters 30–37 selected the foreground color, while 40–47 selected the background
Later terminals added the ability to directly specify the "bright" colors with 90–97 and 100–107.  

ESC[38;5;⟨n⟩m Select foreground color      where n is a number from the table below
ESC[48;5;⟨n⟩m Select background color
  0-  7:  standard colors (as in ESC [ 30–37 m)
  8- 15:  high intensity colors (as in ESC [ 90–97 m)
 16-231:  6 × 6 × 6 cube (216 colors): 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
232-255:  grayscale from dark to light in 24 steps   

ESC[38;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB foreground color
ESC[48;2;⟨r⟩;⟨g⟩;⟨b⟩ m Select RGB background color
image

请用相关工具软件分析并给出应用程序A的代码段/数据段/堆/栈的地址空间范围。

    简便起见,我们静态编译该程序生成可执行文件。使用 readelf 工具查看地址空间:

    数据段(.data)和代码段(.text)的起止地址可以从输出信息中看出。
首先在程序最后一行加上无限循环的 for 语句,使其能一直保持运行,方便我们查看:for (;;);
    应用程序的堆栈是由内核为其动态分配的,需要在运行时查看。将 A 程序置于后台执行,通过查看 /proc/[pid]/maps 得到堆栈空间的分布
https://blog.csdn.net/skywt_cn/article/details/130070422
gcc  -static   ./list_dir.c   -o   list_dir_static
readelf -a ./list_dir_static | less
ps -aux | grep list_dir_static
cat /proc/22936/maps

请基于QEMU模拟RISC—V的执行过程和QEMU源代码,说明RISC-V硬件加电后的几条指令在哪里?完成了哪些功能?

在 QEMU 源码中可以找到“上电”的时候刚执行的几条指令
https://github.com/qemu/qemu/blob/0ebf76aae58324b8f7bf6af798696687f5f4c2a9/hw/riscv/boot.c#L300
完成的工作是:

    读取当前的 Hart ID CSR mhartid 写入寄存器 a0

    (我们还没有用到:将 FDT (Flatten device tree) 在物理内存中的地址写入 a1)

    跳转到 start_addr ,在我们实验中是 RustSBI 的地址

colorful logging
http://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/7exercise.html#log
对编译器如何向调试器提供生成的代码的信息,有兴趣可以参阅 DWARF 规范
https://dwarfstd.org/

use option_env!("LOG") to set max level

$ make run LOG=INFO

using option_env!("LOG") will get Option<&str>, that is Some("INFO") here

OccupyMars2025 commented 1 year ago

Is "ra" register saved by the caller or the callee ?

http://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/5support-func-call.html#id8

image

https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf

image

OccupyMars2025 commented 1 year ago

code to print on the console and shutdown

https://github.com/OccupyMars2025/rustsbi/issues/1

OccupyMars2025 commented 1 year ago

[NOT solved]Can you figure out how to implement the functionality of SystemResetType::WarmReboot and SystemResetType::ColdReboot in RustSBI ?

// os/src/sbi.rs
// https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/riscv-sbi.adoc#function-system-reset-fid-0
pub fn shutdown() -> ! {
    sbi_call(SRST_EXTENSION, SYSTEM_RESET_FUNCTION, 
        SystemResetType::Shutdown as usize, 

        /* SystemResetType::ColdReboot as usize
2023/7/2: Not implemented 
[rustsbi-panic] hart 0 panicked at 'not yet implemented', rustsbi-qemu/src/qemu_test.rs:37:17
[rustsbi-panic] system shutdown scheduled due to RustSBI panic
make: *** [Makefile:9: run] Error 255
         */
        // SystemResetType::ColdReboot as usize, 

        /*SystemResetType::WarmReboot as usize, 
2023/7/2: Not implemented 
[rustsbi-panic] hart 0 panicked at 'not yet implemented', rustsbi-qemu/src/qemu_test.rs:37:17
[rustsbi-panic] system shutdown scheduled due to RustSBI panic
make: *** [Makefile:9: run] Error 255
         */
        // SystemResetType::WarmReboot as usize, 
        SystemResetReason::NoReason as usize, 
        0);
    panic!("It should have shutdown !")
}
OccupyMars2025 commented 1 year ago

(Important, NOT solved) cannot compile the provided answer

http://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/8answer.html

实现一个linux应用程序B,能打印出调用栈链信息。(用C或Rust编程)

/* 
file name: print_stack_trace_fp_chain_using_libunwind.c
2023/7/2:
/usr/include/x86_64-linux-gnu/libunwind.h

$ gcc  print_stack_trace_fp_chain_using_libunwind.c  -o print_stack_trace_fp_chain_using_libunwind  -funwind-tables -lunwind
/usr/bin/ld: /usr/lib/gcc/x86_64-linux-gnu/11/../../../x86_64-linux-gnu/Scrt1.o: in function `_start':
(.text+0x1b): undefined reference to `main'
collect2: error: ld returned 1 exit status

Failure: what's wrong ?
*/

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>

#define UNW_LOCAL_ONLY
#include <libunwind.h>

// Compile with -funwind-tables -lunwind
void print_stack_trace_libunwind() {
    printf("=== Stack trace from libunwind ===\n");

    unw_cursor_t cursor; unw_context_t uc;
    unw_word_t pc, sp;

    unw_getcontext(&uc);
    unw_init_local(&cursor, &uc);

    while (unw_step(&cursor) > 0) {
        unw_get_reg(&cursor, UNW_REG_IP, &pc);
        unw_get_reg(&cursor, UNW_REG_SP, &sp);

        printf("Program counter: 0x%016" PRIxPTR "\n", (uintptr_t) pc);
        printf("Stack pointer: 0x%016" PRIxPTR "\n", (uintptr_t) sp);
        printf("\n");
    }
    printf("=== End ===\n\n");
}
OccupyMars2025 commented 1 year ago
/*
2023/7/2: This solution works
filename : print_stack_trace_fp_chain.c
http://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/8answer.html
 实现一个linux应用程序B,能打印出调用栈链信息。(用C或Rust编程)

以使用 GCC 编译的 C 语言程序为例,使用编译参数 -fno-omit-frame-pointer 的情况下,会保存栈帧指针 fp 。

fp 指向的栈位置的负偏移量处保存了两个值:

    -8(fp) 是保存的 ra

    -16(fp) 是保存的上一个 fp

因此我们可以像链表一样,从当前的 fp 寄存器的值开始,每次找到上一个 fp ,逐帧恢复我们的调用栈:
*/

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>

/* Compile with -fno-omit-frame-pointer
$ riscv64-unknown-elf-gcc  print_stack_trace_fp_chain.c  -o print_stack_trace_fp_chain -fno-omit-frame-pointer
$ qemu-riscv64 ./print_stack_trace_fp_chain
*/
void print_stack_trace_fp_chain() {
    printf("=== Stack trace from fp chain ===\n");

    uintptr_t *fp;
    asm("mv %0, fp" : "=r"(fp) : : );

    // When should this stop?
    while (fp) {
        printf("Return address: 0x%016" PRIxPTR "\n", fp[-1]);

        // maybe fp[-2] should be named "last fp"
        printf("Old stack pointer: 0x%016" PRIxPTR "\n", fp[-2]);
        printf("\n");

        fp = (uintptr_t *) fp[-2];
    }

    printf("=== End ===\n\n");
}

int main() {
    print_stack_trace_fp_chain();

    return 0;
}
OccupyMars2025 commented 1 year ago

[NOT solved] 实现一个基于rcore/ucore tutorial的应用程序C,用sleep系统调用睡眠5秒(in rcore/ucore tutorial v3: Branch ch1)

http://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/8answer.html

maybe useful infomation 1:

https://github.com/riscv-non-isa/riscv-sbi-doc/blob/master/riscv-sbi.adoc#system-suspend-extension-eid-0x53555350-susp

// from opensbi project
// lib/sbi/sbi_ecall_susp.c
    if (funcid == SBI_EXT_SUSP_SUSPEND)
        ret = sbi_system_suspend(regs->a0, regs->a1, regs->a2);
// from opensbi project
//  opensbi-master/lib/sbi/sbi_system.c
int sbi_system_suspend(u32 sleep_type, ulong resume_addr, ulong opaque)

maybe useful infomation 2:

https://github.com/torvalds/linux/blob/995b406c7e972fab181a4bb57f3b95e59b8e5bf3/arch/riscv/kernel/sbi.c#L25

struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
            unsigned long arg1, unsigned long arg2,
            unsigned long arg3, unsigned long arg4,
            unsigned long arg5)
{
    struct sbiret ret;

    register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);
    register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);
    register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);
    register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);
    register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);
    register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);
    register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);
    register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);
    asm volatile ("ecall"
              : "+r" (a0), "+r" (a1)
              : "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)
              : "memory");
    ret.error = a0;
    ret.value = a1;

    return ret;
}

maybe useful infomation 3:

use "strace" to trace system calls of a sleep application

refer to the implementation of xv6-riscv

#include <time.h>
#include <stdio.h>

// provided by AI
void sleep(unsigned int seconds) {
    struct timespec ts;
    ts.tv_sec = seconds;
    ts.tv_nsec = 0;
    while (nanosleep(&ts, &ts) == -1) {
        perror("nanosleep");
    }
}

int main() {
    printf("Start sleeping:\n");
    sleep(10);
    printf("End sleeping:\n");
}

https://man7.org/training/ https://www.man7.org/linux/man-pages/man3/sleep.3.html https://patchwork.kernel.org/project/linux-riscv/list/

https://patchwork.kernel.org/project/linux-riscv/patch/20230106113216.443057-2-ajones@ventanamicro.com/ (very important)

https://lists.riscv.org/g/tech-prs/message/235 https://docs.rs/sbi-rt/latest/sbi_rt/ opensbi-master/lib/sbi/sbi_system.c rustsbi-main/src/susp.rs

// ch5 branch: user/src/lib.rs
pub fn sleep(period_ms: usize) {
    let start = sys_get_time();
    while sys_get_time() < start + period_ms as isize {
        sys_yield();
    }
}
other's suggestion:
如果有人想要使用riscv的mtime寄存器完成sleep函数,qemu中mtime寄存器的频率是10Mhz。
我花了一点时间才最终在google group chat中找到这一点,我不理解为什么qemu没有在riscv virt中给出这一确切的值。
OccupyMars2025 commented 1 year ago
在以后,我们还可以在 log 信息中增加线程、CPU等信息(只是一个推荐,不做要求),这些信息将极大的方便你的代码调试。

http://rcore-os.cn/rCore-Tutorial-Book-v3/chapter1/7exercise.html#log
可以彩色输出,不要求 log 等级控制
可以关闭内核所有输出。从 lab2 开始要求关闭内核所有输出(如果实现了 log 等级控制,那么这一点自然就实现了)
os/Makefile (要求 make run LOG=xxx 可以正确执行,可以不实现对 LOG 这一属性的支持,设置默认输出等级为 INFO)

https://docs.rs/log/latest/log/index.html
The "Available logging implementations" part lists many logging implementations, you can study their 
source code
Warning: The logging system may only be initialized once.

Method to implement the colorful logging:

Step 1: copy the example code in the documentation of log crate

https://docs.rs/log/latest/log/index.html

Step 2: modify the println macro in the example code