mirror of
https://github.com/lkl/linux.git
synced 2025-12-18 23:53:03 +09:00
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
This commit is contained in:
@@ -103,6 +103,16 @@ static enum es_result vc_read_mem(struct es_em_ctxt *ctxt,
|
||||
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
|
||||
#define __init
|
||||
|
||||
|
||||
@@ -632,6 +632,23 @@ fail:
|
||||
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,
|
||||
void *src, char *buf,
|
||||
unsigned int data_size,
|
||||
@@ -639,7 +656,12 @@ static enum es_result vc_insn_string_read(struct es_em_ctxt *ctxt,
|
||||
bool backwards)
|
||||
{
|
||||
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++) {
|
||||
void *s = src + (i * data_size * b);
|
||||
@@ -660,7 +682,12 @@ static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt,
|
||||
bool backwards)
|
||||
{
|
||||
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++) {
|
||||
void *d = dst + (i * data_size * s);
|
||||
@@ -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)
|
||||
{
|
||||
struct insn *insn = &ctxt->insn;
|
||||
size_t size;
|
||||
u64 port;
|
||||
|
||||
*exitinfo = 0;
|
||||
|
||||
switch (insn->opcode.bytes[0]) {
|
||||
@@ -704,7 +734,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
|
||||
case 0x6d:
|
||||
*exitinfo |= IOIO_TYPE_INS;
|
||||
*exitinfo |= IOIO_SEG_ES;
|
||||
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
|
||||
port = ctxt->regs->dx & 0xffff;
|
||||
break;
|
||||
|
||||
/* OUTS opcodes */
|
||||
@@ -712,41 +742,43 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
|
||||
case 0x6f:
|
||||
*exitinfo |= IOIO_TYPE_OUTS;
|
||||
*exitinfo |= IOIO_SEG_DS;
|
||||
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
|
||||
port = ctxt->regs->dx & 0xffff;
|
||||
break;
|
||||
|
||||
/* IN immediate opcodes */
|
||||
case 0xe4:
|
||||
case 0xe5:
|
||||
*exitinfo |= IOIO_TYPE_IN;
|
||||
*exitinfo |= (u8)insn->immediate.value << 16;
|
||||
port = (u8)insn->immediate.value & 0xffff;
|
||||
break;
|
||||
|
||||
/* OUT immediate opcodes */
|
||||
case 0xe6:
|
||||
case 0xe7:
|
||||
*exitinfo |= IOIO_TYPE_OUT;
|
||||
*exitinfo |= (u8)insn->immediate.value << 16;
|
||||
port = (u8)insn->immediate.value & 0xffff;
|
||||
break;
|
||||
|
||||
/* IN register opcodes */
|
||||
case 0xec:
|
||||
case 0xed:
|
||||
*exitinfo |= IOIO_TYPE_IN;
|
||||
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
|
||||
port = ctxt->regs->dx & 0xffff;
|
||||
break;
|
||||
|
||||
/* OUT register opcodes */
|
||||
case 0xee:
|
||||
case 0xef:
|
||||
*exitinfo |= IOIO_TYPE_OUT;
|
||||
*exitinfo |= (ctxt->regs->dx & 0xffff) << 16;
|
||||
port = ctxt->regs->dx & 0xffff;
|
||||
break;
|
||||
|
||||
default:
|
||||
return ES_DECODE_FAILED;
|
||||
}
|
||||
|
||||
*exitinfo |= port << 16;
|
||||
|
||||
switch (insn->opcode.bytes[0]) {
|
||||
case 0x6c:
|
||||
case 0x6e:
|
||||
@@ -756,12 +788,15 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
|
||||
case 0xee:
|
||||
/* Single byte opcodes */
|
||||
*exitinfo |= IOIO_DATA_8;
|
||||
size = 1;
|
||||
break;
|
||||
default:
|
||||
/* Length determined by instruction parsing */
|
||||
*exitinfo |= (insn->opnd_bytes == 2) ? IOIO_DATA_16
|
||||
: IOIO_DATA_32;
|
||||
size = (insn->opnd_bytes == 2) ? 2 : 4;
|
||||
}
|
||||
|
||||
switch (insn->addr_bytes) {
|
||||
case 2:
|
||||
*exitinfo |= IOIO_ADDR_16;
|
||||
@@ -777,7 +812,7 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
|
||||
if (insn_has_rep_prefix(insn))
|
||||
*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)
|
||||
|
||||
@@ -524,6 +524,33 @@ static enum es_result vc_slow_virt_to_phys(struct ghcb *ghcb, struct es_em_ctxt
|
||||
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 = ¤t->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 "sev-shared.c"
|
||||
|
||||
@@ -1508,6 +1535,9 @@ static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
|
||||
return ES_DECODE_FAILED;
|
||||
}
|
||||
|
||||
if (user_mode(ctxt->regs))
|
||||
return ES_UNSUPPORTED;
|
||||
|
||||
switch (mmio) {
|
||||
case INSN_MMIO_WRITE:
|
||||
memcpy(ghcb->shared_buffer, reg_data, bytes);
|
||||
|
||||
Reference in New Issue
Block a user