rusty_mos/memory/
tlbex.rs

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
use crate::{
    memory::regions::{
        NASID, PAGE_SIZE, PGSHIFT, PTE_D, UENVS, ULIM, UPAGES, USTACKTOP, UTEMP, UVPT, UXSTACKTOP,
    },
    process::envs::{CUR_ENV_IDX, ENVS_DATA},
    GEN_MASK, PTE_ADDR,
};
use core::mem::{size_of, size_of_val};
use core::sync::atomic::Ordering::SeqCst;

use crate::memory::pmap::{page_alloc, page_insert, page_lookup, Pde, Pte, CUR_PGDIR};

use crate::kernel::trap::TrapFrame;

extern "C" {
    /// ASM-Functions. Clear the tlb entry.
    pub fn tlb_out(entryhi: u32);
}

/// Invalidate the specified tlb entry.
pub fn tlb_invalidate(asid: u32, va: usize) {
    let entryhi = (va & !GEN_MASK!(PGSHIFT, 0)) as u32 | (asid & (NASID - 1));
    unsafe {
        tlb_out(entryhi);
    }
}

/// Alloc a page and map it to the virtual address `va`.
fn passive_alloc(va: usize, pgdir: *mut Pde, asid: u32) {
    if va < UTEMP {
        panic!("Address too low");
    }

    if (USTACKTOP..USTACKTOP + PAGE_SIZE).contains(&va) {
        panic!("Invalid memory");
    }

    if (UENVS..UPAGES).contains(&va) {
        panic!("Envs zero");
    }

    if (UPAGES..UVPT).contains(&va) {
        panic!("Pages zero");
    }

    if va >= ULIM {
        panic!("Kernel address");
    }
    let pp = page_alloc().unwrap();
    page_insert(
        pgdir,
        PTE_ADDR!(va),
        asid,
        if (UVPT..ULIM).contains(&va) { 0 } else { PTE_D },
        pp,
    )
    .unwrap();
}

/// Do TLB Refill.
#[no_mangle]
pub fn _do_tlb_refill(pentrylo: &mut [u32; 2], va: usize, asid: u32) {
    tlb_invalidate(asid, va);

    loop {
        let cur_pgdir = *CUR_PGDIR.borrow();
        match page_lookup(cur_pgdir, va) {
            None => passive_alloc(va, cur_pgdir, asid),
            Some((_, ppte)) => {
                let ppte = (ppte as u32 & !0x7) as *mut Pte;

                pentrylo[0] = unsafe { *ppte } >> 6;
                pentrylo[1] = unsafe { *ppte.wrapping_add(1) } >> 6;

                break;
            }
        }
    }
}

/// Handle the TLB Mod Exception. Invoke the
/// [tlb_mod_entry](crate::process::envs::EnvData::user_tlb_mod_entry) via
/// `eret`.
#[no_mangle]
pub fn do_tlb_mod(trapframe: *mut TrapFrame) {
    let tf = trapframe; // deceits
    let stored_trapframe = unsafe { *tf };
    let mut modified_trapframe = unsafe { *tf };
    if !(USTACKTOP..UXSTACKTOP).contains(&(modified_trapframe.regs[29] as usize)) {
        modified_trapframe.regs[29] = UXSTACKTOP as u32;
    }
    modified_trapframe.regs[29] -= size_of::<TrapFrame>() as u32;
    let target = modified_trapframe.regs[29] as *mut TrapFrame;
    unsafe { target.write(stored_trapframe) };
    // Pte was ignored in the C-Edition Mos
    let _ = page_lookup(
        *CUR_PGDIR.borrow(),
        modified_trapframe.cp0_badvaddr as usize,
    );
    let tlb_entry = ENVS_DATA.borrow().0[CUR_ENV_IDX.load(SeqCst)].user_tlb_mod_entry;
    if tlb_entry != 0 {
        modified_trapframe.regs[4] = modified_trapframe.regs[29];
        modified_trapframe.regs[29] -= size_of_val(&modified_trapframe.regs[4]) as u32;
        modified_trapframe.cp0_epc = tlb_entry;
    } else {
        panic!("TLB Mod but no user handler registered")
    }
    unsafe { core::ptr::write_volatile(tf, modified_trapframe) }
}