diff mbox series

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

Message ID 20220209164420.8894-10-varad.gautam@suse.com (mailing list archive)
State New, archived
Headers show
Series Add #VC exception handling for AMD SEV-ES | expand

Commit Message

Varad Gautam Feb. 9, 2022, 4:44 p.m. UTC
Using Linux's IOIO #VC processing logic.

Signed-off-by: Varad Gautam <varad.gautam@suse.com>
---
 lib/x86/amd_sev_vc.c | 146 +++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 146 insertions(+)

Comments

Marc Orr Feb. 12, 2022, 11:03 p.m. UTC | #1
On Wed, Feb 9, 2022 at 8:44 AM Varad Gautam <varad.gautam@suse.com> wrote:
>
> Using Linux's IOIO #VC processing logic.
>
> Signed-off-by: Varad Gautam <varad.gautam@suse.com>
> ---
>  lib/x86/amd_sev_vc.c | 146 +++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 146 insertions(+)
>
> diff --git a/lib/x86/amd_sev_vc.c b/lib/x86/amd_sev_vc.c
> index 401cb29..88c95e1 100644
> --- a/lib/x86/amd_sev_vc.c
> +++ b/lib/x86/amd_sev_vc.c
> @@ -172,6 +172,149 @@ static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
>         return ret;
>  }
>
> +#define IOIO_TYPE_STR  BIT(2)
> +#define IOIO_TYPE_IN   1
> +#define IOIO_TYPE_INS  (IOIO_TYPE_IN | IOIO_TYPE_STR)
> +#define IOIO_TYPE_OUT  0
> +#define IOIO_TYPE_OUTS (IOIO_TYPE_OUT | IOIO_TYPE_STR)
> +
> +#define IOIO_REP       BIT(3)
> +
> +#define IOIO_ADDR_64   BIT(9)
> +#define IOIO_ADDR_32   BIT(8)
> +#define IOIO_ADDR_16   BIT(7)
> +
> +#define IOIO_DATA_32   BIT(6)
> +#define IOIO_DATA_16   BIT(5)
> +#define IOIO_DATA_8    BIT(4)
> +
> +#define IOIO_SEG_ES    (0 << 10)
> +#define IOIO_SEG_DS    (3 << 10)
> +
> +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 |= IOIO_TYPE_INS;
> +               *exitinfo |= IOIO_SEG_ES;
> +               *exitinfo |= (ctxt->regs->rdx & 0xffff) << 16;
> +               break;
> +
> +       /* OUTS opcodes */
> +       case 0x6e:
> +       case 0x6f:
> +               *exitinfo |= IOIO_TYPE_OUTS;
> +               *exitinfo |= IOIO_SEG_DS;
> +               *exitinfo |= (ctxt->regs->rdx & 0xffff) << 16;
> +               break;
> +
> +       /* IN immediate opcodes */
> +       case 0xe4:
> +       case 0xe5:
> +               *exitinfo |= IOIO_TYPE_IN;
> +               *exitinfo |= (u8)insn->immediate.value << 16;
> +               break;
> +
> +       /* OUT immediate opcodes */
> +       case 0xe6:
> +       case 0xe7:
> +               *exitinfo |= IOIO_TYPE_OUT;
> +               *exitinfo |= (u8)insn->immediate.value << 16;
> +               break;
> +
> +       /* IN register opcodes */
> +       case 0xec:
> +       case 0xed:
> +               *exitinfo |= IOIO_TYPE_IN;
> +               *exitinfo |= (ctxt->regs->rdx & 0xffff) << 16;
> +               break;
> +
> +       /* OUT register opcodes */
> +       case 0xee:
> +       case 0xef:
> +               *exitinfo |= 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 |= IOIO_DATA_8;
> +               break;
> +       default:
> +               /* Length determined by instruction parsing */
> +               *exitinfo |= (insn->opnd_bytes == 2) ? IOIO_DATA_16
> +                                                    : IOIO_DATA_32;
> +       }
> +       switch (insn->addr_bytes) {
> +       case 2:
> +               *exitinfo |= IOIO_ADDR_16;
> +               break;
> +       case 4:
> +               *exitinfo |= IOIO_ADDR_32;
> +               break;
> +       case 8:
> +               *exitinfo |= IOIO_ADDR_64;
> +               break;
> +       }
> +
> +       if (insn_has_rep_prefix(insn))
> +               *exitinfo |= 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 & 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 & 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 & 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)
> @@ -185,6 +328,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
> --
> 2.32.0
>

Reviewed-by: Marc Orr <marcorr@google.com>
diff mbox series

Patch

diff --git a/lib/x86/amd_sev_vc.c b/lib/x86/amd_sev_vc.c
index 401cb29..88c95e1 100644
--- a/lib/x86/amd_sev_vc.c
+++ b/lib/x86/amd_sev_vc.c
@@ -172,6 +172,149 @@  static enum es_result vc_handle_msr(struct ghcb *ghcb, struct es_em_ctxt *ctxt)
 	return ret;
 }
 
+#define IOIO_TYPE_STR  BIT(2)
+#define IOIO_TYPE_IN   1
+#define IOIO_TYPE_INS  (IOIO_TYPE_IN | IOIO_TYPE_STR)
+#define IOIO_TYPE_OUT  0
+#define IOIO_TYPE_OUTS (IOIO_TYPE_OUT | IOIO_TYPE_STR)
+
+#define IOIO_REP       BIT(3)
+
+#define IOIO_ADDR_64   BIT(9)
+#define IOIO_ADDR_32   BIT(8)
+#define IOIO_ADDR_16   BIT(7)
+
+#define IOIO_DATA_32   BIT(6)
+#define IOIO_DATA_16   BIT(5)
+#define IOIO_DATA_8    BIT(4)
+
+#define IOIO_SEG_ES    (0 << 10)
+#define IOIO_SEG_DS    (3 << 10)
+
+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 |= IOIO_TYPE_INS;
+		*exitinfo |= IOIO_SEG_ES;
+		*exitinfo |= (ctxt->regs->rdx & 0xffff) << 16;
+		break;
+
+	/* OUTS opcodes */
+	case 0x6e:
+	case 0x6f:
+		*exitinfo |= IOIO_TYPE_OUTS;
+		*exitinfo |= IOIO_SEG_DS;
+		*exitinfo |= (ctxt->regs->rdx & 0xffff) << 16;
+		break;
+
+	/* IN immediate opcodes */
+	case 0xe4:
+	case 0xe5:
+		*exitinfo |= IOIO_TYPE_IN;
+		*exitinfo |= (u8)insn->immediate.value << 16;
+		break;
+
+	/* OUT immediate opcodes */
+	case 0xe6:
+	case 0xe7:
+		*exitinfo |= IOIO_TYPE_OUT;
+		*exitinfo |= (u8)insn->immediate.value << 16;
+		break;
+
+	/* IN register opcodes */
+	case 0xec:
+	case 0xed:
+		*exitinfo |= IOIO_TYPE_IN;
+		*exitinfo |= (ctxt->regs->rdx & 0xffff) << 16;
+		break;
+
+	/* OUT register opcodes */
+	case 0xee:
+	case 0xef:
+		*exitinfo |= 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 |= IOIO_DATA_8;
+		break;
+	default:
+		/* Length determined by instruction parsing */
+		*exitinfo |= (insn->opnd_bytes == 2) ? IOIO_DATA_16
+						     : IOIO_DATA_32;
+	}
+	switch (insn->addr_bytes) {
+	case 2:
+		*exitinfo |= IOIO_ADDR_16;
+		break;
+	case 4:
+		*exitinfo |= IOIO_ADDR_32;
+		break;
+	case 8:
+		*exitinfo |= IOIO_ADDR_64;
+		break;
+	}
+
+	if (insn_has_rep_prefix(insn))
+		*exitinfo |= 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 & 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 & 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 & 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)
@@ -185,6 +328,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