From 2eccb107eb7dffedb4fd7efcda9528c1d95f119f Mon Sep 17 00:00:00 2001 From: Masato Imai Date: Fri, 13 Jun 2025 15:40:32 +0000 Subject: [PATCH] io emulation --- src/serial.rs | 15 +++++++++ src/vmm/io.rs | 83 +++++++++++++++++++++++++++++++++++++++++++++++++ src/vmm/mod.rs | 1 + src/vmm/qual.rs | 12 +++---- src/vmm/vcpu.rs | 13 +++++--- 5 files changed, 114 insertions(+), 10 deletions(-) create mode 100644 src/vmm/io.rs diff --git a/src/serial.rs b/src/serial.rs index 919ad95..cb1a4f6 100644 --- a/src/serial.rs +++ b/src/serial.rs @@ -1,3 +1,5 @@ +use core::fmt::Write; + use lazy_static::lazy_static; use spin::Mutex; use uart_16550::SerialPort; @@ -23,6 +25,19 @@ pub fn _print(args: ::core::fmt::Arguments) { }); } +#[inline(always)] +pub fn write_byte(byte: u8) { + use x86_64::instructions::interrupts; + + if interrupts::are_enabled() { + interrupts::without_interrupts(|| { + SERIAL1.lock().send(byte); + }); + } else { + SERIAL1.lock().send(byte); + } +} + #[macro_export] macro_rules! serial_print { ($($arg:tt)*) => { diff --git a/src/vmm/io.rs b/src/vmm/io.rs new file mode 100644 index 0000000..455d1dd --- /dev/null +++ b/src/vmm/io.rs @@ -0,0 +1,83 @@ +use x86::io::inb; + +use crate::{ + serial, + vmm::{qual::QualIo, vcpu::VCpu}, +}; + +#[derive(Default)] +pub struct Serial { + pub ier: u8, + pub mcr: u8, +} + +pub fn handle_io(vcpu: &mut VCpu, qual: QualIo) { + match qual.direction() { + 0 => { + handle_io_out(vcpu, qual); + } + 1 => { + handle_io_in(vcpu, qual); + } + _ => {} + } +} + +pub fn handle_io_in(vcpu: &mut VCpu, qual: QualIo) { + let regs = &mut vcpu.guest_registers; + match qual.port() { + 0x0CF8..0x0CFF => { + regs.rax = 0; + } + 0xC000..0xCFFF => {} //ignore + 0x03F..0x03FF => handle_serial_in(vcpu, qual), + _ => { + panic!("IO in: invalid port: {:#x}", qual.port()); + } + } +} + +pub fn handle_io_out(vcpu: &mut VCpu, qual: QualIo) { + let regs = &vcpu.guest_registers; + match qual.port() { + 0x0CF8..0x0CFF => {} //ignore + 0xC000..0xCFFF => {} //ignore + 0x03F8..0x03FF => handle_serial_out(vcpu, qual), + _ => { + panic!("IO out: invalid port: {:#x}", qual.port()); + } + } +} + +fn handle_serial_in(vcpu: &mut VCpu, qual: QualIo) { + let regs = &mut vcpu.guest_registers; + match qual.port() { + 0x3F8 => regs.rax = unsafe { inb(qual.port()).into() }, + 0x3F9 => regs.rax = vcpu.serial.ier as u64, + 0x3FA => regs.rax = unsafe { inb(qual.port()).into() }, + 0x3FB => regs.rax = 0, + 0x3FC => regs.rax = vcpu.serial.mcr as u64, + 0x3FD => regs.rax = unsafe { inb(qual.port()).into() }, + 0x3FE => regs.rax = unsafe { inb(qual.port()).into() }, + 0x3FF => regs.rax = 0, + _ => { + panic!("Serial in: invalid port: {:#x}", qual.port()); + } + } +} + +fn handle_serial_out(vcpu: &mut VCpu, qual: QualIo) { + let regs = &mut vcpu.guest_registers; + match qual.port() { + 0x3F8 => serial::write_byte(regs.rax as u8), + 0x3F9 => vcpu.serial.ier = regs.rax as u8, + 0x3FA => {} + 0x3FB => {} + 0x3FC => vcpu.serial.mcr = regs.rax as u8, + 0x3FD => {} + 0x3FF => {} + _ => { + panic!("Serial out: invalid port: {:#x}", qual.port()); + } + } +} diff --git a/src/vmm/mod.rs b/src/vmm/mod.rs index 0b73ad7..780a419 100644 --- a/src/vmm/mod.rs +++ b/src/vmm/mod.rs @@ -4,6 +4,7 @@ pub mod cr; pub mod ept; pub mod error; pub mod fpu; +pub mod io; pub mod linux; pub mod msr; pub mod qual; diff --git a/src/vmm/qual.rs b/src/vmm/qual.rs index 3b01842..d24c720 100644 --- a/src/vmm/qual.rs +++ b/src/vmm/qual.rs @@ -131,10 +131,10 @@ bitfield! { pub struct QualIo(u64); impl Debug; - u8, size, set_size: 3, 0; - u8, direction, set_direction: 4, 4; - u8, string, set_string: 5, 5; - u8, rep, set_rep: 6, 6; - u8, operand_encoding, set_operand_encoding: 7, 7; - u16, port, set_port: 31, 16; + pub u8, size, set_size: 3, 0; + pub u8, direction, set_direction: 4, 4; + pub u8, string, set_string: 5, 5; + pub u8, rep, set_rep: 6, 6; + pub u8, operand_encoding, set_operand_encoding: 7, 7; + pub u16, port, set_port: 31, 16; } diff --git a/src/vmm/vcpu.rs b/src/vmm/vcpu.rs index 7ce1097..d327a4e 100644 --- a/src/vmm/vcpu.rs +++ b/src/vmm/vcpu.rs @@ -16,8 +16,10 @@ use crate::{ info, memory::BootInfoFrameAllocator, vmm::{ - cpuid, cr, fpu, msr, - qual::QualCr, + cpuid, cr, fpu, + io::{self, Serial}, + msr, + qual::{QualCr, QualIo}, vmcs::{ DescriptorType, EntryControls, Granularity, PrimaryExitControls, PrimaryProcessorBasedVmExecutionControls, SecondaryProcessorBasedVmExecutionControls, @@ -50,6 +52,7 @@ pub struct VCpu { pub ia32e_enabled: bool, pub xcr0: XCR0, pub host_xcr0: u64, + pub serial: Serial, } const TEMP_STACK_SIZE: usize = 4096; @@ -76,6 +79,7 @@ impl VCpu { ia32e_enabled: false, xcr0: XCR0(3), host_xcr0: 0, + serial: Serial::default(), } } @@ -694,7 +698,6 @@ impl VCpu { _ => {} } } else { - info!("RIP: {:#x}", unsafe { vmread(vmcs::guest::RIP) }.unwrap()); match info.get_reason() { VmxExitReason::HLT => { info!("HLT instruction executed"); @@ -730,7 +733,9 @@ impl VCpu { self.step_next_inst().unwrap(); } VmxExitReason::IO_INSTRUCTION => { - info!("IO instruction executed"); + let qual = unsafe { vmread(vmcs::ro::EXIT_QUALIFICATION).unwrap() }; + let qual = QualIo(qual); + io::handle_io(self, qual); self.step_next_inst().unwrap(); } _ => {