添加地址空间, "伤齿龙"现在任务可以正常切换了,

ch4
zhangxinyu 2 years ago
parent d543a15f18
commit 6d9e06efa6

@ -15,3 +15,4 @@ lazy_static = { version = "1.4.0", features = ["spin_no_std"] }
sbi-rt = { version = "0.0.2", features = ["legacy"] } sbi-rt = { version = "0.0.2", features = ["legacy"] }
buddy_system_allocator = "0.6" buddy_system_allocator = "0.6"
bitflags = "1.2.1" bitflags = "1.2.1"
xmas-elf = "0.7.0"

@ -43,7 +43,7 @@ run:build_elf $(KERNEL_BIN) $(SYMBOL_MAP) kill
-device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY) -device loader,file=$(KERNEL_BIN),addr=$(KERNEL_ENTRY)
clean: clean:
#rm -rf ./target* && rm -rf ./src/link_app.S # rm -rf ./target* && rm -rf ./src/link_app.S
kill: kill:
-pkill -9 qemu -pkill -9 qemu

@ -61,7 +61,7 @@ _num_app:
.global app_{0}_start .global app_{0}_start
.global app_{0}_end .global app_{0}_end
app_{0}_start: app_{0}_start:
.incbin "{2}{1}.bin" .incbin "{2}{1}"
app_{0}_end:"#, app_{0}_end:"#,
idx, app, TARGET_PATH idx, app, TARGET_PATH
)?; )?;

@ -17,79 +17,18 @@ pub fn get_num_app() -> usize{
} }
} }
// 把 app一次性都加载到内存指定位置 pub fn get_app_data(app_id: usize) -> &'static [u8] {
pub fn load_app() { extern "C" {
// 得到符号位 fn _num_app();
}
let num_app_ptr = _num_app as usize as *const usize; let num_app_ptr = _num_app as usize as *const usize;
// 得到 符号开始的前8个字节, 这里保存的app的数量
let num_app = get_num_app(); let num_app = get_num_app();
let app_start = unsafe { core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1) };
// 得到 app数组的起始位置 num_app_ptr.add(1)跳过上方的 metadata, num_app+1 是确保切免得长度足够, 因为后面还有一个符号在linker.ld中 .quad app_2_end 表示结束的内存地址 assert!(app_id < num_app);
let app_start = unsafe {
core::slice::from_raw_parts(num_app_ptr.add(1), num_app+1)
};
// 清除缓存
unsafe { asm!("fence.i"); }
// 加载app
for app_id in 0..num_app {
// 得到每个app的起始位置
let base_ptr = APP_BASE_ADDRESS + app_id * APP_SIZE_LIMIT;
// 清理这个应用可以占用的内存(好像可以不用做吧? 下面直接dst.copy_from_slice全部覆盖了)
(base_ptr..base_ptr + APP_SIZE_LIMIT).for_each(|addr| unsafe {
(addr as *mut u8).write_volatile(0)
});
// 加载 app_start 处二进制数据到内存里
let src = unsafe {
let app_size = app_start[app_id + 1] - app_start[app_id]; // 下一个app的起始位置, 减去当前的起始位置就是app的大小
core::slice::from_raw_parts(app_start[app_id] as *const u8, app_size)
};
// 得到app占用的内存, 后面需要把app 二进制数据加载到这里
let dst = unsafe {
core::slice::from_raw_parts_mut(base_ptr as *mut u8, src.len())
};
dst.copy_from_slice(src);
}
}
// 初始化用户应用栈, 并返回用户应用栈在内核栈中的trap_context的地址
// 这个函数根据 app_id得到 USER_STACK的所在的位置, 初始化之后, 再构造 KERNEL_STACK
pub fn init_app_cx(app_id: usize) -> usize {
// 得到 用户栈
unsafe { unsafe {
// 构造 trap_context core::slice::from_raw_parts(
let user_trap_context = { app_start[app_id] as *const u8,
// 初始化用户栈顶 app_start[app_id + 1] - app_start[app_id],
let user_stack_top = { )
let _tmp_stack = USER_STACK.as_ptr() as usize + USER_STACK_SIZE * app_id; // 用户栈开始 + (每个用户栈的大小 * 指定用户栈的索引) 得到栈顶位置,
_tmp_stack + USER_STACK_SIZE // 加上栈大小, 得到新的栈顶位置
};
// 用户应用的开始位置
let user_entry = APP_BASE_ADDRESS + APP_SIZE_LIMIT * app_id;
TrapContext::from(user_entry, user_stack_top)
};
// 把构造的 user_trap_context 放到 KERNEL_STACK指定应用的内核栈中
let kernel_app_stack_ptr = {
// 栈顶位置 + 栈大小 = 栈底
let _tme_stack_low = KERNEL_STACK.as_ptr() as usize + KERNEL_STACK_SIZE * app_id + KERNEL_STACK_SIZE;
// 给TrapContext 分配栈空间, 得到新的栈顶
(_tme_stack_low - core::mem::size_of::<TrapContext>()) as * mut TrapContext
};
// copy 数据到内核栈中
*kernel_app_stack_ptr = user_trap_context;
// 返回内核栈的地址
kernel_app_stack_ptr as usize
} }
} }

