    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147

    //! batch subsystem

    use crate::sync::UPSafeCell;
    use crate::trap::TrapContext;
    use core::arch::asm;
    use lazy_static::*;

    const USER_STACK_SIZE: usize = 4096 * 2;
    const KERNEL_STACK_SIZE: usize = 4096 * 2;
    const MAX_APP_NUM: usize = 16;
    const APP_BASE_ADDRESS: usize = 0x80400000;
    const APP_SIZE_LIMIT: usize = 0x20000;

    #[repr(align(4096))]
    struct KernelStack {
        data: [u8; KERNEL_STACK_SIZE],
    }

    #[repr(align(4096))]
    struct UserStack {
        data: [u8; USER_STACK_SIZE],
    }

    static KERNEL_STACK: KernelStack = KernelStack {
        data: [0; KERNEL_STACK_SIZE],
    };
    static USER_STACK: UserStack = UserStack {
        data: [0; USER_STACK_SIZE],
    };

    impl KernelStack {
        fn get_sp(&self) -> usize {
            self.data.as_ptr() as usize + KERNEL_STACK_SIZE
        }
        pub fn push_context(&self, cx: TrapContext) -> &'static mut TrapContext {
            let cx_ptr = (self.get_sp() - core::mem::size_of::<TrapContext>()) as *mut TrapContext;
            unsafe {
                *cx_ptr = cx;
            }
            unsafe { cx_ptr.as_mut().unwrap() }
        }
    }

    impl UserStack {
        fn get_sp(&self) -> usize {
            self.data.as_ptr() as usize + USER_STACK_SIZE
        }
    }

    struct AppManager {
        num_app: usize,
        current_app: usize,
        app_start: [usize; MAX_APP_NUM + 1],
    }

    impl AppManager {
        pub fn print_app_info(&self) {
            println!("[kernel] num_app = {}", self.num_app);
            for i in 0..self.num_app {
                println!(
                    "[kernel] app_{} [{:#x}, {:#x})",
                    i,
                    self.app_start[i],
                    self.app_start[i + 1]
                );
            }
        }

        unsafe fn load_app(&self, app_id: usize) {
            if app_id >= self.num_app {
                panic!("All applications completed!");
            }
            println!("[kernel] Loading app_{}", app_id);
            // clear icache
            asm!("fence.i");
            // clear app area
            core::slice::from_raw_parts_mut(APP_BASE_ADDRESS as *mut u8, APP_SIZE_LIMIT).fill(0);
            let app_src = core::slice::from_raw_parts(
                self.app_start[app_id] as *const u8,
                self.app_start[app_id + 1] - self.app_start[app_id],
            );
            let app_dst = core::slice::from_raw_parts_mut(APP_BASE_ADDRESS as *mut u8, app_src.len());
            app_dst.copy_from_slice(app_src);
        }

        pub fn get_current_app(&self) -> usize {
            self.current_app
        }

        pub fn move_to_next_app(&mut self) {
            self.current_app += 1;
        }
    }

    lazy_static! {
        static ref APP_MANAGER: UPSafeCell<AppManager> = unsafe {
            UPSafeCell::new({
                extern "C" {
                    fn _num_app();
                }
                let num_app_ptr = _num_app as usize as *const usize;
                let num_app = num_app_ptr.read_volatile();
                let mut app_start: [usize; MAX_APP_NUM + 1] = [0; MAX_APP_NUM + 1];
                let app_start_raw: &[usize] =
                    core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1);
                app_start[..=num_app].copy_from_slice(app_start_raw);
                AppManager {
                    num_app,
                    current_app: 0,
                    app_start,
                }
            })
        };
    }

    /// init batch subsystem
    pub fn init() {
        print_app_info();
    }

    /// print apps info
    pub fn print_app_info() {
        APP_MANAGER.exclusive_access().print_app_info();
    }

    /// run next app
    pub fn run_next_app() -> ! {
        let mut app_manager = APP_MANAGER.exclusive_access();
        let current_app = app_manager.get_current_app();
        unsafe {
            app_manager.load_app(current_app);
        }
        app_manager.move_to_next_app();
        drop(app_manager);
        // before this we have to drop local variables related to resources manually
        // and release the resources
        extern "C" {
            fn __restore(cx_addr: usize);
        }
        unsafe {
            __restore(KERNEL_STACK.push_context(TrapContext::app_init_context(
                APP_BASE_ADDRESS,
                USER_STACK.get_sp(),
            )) as *const _ as usize);
        }
        panic!("Unreachable in batch::run_current_app!");
    }