mirror of
https://github.com/rcore-os/rCore-Tutorial-v3.git
synced 2024-11-25 10:56:25 +04:00
Merge recent patch on ch3.
This commit is contained in:
parent
9fa4fd6daa
commit
c19662b70c
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,6 +3,7 @@ os/target/*
|
||||
os/.idea/*
|
||||
os/Cargo.lock
|
||||
os/src/link_app.S
|
||||
os/last-*
|
||||
user/target/*
|
||||
user/.idea/*
|
||||
user/Cargo.lock
|
||||
|
12
README.md
12
README.md
@ -1,2 +1,12 @@
|
||||
# rCore-Tutorial-v3
|
||||
rCore-Tutorial version 3.
|
||||
rCore-Tutorial version 3.x
|
||||
|
||||
## Dependency
|
||||
|
||||
### Binaries
|
||||
|
||||
* rustc 1.56.0-nightly (b03ccace5 2021-08-24)
|
||||
|
||||
* qemu: 5.0.0
|
||||
|
||||
* rustsbi: qemu[7d71bfb7] k210[563144b0]
|
||||
|
13
os/Makefile
13
os/Makefile
@ -29,12 +29,19 @@ OBJCOPY := rust-objcopy --binary-architecture=riscv64
|
||||
# Disassembly
|
||||
DISASM ?= -x
|
||||
|
||||
build: env $(KERNEL_BIN)
|
||||
build: env switch-check $(KERNEL_BIN)
|
||||
|
||||
switch-check:
|
||||
ifeq ($(BOARD), qemu)
|
||||
(which last-qemu) || (rm last-k210 -f && touch last-qemu && make clean)
|
||||
else ifeq ($(BOARD), k210)
|
||||
(which last-k210) || (rm last-qemu -f && touch last-k210 && make clean)
|
||||
endif
|
||||
|
||||
env:
|
||||
rustup component add rust-src
|
||||
rustup component add llvm-tools-preview
|
||||
cargo install cargo-binutils --vers ~0.2
|
||||
cargo install cargo-binutils --vers =0.3.3
|
||||
rustup target add riscv64gc-unknown-none-elf
|
||||
|
||||
$(KERNEL_BIN): kernel
|
||||
@ -85,4 +92,4 @@ debug: build
|
||||
tmux split-window -h "riscv64-unknown-elf-gdb -ex 'file $(KERNEL_ELF)' -ex 'set arch riscv:rv64' -ex 'target remote localhost:1234'" && \
|
||||
tmux -2 attach-session -d
|
||||
|
||||
.PHONY: build env kernel clean disasm disasm-vim run-inner
|
||||
.PHONY: build env kernel clean disasm disasm-vim run-inner switch-check
|
||||
|
@ -1,13 +1,14 @@
|
||||
use crate::trap::TrapContext;
|
||||
use crate::task::TaskContext;
|
||||
use crate::config::*;
|
||||
|
||||
#[repr(align(4096))]
|
||||
#[derive(Copy, Clone)]
|
||||
struct KernelStack {
|
||||
data: [u8; KERNEL_STACK_SIZE],
|
||||
}
|
||||
|
||||
#[repr(align(4096))]
|
||||
#[derive(Copy, Clone)]
|
||||
struct UserStack {
|
||||
data: [u8; USER_STACK_SIZE],
|
||||
}
|
||||
@ -26,14 +27,10 @@ impl KernelStack {
|
||||
fn get_sp(&self) -> usize {
|
||||
self.data.as_ptr() as usize + KERNEL_STACK_SIZE
|
||||
}
|
||||
pub fn push_context(&self, trap_cx: TrapContext, task_cx: TaskContext) -> &'static mut TaskContext {
|
||||
unsafe {
|
||||
let trap_cx_ptr = (self.get_sp() - core::mem::size_of::<TrapContext>()) as *mut TrapContext;
|
||||
*trap_cx_ptr = trap_cx;
|
||||
let task_cx_ptr = (trap_cx_ptr as usize - core::mem::size_of::<TaskContext>()) as *mut TaskContext;
|
||||
*task_cx_ptr = task_cx;
|
||||
task_cx_ptr.as_mut().unwrap()
|
||||
}
|
||||
pub fn push_context(&self, trap_cx: TrapContext) -> usize {
|
||||
let trap_cx_ptr = (self.get_sp() - core::mem::size_of::<TrapContext>()) as *mut TrapContext;
|
||||
unsafe { *trap_cx_ptr = trap_cx; }
|
||||
trap_cx_ptr as usize
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +57,7 @@ pub fn load_apps() {
|
||||
core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1)
|
||||
};
|
||||
// clear i-cache first
|
||||
unsafe { llvm_asm!("fence.i" :::: "volatile"); }
|
||||
unsafe { asm!("fence.i"); }
|
||||
// load apps
|
||||
for i in 0..num_app {
|
||||
let base_i = get_base_i(i);
|
||||
@ -79,9 +76,8 @@ pub fn load_apps() {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init_app_cx(app_id: usize) -> &'static TaskContext {
|
||||
pub fn init_app_cx(app_id: usize) -> usize {
|
||||
KERNEL_STACK[app_id].push_context(
|
||||
TrapContext::app_init_context(get_base_i(app_id), USER_STACK[app_id].get_sp()),
|
||||
TaskContext::goto_restore(),
|
||||
)
|
||||
}
|
||||
|
@ -1,9 +1,8 @@
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
#![feature(global_asm)]
|
||||
#![feature(llvm_asm)]
|
||||
#![feature(asm)]
|
||||
#![feature(panic_info_message)]
|
||||
#![feature(const_in_array_repeat_expressions)]
|
||||
|
||||
#[macro_use]
|
||||
mod console;
|
||||
@ -14,6 +13,7 @@ mod trap;
|
||||
mod loader;
|
||||
mod config;
|
||||
mod task;
|
||||
mod sync;
|
||||
|
||||
global_asm!(include_str!("entry.asm"));
|
||||
global_asm!(include_str!("link_app.S"));
|
||||
@ -23,9 +23,12 @@ fn clear_bss() {
|
||||
fn sbss();
|
||||
fn ebss();
|
||||
}
|
||||
(sbss as usize..ebss as usize).for_each(|a| {
|
||||
unsafe { (a as *mut u8).write_volatile(0) }
|
||||
});
|
||||
unsafe {
|
||||
core::slice::from_raw_parts_mut(
|
||||
sbss as usize as *mut u8,
|
||||
ebss as usize - sbss as usize,
|
||||
).fill(0);
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -36,4 +39,4 @@ pub fn rust_main() -> ! {
|
||||
loader::load_apps();
|
||||
task::run_first_task();
|
||||
panic!("Unreachable in rust_main!");
|
||||
}
|
||||
}
|
||||
|
@ -14,11 +14,12 @@ const SBI_SHUTDOWN: usize = 8;
|
||||
fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
|
||||
let mut ret;
|
||||
unsafe {
|
||||
llvm_asm!("ecall"
|
||||
: "={x10}" (ret)
|
||||
: "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (which)
|
||||
: "memory"
|
||||
: "volatile"
|
||||
asm!(
|
||||
"ecall",
|
||||
inlateout("x10") arg0 => ret,
|
||||
in("x11") arg1,
|
||||
in("x12") arg2,
|
||||
in("x17") which,
|
||||
);
|
||||
}
|
||||
ret
|
||||
|
3
os/src/sync/mod.rs
Normal file
3
os/src/sync/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod up;
|
||||
|
||||
pub use up::UPSafeCell;
|
27
os/src/sync/up.rs
Normal file
27
os/src/sync/up.rs
Normal file
@ -0,0 +1,27 @@
|
||||
use core::cell::{RefCell, RefMut};
|
||||
|
||||
/// Wrap a static data structure inside it so that we are
|
||||
/// able to access it without any `unsafe`.
|
||||
///
|
||||
/// We should only use it in uniprocessor.
|
||||
///
|
||||
/// In order to get mutable reference of inner data, call
|
||||
/// `exclusive_access`.
|
||||
pub struct UPSafeCell<T> {
|
||||
/// inner data
|
||||
inner: RefCell<T>,
|
||||
}
|
||||
|
||||
unsafe impl<T> Sync for UPSafeCell<T> {}
|
||||
|
||||
impl<T> UPSafeCell<T> {
|
||||
/// User is responsible to guarantee that inner struct is only used in
|
||||
/// uniprocessor.
|
||||
pub unsafe fn new(value: T) -> Self {
|
||||
Self { inner: RefCell::new(value) }
|
||||
}
|
||||
/// Panic if the data has been borrowed.
|
||||
pub fn exclusive_access(&self) -> RefMut<'_, T> {
|
||||
self.inner.borrow_mut()
|
||||
}
|
||||
}
|
@ -1,14 +1,24 @@
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct TaskContext {
|
||||
ra: usize,
|
||||
sp: usize,
|
||||
s: [usize; 12],
|
||||
}
|
||||
|
||||
impl TaskContext {
|
||||
pub fn goto_restore() -> Self {
|
||||
pub fn zero_init() -> Self {
|
||||
Self {
|
||||
ra: 0,
|
||||
sp: 0,
|
||||
s: [0; 12],
|
||||
}
|
||||
}
|
||||
pub fn goto_restore(kstack_ptr: usize) -> Self {
|
||||
extern "C" { fn __restore(); }
|
||||
Self {
|
||||
ra: __restore as usize,
|
||||
sp: kstack_ptr,
|
||||
s: [0; 12],
|
||||
}
|
||||
}
|
||||
|
@ -4,16 +4,16 @@ mod task;
|
||||
|
||||
use crate::config::MAX_APP_NUM;
|
||||
use crate::loader::{get_num_app, init_app_cx};
|
||||
use core::cell::RefCell;
|
||||
use lazy_static::*;
|
||||
use switch::__switch;
|
||||
use task::{TaskControlBlock, TaskStatus};
|
||||
use crate::sync::UPSafeCell;
|
||||
|
||||
pub use context::TaskContext;
|
||||
|
||||
pub struct TaskManager {
|
||||
num_app: usize,
|
||||
inner: RefCell<TaskManagerInner>,
|
||||
inner: UPSafeCell<TaskManagerInner>,
|
||||
}
|
||||
|
||||
struct TaskManagerInner {
|
||||
@ -21,56 +21,62 @@ struct TaskManagerInner {
|
||||
current_task: usize,
|
||||
}
|
||||
|
||||
unsafe impl Sync for TaskManager {}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref TASK_MANAGER: TaskManager = {
|
||||
let num_app = get_num_app();
|
||||
let mut tasks = [
|
||||
TaskControlBlock { task_cx_ptr: 0, task_status: TaskStatus::UnInit };
|
||||
TaskControlBlock {
|
||||
task_cx: TaskContext::zero_init(),
|
||||
task_status: TaskStatus::UnInit
|
||||
};
|
||||
MAX_APP_NUM
|
||||
];
|
||||
for i in 0..num_app {
|
||||
tasks[i].task_cx_ptr = init_app_cx(i) as * const _ as usize;
|
||||
tasks[i].task_cx = TaskContext::goto_restore(init_app_cx(i));
|
||||
tasks[i].task_status = TaskStatus::Ready;
|
||||
}
|
||||
TaskManager {
|
||||
num_app,
|
||||
inner: RefCell::new(TaskManagerInner {
|
||||
inner: unsafe { UPSafeCell::new(TaskManagerInner {
|
||||
tasks,
|
||||
current_task: 0,
|
||||
}),
|
||||
})},
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl TaskManager {
|
||||
fn run_first_task(&self) {
|
||||
self.inner.borrow_mut().tasks[0].task_status = TaskStatus::Running;
|
||||
let next_task_cx_ptr2 = self.inner.borrow().tasks[0].get_task_cx_ptr2();
|
||||
let _unused: usize = 0;
|
||||
fn run_first_task(&self) -> ! {
|
||||
let mut inner = self.inner.exclusive_access();
|
||||
let task0 = &mut inner.tasks[0];
|
||||
task0.task_status = TaskStatus::Running;
|
||||
let next_task_cx_ptr = &task0.task_cx as *const TaskContext;
|
||||
drop(inner);
|
||||
let mut _unused = TaskContext::zero_init();
|
||||
// before this, we should drop local variables that must be dropped manually
|
||||
unsafe {
|
||||
__switch(
|
||||
&_unused as *const _,
|
||||
next_task_cx_ptr2,
|
||||
&mut _unused as *mut TaskContext,
|
||||
next_task_cx_ptr,
|
||||
);
|
||||
}
|
||||
panic!("unreachable in run_first_task!");
|
||||
}
|
||||
|
||||
fn mark_current_suspended(&self) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let mut inner = self.inner.exclusive_access();
|
||||
let current = inner.current_task;
|
||||
inner.tasks[current].task_status = TaskStatus::Ready;
|
||||
}
|
||||
|
||||
fn mark_current_exited(&self) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let mut inner = self.inner.exclusive_access();
|
||||
let current = inner.current_task;
|
||||
inner.tasks[current].task_status = TaskStatus::Exited;
|
||||
}
|
||||
|
||||
fn find_next_task(&self) -> Option<usize> {
|
||||
let inner = self.inner.borrow();
|
||||
let inner = self.inner.exclusive_access();
|
||||
let current = inner.current_task;
|
||||
(current + 1..current + self.num_app + 1)
|
||||
.map(|id| id % self.num_app)
|
||||
@ -81,19 +87,21 @@ impl TaskManager {
|
||||
|
||||
fn run_next_task(&self) {
|
||||
if let Some(next) = self.find_next_task() {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let mut inner = self.inner.exclusive_access();
|
||||
let current = inner.current_task;
|
||||
inner.tasks[next].task_status = TaskStatus::Running;
|
||||
inner.current_task = next;
|
||||
let current_task_cx_ptr2 = inner.tasks[current].get_task_cx_ptr2();
|
||||
let next_task_cx_ptr2 = inner.tasks[next].get_task_cx_ptr2();
|
||||
core::mem::drop(inner);
|
||||
let current_task_cx_ptr = &mut inner.tasks[current].task_cx as *mut TaskContext;
|
||||
let next_task_cx_ptr = &inner.tasks[next].task_cx as *const TaskContext;
|
||||
drop(inner);
|
||||
// before this, we should drop local variables that must be dropped manually
|
||||
unsafe {
|
||||
__switch(
|
||||
current_task_cx_ptr2,
|
||||
next_task_cx_ptr2,
|
||||
current_task_cx_ptr,
|
||||
next_task_cx_ptr,
|
||||
);
|
||||
}
|
||||
// go back to user mode
|
||||
} else {
|
||||
panic!("All applications completed!");
|
||||
}
|
||||
|
@ -1,37 +1,34 @@
|
||||
.altmacro
|
||||
.macro SAVE_SN n
|
||||
sd s\n, (\n+1)*8(sp)
|
||||
sd s\n, (\n+2)*8(a0)
|
||||
.endm
|
||||
.macro LOAD_SN n
|
||||
ld s\n, (\n+1)*8(sp)
|
||||
ld s\n, (\n+2)*8(a1)
|
||||
.endm
|
||||
.section .text
|
||||
.globl __switch
|
||||
__switch:
|
||||
# __switch(
|
||||
# current_task_cx_ptr2: &*const TaskContext,
|
||||
# next_task_cx_ptr2: &*const TaskContext
|
||||
# current_task_cx_ptr: *mut TaskContext,
|
||||
# next_task_cx_ptr: *const TaskContext
|
||||
# )
|
||||
# push TaskContext to current sp and save its address to where a0 points to
|
||||
addi sp, sp, -13*8
|
||||
sd sp, 0(a0)
|
||||
# fill TaskContext with ra & s0-s11
|
||||
sd ra, 0(sp)
|
||||
# save kernel stack of current task
|
||||
sd sp, 8(a0)
|
||||
# save ra & s0~s11 of current execution
|
||||
sd ra, 0(a0)
|
||||
.set n, 0
|
||||
.rept 12
|
||||
SAVE_SN %n
|
||||
.set n, n + 1
|
||||
.endr
|
||||
# ready for loading TaskContext a1 points to
|
||||
ld sp, 0(a1)
|
||||
# load registers in the TaskContext
|
||||
ld ra, 0(sp)
|
||||
# restore ra & s0~s11 of next execution
|
||||
ld ra, 0(a1)
|
||||
.set n, 0
|
||||
.rept 12
|
||||
LOAD_SN %n
|
||||
.set n, n + 1
|
||||
.endr
|
||||
# pop TaskContext
|
||||
addi sp, sp, 13*8
|
||||
# restore kernel stack of next task
|
||||
ld sp, 8(a1)
|
||||
ret
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
global_asm!(include_str!("switch.S"));
|
||||
|
||||
use super::TaskContext;
|
||||
|
||||
extern "C" {
|
||||
pub fn __switch(
|
||||
current_task_cx_ptr2: *const usize,
|
||||
next_task_cx_ptr2: *const usize
|
||||
current_task_cx_ptr: *mut TaskContext,
|
||||
next_task_cx_ptr: *const TaskContext
|
||||
);
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
pub struct TaskControlBlock {
|
||||
pub task_cx_ptr: usize,
|
||||
pub task_status: TaskStatus,
|
||||
}
|
||||
use super::TaskContext;
|
||||
|
||||
impl TaskControlBlock {
|
||||
pub fn get_task_cx_ptr2(&self) -> *const usize {
|
||||
&self.task_cx_ptr as *const usize
|
||||
}
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct TaskControlBlock {
|
||||
pub task_status: TaskStatus,
|
||||
pub task_cx: TaskContext,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq)]
|
||||
|
@ -1 +1 @@
|
||||
nightly-2021-01-30
|
||||
nightly-2021-10-15
|
||||
|
@ -1,5 +1,5 @@
|
||||
#![no_std]
|
||||
#![feature(llvm_asm)]
|
||||
#![feature(asm)]
|
||||
#![feature(linkage)]
|
||||
#![feature(panic_info_message)]
|
||||
|
||||
|
@ -5,11 +5,12 @@ const SYSCALL_YIELD: usize = 124;
|
||||
fn syscall(id: usize, args: [usize; 3]) -> isize {
|
||||
let mut ret: isize;
|
||||
unsafe {
|
||||
llvm_asm!("ecall"
|
||||
: "={x10}" (ret)
|
||||
: "{x10}" (args[0]), "{x11}" (args[1]), "{x12}" (args[2]), "{x17}" (id)
|
||||
: "memory"
|
||||
: "volatile"
|
||||
asm!(
|
||||
"ecall",
|
||||
inlateout("x10") args[0] => ret,
|
||||
in("x11") args[1],
|
||||
in("x12") args[2],
|
||||
in("x17") id
|
||||
);
|
||||
}
|
||||
ret
|
||||
|
Loading…
Reference in New Issue
Block a user