rCore-N代码分析
环境
后续均以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使其处于不可访问状态。
疑问:
- 到这里内存管理其实已经初始化完毕,那么这个NET_DEVICE_ADDR是物理地址还是内核S态的虚拟地址?
- NET_DEVICE_ADDR只是一个起始地址,MmioTransport::new读取了magic, device_id, vresion, 这些是volread!直接从硬件寄存器读取的吗?
- 最后的forget作用是什么?我理解是后续就只能通过NET_DEVICE来访问这个虚拟网络设备了。
PLIC
PLIC大致看了下文档,应该是集中管理中断的功能,先理清楚流程回头细看。
plic:init()
按PLIC_BASE: 0xc00_0000
和PLIC_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()。
疑问:
- 中断8的具体作用是什么?
- plic的相关调用,在crate的doc里面没有比较详细的介绍,不太明白具体作用。比如Priority各级别的区别和set_threshold的作用等
uart:init()
串口设备的主要数据结构是BufferedSerial
,它的hardware
字段指向的是SerialHardware, 它是MmioUart8250类型,原理跟上面的MmioTransport类似,都是传入一个base地址,配合区间长度等参数将这段地址初始化为Uart8250类型的设备。 uart:init()
初始化过程如下:
- 定义全局变量BUFFERED_SERIAL,通过lazy_staic加载。
BufferedSerial::new
先初始化MmioUart8250
设备,以SERIAL_BASE_ADDRESS:0x1000_2000 + i * SERIAL_ADDRESS_STRIDE:0x1000
为base_address初始化串口设备,一共4个,并且初始化接收发送队列,均使用的是VecDeque, 队列长度由DEFAULT_RX_BUFFER_SIZE
和DEFAULT_RX_BUFFER_SIZE
定义。- 调用hardware_init(),参数是波特率, 初始化三个波特率为
115200
和一个6250000
的串口。 hardware.write_ier(0)
开启第一个串口的中断使能。- 调用read_msr(), 该函数返回串口数模转换器(Modem)的状态。贴一下uart8205文档的说明:
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
- 调用read_lsr(),返回线路状态。还是贴一下uart8205文档的说明
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
调用write_mcr(0)写Modem控制寄存器以控制流量。
调用init初始化串口,参数是时钟和波特率。这里传入的时钟是100_000_000(单位是Hz?),波特率是之前传入的参数。
调用enable_received_data_available_interrupt(),使串口进入可以接收数据的状态
设置rx_intr_enabled为true,表明可以接收数据了。
调用write_fcr(0b11_000_11_1),代码有注释:从最低位开始,分别代表:
1 | 1 | 0 | 0 | 0 | 1 | 1 | 1 |
中断触发等级=14字节(Bytes) | 触发等级=56字节 | 启用64字节FIFO(16750) | 保留 | DMA模式选择 | FIFO清除(复位)发送 | FIFO清除接收 | 启用FIFO |
疑问: 最开始找到的是uart8250 crate 0.6版本的文档,接口有比较大的变化,后续有时间来看
lkm::init()
通过lazy_static做了三件事:
- 加载全局变量SHARED_SCHE,加载了名为
sharedscheduler
的应用程序,作为共享调度器。 - 为之创建了名为SHARED_SCHE_MEMORYSET的全局内存管理实例。
- 创建全局变量SHARED_ELF,保存从
sharedscheduler
的应用程序文件读取的elf数据。