1use crate::cpu::{apic, cpu, global_pointers, ioapic, memory, pic};
2use crate::native_devices;
3use std::collections::VecDeque;
4use std::io::Write;
5use std::sync::{Mutex, OnceLock};
6use std::sync::atomic::{AtomicBool, Ordering};
7use std::time::Instant;
8
9static START: OnceLock<Instant> = OnceLock::new();
10static UART0: OnceLock<Mutex<UartState>> = OnceLock::new();
11static PS2: OnceLock<Mutex<Ps2State>> = OnceLock::new();
12static PIT: OnceLock<Mutex<PitState>> = OnceLock::new();
13static RTC: OnceLock<Mutex<RtcState>> = OnceLock::new();
14static VGA_TEXT: OnceLock<Mutex<Vec<u8>>> = OnceLock::new();
15static UART_OUTPUT_ENABLED: AtomicBool = AtomicBool::new(true);
16
17pub fn set_uart_output_enabled(enabled: bool) {
18 UART_OUTPUT_ENABLED.store(enabled, Ordering::Relaxed);
19}
20
21fn vga_text_memory() -> &'static Mutex<Vec<u8>> {
22 VGA_TEXT.get_or_init(|| Mutex::new(vec![0; 0x40000]))
23}
24
25fn legacy_vga_offset(addr: u32) -> Option<usize> {
26 if (0xA0000..0xB8000).contains(&addr) {
30 Some((addr - 0xA0000) as usize)
31 } else if (0xB8000..0xC0000).contains(&addr) {
32 Some((addr - 0xB8000) as usize)
33 } else {
34 None
35 }
36}
37
38#[derive(Clone)]
39struct RtcState {
40 index: u8,
41 data: [u8; 128],
42 status_a: u8,
43 status_b: u8,
44 status_c: u8,
45 status_d: u8,
46 nmi_disabled: bool,
47}
48
49impl Default for RtcState {
50 fn default() -> Self {
51 let mut data = [0u8; 128];
52 data[0x0A] = 0x26;
53 data[0x0B] = 0x02;
54 data[0x0D] = 0x80;
55 Self {
56 index: 0,
57 data,
58 status_a: 0x26,
59 status_b: 0x02,
60 status_c: 0,
61 status_d: 0x80,
62 nmi_disabled: false,
63 }
64 }
65}
66
67fn rtc() -> &'static Mutex<RtcState> {
68 RTC.get_or_init(|| Mutex::new(RtcState::default()))
69}
70
71fn rtc_read(port: i32) -> Option<i32> {
72 let state = rtc().lock().ok()?;
73 match port {
74 0x71 => Some(match state.index & 0x7F {
75 0x0A => state.status_a,
76 0x0B => state.status_b,
77 0x0C => state.status_c,
78 0x0D => state.status_d,
79 index => state.data[index as usize],
80 } as i32),
81 0x70 => Some(state.index as i32 | if state.nmi_disabled { 0x80 } else { 0 }),
82 _ => None,
83 }
84}
85
86fn rtc_write(port: i32, value: i32) -> bool {
87 let Ok(mut state) = rtc().lock() else {
88 return false;
89 };
90 match port {
91 0x70 => {
92 let byte = value as u8;
93 state.index = byte & 0x7F;
94 state.nmi_disabled = byte & 0x80 != 0;
95 true
96 }
97 0x71 => {
98 let index = state.index & 0x7F;
99 let byte = value as u8;
100 match index {
101 0x0A => state.status_a = byte,
102 0x0B => state.status_b = byte,
103 0x0C | 0x0D => {}
104 _ => state.data[index as usize] = byte,
105 }
106 true
107 }
108 _ => false,
109 }
110}
111
112#[derive(Clone)]
113struct PitState {
114 next_low: [u8; 3],
115 enabled: [bool; 3],
116 mode: [u8; 3],
117 read_mode: [u8; 3],
118 latch: [u8; 3],
119 latch_value: [u16; 3],
120 reload: [u16; 3],
121 start_value: [u16; 3],
122 start: [Instant; 3],
123}
124
125impl Default for PitState {
126 fn default() -> Self {
127 Self {
128 next_low: [1; 3],
129 enabled: [false; 3],
130 mode: [3; 3],
131 read_mode: [3; 3],
132 latch: [0; 3],
133 latch_value: [0; 3],
134 reload: [0; 3],
135 start_value: [0; 3],
136 start: [Instant::now(), Instant::now(), Instant::now()],
137 }
138 }
139}
140
141fn pit() -> &'static Mutex<PitState> {
142 PIT.get_or_init(|| Mutex::new(PitState::default()))
143}
144
145const PIT_HZ: f64 = 1_193_181.6666;
146
147fn pit_counter_value(state: &PitState, channel: usize) -> u16 {
148 if !state.enabled[channel] || state.reload[channel] == 0 {
149 return 0;
150 }
151 let elapsed = state.start[channel].elapsed().as_secs_f64();
152 let ticks = (elapsed * PIT_HZ) as u64;
153 let reload = state.reload[channel] as u64;
154 (state.start_value[channel] as u64).wrapping_sub(ticks % reload.max(1)) as u16
155}
156
157fn pit_read(port: i32) -> Option<i32> {
158 if !(0x40..=0x42).contains(&port) {
159 return None;
160 }
161 let channel = (port - 0x40) as usize;
162 let mut state = pit().lock().ok()?;
163 if state.latch[channel] != 0 {
164 state.latch[channel] -= 1;
165 return Some(if state.latch[channel] == 1 {
166 (state.latch_value[channel] & 0xFF) as i32
167 } else {
168 (state.latch_value[channel] >> 8) as i32
169 });
170 }
171 let value = pit_counter_value(&state, channel);
172 let low = state.next_low[channel] != 0;
173 if state.mode[channel] == 3 {
174 state.next_low[channel] ^= 1;
175 }
176 Some(if low {
177 (value & 0xFF) as i32
178 } else {
179 (value >> 8) as i32
180 })
181}
182
183fn pit_poll() -> bool {
184 let Ok(mut state) = pit().lock() else {
185 return false;
186 };
187 if !state.enabled[0] || state.reload[0] == 0 {
188 return false;
189 }
190 let elapsed_ticks = (state.start[0].elapsed().as_secs_f64() * PIT_HZ) as u64;
191 if elapsed_ticks >= state.start_value[0] as u64 {
192 state.start[0] = Instant::now();
193 state.start_value[0] = state.reload[0];
194 drop(state);
195 unsafe {
196 crate::cpu::cpu::device_lower_irq(0);
197 crate::cpu::cpu::device_raise_irq(0);
198 }
199 }
200 true
201}
202
203fn pit_write(port: i32, value: i32) -> bool {
204 let Ok(mut state) = pit().lock() else {
205 return false;
206 };
207 if (0x40..=0x42).contains(&port) {
208 let channel = (port - 0x40) as usize;
209 let byte = value as u8;
210 if state.next_low[channel] != 0 {
211 state.reload[channel] = (state.reload[channel] & 0xFF00) | byte as u16;
212 } else {
213 state.reload[channel] = (state.reload[channel] & 0x00FF) | ((byte as u16) << 8);
214 if state.reload[channel] == 0 {
215 state.reload[channel] = 0xFFFF;
216 }
217 state.start_value[channel] = state.reload[channel];
218 state.start[channel] = Instant::now();
219 state.enabled[channel] = true;
220 }
221 state.next_low[channel] ^= 1;
222 return true;
223 }
224 if port == 0x43 {
225 let command = value as u8;
226 let channel = ((command >> 6) & 3) as usize;
227 if channel >= 3 {
228 return true;
229 }
230 let read_mode = (command >> 4) & 3;
231 if read_mode == 0 {
232 state.latch_value[channel] = pit_counter_value(&state, channel);
233 state.latch[channel] = 2;
234 } else {
235 state.read_mode[channel] = read_mode;
236 state.mode[channel] = (command >> 1) & 7;
237 state.next_low[channel] = if read_mode == 3 { 1 } else { 0 };
238 }
239 return true;
240 }
241 port == 0x61
242}
243
244#[derive(Default)]
245struct Ps2State {
246 output: VecDeque<u8>,
247 command_byte: u8,
248 pending_command: u8,
249}
250
251fn ps2() -> &'static Mutex<Ps2State> {
252 PS2.get_or_init(|| {
253 Mutex::new(Ps2State {
254 command_byte: 0x01,
255 ..Ps2State::default()
256 })
257 })
258}
259
260#[derive(Default)]
261struct UartState {
262 ints: u8,
263 baud_rate: u16,
264 line_control: u8,
265 lsr: u8,
266 fifo_control: u8,
267 ier: u8,
268 iir: u8,
269 modem_control: u8,
270 modem_status: u8,
271 scratch: u8,
272 irq: u8,
273 input: VecDeque<u8>,
274}
275
276fn uart0() -> &'static Mutex<UartState> {
277 UART0.get_or_init(|| Mutex::new(UartState::default()))
278}
279
280fn ps2_read(port: i32) -> Option<i32> {
281 let mut controller = ps2().lock().ok()?;
282 match port {
283 0x60 => {
284 let value = controller.output.pop_front().unwrap_or(0);
285 let more = !controller.output.is_empty();
286 drop(controller);
287 unsafe {
288 crate::cpu::cpu::device_lower_irq(1);
289 if more {
290 crate::cpu::cpu::device_raise_irq(1);
291 }
292 }
293 Some(value as i32)
294 }
295 0x64 => Some(if controller.output.is_empty() { 0 } else { 1 }),
296 _ => None,
297 }
298}
299
300fn ps2_write(port: i32, value: i32) -> bool {
301 let Ok(mut controller) = ps2().lock() else {
302 return false;
303 };
304 match port {
305 0x64 => {
306 controller.pending_command = value as u8;
307 true
308 }
309 0x60 => {
310 if controller.pending_command == 0x60 {
311 controller.command_byte = value as u8;
312 }
313 controller.pending_command = 0;
314 true
315 }
316 _ => false,
317 }
318}
319
320fn keycode_for_ascii(byte: u8) -> Option<(u8, bool)> {
321 let upper = byte.to_ascii_uppercase();
322 let shifted = byte.is_ascii_uppercase();
323 let code = match upper {
324 b'A' => 0x1E,
325 b'B' => 0x30,
326 b'C' => 0x2E,
327 b'D' => 0x20,
328 b'E' => 0x12,
329 b'F' => 0x21,
330 b'G' => 0x22,
331 b'H' => 0x23,
332 b'I' => 0x17,
333 b'J' => 0x24,
334 b'K' => 0x25,
335 b'L' => 0x26,
336 b'M' => 0x32,
337 b'N' => 0x31,
338 b'O' => 0x18,
339 b'P' => 0x19,
340 b'Q' => 0x10,
341 b'R' => 0x13,
342 b'S' => 0x1F,
343 b'T' => 0x14,
344 b'U' => 0x16,
345 b'V' => 0x2F,
346 b'W' => 0x11,
347 b'X' => 0x2D,
348 b'Y' => 0x15,
349 b'Z' => 0x2C,
350 b'1' | b'!' => 0x02,
351 b'2' | b'@' => 0x03,
352 b'3' | b'#' => 0x04,
353 b'4' | b'$' => 0x05,
354 b'5' | b'%' => 0x06,
355 b'6' | b'^' => 0x07,
356 b'7' | b'&' => 0x08,
357 b'8' | b'*' => 0x09,
358 b'9' | b'(' => 0x0A,
359 b'0' | b')' => 0x0B,
360 b'-' | b'_' => 0x0C,
361 b'=' | b'+' => 0x0D,
362 b'[' | b'{' => 0x1A,
363 b']' | b'}' => 0x1B,
364 b';' | b':' => 0x27,
365 b'\'' | b'"' => 0x28,
366 b'`' | b'~' => 0x29,
367 b'\\' | b'|' => 0x2B,
368 b',' | b'<' => 0x33,
369 b'.' | b'>' => 0x34,
370 b'/' | b'?' => 0x35,
371 b' ' => 0x39,
372 b'\n' | b'\r' => 0x1C,
373 b'\t' => 0x0F,
374 8 => 0x0E,
375 _ => return None,
376 };
377 let shifted = shifted
378 || matches!(byte, b'!'..=b'&' | b'('..=b'+' | b':' | b'<'..=b'>' | b'?' | b'@' | b'^' | b'_' | b'{' | b'|' | b'}' | b'~' | b'"');
379 Some((code, shifted))
380}
381
382pub fn inject_keyboard_text(text: &str) -> usize {
383 let Ok(mut controller) = ps2().lock() else {
384 return 0;
385 };
386 let mut count = 0;
387 for byte in text.bytes() {
388 let Some((code, shifted)) = keycode_for_ascii(byte) else {
389 continue;
390 };
391 if shifted {
392 controller.output.push_back(0x2A);
393 }
394 controller.output.push_back(code);
395 controller.output.push_back(code | 0x80);
396 if shifted {
397 controller.output.push_back(0xAA);
398 }
399 count += 1;
400 }
401 drop(controller);
402 if count > 0 {
403 unsafe { crate::cpu::cpu::device_raise_irq(1) };
404 }
405 count
406}
407
408fn uart_read(port: i32) -> i32 {
409 let offset = (port - 0x3F8) as u8;
410 let mut uart = uart0().lock().expect("UART0 mutex poisoned");
411 match offset {
412 0 if uart.line_control & 0x80 != 0 => (uart.baud_rate & 0xFF) as i32,
413 0 => uart.input.pop_front().unwrap_or(0) as i32,
414 1 if uart.line_control & 0x80 != 0 => (uart.baud_rate >> 8) as i32,
415 1 => (uart.ier & 0x0F) as i32,
416 2 => {
417 let fifo = if uart.fifo_control & 1 != 0 { 0xC0 } else { 0 };
418 (uart.iir | fifo) as i32
419 }
420 3 => uart.line_control as i32,
421 4 => uart.modem_control as i32,
422 5 => (uart.lsr | if uart.input.is_empty() { 0 } else { 0x01 }) as i32,
423 6 => uart.modem_status as i32,
424 7 => uart.scratch as i32,
425 _ => 0xFF,
426 }
427}
428
429fn restore_uart_state(state: &[serde_json::Value]) -> Result<(), String> {
430 if state.len() < 11 {
431 return Err(format!(
432 "UART state has {} fields; expected 11",
433 state.len()
434 ));
435 }
436 let mut uart = uart0()
437 .lock()
438 .map_err(|_| "UART0 mutex poisoned".to_owned())?;
439 uart.ints = state[0]
440 .as_i64()
441 .ok_or_else(|| "UART ints is not an integer".to_owned())? as u8;
442 uart.baud_rate = state[1]
443 .as_i64()
444 .ok_or_else(|| "UART baud rate is not an integer".to_owned())? as u16;
445 uart.line_control = state[2]
446 .as_i64()
447 .ok_or_else(|| "UART line control is not an integer".to_owned())?
448 as u8;
449 uart.lsr = state[3]
450 .as_i64()
451 .ok_or_else(|| "UART LSR is not an integer".to_owned())? as u8;
452 uart.fifo_control = state[4]
453 .as_i64()
454 .ok_or_else(|| "UART FIFO control is not an integer".to_owned())?
455 as u8;
456 uart.ier = state[5]
457 .as_i64()
458 .ok_or_else(|| "UART IER is not an integer".to_owned())? as u8;
459 uart.iir = state[6]
460 .as_i64()
461 .ok_or_else(|| "UART IIR is not an integer".to_owned())? as u8;
462 uart.modem_control = state[7]
463 .as_i64()
464 .ok_or_else(|| "UART modem control is not an integer".to_owned())?
465 as u8;
466 uart.modem_status = state[8]
467 .as_i64()
468 .ok_or_else(|| "UART modem status is not an integer".to_owned())?
469 as u8;
470 uart.scratch = state[9]
471 .as_i64()
472 .ok_or_else(|| "UART scratch is not an integer".to_owned())? as u8;
473 uart.irq = state[10]
474 .as_i64()
475 .ok_or_else(|| "UART IRQ is not an integer".to_owned())? as u8;
476 Ok(())
477}
478
479fn nested_buffer<'a>(
480 state: &[serde_json::Value],
481 index: usize,
482 buffers: &'a [Vec<u8>],
483) -> Result<&'a [u8], String> {
484 let buffer_id = state
485 .get(index)
486 .and_then(serde_json::Value::as_object)
487 .and_then(|object| object.get("buffer_id"))
488 .and_then(serde_json::Value::as_u64)
489 .ok_or_else(|| format!("nested state[{index}] is not a typed buffer"))?
490 as usize;
491 buffers
492 .get(buffer_id)
493 .map(Vec::as_slice)
494 .ok_or_else(|| format!("nested buffer id {buffer_id} is out of range"))
495}
496
497fn restore_rtc_state(state: &[serde_json::Value], buffers: &[Vec<u8>]) -> Result<(), String> {
498 if state.len() < 14 {
499 return Err(format!("RTC state has {} fields; expected 14", state.len()));
500 }
501 let data = nested_buffer(state, 1, buffers)?;
502 let mut rtc = rtc().lock().map_err(|_| "RTC mutex poisoned".to_owned())?;
503 rtc.index = state[0].as_i64().unwrap_or(0) as u8;
504 rtc.data.fill(0);
505 let copy_len = data.len().min(rtc.data.len());
506 rtc.data[..copy_len].copy_from_slice(&data[..copy_len]);
507 rtc.status_a = state[8].as_i64().unwrap_or(rtc.data[0x0A] as i64) as u8;
508 rtc.status_b = state[9].as_i64().unwrap_or(rtc.data[0x0B] as i64) as u8;
509 rtc.status_c = state[10].as_i64().unwrap_or(0) as u8;
510 rtc.nmi_disabled = state[11].as_i64().unwrap_or(0) != 0;
511 rtc.status_d = rtc.data[0x0D].max(0x80);
512 Ok(())
513}
514
515fn restore_pit_state(state: &[serde_json::Value], buffers: &[Vec<u8>]) -> Result<(), String> {
516 if state.len() < 9 {
517 return Err(format!("PIT state has {} fields; expected 9", state.len()));
518 }
519 let next_low = nested_buffer(state, 0, buffers)?;
520 let enabled = nested_buffer(state, 1, buffers)?;
521 let mode = nested_buffer(state, 2, buffers)?;
522 let read_mode = nested_buffer(state, 3, buffers)?;
523 let latch = nested_buffer(state, 4, buffers)?;
524 let reload = nested_buffer(state, 6, buffers)?;
525 let start_value = nested_buffer(state, 8, buffers)?;
526 let mut pit = pit().lock().map_err(|_| "PIT mutex poisoned".to_owned())?;
527 for channel in 0..3 {
528 pit.next_low[channel] = *next_low.get(channel).unwrap_or(&1);
529 pit.enabled[channel] = *enabled.get(channel).unwrap_or(&0) != 0;
530 pit.mode[channel] = *mode.get(channel).unwrap_or(&3);
531 pit.read_mode[channel] = *read_mode.get(channel).unwrap_or(&3);
532 pit.latch[channel] = *latch.get(channel).unwrap_or(&0);
533 let offset = channel * 2;
534 pit.reload[channel] = u16::from_le_bytes([
535 *reload.get(offset).unwrap_or(&0),
536 *reload.get(offset + 1).unwrap_or(&0),
537 ]);
538 pit.start_value[channel] = u16::from_le_bytes([
539 *start_value.get(offset).unwrap_or(&0),
540 *start_value.get(offset + 1).unwrap_or(&0),
541 ]);
542 pit.start[channel] = Instant::now();
543 }
544 Ok(())
545}
546
547fn uart_write(port: i32, value: i32) {
548 let offset = (port - 0x3F8) as u8;
549 let byte = value as u8;
550 let mut output = None;
551 {
552 let mut uart = uart0().lock().expect("UART0 mutex poisoned");
553 match offset {
554 0 if uart.line_control & 0x80 != 0 => {
555 uart.baud_rate = (uart.baud_rate & 0xFF00) | byte as u16;
556 }
557 0 => output = Some(byte),
558 1 if uart.line_control & 0x80 != 0 => {
559 uart.baud_rate = (uart.baud_rate & 0x00FF) | ((byte as u16) << 8);
560 }
561 1 => uart.ier = byte & 0x0F,
562 2 => uart.fifo_control = byte,
563 3 => uart.line_control = byte,
564 4 => uart.modem_control = byte,
565 7 => uart.scratch = byte,
566 _ => {}
567 }
568 }
569 if let Some(byte) = output {
570 if !UART_OUTPUT_ENABLED.load(Ordering::Relaxed) {
571 return;
572 }
573 let mut stdout = std::io::stdout().lock();
574 let _ = stdout.write_all(&[byte]);
575 let _ = stdout.flush();
576 }
577}
578
579#[no_mangle]
583pub extern "C" fn cpu_exception_hook(_interrupt: i32) -> bool {
584 false
585}
586
587#[no_mangle]
588pub extern "C" fn microtick() -> f64 {
589 START.get_or_init(Instant::now).elapsed().as_secs_f64() * 1000.0
590}
591
592#[no_mangle]
593pub extern "C" fn run_hardware_timers(_acpi_enabled: bool, _now: f64) -> f64 {
594 let _ = pit_poll();
595 0.0
596}
597
598#[no_mangle]
599pub extern "C" fn cpu_event_halt() {}
600
601#[no_mangle]
602pub extern "C" fn stop_idling() {}
603
604#[no_mangle]
605pub extern "C" fn get_rand_int() -> i32 {
606 0x1357_9BDF
607}
608
609#[no_mangle]
610pub extern "C" fn io_port_read8(port: i32) -> i32 {
611 if let Some(value) = rtc_read(port) {
612 value
613 } else if let Some(value) = pit_read(port) {
614 value
615 } else if let Some(value) = ps2_read(port) {
616 value
617 } else if let Some(value) = native_devices::io_read8(port) {
618 value
619 } else if (0x3F8..=0x3FF).contains(&port) {
620 uart_read(port)
621 } else {
622 0xFF
623 }
624}
625
626#[no_mangle]
627pub extern "C" fn io_port_read16(port: i32) -> i32 {
628 native_devices::io_read16(port).unwrap_or(0xFFFF)
629}
630
631#[no_mangle]
632pub extern "C" fn io_port_read32(port: i32) -> i32 {
633 native_devices::io_read32(port).unwrap_or(-1)
634}
635
636#[no_mangle]
637pub extern "C" fn io_port_write8(port: i32, value: i32) {
638 if !rtc_write(port, value)
639 && !pit_write(port, value)
640 && !ps2_write(port, value)
641 && !native_devices::io_write8(port, value)
642 && (0x3F8..=0x3FF).contains(&port)
643 {
644 uart_write(port, value);
645 }
646}
647
648#[no_mangle]
649pub extern "C" fn io_port_write16(port: i32, value: i32) {
650 if !native_devices::io_write16(port, value) {}
651}
652
653#[no_mangle]
654pub extern "C" fn io_port_write32(port: i32, value: i32) {
655 if !native_devices::io_write32(port, value) {}
656}
657
658#[no_mangle]
659pub extern "C" fn mmap_read8(addr: u32) -> i32 {
660 if let Some(offset) = legacy_vga_offset(addr) {
661 return vga_text_memory()
662 .lock()
663 .ok()
664 .and_then(|m| m.get(offset).copied())
665 .unwrap_or(0xFF) as i32;
666 }
667 native_devices::mmio_read8(addr).unwrap_or(0xFF)
668}
669
670#[no_mangle]
671pub extern "C" fn mmap_read32(addr: u32) -> i32 {
672 if legacy_vga_offset(addr).is_some() && addr <= 0xBFFFC {
673 return i32::from_le_bytes([
674 mmap_read8(addr) as u8,
675 mmap_read8(addr + 1) as u8,
676 mmap_read8(addr + 2) as u8,
677 mmap_read8(addr + 3) as u8,
678 ]);
679 }
680 native_devices::mmio_read32(addr).unwrap_or(-1)
681}
682
683#[no_mangle]
684pub extern "C" fn mmap_write8(addr: u32, value: i32) {
685 if let Some(offset) = legacy_vga_offset(addr) {
686 if let Ok(mut memory) = vga_text_memory().lock() {
687 if let Some(byte) = memory.get_mut(offset) {
688 *byte = value as u8;
689 }
690 }
691 return;
692 }
693 let _ = native_devices::mmio_write8(addr, value);
694}
695
696#[no_mangle]
697pub extern "C" fn mmap_write16(addr: u32, value: i32) {
698 if legacy_vga_offset(addr).is_some() {
699 mmap_write8(addr, value);
700 mmap_write8(addr + 1, value >> 8);
701 return;
702 }
703 let _ = native_devices::mmio_write16(addr, value);
704}
705
706#[no_mangle]
707pub extern "C" fn mmap_write32(addr: u32, value: i32) {
708 if legacy_vga_offset(addr).is_some() {
709 for offset in 0..4 {
710 mmap_write8(addr + offset, value >> (offset * 8));
711 }
712 return;
713 }
714 let _ = native_devices::mmio_write32(addr, value);
715}
716
717#[no_mangle]
718pub extern "C" fn mmap_write64(addr: u32, v0: i32, v1: i32) {
719 mmap_write32(addr, v0);
720 mmap_write32(addr + 4, v1);
721}
722
723#[no_mangle]
724pub extern "C" fn mmap_write128(addr: u32, v0: i32, v1: i32, v2: i32, v3: i32) {
725 mmap_write32(addr, v0);
726 mmap_write32(addr + 4, v1);
727 mmap_write32(addr + 8, v2);
728 mmap_write32(addr + 12, v3);
729}
730
731pub struct NativeCpu {
737 state_arena: Box<[u8; 4096]>,
738 ram_bytes: u32,
739 vga_bytes: u32,
740 last_timer_tick: Instant,
741 screen_width: u32,
742 screen_height: u32,
743 screen_bpp: u32,
744 graphical_mode: bool,
745}
746
747impl NativeCpu {
748 pub fn new(ram_bytes: u32, vga_bytes: u32) -> Self {
749 assert!(ram_bytes > 0, "RAM size must be non-zero");
750 assert!(vga_bytes > 0, "VGA memory size must be non-zero");
751
752 let mut state_arena = Box::new([0u8; 4096]);
753 if let Ok(mut text) = vga_text_memory().lock() {
754 text.fill(0);
755 }
756 unsafe {
757 global_pointers::init(state_arena.as_mut_ptr());
758 let _ = memory::allocate_memory(ram_bytes);
759 let _ = memory::svga_allocate_memory(vga_bytes);
760 *global_pointers::memory_size = ram_bytes;
761 memory::vga_memory_size = vga_bytes;
762 cpu::reset_cpu();
763 }
764
765 Self {
766 state_arena,
767 ram_bytes,
768 vga_bytes,
769 last_timer_tick: Instant::now(),
770 screen_width: 80,
771 screen_height: 25,
772 screen_bpp: 0,
773 graphical_mode: false,
774 }
775 }
776
777 pub fn ram_bytes(&self) -> u32 {
778 self.ram_bytes
779 }
780
781 pub fn vga_bytes(&self) -> u32 {
782 self.vga_bytes
783 }
784
785 pub fn step(&mut self, max_instructions: u32) -> u32 {
786 unsafe {
787 let halted = *global_pointers::in_hlt;
788 let timer_due = self.last_timer_tick.elapsed() >= std::time::Duration::from_millis(1);
789 if halted || timer_due {
790 let now = microtick();
791 let pit_active = pit_poll();
792 if *global_pointers::acpi_enabled {
793 let _ = apic::apic_timer(now);
794 cpu::handle_irqs();
795 } else if !pit_active {
796 pic::set_irq(0);
797 cpu::handle_irqs();
798 pic::clear_irq(0);
799 cpu::handle_irqs();
800 }
801 self.last_timer_tick = Instant::now();
802 }
803 cpu::main_loop_native_interpreter(max_instructions)
804 }
805 }
806
807 pub fn read_memory(&self, address: u32, output: &mut [u8]) -> bool {
808 if address.checked_add(output.len() as u32).is_none()
809 || address + output.len() as u32 > self.ram_bytes
810 {
811 return false;
812 }
813 unsafe {
814 output.copy_from_slice(std::slice::from_raw_parts(
815 memory::mem8.add(address as usize),
816 output.len(),
817 ));
818 }
819 true
820 }
821
822 pub fn write_memory(&mut self, address: u32, input: &[u8]) -> bool {
823 if address.checked_add(input.len() as u32).is_none()
824 || address + input.len() as u32 > self.ram_bytes
825 {
826 return false;
827 }
828 unsafe {
829 std::slice::from_raw_parts_mut(memory::mem8.add(address as usize), input.len())
830 .copy_from_slice(input);
831 }
832 true
833 }
834
835 pub fn instruction_pointer(&self) -> u32 {
836 unsafe { *global_pointers::instruction_pointer as u32 }
837 }
838
839 pub fn halted(&self) -> bool {
840 unsafe { *global_pointers::in_hlt }
841 }
842
843 pub fn state_arena(&self) -> &[u8; 4096] {
844 &self.state_arena
845 }
846
847 pub fn vga_text_snapshot(&self) -> Option<(u32, u32, Vec<u8>)> {
851 if self.graphical_mode {
852 return None;
853 }
854 let memory = vga_text_memory().lock().ok()?;
855 if memory.len() < 80 * 25 * 2 {
856 return None;
857 }
858 Some((80, 25, memory[..80 * 25 * 2].to_vec()))
859 }
860
861 pub fn vga_framebuffer_rgb(&self) -> Option<(u32, u32, Vec<u8>)> {
862 if !self.graphical_mode || self.screen_width == 0 || self.screen_height == 0 {
863 return None;
864 }
865 let pixels = (self.screen_width as usize).checked_mul(self.screen_height as usize)?;
866 let mut output = vec![0u8; pixels.checked_mul(3)?];
867 unsafe {
868 if memory::vga_mem8.is_null() || self.screen_bpp != 32 {
869 return None;
870 }
871 let source_len = pixels.checked_mul(4)?;
872 if source_len > self.vga_bytes as usize {
873 return None;
874 }
875 let source = std::slice::from_raw_parts(memory::vga_mem8, source_len);
876 for (index, rgb) in output.chunks_exact_mut(3).enumerate() {
877 let pixel = &source[index * 4..index * 4 + 4];
878 rgb.copy_from_slice(&[pixel[2], pixel[1], pixel[0]]);
879 }
880 }
881 Some((self.screen_width, self.screen_height, output))
882 }
883
884 pub fn set_9p_root(&mut self, path: impl AsRef<std::path::Path>) -> Result<(), String> {
885 native_devices::set_9p_root(path)
886 }
887}
888
889#[cfg(test)]
890mod tests {
891 use super::NativeCpu;
892
893 #[test]
894 fn native_interpreter_executes_reset_vector_hlt() {
895 let mut cpu = NativeCpu::new(128 * 1024 * 1024, 8 * 1024 * 1024);
896 assert!(cpu.write_memory(0xFFFF0, &[0xF4]));
897 assert_eq!(cpu.instruction_pointer(), 0xFFFF0);
898 assert_eq!(cpu.step(1), 1);
899 assert!(cpu.halted());
900 }
901}
902
903impl NativeCpu {
904 pub fn restore_v86_state(
909 &mut self,
910 state: &serde_json::Value,
911 buffers: &[Vec<u8>],
912 ) -> Result<(), String> {
913 let slots = state
914 .as_array()
915 .ok_or_else(|| "v86 state is not an array".to_owned())?;
916
917 let memory_size = scalar(slots, 0)? as u32;
918 if memory_size != self.ram_bytes {
919 return Err(format!(
920 "state RAM is {memory_size} bytes, NativeCpu has {} bytes",
921 self.ram_bytes
922 ));
923 }
924
925 let segment_state = buffer_for(slots, buffers, 1)?;
926 if segment_state.len() != 16 {
927 return Err(format!(
928 "state[1] length {} != expected 16",
929 segment_state.len()
930 ));
931 }
932 unsafe {
933 std::slice::from_raw_parts_mut(global_pointers::segment_is_null as *mut u8, 8)
934 .copy_from_slice(&segment_state[..8]);
935 std::slice::from_raw_parts_mut(global_pointers::segment_access_bytes, 8)
936 .copy_from_slice(&segment_state[8..]);
937 }
938 copy_i32_buffer(slots, buffers, 2, unsafe {
939 std::slice::from_raw_parts_mut(global_pointers::segment_offsets as *mut u8, 32)
940 })?;
941 copy_u32_buffer(slots, buffers, 3, unsafe {
942 std::slice::from_raw_parts_mut(global_pointers::segment_limits as *mut u8, 32)
943 })?;
944
945 unsafe {
946 *global_pointers::memory_size = memory_size;
947 *global_pointers::protected_mode = scalar(slots, 4)? != 0;
948 *global_pointers::idtr_offset = scalar(slots, 5)? as i32;
949 *global_pointers::idtr_size = scalar(slots, 6)? as i32;
950 *global_pointers::gdtr_offset = scalar(slots, 7)? as i32;
951 *global_pointers::gdtr_size = scalar(slots, 8)? as i32;
952 }
953 copy_i32_buffer(slots, buffers, 10, unsafe {
954 std::slice::from_raw_parts_mut(global_pointers::cr as *mut u8, 32)
955 })?;
956 unsafe {
957 *global_pointers::cpl = scalar(slots, 11)? as u8;
958 *global_pointers::is_32 = scalar(slots, 13)? != 0;
959 *global_pointers::stack_size_32 = scalar(slots, 16)? != 0;
960 *global_pointers::in_hlt = scalar(slots, 17)? != 0;
961 *global_pointers::last_virt_eip = scalar(slots, 18)? as i32;
962 *global_pointers::eip_phys = scalar(slots, 19)? as i32;
963 *global_pointers::sysenter_cs = scalar(slots, 22)? as i32;
964 *global_pointers::sysenter_eip = scalar(slots, 23)? as i32;
965 *global_pointers::sysenter_esp = scalar(slots, 24)? as i32;
966 *global_pointers::prefixes = scalar(slots, 25)? as u8;
967 *global_pointers::flags = scalar(slots, 26)? as i32;
968 *global_pointers::flags_changed = scalar(slots, 27)? as i32;
969 *global_pointers::last_op1 = scalar(slots, 28)? as i32;
970 *global_pointers::last_op_size = scalar(slots, 30)? as i32;
971 *global_pointers::instruction_pointer = scalar(slots, 37)? as i32;
972 *global_pointers::previous_ip = scalar(slots, 38)? as i32;
973 }
974 copy_i32_buffer(slots, buffers, 39, unsafe {
975 std::slice::from_raw_parts_mut(global_pointers::reg32 as *mut u8, 32)
976 })?;
977 copy_u16_buffer(slots, buffers, 40, unsafe {
978 std::slice::from_raw_parts_mut(global_pointers::sreg as *mut u8, 16)
979 })?;
980 copy_i32_buffer(slots, buffers, 41, unsafe {
981 std::slice::from_raw_parts_mut(global_pointers::dreg as *mut u8, 32)
982 })?;
983 copy_u64_buffer(slots, buffers, 42, unsafe {
984 std::slice::from_raw_parts_mut(global_pointers::reg_pdpte as *mut u8, 32)
985 })?;
986
987 let tsc = buffer_for(slots, buffers, 43)?;
988 if tsc.len() >= 8 {
989 let low = u32::from_le_bytes(tsc[0..4].try_into().unwrap());
990 let high = u32::from_le_bytes(tsc[4..8].try_into().unwrap());
991 unsafe {
992 cpu::set_tsc(low, high);
993 }
994 }
995
996 if let Some(uart_state) = slots.get(54).and_then(serde_json::Value::as_array) {
997 restore_uart_state(uart_state)?;
998 }
999 if let Some(rtc_state) = slots.get(47).and_then(serde_json::Value::as_array) {
1000 restore_rtc_state(rtc_state, buffers)?;
1001 }
1002 if let Some(pit_state) = slots.get(58).and_then(serde_json::Value::as_array) {
1003 restore_pit_state(pit_state, buffers)?;
1004 }
1005 if let Some(pic_state) = slots.get(60).and_then(serde_json::Value::as_array) {
1006 let master = byte_array_from_state(pic_state, 13, "PIC master")?;
1007 let slave_value = pic_state
1008 .get(5)
1009 .ok_or_else(|| "PIC state has no slave controller".to_owned())?;
1010 let slave_array = slave_value
1011 .as_array()
1012 .ok_or_else(|| "PIC slave state is not an array".to_owned())?;
1013 let slave = byte_array_from_values(slave_array, 13, "PIC slave")?;
1014 pic::restore_state(&master, &slave);
1015 }
1016
1017 if slots.get(46).is_some_and(|value| !value.is_null()) {
1018 let apic_state = buffer_for(slots, buffers, 46)?;
1019 apic::restore_state_bytes(apic_state)?;
1020 unsafe {
1021 *global_pointers::apic_enabled = true;
1022 *global_pointers::acpi_enabled = true;
1023 }
1024 }
1025 if slots.get(63).is_some_and(|value| !value.is_null()) {
1026 let ioapic_state = buffer_for(slots, buffers, 63)?;
1027 ioapic::restore_state_bytes(ioapic_state)?;
1028 }
1029
1030 if let Some(vga_state) = slots.get(52).and_then(serde_json::Value::as_array) {
1031 self.screen_width = vga_state
1032 .get(15)
1033 .and_then(serde_json::Value::as_i64)
1034 .unwrap_or(0) as u32;
1035 self.screen_height = vga_state
1036 .get(16)
1037 .and_then(serde_json::Value::as_i64)
1038 .unwrap_or(0) as u32;
1039 self.screen_bpp = vga_state
1040 .get(19)
1041 .and_then(serde_json::Value::as_i64)
1042 .unwrap_or(0) as u32;
1043 self.graphical_mode = vga_state
1044 .get(9)
1045 .and_then(serde_json::Value::as_bool)
1046 .unwrap_or(false);
1047 if let Some(value) = vga_state.get(39) {
1048 let buffer_id = value
1049 .get("buffer_id")
1050 .and_then(serde_json::Value::as_u64)
1051 .ok_or_else(|| "VGA state[39] is not a typed buffer".to_owned())?
1052 as usize;
1053 let svga = buffers
1054 .get(buffer_id)
1055 .ok_or_else(|| format!("VGA buffer id {buffer_id} is out of range"))?;
1056 let vga_len = self.vga_bytes as usize;
1057 if svga.len() > vga_len {
1058 return Err(format!(
1059 "VGA framebuffer {} exceeds allocated {} bytes",
1060 svga.len(),
1061 vga_len
1062 ));
1063 }
1064 unsafe {
1065 std::ptr::copy_nonoverlapping(svga.as_ptr(), memory::vga_mem8, svga.len());
1066 }
1067 }
1068 if vga_state.get(6).is_some() {
1069 let text = nested_buffer(vga_state, 6, buffers)?;
1070 let mut target = vga_text_memory()
1071 .lock()
1072 .map_err(|_| "VGA text mutex poisoned".to_owned())?;
1073 let copy_len = text.len().min(target.len());
1074 target[..copy_len].copy_from_slice(&text[..copy_len]);
1075 if copy_len < target.len() {
1076 target[copy_len..].fill(0);
1077 }
1078 }
1079 }
1080
1081 unsafe {
1082 *global_pointers::tss_size_32 = scalar(slots, 64)? != 0;
1083 }
1084 copy_buffer(slots, buffers, 66, unsafe {
1085 std::slice::from_raw_parts_mut(global_pointers::reg_xmm as *mut u8, 128)
1086 })?;
1087 copy_buffer(slots, buffers, 67, unsafe {
1088 std::slice::from_raw_parts_mut(global_pointers::fpu_st as *mut u8, 128)
1089 })?;
1090 unsafe {
1091 *global_pointers::fpu_stack_empty = scalar(slots, 68)? as u8;
1092 *global_pointers::fpu_stack_ptr = scalar(slots, 69)? as u8;
1093 *global_pointers::fpu_control_word = scalar(slots, 70)? as u16;
1094 *global_pointers::fpu_ip = scalar(slots, 71)? as i32;
1095 *global_pointers::fpu_ip_selector = scalar(slots, 72)? as i32;
1096 *global_pointers::fpu_dp = scalar(slots, 73)? as i32;
1097 *global_pointers::fpu_dp_selector = scalar(slots, 74)? as i32;
1098 *global_pointers::fpu_opcode = scalar(slots, 75)? as i32;
1099 *global_pointers::last_result = slots
1100 .get(86)
1101 .and_then(serde_json::Value::as_i64)
1102 .unwrap_or(0) as i32;
1103 *global_pointers::fpu_status_word = slots
1104 .get(87)
1105 .and_then(serde_json::Value::as_i64)
1106 .unwrap_or(0) as u16;
1107 *global_pointers::mxcsr = slots
1108 .get(88)
1109 .and_then(serde_json::Value::as_i64)
1110 .unwrap_or(0x1F80) as i32;
1111 }
1112
1113 let packed_memory = buffer_for(slots, buffers, 77)?;
1114 let bitmap = buffer_for(slots, buffers, 78)?;
1115 unsafe {
1116 std::ptr::write_bytes(memory::mem8, 0, self.ram_bytes as usize);
1117 }
1118 let page_count = self.ram_bytes as usize / 0x1000;
1119 let mut packed_page = 0usize;
1120 for page in 0..page_count {
1121 if bitmap
1122 .get(page >> 3)
1123 .map_or(false, |byte| byte & (1 << (page & 7)) != 0)
1124 {
1125 let src_start = packed_page * 0x1000;
1126 let src_end = src_start + 0x1000;
1127 if src_end > packed_memory.len() {
1128 return Err("packed memory buffer is shorter than bitmap population".to_owned());
1129 }
1130 unsafe {
1131 std::ptr::copy_nonoverlapping(
1132 packed_memory.as_ptr().add(src_start),
1133 memory::mem8.add(page * 0x1000),
1134 0x1000,
1135 );
1136 }
1137 packed_page += 1;
1138 }
1139 }
1140 if packed_page * 0x1000 != packed_memory.len() {
1141 return Err(format!(
1142 "packed memory has {} pages but bitmap references {}",
1143 packed_memory.len() / 0x1000,
1144 packed_page
1145 ));
1146 }
1147
1148 native_devices::restore_state(state, buffers)?;
1149 cpu::update_state_flags();
1150 unsafe {
1151 cpu::full_clear_tlb();
1152 }
1153 Ok(())
1154 }
1155}
1156
1157fn buffer_for<'a>(
1158 state: &[serde_json::Value],
1159 buffers: &'a [Vec<u8>],
1160 index: usize,
1161) -> Result<&'a [u8], String> {
1162 let buffer_id = state
1163 .get(index)
1164 .and_then(serde_json::Value::as_object)
1165 .and_then(|object| object.get("buffer_id"))
1166 .and_then(serde_json::Value::as_u64)
1167 .ok_or_else(|| format!("state[{index}] is not a typed buffer"))?
1168 as usize;
1169 buffers
1170 .get(buffer_id)
1171 .map(Vec::as_slice)
1172 .ok_or_else(|| format!("buffer id {buffer_id} is out of range"))
1173}
1174
1175fn byte_array_from_state(
1176 state: &[serde_json::Value],
1177 len: usize,
1178 name: &str,
1179) -> Result<[u8; 13], String> {
1180 byte_array_from_values(state, len, name)
1181}
1182
1183fn byte_array_from_values(
1184 state: &[serde_json::Value],
1185 len: usize,
1186 name: &str,
1187) -> Result<[u8; 13], String> {
1188 if len != 13 || state.len() < len {
1189 return Err(format!("{name} has {} fields; expected {len}", state.len()));
1190 }
1191 let mut result = [0u8; 13];
1192 for (index, value) in state.iter().take(len).enumerate() {
1193 if index == 5 {
1194 continue;
1197 }
1198 result[index] = value
1199 .as_i64()
1200 .ok_or_else(|| format!("{name}[{index}] is not an integer"))?
1201 as u8;
1202 }
1203 Ok(result)
1204}
1205
1206fn scalar(state: &[serde_json::Value], index: usize) -> Result<i64, String> {
1207 state
1208 .get(index)
1209 .and_then(serde_json::Value::as_i64)
1210 .ok_or_else(|| format!("state[{index}] is not an integer scalar"))
1211}
1212
1213fn copy_buffer(
1214 state: &[serde_json::Value],
1215 buffers: &[Vec<u8>],
1216 index: usize,
1217 target: &mut [u8],
1218) -> Result<(), String> {
1219 let source = buffer_for(state, buffers, index)?;
1220 if source.len() != target.len() {
1221 return Err(format!(
1222 "state[{index}] length {} != expected {}",
1223 source.len(),
1224 target.len()
1225 ));
1226 }
1227 target.copy_from_slice(source);
1228 Ok(())
1229}
1230
1231fn copy_i32_buffer(
1232 state: &[serde_json::Value],
1233 buffers: &[Vec<u8>],
1234 index: usize,
1235 target: &mut [u8],
1236) -> Result<(), String> {
1237 copy_buffer(state, buffers, index, target)
1238}
1239
1240fn copy_u16_buffer(
1241 state: &[serde_json::Value],
1242 buffers: &[Vec<u8>],
1243 index: usize,
1244 target: &mut [u8],
1245) -> Result<(), String> {
1246 copy_buffer(state, buffers, index, target)
1247}
1248
1249fn copy_u32_buffer(
1250 state: &[serde_json::Value],
1251 buffers: &[Vec<u8>],
1252 index: usize,
1253 target: &mut [u8],
1254) -> Result<(), String> {
1255 copy_buffer(state, buffers, index, target)
1256}
1257
1258fn copy_u64_buffer(
1259 state: &[serde_json::Value],
1260 buffers: &[Vec<u8>],
1261 index: usize,
1262 target: &mut [u8],
1263) -> Result<(), String> {
1264 copy_buffer(state, buffers, index, target)
1265}
1266
1267#[cfg(test)]
1268mod keyboard_tests {
1269 use super::keycode_for_ascii;
1270
1271 #[test]
1272 fn maps_lowercase_without_shift() {
1273 assert_eq!(keycode_for_ascii(b'a'), Some((0x1E, false)));
1274 assert_eq!(keycode_for_ascii(b'z'), Some((0x2C, false)));
1275 }
1276
1277 #[test]
1278 fn maps_uppercase_and_punctuation_with_shift() {
1279 assert_eq!(keycode_for_ascii(b'A'), Some((0x1E, true)));
1280 assert_eq!(keycode_for_ascii(b'!'), Some((0x02, true)));
1281 assert_eq!(keycode_for_ascii(b'_'), Some((0x0C, true)));
1282 }
1283
1284 #[test]
1285 fn maps_shell_control_characters() {
1286 assert_eq!(keycode_for_ascii(b' '), Some((0x39, false)));
1287 assert_eq!(keycode_for_ascii(b'\n'), Some((0x1C, false)));
1288 assert_eq!(keycode_for_ascii(b'\t'), Some((0x0F, false)));
1289 }
1290}