Merge branch 'OsTrain2018-g4' of https://github.com/oscourse-tsinghua/RustOS into crate-memory-comment

master
Ben Pig Chu 6 years ago
commit fc727b9618

5
.gitignore vendored

@ -1,3 +1,8 @@
build
target
/kernel/src/arch/x86_64/interrupt/vector.asm
/crate/bit-allocator/Cargo.lock
/crate/memory/Cargo.lock
/crate/bbl/Cargo.lock
/crate/sync/Cargo.lock
/crate/process/Cargo.lock

@ -1,5 +1,13 @@
//! Port from sbi.h
//!
//! This code is used for OS to use hardware outside with calling these implements
/*
** @brief translate implement calling message to RISCV asm
** @param which: usize ecall type
** arg0, arg1, arg2: usize ecall args
** @retval ret: usize the result of the asm
*/
#[inline(always)]
fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
let ret;
@ -13,18 +21,38 @@ fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
ret
}
/*
** @brief output char to console
** @param ch: usize the char to output to console
** @retval none
*/
pub fn console_putchar(ch: usize) {
sbi_call(SBI_CONSOLE_PUTCHAR, ch, 0, 0);
}
/*
** @brief input char from console
** @param none
** @retval ch: usize the char get from console
*/
pub fn console_getchar() -> usize {
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
}
/*
** @brief call this function to shutdown
** @param none
** @retval none
*/
pub fn shutdown() {
sbi_call(SBI_SHUTDOWN, 0, 0, 0);
}
/*
** @brief set a timer when running
** @param stime_value: u64 time to be set
** @retval none
*/
pub fn set_timer(stime_value: u64) {
#[cfg(target_pointer_width = "32")]
sbi_call(SBI_SET_TIMER, stime_value as usize, (stime_value >> 32) as usize, 0);
@ -32,22 +60,47 @@ pub fn set_timer(stime_value: u64) {
sbi_call(SBI_SET_TIMER, stime_value as usize, 0, 0);
}
/*
** @brief clear the ipi
** @param none
** @retval none
*/
pub fn clear_ipi() {
sbi_call(SBI_CLEAR_IPI, 0, 0, 0);
}
/*
** @brief
** @param
** @retval none
*/
pub fn send_ipi(hart_mask: *const usize) {
sbi_call(SBI_SEND_IPI, hart_mask as usize, 0, 0);
}
/*
** @brief
** @param
** @retval none
*/
pub fn remote_fence_i(hart_mask: *const usize) {
sbi_call(SBI_REMOTE_FENCE_I, hart_mask as usize, 0, 0);
}
/*
** @brief
** @param
** @retval none
*/
pub fn remote_sfence_vma(hart_mask: *const usize, _start: usize, _size: usize) {
sbi_call(SBI_REMOTE_SFENCE_VMA, hart_mask as usize, 0, 0);
}
/*
** @brief
** @param
** @retval none
*/
pub fn remote_sfence_vma_asid(hart_mask: *const usize, _start: usize, _size: usize, _asid: usize) {
sbi_call(SBI_REMOTE_SFENCE_VMA_ASID, hart_mask as usize, 0, 0);
}

@ -50,6 +50,13 @@ impl<T> Process<T> {
// TODO: 除schedule()外的其它函数应该只设置进程状态不应调用schedule
impl<T: Context, S: Scheduler> Processor_<T, S> {
/*
** @brief create a new Processor
** @param init_context: T initiate context
** scheduler: S the scheduler to use
** @retval the Processor created
*/
pub fn new(init_context: T, scheduler: S) -> Self {
let init_proc = Process {
pid: 0,
@ -70,15 +77,30 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
}
}
/*
** @brief set the priority of current process
** @param priority: u8 the priority to set
** @retval none
*/
pub fn set_priority(&mut self, priority: u8) {
self.scheduler.set_priority(self.current_pid, priority);
}
/*
** @brief mark the current process to reschedule
** @param none
** @retval none
*/
pub fn set_reschedule(&mut self) {
let pid = self.current_pid;
self.set_status(pid, Status::Ready);
}
/*
** @brief allocate the pid of the process
** @param none
** @retval the pid allocated
*/
fn alloc_pid(&self) -> Pid {
let mut next: Pid = 0;
for &i in self.procs.keys() {
@ -91,6 +113,12 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
return next;
}
/*
** @brief set the status of the process
** @param pid: Pid the pid of process which needs to be set
** status: Status the status to be set
** @retval none
*/
fn set_status(&mut self, pid: Pid, status: Status) {
let status0 = self.get(pid).status.clone();
match (&status0, &status) {
@ -103,8 +131,12 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
self.get_mut(pid).status = status;
}
/// Called by timer.
/// Handle events.
/*
** @brief Called by timer.
** Handle events.
** @param none
** @retval none
*/
pub fn tick(&mut self) {
let current_pid = self.current_pid;
if self.scheduler.tick(current_pid) {
@ -127,10 +159,20 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
}
}
/*
** @brief get now time
** @param none
** @retval the time got
*/
pub fn get_time(&self) -> usize {
self.event_hub.get_time()
}
/*
** @brief add a new process
** @param context: T the context fo the process
** @retval the pid of new process
*/
pub fn add(&mut self, context: T) -> Pid {
let pid = self.alloc_pid();
let process = Process {
@ -144,8 +186,12 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
pid
}
/// Called every interrupt end
/// Do schedule ONLY IF current status != Running
/*
** @brief Called every interrupt end
** Do schedule ONLY IF current status != Running
** @param none
** @retval none
*/
pub fn schedule(&mut self) {
if self.get(self.current_pid).status == Status::Running {
return;
@ -154,9 +200,13 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
self.switch_to(pid);
}
/// Switch process to `pid`, switch page table if necessary.
/// Store `rsp` and point it to target kernel stack.
/// The current status must be set before, and not be `Running`.
/*
** @brief Switch process to `pid`, switch page table if necessary.
Store `rsp` and point it to target kernel stack.
The current status must be set before, and not be `Running`.
** @param the pid of the process to switch
** @retval none
*/
fn switch_to(&mut self, pid: Pid) {
// for debug print
let pid0 = self.current_pid;
@ -176,27 +226,61 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
to.status = Status::Running;
self.scheduler.remove(pid);
info!("switch from {} to {} {:x?}", pid0, pid, to.context);
//info!("switch from {} to {} {:x?}", pid0, pid, to.context);
unsafe { from.context.switch(&mut to.context); }
}
/*
** @brief get process by pid
** @param pid: Pid the pid of the process
** @retval the process struct
*/
fn get(&self, pid: Pid) -> &Process<T> {
self.procs.get(&pid).unwrap()
}
/*
** @brief get mut process struct by pid
** @param pid: Pid the pid of the process
** @retval the mut process struct
*/
fn get_mut(&mut self, pid: Pid) -> &mut Process<T> {
self.procs.get_mut(&pid).unwrap()
}
/*
** @brief get context of current process
** @param none
** @retval current context
*/
pub fn current_context(&self) -> &T {
&self.get(self.current_pid).context
}
/*
** @brief get pid of current process
** @param none
** @retval current pid
*/
pub fn current_pid(&self) -> Pid {
self.current_pid
}
/*
** @brief kill a process by pid
** @param pid: Pid the pid of the process to kill
** @retval none
*/
pub fn kill(&mut self, pid: Pid) {
self.exit(pid, 0x1000); // TODO: error code for killed
}
/*
** @brief exit a process by pid
** @param pid: Pid the pid to exit
** error_code: ErrorCode the error code when exiting
** @retval none
*/
pub fn exit(&mut self, pid: Pid, error_code: ErrorCode) {
info!("{} exit, code: {}", pid, error_code);
self.set_status(pid, Status::Exited(error_code));
@ -207,18 +291,40 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
}
}
/*
** @brief let a process to sleep for a while
** @param pid: Pid the pid of the process to sleep
** time: usize the time to sleep
** @retval none
*/
pub fn sleep(&mut self, pid: Pid, time: usize) {
self.set_status(pid, Status::Sleeping);
self.event_hub.push(time, Event::Wakeup(pid));
}
/*
** @brief let a process to sleep until wake up
** @param pid: Pid the pid of the process to sleep
** @retval none
*/
pub fn sleep_(&mut self, pid: Pid) {
self.set_status(pid, Status::Sleeping);
}
/*
** @brief wake up al sleeping process
** @param pid: Pid the pid of the process to wake up
** @retval none
*/
pub fn wakeup_(&mut self, pid: Pid) {
self.set_status(pid, Status::Ready);
}
/// Let current process wait for another
/*
** @brief Let current process wait for another
** @param pid: Pid the pid of the process to wait for
** @retval the result of wait
*/
pub fn current_wait_for(&mut self, pid: Pid) -> WaitResult {
info!("current {} wait for {:?}", self.current_pid, pid);
if self.procs.values().filter(|&p| p.parent == self.current_pid).next().is_none() {
@ -236,7 +342,11 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
WaitResult::Ok(pid, exit_code)
}
/// Try to find a exited wait target
/*
** @brief Try to find a exited wait target
** @param pid: Pid the pid of the process to wait for
** @retval the pid found or none
*/
fn try_wait(&mut self, pid: Pid) -> Option<Pid> {
match pid {
0 => self.procs.values()
@ -246,6 +356,11 @@ impl<T: Context, S: Scheduler> Processor_<T, S> {
}
}
/*
** @brief find one process which is waiting for the input process
** @param pid: Pid the pid of the target process
** @retval the pid of the waiting process or none
*/
fn find_waiter(&self, pid: Pid) -> Option<Pid> {
self.procs.values().find(|&p| {
p.status == Status::Waiting(pid) ||

@ -2,18 +2,51 @@ use alloc::{collections::BinaryHeap, vec::Vec};
type Pid = usize;
///
// implements of process scheduler
pub trait Scheduler {
/*
** @brief add a new process
** @param pid: Pid the pid of the process to add
** @retval none
*/
fn insert(&mut self, pid: Pid);
/*
** @brief remove a processs from the list
** @param pid: Pid the pid of the process to remove
** @retval none
*/
fn remove(&mut self, pid: Pid);
/*
** @brief choose a process to run next
** @param none
** @retval Option<Pid> the pid of the process to run or none
*/
fn select(&mut self) -> Option<Pid>;
/*
** @brief when a clock interrupt occurs, update the list and check whether need to reschedule
** @param current: Pid the pid of the process which is running now
** @retval bool if need to reschedule
*/
fn tick(&mut self, current: Pid) -> bool; // need reschedule?
/*
** @brief set the priority of the process
** @param pid: Pid the pid of the process to be set
** priority: u8 the priority to be set
** @retval none
*/
fn set_priority(&mut self, pid: Pid, priority: u8);
}
pub use self::rr::RRScheduler;
pub use self::stride::StrideScheduler;
// use round-robin scheduling
mod rr {
use super::*;
@ -106,6 +139,7 @@ mod rr {
}
}
// use stride scheduling
mod stride {
use super::*;

@ -50,7 +50,11 @@ pub struct ThreadMod<S: ThreadSupport> {
}
impl<S: ThreadSupport> ThreadMod<S> {
/// Gets a handle to the thread that invokes it.
/*
** @brief Gets a handle to the thread that invokes it.
** @param none
** @retval the thread to get
*/
pub fn current() -> Thread<S> {
Thread {
pid: S::processor().current_pid(),
@ -58,7 +62,11 @@ impl<S: ThreadSupport> ThreadMod<S> {
}
}
/// Puts the current thread to sleep for the specified amount of time.
/*
** @brief Puts the current thread to sleep for the specified amount of time.
** @param dur: Duration the time to sleep
** @retval none
*/
pub fn sleep(dur: Duration) {
let time = dur_to_ticks(dur);
info!("sleep: {:?} ticks", time);
@ -72,7 +80,11 @@ impl<S: ThreadSupport> ThreadMod<S> {
}
}
/// Spawns a new thread, returning a JoinHandle for it.
/*
** @brief Spawns a new thread, returning a JoinHandle for it.
** @param f: F the thread to start
** @retval JoinHandle the JoinHandle of the new thread
*/
pub fn spawn<F, T>(f: F) -> JoinHandle<S, T>
where
F: Send + 'static + FnOnce() -> T,
@ -103,7 +115,11 @@ impl<S: ThreadSupport> ThreadMod<S> {
}
}
/// Cooperatively gives up a timeslice to the OS scheduler.
/*
** @brief Cooperatively gives up a timeslice to the OS scheduler.
** @param none
** @retval none
*/
pub fn yield_now() {
info!("yield:");
let mut processor = S::processor();
@ -111,7 +127,11 @@ impl<S: ThreadSupport> ThreadMod<S> {
processor.schedule();
}
/// Blocks unless or until the current thread's token is made available.
/*
** @brief Blocks unless or until the current thread's token is made available.
** @param none
** @retval none
*/
pub fn park() {
info!("park:");
let mut processor = S::processor();
@ -128,12 +148,20 @@ pub struct Thread<S: ThreadSupport> {
}
impl<S: ThreadSupport> Thread<S> {
/// Atomically makes the handle's token available if it is not already.
/*
** @brief Atomically makes the handle's token available if it is not already.
** @param none
** @retval none
*/
pub fn unpark(&self) {
let mut processor = S::processor();
processor.wakeup_(self.pid);
}
/// Gets the thread's unique identifier.
/*
** @brief Gets the thread's unique identifier.
** @param none
** @retval usize the the thread's unique identifier
*/
pub fn id(&self) -> usize {
self.pid
}
@ -146,11 +174,19 @@ pub struct JoinHandle<S: ThreadSupport, T> {
}
impl<S: ThreadSupport, T> JoinHandle<S, T> {
/// Extracts a handle to the underlying thread.
/*
** @brief Extracts a handle to the underlying thread.
** @param none
** @retval the thread of the handle
*/
pub fn thread(&self) -> &Thread<S> {
&self.thread
}
/// Waits for the associated thread to finish.
/*
** @brief Waits for the associated thread to finish.
** @param none
** @retval Result<T, ()> the result of the associated thread
*/
pub fn join(self) -> Result<T, ()> {
let mut processor = S::processor();
match processor.current_wait_for(self.thread.pid) {

@ -1,3 +1,5 @@
//! entrance to test the communication in processes with solving five philosophers problem
mod mutex;
mod monitor;

@ -1,3 +1,5 @@
//! solve the five philosophers problem with monitor
use std::thread;
use std::sync::{Mutex, Condvar, Arc};
use std::time::Duration;
@ -51,6 +53,7 @@ struct Table {
fork_condvar: Vec<Condvar>,
}
// the main function to test
pub fn main() {
let table = Arc::new(Table {
fork_status: Mutex::new(vec![false; 5]),

@ -1,3 +1,5 @@
//! solve the five philosophers problem with mutex
use std::thread;
use std::sync::{Mutex, Arc};
use std::time::Duration;
@ -35,6 +37,7 @@ struct Table {
forks: Vec<Mutex<()>>,
}
// the main function to test
pub fn main() {
let table = Arc::new(Table {
forks: vec![

@ -0,0 +1,38 @@
# Rust OS 实验一分析报告
## 1 环境配置
依据[RustOS开发文档](https://rucore.gitbook.io/rust-os-docs/kai-fa-huan-jing-pei-zhi)中的说明进行安装。编辑器采用VSCode+Rust插件。
成功重现RustOS的交叉编译和在qemu上的x86-64和riscv32的运行步骤重现。
## 2 注释完善
我负责kernel模块的注释完善工作主要对arch=riscv32从启动到进入shell涉及的代码注释进行了完善其余部分注释尚未完成有待后续分析完善。
## 3 分析测试
### 3.0 现有测试分析
现有RustOS shell下已经包含了一部分测试程序测试结果如下(以下测试结果基于arch=riscv32下)
| 测试程序 | 是否通过 | 错误原因分析 |
| :------: | :------: | :------: |
| waitkill | 通过 | |
| sleep | 通过 | |
| spin | 通过 | |
| sh | 未通过 | unknown syscall id: 0x66, args: [0, 7000ff97, 1, 7000ff70, 25, 73] <br> 可能是由于尚未实现sh这一系统调用 |
| forktest | 通过 | |
| faultread | 通过 | [INFO] open: path: "stdin:", flags: 0 <br>[INFO] open: path: "stdout:", flags: 1 <br>[ERROR] Process 2 error <br> 系统似乎成功处理了该异常并正确结束该进程? |
| forktree | 未通过 | PANIC in src/lang.rs at line 22 <br>out of memory<br>似乎是由于“目前所有没被wait过的进程退出后内存不会被回收”导致的问题|
| divzero | 通过 | |
| yield | 通过 | |
| faultreadkernel | 通过 | 原因同faultread |
| exit | 通过 | |
| softint | 未实现? | |
| badsegment | 通过 | |
| hello | 通过 | |
| ls | 未实现 | |
| priority | 通过 | |
| badarg | 未通过 | PANIC in /home/lcy1996/.rustup/toolchains/nightly-2018-09-18-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/option.rs at line 345 <br> called \`Option::unwrap()\` on a \`None\` value <br> 内核bug?错误原因需要进一步探索|
| testbss | 未通过 | PANIC in /home/lcy1996/.rustup/toolchains/nightly-2018-09-18-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/src/libcore/option.rs at line 1000<br>failed to allocate frame <br>内核bug?错误原因需要进一步探索|
| pgdir | 未通过 | [ERROR] unknown syscall id: 0x1f, args: [a, ffff6ad9, 2, 0, 15, ffffffff] <br> 阅读syscall.rs推测原因是尚未实现0x1f(PGDIR)这个系统调用 |
| matrix | 通过 | |
| sleepkill | 未通过 | PANIC in /home/lcy1996/Documents/OSTrain/RustOS/crate/process/src/event_hub.rs at line 55 <br> attempt to add with overflow <br> 推测与forktree出错原因相同|
此外现有框架下已经有sync和thread的test但是riscv32下这些test均无法编译通过询问王润基后推测原因是目前编译器对RISCV原子指令支持不全。因此尝试从ucore lab中移植一些test过来便于后续lab的裁剪。

@ -0,0 +1,49 @@
# Rust OS 教学lab实验的制作 方案设计文档
## 1 实验目标
* 补全部分Rust OS尚未实现的基础功能
* 从现有Rust OS中裁剪出一个子集用于教学lab
* 参考现有ucore OS实验设计Rust OS教学lab基于riscv32并完成相关测试代码的编写
* 完成教学实验说明文档并补充每一个lab所需的代码注释
* 尝试配置基于浏览器的虚拟机实验环境
## 2 背景
现有的操作系统大实验是基于ucore OS完成的总共由8个小实验完成包含了操作系统从启动到进入shell的全过程内容包括硬件启动异常处理内存管理进程管理同步互斥文件系统等操作系统基础知识点。[ucore OS lab](https://github.com/chyyuu/ucore_os_lab)已经具有十分完善的[教学文档](https://objectkuan.gitbooks.io/ucore-docs/content/index.html)。
ucoreOS lab基于x86结构主要由C语言完成编写。x86架构由于其向后兼容性等种种原因整体结构比较复杂作为教学操作系统门槛比较高。相比较而言riscv32架构简洁高效易于实现是教学用cpu结构的不二选择。此外C语言的安全性一直以来饱受诟病。相较C语言Rust语言更加简洁、安全模块性好可复用性强可以通过采用现有库文件更优雅的解决文件。**最重要的是王润基等同学已经参照ucore OS实现了一个比较完整的基于x86_64和riscv32的Rust OS**因此我们决定再次基础上进行完善并裁剪出一个基于riscv32的教学用RustOS lab。
## 3 实验设计与分工
### 3.1 完善现有Rust OS基础功能
现有Rust OS在riscv32环境下还存在一些bugs或unimplemented features要想完成一个类似于ucore OS lab的教学实验框架首先要把这些漏洞完善。现简单将每个lab目前待完善的地方和负责填坑的人列举如下
**lab1:**主要涉及实验框架的编译、调试OS bootloader启动过程。现有实验框架已经完成不需要完善。
**lab2:**主要涉及物理内存管理由于riscv32结构的物理内存管理仅通过一个二级页表来实现不像x86架构下还需要兼容段表(虽然最终只是一个自映射)因此这部分比原本的ucore OS lab会更简洁、易理解一些。另外RustOS 的现有物理内存分配算法是通过线段树实现的,且相较原本的内存分配算法复用性更好。本分内容已经完成。
**lab3:**该部分实验主要涉及虚存管理部分,**目前RUST OS仅针对x86_64结构实现了page fault的处理**后续需要完成riscv32的page fault处理和虚存管理部分。
**lab4:**主要涉及内核线程创建和调度,现有框架中本部分内容已经完成,是否存在问题有待后续测试。
**lab5:**主要涉及用户线程管理,此部分内容似乎同样已经完成,是否存在问题有待后续测试。**事实上之前测试中似乎又提到没有wait过的进程退出内存不会被回收有待完善。**
**lab6:**此部分主要涉及进程调度器的相关内容,现有框架中本部分内容已经完成,是否存在问题有待后续测试。
**lab7:**主要涉及的是进程间的同步、共享资源的竞争,要求熟悉掌握信号量的原理,管程与条件变量的原理与实现,并用其来解决哲学家问题。**但是目前的rustOS-riscv中由于编译器对于原子指令的支持问题导致相关功能未能实现**需要寻求解决的办法来完善该实验。而且x86_64下该模块与kernel尚未分离导致结构不够清晰需要进行调整。
**lab8:**@朱书聪
其中前6个lab现有框架已经实现完成主要涉及虚存管理和进程管理的部分内容有待完善此部分计划由刘辰屹来完成。lab7在riscv32下的完善计划由陈秋昊来完成lab8的完善计划由朱书聪来完成。上述为暂时的分工实际分工视各部分难度大小再进行调整。
此部分计划在第5周结束前完成。
### 3.2 教学lab的裁剪和测试程序的编写
此部分内容计划在第6周结束前完成
### 3.3 教学lab文档编写
此部分内容计划在第7周结束前完成可能向后顺延至第8周
### 3.4 在浏览器中运行Rust OS lab
目前没有能够探索到实现此计划的可行性,待定。
## 4 参考文献
ucore OS lab实验指导书https://objectkuan.gitbooks.io/ucore-docs/content/index.html
王润基同学的Rust OS开发文档https://rucore.gitbook.io/rust-os-docs/

@ -3,43 +3,69 @@ use super::super::riscv::register::*;
#[derive(Debug, Clone)]
#[repr(C)]
pub struct TrapFrame {
pub x: [usize; 32],
pub sstatus: sstatus::Sstatus,
pub sepc: usize,
pub sbadaddr: usize,
pub scause: scause::Scause,
pub x: [usize; 32], // general registers
pub sstatus: sstatus::Sstatus, // Supervisor Status Register
pub sepc: usize, // Supervisor exception program counter, save the trap virtual address (here is used to save the process program entry addr?)
pub sbadaddr: usize, // Supervisor bad address
pub scause: scause::Scause, // scause register: record the cause of exception/interrupt/trap
}
/// 用于在内核栈中构造新线程的中断帧
/// Generate the trapframe for building new thread in kernel
impl TrapFrame {
/*
* @param:
* entry: program entry for the thread
* arg: a0
* sp: stack top
* @brief:
* generate a trapfram for building a new kernel thread
* @retval:
* the trapframe for new kernel thread
*/
fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, sp: usize) -> Self {
use core::mem::zeroed;
let mut tf: Self = unsafe { zeroed() };
tf.x[10] = arg; // a0
tf.x[2] = sp;
tf.x[2] = sp;
tf.sepc = entry as usize;
tf.sstatus = sstatus::read();
// Supervisor Previous Interrupt Enable
tf.sstatus.set_spie(true);
// Supervisor Interrupt Disable
tf.sstatus.set_sie(false);
// Supervisor Previous Privilege Mode is Supervisor
tf.sstatus.set_spp(sstatus::SPP::Supervisor);
tf
}
/*
* @param:
* entry_addr: program entry for the thread
* sp: stack top
* @brief:
* generate a trapfram for building a new user thread
* @retval:
* the trapframe for new user thread
*/
fn new_user_thread(entry_addr: usize, sp: usize) -> Self {
use core::mem::zeroed;
let mut tf: Self = unsafe { zeroed() };
tf.x[2] = sp;
tf.sepc = entry_addr;
tf.sstatus = sstatus::read();
// Supervisor Previous Interrupt Disable ?
tf.sstatus.set_spie(false); // Enable interrupt
// Supervisor Previous Privilege Mode is User
tf.sstatus.set_spp(sstatus::SPP::User);
tf
}
pub fn is_user(&self) -> bool {
unimplemented!()
}
}
/// 新线程的内核栈初始内容
/// kernel stack contents for a new thread
#[derive(Debug)]
#[repr(C)]
pub struct InitStack {
@ -48,8 +74,16 @@ pub struct InitStack {
}
impl InitStack {
/*
* @param:
* stack_top: the pointer to kernel stack stop
* @brief:
* save the InitStack on the kernel stack stop
* @retval:
* a Context with ptr in it
*/
unsafe fn push_at(self, stack_top: usize) -> Context {
let ptr = (stack_top as *mut Self).offset(-1);
let ptr = (stack_top as *mut Self).offset(-1); //real kernel stack top
*ptr = self;
Context(ptr as usize)
}
@ -69,10 +103,12 @@ struct ContextData {
impl ContextData {
fn new(satp: usize) -> Self {
// satp(asid) just like cr3, save the physical address for Page directory?
ContextData { ra: __trapret as usize, satp, ..ContextData::default() }
}
}
/// A struct only contain one usize element
#[derive(Debug)]
pub struct Context(usize);
@ -132,27 +168,69 @@ impl Context {
: : : : "volatile" )
}
/*
* @brief:
* generate a null Context
* @retval:
* a null Context
*/
pub unsafe fn null() -> Self {
Context(0)
}
/*
* @param:
* entry: program entry for the thread
* arg: a0
* kstack_top: kernel stack top
* cr3: cr3 register, save the physical address of Page directory
* @brief:
* generate the content of kernel stack for the new kernel thread and save it's address at kernel stack top - 1
* @retval:
* a Context struct with the pointer to the kernel stack top - 1 as its only element
*/
pub unsafe fn new_kernel_thread(entry: extern fn(usize) -> !, arg: usize, kstack_top: usize, cr3: usize) -> Self {
InitStack {
context: ContextData::new(cr3),
tf: TrapFrame::new_kernel_thread(entry, arg, kstack_top),
}.push_at(kstack_top)
}
/*
* @param:
* entry_addr: program entry for the thread
* ustack_top: user stack top
* kstack_top: kernel stack top
* is32: whether the cpu is 32 bit or not
* cr3: cr3 register, save the physical address of Page directory
* @brief:
* generate the content of kernel stack for the new user thread and save it's address at kernel stack top - 1
* @retval:
* a Context struct with the pointer to the kernel stack top - 1 as its only element
*/
pub unsafe fn new_user_thread(entry_addr: usize, ustack_top: usize, kstack_top: usize, is32: bool, cr3: usize) -> Self {
InitStack {
context: ContextData::new(cr3),
tf: TrapFrame::new_user_thread(entry_addr, ustack_top),
}.push_at(kstack_top)
}
/*
* @param:
* TrapFrame: the trapframe of the forked process(thread)
* kstack_top: kernel stack top
* cr3: cr3 register, save the physical address of Page directory
* @brief:
* fork and generate a new process(thread) Context according to the TrapFrame and save it's address at kernel stack top - 1
* @retval:
* a Context struct with the pointer to the kernel stack top - 1 as its only element
*/
pub unsafe fn new_fork(tf: &TrapFrame, kstack_top: usize, cr3: usize) -> Self {
InitStack {
context: ContextData::new(cr3),
tf: {
let mut tf = tf.clone();
// fork function's ret value, the new process is 0
tf.x[10] = 0; // a0
tf
},

@ -4,6 +4,10 @@ pub use self::context::*;
#[path = "context.rs"]
mod context;
/*
* @brief:
* initialize the interrupt status
*/
pub fn init() {
extern {
fn __alltraps();
@ -18,11 +22,21 @@ pub fn init() {
info!("interrupt: init end");
}
/*
* @brief:
* enable interrupt
*/
#[inline(always)]
pub unsafe fn enable() {
sstatus::set_sie();
}
/*
* @brief:
* store and disable interrupt
* @retbal:
* a usize value store the origin sie
*/
#[inline(always)]
pub unsafe fn disable_and_store() -> usize {
let e = sstatus::read().sie() as usize;
@ -30,6 +44,12 @@ pub unsafe fn disable_and_store() -> usize {
e
}
/*
* @param:
* flags: input flag
* @brief:
* enable interrupt if flags != 0
*/
#[inline(always)]
pub unsafe fn restore(flags: usize) {
if flags != 0 {
@ -37,10 +57,17 @@ pub unsafe fn restore(flags: usize) {
}
}
/*
* @param:
* TrapFrame: the trapFrame of the Interrupt/Exception/Trap to be processed
* @brief:
* process the Interrupt/Exception/Trap
*/
#[no_mangle]
pub extern fn rust_trap(tf: &mut TrapFrame) {
use super::riscv::register::scause::{Trap, Interrupt as I, Exception as E};
trace!("Interrupt: {:?}", tf.scause.cause());
// page should be processed here but not now
match tf.scause.cause() {
Trap::Interrupt(I::SupervisorTimer) => timer(),
Trap::Exception(E::IllegalInstruction) => illegal_inst(tf),
@ -51,17 +78,33 @@ pub extern fn rust_trap(tf: &mut TrapFrame) {
trace!("Interrupt end");
}
/*
* @brief:
* process timer interrupt
*/
fn timer() {
::trap::timer();
super::timer::set_next();
}
/*
* @param:
* TrapFrame: the Trapframe for the syscall
* @brief:
* process syscall
*/
fn syscall(tf: &mut TrapFrame) {
tf.sepc += 4; // Must before syscall, because of fork.
let ret = ::syscall::syscall(tf.x[10], [tf.x[11], tf.x[12], tf.x[13], tf.x[14], tf.x[15], tf.x[16]], tf);
tf.x[10] = ret as usize;
}
/*
* @param:
* TrapFrame: the Trapframe for the illegal inst exception
* @brief:
* process IllegalInstruction exception
*/
fn illegal_inst(tf: &mut TrapFrame) {
if !emulate_mul_div(tf) {
::trap::error(tf);
@ -69,6 +112,14 @@ fn illegal_inst(tf: &mut TrapFrame) {
}
/// Migrate from riscv-pk
/*
* @param:
* TrapFrame: the Trapframe for the illegal inst exception
* @brief:
* emulate the multiply and divide operation (if not this kind of operation return false)
* @retval:
* a bool indicates whether emulate the multiply and divide operation successfully
*/
fn emulate_mul_div(tf: &mut TrapFrame) -> bool {
let insn = unsafe { *(tf.sepc as *const usize) };
let rs1 = tf.x[get_reg(insn, RS1)];
@ -94,7 +145,7 @@ fn emulate_mul_div(tf: &mut TrapFrame) -> bool {
return false;
};
tf.x[get_reg(insn, RD)] = rd;
tf.sepc += 4;
tf.sepc += 4; // jump to next instruction
return true;
fn get_reg(inst: usize, offset: usize) -> usize {

@ -3,19 +3,28 @@ use memory::{active_table, FRAME_ALLOCATOR, init_heap, MemoryArea, MemoryAttr, M
use super::riscv::{addr::*, register::sstatus};
use ucore_memory::PAGE_SIZE;
/*
* @brief:
* Init the mermory management module, allow memory access and set up page table and init heap and frame allocator
*/
pub fn init() {
#[repr(align(4096))]
#[repr(align(4096))] // align the PageData struct to 4096 bytes
struct PageData([u8; PAGE_SIZE]);
static PAGE_TABLE_ROOT: PageData = PageData([0; PAGE_SIZE]);
unsafe { sstatus::set_sum(); } // Allow user memory access
let frame = Frame::of_addr(PhysAddr::new(&PAGE_TABLE_ROOT as *const _ as u32));
super::paging::setup_page_table(frame);
let frame = Frame::of_addr(PhysAddr::new(&PAGE_TABLE_ROOT as *const _ as u32));
super::paging::setup_page_table(frame); // set up page table
// initialize heap and Frame allocator
init_frame_allocator();
remap_the_kernel();
init_heap();
}
/*
* @brief:
* Init frame allocator, here use a BitAlloc implemented by segment tree.
*/
fn init_frame_allocator() {
use bit_allocator::BitAlloc;
use core::ops::Range;
@ -23,9 +32,19 @@ fn init_frame_allocator() {
let mut ba = FRAME_ALLOCATOR.lock();
use consts::{KERNEL_HEAP_OFFSET, KERNEL_HEAP_SIZE};
// keep memory for the kernel heap and set other physical memory available in BitAlloc
ba.insert(to_range(KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE, MEMORY_END));
info!("FrameAllocator init end");
/*
* @param:
* start: start address
* end: end address
* @brief:
* transform the memory address to the page number
* @retval:
* the page number range from start address to end address
*/
fn to_range(start: usize, end: usize) -> Range<usize> {
let page_start = (start - MEMORY_OFFSET) / PAGE_SIZE;
let page_end = (end - MEMORY_OFFSET - 1) / PAGE_SIZE + 1;
@ -33,8 +52,13 @@ fn init_frame_allocator() {
}
}
/*
* @brief:
* remmap the kernel memory address
*/
fn remap_the_kernel() {
use consts::{KERNEL_HEAP_OFFSET, KERNEL_HEAP_SIZE};
// set up kernel stack
let kstack = Stack {
top: bootstacktop as usize,
bottom: bootstack as usize + PAGE_SIZE,

@ -11,10 +11,15 @@ pub mod compiler_rt;
#[no_mangle]
pub extern fn rust_main() -> ! {
println!("Hello RISCV! {}", 123);
// First init log mod, so that we can print log info.
::logging::init();
// Init interrupt handling.
interrupt::init();
// Init physical memory management and heap
memory::init();
// Init timer interrupt
timer::init();
::kmain();
}

@ -10,6 +10,12 @@ use ucore_memory::memory_set::*;
use ucore_memory::PAGE_SIZE;
use ucore_memory::paging::*;
/*
* @param:
* Frame: page table root frame
* @brief:
* setup page table in the frame
*/
// need 1 page
pub fn setup_page_table(frame: Frame) {
let p2 = unsafe { &mut *(frame.start_address().as_u32() as *mut RvPageTable) };
@ -36,42 +42,92 @@ pub struct PageEntry(PageTableEntry);
impl PageTable for ActivePageTable {
type Entry = PageEntry;
/*
* @param:
* addr: the virtual addr to be matched
* target: the physical addr to be matched with addr
* @brief:
* map the virtual address 'addr' to the physical address 'target' in pagetable.
* @retval:
* the matched PageEntry
*/
fn map(&mut self, addr: usize, target: usize) -> &mut PageEntry {
// the flag for the new page entry
let flags = EF::VALID | EF::READABLE | EF::WRITABLE;
// here page is for the virtual address while frame is for the physical, both of them is 4096 bytes align
let page = Page::of_addr(VirtAddr::new(addr));
let frame = Frame::of_addr(PhysAddr::new(target as u32));
// map the page to the frame using FrameAllocatorForRiscv
self.0.map_to(page, frame, flags, &mut FrameAllocatorForRiscv)
.unwrap().flush();
self.get_entry(addr)
}
/*
* @param:
* addr: virtual address of which the mapped physical frame should be unmapped
* @bridf:
^ unmap the virtual addresses' mapped physical frame
*/
fn unmap(&mut self, addr: usize) {
let page = Page::of_addr(VirtAddr::new(addr));
let (frame, flush) = self.0.unmap(page).unwrap();
flush.flush();
}
/*
* @param:
* addr:input virtual address
* @brief:
* get the pageEntry of 'addr'
* @retval:
* a mutable PageEntry reference of 'addr'
*/
fn get_entry(&mut self, addr: usize) -> &mut PageEntry {
let page = Page::of_addr(VirtAddr::new(addr));
// ???
let _ = self.0.translate_page(page);
let entry_addr = ((addr >> 10) & 0x003ffffc) | (RECURSIVE_PAGE_PML4 << 22);
unsafe { &mut *(entry_addr as *mut PageEntry) }
}
/*
* @param:
* addr:the input (virutal) address
* @brief:
* get the addr's memory page slice
* @retval:
* a mutable reference slice of 'addr' 's page
*/
fn get_page_slice_mut<'a, 'b>(&'a mut self, addr: usize) -> &'b mut [u8] {
use core::slice;
unsafe { slice::from_raw_parts_mut((addr & !(PAGE_SIZE - 1)) as *mut u8, PAGE_SIZE) }
}
/*
* @param:
* addr: virtual address
* @brief:
* get the address's content
* @retval:
* the content(u8) of 'addr'
*/
fn read(&mut self, addr: usize) -> u8 {
unsafe { *(addr as *const u8) }
}
/*
* @param:
* addr: virtual address
* @brief:
* write the address's content
*/
fn write(&mut self, addr: usize, data: u8) {
unsafe { *(addr as *mut u8) = data; }
}
}
// define the ROOT_PAGE_TABLE, and the virtual address of it?
const ROOT_PAGE_TABLE: *mut RvPageTable =
(((RECURSIVE_PAGE_PML4 << 10) | (RECURSIVE_PAGE_PML4 + 1)) << 12) as *mut RvPageTable;

@ -1,11 +1,19 @@
use super::riscv::register::*;
use super::bbl::sbi;
/*
* @brief:
* get timer cycle for 64 bit cpu
*/
#[cfg(target_pointer_width = "64")]
pub fn get_cycle() -> u64 {
time::read() as u64
}
/*
* @brief:
* get timer cycle for 32 bit cpu
*/
#[cfg(target_pointer_width = "32")]
pub fn get_cycle() -> u64 {
loop {
@ -18,6 +26,10 @@ pub fn get_cycle() -> u64 {
}
}
/*
* @brief:
* enable supervisor timer interrupt and set next timer interrupt
*/
pub fn init() {
// Enable supervisor timer interrupt
unsafe { sie::set_stimer(); }
@ -26,12 +38,20 @@ pub fn init() {
info!("timer: init end");
}
/*
* @brief:
* set the next timer interrupt
*/
pub fn set_next() {
// 100Hz @ QEMU
let timebase = 250000;
set_timer(get_cycle() + timebase);
}
/*
* @brief:
* set time for timer interrupt
*/
fn set_timer(t: u64) {
#[cfg(feature = "no_bbl")]
unsafe {

@ -8,14 +8,18 @@ pub use self::x86_64::*;
pub const MAX_CPU_NUM: usize = 8;
pub const MAX_PROCESS_NUM: usize = 48;
// Memory address for riscv32
#[cfg(target_arch = "riscv32")]
mod riscv {
// Physical address available on THINPAD:
// [0x80000000, 0x80800000]
const P2_SIZE: usize = 1 << 22;
const P2_MASK: usize = 0x3ff << 22;
// RECURSIVE_PAGE_PML4 indicate the index of the self-maping entry in root pagetable
pub const RECURSIVE_PAGE_PML4: usize = 0x3fe;
// KERNEL_OFFSET indicate (virtual kernel address - physical kernel address) ???
pub const KERNEL_OFFSET: usize = 0;
// KERNEL_PML4 indicate the index of the kernel entry in root pagetable
pub const KERNEL_PML4: usize = 0x8000_0000 >> 22;
pub const KERNEL_HEAP_OFFSET: usize = 0x8020_0000;
pub const KERNEL_HEAP_SIZE: usize = 0x0020_0000;
@ -26,6 +30,7 @@ mod riscv {
pub const USER32_STACK_OFFSET: usize = USER_STACK_OFFSET;
}
// Memory address for x86_64
#[cfg(target_arch = "x86_64")]
mod x86_64 {
// Copy from Redox consts.rs:

@ -15,6 +15,7 @@ _binary_user_riscv_img_end:
"#);
pub fn shell() {
// load riscv32/x86_64 user program
#[cfg(target_arch = "riscv32")]
let device = {
extern {
@ -36,6 +37,7 @@ pub fn shell() {
const BUF_SIZE: usize = 0x40000;
let layout = Layout::from_size_align(BUF_SIZE, 0x1000).unwrap();
let buf = unsafe{ slice::from_raw_parts_mut(alloc(layout), BUF_SIZE) };
// start interaction
loop {
print!(">> ");
use console::get_line;

@ -61,16 +61,20 @@ pub mod arch;
pub mod arch;
pub fn kmain() -> ! {
// Init the first kernel process(idle proc)
process::init();
// enable the interrupt
unsafe { arch::interrupt::enable(); }
fs::shell();
// the test is not supported in riscv32(maybe)
//thread::test::local_key();
//thread::test::unpack();
//sync::test::philosopher_using_mutex();
//sync::test::philosopher_using_monitor();
//sync::mpsc::test::test_all();
// thread::test::local_key();
// thread::test::unpack();
// sync::test::philosopher_using_mutex();
// sync::test::philosopher_using_monitor();
// sync::mpsc::test::test_all();
// come into shell
fs::shell();
loop {}
}

@ -9,6 +9,10 @@ mod context;
type Processor = Processor_<Context, StrideScheduler>;
/*
* @brief:
* initialize a new kernel process (idleproc)
*/
pub fn init() {
PROCESSOR.call_once(||
SpinNoIrqLock::new({

@ -1,6 +1,10 @@
use process::*;
use arch::interrupt::TrapFrame;
/*
* @brief:
* process timer interrupt
*/
pub fn timer() {
let mut processor = processor();
processor.tick();
@ -12,6 +16,12 @@ pub fn before_return() {
}
}
/*
* @param:
* TrapFrame: the error's trapframe
* @brief:
* process the error trap, if processor inited then exit else panic!
*/
pub fn error(tf: &TrapFrame) -> ! {
if let Some(processor) = PROCESSOR.try() {
let mut processor = processor.lock();

@ -0,0 +1,4 @@
[[package]]
name = "ucore-ulib"
version = "0.1.0"
Loading…
Cancel
Save