前面四章的内容使得我们的 main.rs 变得异常冗长,代码结构也并不清晰。磨刀不误砍柴工,所以在继续编写 os 之前,我们先来对已有文件进行一些整理:
创建 lang_items.rs :
usecore::panic::PanicInfo;// This function is called on panic.#[panic_handler]fnpanic(_info:&PanicInfo)->!{println!("{}",_info);loop{}}#[no_mangle]pubexternfnabort(){panic!("abort!");}
timebase 的数值约为 cpu 频率的 1% ,防止时钟中断占用过多的 cpu 资源。get_cycle 可以获取当前时间,当前时间加上 timebase(两次中断的时间差) 为下一次中断产生的时间,通过 set_timer 设置。
cpu 中有一个专门用于储存时间的 64 位寄存器。由于 system call 的返回值存放于 32 位的 x10 通用寄存器,所以需要分别读取时间的前 32 位和后 32 位:
hi 是时间的高 32 位,lo 是时间的低 32 位。注意到这里并没有之间拼接 hi 和 lo 然后将其返回,而是多了一步 if (hi == tmp) 判断。这是由于在执行完 let lo = time::read() 后,当前时间会改变。尽管时间的前 32 位改变的概率很小,但是仍然需要进行一次判断。
step2: 外设中断初始化
前面看到,时钟中断会用到 sie 寄存器。为了能够正确的产生时钟中断,我们需要先对 sie 寄存器进行初始化:
use crate::interrupt::init as interrupt_init;
global_asm!(include_str!("boot/entry.asm"));
#[no_mangle]
pub extern "C" fn rust_main() -> ! {
interrupt_init();
println!("Hello World");
unsafe{
asm!("ebreak\n"::::);
}
panic!("End of rust_main");
}
global_asm!(include_str!("trap/trap.asm"));
#![feature(lang_items)]
#![feature(asm)]
#![feature(panic_info_message)]
#![feature(global_asm)]
#![no_std]
#[macro_use]
pub mod io;
mod lang_items;
mod context;
mod interrupt;
mod init;
#![no_std]
#![no_main]
#[allow(unused_imports)]
use os;
use riscv::register::{ time, timeh};
fn get_cycle() -> u64 {
loop {
let hi = timeh::read();
let lo = time::read();
let tmp = timeh::read();
if (hi == tmp) {
return ((hi as u64) << 32) | (lo as u64);
}
}
}
// in interrupt.rs
use riscv::register::{stvec, sstatus};
#[no_mangle]
pub fn init() {
extern {
fn __alltraps();
}
unsafe {
sstatus::set_sie();
stvec::write(__alltraps as usize, stvec::TrapMode::Direct);
}
}
// in interrupt.rs
use riscv::register::scause::Trap;
use riscv::register::scause::Exception;
use riscv::register::scause::Interrupt;
use crate::clock::{ TICK, clock_set_next_event };
#[no_mangle]
pub extern "C" fn rust_trap(tf: &mut TrapFrame) {
match tf.scause.cause() {
Trap::Exception(Exception::Breakpoint) => breakpoint(),
Trap::Interrupt(Interrupt::SupervisorTimer) => super_timer(),
_ => panic!("unexpected trap"),
}
}
fn breakpoint() {
panic!("a breakpoint set by kernel");
}
fn super_timer() {
// 响应当前时钟中断的同时,手动设置下一个时钟中断。这一函数调用同时清除了 MTIP ,使得 CPU 知道当前这个中断被正确处理。
clock_set_next_event();
unsafe{
TICK = TICK + 1;
if TICK % 100 == 0 {
println!("100 ticks!");
}
}
}
// in init.rs
use crate::clock::init as clock_init;
#[no_mangle]
pub extern "C" fn rust_main() -> ! {
interrupt_init();
clock_init();
loop {}
}