computer_knowledge_notes/OS/Rcore/ch1_print.md

3.5 KiB
Raw Blame History

概述

从C与Rust、Rcore与其它OS对比的角度描述Rcore。

  • Rcore用Rust编写运行于qemu和k210。
  • Ucore用C编写运行于qemu。系统的结构应是最接近Rcore的。
  • xv6-riscv用C编写运行于qemu。
  • RT-Thread用C编写运行于k210。

第一节的目标是在屏幕上打印一些字符串。要实现此目标需要做以下的事:

  • os成功从CPU手中接管控制权。
  • os成功控制uart设备。

从CPU手中接管控制权

Rcore

要从CPU手中正确接管管控权os就需要放在内存中合适的位置这是由链接脚本控制的。

对于Rcore来说它的链接脚本是linker-<platform>.ld。两个平台唯一的差别就是BASE_ADDRESS不一样k210是0x80020000而qemu是0x80200000

硬件会从0x80000000开始执行代码由于Rcore设置了bootloader所以bootloader会先从0x80000000开始执行再把控制权转交给os。k210只有6M的内存在bootloader目录下可以查到rustsbi-k210.bin是78K所以rustsbi-k210设定从它后面128K开始执行os是合适的。qemu默认的内存就达到了128Mrustsbi_qemu.bin是107K所以rustsbi-qemu设定从它后面2M开始执行os也是没问题的。

Ucore

Ucore的链接脚本和Rcore是一样的。

xv6-riscv

与Rcore不一样xv6-riscv的链接脚本设定os是从0x80000000开始的这是因为xv6没有使用bootloader不用给它留位置。

TR-Tread

RT-Thread针对K210的链接脚本是bsp/k210/link.lds它和xv6-riscv一样也是从0x80000000开始的因为它没有使用像rustsbi这样的bootloader而自己直接控制了所有的硬件。

控制uart设备

Rcore

os通过K210上的uart设备向主机发送信息主机接收到信息后打印出来。

Rcore通过宏println!打印出"Hello world"。这个宏在src/console.rs里定义最终调用了console_putchar()console_putchar()定义在src/sbi.rs最终使用ecall指令调用了机器态的rustsbi传递的参数是SBI_CONSOLE_PUTCHAR

上面通过ecall进到rustsbi的中断处理入口_start_trap,保存寄存器的值然后调用_start_trap_rust,即start_trap_rust函数。通过match运算匹配到SupervisorEnvCall,调用到rustsbi::ecall()执行具体的处理。从lib.rs可见ecall()handle_ecall()handle_ecall()在ecall.rs。在handle_ecall()里通过match运算匹配到LEGACY_CONSOLE_PUTCHAR,进而执行legacy::console_putchar()。在ecall/legacy.rs可见console_putchar()调用的是legacy_stdio_putchar()从legacy_stdio.rs可见legacy_stdio_putchar()的定义最终使用的cratek210_pac提供的try_write()try_flush()两个函数。可以从docs.rs网站得到k210_pac的详细介绍(包括源码)。

Ucore

Ucore也是通过ecall指令进到rustsbi来实现printf()函数。从而打印"Hello world"。

xv6-riscv

xv6-riscv有自己定义对UART的驱动详见kernel/uart.c文件。比如通过Reg(THR)即可定位到UART设备的传输保持寄存器(transmit holding register),只要向这个内存地址写入数据即可实现通过串口输出的功能。

RT-Thread

RT-Thread也有定义对K210的驱动在bsp/k210/driver。比如drv_uart.c即为UART的驱动其中_uarths即为UARTHS寄存器组的起始地址drv_uarths_putc()函数就实现了向UARTHS的传输保持寄存器txdata写入数据的功能。