@ -50,12 +50,14 @@ pub fn rust_main(){
// 初始化动态内存分配器, 使我们能在内核中使用动态大小数据类型 // 初始化动态内存分配器, 使我们能在内核中使用动态大小数据类型
mm::init(); mm::init();
println!("main 0");
trap::init(); trap::init();
loader::load_app(); println!("main 1");
trap::enable_timer_interrupt(); // 允许定时器中断 trap::enable_timer_interrupt(); // 允许定时器中断
println!("main 2");
timer::set_next_trigger(); // 在进入用户态之前, 设置一个时钟中断, 防止第一个用户任务死循环 timer::set_next_trigger(); // 在进入用户态之前, 设置一个时钟中断, 防止第一个用户任务死循环
// task::run_first_task(); println!("main 3");
task::run_first_task();
panic!("Disable run here") panic!("Disable run here")
} }

@ -126,7 +126,7 @@ impl VirtAddr{
// 把usize 转为 虚拟地址, 只保留39位 // 把usize 转为 虚拟地址, 只保留39位
impl From<usize> for VirtAddr { impl From<usize> for VirtAddr {
fn from(value: usize) -> Self { fn from(value: usize) -> Self {
Self(value & ((1 << PPN_WIDTH_SV39) - 1 )) Self(value & ((1 << VA_WIDTH_SV39) - 1))
} }
} }

@ -5,8 +5,9 @@ use core::arch::asm;
use bitflags::bitflags; use bitflags::bitflags;
use lazy_static::lazy_static; use lazy_static::lazy_static;
use riscv::register::satp; use riscv::register::satp;
use xmas_elf;
use crate::sync::UPSafeCell; use crate::sync::UPSafeCell;
use crate::config::{MEMORY_END, PAGE_SIZE, TRAMPOLINE}; use crate::config::{MEMORY_END, PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT, USER_STACK_SIZE};
use crate::mm::address::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum, VPNRange}; use crate::mm::address::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum, VPNRange};
use crate::mm::frame_allocator::{frame_alloc, FrameTracker}; use crate::mm::frame_allocator::{frame_alloc, FrameTracker};
use crate::mm::page_table::{PageTable, PTEFlags}; use crate::mm::page_table::{PageTable, PTEFlags};
@ -65,7 +66,7 @@ impl MapArea {
// 截取抛弃内存碎片 // 截取抛弃内存碎片
let start_vpn: VirtPageNum = start_va.floor(); let start_vpn: VirtPageNum = start_va.floor();
let end_vpn: VirtPageNum = end_va.ceil(); let end_vpn: VirtPageNum = end_va.ceil();
println!("start va: {} start vpn {}", start_va.0, start_vpn.0);
Self { Self {
vpn_range: VPNRange {l:start_vpn, r: end_vpn}, vpn_range: VPNRange {l:start_vpn, r: end_vpn},
data_frames: BTreeMap::new(), data_frames: BTreeMap::new(),
@ -110,7 +111,9 @@ impl MapArea {
// 映射 当前逻辑段中的每一个虚拟页, 到页表中 // 映射 当前逻辑段中的每一个虚拟页, 到页表中
pub fn map(&mut self, page_table: &mut PageTable) { pub fn map(&mut self, page_table: &mut PageTable) {
println!("self.vpn_range.get_start {} {}", self.vpn_range.l.0, self.vpn_range.r.0);
for vpn in self.vpn_range { for vpn in self.vpn_range {
println!("range vpn :{}", vpn.0);
self.map_one(page_table, vpn); self.map_one(page_table, vpn);
} }
} }
@ -155,8 +158,8 @@ impl MapArea {
// 表示一个地址空间, 它是一个页表和 包含所有段信息的页表组成 // 表示一个地址空间, 它是一个页表和 包含所有段信息的页表组成
pub struct MemorySet { pub struct MemorySet {
page_table: PageTable, pub page_table: PageTable,
areas: Vec<MapArea>, pub areas: Vec<MapArea>,
} }
impl MemorySet { impl MemorySet {
@ -166,6 +169,114 @@ impl MemorySet {
areas: Vec::new(), areas: Vec::new(),
} }
} }
pub fn from_elf(elf_data: &[u8]) -> (Self, usize, usize) {
let mut memory_set = Self::new();
// map trampoline
// 创建一个地址空间
memory_set.map_trampoline();
// map program headers of elf, with U flag
// 分析外部传进来的 elf文件数据
println!("enf len: {}", elf_data.len());
let elf = xmas_elf::ElfFile::new(elf_data).unwrap();
let elf_header = elf.header;
let magic = elf_header.pt1.magic;
assert_eq!(magic, [0x7f, 0x45, 0x4c, 0x46], "invalid elf!");
let ph_count = elf_header.pt2.ph_count();
// 得到所有的段数量
let mut max_end_vpn = VirtPageNum(0);
// 循环所有的段
for i in 0..ph_count {
// 得到当前段
let ph = elf.program_header(i).unwrap();
// 确定有加载的必要
if ph.get_type().unwrap() == xmas_elf::program::Type::Load {
// 当前段开始位置
let start_va: VirtAddr = (ph.virtual_addr() as usize).into();
// 当前段结束位置 (开始位置+大小)
let end_va: VirtAddr = ((ph.virtual_addr() + ph.mem_size()) as usize).into();
println!("start_va {} end_va {}", start_va.0, end_va.0);
// 当前段权限
let mut map_perm = MapPermission::U;
let ph_flags = ph.flags();
if ph_flags.is_read() {
map_perm |= MapPermission::R;
}
if ph_flags.is_write() {
map_perm |= MapPermission::W;
}
if ph_flags.is_execute() {
map_perm |= MapPermission::X;
}
// 创建一个 逻辑段
let map_area = MapArea::from(start_va, end_va, MapType::Framed, map_perm);
// 每次都更新最后的逻辑段的最后结束最大的逻辑地址
max_end_vpn = map_area.vpn_range.r;
// 当前逻辑段, 添加到地址空间, 并copy数据进去
memory_set.push(
map_area,
Some(&elf.input[ph.offset() as usize..(ph.offset() + ph.file_size()) as usize]),
);
}
}
// map user stack with U flags
// 将最后一个最大的逻辑段的结束的页, 转为逻辑地址 左移12位 得到39位的VirtAddr
let max_end_va: VirtAddr = max_end_vpn.into();
// 根据riscv 的规则, 转成一个 合法的地址 (todo 为什么这里需要转成合法的地址:将高位全部设置为1, 上面的start_va 不转就传?, 而且不直接可以使用end_va 吗? 这里还创建一个max_end_vpn?)
let mut user_stack_bottom: usize = max_end_va.into();
// guard page 放置 一页 保护页隔离用户栈
user_stack_bottom += PAGE_SIZE;
// 保护页之后 再增加 用户栈大小,得到栈底和当前的栈顶
// 此时 user_stack_top 距离 max_end_vpn 三个页
let user_stack_top = user_stack_bottom + USER_STACK_SIZE;
// 添加 用户逻辑栈段并映射物理地址页, 到当前的地址空间
memory_set.push(
MapArea::from(
user_stack_bottom.into(),
user_stack_top.into(),
MapType::Framed,
MapPermission::R | MapPermission::W | MapPermission::U,
),
None,
);
// used in sbrk
memory_set.push(
MapArea::from(
user_stack_top.into(),
user_stack_top.into(),
MapType::Framed,
MapPermission::R | MapPermission::W | MapPermission::U,
),
None,
);
// 设置 TrapContext为统一的虚拟地址
// 将 虚拟地址的 次高页添加到用户空间, 后面会被内核强行找到物理地址并修改为 trap context
println!("TRAP_CONTEXT: {}, TRAMPOLINE: {}", TRAP_CONTEXT, TRAMPOLINE);
memory_set.push(
MapArea::from(
TRAP_CONTEXT.into(),
TRAMPOLINE.into(),
MapType::Framed,
MapPermission::R | MapPermission::W,
),
None,
);
println!(3);
(
memory_set, // 地址空间
user_stack_top, // 用户栈底和当前的栈顶, 他在地址空间各个逻辑段的上面, 同时
elf.header.pt2.entry_point() as usize, // elf文件的入口地址
)
}
} }
impl MemorySet { impl MemorySet {

@ -1,4 +1,6 @@
use crate::mm::address::VirtAddr;
use crate::mm::memory_set::KERNEL_SPACE; use crate::mm::memory_set::KERNEL_SPACE;
use crate::{etext, println, stext};
pub mod heap_allocator; pub mod heap_allocator;
pub mod address; pub mod address;
@ -15,4 +17,43 @@ pub fn init(){
frame_allocator::frame_allocator_test(); frame_allocator::frame_allocator_test();
// 激活内核的地址空间 // 激活内核的地址空间
KERNEL_SPACE.exclusive_access().activate(); KERNEL_SPACE.exclusive_access().activate();
remap_test();
}
pub fn remap_test() {
extern "C" {
fn stext();
fn etext();
fn srodata();
fn erodata();
fn sdata();
fn edata();
fn sbss_with_stack();
fn ebss();
fn ekernel();
fn strampoline(); // 这个符号 在linker.ld 中定义
}
let mut kernel_space = KERNEL_SPACE.exclusive_access();
let mid_text: VirtAddr = ((stext as usize + etext as usize) / 2).into();
let mid_rodata: VirtAddr = ((srodata as usize + erodata as usize) / 2).into();
let mid_data: VirtAddr = ((sdata as usize + edata as usize) / 2).into();
assert!(!kernel_space
.page_table
.get_pte(mid_text.floor())
.unwrap()
.writable(),);
assert!(!kernel_space
.page_table
.get_pte(mid_rodata.floor())
.unwrap()
.writable(),);
assert!(!kernel_space
.page_table
.get_pte(mid_data.floor())
.unwrap()
.executable(),);
println!("remap_test passed!");
} }

@ -1,8 +1,11 @@
use alloc::vec; use alloc::vec;
use alloc::vec::Vec; use alloc::vec::Vec;
use bitflags::*; use bitflags::*;
use crate::config::TRAMPOLINE;
use crate::mm::address::{PhysPageNum, VirtAddr, VirtPageNum}; use crate::mm::address::{PhysPageNum, VirtAddr, VirtPageNum};
use crate::mm::frame_allocator::{frame_alloc, FrameTracker}; use crate::mm::frame_allocator::{frame_alloc, FrameTracker};
use crate::println;
use crate::task::TASK_MANAGER;
bitflags! { bitflags! {
@ -155,6 +158,9 @@ impl PageTable {
pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) { pub fn map(&mut self, vpn: VirtPageNum, ppn: PhysPageNum, flags: PTEFlags) {
// 查找vpn 没有中间节点就创建出来 // 查找vpn 没有中间节点就创建出来
let pte = self.find_pte_create(vpn).unwrap(); let pte = self.find_pte_create(vpn).unwrap();
// let a = self.get_pte(vpn).unwrap().ppn().0;
println!("is_valid: {}", pte.is_valid());
println!("vpn: {:?} pte.ppn:{}", vpn.0, pte.ppn().0);
assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn.0); assert!(!pte.is_valid(), "vpn {:?} is mapped before mapping", vpn.0);
*pte = PageTableEntry::from(ppn, flags | PTEFlags::V); *pte = PageTableEntry::from(ppn, flags | PTEFlags::V);
} }

@ -8,9 +8,9 @@ const FD_STDOUT: usize = 1;
pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize { pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
match fd { match fd {
FD_STDOUT => { FD_STDOUT => {
let slice = unsafe { core::slice::from_raw_parts(buf, len) }; // let slice = unsafe { core::slice::from_raw_parts(buf, len) };
let str = core::str::from_utf8(slice).unwrap(); // let str = core::str::from_utf8(slice).unwrap();
print!("{}", str); // print!("{}", str);
len as isize len as isize
} }
_ => { _ => {

@ -8,9 +8,12 @@ mod process;
use fs::*; use fs::*;
use process::*; use process::*;
use crate::println;
/// 根据syscall_id 进行分发 /// 根据syscall_id 进行分发
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize { pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
println!("syscall_id......{} {:?}", syscall_id, args);
match syscall_id { match syscall_id {
SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]), SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]),
SYSCALL_EXIT => sys_exit(args[0] as i32), SYSCALL_EXIT => sys_exit(args[0] as i32),

@ -1,4 +1,4 @@
use crate::trap::trap_return;
// TCB的字段 用来保存cpu 在内核切换人物的时候 寄存器还有栈顶的信息, 这个结构体将来会传到 switch.S 中的汇编中 // TCB的字段 用来保存cpu 在内核切换人物的时候 寄存器还有栈顶的信息, 这个结构体将来会传到 switch.S 中的汇编中
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
@ -32,4 +32,13 @@ impl TaskContext{
s: [0; 12], s: [0; 12],
} }
} }
/// 根据用户当前内核栈做的栈顶 构造一个 任务上下文, 并把 trap_return 设置为 switch切换 ret的返回地址
pub fn goto_trap_return(kstack_ptr: usize) -> Self {
Self {
ra: trap_return as usize, // 这里是 在内核空间的 函数地址 (当然 task context 只有在内核态才是可见的)
sp: kstack_ptr,
s: [0; 12],
}
}
} }

@ -1,12 +1,14 @@
use lazy_static::lazy_static; use lazy_static::lazy_static;
use alloc::vec::Vec;
use crate::config::MAX_APP_NUM; use crate::config::MAX_APP_NUM;
use crate::sync::UPSafeCell; use crate::sync::UPSafeCell;
use crate::task::task::{TaskControlBlock, TaskStatus}; use crate::task::task::{TaskControlBlock, TaskStatus};
use crate::loader::{get_num_app, init_app_cx}; use crate::loader::{get_num_app, get_app_data};
use crate::println; use crate::println;
use crate::task::context::TaskContext; use crate::task::context::TaskContext;
use crate::task::switch::__switch; use crate::task::switch::__switch;
use crate::timer::{get_time_ms, get_time_us}; use crate::timer::{get_time_ms, get_time_us};
use crate::trap::TrapContext;
mod context; mod context;
mod switch; mod switch;
@ -57,6 +59,7 @@ impl TaskManager {
// 不会走到这里了 // 不会走到这里了
let mut unused = TaskContext::new(); let mut unused = TaskContext::new();
println!("start run app");
unsafe { unsafe {
__switch(&mut unused as *mut TaskContext, next_task_context_ptr) __switch(&mut unused as *mut TaskContext, next_task_context_ptr)
} }
@ -136,12 +139,24 @@ impl TaskManager {
} }
None None
} }
// 得到当前正在执行用户应用的 trap context
pub fn get_current_trap_cx(&self) -> &'static mut TrapContext {
let inner = self.inner.exclusive_access();
inner.tasks[inner.current_task_id].get_trap_cx()
}
// 得到当前正在只用的用户应用的 页表
pub fn get_current_token(&self) -> usize {
let inner = self.inner.exclusive_access();
inner.tasks[inner.current_task_id].get_user_token()
}
} }
// 不公开的结构体, 有一个MAX_APP_NUM大小的数组, 用来保存TCB, 和当前任务的的TCB的下标 // 不公开的结构体, 有一个MAX_APP_NUM大小的数组, 用来保存TCB, 和当前任务的的TCB的下标
struct TaskManagerInner { struct TaskManagerInner {
tasks: [TaskControlBlock; MAX_APP_NUM], tasks: Vec<TaskControlBlock>,
current_task_id: usize, current_task_id: usize,
stop_clock_time: usize, // 记录最近一次停表时间 stop_clock_time: usize, // 记录最近一次停表时间
} }
@ -157,47 +172,29 @@ impl TaskManagerInner {
} }
} }
lazy_static!{ lazy_static! {
pub static ref TASK_MANAGER: TaskManager = { pub static ref TASK_MANAGER: TaskManager = {
// 得到app的总数 println!("init TASK_MANAGER");
let num_app = get_num_app(); let num_app = get_num_app();
println!("num_app = {}", num_app);
// 初始化内核中的任务列表 let mut tasks: Vec<TaskControlBlock> = Vec::new();
let mut tasks = [
TaskControlBlock { for i in 0..num_app {
task_cx: TaskContext::new(), // 得到elf 的二进制数据
// UnInit 这个tcb 还没用户应用填充加载 let app_elf_data = get_app_data(i);
task_status: TaskStatus::UnInit, // 根据二进制数据创建 TCB
kernel_time: 0, let tcb = TaskControlBlock::from(app_elf_data, i);
user_time: 0, tasks.push(tcb);
}; }
MAX_APP_NUM TaskManager {
];
// 初始化用户的应用对应的 TCB
for app_idx in 0..num_app {
// 初始化用户应用的内核栈
let kernel_app_stack_ptr = init_app_cx(app_idx);
// 从 初始内核栈的栈顶(保存 trap_context的位置) 创建 任务context
let task_context = TaskContext::from(kernel_app_stack_ptr);
// 把内核栈的信息保存到 TCB中
tasks[app_idx].task_cx = task_context;
// 初始为 准备好的状态
tasks[app_idx].task_status = TaskStatus::Ready
};
TaskManager { // 直接返回即可, 所有字段都是sync的, 那这个结构体也是sync
num_app, num_app,
inner: unsafe { inner: unsafe {
UPSafeCell::new(TaskManagerInner { UPSafeCell::new(TaskManagerInner {
tasks, tasks,
current_task_id: 0, stop_clock_time: 0,
stop_clock_time: 0 current_task_id: 0, // 从第0个启动
}) })
} },
} }
}; };
} }
@ -219,3 +216,9 @@ pub fn exit_current_and_run_next(){
TASK_MANAGER.mark_current_exit(); TASK_MANAGER.mark_current_exit();
TASK_MANAGER.run_next_task(); TASK_MANAGER.run_next_task();
} }
// 得到当前运行任务的 trap context 的地址
pub fn current_trap_cx() -> &'static mut TrapContext {
TASK_MANAGER.get_current_trap_cx()
}

