Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add instruction decoding and emulating support for IOIO instructions #391

Merged
merged 12 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion fuzz/fuzz_targets/insn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@ fuzz_target!(|input: &[u8]| -> Corpus {
data.copy_from_slice(input);

let insn = Instruction::new(data);
let _ = core::hint::black_box(insn.decode(&TestCtx));
let _ = core::hint::black_box({
let mut ctx = TestCtx::default();
match insn.decode(&ctx) {
Ok(insn_ctx) => insn_ctx.emulate(&mut ctx),
Err(e) => Err(e),
}
});

Corpus::Keep
});
1 change: 1 addition & 0 deletions kernel/src/cpu/efer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ use crate::platform::SvsmPlatform;
use bitflags::bitflags;

bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct EFERFlags: u64 {
const SCE = 1 << 0; // System Call Extensions
const LME = 1 << 8; // Long Mode Enable
Expand Down
113 changes: 111 additions & 2 deletions kernel/src/cpu/idt/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@
//
// Author: Joerg Roedel <[email protected]>

extern crate alloc;

use crate::address::{Address, VirtAddr};
use crate::cpu::control_regs::{read_cr0, read_cr4};
use crate::cpu::efer::read_efer;
use crate::cpu::gdt::gdt;
use crate::cpu::registers::{X86GeneralRegs, X86InterruptFrame};
use crate::insn_decode::{InsnMachineCtx, SegRegister};
use crate::insn_decode::{InsnError, InsnMachineCtx, InsnMachineMem, Register, SegRegister};
use crate::locking::{RWLock, ReadLockGuard, WriteLockGuard};
use crate::types::SVSM_CS;
use crate::mm::GuestPtr;
use crate::platform::SVSM_PLATFORM;
use crate::types::{Bytes, SVSM_CS};
use alloc::boxed::Box;
use core::arch::{asm, global_asm};
use core::mem;
use core::ptr::addr_of;
Expand Down Expand Up @@ -44,6 +49,18 @@ pub const PF_ERROR_WRITE: usize = 2;

pub const INT_INJ_VECTOR: usize = 0x50;

bitflags::bitflags! {
/// Page fault error code flags.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PageFaultError :u32 {
const P = 1 << 0;
const W = 1 << 1;
const U = 1 << 2;
const R = 1 << 3;
const I = 1 << 4;
}
}

#[repr(C, packed)]
#[derive(Default, Debug, Clone, Copy)]
pub struct X86ExceptionContext {
Expand Down Expand Up @@ -71,6 +88,98 @@ impl InsnMachineCtx for X86ExceptionContext {
fn read_cr4(&self) -> u64 {
read_cr4().bits()
}

fn read_reg(&self, reg: Register) -> usize {
match reg {
Register::Rax => self.regs.rax,
Register::Rdx => self.regs.rdx,
Register::Rcx => self.regs.rcx,
Register::Rbx => self.regs.rdx,
Register::Rsp => self.frame.rsp,
Register::Rbp => self.regs.rbp,
Register::Rdi => self.regs.rdi,
Register::Rsi => self.regs.rsi,
Register::R8 => self.regs.r8,
Register::R9 => self.regs.r9,
Register::R10 => self.regs.r10,
Register::R11 => self.regs.r11,
Register::R12 => self.regs.r12,
Register::R13 => self.regs.r13,
Register::R14 => self.regs.r14,
Register::R15 => self.regs.r15,
Register::Rip => self.frame.rip,
}
}

fn read_flags(&self) -> usize {
self.frame.flags
}

fn write_reg(&mut self, reg: Register, val: usize) {
match reg {
Register::Rax => self.regs.rax = val,
Register::Rdx => self.regs.rdx = val,
Register::Rcx => self.regs.rcx = val,
Register::Rbx => self.regs.rdx = val,
Register::Rsp => self.frame.rsp = val,
Register::Rbp => self.regs.rbp = val,
Register::Rdi => self.regs.rdi = val,
Register::Rsi => self.regs.rsi = val,
Register::R8 => self.regs.r8 = val,
Register::R9 => self.regs.r9 = val,
Register::R10 => self.regs.r10 = val,
Register::R11 => self.regs.r11 = val,
Register::R12 => self.regs.r12 = val,
Register::R13 => self.regs.r13 = val,
Register::R14 => self.regs.r14 = val,
Register::R15 => self.regs.r15 = val,
Register::Rip => self.frame.rip = val,
}
}

fn read_cpl(&self) -> usize {
self.frame.cs & 3
}

fn map_linear_addr<T: Copy + 'static>(
&self,
la: usize,
_write: bool,
_fetch: bool,
) -> Result<Box<dyn InsnMachineMem<Item = T>>, InsnError> {
if user_mode(self) {
todo!();
} else {
Ok(Box::new(GuestPtr::<T>::new(VirtAddr::from(la))))
cxdong marked this conversation as resolved.
Show resolved Hide resolved
}
}

fn ioio_perm(&self, _port: u16, _size: Bytes, _io_read: bool) -> bool {
// Check if the IO port can be supported by user mode
todo!();
}

fn ioio_in(&self, port: u16, size: Bytes) -> Result<u64, InsnError> {
let io_port = SVSM_PLATFORM.as_dyn_ref().get_io_port();
let data = match size {
Bytes::One => io_port.inb(port) as u64,
Bytes::Two => io_port.inw(port) as u64,
Bytes::Four => io_port.inl(port) as u64,
_ => return Err(InsnError::IoIoIn),
};
Ok(data)
}

fn ioio_out(&mut self, port: u16, size: Bytes, data: u64) -> Result<(), InsnError> {
let io_port = SVSM_PLATFORM.as_dyn_ref().get_io_port();
match size {
Bytes::One => io_port.outb(port, data as u8),
Bytes::Two => io_port.outw(port, data as u16),
Bytes::Four => io_port.outl(port, data as u32),
_ => return Err(InsnError::IoIoOut),
}
Ok(())
}
}

