Message ID | 20220209164420.8894-11-varad.gautam@suse.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | Add #VC exception handling for AMD SEV-ES | expand |
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 | 108 ++++++++++++++++++++++++++++++++++++++++++- > 1 file changed, 106 insertions(+), 2 deletions(-) > > diff --git a/lib/x86/amd_sev_vc.c b/lib/x86/amd_sev_vc.c > index 88c95e1..c79d9be 100644 > --- a/lib/x86/amd_sev_vc.c > +++ b/lib/x86/amd_sev_vc.c > @@ -278,10 +278,46 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) > return ES_OK; > } > > +static enum es_result vc_insn_string_read(struct es_em_ctxt *ctxt, > + void *src, unsigned char *buf, > + unsigned int data_size, > + unsigned int count, > + bool backwards) > +{ > + int i, b = backwards ? -1 : 1; > + > + for (i = 0; i < count; i++) { > + void *s = src + (i * data_size * b); > + unsigned char *d = buf + (i * data_size); > + > + memcpy(d, s, data_size); > + } > + > + return ES_OK; > +} > + > +static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt, > + void *dst, unsigned char *buf, > + unsigned int data_size, > + unsigned int count, > + bool backwards) > +{ > + int i, s = backwards ? -1 : 1; > + > + for (i = 0; i < count; i++) { > + void *d = dst + (i * data_size * s); > + unsigned char *b = buf + (i * data_size); > + > + memcpy(d, b, data_size); > + } > + > + 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; > + u64 exit_info_1, exit_info_2; > enum es_result ret; > > ret = vc_ioio_exitinfo(ctxt, &exit_info_1); > @@ -289,7 +325,75 @@ static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt) > return ret; > > if (exit_info_1 & IOIO_TYPE_STR) { > - ret = ES_VMM_ERROR; > + /* (REP) INS/OUTS */ > + > + bool df = ((regs->rflags & X86_EFLAGS_DF) == X86_EFLAGS_DF); > + unsigned int io_bytes, exit_bytes; > + unsigned int ghcb_count, op_count; > + unsigned long es_base; > + u64 sw_scratch; > + > + /* > + * For the string variants with rep prefix the amount of in/out > + * operations per #VC exception is limited so that the kernel > + * has a chance to take interrupts and re-schedule while the > + * instruction is emulated. > + */ > + io_bytes = (exit_info_1 >> 4) & 0x7; > + ghcb_count = sizeof(ghcb->shared_buffer) / io_bytes; > + > + op_count = (exit_info_1 & IOIO_REP) ? regs->rcx : 1; > + exit_info_2 = op_count < ghcb_count ? op_count : ghcb_count; > + exit_bytes = exit_info_2 * io_bytes; > + > + es_base = 0; > + > + /* Read bytes of OUTS into the shared buffer */ > + if (!(exit_info_1 & IOIO_TYPE_IN)) { > + ret = vc_insn_string_read(ctxt, > + (void *)(es_base + regs->rsi), > + ghcb->shared_buffer, io_bytes, > + exit_info_2, df); > + if (ret) > + return ret; > + } > + > + /* > + * Issue an VMGEXIT to the HV to consume the bytes from the > + * shared buffer or to have it write them into the shared buffer > + * depending on the instruction: OUTS or INS. > + */ > + sw_scratch = __pa(ghcb) + offsetof(struct ghcb, shared_buffer); > + ghcb_set_sw_scratch(ghcb, sw_scratch); > + ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_IOIO, > + exit_info_1, exit_info_2); > + if (ret != ES_OK) > + return ret; > + > + /* Read bytes from shared buffer into the guest's destination. */ > + if (exit_info_1 & IOIO_TYPE_IN) { > + ret = vc_insn_string_write(ctxt, > + (void *)(es_base + regs->rdi), > + ghcb->shared_buffer, io_bytes, > + exit_info_2, df); > + if (ret) > + return ret; > + > + if (df) > + regs->rdi -= exit_bytes; > + else > + regs->rdi += exit_bytes; > + } else { > + if (df) > + regs->rsi -= exit_bytes; > + else > + regs->rsi += exit_bytes; > + } > + > + if (exit_info_1 & IOIO_REP) > + regs->rcx -= exit_info_2; > + > + ret = regs->rcx ? ES_RETRY : ES_OK; > } else { > /* IN/OUT into/from rAX */ > > -- > 2.32.0 > I was able to run both the amd_sev and msr tests under SEV-ES using this built-in #VC handler on my setup. Obviously, that doesn't exercise all of this #VC handler code. But I also compared it against what's in LInux and read through all of it as well. Great job, Varad! Reviewed-by: Marc Orr <marcorr@google.com> Tested-by: Marc Orr <marcorr@google.com>
On 2/13/22 2:31 AM, Marc Orr wrote: > 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 | 108 ++++++++++++++++++++++++++++++++++++++++++- >> 1 file changed, 106 insertions(+), 2 deletions(-) >> >> diff --git a/lib/x86/amd_sev_vc.c b/lib/x86/amd_sev_vc.c >> index 88c95e1..c79d9be 100644 >> --- a/lib/x86/amd_sev_vc.c >> +++ b/lib/x86/amd_sev_vc.c >> @@ -278,10 +278,46 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) >> return ES_OK; >> } >> >> +static enum es_result vc_insn_string_read(struct es_em_ctxt *ctxt, >> + void *src, unsigned char *buf, >> + unsigned int data_size, >> + unsigned int count, >> + bool backwards) >> +{ >> + int i, b = backwards ? -1 : 1; >> + >> + for (i = 0; i < count; i++) { >> + void *s = src + (i * data_size * b); >> + unsigned char *d = buf + (i * data_size); >> + >> + memcpy(d, s, data_size); >> + } >> + >> + return ES_OK; >> +} >> + >> +static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt, >> + void *dst, unsigned char *buf, >> + unsigned int data_size, >> + unsigned int count, >> + bool backwards) >> +{ >> + int i, s = backwards ? -1 : 1; >> + >> + for (i = 0; i < count; i++) { >> + void *d = dst + (i * data_size * s); >> + unsigned char *b = buf + (i * data_size); >> + >> + memcpy(d, b, data_size); >> + } >> + >> + 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; >> + u64 exit_info_1, exit_info_2; >> enum es_result ret; >> >> ret = vc_ioio_exitinfo(ctxt, &exit_info_1); >> @@ -289,7 +325,75 @@ static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt) >> return ret; >> >> if (exit_info_1 & IOIO_TYPE_STR) { >> - ret = ES_VMM_ERROR; >> + /* (REP) INS/OUTS */ >> + >> + bool df = ((regs->rflags & X86_EFLAGS_DF) == X86_EFLAGS_DF); >> + unsigned int io_bytes, exit_bytes; >> + unsigned int ghcb_count, op_count; >> + unsigned long es_base; >> + u64 sw_scratch; >> + >> + /* >> + * For the string variants with rep prefix the amount of in/out >> + * operations per #VC exception is limited so that the kernel >> + * has a chance to take interrupts and re-schedule while the >> + * instruction is emulated. >> + */ >> + io_bytes = (exit_info_1 >> 4) & 0x7; >> + ghcb_count = sizeof(ghcb->shared_buffer) / io_bytes; >> + >> + op_count = (exit_info_1 & IOIO_REP) ? regs->rcx : 1; >> + exit_info_2 = op_count < ghcb_count ? op_count : ghcb_count; >> + exit_bytes = exit_info_2 * io_bytes; >> + >> + es_base = 0; >> + >> + /* Read bytes of OUTS into the shared buffer */ >> + if (!(exit_info_1 & IOIO_TYPE_IN)) { >> + ret = vc_insn_string_read(ctxt, >> + (void *)(es_base + regs->rsi), >> + ghcb->shared_buffer, io_bytes, >> + exit_info_2, df); >> + if (ret) >> + return ret; >> + } >> + >> + /* >> + * Issue an VMGEXIT to the HV to consume the bytes from the >> + * shared buffer or to have it write them into the shared buffer >> + * depending on the instruction: OUTS or INS. >> + */ >> + sw_scratch = __pa(ghcb) + offsetof(struct ghcb, shared_buffer); >> + ghcb_set_sw_scratch(ghcb, sw_scratch); >> + ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_IOIO, >> + exit_info_1, exit_info_2); >> + if (ret != ES_OK) >> + return ret; >> + >> + /* Read bytes from shared buffer into the guest's destination. */ >> + if (exit_info_1 & IOIO_TYPE_IN) { >> + ret = vc_insn_string_write(ctxt, >> + (void *)(es_base + regs->rdi), >> + ghcb->shared_buffer, io_bytes, >> + exit_info_2, df); >> + if (ret) >> + return ret; >> + >> + if (df) >> + regs->rdi -= exit_bytes; >> + else >> + regs->rdi += exit_bytes; >> + } else { >> + if (df) >> + regs->rsi -= exit_bytes; >> + else >> + regs->rsi += exit_bytes; >> + } >> + >> + if (exit_info_1 & IOIO_REP) >> + regs->rcx -= exit_info_2; >> + >> + ret = regs->rcx ? ES_RETRY : ES_OK; >> } else { >> /* IN/OUT into/from rAX */ >> >> -- >> 2.32.0 >> > > I was able to run both the amd_sev and msr tests under SEV-ES using > this built-in #VC handler on my setup. Obviously, that doesn't > exercise all of this #VC handler code. But I also compared it against > what's in LInux and read through all of it as well. Great job, Varad! > > Reviewed-by: Marc Orr <marcorr@google.com> > Tested-by: Marc Orr <marcorr@google.com> > Thank you Marc for going over the series! I'll have the v3 out soon. Regards, Varad
diff --git a/lib/x86/amd_sev_vc.c b/lib/x86/amd_sev_vc.c index 88c95e1..c79d9be 100644 --- a/lib/x86/amd_sev_vc.c +++ b/lib/x86/amd_sev_vc.c @@ -278,10 +278,46 @@ static enum es_result vc_ioio_exitinfo(struct es_em_ctxt *ctxt, u64 *exitinfo) return ES_OK; } +static enum es_result vc_insn_string_read(struct es_em_ctxt *ctxt, + void *src, unsigned char *buf, + unsigned int data_size, + unsigned int count, + bool backwards) +{ + int i, b = backwards ? -1 : 1; + + for (i = 0; i < count; i++) { + void *s = src + (i * data_size * b); + unsigned char *d = buf + (i * data_size); + + memcpy(d, s, data_size); + } + + return ES_OK; +} + +static enum es_result vc_insn_string_write(struct es_em_ctxt *ctxt, + void *dst, unsigned char *buf, + unsigned int data_size, + unsigned int count, + bool backwards) +{ + int i, s = backwards ? -1 : 1; + + for (i = 0; i < count; i++) { + void *d = dst + (i * data_size * s); + unsigned char *b = buf + (i * data_size); + + memcpy(d, b, data_size); + } + + 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; + u64 exit_info_1, exit_info_2; enum es_result ret; ret = vc_ioio_exitinfo(ctxt, &exit_info_1); @@ -289,7 +325,75 @@ static enum es_result vc_handle_ioio(struct ghcb *ghcb, struct es_em_ctxt *ctxt) return ret; if (exit_info_1 & IOIO_TYPE_STR) { - ret = ES_VMM_ERROR; + /* (REP) INS/OUTS */ + + bool df = ((regs->rflags & X86_EFLAGS_DF) == X86_EFLAGS_DF); + unsigned int io_bytes, exit_bytes; + unsigned int ghcb_count, op_count; + unsigned long es_base; + u64 sw_scratch; + + /* + * For the string variants with rep prefix the amount of in/out + * operations per #VC exception is limited so that the kernel + * has a chance to take interrupts and re-schedule while the + * instruction is emulated. + */ + io_bytes = (exit_info_1 >> 4) & 0x7; + ghcb_count = sizeof(ghcb->shared_buffer) / io_bytes; + + op_count = (exit_info_1 & IOIO_REP) ? regs->rcx : 1; + exit_info_2 = op_count < ghcb_count ? op_count : ghcb_count; + exit_bytes = exit_info_2 * io_bytes; + + es_base = 0; + + /* Read bytes of OUTS into the shared buffer */ + if (!(exit_info_1 & IOIO_TYPE_IN)) { + ret = vc_insn_string_read(ctxt, + (void *)(es_base + regs->rsi), + ghcb->shared_buffer, io_bytes, + exit_info_2, df); + if (ret) + return ret; + } + + /* + * Issue an VMGEXIT to the HV to consume the bytes from the + * shared buffer or to have it write them into the shared buffer + * depending on the instruction: OUTS or INS. + */ + sw_scratch = __pa(ghcb) + offsetof(struct ghcb, shared_buffer); + ghcb_set_sw_scratch(ghcb, sw_scratch); + ret = sev_es_ghcb_hv_call(ghcb, ctxt, SVM_EXIT_IOIO, + exit_info_1, exit_info_2); + if (ret != ES_OK) + return ret; + + /* Read bytes from shared buffer into the guest's destination. */ + if (exit_info_1 & IOIO_TYPE_IN) { + ret = vc_insn_string_write(ctxt, + (void *)(es_base + regs->rdi), + ghcb->shared_buffer, io_bytes, + exit_info_2, df); + if (ret) + return ret; + + if (df) + regs->rdi -= exit_bytes; + else + regs->rdi += exit_bytes; + } else { + if (df) + regs->rsi -= exit_bytes; + else + regs->rsi += exit_bytes; + } + + if (exit_info_1 & IOIO_REP) + regs->rcx -= exit_info_2; + + ret = regs->rcx ? ES_RETRY : ES_OK; } else { /* IN/OUT into/from rAX */
Using Linux's IOIO #VC processing logic. Signed-off-by: Varad Gautam <varad.gautam@suse.com> --- lib/x86/amd_sev_vc.c | 108 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-)