diff mbox series

[kvm-unit-tests,v8,10/12] x86: AMD SEV-ES: Handle IOIO #VC

Message ID 20240612144539.16147-11-vsntk18@gmail.com (mailing list archive)
State New, archived
Headers show
Series Add #VC exception handling for AMD SEV-ES | expand

Commit Message

Vasant Karasulli June 12, 2024, 2:45 p.m. UTC
From: Vasant Karasulli <vkarasulli@suse.de>

Using Linux's IOIO #VC processing logic.

Signed-off-by: Vasant Karasulli <vkarasulli@suse.de>
---
 lib/x86/amd_sev_vc.c | 150 +++++++++++++++++++++++++++++++++++++++++++
 lib/x86/processor.h  |   7 ++
 lib/x86/svm.h        |  19 ++++++
 3 files changed, 176 insertions(+)

--
2.34.1
diff mbox series

Patch

diff --git a/lib/x86/amd_sev_vc.c b/lib/x86/amd_sev_vc.c
index 72253817..0cdb9c06 100644
--- a/lib/x86/amd_sev_vc.c
+++ b/lib/x86/amd_sev_vc.c
@@ -165,6 +165,153 @@  static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
 	return ret;
 }

+/**
+ * insn_has_rep_prefix() - Determine if instruction has a REP prefix
+ * @insn:       Instruction containing the prefix to inspect
+ *
+ * Returns:
+ *
+ * 1 if the instruction has a REP prefix, 0 if not.
+ */
+static int insn_has_rep_prefix(struct insn *insn)
+{
+	insn_byte_t p;
+	int i;
+
+	insn_get_prefixes(insn);
+
+	for_each_insn_prefix(insn, i, p) {
+		if (p == 0xf2 || p == 0xf3)
+			return 1;
+	}
+
+	return 0;
+}
+
+static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo)
+{
+	struct insn *insn = &ctxt->insn;
+	*exitinfo = 0;
+
+	switch (insn->opcode.bytes[0]) {
+	/* INS opcodes */
+	case 0x6c:
+	case 0x6d:
+		*exitinfo |= SVM_IOIO_TYPE_INS;
+		*exitinfo |= SVM_IOIO_SEG_ES;
+		*exitinfo |= (ctxt->regs->rdx & 0xffff) << 16;
+		break;
+
+	/* OUTS opcodes */
+	case 0x6e:
+	case 0x6f:
+		*exitinfo |= SVM_IOIO_TYPE_OUTS;
+		*exitinfo |= SVM_IOIO_SEG_DS;
+		*exitinfo |= (ctxt->regs->rdx & 0xffff) << 16;
+		break;
+
+	/* IN immediate opcodes */
+	case 0xe4:
+	case 0xe5:
+		*exitinfo |= SVM_IOIO_TYPE_IN;
+		*exitinfo |= (u8)insn->immediate.value << 16;
+		break;
+
+	/* OUT immediate opcodes */
+	case 0xe6:
+	case 0xe7:
+		*exitinfo |= SVM_IOIO_TYPE_OUT;
+		*exitinfo |= (u8)insn->immediate.value << 16;
+		break;
+
+	/* IN register opcodes */
+	case 0xec:
+	case 0xed:
+		*exitinfo |= SVM_IOIO_TYPE_IN;
+		*exitinfo |= (ctxt->regs->rdx & 0xffff) << 16;
+		break;
+
+	/* OUT register opcodes */
+	case 0xee:
+	case 0xef:
+		*exitinfo |= SVM_IOIO_TYPE_OUT;
+		*exitinfo |= (ctxt->regs->rdx & 0xffff) << 16;
+		break;
+
+	default:
+		return ES_DECODE_FAILED;
+	}
+
+	switch (insn->opcode.bytes[0]) {
+	case 0x6c:
+	case 0x6e:
+	case 0xe4:
+	case 0xe6:
+	case 0xec:
+	case 0xee:
+		/* Single byte opcodes */
+		*exitinfo |= SVM_IOIO_DATA_8;
+		break;
+	default:
+		/* Length determined by instruction parsing */
+		*exitinfo |= (insn->opnd_bytes == 2) ? SVM_IOIO_DATA_16
+						     : SVM_IOIO_DATA_32;
+	}
+	switch (insn->addr_bytes) {
+	case 2:
+		*exitinfo |= SVM_IOIO_ADDR_16;
+		break;
+	case 4:
+		*exitinfo |= SVM_IOIO_ADDR_32;
+		break;
+	case 8:
+		*exitinfo |= SVM_IOIO_ADDR_64;
+		break;
+	}
+
+	if (insn_has_rep_prefix(insn))
+		*exitinfo |= SVM_IOIO_REP;
+
+	return ES_OK;
+}
+
+static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
+{
+	struct ex_regs *regs = ctxt->regs;
+	u64 exit_info_1;
+	enum es_result ret;
+
+	ret = vc_ioio_exitinfo(ctxt, &exit_info_1);
+	if (ret != ES_OK)
+		return ret;
+
+	if (exit_info_1 & SVM_IOIO_TYPE_STR) {
+		ret = ES_VMM_ERROR;
+	} else {
+		/* IN/OUT into/from rAX */
+
+		int bits = (exit_info_1 & 0x70) >> 1;
+		u64 rax = 0;
+
+		if (!(exit_info_1 & SVM_IOIO_TYPE_IN))
+			rax = lower_bits(regs->rax, bits);
+
+		ghcb_set_rax(ghcb, rax);
+
+		ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_IOIO, exit_info_1, 0);
+		if (ret != ES_OK)
+			return ret;
+
+		if (exit_info_1 & SVM_IOIO_TYPE_IN) {
+			if (!ghcb_rax_is_valid(ghcb))
+				return ES_VMM_ERROR;
+			regs->rax = lower_bits(ghcb->save.rax, bits);
+		}
+	}
+
+	return ret;
+}
+
 static enum es_result vc_handle_exitcode(struct es_em_ctxt *ctxt,
 					 struct ghcb *ghcb,
 					 unsigned long exit_code)