pub fn user_mode(ctxt: &X86ExceptionContext) -> bool {
Expand Down
25 changes: 25 additions & 0 deletions kernel/src/cpu/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,28 @@ bitflags! {
const G = 1 << 55;
}
}

bitflags! {
#[derive(Clone, Copy, Debug)]
pub struct RFlags: usize {
const CF = 1 << 0;
const FIXED = 1 << 1;
const PF = 1 << 2;
const AF = 1 << 4;
const ZF = 1 << 6;
const SF = 1 << 7;
const TF = 1 << 8;
const IF = 1 << 9;
const DF = 1 << 10;
const OF = 1 << 11;
const IOPL = 3 << 12;
const NT = 1 << 14;
const MD = 1 << 15;
const RF = 1 << 16;
const VM = 1 << 17;
const AC = 1 << 18;
const VIF = 1 << 19;
const VIP = 1 << 20;
const ID = 1 << 21;
}
}
27 changes: 21 additions & 6 deletions kernel/src/cpu/vc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ pub fn handle_vc_exception(ctx: &mut X86ExceptionContext, vector: usize) -> Resu

let insn_ctx = vc_decode_insn(ctx)?;

match (error_code, insn_ctx.and_then(|d| d.insn())) {
match (error_code, insn_ctx.as_ref().and_then(|d| d.insn())) {
// If the gdb stub is enabled then debugging operations such as single stepping
// will cause either an exception via DB_VECTOR if the DEBUG_SWAP sev_feature is
// clear, or a VC exception with an error code of X86_TRAP if set.
Expand All @@ -143,7 +143,11 @@ pub fn handle_vc_exception(ctx: &mut X86ExceptionContext, vector: usize) -> Resu
Ok(())
}
(SVM_EXIT_CPUID, Some(DecodedInsn::Cpuid)) => handle_cpuid(ctx),
(SVM_EXIT_IOIO, Some(ins)) => handle_ioio(ctx, ghcb, ins),
(SVM_EXIT_IOIO, Some(_)) => insn_ctx
.as_ref()
.unwrap()
cxdong marked this conversation as resolved.
Show resolved Hide resolved
.emulate(ctx)
.map_err(SvsmError::from),
(SVM_EXIT_MSR, Some(ins)) => handle_msr(ctx, ghcb, ins),
(SVM_EXIT_RDTSC, Some(DecodedInsn::Rdtsc)) => ghcb.rdtsc_regs(&mut ctx.regs),
(SVM_EXIT_RDTSCP, Some(DecodedInsn::Rdtsc)) => ghcb.rdtscp_regs(&mut ctx.regs),
Expand Down Expand Up @@ -225,7 +229,7 @@ fn snp_cpuid(ctx: &mut X86ExceptionContext) -> Result<(), SvsmError> {
}

fn vc_finish_insn(ctx: &mut X86ExceptionContext, insn_ctx: &Option<DecodedInsnCtx>) {
ctx.frame.rip += insn_ctx.map_or(0, |d| d.size())
ctx.frame.rip += insn_ctx.as_ref().map_or(0, |d| d.size())
}

fn ioio_get_port(source: Operand, ctx: &X86ExceptionContext) -> u16 {
Expand Down Expand Up @@ -415,7 +419,13 @@ mod tests {

fn rep_outsw(port: u16, data: &[u16]) {
unsafe {
asm!("rep outsw", in("dx") port, in("rsi") data.as_ptr(), in("rcx") data.len(), options(att_syntax))
asm!("rep outsw", in("dx") port, in("rsi") data.as_ptr(), inout("rcx") data.len() => _, options(att_syntax))
}
}

fn rep_insw(port: u16, data: &mut [u16]) {
unsafe {
asm!("rep insw", in("dx") port, in("rdi") data.as_ptr(), inout("rcx") data.len() => _, options(att_syntax))
}
}

Expand Down Expand Up @@ -477,15 +487,20 @@ mod tests {
}

#[test]
// #[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
#[ignore = "Currently unhandled by #VC handler"]
#[cfg_attr(not(test_in_svsm), ignore = "Can only be run inside guest")]
fn test_port_io_string_16_get_last() {
const TEST_DATA: &[u16] = &[0x1234, 0x5678, 0x9abc, 0xdef0];
verify_ghcb_gets_altered(|| rep_outsw(TESTDEV_ECHO_LAST_PORT, TEST_DATA));
assert_eq!(
TEST_DATA.last().unwrap(),
&verify_ghcb_gets_altered(|| inw(TESTDEV_ECHO_LAST_PORT))
);

let mut test_data: [u16; 4] = [0; 4];
verify_ghcb_gets_altered(|| rep_insw(TESTDEV_ECHO_LAST_PORT, &mut test_data));
for d in test_data.iter() {
assert_eq!(d, TEST_DATA.last().unwrap());
}
}

#[test]
Expand Down
Loading
Loading