Commit b9cb9c45 authored by Joerg Roedel's avatar Joerg Roedel Committed by Borislav Petkov (AMD)

x86/sev: Check IOBM for IOIO exceptions from user-space

Check the IO permission bitmap (if present) before emulating IOIO #VC
exceptions for user-space. These permissions are checked by hardware
already before the #VC is raised, but due to the VC-handler decoding
race it needs to be checked again in software.

Fixes: 25189d08 ("x86/sev-es: Add support for handling IOIO exceptions")
Reported-by: default avatarTom Dohrmann <erbse.13@gmx.de>
Signed-off-by: default avatarJoerg Roedel <jroedel@suse.de>
Signed-off-by: default avatarBorislav Petkov (AMD) <bp@alien8.de>
Tested-by: default avatarTom Dohrmann <erbse.13@gmx.de>
Cc: <stable@kernel.org>
parent a37cd2a5
...@@ -103,6 +103,11 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt, ...@@ -103,6 +103,11 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
return ES_OK; return ES_OK;
} }
static enum es_result vc_ioio_check(struct es_em_ctxt *ctxt, u16 port, size_t size)
{
return ES_OK;
}
#undef __init #undef __init
#define __init #define __init
......
...@@ -656,6 +656,9 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt, ...@@ -656,6 +656,9 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt,
static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
{ {
struct insn *insn = &ctxt->insn; struct insn *insn = &ctxt->insn;
size_t size;
u64 port;
*exitinfo = 0; *exitinfo = 0;
switch (insn->opcode.bytes[0]) { switch (insn->opcode.bytes[0]) {
...@@ -664,7 +667,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) ...@@ -664,7 +667,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
case 0x6d: case 0x6d:
*exitinfo |= IOIO_TYPE_INS; *exitinfo |= IOIO_TYPE_INS;
*exitinfo |= IOIO_SEG_ES; *exitinfo |= IOIO_SEG_ES;
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16; port = ctxt->regs->dx & 0xffff;
break; break;
/* OUTS opcodes */ /* OUTS opcodes */
...@@ -672,41 +675,43 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) ...@@ -672,41 +675,43 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
case 0x6f: case 0x6f:
*exitinfo |= IOIO_TYPE_OUTS; *exitinfo |= IOIO_TYPE_OUTS;
*exitinfo |= IOIO_SEG_DS; *exitinfo |= IOIO_SEG_DS;
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16; port = ctxt->regs->dx & 0xffff;
break; break;
/* IN immediate opcodes */ /* IN immediate opcodes */
case 0xe4: case 0xe4:
case 0xe5: case 0xe5:
*exitinfo |= IOIO_TYPE_IN; *exitinfo |= IOIO_TYPE_IN;
*exitinfo |= (u8)insn->immediate.value << 16; port = (u8)insn->immediate.value & 0xffff;
break; break;
/* OUT immediate opcodes */ /* OUT immediate opcodes */
case 0xe6: case 0xe6:
case 0xe7: case 0xe7:
*exitinfo |= IOIO_TYPE_OUT; *exitinfo |= IOIO_TYPE_OUT;
*exitinfo |= (u8)insn->immediate.value << 16; port = (u8)insn->immediate.value & 0xffff;
break; break;
/* IN register opcodes */ /* IN register opcodes */
case 0xec: case 0xec:
case 0xed: case 0xed:
*exitinfo |= IOIO_TYPE_IN; *exitinfo |= IOIO_TYPE_IN;
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16; port = ctxt->regs->dx & 0xffff;
break; break;
/* OUT register opcodes */ /* OUT register opcodes */
case 0xee: case 0xee:
case 0xef: case 0xef:
*exitinfo |= IOIO_TYPE_OUT; *exitinfo |= IOIO_TYPE_OUT;
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16; port = ctxt->regs->dx & 0xffff;
break; break;
default: default:
return ES_DECODE_FAILED; return ES_DECODE_FAILED;
} }
*exitinfo |= port << 16;
switch (insn->opcode.bytes[0]) { switch (insn->opcode.bytes[0]) {
case 0x6c: case 0x6c:
case 0x6e: case 0x6e:
...@@ -716,12 +721,15 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) ...@@ -716,12 +721,15 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
case 0xee: case 0xee:
/* Single byte opcodes */ /* Single byte opcodes */
*exitinfo |= IOIO_DATA_8; *exitinfo |= IOIO_DATA_8;
size = 1;
break; break;
default: default:
/* Length determined by instruction parsing */ /* Length determined by instruction parsing */
*exitinfo |= (insn->opnd_bytes == 2) ? IOIO_DATA_16 *exitinfo |= (insn->opnd_bytes == 2) ? IOIO_DATA_16
: IOIO_DATA_32; : IOIO_DATA_32;
size = (insn->opnd_bytes == 2) ? 2 : 4;
} }
switch (insn->addr_bytes) { switch (insn->addr_bytes) {
case 2: case 2:
*exitinfo |= IOIO_ADDR_16; *exitinfo |= IOIO_ADDR_16;
...@@ -737,7 +745,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) ...@@ -737,7 +745,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
if (insn_has_rep_prefix(insn)) if (insn_has_rep_prefix(insn))
*exitinfo |= IOIO_REP; *exitinfo |= IOIO_REP;
return ES_OK; return vc_ioio_check(ctxt, (u16)port, size);
} }
static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt) static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
......
...@@ -524,6 +524,33 @@ static enum es_result vc_slow_virt_to_phys(struct ghcb *ghcb, struct es_em_ctxt ...@@ -524,6 +524,33 @@ static enum es_result vc_slow_virt_to_phys(struct ghcb *ghcb, struct es_em_ctxt
return ES_OK; return ES_OK;
} }
static enum es_result vc_ioio_check(struct es_em_ctxt *ctxt, u16 port, size_t size)
{
BUG_ON(size > 4);
if (user_mode(ctxt->regs)) {
struct thread_struct *t = &current->thread;
struct io_bitmap *iobm = t->io_bitmap;
size_t idx;
if (!iobm)
goto fault;
for (idx = port; idx < port + size; ++idx) {
if (test_bit(idx, iobm->bitmap))
goto fault;
}
}
return ES_OK;
fault:
ctxt->fi.vector = X86_TRAP_GP;
ctxt->fi.error_code = 0;
return ES_EXCEPTION;
}
/* Include code shared with pre-decompression boot stage */ /* Include code shared with pre-decompression boot stage */
#include "sev-shared.c" #include "sev-shared.c"
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment