-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathmmu.rs
113 lines (94 loc) · 3.49 KB
/
mmu.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
110
111
112
113
use crate::{cpu::Registers, mem::MappedMemory};
/// Memory Management Unit (MMU)
#[derive(Default)]
pub struct Mmu {}
pub enum EntryDecodingResult {
Decoded(u32),
PassThrough,
PermissionNotSet,
HwException(u16),
}
impl Mmu {
pub fn new() -> Self {
Self {}
}
pub fn decode_entry(
&mut self,
mem: &mut MappedMemory,
regs: &Registers,
entry_addr: u32,
action: MemAction,
) -> EntryDecodingResult {
// Exception receiver
let mut ex = 0;
// Get the permissions from the provided entry address in memory
let v_entry = mem.read(entry_addr, &mut ex);
// Handle memory errors
if ex != 0 {
return EntryDecodingResult::HwException(ex);
}
// Check if pass-through is enabled for this entry
if v_entry & (0b1 << if regs.smt != 0 { 31 } else { 30 }) == 0b0 {
return EntryDecodingResult::PassThrough;
}
// 1. Determine the shift for current mode
let mode_shift = if regs.smt != 0 { 3 } else { 0 };
// 2. Determine the shift for the provided type of action
let action_shift = match action {
MemAction::Read => 2,
MemAction::Write => 1,
MemAction::Exec => 0,
};
// 3. Check if the permission bit is set
if (v_entry & (0b1 << (24 + action_shift + mode_shift))) == 1 {
// 4. If so, get the weakest 24 bits as the entry value
EntryDecodingResult::Decoded(v_entry & 0b1111_1111_1111_1111_1111_1111)
} else {
// 5. Else, return an error
EntryDecodingResult::PermissionNotSet
}
}
pub fn translate(
&mut self,
mem: &mut MappedMemory,
regs: &Registers,
v_addr: u32,
action: MemAction,
) -> Result<u32, Option<u16>> {
// Skip this if the MMU is disabled
if regs.mtt == 0 {
return Ok(v_addr);
}
// Get the entry number in the VPI
let vpi_entry_number = v_addr & 0b11_1111_1111;
// Get the address of the VPI entry to read
let vpi_entry_addr = regs.pda + (vpi_entry_number * 4);
// Get the virtual page number from the VPI entry
let v_page_number = match self.decode_entry(mem, regs, vpi_entry_addr, action) {
EntryDecodingResult::Decoded(value) => value,
EntryDecodingResult::PassThrough => return Ok(v_addr),
EntryDecodingResult::PermissionNotSet => return Err(None),
EntryDecodingResult::HwException(ex) => return Err(Some(ex)),
};
// Get the address of the virtual page
let v_page_addr = v_page_number * 16384;
// Get the address of the virtual page entry to read
let v_page_entry_addr = v_page_addr + (v_addr.wrapping_shl(10) >> 22) * 4;
// Get the physical page's number from the virtual page entry
let p_page_number = match self.decode_entry(mem, regs, v_page_entry_addr, action) {
EntryDecodingResult::Decoded(value) => value,
EntryDecodingResult::PassThrough => return Ok(v_addr),
EntryDecodingResult::PermissionNotSet => return Err(None),
EntryDecodingResult::HwException(ex) => return Err(Some(ex)),
};
// Translate the virtual address into a physical one
Ok(p_page_number * 1024 + (v_addr & 0b11_1111_1111))
}
}
/// Memory action
#[derive(Copy, Clone, Debug)]
pub enum MemAction {
Read,
Write,
Exec,
}