- 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:
Linus Torvalds 2022-01-10 10:00:03 -08:00
commit 2e97a0c02b
6 changed files with 170 additions and 142 deletions

View File

@ -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 */

View File

@ -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)

View File

@ -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 */
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();

View File

@ -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;
reg_data = vc_insn_get_reg(ctxt);
if (!reg_data)
mmio = insn_decode_mmio(insn, &bytes);
if (mmio == MMIO_DECODE_FAILED)
return ES_DECODE_FAILED;
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;
/* MOVS instruction */
case 0xa4:
bytes = 1;
fallthrough;
case 0xa5:
if (!bytes)
bytes = insn->opnd_bytes;
ret = vc_handle_mmio_movs(ctxt, bytes);
case MMIO_READ_ZERO_EXTEND:
ret = vc_do_mmio(ghcb, ctxt, bytes, true);
if (ret)
break;
/* Two-Byte Opcodes */
case 0x0f:
ret = vc_handle_mmio_twobyte_ops(ghcb, ctxt);
/* Zero extend based on operand size */
memset(reg_data, 0, insn->opnd_bytes);
memcpy(reg_data, ghcb->shared_buffer, bytes);
break;
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;

View File

@ -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;

View File

@ -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;
}