virtual-machine 0.0.4 → 0.0.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/mmu.rs DELETED
@@ -1,331 +0,0 @@
1
- use crate::bus::Bus;
2
- use crate::csr::Mode;
3
- use crate::Trap;
4
-
5
- #[derive(Clone, Copy, PartialEq, Eq, Debug)]
6
- pub enum AccessType {
7
- Instruction,
8
- Load,
9
- Store,
10
- }
11
-
12
- const PAGE_SIZE: u64 = 4096;
13
- const PTE_SIZE: u64 = 8;
14
- const MAX_LEVELS: usize = 4;
15
-
16
- const TLB_SIZE: usize = 64;
17
-
18
- #[derive(Clone, Copy, Debug)]
19
- pub struct TlbEntry {
20
- pub vpn: u64,
21
- pub ppn: u64,
22
- pub valid: bool,
23
- pub asid: u64,
24
- pub global: bool, // Global mapping bit
25
- pub r: bool,
26
- pub w: bool,
27
- pub x: bool,
28
- pub u: bool,
29
- pub a: bool,
30
- pub d: bool,
31
- }
32
-
33
- impl Default for TlbEntry {
34
- fn default() -> Self {
35
- Self {
36
- vpn: 0,
37
- ppn: 0,
38
- valid: false,
39
- asid: 0,
40
- global: false,
41
- r: false,
42
- w: false,
43
- x: false,
44
- u: false,
45
- a: false,
46
- d: false,
47
- }
48
- }
49
- }
50
-
51
- pub struct Tlb {
52
- entries: [TlbEntry; TLB_SIZE],
53
- }
54
-
55
- impl Tlb {
56
- pub fn new() -> Self {
57
- Self {
58
- entries: [TlbEntry::default(); TLB_SIZE],
59
- }
60
- }
61
-
62
- pub fn flush(&mut self) {
63
- for entry in self.entries.iter_mut() {
64
- entry.valid = false;
65
- }
66
- }
67
-
68
- pub fn flush_asid(&mut self, asid: u64) {
69
- for entry in self.entries.iter_mut() {
70
- if !entry.global && entry.asid == asid {
71
- entry.valid = false;
72
- }
73
- }
74
- }
75
-
76
- pub fn flush_page(&mut self, vpn: u64, asid: u64) {
77
- let idx = (vpn as usize) % TLB_SIZE;
78
- let entry = &mut self.entries[idx];
79
-
80
- if entry.valid && entry.vpn == vpn {
81
- // For page-specific flush, invalidate matching ASID mappings.
82
- // Global mappings ignore ASID and are treated as matching.
83
- let match_asid = entry.global || entry.asid == asid;
84
- if match_asid {
85
- entry.valid = false;
86
- }
87
- }
88
- }
89
-
90
- pub fn lookup(&self, vpn: u64, asid: u64) -> Option<&TlbEntry> {
91
- let idx = (vpn as usize) % TLB_SIZE;
92
- let entry = &self.entries[idx];
93
-
94
- // Hit if: valid AND VPN matches AND (entry is global OR ASID matches).
95
- if entry.valid && entry.vpn == vpn && (entry.global || entry.asid == asid) {
96
- Some(entry)
97
- } else {
98
- None
99
- }
100
- }
101
-
102
- pub fn insert(&mut self, entry: TlbEntry) {
103
- let idx = (entry.vpn as usize) % TLB_SIZE;
104
- self.entries[idx] = entry;
105
- }
106
- }
107
-
108
- /// Sv39/Sv48 translation + A/D bit updates.
109
- ///
110
- /// `addr` is a virtual address. Returns the translated physical address or a
111
- /// `Trap` corresponding to the appropriate page/access fault.
112
- pub fn translate(
113
- bus: &mut dyn Bus,
114
- tlb: &mut Tlb,
115
- mode: Mode,
116
- satp: u64,
117
- mstatus: u64,
118
- addr: u64,
119
- access_type: AccessType,
120
- ) -> Result<u64, Trap> {
121
- // No translation in Machine mode (always Bare).
122
- if mode == Mode::Machine {
123
- return Ok(addr);
124
- }
125
-
126
- let satp_mode = (satp >> 60) & 0xF;
127
- let current_asid = (satp >> 44) & 0xFFFF;
128
-
129
- let (levels, va_bits, vpn_full_mask): (usize, u64, u64) = match satp_mode {
130
- 0 => {
131
- // Bare: no translation.
132
- return Ok(addr);
133
- }
134
- 8 => {
135
- // Sv39
136
- let levels = 3;
137
- let va_bits = 39;
138
- let vpn_full_mask = (1u64 << (9 * levels)) - 1;
139
- (levels, va_bits, vpn_full_mask)
140
- }
141
- 9 => {
142
- // Sv48 (supported by this MMU, though not required for virt).
143
- let levels = 4;
144
- let va_bits = 48;
145
- let vpn_full_mask = (1u64 << (9 * levels)) - 1;
146
- (levels, va_bits, vpn_full_mask)
147
- }
148
- _ => {
149
- // Unsupported mode: treat as Bare.
150
- return Ok(addr);
151
- }
152
- };
153
-
154
- // Check canonical form of the virtual address for the configured VA width.
155
- let sign_bit = va_bits - 1;
156
- let upper_mask = !((1u64 << va_bits) - 1);
157
- let sign = (addr >> sign_bit) & 1;
158
- let expected_upper = if sign == 1 { upper_mask } else { 0 };
159
- if (addr & upper_mask) != expected_upper {
160
- return Err(page_fault(access_type, addr));
161
- }
162
-
163
- let vpn_full = (addr >> 12) & vpn_full_mask;
164
-
165
- // TLB hit path.
166
- if let Some(entry) = tlb.lookup(vpn_full, current_asid) {
167
- if check_permission_tlb(mode, mstatus, entry, access_type) {
168
- // For now we do not lazily update A/D on TLB hits – page table
169
- // entries are already marked by the walk that inserted this entry.
170
- let offset = addr & 0xFFF;
171
- let pa = (entry.ppn << 12) | offset;
172
- return Ok(pa);
173
- } else {
174
- return Err(page_fault(access_type, addr));
175
- }
176
- }
177
-
178
- // Page table walk on TLB miss.
179
- let mut vpn = [0u64; MAX_LEVELS];
180
- for level in 0..levels {
181
- vpn[level] = (addr >> (12 + 9 * level as u64)) & 0x1FF;
182
- }
183
-
184
- let root_ppn = satp & ((1u64 << 44) - 1);
185
- let mut a = root_ppn * PAGE_SIZE;
186
-
187
- for i in (0..levels).rev() {
188
- let pte_addr = a + vpn[i] * PTE_SIZE;
189
-
190
- let pte = match bus.load(pte_addr, 8) {
191
- Ok(val) => val,
192
- Err(_) => return Err(access_fault(access_type, addr)),
193
- };
194
-
195
- let v = (pte >> 0) & 1;
196
- let r = (pte >> 1) & 1;
197
- let w = (pte >> 2) & 1;
198
- let x = (pte >> 3) & 1;
199
-
200
- // Invalid or malformed.
201
- if v == 0 || (r == 0 && w == 1) {
202
- return Err(page_fault(access_type, addr));
203
- }
204
-
205
- // Pointer to next level if R=X=0.
206
- if r == 0 && x == 0 {
207
- if i == 0 {
208
- return Err(page_fault(access_type, addr));
209
- }
210
- let ppn = (pte >> 10) & 0xFFF_FFFF_FFFF;
211
- a = ppn * PAGE_SIZE;
212
- continue;
213
- }
214
-
215
- // Leaf PTE.
216
- let mut entry = TlbEntry {
217
- vpn: vpn_full,
218
- ppn: (pte >> 10) & 0xFFF_FFFF_FFFF,
219
- valid: true,
220
- asid: current_asid,
221
- global: (pte >> 5) & 1 != 0, // G bit
222
- r: r != 0,
223
- w: w != 0,
224
- x: x != 0,
225
- u: (pte >> 4) & 1 != 0,
226
- a: (pte >> 6) & 1 != 0,
227
- d: (pte >> 7) & 1 != 0,
228
- };
229
-
230
- if !check_permission_tlb(mode, mstatus, &entry, access_type) {
231
- return Err(page_fault(access_type, addr));
232
- }
233
-
234
- // Superpage alignment checks (Sv39/48 spec).
235
- if i > 0 {
236
- let ppn_mask = (1 << (9 * i)) - 1;
237
- let ppn = (pte >> 10) & 0xFFF_FFFF_FFFF;
238
- if (ppn & ppn_mask) != 0 {
239
- return Err(page_fault(access_type, addr));
240
- }
241
- }
242
-
243
- // A/D bit updates: set in memory and in the cached entry.
244
- let mut new_pte = pte;
245
- let mut update = false;
246
-
247
- if !entry.a {
248
- new_pte |= 1 << 6;
249
- entry.a = true;
250
- update = true;
251
- }
252
- if matches!(access_type, AccessType::Store) && !entry.d {
253
- new_pte |= 1 << 7;
254
- entry.d = true;
255
- update = true;
256
- }
257
-
258
- if update {
259
- if bus.store(pte_addr, 8, new_pte).is_err() {
260
- return Err(access_fault(access_type, addr));
261
- }
262
- }
263
-
264
- let offset_in_page = addr & 0xFFF;
265
-
266
- // Construct final PPN, filling low parts from the VA on superpages.
267
- let ppn = (pte >> 10) & 0xFFF_FFFF_FFFF;
268
- let vpn_mask = (1 << (9 * i)) - 1;
269
- let result_ppn = (ppn & !vpn_mask) | ((addr >> 12) & vpn_mask);
270
-
271
- entry.ppn = result_ppn;
272
- tlb.insert(entry);
273
-
274
- let pa = (result_ppn << 12) | offset_in_page;
275
- return Ok(pa);
276
- }
277
-
278
- Err(page_fault(access_type, addr))
279
- }
280
-
281
- fn check_permission_tlb(mode: Mode, mstatus: u64, entry: &TlbEntry, access_type: AccessType) -> bool {
282
- let mxr = (mstatus >> 19) & 1;
283
- let sum = (mstatus >> 18) & 1;
284
-
285
- match mode {
286
- Mode::Supervisor => {
287
- if entry.u {
288
- if matches!(access_type, AccessType::Instruction) {
289
- return false;
290
- }
291
- if sum == 0 {
292
- return false;
293
- }
294
- }
295
- }
296
- Mode::User => {
297
- if !entry.u {
298
- return false;
299
- }
300
- }
301
- Mode::Machine => {}
302
- }
303
-
304
- match access_type {
305
- AccessType::Instruction => entry.x,
306
- AccessType::Store => entry.w,
307
- AccessType::Load => {
308
- if entry.r {
309
- true
310
- } else {
311
- mxr == 1 && entry.x
312
- }
313
- }
314
- }
315
- }
316
-
317
- fn page_fault(access_type: AccessType, addr: u64) -> Trap {
318
- match access_type {
319
- AccessType::Instruction => Trap::InstructionPageFault(addr),
320
- AccessType::Load => Trap::LoadPageFault(addr),
321
- AccessType::Store => Trap::StorePageFault(addr),
322
- }
323
- }
324
-
325
- fn access_fault(access_type: AccessType, addr: u64) -> Trap {
326
- match access_type {
327
- AccessType::Instruction => Trap::InstructionAccessFault(addr),
328
- AccessType::Load => Trap::LoadAccessFault(addr),
329
- AccessType::Store => Trap::StoreAccessFault(addr),
330
- }
331
- }
package/src/net.rs DELETED
@@ -1,121 +0,0 @@
1
- //! Network backend abstraction for VirtIO networking.
2
- //!
3
- //! This module defines the `NetworkBackend` trait that abstracts packet I/O
4
- //! to support both Host (TAP) and WASM (WebSocket) environments.
5
-
6
- /// Trait for network backends that provide packet I/O.
7
- ///
8
- /// Implementations must be `Send` to allow the backend to be used
9
- /// across thread boundaries (e.g., when the VM runs in a separate thread).
10
- pub trait NetworkBackend: Send {
11
- /// Initialize the backend (e.g., open TAP device or connect WebSocket).
12
- fn init(&mut self) -> Result<(), String>;
13
-
14
- /// Poll for an incoming packet. Returns None if no packet is available.
15
- /// This should be non-blocking.
16
- fn recv(&mut self) -> Result<Option<Vec<u8>>, String>;
17
-
18
- /// Send a packet.
19
- fn send(&self, buf: &[u8]) -> Result<(), String>;
20
-
21
- /// Get the MAC address of the backend (if available).
22
- /// Returns a default MAC if the backend doesn't have one.
23
- fn mac_address(&self) -> [u8; 6] {
24
- // Default MAC: locally administered, unicast
25
- [0x52, 0x54, 0x00, 0x12, 0x34, 0x56]
26
- }
27
- }
28
-
29
- /// A no-op network backend for testing purposes.
30
- ///
31
- /// This backend discards all sent packets and never receives any packets.
32
- pub struct DummyBackend {
33
- initialized: bool,
34
- mac: [u8; 6],
35
- }
36
-
37
- impl DummyBackend {
38
- pub fn new() -> Self {
39
- Self {
40
- initialized: false,
41
- mac: [0x52, 0x54, 0x00, 0x12, 0x34, 0x56],
42
- }
43
- }
44
-
45
- /// Create a dummy backend with a custom MAC address.
46
- pub fn with_mac(mac: [u8; 6]) -> Self {
47
- Self {
48
- initialized: false,
49
- mac,
50
- }
51
- }
52
- }
53
-
54
- impl Default for DummyBackend {
55
- fn default() -> Self {
56
- Self::new()
57
- }
58
- }
59
-
60
- impl NetworkBackend for DummyBackend {
61
- fn init(&mut self) -> Result<(), String> {
62
- self.initialized = true;
63
- log::debug!("[DummyBackend] Initialized (no-op)");
64
- Ok(())
65
- }
66
-
67
- fn recv(&mut self) -> Result<Option<Vec<u8>>, String> {
68
- // No packets ever available
69
- Ok(None)
70
- }
71
-
72
- fn send(&self, buf: &[u8]) -> Result<(), String> {
73
- // Discard packet, but log it for debugging
74
- log::trace!("[DummyBackend] Discarding {} byte packet", buf.len());
75
- Ok(())
76
- }
77
-
78
- fn mac_address(&self) -> [u8; 6] {
79
- self.mac
80
- }
81
- }
82
-
83
- #[cfg(test)]
84
- mod tests {
85
- use super::*;
86
-
87
- #[test]
88
- fn test_dummy_backend_init() {
89
- let mut backend = DummyBackend::new();
90
- assert!(backend.init().is_ok());
91
- }
92
-
93
- #[test]
94
- fn test_dummy_backend_recv_returns_none() {
95
- let mut backend = DummyBackend::new();
96
- backend.init().unwrap();
97
- assert!(backend.recv().unwrap().is_none());
98
- }
99
-
100
- #[test]
101
- fn test_dummy_backend_send_succeeds() {
102
- let backend = DummyBackend::new();
103
- assert!(backend.send(&[1, 2, 3, 4]).is_ok());
104
- }
105
-
106
- #[test]
107
- fn test_dummy_backend_mac_address() {
108
- let backend = DummyBackend::new();
109
- let mac = backend.mac_address();
110
- // Check locally administered bit is set (second bit of first byte)
111
- assert_eq!(mac[0] & 0x02, 0x02);
112
- }
113
-
114
- #[test]
115
- fn test_dummy_backend_custom_mac() {
116
- let custom_mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
117
- let backend = DummyBackend::with_mac(custom_mac);
118
- assert_eq!(backend.mac_address(), custom_mac);
119
- }
120
- }
121
-