From 23ef731e4365196bfc186358eb4f6265d60ab352 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Tue, 30 Nov 2021 21:49:30 +0300 Subject: x86/insn-eval: Handle insn_get_opcode() failure is_string_insn() calls insn_get_opcode() that can fail, but does not handle the failure. is_string_insn() interface does not allow to communicate an error to the caller. Push insn_get_opcode() to the only non-static user of is_string_insn() and fail it early if insn_get_opcode() fails. [ dhansen: fix tabs-versus-spaces breakage ] Signed-off-by: Kirill A. Shutemov Signed-off-by: Dave Hansen Tested-by: Joerg Roedel Acked-by: Tom Lendacky Link: https://lkml.kernel.org/r/20211130184933.31005-2-kirill.shutemov@linux.intel.com --- arch/x86/lib/insn-eval.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'arch/x86/lib/insn-eval.c') diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c index eb3ccffb9b9d..1c51e8a415e2 100644 --- a/arch/x86/lib/insn-eval.c +++ b/arch/x86/lib/insn-eval.c @@ -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; @@ -1405,6 +1403,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); -- cgit From d5ec1877df6da6a14bbc353aff50689528930dd2 Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Tue, 30 Nov 2021 21:49:31 +0300 Subject: x86/insn-eval: Introduce insn_get_modrm_reg_ptr() The helper returns a pointer to the register indicated by ModRM byte. It's going to replace vc_insn_get_reg() in the SEV MMIO implementation. TDX MMIO implementation will also use it. Signed-off-by: Kirill A. Shutemov Signed-off-by: Dave Hansen Reviewed-by: Andi Kleen Reviewed-by: Tony Luck Tested-by: Joerg Roedel Acked-by: Tom Lendacky Link: https://lkml.kernel.org/r/20211130184933.31005-3-kirill.shutemov@linux.intel.com --- arch/x86/lib/insn-eval.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'arch/x86/lib/insn-eval.c') diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c index 1c51e8a415e2..8df6236e441c 100644 --- a/arch/x86/lib/insn-eval.c +++ b/arch/x86/lib/insn-eval.c @@ -848,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. -- cgit From 70a81f99e45b15b3e77f70720742545e1c7541da Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Tue, 30 Nov 2021 21:49:32 +0300 Subject: x86/insn-eval: Introduce insn_decode_mmio() In preparation for sharing MMIO instruction decode between SEV-ES and TDX, factor out the common decode into a new insn_decode_mmio() helper. For regular virtual machine, MMIO is handled by the VMM and KVM emulates instructions that caused MMIO. But, this model doesn't work for a secure VMs (like SEV or TDX) as VMM doesn't have access to the guest memory and register state. So, for TDX or SEV VMM needs assistance in handling MMIO. It induces exception in the guest. Guest has to decode the instruction and handle it on its own. The code is based on the current SEV MMIO implementation. Signed-off-by: Kirill A. Shutemov Signed-off-by: Dave Hansen Reviewed-by: Andi Kleen Reviewed-by: Tony Luck Tested-by: Joerg Roedel Acked-by: Tom Lendacky Link: https://lkml.kernel.org/r/20211130184933.31005-4-kirill.shutemov@linux.intel.com --- arch/x86/lib/insn-eval.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) (limited to 'arch/x86/lib/insn-eval.c') diff --git a/arch/x86/lib/insn-eval.c b/arch/x86/lib/insn-eval.c index 8df6236e441c..53e57ef5925c 100644 --- a/arch/x86/lib/insn-eval.c +++ b/arch/x86/lib/insn-eval.c @@ -1560,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; +} -- cgit