forked from Minki/linux
- Add support for decoding instructions which do MMIO accesses in order
to use it in SEV and TDX guests - An include fix and reorg to allow for removing set_fs in UML later -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEzv7L6UO9uDPlPSfHEsHwGGHeVUoFAmHcEl4ACgkQEsHwGGHe VUqFTQ/+K9Kb6X0+r7wBSRTeAIWaYewmgOdf+7rpFVyFqQtNecKbuSAWGgFnEHc8 8HUB/krNa+odtx7mAy73wNALUaPmR0KUg6O+YKrvT6LHt8DLlGl5u0g/hihzFdAB PW7auuxqt9TvK1i8PkYAI+W7t93o4mw4LzgDCVvoLPQUutRZEV1gHRht8Tn8SjaN 3EmEiazpFDrXNGWl/3rnS0qIyvtiZu7KNtibE6ljbUgse9cgxOt733mykH6eO9RJ hXOfewKML72UxmgWig01pElgLaXeYI5rpSoG7usm4FwwYh+tmBIA8S/EoeE24gn0 e82lxwRCcHjqUDRp2//gz16sYhs//K6bcViT/4FtnL33e2CjK2/J4MwHPn9zgimO VvxSdAes7UFiA/gDIomFt3gJij+hfy4TGKg5d3326Nm9rsQLpxg49WkozYJZ8m/f 75VVlC4BAj9SnYLQYhSm9buF7pIXmfwN3yWkYJsebl18C6/4FXLLomiqOgWpo3mG D0e+CXhLZsEaU5NTiVuaPySzjtpRUzmfWf3S9GifJZex0rX+et7+mqIuC92aHbtD Dc+nNFX/D77Fq8Uoe8bIEt8QsnjdACov1TI/S8h2rSjt5R/Lyg73qh0CpN0jtQ+S 9dUooJWwE4RXnuVMpFq/Xea/BYj1lQ72kMeyFiCNc0/hnzYhZNM= =NBcE -----END PGP SIGNATURE----- Merge tag 'x86_misc_for_v5.17_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip Pull misc x86 updates from Borislav Petkov: "The pile which we cannot find the proper topic for so we stick it in x86/misc: - Add support for decoding instructions which do MMIO accesses in order to use it in SEV and TDX guests - An include fix and reorg to allow for removing set_fs in UML later" * tag 'x86_misc_for_v5.17_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: x86/mtrr: Remove the mtrr_bp_init() stub x86/sev-es: Use insn_decode_mmio() for MMIO implementation x86/insn-eval: Introduce insn_decode_mmio() x86/insn-eval: Introduce insn_get_modrm_reg_ptr() x86/insn-eval: Handle insn_get_opcode() failure
This commit is contained in:
commit
2e97a0c02b
@ -19,6 +19,7 @@ bool insn_has_rep_prefix(struct insn *insn);
|
||||
void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs);
|
||||
int insn_get_modrm_rm_off(struct insn *insn, struct pt_regs *regs);
|
||||
int insn_get_modrm_reg_off(struct insn *insn, struct pt_regs *regs);
|
||||
unsigned long *insn_get_modrm_reg_ptr(struct insn *insn, struct pt_regs *regs);
|
||||
unsigned long insn_get_seg_base(struct pt_regs *regs, int seg_reg_idx);
|
||||
int insn_get_code_seg_params(struct pt_regs *regs);
|
||||
int insn_get_effective_ip(struct pt_regs *regs, unsigned long *ip);
|
||||
@ -29,4 +30,16 @@ int insn_fetch_from_user_inatomic(struct pt_regs *regs,
|
||||
bool insn_decode_from_regs(struct insn *insn, struct pt_regs *regs,
|
||||
unsigned char buf[MAX_INSN_SIZE], int buf_size);
|
||||
|
||||
enum mmio_type {
|
||||
MMIO_DECODE_FAILED,
|
||||
MMIO_WRITE,
|
||||
MMIO_WRITE_IMM,
|
||||
MMIO_READ,
|
||||
MMIO_READ_ZERO_EXTEND,
|
||||
MMIO_READ_SIGN_EXTEND,
|
||||
MMIO_MOVS,
|
||||
};
|
||||
|
||||
enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes);
|
||||
|
||||
#endif /* _ASM_X86_INSN_EVAL_H */
|
||||
|
@ -24,8 +24,8 @@
|
||||
#define _ASM_X86_MTRR_H
|
||||
|
||||
#include <uapi/asm/mtrr.h>
|
||||
#include <asm/memtype.h>
|
||||
|
||||
void mtrr_bp_init(void);
|
||||
|
||||
/*
|
||||
* The following functions are for use by other drivers that cannot use
|
||||
@ -43,7 +43,6 @@ extern int mtrr_del(int reg, unsigned long base, unsigned long size);
|
||||
extern int mtrr_del_page(int reg, unsigned long base, unsigned long size);
|
||||
extern void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi);
|
||||
extern void mtrr_ap_init(void);
|
||||
extern void mtrr_bp_init(void);
|
||||
extern void set_mtrr_aps_delayed_init(void);
|
||||
extern void mtrr_aps_init(void);
|
||||
extern void mtrr_bp_restore(void);
|
||||
@ -84,11 +83,6 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn)
|
||||
static inline void mtrr_centaur_report_mcr(int mcr, u32 lo, u32 hi)
|
||||
{
|
||||
}
|
||||
static inline void mtrr_bp_init(void)
|
||||
{
|
||||
pat_disable("PAT support disabled because CONFIG_MTRR is disabled in the kernel.");
|
||||
}
|
||||
|
||||
#define mtrr_ap_init() do {} while (0)
|
||||
#define set_mtrr_aps_delayed_init() do {} while (0)
|
||||
#define mtrr_aps_init() do {} while (0)
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include <asm/kasan.h>
|
||||
#include <asm/kaslr.h>
|
||||
#include <asm/mce.h>
|
||||
#include <asm/memtype.h>
|
||||
#include <asm/mtrr.h>
|
||||
#include <asm/realmode.h>
|
||||
#include <asm/olpc_ofw.h>
|
||||
@ -967,7 +968,11 @@ void __init setup_arch(char **cmdline_p)
|
||||
max_pfn = e820__end_of_ram_pfn();
|
||||
|
||||
/* update e820 for memory not covered by WB MTRRs */
|
||||
mtrr_bp_init();
|
||||
if (IS_ENABLED(CONFIG_MTRR))
|
||||
mtrr_bp_init();
|
||||
else
|
||||
pat_disable("PAT support disabled because CONFIG_MTRR is disabled in the kernel.");
|
||||
|
||||
if (mtrr_trim_uncached_memory(max_pfn))
|
||||
max_pfn = e820__end_of_ram_pfn();
|
||||
|
||||
|
@ -792,22 +792,6 @@ static void __init vc_early_forward_exception(struct es_em_ctxt *ctxt)
|
||||
do_early_exception(ctxt->regs, trapnr);
|
||||
}
|
||||
|
||||
static long *vc_insn_get_reg(struct es_em_ctxt *ctxt)
|
||||
{
|
||||
long *reg_array;
|
||||
int offset;
|
||||
|
||||
reg_array = (long *)ctxt->regs;
|
||||
offset = insn_get_modrm_reg_off(&ctxt->insn, ctxt->regs);
|
||||
|
||||
if (offset < 0)
|
||||
return NULL;
|
||||
|
||||
offset /= sizeof(long);
|
||||
|
||||
return reg_array + offset;
|
||||
}
|
||||
|
||||
static long *vc_insn_get_rm(struct es_em_ctxt *ctxt)
|
||||
{
|
||||
long *reg_array;
|
||||
@ -855,76 +839,6 @@ static enum es_result vc_do_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt,
|
||||
return sev_es_ghcb_hv_call(ghcb, true, ctxt, exit_code, exit_info_1, exit_info_2);
|
||||
}
|
||||
|
||||
static enum es_result vc_handle_mmio_twobyte_ops(struct ghcb *ghcb,
|
||||
struct es_em_ctxt *ctxt)
|
||||
{
|
||||
struct insn *insn = &ctxt->insn;
|
||||
unsigned int bytes = 0;
|
||||
enum es_result ret;
|
||||
int sign_byte;
|
||||
long *reg_data;
|
||||
|
||||
switch (insn->opcode.bytes[1]) {
|
||||
/* MMIO Read w/ zero-extension */
|
||||
case 0xb6:
|
||||
bytes = 1;
|
||||
fallthrough;
|
||||
case 0xb7:
|
||||
if (!bytes)
|
||||
bytes = 2;
|
||||
|
||||
ret = vc_do_mmio(ghcb, ctxt, bytes, true);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/* Zero extend based on operand size */
|
||||
reg_data = vc_insn_get_reg(ctxt);
|
||||
if (!reg_data)
|
||||
return ES_DECODE_FAILED;
|
||||
|
||||
memset(reg_data, 0, insn->opnd_bytes);
|
||||
|
||||
memcpy(reg_data, ghcb->shared_buffer, bytes);
|
||||
break;
|
||||
|
||||
/* MMIO Read w/ sign-extension */
|
||||
case 0xbe:
|
||||
bytes = 1;
|
||||
fallthrough;
|
||||
case 0xbf:
|
||||
if (!bytes)
|
||||
bytes = 2;
|
||||
|
||||
ret = vc_do_mmio(ghcb, ctxt, bytes, true);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/* Sign extend based on operand size */
|
||||
reg_data = vc_insn_get_reg(ctxt);
|
||||
if (!reg_data)
|
||||
return ES_DECODE_FAILED;
|
||||
|
||||
if (bytes == 1) {
|
||||
u8 *val = (u8 *)ghcb->shared_buffer;
|
||||
|
||||
sign_byte = (*val & 0x80) ? 0xff : 0x00;
|
||||
} else {
|
||||
u16 *val = (u16 *)ghcb->shared_buffer;
|
||||
|
||||
sign_byte = (*val & 0x8000) ? 0xff : 0x00;
|
||||
}
|
||||
memset(reg_data, sign_byte, insn->opnd_bytes);
|
||||
|
||||
memcpy(reg_data, ghcb->shared_buffer, bytes);
|
||||
break;
|
||||
|
||||
default:
|
||||
ret = ES_UNSUPPORTED;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* The MOVS instruction has two memory operands, which raises the
|
||||
* problem that it is not known whether the access to the source or the
|
||||
@ -992,83 +906,79 @@ static enum es_result vc_handle_mmio_movs(struct es_em_ctxt *ctxt,
|
||||
return ES_RETRY;
|
||||
}
|
||||
|
||||
static enum es_result vc_handle_mmio(struct ghcb *ghcb,
|
||||
struct es_em_ctxt *ctxt)
|
||||
static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
|
||||
{
|
||||
struct insn *insn = &ctxt->insn;
|
||||
unsigned int bytes = 0;
|
||||
enum mmio_type mmio;
|
||||
enum es_result ret;
|
||||
u8 sign_byte;
|
||||
long *reg_data;
|
||||
|
||||
switch (insn->opcode.bytes[0]) {
|
||||
/* MMIO Write */
|
||||
case 0x88:
|
||||
bytes = 1;
|
||||
fallthrough;
|
||||
case 0x89:
|
||||
if (!bytes)
|
||||
bytes = insn->opnd_bytes;
|
||||
mmio = insn_decode_mmio(insn, &bytes);
|
||||
if (mmio == MMIO_DECODE_FAILED)
|
||||
return ES_DECODE_FAILED;
|
||||
|
||||
reg_data = vc_insn_get_reg(ctxt);
|
||||
if (mmio != MMIO_WRITE_IMM && mmio != MMIO_MOVS) {
|
||||
reg_data = insn_get_modrm_reg_ptr(insn, ctxt->regs);
|
||||
if (!reg_data)
|
||||
return ES_DECODE_FAILED;
|
||||
}
|
||||
|
||||
switch (mmio) {
|
||||
case MMIO_WRITE:
|
||||
memcpy(ghcb->shared_buffer, reg_data, bytes);
|
||||
|
||||
ret = vc_do_mmio(ghcb, ctxt, bytes, false);
|
||||
break;
|
||||
|
||||
case 0xc6:
|
||||
bytes = 1;
|
||||
fallthrough;
|
||||
case 0xc7:
|
||||
if (!bytes)
|
||||
bytes = insn->opnd_bytes;
|
||||
|
||||
case MMIO_WRITE_IMM:
|
||||
memcpy(ghcb->shared_buffer, insn->immediate1.bytes, bytes);
|
||||
|
||||
ret = vc_do_mmio(ghcb, ctxt, bytes, false);
|
||||
break;
|
||||
|
||||
/* MMIO Read */
|
||||
case 0x8a:
|
||||
bytes = 1;
|
||||
fallthrough;
|
||||
case 0x8b:
|
||||
if (!bytes)
|
||||
bytes = insn->opnd_bytes;
|
||||
|
||||
case MMIO_READ:
|
||||
ret = vc_do_mmio(ghcb, ctxt, bytes, true);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
reg_data = vc_insn_get_reg(ctxt);
|
||||
if (!reg_data)
|
||||
return ES_DECODE_FAILED;
|
||||
|
||||
/* Zero-extend for 32-bit operation */
|
||||
if (bytes == 4)
|
||||
*reg_data = 0;
|
||||
|
||||
memcpy(reg_data, ghcb->shared_buffer, bytes);
|
||||
break;
|
||||
case MMIO_READ_ZERO_EXTEND:
|
||||
ret = vc_do_mmio(ghcb, ctxt, bytes, true);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
/* MOVS instruction */
|
||||
case 0xa4:
|
||||
bytes = 1;
|
||||
fallthrough;
|
||||
case 0xa5:
|
||||
if (!bytes)
|
||||
bytes = insn->opnd_bytes;
|
||||
|
||||
ret = vc_handle_mmio_movs(ctxt, bytes);
|
||||
/* Zero extend based on operand size */
|
||||
memset(reg_data, 0, insn->opnd_bytes);
|
||||
memcpy(reg_data, ghcb->shared_buffer, bytes);
|
||||
break;
|
||||
/* Two-Byte Opcodes */
|
||||
case 0x0f:
|
||||
ret = vc_handle_mmio_twobyte_ops(ghcb, ctxt);
|
||||
case MMIO_READ_SIGN_EXTEND:
|
||||
ret = vc_do_mmio(ghcb, ctxt, bytes, true);
|
||||
if (ret)
|
||||
break;
|
||||
|
||||
if (bytes == 1) {
|
||||
u8 *val = (u8 *)ghcb->shared_buffer;
|
||||
|
||||
sign_byte = (*val & 0x80) ? 0xff : 0x00;
|
||||
} else {
|
||||
u16 *val = (u16 *)ghcb->shared_buffer;
|
||||
|
||||
sign_byte = (*val & 0x8000) ? 0xff : 0x00;
|
||||
}
|
||||
|
||||
/* Sign extend based on operand size */
|
||||
memset(reg_data, sign_byte, insn->opnd_bytes);
|
||||
memcpy(reg_data, ghcb->shared_buffer, bytes);
|
||||
break;
|
||||
case MMIO_MOVS:
|
||||
ret = vc_handle_mmio_movs(ctxt, bytes);
|
||||
break;
|
||||
default:
|
||||
ret = ES_UNSUPPORTED;
|
||||
break;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
@ -16,6 +16,7 @@
|
||||
#include "spte.h"
|
||||
|
||||
#include <asm/e820/api.h>
|
||||
#include <asm/memtype.h>
|
||||
#include <asm/vmx.h>
|
||||
|
||||
static bool __read_mostly enable_mmio_caching = true;
|
||||
|
@ -37,8 +37,6 @@ enum reg_type {
|
||||
*/
|
||||
static bool is_string_insn(struct insn *insn)
|
||||
{
|
||||
insn_get_opcode(insn);
|
||||
|
||||
/* All string instructions have a 1-byte opcode. */
|
||||
if (insn->opcode.nbytes != 1)
|
||||
return false;
|
||||
@ -850,6 +848,26 @@ int insn_get_modrm_reg_off(struct insn *insn, struct pt_regs *regs)
|
||||
return get_reg_offset(insn, regs, REG_TYPE_REG);
|
||||
}
|
||||
|
||||
/**
|
||||
* insn_get_modrm_reg_ptr() - Obtain register pointer based on ModRM byte
|
||||
* @insn: Instruction containing the ModRM byte
|
||||
* @regs: Register values as seen when entering kernel mode
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* The register indicated by the reg part of the ModRM byte.
|
||||
* The register is obtained as a pointer within pt_regs.
|
||||
*/
|
||||
unsigned long *insn_get_modrm_reg_ptr(struct insn *insn, struct pt_regs *regs)
|
||||
{
|
||||
int offset;
|
||||
|
||||
offset = insn_get_modrm_reg_off(insn, regs);
|
||||
if (offset < 0)
|
||||
return NULL;
|
||||
return (void *)regs + offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* get_seg_base_limit() - obtain base address and limit of a segment
|
||||
* @insn: Instruction. Must be valid.
|
||||
@ -1405,6 +1423,9 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
|
||||
if (!insn || !regs)
|
||||
return (void __user *)-1L;
|
||||
|
||||
if (insn_get_opcode(insn))
|
||||
return (void __user *)-1L;
|
||||
|
||||
switch (insn->addr_bytes) {
|
||||
case 2:
|
||||
return get_addr_ref_16(insn, regs);
|
||||
@ -1539,3 +1560,87 @@ bool insn_decode_from_regs(struct insn *insn, struct pt_regs *regs,
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* insn_decode_mmio() - Decode a MMIO instruction
|
||||
* @insn: Structure to store decoded instruction
|
||||
* @bytes: Returns size of memory operand
|
||||
*
|
||||
* Decodes instruction that used for Memory-mapped I/O.
|
||||
*
|
||||
* Returns:
|
||||
*
|
||||
* Type of the instruction. Size of the memory operand is stored in
|
||||
* @bytes. If decode failed, MMIO_DECODE_FAILED returned.
|
||||
*/
|
||||
enum mmio_type insn_decode_mmio(struct insn *insn, int *bytes)
|
||||
{
|
||||
enum mmio_type type = MMIO_DECODE_FAILED;
|
||||
|
||||
*bytes = 0;
|
||||
|
||||
if (insn_get_opcode(insn))
|
||||
return MMIO_DECODE_FAILED;
|
||||
|
||||
switch (insn->opcode.bytes[0]) {
|
||||
case 0x88: /* MOV m8,r8 */
|
||||
*bytes = 1;
|
||||
fallthrough;
|
||||
case 0x89: /* MOV m16/m32/m64, r16/m32/m64 */
|
||||
if (!*bytes)
|
||||
*bytes = insn->opnd_bytes;
|
||||
type = MMIO_WRITE;
|
||||
break;
|
||||
|
||||
case 0xc6: /* MOV m8, imm8 */
|
||||
*bytes = 1;
|
||||
fallthrough;
|
||||
case 0xc7: /* MOV m16/m32/m64, imm16/imm32/imm64 */
|
||||
if (!*bytes)
|
||||
*bytes = insn->opnd_bytes;
|
||||
type = MMIO_WRITE_IMM;
|
||||
break;
|
||||
|
||||
case 0x8a: /* MOV r8, m8 */
|
||||
*bytes = 1;
|
||||
fallthrough;
|
||||
case 0x8b: /* MOV r16/r32/r64, m16/m32/m64 */
|
||||
if (!*bytes)
|
||||
*bytes = insn->opnd_bytes;
|
||||
type = MMIO_READ;
|
||||
break;
|
||||
|
||||
case 0xa4: /* MOVS m8, m8 */
|
||||
*bytes = 1;
|
||||
fallthrough;
|
||||
case 0xa5: /* MOVS m16/m32/m64, m16/m32/m64 */
|
||||
if (!*bytes)
|
||||
*bytes = insn->opnd_bytes;
|
||||
type = MMIO_MOVS;
|
||||
break;
|
||||
|
||||
case 0x0f: /* Two-byte instruction */
|
||||
switch (insn->opcode.bytes[1]) {
|
||||
case 0xb6: /* MOVZX r16/r32/r64, m8 */
|
||||
*bytes = 1;
|
||||
fallthrough;
|
||||
case 0xb7: /* MOVZX r32/r64, m16 */
|
||||
if (!*bytes)
|
||||
*bytes = 2;
|
||||
type = MMIO_READ_ZERO_EXTEND;
|
||||
break;
|
||||
|
||||
case 0xbe: /* MOVSX r16/r32/r64, m8 */
|
||||
*bytes = 1;
|
||||
fallthrough;
|
||||
case 0xbf: /* MOVSX r32/r64, m16 */
|
||||
if (!*bytes)
|
||||
*bytes = 2;
|
||||
type = MMIO_READ_SIGN_EXTEND;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user