Skip to main content

native_v86_core/
native_runtime.rs

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