@ -40,4 +40,5 @@ __switch:
# restore kernel stack of next task # restore kernel stack of next task
ld sp, 8(a1) # ld sp, 8(a1) #
# [4] # [4]
__switch_ret:
ret # ret #

@ -1,4 +1,8 @@
use crate::config::{kernel_stack_position, TRAP_CONTEXT};
use crate::mm::address::{PhysPageNum, VirtAddr};
use crate::mm::memory_set::{KERNEL_SPACE, MapPermission, MemorySet};
use crate::task::context::{TaskContext}; use crate::task::context::{TaskContext};
use crate::trap::{trap_handler, TrapContext};
// TCB的字段, 用来保存任务的状态 // TCB的字段, 用来保存任务的状态
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
@ -11,10 +15,94 @@ pub enum TaskStatus {
// 一个任务的主体, 用来保存或者控制一个任务所有需要的东西 // 一个任务的主体, 用来保存或者控制一个任务所有需要的东西
#[derive(Copy, Clone)]
pub struct TaskControlBlock { pub struct TaskControlBlock {
pub user_time: usize, // 用户态程序用的时间 pub user_time: usize, // 用户态程序用的时间
pub kernel_time: usize, // 内核态程序所用的时间 pub kernel_time: usize, // 内核态程序所用的时间
pub task_status: TaskStatus, pub task_status: TaskStatus,
pub task_cx: TaskContext, pub task_cx: TaskContext,
pub memory_set: MemorySet, // tcb他自己的地址空间
pub trap_cx_ppn: PhysPageNum, // tcb访问 trap context所在的真实的物理页, 它对应逻辑页的次高页
pub base_size: usize, // 应用地址空间中从0x0开始到用户栈结束一共包含多少字节, 就是用户数据有多大
pub heap_bottom: usize, // 堆底
pub program_brk: usize,
}
impl TaskControlBlock {
pub fn from(elf_data: &[u8], app_id: usize) -> Self {
// memory_set with elf program headers/trampoline/trap context/user stack
// 返回 内存空间, 用户栈的栈底和当前栈顶, 和入口地址
let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data);
// 在当前的地址空间中, 找到 trap_context 逻辑页 所在的真实物理页, 他逻辑地址在次高页, 真实物理地址是我们在 from_elf 申请映射的未知地址
// 这个真实的物理地址, 我们后续再陷入的时候会用到, 进行强写数据
let trap_cx_ppn = memory_set
.page_table
.get_pte(VirtAddr::from(TRAP_CONTEXT).into()) // 虚拟地址次高页, 所在对应的物理内存的pte,
.unwrap()
.ppn(); // pte 内的ppn, 得到实际 虚拟内存次高页 所在的物理页号
// 初始化任务状态
let task_status = TaskStatus::Ready;
// map a kernel-stack in kernel space
// 根据 app_id 创建受保护的用户的任务内核栈, 并得到栈的起始地址和结束地址, 这个在次高页的下面的 某个位置
// kernel_stack_bottom = kernel_stack_top - PAGESIZE * 2
// kernel_stack_top 是当前栈的栈顶, 同时也是现在没有push操作也是栈底
// 这里都是虚拟地址
let (kernel_stack_bottom, kernel_stack_top) = kernel_stack_position(app_id);
// 把用户的内核栈逻辑段, 映射并创建出来实际的物理页帧, 到内核地址空间, 供内核态可以进行寻址找到用户应用的内核栈,
// 这里使用虚拟映射 而不是恒等映射, 是因为需要动态调整?
// 这个只有在内核会用到
KERNEL_SPACE.exclusive_access().insert_framed_area(
kernel_stack_bottom.into(),
kernel_stack_top.into(),
MapPermission::R | MapPermission::W,
);
// 构建 任务控制块
let task_control_block = Self {
user_time: 0,
kernel_time: 0,
task_status,
task_cx: TaskContext::goto_trap_return(kernel_stack_top), // 根据内核栈构造任务切换上下文, 并把 switch任务切换的 ra 设置为 trap_return的函数地址
memory_set, // 新增, 当前任务的地址空间
trap_cx_ppn, // 新增 逻辑次高页的trap context, 对应的这个是真实的物理页, 我们这里保存一份, 省的在memory_set 里面查找了
base_size: user_sp, // 用户栈顶以下 是代码中的各种逻辑段+栈段, 应用地址空间中从0x0开始到用户栈结束一共包含多少字节, 所以大小就是截止到user_sp
heap_bottom: user_sp, // todo 这里书里没写干嘛的 但是 user_sp 这个是用户空间的初始栈顶位置 指向最后的用户段中用户栈的高地址栈底的位置 在往上是空的空间了
program_brk: user_sp, // 同上
};
// prepare TrapContext in user space
// 根据 trap_cx_ppn 构建 陷入 trap context 的结构体
let trap_cx = task_control_block.get_trap_cx(); // 根据trap_cx_ppn得到真实的物理页的 trap context的地址
*trap_cx = TrapContext::from( // 对 tcb 的 TrapContext 的物理页进行 修改,
entry_point, // 在陷入完成后, 准备返回用户态执行的, 用户代码入口地址
user_sp, // 用户栈, 这个栈的地址是虚拟地址
KERNEL_SPACE.exclusive_access().token(), // 内核 satp 页表的寄存器信息
kernel_stack_top, // 用户应用的内核栈顶 在内核空间的位置 (这个是根据appid 计算出来的, 也只能在内核的地址空间中才能根据这个地址 看到相关的栈)
trap_handler as usize, // 内核中 trap handler的入口点虚拟地址(恒等映射所以 这里也能找到)
);
task_control_block
// 以上:
// 任务控制块和 trap控制块, 分别多了一些字段
// trap控制块(这个每次陷入都会被 sscratch 寄存器临时保存):
// 多的三个字段 首次写入后, 后续只会读取和恢复,不会被再次写入新值, 这是为了
// kernel_satp 内核空间页表所在位置的实际物理地址, 这个值以后会在切换的时候替换到satp寄存器
// kernel_sp, 这个保存这个应用 他 根据appid计算出来 所持有的任务内核栈, 在jump context之前 会恢复
// trap_handler 这是在内核在陷入的时候, 我们自定义的 trap_handle 的地址, 在陷入保存完寄存器之后, 需要 jmp 到这个函数地址
// task 控制块 TCB, 这个是在内核进行 switch时的上下文结构:
// memory_set 表示当前任务他的地址空间
// trap_cx_ppn 用来保存 上方 trap控制块的 实际的物理页帧, 在用户空间中都被应设在了TRAP_CONTEXT相关的次高位置虚拟地址处, 在内核空间我们这保存一份实际的物理地址, 在内核空间读取这个通过恒等映射就得到任务的 trap context
// base_size 用户栈顶距离0x0的距离, 即用户应用已知的大小
//
}
}
impl TaskControlBlock {
// 根据 trap context 的实际的物理地址, 强转为 TrapContext 结构体
pub fn get_trap_cx(&self) -> &'static mut TrapContext {
self.trap_cx_ppn.get_mut()
}
pub fn get_user_token(&self) -> usize {
self.memory_set.token()
}
} }

