Skip to content

Commit f90c7a9

Browse files
committed
cpu/vc: add #VC IOIO handler
Handle the IN and OUT instructions by calling the hypervisor through the existing GHCB ioio methods. At this point, IN and OUT instructions with the port given as immediate value is not supported yet. INS and OUTS are also not supported yet. Signed-off-by: Thomas Leroy <[email protected]>
1 parent 3bdf708 commit f90c7a9

File tree

4 files changed

+101
-77
lines changed

4 files changed

+101
-77
lines changed

src/cpu/idt/common.rs

-42
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
// Author: Joerg Roedel <[email protected]>
66

77
use crate::address::{Address, VirtAddr};
8-
use crate::cpu::insn::Instruction;
98
use crate::cpu::registers::{X86GeneralRegs, X86InterruptFrame};
109
use crate::types::SVSM_CS;
1110
use core::arch::{asm, global_asm};
@@ -45,7 +44,6 @@ pub struct X86ExceptionContext {
4544
pub vector: usize,
4645
pub error_code: usize,
4746
pub frame: X86InterruptFrame,
48-
pub insn: Instruction,
4947
}
5048

5149
#[derive(Copy, Clone, Default, Debug)]
@@ -145,8 +143,6 @@ pub fn triple_fault() {
145143
#[cfg(feature = "enable-stacktrace")]
146144
extern "C" {
147145
static generic_idt_handler_return: u8;
148-
pub static stage2_idt_handler_array: u8;
149-
pub static svsm_idt_handler_array: u8;
150146
}
151147

152148
#[cfg(feature = "enable-stacktrace")]
@@ -157,44 +153,6 @@ pub fn is_exception_handler_return_site(rip: VirtAddr) -> bool {
157153

158154
global_asm!(
159155
r#"
160-
/* Stage 2 handler array setup */
161-
.text
162-
push_regs:
163-
pushq %rax
164-
pushq %rbx
165-
pushq %rcx
166-
pushq %rdx
167-
pushq %rsi
168-
pushq %rdi
169-
pushq %rbp
170-
pushq %r8
171-
pushq %r9
172-
pushq %r10
173-
pushq %r11
174-
pushq %r12
175-
pushq %r13
176-
pushq %r14
177-
pushq %r15
178-
179-
movq %rsp, %rdi
180-
call stage2_generic_idt_handler
181-
182-
jmp generic_idt_handler_return
183-
184-
.align 32
185-
.globl stage2_idt_handler_array
186-
stage2_idt_handler_array:
187-
i = 0
188-
.rept 32
189-
.align 32
190-
.if ((0x20027d00 >> i) & 1) == 0
191-
pushq $0
192-
.endif
193-
pushq $i /* Vector Number */
194-
jmp push_regs
195-
i = i + 1
196-
.endr
197-
198156
/* Needed by the stack unwinder to recognize exception frames. */
199157
.globl generic_idt_handler_return
200158
generic_idt_handler_return:

src/cpu/idt/svsm.rs

+8-5
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77
use super::super::control_regs::read_cr2;
88
use super::super::extable::handle_exception_table;
99
use super::super::tss::IST_DF;
10-
use super::super::vc::stage2_handle_vc_exception;
10+
use super::super::vc::handle_vc_exception;
1111
use super::common::{
12-
load_idt, svsm_idt_handler_array, Idt, IdtEntry, BP_VECTOR, DF_VECTOR, GLOBAL_IDT, GP_VECTOR,
13-
PF_VECTOR, VC_VECTOR,
12+
load_idt, Idt, IdtEntry, BP_VECTOR, DF_VECTOR, GLOBAL_IDT, GP_VECTOR, PF_VECTOR, VC_VECTOR,
1413
};
1514
use crate::address::VirtAddr;
1615
use crate::cpu::X86ExceptionContext;
@@ -45,7 +44,7 @@ pub fn idt_init() {
4544
}
4645

4746
#[no_mangle]
48-
pub fn generic_idt_handler(ctx: &mut X86ExceptionContext) {
47+
pub extern "C" fn generic_idt_handler(ctx: &mut X86ExceptionContext) {
4948
if ctx.vector == DF_VECTOR {
5049
let cr2 = read_cr2();
5150
let rip = ctx.frame.rip;
@@ -76,7 +75,7 @@ pub fn generic_idt_handler(ctx: &mut X86ExceptionContext) {
7675
);
7776
}
7877
} else if ctx.vector == VC_VECTOR {
79-
stage2_handle_vc_exception(ctx);
78+
handle_vc_exception(ctx);
8079
} else if ctx.vector == BP_VECTOR {
8180
handle_bp_exception(ctx);
8281
} else {
@@ -93,6 +92,10 @@ pub fn generic_idt_handler(ctx: &mut X86ExceptionContext) {
9392
}
9493
}
9594

95+
extern "C" {
96+
static svsm_idt_handler_array: u8;
97+
}
98+
9699
global_asm!(
97100
r#"
98101
.text

src/cpu/insn.rs

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ extern crate alloc;
99
use crate::cpu::vc::VcError;
1010
use crate::error::SvsmError;
1111
use core::ptr;
12-
use log::info;
1312

1413
const MAX_INSN_SIZE: usize = 15;
1514
const MAX_INSN_FIELD_SIZE: usize = 3;

src/cpu/vc.rs

+93-29
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,18 @@
66

77
use super::idt::common::X86ExceptionContext;
88
use crate::cpu::cpuid::{cpuid_table_raw, CpuidLeaf};
9-
use crate::cpu::extable::handle_exception_table;
109
use crate::cpu::insn::{insn_fetch, Instruction};
10+
use crate::cpu::percpu::this_cpu_mut;
1111
use crate::cpu::registers::X86GeneralRegs;
1212
use crate::debug::gdbstub::svsm_gdbstub::handle_db_exception;
1313
use crate::error::SvsmError;
14+
use crate::sev::ghcb::{GHCBIOSize, GHCB};
1415
use core::fmt;
1516

1617
pub const SVM_EXIT_EXCP_BASE: usize = 0x40;
1718
pub const SVM_EXIT_LAST_EXCP: usize = 0x5f;
1819
pub const SVM_EXIT_CPUID: usize = 0x72;
20+
pub const SVM_EXIT_IOIO: usize = 0x7b;
1921
pub const X86_TRAP_DB: usize = 0x01;
2022
pub const X86_TRAP: usize = SVM_EXIT_EXCP_BASE + X86_TRAP_DB;
2123

@@ -52,7 +54,7 @@ pub fn stage2_handle_vc_exception(ctx: &mut X86ExceptionContext) {
5254
let err = ctx.error_code;
5355
let rip = ctx.frame.rip;
5456

55-
vc_decode_insn(ctx).unwrap_or_else(|e| {
57+
let insn = vc_decode_insn(ctx).unwrap_or_else(|e| {
5658
panic!(
5759
"Unhandled #VC exception RIP {:#018x} error code: {:#018x} error {:?}",
5860
rip, err, e
@@ -77,7 +79,48 @@ pub fn stage2_handle_vc_exception(ctx: &mut X86ExceptionContext) {
7779
)
7880
});
7981

80-
vc_finish_insn(ctx);
82+
vc_finish_insn(ctx, &insn);
83+
}
84+
85+
pub fn handle_vc_exception(ctx: &mut X86ExceptionContext) {
86+
let error_code = ctx.error_code;
87+
let rip = ctx.frame.rip;
88+
89+
/*
90+
* To handle NAE events, we're supposed to reset the VALID_BITMAP field of the GHCB.
91+
* This is currently only relevant for IOIO handling. This field is currently reset in
92+
* the ioio_{in,ou} methods but it would be better to move the reset out of the different
93+
* handlers.
94+
*/
95+
let ghcb = this_cpu_mut().ghcb();
96+
97+
let insn = vc_decode_insn(ctx).unwrap_or_else(|e| {
98+
panic!(
99+
"Unhandled #VC exception RIP {:#018x} error code: {:#018x} error {:?}",
100+
rip, error_code, e
101+
)
102+
});
103+
104+
match error_code {
105+
// If the debugger is enabled then handle the DB exception
106+
// by directly invoking the exception handler
107+
X86_TRAP_DB => {
108+
handle_db_exception(ctx);
109+
Ok(())
110+
}
111+
112+
SVM_EXIT_CPUID => handle_cpuid(ctx),
113+
SVM_EXIT_IOIO => handle_ioio(ctx, ghcb, &insn),
114+
_ => Err(SvsmError::Vc(VcError::Unsupported)),
115+
}
116+
.unwrap_or_else(|err| {
117+
panic!(
118+
"Unhandled #VC exception RIP {:#018x} error code: {:#018x}: #VC error: {:?}",
119+
rip, error_code, err
120+
)
121+
});
122+
123+
vc_finish_insn(ctx, &insn);
81124
}
82125

83126
fn handle_cpuid(ctx: &mut X86ExceptionContext) -> Result<(), SvsmError> {
@@ -117,13 +160,55 @@ fn snp_cpuid(regs: &mut X86GeneralRegs) -> Result<(), SvsmError> {
117160
Ok(())
118161
}
119162

120-
fn vc_finish_insn(ctx: &mut X86ExceptionContext) {
121-
ctx.frame.rip += ctx.insn.length;
163+
fn vc_finish_insn(ctx: &mut X86ExceptionContext, insn: &Instruction) {
164+
ctx.frame.rip += insn.length;
165+
}
166+
167+
fn handle_ioio(
168+
ctx: &mut X86ExceptionContext,
169+
ghcb: &mut GHCB,
170+
insn: &Instruction,
171+
) -> Result<(), SvsmError> {
172+
let port: u16 = (ctx.regs.rdx & 0xffff) as u16;
173+
let out_value: u64 = ctx.regs.rax as u64;
174+
175+
match insn.opcode.bytes[0] {
176+
0x6C..=0x6F | 0xE4..=0xE7 => Err(SvsmError::Vc(VcError::Unsupported)),
177+
0xEC => {
178+
let ret = ghcb.ioio_in(port, GHCBIOSize::Size8)?;
179+
ctx.regs.rax = (ret & 0xff) as usize;
180+
Ok(())
181+
}
182+
0xED => {
183+
let mut size: GHCBIOSize = GHCBIOSize::Size32;
184+
let mut mask = 0xffffffff;
185+
if insn.prefixes.nb_bytes > 0 {
186+
// inw instruction has a 0x66 operand-size prefix for word-sized operands.
187+
size = GHCBIOSize::Size16;
188+
mask = 0xffff;
189+
}
190+
191+
let ret = ghcb.ioio_in(port, size)?;
192+
ctx.regs.rax = (ret & mask) as usize;
193+
Ok(())
194+
}
195+
0xEE => ghcb.ioio_out(port, GHCBIOSize::Size8, out_value),
196+
0xEF => {
197+
let mut size: GHCBIOSize = GHCBIOSize::Size32;
198+
if insn.prefixes.nb_bytes > 0 {
199+
// outw instruction has a 0x66 operand-size prefix for word-sized operands.
200+
size = GHCBIOSize::Size16;
201+
}
202+
203+
ghcb.ioio_out(port, size, out_value)
204+
}
205+
_ => Err(SvsmError::Vc(VcError::DecodeFailed)),
206+
}
122207
}
123208

124-
fn vc_decode_insn(ctx: &mut X86ExceptionContext) -> Result<(), SvsmError> {
209+
fn vc_decode_insn(ctx: &mut X86ExceptionContext) -> Result<Instruction, SvsmError> {
125210
if !vc_decoding_needed(ctx.error_code) {
126-
return Ok(());
211+
return Ok(Instruction::default());
127212
}
128213

129214
/*
@@ -135,30 +220,9 @@ fn vc_decode_insn(ctx: &mut X86ExceptionContext) -> Result<(), SvsmError> {
135220
let mut insn = Instruction::new(insn_raw);
136221
insn.decode()?;
137222

138-
ctx.insn = insn;
139-
140-
Ok(())
223+
Ok(insn)
141224
}
142225

143226
fn vc_decoding_needed(error_code: usize) -> bool {
144227
!(SVM_EXIT_EXCP_BASE..=SVM_EXIT_LAST_EXCP).contains(&error_code)
145228
}
146-
147-
pub fn handle_vc_exception(ctx: &mut X86ExceptionContext) {
148-
let err = ctx.error_code;
149-
let rip = ctx.frame.rip;
150-
151-
// If the debugger is enabled then handle the DB exception
152-
// by directly invoking the exception hander
153-
if err == (SVM_EXIT_EXCP_BASE + X86_TRAP_DB) {
154-
handle_db_exception(ctx);
155-
return;
156-
}
157-
158-
if !handle_exception_table(ctx) {
159-
panic!(
160-
"Unhandled #VC exception RIP {:#018x} error code: {:#018x}",
161-
rip, err
162-
);
163-
}
164-
}

0 commit comments

Comments
 (0)