Skip to main content

os/
batch.rs

1//! batch subsystem
2
3use crate::sbi::shutdown;
4use crate::sync::UPSafeCell;
5use crate::trap::TrapContext;
6use core::arch::asm;
7use lazy_static::*;
8
9const USER_STACK_SIZE: usize = 4096 * 2;
10const KERNEL_STACK_SIZE: usize = 4096 * 2;
11const MAX_APP_NUM: usize = 16;
12const APP_BASE_ADDRESS: usize = 0x80400000;
13const APP_SIZE_LIMIT: usize = 0x20000;
14
15#[repr(align(4096))]
16struct KernelStack {
17    data: [u8; KERNEL_STACK_SIZE],
18}
19
20#[repr(align(4096))]
21struct UserStack {
22    data: [u8; USER_STACK_SIZE],
23}
24
25static KERNEL_STACK: KernelStack = KernelStack {
26    data: [0; KERNEL_STACK_SIZE],
27};
28static USER_STACK: UserStack = UserStack {
29    data: [0; USER_STACK_SIZE],
30};
31
32impl KernelStack {
33    fn get_sp(&self) -> usize {
34        self.data.as_ptr() as usize + KERNEL_STACK_SIZE
35    }
36    pub fn push_context(&self, cx: TrapContext) -> &'static mut TrapContext {
37        let cx_ptr = (self.get_sp() - core::mem::size_of::<TrapContext>()) as *mut TrapContext;
38        unsafe {
39            *cx_ptr = cx;
40        }
41        unsafe { cx_ptr.as_mut().unwrap() }
42    }
43}
44
45impl UserStack {
46    fn get_sp(&self) -> usize {
47        self.data.as_ptr() as usize + USER_STACK_SIZE
48    }
49}
50
51struct AppManager {
52    num_app: usize,
53    current_app: usize,
54    app_start: [usize; MAX_APP_NUM + 1],
55}
56
57impl AppManager {
58    pub fn print_app_info(&self) {
59        println!("[kernel] num_app = {}", self.num_app);
60        for i in 0..self.num_app {
61            println!(
62                "[kernel] app_{} [{:#x}, {:#x})",
63                i,
64                self.app_start[i],
65                self.app_start[i + 1]
66            );
67        }
68    }
69
70    fn load_app(&self, app_id: usize) {
71        if app_id >= self.num_app {
72            println!("All applications completed!");
73            shutdown(false);
74        }
75        println!("[kernel] Loading app_{}", app_id);
76        unsafe {
77            // clear app area
78            core::slice::from_raw_parts_mut(APP_BASE_ADDRESS as *mut u8, APP_SIZE_LIMIT).fill(0);
79            let app_src = core::slice::from_raw_parts(
80                self.app_start[app_id] as *const u8,
81                self.app_start[app_id + 1] - self.app_start[app_id],
82            );
83            let app_dst =
84                core::slice::from_raw_parts_mut(APP_BASE_ADDRESS as *mut u8, app_src.len());
85            app_dst.copy_from_slice(app_src);
86            // Memory fence about fetching the instruction memory
87            // It is guaranteed that a subsequent instruction fetch must
88            // observes all previous writes to the instruction memory.
89            // Therefore, fence.i must be executed after we have loaded
90            // the code of the next app into the instruction memory.
91            // See also: riscv non-priv spec chapter 3, 'Zifencei' extension.
92            asm!("fence.i");
93        }
94    }
95
96    pub fn get_current_app(&self) -> usize {
97        self.current_app
98    }
99
100    pub fn move_to_next_app(&mut self) {
101        self.current_app += 1;
102    }
103}
104
105lazy_static! {
106    static ref APP_MANAGER: UPSafeCell<AppManager> = unsafe {
107        UPSafeCell::new({
108            unsafe extern "C" {
109                safe fn _num_app();
110            }
111            let num_app_ptr = linker_symbol_addr!(_num_app) as *const usize;
112            let num_app = num_app_ptr.read_volatile();
113            let mut app_start: [usize; MAX_APP_NUM + 1] = [0; MAX_APP_NUM + 1];
114            let app_start_raw: &[usize] =
115                core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1);
116            app_start[..=num_app].copy_from_slice(app_start_raw);
117            AppManager {
118                num_app,
119                current_app: 0,
120                app_start,
121            }
122        })
123    };
124}
125
126/// init batch subsystem
127pub fn init() {
128    print_app_info();
129}
130
131/// print apps info
132pub fn print_app_info() {
133    APP_MANAGER.exclusive_access().print_app_info();
134}
135
136/// run next app
137pub fn run_next_app() -> ! {
138    let mut app_manager = APP_MANAGER.exclusive_access();
139    let current_app = app_manager.get_current_app();
140    app_manager.load_app(current_app);
141    app_manager.move_to_next_app();
142    drop(app_manager);
143    // before this we have to drop local variables related to resources manually
144    // and release the resources
145    unsafe extern "C" {
146        unsafe fn __restore(cx_addr: usize);
147    }
148    unsafe {
149        __restore(KERNEL_STACK.push_context(TrapContext::app_init_context(
150            APP_BASE_ADDRESS,
151            USER_STACK.get_sp(),
152        )) as *const _ as usize);
153    }
154    panic!("Unreachable in batch::run_current_app!");
155}