Skip to content

Commit 25189d0

Browse files
tlendackysuryasaimadhu
authored andcommitted
x86/sev-es: Add support for handling IOIO exceptions
Add support for decoding and handling #VC exceptions for IOIO events. [ [email protected]: Adapted code to #VC handling framework ] Co-developed-by: Joerg Roedel <[email protected]> Signed-off-by: Tom Lendacky <[email protected]> Signed-off-by: Joerg Roedel <[email protected]> Signed-off-by: Borislav Petkov <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 69add17 commit 25189d0

File tree

2 files changed

+246
-0
lines changed

2 files changed

+246
-0
lines changed

arch/x86/boot/compressed/sev-es.c

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,35 @@
2424
struct ghcb boot_ghcb_page __aligned(PAGE_SIZE);
2525
struct ghcb *boot_ghcb;
2626

27+
/*
28+
* Copy a version of this function here - insn-eval.c can't be used in
29+
* pre-decompression code.
30+
*/
31+
static bool insn_has_rep_prefix(struct insn *insn)
32+
{
33+
int i;
34+
35+
insn_get_prefixes(insn);
36+
37+
for (i = 0; i < insn->prefixes.nbytes; i++) {
38+
insn_byte_t p = insn->prefixes.bytes[i];
39+
40+
if (p == 0xf2 || p == 0xf3)
41+
return true;
42+
}
43+
44+
return false;
45+
}
46+
47+
/*
48+
* Only a dummy for insn_get_seg_base() - Early boot-code is 64bit only and
49+
* doesn't use segments.
50+
*/
51+
static unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx)
52+
{
53+
return 0UL;
54+
}
55+
2756
static inline u64 sev_es_rd_ghcb_msr(void)
2857
{
2958
unsigned long low, high;
@@ -151,6 +180,9 @@ void do_boot_stage2_vc(struct pt_regs *regs, unsigned long exit_code)
151180
goto finish;
152181

153182
switch (exit_code) {
183+
case SVM_EXIT_IOIO:
184+
result = vc_handle_ioio(boot_ghcb, &ctxt);
185+
break;
154186
default:
155187
result = ES_UNSUPPORTED;
156188
break;

arch/x86/kernel/sev-es-shared.c

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,3 +218,217 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt,
218218

219219
return ret;
220220
}
221+
222+
#define IOIO_TYPE_STR BIT(2)
223+
#define IOIO_TYPE_IN 1
224+
#define IOIO_TYPE_INS (IOIO_TYPE_IN | IOIO_TYPE_STR)
225+
#define IOIO_TYPE_OUT 0
226+
#define IOIO_TYPE_OUTS (IOIO_TYPE_OUT | IOIO_TYPE_STR)
227+
228+
#define IOIO_REP BIT(3)
229+
230+
#define IOIO_ADDR_64 BIT(9)
231+
#define IOIO_ADDR_32 BIT(8)
232+
#define IOIO_ADDR_16 BIT(7)
233+
234+
#define IOIO_DATA_32 BIT(6)
235+
#define IOIO_DATA_16 BIT(5)
236+
#define IOIO_DATA_8 BIT(4)
237+
238+
#define IOIO_SEG_ES (0 << 10)
239+
#define IOIO_SEG_DS (3 << 10)
240+
241+
static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
242+
{
243+
struct insn *insn = &ctxt->insn;
244+
*exitinfo = 0;
245+
246+
switch (insn->opcode.bytes[0]) {
247+
/* INS opcodes */
248+
case 0x6c:
249+
case 0x6d:
250+
*exitinfo |= IOIO_TYPE_INS;
251+
*exitinfo |= IOIO_SEG_ES;
252+
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
253+
break;
254+
255+
/* OUTS opcodes */
256+
case 0x6e:
257+
case 0x6f:
258+
*exitinfo |= IOIO_TYPE_OUTS;
259+
*exitinfo |= IOIO_SEG_DS;
260+
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
261+
break;
262+
263+
/* IN immediate opcodes */
264+
case 0xe4:
265+
case 0xe5:
266+
*exitinfo |= IOIO_TYPE_IN;
267+
*exitinfo |= (u64)insn->immediate.value << 16;
268+
break;
269+
270+
/* OUT immediate opcodes */
271+
case 0xe6:
272+
case 0xe7:
273+
*exitinfo |= IOIO_TYPE_OUT;
274+
*exitinfo |= (u64)insn->immediate.value << 16;
275+
break;
276+
277+
/* IN register opcodes */
278+
case 0xec:
279+
case 0xed:
280+
*exitinfo |= IOIO_TYPE_IN;
281+
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
282+
break;
283+
284+
/* OUT register opcodes */
285+
case 0xee:
286+
case 0xef:
287+
*exitinfo |= IOIO_TYPE_OUT;
288+
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
289+
break;
290+
291+
default:
292+
return ES_DECODE_FAILED;
293+
}
294+
295+
switch (insn->opcode.bytes[0]) {
296+
case 0x6c:
297+
case 0x6e:
298+
case 0xe4:
299+
case 0xe6:
300+
case 0xec:
301+
case 0xee:
302+
/* Single byte opcodes */
303+
*exitinfo |= IOIO_DATA_8;
304+
break;
305+
default:
306+
/* Length determined by instruction parsing */
307+
*exitinfo |= (insn->opnd_bytes == 2) ? IOIO_DATA_16
308+
: IOIO_DATA_32;
309+
}
310+
switch (insn->addr_bytes) {
311+
case 2:
312+
*exitinfo |= IOIO_ADDR_16;
313+
break;
314+
case 4:
315+
*exitinfo |= IOIO_ADDR_32;
316+
break;
317+
case 8:
318+
*exitinfo |= IOIO_ADDR_64;
319+
break;
320+
}
321+
322+
if (insn_has_rep_prefix(insn))
323+
*exitinfo |= IOIO_REP;
324+
325+
return ES_OK;
326+
}
327+
328+
static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
329+
{
330+
struct pt_regs *regs = ctxt->regs;
331+
u64 exit_info_1, exit_info_2;
332+
enum es_result ret;
333+
334+
ret = vc_ioio_exitinfo(ctxt, &exit_info_1);
335+
if (ret != ES_OK)
336+
return ret;
337+
338+
if (exit_info_1 & IOIO_TYPE_STR) {
339+
340+
/* (REP) INS/OUTS */
341+
342+
bool df = ((regs->flags & X86_EFLAGS_DF) == X86_EFLAGS_DF);
343+
unsigned int io_bytes, exit_bytes;
344+
unsigned int ghcb_count, op_count;
345+
unsigned long es_base;
346+
u64 sw_scratch;
347+
348+
/*
349+
* For the string variants with rep prefix the amount of in/out
350+
* operations per #VC exception is limited so that the kernel
351+
* has a chance to take interrupts and re-schedule while the
352+
* instruction is emulated.
353+
*/
354+
io_bytes = (exit_info_1 >> 4) & 0x7;
355+
ghcb_count = sizeof(ghcb->shared_buffer) / io_bytes;
356+
357+
op_count = (exit_info_1 & IOIO_REP) ? regs->cx : 1;
358+
exit_info_2 = min(op_count, ghcb_count);
359+
exit_bytes = exit_info_2 * io_bytes;
360+
361+
es_base = insn_get_seg_base(ctxt->regs, INAT_SEG_REG_ES);
362+
363+
/* Read bytes of OUTS into the shared buffer */
364+
if (!(exit_info_1 & IOIO_TYPE_IN)) {
365+
ret = vc_insn_string_read(ctxt,
366+
(void *)(es_base + regs->si),
367+
ghcb->shared_buffer, io_bytes,
368+
exit_info_2, df);
369+
if (ret)
370+
return ret;
371+
}
372+
373+
/*
374+
* Issue an VMGEXIT to the HV to consume the bytes from the
375+
* shared buffer or to have it write them into the shared buffer
376+
* depending on the instruction: OUTS or INS.
377+
*/
378+
sw_scratch = __pa(ghcb) + offsetof(struct ghcb, shared_buffer);
379+
ghcb_set_sw_scratch(ghcb, sw_scratch);
380+
ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_IOIO,
381+
exit_info_1, exit_info_2);
382+
if (ret != ES_OK)
383+
return ret;
384+
385+
/* Read bytes from shared buffer into the guest's destination. */
386+
if (exit_info_1 & IOIO_TYPE_IN) {
387+
ret = vc_insn_string_write(ctxt,
388+
(void *)(es_base + regs->di),
389+
ghcb->shared_buffer, io_bytes,
390+
exit_info_2, df);
391+
if (ret)
392+
return ret;
393+
394+
if (df)
395+
regs->di -= exit_bytes;
396+
else
397+
regs->di += exit_bytes;
398+
} else {
399+
if (df)
400+
regs->si -= exit_bytes;
401+
else
402+
regs->si += exit_bytes;
403+
}
404+
405+
if (exit_info_1 & IOIO_REP)
406+
regs->cx -= exit_info_2;
407+
408+
ret = regs->cx ? ES_RETRY : ES_OK;
409+
410+
} else {
411+
412+
/* IN/OUT into/from rAX */
413+
414+
int bits = (exit_info_1 & 0x70) >> 1;
415+
u64 rax = 0;
416+
417+
if (!(exit_info_1 & IOIO_TYPE_IN))
418+
rax = lower_bits(regs->ax, bits);
419+
420+
ghcb_set_rax(ghcb, rax);
421+
422+
ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_IOIO, exit_info_1, 0);
423+
if (ret != ES_OK)
424+
return ret;
425+
426+
if (exit_info_1 & IOIO_TYPE_IN) {
427+
if (!ghcb_rax_is_valid(ghcb))
428+
return ES_VMM_ERROR;
429+
regs->ax = lower_bits(ghcb->save.rax, bits);
430+
}
431+
}
432+
433+
return ret;
434+
}

0 commit comments

Comments
 (0)