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/plic.rs DELETED
@@ -1,261 +0,0 @@
1
- use crate::dram::MemoryError;
2
-
3
- pub const PLIC_BASE: u64 = 0x0C00_0000;
4
- pub const PLIC_SIZE: u64 = 0x400_0000;
5
-
6
- pub const UART_IRQ: u32 = 10;
7
- pub const VIRTIO0_IRQ: u32 = 1;
8
-
9
- const NUM_SOURCES: usize = 32;
10
- const NUM_CONTEXTS: usize = 2; // 0 = M-mode hart0, 1 = S-mode hart0
11
-
12
- pub struct Plic {
13
- pub priority: [u32; NUM_SOURCES],
14
- pub pending: u32, // Level-triggered mirror of device IRQ lines (bit per source)
15
- pub enable: [u32; NUM_CONTEXTS],
16
- pub threshold: [u32; NUM_CONTEXTS],
17
- pub active: [u32; NUM_CONTEXTS], // Per-context in-flight IRQs (claimed but not completed)
18
- pub debug: bool,
19
- // Multi-context arrays enable SMP readiness while preserving single-hart behavior.
20
- }
21
-
22
- impl Plic {
23
- pub fn new() -> Self {
24
- Self {
25
- priority: [0; NUM_SOURCES],
26
- pending: 0,
27
- enable: [0; NUM_CONTEXTS],
28
- threshold: [0; NUM_CONTEXTS],
29
- active: [0; NUM_CONTEXTS],
30
- debug: false,
31
- }
32
- }
33
-
34
- pub fn update_pending(&mut self, source: u32) {
35
- // Backward compatibility helper: set as pending (edge → level).
36
- // Bus.refresh_irqs() may later clear this if device line is low.
37
- if source < 32 {
38
- if self.debug {
39
- eprintln!("[PLIC] Update Pending source={}", source);
40
- }
41
- self.pending |= 1 << source;
42
- }
43
- }
44
-
45
- // New: level-triggered source line setter
46
- pub fn set_source_level(&mut self, source: u32, level: bool) {
47
- if source >= 32 {
48
- return;
49
- }
50
- let was_pending = (self.pending & (1 << source)) != 0;
51
- if level {
52
- if self.debug && !was_pending {
53
- eprintln!("[PLIC] IRQ Line High: source={} enable[0]=0x{:x} enable[1]=0x{:x} prio={}",
54
- source, self.enable[0], self.enable[1], self.priority[source as usize]);
55
- }
56
- self.pending |= 1 << source;
57
- } else {
58
- self.pending &= !(1 << source);
59
- }
60
- }
61
-
62
- pub fn load(&mut self, offset: u64, size: u64) -> Result<u64, MemoryError> {
63
- if size != 4 {
64
- return Ok(0);
65
- }
66
-
67
- // Priority registers: 0x000000 .. 0x0000FC (4 bytes each)
68
- if offset < 0x001000 {
69
- let idx = (offset >> 2) as usize;
70
- if idx < NUM_SOURCES {
71
- return Ok(self.priority[idx] as u64);
72
- }
73
- }
74
- // Pending bits: 0x001000
75
- if offset == 0x001000 {
76
- return Ok(self.pending as u64);
77
- }
78
- // Enable per context: 0x002000 + 0x80 * context
79
- if offset >= 0x002000 && offset < 0x002000 + 0x80 * (NUM_CONTEXTS as u64) {
80
- let ctx = ((offset - 0x002000) / 0x80) as usize;
81
- let inner = (offset - 0x002000) % 0x80;
82
- if ctx < NUM_CONTEXTS && inner == 0 {
83
- return Ok(self.enable[ctx] as u64);
84
- }
85
- }
86
- // Context registers: threshold @ 0x200000 + 0x1000 * ctx, claim @ +4
87
- if offset >= 0x200000 {
88
- let ctx = ((offset - 0x200000) / 0x1000) as usize;
89
- if ctx < NUM_CONTEXTS {
90
- let base = 0x200000 + (0x1000 * ctx as u64);
91
- if offset == base {
92
- return Ok(self.threshold[ctx] as u64);
93
- }
94
- if offset == base + 4 {
95
- let claim = self.claim_interrupt_for(ctx);
96
- if crate::plic::Plic::debug_trace() {
97
- eprintln!("[PLIC] SCLAIM ctx={} -> {}", ctx, claim);
98
- }
99
- return Ok(claim as u64);
100
- }
101
- }
102
- }
103
-
104
- Ok(0)
105
- }
106
-
107
- fn debug_trace() -> bool {
108
- // Helper to check if trace logging is enabled without importing log everywhere if not needed
109
- // or just use std::env
110
- std::env::var("RUST_LOG").map(|s| s.contains("trace")).unwrap_or(false)
111
- }
112
-
113
- pub fn store(&mut self, offset: u64, size: u64, value: u64) -> Result<(), MemoryError> {
114
- if size != 4 {
115
- return Ok(());
116
- }
117
- let val = value as u32;
118
-
119
- // Priority
120
- if offset < 0x001000 {
121
- let idx = (offset >> 2) as usize;
122
- if idx < NUM_SOURCES {
123
- self.priority[idx] = val;
124
- }
125
- return Ok(());
126
- }
127
- // Pending is read-only to software
128
- if offset == 0x001000 {
129
- return Ok(());
130
- }
131
- // Enable per context
132
- if offset >= 0x002000 && offset < 0x002000 + 0x80 * (NUM_CONTEXTS as u64) {
133
- let ctx = ((offset - 0x002000) / 0x80) as usize;
134
- let inner = (offset - 0x002000) % 0x80;
135
- if ctx < NUM_CONTEXTS && inner == 0 {
136
- self.enable[ctx] = val;
137
- }
138
- return Ok(());
139
- }
140
- // Threshold / Claim-Complete per context
141
- if offset >= 0x200000 {
142
- let ctx = ((offset - 0x200000) / 0x1000) as usize;
143
- if ctx < NUM_CONTEXTS {
144
- let base = 0x200000 + (0x1000 * ctx as u64);
145
- if offset == base {
146
- self.threshold[ctx] = val;
147
- return Ok(());
148
- }
149
- if offset == base + 4 {
150
- // Completion: value is the source ID to complete
151
- let id = (val & 0xffff) as u32;
152
- if id > 0 && (id as usize) < NUM_SOURCES {
153
- // eprintln!("[PLIC] Completed IRQ {} for context {}", id, ctx);
154
- self.active[ctx] &= !(1 << id);
155
- }
156
- return Ok(());
157
- }
158
- }
159
- return Ok(());
160
- }
161
-
162
- Ok(())
163
- }
164
-
165
- fn eligible_for_context(&self, source: usize, ctx: usize) -> bool {
166
- let pending = ((self.pending >> source) & 1) == 1;
167
- let enabled = ((self.enable[ctx] >> source) & 1) == 1;
168
- let over_threshold = self.priority[source] > self.threshold[ctx];
169
- let not_active = ((self.active[ctx] >> source) & 1) == 0;
170
- pending && enabled && over_threshold && not_active
171
- }
172
-
173
- pub fn claim_interrupt_for(&mut self, ctx: usize) -> u32 {
174
- let mut max_prio = 0;
175
- let mut max_id = 0;
176
-
177
- for i in 1..NUM_SOURCES {
178
- if self.eligible_for_context(i, ctx) {
179
- let prio = self.priority[i];
180
- if prio > max_prio {
181
- max_prio = prio;
182
- max_id = i as u32;
183
- }
184
- }
185
- }
186
-
187
- if max_id != 0 {
188
- // eprintln!("[PLIC] Claimed IRQ {} for context {} (prio {})", max_id, ctx, max_prio);
189
- // Mark in-flight for this context until completed.
190
- self.active[ctx] |= 1 << max_id;
191
- }
192
- max_id
193
- }
194
-
195
- pub fn is_interrupt_pending(&self) -> bool {
196
- // For current single-hart flow, report S-mode context (1) if available, else context 0.
197
- let ctx = if NUM_CONTEXTS > 1 { 1 } else { 0 };
198
- self.is_interrupt_pending_for(ctx)
199
- }
200
-
201
- pub fn is_interrupt_pending_for(&self, ctx: usize) -> bool {
202
- if ctx >= NUM_CONTEXTS {
203
- return false;
204
- }
205
- for i in 1..NUM_SOURCES {
206
- if self.eligible_for_context(i, ctx) {
207
- if self.debug {
208
- eprintln!("[PLIC] Interrupt pending for ctx={} source={}", ctx, i);
209
- }
210
- return true;
211
- }
212
- }
213
- // Debug: show why no interrupt
214
- if self.debug && self.pending != 0 {
215
- for i in 1..NUM_SOURCES {
216
- let pending = ((self.pending >> i) & 1) == 1;
217
- let enabled = ((self.enable[ctx] >> i) & 1) == 1;
218
- let over_threshold = self.priority[i] > self.threshold[ctx];
219
- let not_active = ((self.active[ctx] >> i) & 1) == 0;
220
- if pending {
221
- eprintln!("[PLIC] Source {} pending but not eligible for ctx={}: enabled={} over_threshold={} (prio={} > thresh={}) not_active={}",
222
- i, ctx, enabled, over_threshold, self.priority[i], self.threshold[ctx], not_active);
223
- }
224
- }
225
- }
226
- false
227
- }
228
- }
229
-
230
- #[cfg(test)]
231
- mod tests {
232
- use super::*;
233
-
234
- #[test]
235
- fn test_plic_claim_complete_context1() {
236
- let mut plic = Plic::new();
237
- // Priorities
238
- plic.priority[1] = 5;
239
- plic.priority[10] = 3;
240
- // Enable sources 1 and 10 for context 1 (S-mode)
241
- let enable_val = (1u32 << 1) | (1u32 << 10);
242
- let _ = plic.store(0x002000 + 0x80 * 1, 4, enable_val as u64);
243
- // Threshold 0 for context 1
244
- let _ = plic.store(0x200000 + 0x1000 * 1, 4, 0);
245
- // Assert device lines
246
- plic.set_source_level(1, true);
247
- plic.set_source_level(10, true);
248
-
249
- // Claim highest priority first (source 1)
250
- let id1 = plic.claim_interrupt_for(1);
251
- assert_eq!(id1, 1);
252
- // Next claim should return source 10 (since 1 is active)
253
- let id2 = plic.claim_interrupt_for(1);
254
- assert_eq!(id2, 10);
255
- // Complete source 1
256
- let _ = plic.store(0x200004 + 0x1000 * 1, 4, 1);
257
- // Claim again should allow source 1
258
- let id3 = plic.claim_interrupt_for(1);
259
- assert_eq!(id3, 1);
260
- }
261
- }
package/src/uart.rs DELETED
@@ -1,231 +0,0 @@
1
- use crate::dram::MemoryError;
2
- use std::collections::VecDeque;
3
-
4
- pub const UART_BASE: u64 = 0x1000_0000;
5
- pub const UART_SIZE: u64 = 0x100;
6
-
7
- // Registers (offset)
8
- const RBR: u64 = 0x00; // Receiver Buffer (Read)
9
- const THR: u64 = 0x00; // Transmitter Holding (Write)
10
- const IER: u64 = 0x01; // Interrupt Enable
11
- const IIR: u64 = 0x02; // Interrupt Identity (Read)
12
- const FCR: u64 = 0x02; // FIFO Control (Write)
13
- const LCR: u64 = 0x03; // Line Control
14
- const MCR: u64 = 0x04; // Modem Control
15
- const LSR: u64 = 0x05; // Line Status
16
- const MSR: u64 = 0x06; // Modem Status
17
- const SCR: u64 = 0x07; // Scratch
18
-
19
- pub struct Uart {
20
- pub input: VecDeque<u8>,
21
- pub output: VecDeque<u8>,
22
-
23
- // Registers
24
- pub ier: u8,
25
- pub iir: u8,
26
- pub fcr: u8,
27
- pub lcr: u8,
28
- pub mcr: u8,
29
- pub lsr: u8,
30
- pub msr: u8,
31
- pub scr: u8,
32
-
33
- // Divisor
34
- pub dll: u8,
35
- pub dlm: u8,
36
-
37
- pub interrupting: bool,
38
-
39
- /// Internal state to track if THRE interrupt is pending (waiting for IIR read or THR write).
40
- /// This separates the "condition" (THR empty) from the "event" (Interrupt Pending).
41
- thre_ip: bool,
42
- }
43
-
44
- impl Uart {
45
- pub fn new() -> Self {
46
- Self {
47
- input: VecDeque::new(),
48
- output: VecDeque::new(),
49
-
50
- ier: 0x00,
51
- iir: 0x01, // Default: no interrupt pending
52
- fcr: 0x00,
53
- lcr: 0x00,
54
- mcr: 0x00,
55
- lsr: 0x60, // Transmitter Empty (bit 5) | Transmitter Holding Register Empty (bit 6)
56
- msr: 0x00,
57
- scr: 0x00,
58
-
59
- dll: 0x00,
60
- dlm: 0x00,
61
-
62
- interrupting: false,
63
- thre_ip: true, // Starts empty, so initial state could be pending if enabled
64
- }
65
- }
66
-
67
- pub fn update_interrupts(&mut self) {
68
- self.interrupting = false;
69
- self.iir = 0x01; // Default: no interrupt pending
70
-
71
- // Priority 1: Receiver Line Status (not implemented extensively)
72
-
73
- // Priority 2: Received Data Available
74
- if (self.lsr & 0x01) != 0 && (self.ier & 0x01) != 0 {
75
- self.interrupting = true;
76
- self.iir = 0x04; // Recieved Data Available
77
- return;
78
- }
79
-
80
- // Priority 3: Transmitter Holding Register Empty
81
- // Triggered if THR is empty AND IER bit 1 is set AND we haven't acknowledged it yet.
82
- if self.thre_ip && (self.ier & 0x02) != 0 {
83
- self.interrupting = true;
84
- self.iir = 0x02; // THRE
85
- return;
86
- }
87
-
88
- // Priority 4: Modem Status (not implemented)
89
- }
90
-
91
- pub fn load(&mut self, offset: u64, size: u64) -> Result<u64, MemoryError> {
92
- if size != 1 {
93
- // Some OS might try 4-byte reads, technically not allowed by spec but we can be lenient or strict.
94
- // Spec says 8-bit width. Let's return 0 for now if not byte access.
95
- return Ok(0);
96
- }
97
-
98
- let val = match offset {
99
- RBR => {
100
- if (self.lcr & 0x80) != 0 {
101
- self.dll
102
- } else {
103
- // RBR: Read from input FIFO
104
- let byte = self.input.pop_front().unwrap_or(0);
105
- // Update LSR: if more data, set bit 0, else clear it
106
- if self.input.is_empty() {
107
- self.lsr &= !0x01;
108
- } else {
109
- self.lsr |= 0x01;
110
- }
111
- self.update_interrupts();
112
- byte
113
- }
114
- }
115
- IER => {
116
- if (self.lcr & 0x80) != 0 {
117
- self.dlm
118
- } else {
119
- self.ier
120
- }
121
- }
122
- IIR => {
123
- let val = self.iir;
124
- // Reading IIR clears THRE interrupt if it is the indicated interrupt
125
- if (val & 0x0F) == 0x02 {
126
- self.thre_ip = false;
127
- self.update_interrupts();
128
- log::trace!("[UART] IIR read cleared THRE ip");
129
- } else {
130
- log::trace!("[UART] IIR read val={:x} (thre_ip={})", val, self.thre_ip);
131
- }
132
- val
133
- }
134
- LCR => self.lcr,
135
- MCR => self.mcr,
136
- LSR => self.lsr,
137
- MSR => self.msr,
138
- SCR => self.scr,
139
- _ => 0,
140
- };
141
-
142
- Ok(val as u64)
143
- }
144
-
145
- pub fn store(&mut self, offset: u64, size: u64, value: u64) -> Result<(), MemoryError> {
146
- if size != 1 {
147
- return Ok(());
148
- }
149
-
150
- let val = (value & 0xff) as u8;
151
-
152
- match offset {
153
- THR => {
154
- if (self.lcr & 0x80) != 0 {
155
- self.dll = val;
156
- } else {
157
- // THR: Write to output
158
- log::trace!(
159
- "[UART] TX '{}' (0x{:02x})",
160
- if val.is_ascii_graphic() {
161
- val as char
162
- } else {
163
- '.'
164
- },
165
- val
166
- );
167
- self.output.push_back(val);
168
- // We instantly "transmit", so THRE (bit 5) is always set.
169
- // Writing to THR clears the THRE interrupt (if pending),
170
- // but since it becomes empty immediately, we set thre_ip to true again?
171
- // In real HW, it goes Not Empty -> Empty.
172
- // So we should clear it, then re-assert it.
173
- // For edge-triggered emulation, simply re-asserting is correct because we transitioned.
174
- self.lsr |= 0x20;
175
- self.thre_ip = true;
176
- self.update_interrupts();
177
- }
178
- }
179
- IER => {
180
- if (self.lcr & 0x80) != 0 {
181
- self.dlm = val;
182
- } else {
183
- self.ier = val;
184
- self.update_interrupts();
185
- }
186
- }
187
- FCR => {
188
- self.fcr = val;
189
- if (self.fcr & 0x02) != 0 {
190
- self.input.clear();
191
- self.lsr &= !0x01;
192
- }
193
- if (self.fcr & 0x04) != 0 {
194
- self.output.clear();
195
- self.lsr |= 0x60; // Empty
196
- }
197
- self.update_interrupts();
198
- }
199
- LCR => {
200
- self.lcr = val;
201
- }
202
- MCR => {
203
- self.mcr = val;
204
- }
205
- LSR => {
206
- // Usually read-only, but factory test mode might write. Ignore.
207
- }
208
- MSR => {
209
- // Read-only.
210
- }
211
- SCR => {
212
- self.scr = val;
213
- }
214
- _ => {}
215
- }
216
- Ok(())
217
- }
218
-
219
- // Interface for the Host
220
- pub fn push_input(&mut self, byte: u8) {
221
- self.input.push_back(byte);
222
- self.lsr |= 0x01; // Data Ready
223
- self.update_interrupts();
224
- }
225
-
226
- pub fn pop_output(&mut self) -> Option<u8> {
227
- self.output.pop_front()
228
- }
229
- }
230
-
231
-