@ -18,6 +18,12 @@ pub struct TrapContext {
pub sstatus: Sstatus, pub sstatus: Sstatus,
/// CSR sepc 保存的是用户态ecall时 所在的那一行的代码 /// CSR sepc 保存的是用户态ecall时 所在的那一行的代码
pub sepc: usize, pub sepc: usize,
/// Addr of Page Table
pub kernel_satp: usize, // 内核页的起始物理地址, 首次写入后, 后续只会读取和恢复,不会被再次写入新值
/// kernel stack
pub kernel_sp: usize, // 当前任务的内核栈顶的虚拟地址
/// Addr of trap_handler function
pub trap_handler: usize, // 陷入处理函数的地址虚拟地址
} }
impl TrapContext { impl TrapContext {
@ -27,13 +33,16 @@ impl TrapContext {
} }
// 根据entry和sp构造一个 trap_context // 根据entry和sp构造一个 trap_context
pub fn from(entry: usize, sp: usize) -> Self { pub fn from(entry: usize, sp: usize, kernel_satp: usize, kernel_sp: usize, trap_handler: usize,) -> Self {
let mut sstatus = sstatus::read(); // 读取CSR sstatus let mut sstatus = sstatus::read(); // 读取CSR sstatus
sstatus.set_spp(SPP::User); // 设置特权级为用户模式 sstatus.set_spp(SPP::User); // 设置特权级为用户模式
let mut cx = Self { let mut cx = Self {
x: [0; 32], x: [0; 32],
sstatus, sstatus,
sepc: entry, // 设置代码执行的开头, 将来条入到这里执行 sepc: entry, // 设置代码执行的开头, 将来条入到这里执行
kernel_satp,
kernel_sp,
trap_handler,
}; };
cx.set_sp(sp); // 设置用户栈的栈顶 cx.set_sp(sp); // 设置用户栈的栈顶
cx // 返回, 外面会恢复完 x[0; 32] 之后最后 sret 根据entry和sstatus 返回 cx // 返回, 外面会恢复完 x[0; 32] 之后最后 sret 根据entry和sstatus 返回

@ -1,10 +1,11 @@
mod context; mod context;
use core::arch::global_asm; use core::arch::{asm, global_asm};
use riscv::register::{mtvec::TrapMode, scause::{self, Exception, Trap}, sie, stval, stvec}; use riscv::register::{mtvec::TrapMode, scause::{self, Exception, Trap}, sie, stval, stvec};
use riscv::register::scause::Interrupt; use riscv::register::scause::Interrupt;
pub use context::TrapContext; pub use context::TrapContext;
use crate::config::{TRAMPOLINE, TRAP_CONTEXT};
use crate::println; use crate::println;
use crate::syscall::syscall; use crate::syscall::syscall;
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next, TASK_MANAGER}; use crate::task::{exit_current_and_run_next, suspend_current_and_run_next, TASK_MANAGER};
@ -17,12 +18,7 @@ global_asm!(include_str!("trap.S"));
/// 初始化stvec 寄存器, 这个寄存器保存陷入时, 入口函数的地址 /// 初始化stvec 寄存器, 这个寄存器保存陷入时, 入口函数的地址
pub fn init() { pub fn init() {
extern "C" { set_kernel_trap_entry();
fn __alltraps();
}
unsafe {
stvec::write(__alltraps as usize, TrapMode::Direct);
}
} }
// 设置riscv 允许定时器中断 // 设置riscv 允许定时器中断
@ -34,23 +30,31 @@ pub fn enable_timer_interrupt() {
// 这个函数是 trap.S 中__alltraps 保存完所有寄存器在内核栈 之后会进入到这里 // 这个函数是 trap.S 中__alltraps 保存完所有寄存器在内核栈 之后会进入到这里
#[no_mangle] #[no_mangle]
pub fn trap_handler(trap_context: &mut TrapContext) -> &mut TrapContext { pub fn trap_handler() -> ! {
// 已经进入内核, 如果再发生中断我们进行的设置, 目前是直接跳转到一个引发panic 的函数
println!("trap_handler......");
set_kernel_trap_entry();
// 进入到了内核态, 需要把之前的用户消耗时间统计在用户时间上 // 进入到了内核态, 需要把之前的用户消耗时间统计在用户时间上
TASK_MANAGER.kernel_time_start(); TASK_MANAGER.kernel_time_start();
// 得到当前用户应用 的 trap context, 需要调用一个函数, 因为用户应用的trap context 不再内核空间, 他是在用户空间的次高地址
let cx = TASK_MANAGER.get_current_trap_cx();
let scause = scause::read(); // trap 发生的原因 let scause = scause::read(); // trap 发生的原因
let stval = stval::read(); // trap的附加信息 let stval = stval::read(); // trap的附加信息
println!("trap_handler......1");
// 根据发生的原因判断是那种类别 // 根据发生的原因判断是那种类别
match scause.cause() { match scause.cause() {
// 用户态发出的系统调用 // 用户态发出的系统调用
Trap::Exception(Exception::UserEnvCall) => { Trap::Exception(Exception::UserEnvCall) => {
trap_context.sepc += 4; // +4 是因为我们需要返回 ecall指令的下一个指令 cx.sepc += 4; // +4 是因为我们需要返回 ecall指令的下一个指令
trap_context.x[10] = syscall(trap_context.x[17], [trap_context.x[10], trap_context.x[11], trap_context.x[12]]) as usize; cx.x[10] = syscall(cx.x[17], [cx.x[10], cx.x[11], cx.x[12]]) as usize;
} }
// 访问不允许的内存 // 访问不允许的内存
Trap::Exception(Exception::StoreFault) | Trap::Exception(Exception::StorePageFault) => { Trap::Exception(Exception::StoreFault)
| Trap::Exception(Exception::StorePageFault)
| Trap::Exception(Exception::LoadFault)
| Trap::Exception(Exception::LoadPageFault) => {
println!("[kernel] PageFault in application, kernel killed it."); println!("[kernel] PageFault in application, kernel killed it.");
exit_current_and_run_next(); exit_current_and_run_next();
} }
@ -76,8 +80,65 @@ pub fn trap_handler(trap_context: &mut TrapContext) -> &mut TrapContext {
} }
// 即将进入用户态, 把内核使用的时间统计在内核时间上 // 即将进入用户态, 把内核使用的时间统计在内核时间上
TASK_MANAGER.user_time_start(); TASK_MANAGER.user_time_start();
trap_context trap_return()
}
#[no_mangle]
/// Unimplement: traps/interrupts/exceptions from kernel mode
/// Todo: Chapter 9: I/O device
pub fn trap_from_kernel() -> ! {
panic!("a trap from kernel!");
}
#[no_mangle]
/// set the new addr of __restore asm function in TRAMPOLINE page,
/// set the reg a0 = trap_cx_ptr, reg a1 = phy addr of usr page table,
/// finally, jump to new addr of __restore asm function
pub fn trap_return() -> ! {
// 设置 用户态的中断处理函数
set_user_trap_entry();
let trap_cx_ptr = TRAP_CONTEXT; // 用户空间虚拟地址的次高页, 保存了trap context, 在返回用户空间的时候, 需要恢复trap context中保存的寄存器信息
let user_satp = TASK_MANAGER.get_current_token(); // 当前用户应用的 页表所在的地址 需要在trap.S中, 等下恢复完寄存器之后 修改的用户应用自己的页表
extern "C" {
fn __alltraps();
fn __restore();
}
// TRAMPOLINE + (__restore - __alltraps), 得到的就是用户空间中 虚拟地址跳板的位置, 增加一个 (__alltraps低地址 到 __restore高地址的偏移值), 就得到了 strampoline -> __restore 的虚拟地址
let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;
println!("restore_va: {:?}", restore_va);
println!("trap_cx_ptr :{:?}", trap_cx_ptr);
println!("user_satp :{:?}", user_satp);
unsafe {
asm!(
"fence.i", // 清空 i-cache
"jr {restore_va}", // 跳转到 strampoline -> __restore 地址
restore_va = in(reg) restore_va,
in("a0") trap_cx_ptr, // a0 = virt addr of Trap Context
in("a1") user_satp, // a1 = phy addr of usr page table
options(noreturn)
)
}
} }
fn set_kernel_trap_entry() {
// 设置内核法中 trap 的处理
// 当前我们直接panic
unsafe {
println!("set_kernel_trap_entry");
stvec::write(trap_from_kernel as usize, TrapMode::Direct);
}
}
fn set_user_trap_entry() {
// 设置 用户态的中断处理函数, 为我们的跳板, 这个跳板在用户空间的虚拟地址最高页, 虚拟地址最高页被映射在了十几物理内存的trap 处理函数所在的段执行__alltraps
unsafe {
println!("set_user_trap_entry");
stvec::write(TRAMPOLINE as usize, TrapMode::Direct);
}
}

@ -9,7 +9,7 @@
ld x\n, \n*8(sp) # x_n ld x\n, \n*8(sp) # x_n
.endm .endm
.section .text # .text .section .text.trampoline # .text.trampoline
.globl __alltraps # __alltraps .globl __alltraps # __alltraps
.globl __restore # __restore .globl __restore # __restore
.align 2 # 2^2 = 4 .align 2 # 2^2 = 4

Loading…
Cancel
Save