@@ -178,6 +325,9 @@  static enum es_result vc_handle_exitcode(struct es_em_ctxt *ctxt,
 	case SVM_EXIT_MSR:
 		result = vc_handle_msr(ghcb, ctxt);
 		break;
+	case SVM_EXIT_IOIO:
+		result = vc_handle_ioio(ghcb, ctxt);
+		break;
 	default:
 		/*
 		 * Unexpected #VC exception
diff --git a/lib/x86/processor.h b/lib/x86/processor.h
index e85f9e0e..a7dd3de3 100644
--- a/lib/x86/processor.h
+++ b/lib/x86/processor.h
@@ -853,6 +853,13 @@  static inline int test_bit(int nr, const volatile unsigned long *addr)
 	return (*word & mask) != 0;
 }

+static inline u64 lower_bits(u64 val, unsigned int bits)
+{
+	u64 mask = (1ULL << bits) - 1;
+
+	return (val & mask);
+}
+
 static inline void flush_tlb(void)
 {
 	ulong cr4;
diff --git a/lib/x86/svm.h b/lib/x86/svm.h
index 01404010..96e17dc3 100644
--- a/lib/x86/svm.h
+++ b/lib/x86/svm.h
@@ -151,6 +151,25 @@  struct __attribute__ ((__packed__)) vmcb_control_area {
 #define SVM_IOIO_SIZE_MASK (7 << SVM_IOIO_SIZE_SHIFT)
 #define SVM_IOIO_ASIZE_MASK (7 << SVM_IOIO_ASIZE_SHIFT)

+#define SVM_IOIO_TYPE_STR  BIT(2)
+#define SVM_IOIO_TYPE_IN   1
+#define SVM_IOIO_TYPE_INS  (SVM_IOIO_TYPE_IN | SVM_IOIO_TYPE_STR)
+#define SVM_IOIO_TYPE_OUT  0
+#define SVM_IOIO_TYPE_OUTS (SVM_IOIO_TYPE_OUT | SVM_IOIO_TYPE_STR)
+
+#define SVM_IOIO_REP       BIT(3)
+
+#define SVM_IOIO_ADDR_64   BIT(9)
+#define SVM_IOIO_ADDR_32   BIT(8)
+#define SVM_IOIO_ADDR_16   BIT(7)
+
+#define SVM_IOIO_DATA_32   BIT(6)
+#define SVM_IOIO_DATA_16   BIT(5)
+#define SVM_IOIO_DATA_8    BIT(4)
+
+#define SVM_IOIO_SEG_ES    (0 << 10)
+#define SVM_IOIO_SEG_DS    (3 << 10)
+
 #define SVM_VM_CR_VALID_MASK	0x001fULL
 #define SVM_VM_CR_SVM_LOCK_MASK 0x0008ULL
 #define SVM_VM_CR_SVM_DIS_MASK  0x0010ULL