Commit 2e97a0c0 authored by Linus Torvalds's avatar Linus Torvalds

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
parents 4a692ae3 4d5cff69
...@@ -19,6 +19,7 @@ bool insn_has_rep_prefix(struct insn *insn); ...@@ -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); 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_rm_off(struct insn *insn, struct pt_regs *regs);
int insn_get_modrm_reg_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); 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_code_seg_params(struct pt_regs *regs);
int insn_get_effective_ip(struct pt_regs *regs, unsigned long *ip); 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, ...@@ -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, bool insn_decode_from_regs(struct insn *insn, struct pt_regs *regs,
unsigned char buf[MAX_INSN_SIZE], int buf_size); 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 */ #endif /* _ASM_X86_INSN_EVAL_H */
...@@ -24,8 +24,8 @@ ...@@ -24,8 +24,8 @@
#define _ASM_X86_MTRR_H #define _ASM_X86_MTRR_H
#include <uapi/asm/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 * 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); ...@@ -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 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_centaur_report_mcr(int mcr, u32 lo, u32 hi);
extern void mtrr_ap_init(void); extern void mtrr_ap_init(void);
extern void mtrr_bp_init(void);
extern void set_mtrr_aps_delayed_init(void); extern void set_mtrr_aps_delayed_init(void);
extern void mtrr_aps_init(void); extern void mtrr_aps_init(void);
extern void mtrr_bp_restore(void); extern void mtrr_bp_restore(void);
...@@ -84,11 +83,6 @@ static inline int mtrr_trim_uncached_memory(unsigned long end_pfn) ...@@ -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_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 mtrr_ap_init() do {} while (0)
#define set_mtrr_aps_delayed_init() do {} while (0) #define set_mtrr_aps_delayed_init() do {} while (0)
#define mtrr_aps_init() do {} while (0) #define mtrr_aps_init() do {} while (0)
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include <asm/kasan.h> #include <asm/kasan.h>
#include <asm/kaslr.h> #include <asm/kaslr.h>
#include <asm/mce.h> #include <asm/mce.h>
#include <asm/memtype.h>
#include <asm/mtrr.h> #include <asm/mtrr.h>
#include <asm/realmode.h> #include <asm/realmode.h>
#include <asm/olpc_ofw.h> #include <asm/olpc_ofw.h>
...@@ -967,7 +968,11 @@ void __init setup_arch(char **cmdline_p) ...@@ -967,7 +968,11 @@ void __init setup_arch(char **cmdline_p)
max_pfn = e820__end_of_ram_pfn(); max_pfn = e820__end_of_ram_pfn();
/* update e820 for memory not covered by WB MTRRs */ /* 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)) if (mtrr_trim_uncached_memory(max_pfn))
max_pfn = e820__end_of_ram_pfn(); max_pfn = e820__end_of_ram_pfn();
......
...@@ -792,22 +792,6 @@ static void __init vc_early_forward_exception(struct es_em_ctxt *ctxt) ...@@ -792,22 +792,6 @@ static void __init vc_early_forward_exception(struct es_em_ctxt *ctxt)
do_early_exception(ctxt->regs, trapnr); 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) static long *vc_insn_get_rm(struct es_em_ctxt *ctxt)
{ {
long *reg_array; long *reg_array;
...@@ -855,76 +839,6 @@ static enum es_result vc_do_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt, ...@@ -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); 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 * The MOVS instruction has two memory operands, which raises the
* problem that it is not known whether the access to the source or 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, ...@@ -992,83 +906,79 @@ static enum es_result vc_handle_mmio_movs(struct es_em_ctxt *ctxt,
return ES_RETRY; return ES_RETRY;
} }
static enum es_result vc_handle_mmio(struct ghcb *ghcb, static enum es_result vc_handle_mmio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
struct es_em_ctxt *ctxt)
{ {
struct insn *insn = &ctxt->insn; struct insn *insn = &ctxt->insn;
unsigned int bytes = 0; unsigned int bytes = 0;
enum mmio_type mmio;
enum es_result ret; enum es_result ret;
u8 sign_byte;
long *reg_data; long *reg_data;
switch (insn->opcode.bytes[0]) { mmio = insn_decode_mmio(insn, &bytes);
/* MMIO Write */ if (mmio == MMIO_DECODE_FAILED)
case 0x88: return ES_DECODE_FAILED;
bytes = 1;
fallthrough;
case 0x89:
if (!bytes)
bytes = insn->opnd_bytes;
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) if (!reg_data)
return ES_DECODE_FAILED; return ES_DECODE_FAILED;
}
switch (mmio) {
case MMIO_WRITE:
memcpy(ghcb->shared_buffer, reg_data, bytes); memcpy(ghcb->shared_buffer, reg_data, bytes);
ret = vc_do_mmio(ghcb, ctxt, bytes, false); ret = vc_do_mmio(ghcb, ctxt, bytes, false);
break; break;
case MMIO_WRITE_IMM:
case 0xc6:
bytes = 1;
fallthrough;
case 0xc7:
if (!bytes)
bytes = insn->opnd_bytes;
memcpy(ghcb->shared_buffer, insn->immediate1.bytes, bytes); memcpy(ghcb->shared_buffer, insn->immediate1.bytes, bytes);
ret = vc_do_mmio(ghcb, ctxt, bytes, false); ret = vc_do_mmio(ghcb, ctxt, bytes, false);
break; break;
case MMIO_READ:
/* MMIO Read */
case 0x8a:
bytes = 1;
fallthrough;
case 0x8b:
if (!bytes)
bytes = insn->opnd_bytes;
ret = vc_do_mmio(ghcb, ctxt, bytes, true); ret = vc_do_mmio(ghcb, ctxt, bytes, true);
if (ret) if (ret)
break; break;
reg_data = vc_insn_get_reg(ctxt);
if (!reg_data)
return ES_DECODE_FAILED;
/* Zero-extend for 32-bit operation */ /* Zero-extend for 32-bit operation */
if (bytes == 4) if (bytes == 4)
*reg_data = 0; *reg_data = 0;
memcpy(reg_data, ghcb->shared_buffer, bytes); memcpy(reg_data, ghcb->shared_buffer, bytes);
break; break;
case MMIO_READ_ZERO_EXTEND:
ret = vc_do_mmio(ghcb, ctxt, bytes, true);
if (ret)
break;
/* 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;
/* MOVS instruction */ if (bytes == 1) {
case 0xa4: u8 *val = (u8 *)ghcb->shared_buffer;
bytes = 1;
fallthrough;
case 0xa5:
if (!bytes)
bytes = insn->opnd_bytes;
ret = vc_handle_mmio_movs(ctxt, bytes); 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; break;
/* Two-Byte Opcodes */ case MMIO_MOVS:
case 0x0f: ret = vc_handle_mmio_movs(ctxt, bytes);
ret = vc_handle_mmio_twobyte_ops(ghcb, ctxt);
break; break;
default: default:
ret = ES_UNSUPPORTED; ret = ES_UNSUPPORTED;
break;
} }
return ret; return ret;
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "spte.h" #include "spte.h"
#include <asm/e820/api.h> #include <asm/e820/api.h>
#include <asm/memtype.h>
#include <asm/vmx.h> #include <asm/vmx.h>
static bool __read_mostly enable_mmio_caching = true; static bool __read_mostly enable_mmio_caching = true;
......
...@@ -37,8 +37,6 @@ enum reg_type { ...@@ -37,8 +37,6 @@ enum reg_type {
*/ */
static bool is_string_insn(struct insn *insn) static bool is_string_insn(struct insn *insn)
{ {
insn_get_opcode(insn);
/* All string instructions have a 1-byte opcode. */ /* All string instructions have a 1-byte opcode. */
if (insn->opcode.nbytes != 1) if (insn->opcode.nbytes != 1)
return false; return false;
...@@ -850,6 +848,26 @@ int insn_get_modrm_reg_off(struct insn *insn, struct pt_regs *regs) ...@@ -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); 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 * get_seg_base_limit() - obtain base address and limit of a segment
* @insn: Instruction. Must be valid. * @insn: Instruction. Must be valid.
...@@ -1405,6 +1423,9 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs) ...@@ -1405,6 +1423,9 @@ void __user *insn_get_addr_ref(struct insn *insn, struct pt_regs *regs)
if (!insn || !regs) if (!insn || !regs)
return (void __user *)-1L; return (void __user *)-1L;
if (insn_get_opcode(insn))
return (void __user *)-1L;
switch (insn->addr_bytes) { switch (insn->addr_bytes) {
case 2: case 2:
return get_addr_ref_16(insn, regs); return get_addr_ref_16(insn, regs);
...@@ -1539,3 +1560,87 @@ bool insn_decode_from_regs(struct insn *insn, struct pt_regs *regs, ...@@ -1539,3 +1560,87 @@ bool insn_decode_from_regs(struct insn *insn, struct pt_regs *regs,
return true; 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;
}
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