Commit 0df072ab authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'sev_fixes_for_v6.6' of //git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull x86 fixes from Borislav Petkov:
 "Take care of a race between when the #VC exception is raised and when
  the guest kernel gets to emulate certain instructions in SEV-{ES,SNP}
  guests by:

   - disabling emulation of MMIO instructions when coming from user mode

   - checking the IO permission bitmap before emulating IO instructions
     and verifying the memory operands of INS/OUTS insns"

* tag 'sev_fixes_for_v6.6' of //git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
  x86/sev: Check for user-space IOIO pointing to kernel space
  x86/sev: Check IOBM for IOIO exceptions from user-space
  x86/sev: Disable MMIO emulation from user mode
parents ce55c22e 63e44bc5
...@@ -103,6 +103,16 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt, ...@@ -103,6 +103,16 @@ 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;
}
static bool fault_in_kernel_space(unsigned long address)
{
return false;
}
#undef __init #undef __init
#define __init #define __init
......
...@@ -632,6 +632,23 @@ void __init do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code) ...@@ -632,6 +632,23 @@ void __init do_vc_no_ghcb(struct pt_regs *regs, unsigned long exit_code)
sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ); sev_es_terminate(SEV_TERM_SET_GEN, GHCB_SEV_ES_GEN_REQ);
} }
static enum es_result vc_insn_string_check(struct es_em_ctxt *ctxt,
unsigned long address,
bool write)
{
if (user_mode(ctxt->regs) && fault_in_kernel_space(address)) {
ctxt->fi.vector = X86_TRAP_PF;
ctxt->fi.error_code = X86_PF_USER;
ctxt->fi.cr2 = address;
if (write)
ctxt->fi.error_code |= X86_PF_WRITE;
return ES_EXCEPTION;
}
return ES_OK;
}
static enum es_result vc_insn_string_read(struct es_em_ctxt *ctxt, static enum es_result vc_insn_string_read(struct es_em_ctxt *ctxt,
void *src, char *buf, void *src, char *buf,
unsigned int data_size, unsigned int data_size,
...@@ -639,7 +656,12 @@ static enum es_result vc_insn_string_read(struct es_em_ctxt *ctxt, ...@@ -639,7 +656,12 @@ static enum es_result vc_insn_string_read(struct es_em_ctxt *ctxt,
bool backwards) bool backwards)
{ {
int i, b = backwards ? -1 : 1; int i, b = backwards ? -1 : 1;
enum es_result ret = ES_OK; unsigned long address = (unsigned long)src;
enum es_result ret;
ret = vc_insn_string_check(ctxt, address, false);
if (ret != ES_OK)
return ret;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
void *s = src + (i * data_size * b); void *s = src + (i * data_size * b);
...@@ -660,7 +682,12 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt, ...@@ -660,7 +682,12 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt,
bool backwards) bool backwards)
{ {
int i, s = backwards ? -1 : 1; int i, s = backwards ? -1 : 1;
enum es_result ret = ES_OK; unsigned long address = (unsigned long)dst;
enum es_result ret;
ret = vc_insn_string_check(ctxt, address, true);
if (ret != ES_OK)
return ret;
for (i = 0; i < count; i++) { for (i = 0; i < count; i++) {
void *d = dst + (i * data_size * s); void *d = dst + (i * data_size * s);
...@@ -696,6 +723,9 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt, ...@@ -696,6 +723,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]) {
...@@ -704,7 +734,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) ...@@ -704,7 +734,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 */
...@@ -712,41 +742,43 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) ...@@ -712,41 +742,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:
...@@ -756,12 +788,15 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) ...@@ -756,12 +788,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;
...@@ -777,7 +812,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) ...@@ -777,7 +812,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"
...@@ -1508,6 +1535,9 @@ static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt) ...@@ -1508,6 +1535,9 @@ static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
return ES_DECODE_FAILED; return ES_DECODE_FAILED;
} }
if (user_mode(ctxt->regs))
return ES_UNSUPPORTED;
switch (mmio) { switch (mmio) {
case INSN_MMIO_WRITE: case INSN_MMIO_WRITE:
memcpy(ghcb->shared_buffer, reg_data, bytes); memcpy(ghcb->shared_buffer, reg_data, bytes);
......
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