mirror of
https://github.com/rcore-os/rCore-Tutorial-v3.git
synced 2024-11-25 10:56:25 +04:00
update ch4 with more comments
This commit is contained in:
parent
93c4ae0f33
commit
f1eae45419
@ -1,3 +1,5 @@
|
|||||||
|
//! Constants used in rCore
|
||||||
|
|
||||||
pub const USER_STACK_SIZE: usize = 4096 * 2;
|
pub const USER_STACK_SIZE: usize = 4096 * 2;
|
||||||
pub const KERNEL_STACK_SIZE: usize = 4096 * 2;
|
pub const KERNEL_STACK_SIZE: usize = 4096 * 2;
|
||||||
pub const KERNEL_HEAP_SIZE: usize = 0x30_0000;
|
pub const KERNEL_HEAP_SIZE: usize = 0x30_0000;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! SBI console driver, for text output
|
||||||
|
|
||||||
use crate::sbi::console_putchar;
|
use crate::sbi::console_putchar;
|
||||||
use core::fmt::{self, Write};
|
use core::fmt::{self, Write};
|
||||||
|
|
||||||
@ -17,6 +19,7 @@ pub fn print(args: fmt::Arguments) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
/// print string macro
|
||||||
macro_rules! print {
|
macro_rules! print {
|
||||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||||
$crate::console::print(format_args!($fmt $(, $($arg)+)?));
|
$crate::console::print(format_args!($fmt $(, $($arg)+)?));
|
||||||
@ -24,6 +27,7 @@ macro_rules! print {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
|
/// println string macro
|
||||||
macro_rules! println {
|
macro_rules! println {
|
||||||
($fmt: literal $(, $($arg: tt)+)?) => {
|
($fmt: literal $(, $($arg: tt)+)?) => {
|
||||||
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
|
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
//! The panic handler
|
||||||
|
|
||||||
use crate::sbi::shutdown;
|
use crate::sbi::shutdown;
|
||||||
use core::panic::PanicInfo;
|
use core::panic::PanicInfo;
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
|
/// panic handler
|
||||||
fn panic(info: &PanicInfo) -> ! {
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
if let Some(location) = info.location() {
|
if let Some(location) = info.location() {
|
||||||
println!(
|
println!(
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
//! Loading user applications into memory
|
||||||
|
|
||||||
|
/// Get the total number of applications.
|
||||||
pub fn get_num_app() -> usize {
|
pub fn get_num_app() -> usize {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn _num_app();
|
fn _num_app();
|
||||||
@ -5,6 +8,7 @@ pub fn get_num_app() -> usize {
|
|||||||
unsafe { (_num_app as usize as *const usize).read_volatile() }
|
unsafe { (_num_app as usize as *const usize).read_volatile() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get applications data
|
||||||
pub fn get_app_data(app_id: usize) -> &'static [u8] {
|
pub fn get_app_data(app_id: usize) -> &'static [u8] {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn _num_app();
|
fn _num_app();
|
||||||
|
@ -1,3 +1,20 @@
|
|||||||
|
//! The main module and entrypoint
|
||||||
|
//!
|
||||||
|
//! Various facilities of the kernels are implemented as submodules. The most
|
||||||
|
//! important ones are:
|
||||||
|
//!
|
||||||
|
//! - [`trap`]: Handles all cases of switching from userspace to the kernel
|
||||||
|
//! - [`task`]: Task management
|
||||||
|
//! - [`syscall`]: System call handling and implementation
|
||||||
|
//!
|
||||||
|
//! The operating system also starts in this module. Kernel code starts
|
||||||
|
//! executing from `entry.asm`, after which [`rust_main()`] is called to
|
||||||
|
//! initialize various pieces of functionality. (See its source code for
|
||||||
|
//! details.)
|
||||||
|
//!
|
||||||
|
//! We then call [`task::run_first_task()`] and for the first time go to
|
||||||
|
//! userspace.
|
||||||
|
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
#![feature(panic_info_message)]
|
#![feature(panic_info_message)]
|
||||||
@ -23,16 +40,15 @@ mod loader;
|
|||||||
mod mm;
|
mod mm;
|
||||||
mod sbi;
|
mod sbi;
|
||||||
mod sync;
|
mod sync;
|
||||||
mod syscall;
|
pub mod syscall;
|
||||||
mod task;
|
pub mod task;
|
||||||
mod timer;
|
mod timer;
|
||||||
mod trap;
|
pub mod trap;
|
||||||
|
|
||||||
use core::arch::global_asm;
|
core::arch::global_asm!(include_str!("entry.asm"));
|
||||||
|
core::arch::global_asm!(include_str!("link_app.S"));
|
||||||
global_asm!(include_str!("entry.asm"));
|
|
||||||
global_asm!(include_str!("link_app.S"));
|
|
||||||
|
|
||||||
|
/// clear BSS segment
|
||||||
fn clear_bss() {
|
fn clear_bss() {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn sbss();
|
fn sbss();
|
||||||
@ -45,6 +61,7 @@ fn clear_bss() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
|
/// the rust entry-point of os
|
||||||
pub fn rust_main() -> ! {
|
pub fn rust_main() -> ! {
|
||||||
clear_bss();
|
clear_bss();
|
||||||
println!("[kernel] Hello, world!");
|
println!("[kernel] Hello, world!");
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
|
//! Implementation of physical and virtual address and page number.
|
||||||
|
|
||||||
use super::PageTableEntry;
|
use super::PageTableEntry;
|
||||||
use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS};
|
use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS};
|
||||||
use core::fmt::{self, Debug, Formatter};
|
use core::fmt::{self, Debug, Formatter};
|
||||||
|
|
||||||
|
/// physical address
|
||||||
const PA_WIDTH_SV39: usize = 56;
|
const PA_WIDTH_SV39: usize = 56;
|
||||||
const VA_WIDTH_SV39: usize = 39;
|
const VA_WIDTH_SV39: usize = 39;
|
||||||
const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS;
|
const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS;
|
||||||
@ -11,12 +14,15 @@ const VPN_WIDTH_SV39: usize = VA_WIDTH_SV39 - PAGE_SIZE_BITS;
|
|||||||
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub struct PhysAddr(pub usize);
|
pub struct PhysAddr(pub usize);
|
||||||
|
|
||||||
|
/// virtual address
|
||||||
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub struct VirtAddr(pub usize);
|
pub struct VirtAddr(pub usize);
|
||||||
|
|
||||||
|
/// physical page number
|
||||||
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub struct PhysPageNum(pub usize);
|
pub struct PhysPageNum(pub usize);
|
||||||
|
|
||||||
|
/// virtual page number
|
||||||
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
|
||||||
pub struct VirtPageNum(pub usize);
|
pub struct VirtPageNum(pub usize);
|
||||||
|
|
||||||
@ -176,6 +182,7 @@ impl StepByOne for VirtPageNum {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
/// a simple range structure for type T
|
||||||
pub struct SimpleRange<T>
|
pub struct SimpleRange<T>
|
||||||
where
|
where
|
||||||
T: StepByOne + Copy + PartialEq + PartialOrd + Debug,
|
T: StepByOne + Copy + PartialEq + PartialOrd + Debug,
|
||||||
@ -208,6 +215,7 @@ where
|
|||||||
SimpleRangeIterator::new(self.l, self.r)
|
SimpleRangeIterator::new(self.l, self.r)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/// iterator for the simple range structure
|
||||||
pub struct SimpleRangeIterator<T>
|
pub struct SimpleRangeIterator<T>
|
||||||
where
|
where
|
||||||
T: StepByOne + Copy + PartialEq + PartialOrd + Debug,
|
T: StepByOne + Copy + PartialEq + PartialOrd + Debug,
|
||||||
@ -238,4 +246,6 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// a simple range structure for virtual page number
|
||||||
pub type VPNRange = SimpleRange<VirtPageNum>;
|
pub type VPNRange = SimpleRange<VirtPageNum>;
|
||||||
|
@ -1,3 +1,6 @@
|
|||||||
|
//! Implementation of [`FrameAllocator`] which
|
||||||
|
//! controls all the frames in the operating system.
|
||||||
|
|
||||||
use super::{PhysAddr, PhysPageNum};
|
use super::{PhysAddr, PhysPageNum};
|
||||||
use crate::config::MEMORY_END;
|
use crate::config::MEMORY_END;
|
||||||
use crate::sync::UPSafeCell;
|
use crate::sync::UPSafeCell;
|
||||||
@ -5,6 +8,7 @@ use alloc::vec::Vec;
|
|||||||
use core::fmt::{self, Debug, Formatter};
|
use core::fmt::{self, Debug, Formatter};
|
||||||
use lazy_static::*;
|
use lazy_static::*;
|
||||||
|
|
||||||
|
/// manage a frame which has the same lifecycle as the tracker
|
||||||
pub struct FrameTracker {
|
pub struct FrameTracker {
|
||||||
pub ppn: PhysPageNum,
|
pub ppn: PhysPageNum,
|
||||||
}
|
}
|
||||||
@ -38,6 +42,7 @@ trait FrameAllocator {
|
|||||||
fn dealloc(&mut self, ppn: PhysPageNum);
|
fn dealloc(&mut self, ppn: PhysPageNum);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// an implementation for frame allocator
|
||||||
pub struct StackFrameAllocator {
|
pub struct StackFrameAllocator {
|
||||||
current: usize,
|
current: usize,
|
||||||
end: usize,
|
end: usize,
|
||||||
@ -82,10 +87,12 @@ impl FrameAllocator for StackFrameAllocator {
|
|||||||
type FrameAllocatorImpl = StackFrameAllocator;
|
type FrameAllocatorImpl = StackFrameAllocator;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
/// frame allocator instance through lazy_static!
|
||||||
pub static ref FRAME_ALLOCATOR: UPSafeCell<FrameAllocatorImpl> =
|
pub static ref FRAME_ALLOCATOR: UPSafeCell<FrameAllocatorImpl> =
|
||||||
unsafe { UPSafeCell::new(FrameAllocatorImpl::new()) };
|
unsafe { UPSafeCell::new(FrameAllocatorImpl::new()) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// initiate the frame allocator using `ekernel` and `MEMORY_END`
|
||||||
pub fn init_frame_allocator() {
|
pub fn init_frame_allocator() {
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn ekernel();
|
fn ekernel();
|
||||||
@ -96,6 +103,7 @@ pub fn init_frame_allocator() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// allocate a frame
|
||||||
pub fn frame_alloc() -> Option<FrameTracker> {
|
pub fn frame_alloc() -> Option<FrameTracker> {
|
||||||
FRAME_ALLOCATOR
|
FRAME_ALLOCATOR
|
||||||
.exclusive_access()
|
.exclusive_access()
|
||||||
@ -103,11 +111,13 @@ pub fn frame_alloc() -> Option<FrameTracker> {
|
|||||||
.map(FrameTracker::new)
|
.map(FrameTracker::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// deallocate a frame
|
||||||
fn frame_dealloc(ppn: PhysPageNum) {
|
fn frame_dealloc(ppn: PhysPageNum) {
|
||||||
FRAME_ALLOCATOR.exclusive_access().dealloc(ppn);
|
FRAME_ALLOCATOR.exclusive_access().dealloc(ppn);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
|
/// a simple test for frame allocator
|
||||||
pub fn frame_allocator_test() {
|
pub fn frame_allocator_test() {
|
||||||
let mut v: Vec<FrameTracker> = Vec::new();
|
let mut v: Vec<FrameTracker> = Vec::new();
|
||||||
for i in 0..5 {
|
for i in 0..5 {
|
||||||
|
@ -1,16 +1,22 @@
|
|||||||
|
//! The global allocator
|
||||||
|
|
||||||
use crate::config::KERNEL_HEAP_SIZE;
|
use crate::config::KERNEL_HEAP_SIZE;
|
||||||
use buddy_system_allocator::LockedHeap;
|
use buddy_system_allocator::LockedHeap;
|
||||||
|
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
|
/// heap allocator instance
|
||||||
static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty();
|
static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||||
|
|
||||||
#[alloc_error_handler]
|
#[alloc_error_handler]
|
||||||
|
/// panic when heap allocation error occurs
|
||||||
pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
|
pub fn handle_alloc_error(layout: core::alloc::Layout) -> ! {
|
||||||
panic!("Heap allocation error, layout = {:?}", layout);
|
panic!("Heap allocation error, layout = {:?}", layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// heap space ([u8; KERNEL_HEAP_SIZE])
|
||||||
static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE];
|
static mut HEAP_SPACE: [u8; KERNEL_HEAP_SIZE] = [0; KERNEL_HEAP_SIZE];
|
||||||
|
|
||||||
|
/// initiate heap allocator
|
||||||
pub fn init_heap() {
|
pub fn init_heap() {
|
||||||
unsafe {
|
unsafe {
|
||||||
HEAP_ALLOCATOR
|
HEAP_ALLOCATOR
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Implementation of [`MapArea`] and [`MemorySet`].
|
||||||
|
|
||||||
use super::{frame_alloc, FrameTracker};
|
use super::{frame_alloc, FrameTracker};
|
||||||
use super::{PTEFlags, PageTable, PageTableEntry};
|
use super::{PTEFlags, PageTable, PageTableEntry};
|
||||||
use super::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum};
|
use super::{PhysAddr, PhysPageNum, VirtAddr, VirtPageNum};
|
||||||
@ -25,10 +27,12 @@ extern "C" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
/// a memory set instance through lazy_static! managing kernel space
|
||||||
pub static ref KERNEL_SPACE: Arc<UPSafeCell<MemorySet>> =
|
pub static ref KERNEL_SPACE: Arc<UPSafeCell<MemorySet>> =
|
||||||
Arc::new(unsafe { UPSafeCell::new(MemorySet::new_kernel()) });
|
Arc::new(unsafe { UPSafeCell::new(MemorySet::new_kernel()) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// memory set structure, controls virtual-memory space
|
||||||
pub struct MemorySet {
|
pub struct MemorySet {
|
||||||
page_table: PageTable,
|
page_table: PageTable,
|
||||||
areas: Vec<MapArea>,
|
areas: Vec<MapArea>,
|
||||||
@ -216,6 +220,7 @@ impl MemorySet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// map area structure, controls a contiguous piece of virtual memory
|
||||||
pub struct MapArea {
|
pub struct MapArea {
|
||||||
vpn_range: VPNRange,
|
vpn_range: VPNRange,
|
||||||
data_frames: BTreeMap<VirtPageNum, FrameTracker>,
|
data_frames: BTreeMap<VirtPageNum, FrameTracker>,
|
||||||
@ -297,12 +302,14 @@ impl MapArea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Debug)]
|
||||||
|
/// map type for memory set: identical or framed
|
||||||
pub enum MapType {
|
pub enum MapType {
|
||||||
Identical,
|
Identical,
|
||||||
Framed,
|
Framed,
|
||||||
}
|
}
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
/// map permission corresponding to that in pte: `R W X U`
|
||||||
pub struct MapPermission: u8 {
|
pub struct MapPermission: u8 {
|
||||||
const R = 1 << 1;
|
const R = 1 << 1;
|
||||||
const W = 1 << 2;
|
const W = 1 << 2;
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
//! Memory management implementation
|
||||||
|
//!
|
||||||
|
//! SV39 page-based virtual-memory architecture for RV64 systems, and
|
||||||
|
//! everything about memory management, like frame allocator, page table,
|
||||||
|
//! map area and memory set, is implemented here.
|
||||||
|
//!
|
||||||
|
//! Every task or process has a memory_set to control its virtual memory.
|
||||||
|
|
||||||
|
|
||||||
mod address;
|
mod address;
|
||||||
mod frame_allocator;
|
mod frame_allocator;
|
||||||
mod heap_allocator;
|
mod heap_allocator;
|
||||||
@ -12,6 +21,7 @@ pub use memory_set::{MapPermission, MemorySet, KERNEL_SPACE};
|
|||||||
pub use page_table::{translated_byte_buffer, PageTableEntry};
|
pub use page_table::{translated_byte_buffer, PageTableEntry};
|
||||||
use page_table::{PTEFlags, PageTable};
|
use page_table::{PTEFlags, PageTable};
|
||||||
|
|
||||||
|
/// initiate heap allocator, frame allocator and kernel space
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
heap_allocator::init_heap();
|
heap_allocator::init_heap();
|
||||||
frame_allocator::init_frame_allocator();
|
frame_allocator::init_frame_allocator();
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
|
//! Implementation of [`PageTableEntry`] and [`PageTable`].
|
||||||
|
|
||||||
use super::{frame_alloc, FrameTracker, PhysPageNum, StepByOne, VirtAddr, VirtPageNum};
|
use super::{frame_alloc, FrameTracker, PhysPageNum, StepByOne, VirtAddr, VirtPageNum};
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use bitflags::*;
|
use bitflags::*;
|
||||||
|
|
||||||
bitflags! {
|
bitflags! {
|
||||||
|
/// page table entry flags
|
||||||
pub struct PTEFlags: u8 {
|
pub struct PTEFlags: u8 {
|
||||||
const V = 1 << 0;
|
const V = 1 << 0;
|
||||||
const R = 1 << 1;
|
const R = 1 << 1;
|
||||||
@ -18,6 +21,7 @@ bitflags! {
|
|||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
/// page table entry structure
|
||||||
pub struct PageTableEntry {
|
pub struct PageTableEntry {
|
||||||
pub bits: usize,
|
pub bits: usize,
|
||||||
}
|
}
|
||||||
@ -51,6 +55,7 @@ impl PageTableEntry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// page table structure
|
||||||
pub struct PageTable {
|
pub struct PageTable {
|
||||||
root_ppn: PhysPageNum,
|
root_ppn: PhysPageNum,
|
||||||
frames: Vec<FrameTracker>,
|
frames: Vec<FrameTracker>,
|
||||||
@ -128,6 +133,7 @@ impl PageTable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// translate a pointer to a mutable u8 Vec through page table
|
||||||
pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> {
|
pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> {
|
||||||
let page_table = PageTable::from_token(token);
|
let page_table = PageTable::from_token(token);
|
||||||
let mut start = ptr as usize;
|
let mut start = ptr as usize;
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
#![allow(unused)]
|
//! SBI call wrappers
|
||||||
|
|
||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
const SBI_SET_TIMER: usize = 0;
|
const SBI_SET_TIMER: usize = 0;
|
||||||
const SBI_CONSOLE_PUTCHAR: usize = 1;
|
const SBI_CONSOLE_PUTCHAR: usize = 1;
|
||||||
const SBI_CONSOLE_GETCHAR: usize = 2;
|
// const SBI_CONSOLE_GETCHAR: usize = 2;
|
||||||
const SBI_CLEAR_IPI: usize = 3;
|
// const SBI_CLEAR_IPI: usize = 3;
|
||||||
const SBI_SEND_IPI: usize = 4;
|
// const SBI_SEND_IPI: usize = 4;
|
||||||
const SBI_REMOTE_FENCE_I: usize = 5;
|
// const SBI_REMOTE_FENCE_I: usize = 5;
|
||||||
const SBI_REMOTE_SFENCE_VMA: usize = 6;
|
// const SBI_REMOTE_SFENCE_VMA: usize = 6;
|
||||||
const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7;
|
// const SBI_REMOTE_SFENCE_VMA_ASID: usize = 7;
|
||||||
const SBI_SHUTDOWN: usize = 8;
|
const SBI_SHUTDOWN: usize = 8;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
/// general sbi call
|
||||||
fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
|
fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
|
||||||
let mut ret;
|
let mut ret;
|
||||||
unsafe {
|
unsafe {
|
||||||
@ -27,18 +28,22 @@ fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
|
|||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// use sbi call to set timer
|
||||||
pub fn set_timer(timer: usize) {
|
pub fn set_timer(timer: usize) {
|
||||||
sbi_call(SBI_SET_TIMER, timer, 0, 0);
|
sbi_call(SBI_SET_TIMER, timer, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// use sbi call to putchar in console (qemu uart handler)
|
||||||
pub fn console_putchar(c: usize) {
|
pub fn console_putchar(c: usize) {
|
||||||
sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0);
|
sbi_call(SBI_CONSOLE_PUTCHAR, c, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn console_getchar() -> usize {
|
/// use sbi call to getchar from console (qemu uart handler)
|
||||||
sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
|
// pub fn console_getchar() -> usize {
|
||||||
}
|
// sbi_call(SBI_CONSOLE_GETCHAR, 0, 0, 0)
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// use sbi call to shutdown the kernel
|
||||||
pub fn shutdown() -> ! {
|
pub fn shutdown() -> ! {
|
||||||
sbi_call(SBI_SHUTDOWN, 0, 0, 0);
|
sbi_call(SBI_SHUTDOWN, 0, 0, 0);
|
||||||
panic!("It should shutdown!");
|
panic!("It should shutdown!");
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Synchronization and interior mutability primitives
|
||||||
|
|
||||||
mod up;
|
mod up;
|
||||||
|
|
||||||
pub use up::UPSafeCell;
|
pub use up::UPSafeCell;
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Uniprocessor interior mutability primitives
|
||||||
|
|
||||||
use core::cell::{RefCell, RefMut};
|
use core::cell::{RefCell, RefMut};
|
||||||
|
|
||||||
/// Wrap a static data structure inside it so that we are
|
/// Wrap a static data structure inside it so that we are
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! File and filesystem-related syscalls
|
||||||
|
|
||||||
use crate::mm::translated_byte_buffer;
|
use crate::mm::translated_byte_buffer;
|
||||||
use crate::task::current_user_token;
|
use crate::task::current_user_token;
|
||||||
|
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
|
//! Implementation of syscalls
|
||||||
|
//!
|
||||||
|
//! The single entry point to all system calls, [`syscall()`], is called
|
||||||
|
//! whenever userspace wishes to perform a system call using the `ecall`
|
||||||
|
//! instruction. In this case, the processor raises an 'Environment call from
|
||||||
|
//! U-mode' exception, which is handled as one of the cases in
|
||||||
|
//! [`crate::trap::trap_handler`].
|
||||||
|
//!
|
||||||
|
//! For clarity, each single syscall is implemented as its own function, named
|
||||||
|
//! `sys_` then the name of the syscall. You can find functions like this in
|
||||||
|
//! submodules, and you should also implement syscalls this way.
|
||||||
|
|
||||||
const SYSCALL_WRITE: usize = 64;
|
const SYSCALL_WRITE: usize = 64;
|
||||||
const SYSCALL_EXIT: usize = 93;
|
const SYSCALL_EXIT: usize = 93;
|
||||||
const SYSCALL_YIELD: usize = 124;
|
const SYSCALL_YIELD: usize = 124;
|
||||||
@ -9,6 +21,7 @@ mod process;
|
|||||||
use fs::*;
|
use fs::*;
|
||||||
use process::*;
|
use process::*;
|
||||||
|
|
||||||
|
/// handle syscall exception with `syscall_id` and other arguments
|
||||||
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
|
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
|
||||||
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]),
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Process management syscalls
|
||||||
|
|
||||||
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next};
|
use crate::task::{exit_current_and_run_next, suspend_current_and_run_next};
|
||||||
use crate::timer::get_time_ms;
|
use crate::timer::get_time_ms;
|
||||||
|
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
|
//! Implementation of [`TaskContext`]
|
||||||
use crate::trap::trap_return;
|
use crate::trap::trap_return;
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
/// task context structure containing some registers
|
||||||
pub struct TaskContext {
|
pub struct TaskContext {
|
||||||
ra: usize,
|
ra: usize,
|
||||||
sp: usize,
|
sp: usize,
|
||||||
|
@ -1,3 +1,14 @@
|
|||||||
|
//! Task management implementation
|
||||||
|
//!
|
||||||
|
//! Everything about task management, like starting and switching tasks is
|
||||||
|
//! implemented here.
|
||||||
|
//!
|
||||||
|
//! A single global instance of [`TaskManager`] called `TASK_MANAGER` controls
|
||||||
|
//! all the tasks in the operating system.
|
||||||
|
//!
|
||||||
|
//! Be careful when you see [`__switch`]. Control flow around this function
|
||||||
|
//! might not be what you expect.
|
||||||
|
|
||||||
mod context;
|
mod context;
|
||||||
mod switch;
|
mod switch;
|
||||||
#[allow(clippy::module_inception)]
|
#[allow(clippy::module_inception)]
|
||||||
@ -13,17 +24,32 @@ use task::{TaskControlBlock, TaskStatus};
|
|||||||
|
|
||||||
pub use context::TaskContext;
|
pub use context::TaskContext;
|
||||||
|
|
||||||
|
/// The task manager, where all the tasks are managed.
|
||||||
|
///
|
||||||
|
/// Functions implemented on `TaskManager` deals with all task state transitions
|
||||||
|
/// and task context switching. For convenience, you can find wrappers around it
|
||||||
|
/// in the module level.
|
||||||
|
///
|
||||||
|
/// Most of `TaskManager` are hidden behind the field `inner`, to defer
|
||||||
|
/// borrowing checks to runtime. You can see examples on how to use `inner` in
|
||||||
|
/// existing functions on `TaskManager`.
|
||||||
pub struct TaskManager {
|
pub struct TaskManager {
|
||||||
|
/// total number of tasks
|
||||||
num_app: usize,
|
num_app: usize,
|
||||||
|
/// use inner value to get mutable access
|
||||||
inner: UPSafeCell<TaskManagerInner>,
|
inner: UPSafeCell<TaskManagerInner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The task manager inner in 'UPSafeCell'
|
||||||
struct TaskManagerInner {
|
struct TaskManagerInner {
|
||||||
|
/// task list
|
||||||
tasks: Vec<TaskControlBlock>,
|
tasks: Vec<TaskControlBlock>,
|
||||||
|
/// id of current `Running` task
|
||||||
current_task: usize,
|
current_task: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
|
/// a `TaskManager` instance through lazy_static!
|
||||||
pub static ref TASK_MANAGER: TaskManager = {
|
pub static ref TASK_MANAGER: TaskManager = {
|
||||||
println!("init TASK_MANAGER");
|
println!("init TASK_MANAGER");
|
||||||
let num_app = get_num_app();
|
let num_app = get_num_app();
|
||||||
@ -45,6 +71,10 @@ lazy_static! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TaskManager {
|
impl TaskManager {
|
||||||
|
/// Run the first task in task list.
|
||||||
|
///
|
||||||
|
/// Generally, the first task in task list is an idle task (we call it zero process later).
|
||||||
|
/// But in ch4, we load apps statically, so the first task is a real app.
|
||||||
fn run_first_task(&self) -> ! {
|
fn run_first_task(&self) -> ! {
|
||||||
let mut inner = self.inner.exclusive_access();
|
let mut inner = self.inner.exclusive_access();
|
||||||
let next_task = &mut inner.tasks[0];
|
let next_task = &mut inner.tasks[0];
|
||||||
@ -59,18 +89,23 @@ impl TaskManager {
|
|||||||
panic!("unreachable in run_first_task!");
|
panic!("unreachable in run_first_task!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change the status of current `Running` task into `Ready`.
|
||||||
fn mark_current_suspended(&self) {
|
fn mark_current_suspended(&self) {
|
||||||
let mut inner = self.inner.exclusive_access();
|
let mut inner = self.inner.exclusive_access();
|
||||||
let cur = inner.current_task;
|
let cur = inner.current_task;
|
||||||
inner.tasks[cur].task_status = TaskStatus::Ready;
|
inner.tasks[cur].task_status = TaskStatus::Ready;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change the status of current `Running` task into `Exited`.
|
||||||
fn mark_current_exited(&self) {
|
fn mark_current_exited(&self) {
|
||||||
let mut inner = self.inner.exclusive_access();
|
let mut inner = self.inner.exclusive_access();
|
||||||
let cur = inner.current_task;
|
let cur = inner.current_task;
|
||||||
inner.tasks[cur].task_status = TaskStatus::Exited;
|
inner.tasks[cur].task_status = TaskStatus::Exited;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Find next task to run and return task id.
|
||||||
|
///
|
||||||
|
/// In this case, we only return the first `Ready` task in task list.
|
||||||
fn find_next_task(&self) -> Option<usize> {
|
fn find_next_task(&self) -> Option<usize> {
|
||||||
let inner = self.inner.exclusive_access();
|
let inner = self.inner.exclusive_access();
|
||||||
let current = inner.current_task;
|
let current = inner.current_task;
|
||||||
@ -79,16 +114,20 @@ impl TaskManager {
|
|||||||
.find(|id| inner.tasks[*id].task_status == TaskStatus::Ready)
|
.find(|id| inner.tasks[*id].task_status == TaskStatus::Ready)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current 'Running' task's token.
|
||||||
fn get_current_token(&self) -> usize {
|
fn get_current_token(&self) -> usize {
|
||||||
let inner = self.inner.exclusive_access();
|
let inner = self.inner.exclusive_access();
|
||||||
inner.tasks[inner.current_task].get_user_token()
|
inner.tasks[inner.current_task].get_user_token()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current 'Running' task's trap contexts.
|
||||||
fn get_current_trap_cx(&self) -> &'static mut TrapContext {
|
fn get_current_trap_cx(&self) -> &'static mut TrapContext {
|
||||||
let inner = self.inner.exclusive_access();
|
let inner = self.inner.exclusive_access();
|
||||||
inner.tasks[inner.current_task].get_trap_cx()
|
inner.tasks[inner.current_task].get_trap_cx()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Switch current `Running` task to the task we have found,
|
||||||
|
/// or there is no `Ready` task and we can exit with all applications completed
|
||||||
fn run_next_task(&self) {
|
fn run_next_task(&self) {
|
||||||
if let Some(next) = self.find_next_task() {
|
if let Some(next) = self.find_next_task() {
|
||||||
let mut inner = self.inner.exclusive_access();
|
let mut inner = self.inner.exclusive_access();
|
||||||
@ -109,36 +148,45 @@ impl TaskManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Run the first task in task list.
|
||||||
pub fn run_first_task() {
|
pub fn run_first_task() {
|
||||||
TASK_MANAGER.run_first_task();
|
TASK_MANAGER.run_first_task();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Switch current `Running` task to the task we have found,
|
||||||
|
/// or there is no `Ready` task and we can exit with all applications completed
|
||||||
fn run_next_task() {
|
fn run_next_task() {
|
||||||
TASK_MANAGER.run_next_task();
|
TASK_MANAGER.run_next_task();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change the status of current `Running` task into `Ready`.
|
||||||
fn mark_current_suspended() {
|
fn mark_current_suspended() {
|
||||||
TASK_MANAGER.mark_current_suspended();
|
TASK_MANAGER.mark_current_suspended();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Change the status of current `Running` task into `Exited`.
|
||||||
fn mark_current_exited() {
|
fn mark_current_exited() {
|
||||||
TASK_MANAGER.mark_current_exited();
|
TASK_MANAGER.mark_current_exited();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Suspend the current 'Running' task and run the next task in task list.
|
||||||
pub fn suspend_current_and_run_next() {
|
pub fn suspend_current_and_run_next() {
|
||||||
mark_current_suspended();
|
mark_current_suspended();
|
||||||
run_next_task();
|
run_next_task();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Exit the current 'Running' task and run the next task in task list.
|
||||||
pub fn exit_current_and_run_next() {
|
pub fn exit_current_and_run_next() {
|
||||||
mark_current_exited();
|
mark_current_exited();
|
||||||
run_next_task();
|
run_next_task();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current 'Running' task's token.
|
||||||
pub fn current_user_token() -> usize {
|
pub fn current_user_token() -> usize {
|
||||||
TASK_MANAGER.get_current_token()
|
TASK_MANAGER.get_current_token()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Get the current 'Running' task's trap contexts.
|
||||||
pub fn current_trap_cx() -> &'static mut TrapContext {
|
pub fn current_trap_cx() -> &'static mut TrapContext {
|
||||||
TASK_MANAGER.get_current_trap_cx()
|
TASK_MANAGER.get_current_trap_cx()
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,15 @@
|
|||||||
use super::TaskContext;
|
//! Rust wrapper around `__switch`.
|
||||||
use core::arch::global_asm;
|
//!
|
||||||
|
//! Switching to a different task's context happens here. The actual
|
||||||
|
//! implementation must not be in Rust and (essentially) has to be in assembly
|
||||||
|
//! language (Do you know why?), so this module really is just a wrapper around
|
||||||
|
//! `switch.S`.
|
||||||
|
|
||||||
global_asm!(include_str!("switch.S"));
|
core::arch::global_asm!(include_str!("switch.S"));
|
||||||
|
use super::TaskContext;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
/// Switch to the context of `next_task_cx_ptr`, saving the current context
|
||||||
|
/// in `current_task_cx_ptr`.
|
||||||
pub fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext);
|
pub fn __switch(current_task_cx_ptr: *mut TaskContext, next_task_cx_ptr: *const TaskContext);
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
|
//! Types related to task management
|
||||||
use super::TaskContext;
|
use super::TaskContext;
|
||||||
use crate::config::{kernel_stack_position, TRAP_CONTEXT};
|
use crate::config::{kernel_stack_position, TRAP_CONTEXT};
|
||||||
use crate::mm::{MapPermission, MemorySet, PhysPageNum, VirtAddr, KERNEL_SPACE};
|
use crate::mm::{MapPermission, MemorySet, PhysPageNum, VirtAddr, KERNEL_SPACE};
|
||||||
use crate::trap::{trap_handler, TrapContext};
|
use crate::trap::{trap_handler, TrapContext};
|
||||||
|
|
||||||
|
/// task control block structure
|
||||||
pub struct TaskControlBlock {
|
pub struct TaskControlBlock {
|
||||||
pub task_status: TaskStatus,
|
pub task_status: TaskStatus,
|
||||||
pub task_cx: TaskContext,
|
pub task_cx: TaskContext,
|
||||||
@ -54,6 +56,7 @@ impl TaskControlBlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq)]
|
#[derive(Copy, Clone, PartialEq)]
|
||||||
|
/// task status: UnInit, Ready, Running, Exited
|
||||||
pub enum TaskStatus {
|
pub enum TaskStatus {
|
||||||
Ready,
|
Ready,
|
||||||
Running,
|
Running,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! RISC-V timer-related functionality
|
||||||
|
|
||||||
use crate::config::CLOCK_FREQ;
|
use crate::config::CLOCK_FREQ;
|
||||||
use crate::sbi::set_timer;
|
use crate::sbi::set_timer;
|
||||||
use riscv::register::time;
|
use riscv::register::time;
|
||||||
@ -9,10 +11,12 @@ pub fn get_time() -> usize {
|
|||||||
time::read()
|
time::read()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// get current time in microseconds
|
||||||
pub fn get_time_ms() -> usize {
|
pub fn get_time_ms() -> usize {
|
||||||
time::read() / (CLOCK_FREQ / MSEC_PER_SEC)
|
time::read() / (CLOCK_FREQ / MSEC_PER_SEC)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// set the next timer interrupt
|
||||||
pub fn set_next_trigger() {
|
pub fn set_next_trigger() {
|
||||||
set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);
|
set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
|
//! Implementation of [`TrapContext`]
|
||||||
|
|
||||||
use riscv::register::sstatus::{self, Sstatus, SPP};
|
use riscv::register::sstatus::{self, Sstatus, SPP};
|
||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
|
/// trap context structure containing sstatus, sepc and registers
|
||||||
pub struct TrapContext {
|
pub struct TrapContext {
|
||||||
pub x: [usize; 32],
|
pub x: [usize; 32],
|
||||||
pub sstatus: Sstatus,
|
pub sstatus: Sstatus,
|
||||||
|
@ -1,3 +1,16 @@
|
|||||||
|
//! Trap handling functionality
|
||||||
|
//!
|
||||||
|
//! For rCore, we have a single trap entry point, namely `__alltraps`. At
|
||||||
|
//! initialization in [`init()`], we set the `stvec` CSR to point to it.
|
||||||
|
//!
|
||||||
|
//! All traps go through `__alltraps`, which is defined in `trap.S`. The
|
||||||
|
//! assembly language code does just enough work restore the kernel space
|
||||||
|
//! context, ensuring that Rust code safely runs, and transfers control to
|
||||||
|
//! [`trap_handler()`].
|
||||||
|
//!
|
||||||
|
//! It then calls different functionality based on what exactly the exception
|
||||||
|
//! was. For example, timer interrupts trigger task preemption, and syscalls go
|
||||||
|
//! to [`syscall()`].
|
||||||
mod context;
|
mod context;
|
||||||
|
|
||||||
use crate::config::{TRAMPOLINE, TRAP_CONTEXT};
|
use crate::config::{TRAMPOLINE, TRAP_CONTEXT};
|
||||||
|
Loading…
Reference in New Issue
Block a user