rCore-N代码分析

  • 2023年 11月17日
  • 读完约需 7 分钟
  • 最后更新于 2023年 11月17日

环境

后续均以qemu为基础进行分析。主要对一些不熟悉、容易忘记或是还有疑问的地方进行记录。

rust_main

首先初始化的是hart 0, 不确定怎么翻译好,保留hart, 类似多处理器?驱动从device::init开始。

device::init()

目前只调用了net:init(), 把NET_DEVICE_ADDR指向的地址0x10008000,然后以这块内存建立MmioTransport,再以这个MmioTransport, NET_QUEUE_SIZE,NET_BUFFER_LEN建立VirtIONet设备,然后是加锁,绑定到全局静态变量NET_DEVICE。最后调用mem::forgot使其处于不可访问状态。

疑问:

  1. 到这里内存管理其实已经初始化完毕,那么这个NET_DEVICE_ADDR是物理地址还是内核S态的虚拟地址?
  2. NET_DEVICE_ADDR只是一个起始地址,MmioTransport::new读取了magic, device_id, vresion, 这些是volread!直接从硬件寄存器读取的吗?
  3. 最后的forget作用是什么?我理解是后续就只能通过NET_DEVICE来访问这个虚拟网络设备了。

PLIC

PLIC大致看了下文档,应该是集中管理中断的功能,先理清楚流程回头细看。

plic:init()

PLIC_BASE: 0xc00_0000PLIC_PRIORITY_BIT: 3来在plic中注册qemu的uart中断12~15的优先级,均设置为Priority::lowest(),如果hart是0还注册了一个中断8,

plic::init_hart(hart_id);

根据hart_id初始化每个hart的中断上下文,其中用到了get_context,它用3 * hart_id + 0 ~ 2 的方式来标识每个hart的M、S、U特权级。然后使能ini中注册的中断,最后调用Plic::set_threshold把中断临界值设置为Priority::any()。

疑问:

  1. 中断8的具体作用是什么?
  2. plic的相关调用,在crate的doc里面没有比较详细的介绍,不太明白具体作用。比如Priority各级别的区别和set_threshold的作用等

uart:init()

串口设备的主要数据结构是BufferedSerial,它的hardware字段指向的是SerialHardware, 它是MmioUart8250类型,原理跟上面的MmioTransport类似,都是传入一个base地址,配合区间长度等参数将这段地址初始化为Uart8250类型的设备。 uart:init()初始化过程如下:

  1. 定义全局变量BUFFERED_SERIAL,通过lazy_staic加载。
  2. BufferedSerial::new先初始化MmioUart8250设备,以SERIAL_BASE_ADDRESS:0x1000_2000 + i * SERIAL_ADDRESS_STRIDE:0x1000为base_address初始化串口设备,一共4个,并且初始化接收发送队列,均使用的是VecDeque, 队列长度由DEFAULT_RX_BUFFER_SIZEDEFAULT_RX_BUFFER_SIZE定义。
  3. 调用hardware_init(),参数是波特率, 初始化三个波特率为115200和一个6250000的串口。
  4. hardware.write_ier(0) 开启第一个串口的中断使能。
  5. 调用read_msr(), 该函数返回串口数模转换器(Modem)的状态。贴一下uart8205文档的说明:
pub fn read_msr(&self) -> u8
Read MSR (offset + 6)

Modem Status Register
Offset: +6 . This register is another read-only register that is here to inform your software about the current status of the modem. The modem accessed in this manner can either be an external modem, or an internal modem that uses a UART as an interface to the computer.
Bit	Notes
7	Carrier Detect
6	Ring Indicator
5	Data Set Ready
4	Clear To Send
3	Delta Data Carrier Detect
2	Trailing Edge Ring Indicator
1	Delta Data Set Ready
0	Delta Clear To Send
  1. 调用read_lsr(),返回线路状态。还是贴一下uart8205文档的说明
pub fn read_lsr(&self) -> u8
Read LSR (offset + 5)
Line Status Register
Offset: +5 . This register is used primarily to give you information on possible error conditions that may exist within the UART, based on the data that has been received. Keep in mind that this is a “read only” register, and any data written to this register is likely to be ignored or worse, cause different behavior in the UART. There are several uses for this information, and some information will be given below on how it can be useful for diagnosing problems with your serial data connection:

Bit	Notes
7	Error in Received FIFO
6	Empty Data Holding Registers
5	Empty Transmitter Holding Register
4	Break Interrupt
3	Framing Error
2	Parity Error
1	Overrun Error
0	Data Ready
  1. 调用write_mcr(0)写Modem控制寄存器以控制流量。

  2. 调用init初始化串口,参数是时钟和波特率。这里传入的时钟是100_000_000(单位是Hz?),波特率是之前传入的参数。

  3. 调用enable_received_data_available_interrupt(),使串口进入可以接收数据的状态

  4. 设置rx_intr_enabled为true,表明可以接收数据了。

  5. 调用write_fcr(0b11_000_11_1),代码有注释:从最低位开始,分别代表:

11000111
中断触发等级=14字节(Bytes)触发等级=56字节启用64字节FIFO(16750)保留DMA模式选择FIFO清除(复位)发送FIFO清除接收启用FIFO

疑问: 最开始找到的是uart8250 crate 0.6版本的文档,接口有比较大的变化,后续有时间来看

lkm::init()

通过lazy_static做了三件事:

  1. 加载全局变量SHARED_SCHE,加载了名为sharedscheduler的应用程序,作为共享调度器。
  2. 为之创建了名为SHARED_SCHE_MEMORYSET的全局内存管理实例。
  3. 创建全局变量SHARED_ELF,保存从sharedscheduler的应用程序文件读取的elf数据。