From: Paul Mackerras <hidden> Date: 2021-03-31 04:54:54
On Tue, Mar 23, 2021 at 11:02:28AM +1000, Nicholas Piggin wrote:
The code being executed in KVM_GUEST_MODE_SKIP is hypervisor code with
MSR[IR]=0, so the faults of concern are the d-side ones caused by access
to guest context by the hypervisor.
Instruction breakpoint interrupts are not a concern here. It's unlikely
any good would come of causing breaks in this code, but skipping the
instruction that caused it won't help matters (e.g., skip the mtmsr that
sets MSR[DR]=0 or clears KVM_GUEST_MODE_SKIP).
Reviewed-by: Daniel Axtens <redacted>
Reviewed-by: Fabiano Rosas <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
It might be worth noting in the commit message that the 0x1300
interrupt was dropped from the architecture a long time ago and is not
generated by P7, P8, P9 or P10.
Acked-by: Paul Mackerras <redacted>
From: Paul Mackerras <hidden> Date: 2021-03-31 04:57:17
On Tue, Mar 23, 2021 at 11:02:29AM +1000, Nicholas Piggin wrote:
Rather than add the ME bit to the MSR at guest entry, make it clear
that the hypervisor does not allow the guest to clear the bit.
The ME set is kept in guest entry for now, but a future patch will
warn if it's not present.
Reviewed-by: Daniel Axtens <redacted>
Reviewed-by: Fabiano Rosas <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
From: Paul Mackerras <hidden> Date: 2021-03-31 04:59:07
On Tue, Mar 23, 2021 at 11:02:30AM +1000, Nicholas Piggin wrote:
Rather than clear the HV bit from the MSR at guest entry, make it clear
that the hypervisor does not allow the guest to set the bit.
The HV clear is kept in guest entry for now, but a future patch will
warn if it's not present.
Will warn if it *is* present, surely?
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
Almost all logic is moved to C, by introducing a new in_guest mode that
selects and branches very early in the interrupt handler to the P9 exit
code.
The remaining assembly is only about 160 lines of low level stack setup,
with VCPU vs host register save and restore, plus a small shim to the
legacy paths in the interrupt handler.
There are two motivations for this, the first is just make the code more
maintainable being in C. The second is to reduce the amount of code
running in a special KVM mode, "realmode". I put that in quotes because
with radix it is no longer necessarily real-mode in the MMU, but it
still has to be treated specially because it may be in real-mode, and
has various important registers like PID, DEC, TB, etc set to guest.
This is hostile to the rest of Linux and can't use arbitrary kernel
functionality or be instrumented well.
This initial patch is a reasonably faithful conversion of the asm code.
It does lack any loop to return quickly back into the guest without
switching out of realmode in the case of unimportant or easily handled
interrupts, as explained in the previous change, handling HV interrupts
in real mode is not so important for P9.
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
arch/powerpc/include/asm/asm-prototypes.h | 3 +-
arch/powerpc/include/asm/kvm_asm.h | 3 +-
arch/powerpc/include/asm/kvm_book3s_64.h | 8 +
arch/powerpc/kernel/security.c | 5 +-
arch/powerpc/kvm/Makefile | 3 +
arch/powerpc/kvm/book3s_64_entry.S | 246 ++++++++++++++++++++++
arch/powerpc/kvm/book3s_hv.c | 9 +-
arch/powerpc/kvm/book3s_hv_interrupt.c | 223 ++++++++++++++++++++
arch/powerpc/kvm/book3s_hv_rmhandlers.S | 123 +----------
9 files changed, 500 insertions(+), 123 deletions(-)
create mode 100644 arch/powerpc/kvm/book3s_hv_interrupt.c
@@ -430,16 +430,19 @@ device_initcall(stf_barrier_debugfs_init);staticvoidupdate_branch_cache_flush(void){-u32*site;+u32*site,__maybe_unused*site2;#ifdef CONFIG_KVM_BOOK3S_HV_POSSIBLEsite=&patch__call_kvm_flush_link_stack;+site2=&patch__call_kvm_flush_link_stack_p9;// This controls the branch from guest_exit_cont to kvm_flush_link_stackif(link_stack_flush_type==BRANCH_CACHE_FLUSH_NONE){patch_instruction_site(site,ppc_inst(PPC_INST_NOP));+patch_instruction_site(site2,ppc_inst(PPC_INST_NOP));}else{// Could use HW flush, but that could also flush count cachepatch_branch_site(site,(u64)&kvm_flush_link_stack,BRANCH_SET_LINK);+patch_branch_site(site2,(u64)&kvm_flush_link_stack,BRANCH_SET_LINK);}#endif
What does "this code" refer to? If it is the asm below, then it does not
save CTR, it is in the c code. Otherwise it is confusing (to me) :)
quoted hunk
+ */
+.balign IFETCH_ALIGN_BYTES
+kvmppc_p9_exit_hcall:
+ mfspr r11,SPRN_SRR0
+ mfspr r12,SPRN_SRR1
+ li r10,0xc00
+ std r10,HSTATE_SCRATCH0(r13)
+
+.balign IFETCH_ALIGN_BYTES
+kvmppc_p9_exit_interrupt:
+ /*
+ * If set to KVM_GUEST_MODE_GUEST_HV_FAST but we're still in the
+ * hypervisor, that means we can't return from the entry stack.
+ */
+ rldicl. r10,r12,64-MSR_HV_LG,63
+ bne- kvmppc_p9_bad_interrupt
+
+ std r1,HSTATE_SCRATCH1(r13)
+ std r3,HSTATE_SCRATCH2(r13)
+ ld r1,HSTATE_HOST_R1(r13)
+ ld r3,HSTATE_KVM_VCPU(r13)
+
+ std r9,VCPU_CR(r3)
+
+1:
+ std r11,VCPU_PC(r3)
+ std r12,VCPU_MSR(r3)
+
+ reg = 14
+ .rept 18
+ std reg,__VCPU_GPR(reg)(r3)
+ reg = reg + 1
+ .endr
+
+ /* r1, r3, r9-r13 are saved to vcpu by C code */
+ std r0,VCPU_GPR(R0)(r3)
+ std r2,VCPU_GPR(R2)(r3)
+ reg = 4
+ .rept 5
+ std reg,__VCPU_GPR(reg)(r3)
+ reg = reg + 1
+ .endr
+
+ ld r2,PACATOC(r13)
+
+ mflr r4
+ std r4,VCPU_LR(r3)
+ mfspr r4,SPRN_XER
+ std r4,VCPU_XER(r3)
+
+ reg = 14
+ .rept 18
+ ld reg,STACK_SLOT_NVGPRS + ((reg - 14) * 8)(r1)
+ reg = reg + 1
+ .endr
+
+ lwz r4,SFS+8(r1)
+ mtcr r4
+
+ /*
+ * Flush the link stack here, before executing the first blr on the
+ * way out of the guest.
+ *
+ * The link stack won't match coming out of the guest anyway so the
+ * only cost is the flush itself. The call clobbers r0.
+ */
+1: nop
+ patch_site 1b patch__call_kvm_flush_link_stack_p9
+
+ addi r1,r1,SFS
+ ld r0,PPC_LR_STKOFF(r1)
+ mtlr r0
+ blr
+
+/*
+ * Took an interrupt somewhere right before HRFID to guest, so registers are
+ * in a bad way. Return things hopefully enough to run host virtual code and
+ * run the Linux interrupt handler (SRESET or MCE) to print something useful.
+ *
+ * We could be really clever and save all host registers in known locations
+ * before setting HSTATE_IN_GUEST, then restoring them all here, and setting
+ * return address to a fixup that sets them up again. But that's a lot of
+ * effort for a small bit of code. Lots of other things to do first.
+ */
+kvmppc_p9_bad_interrupt:
+ /*
+ * Set GUEST_MODE_NONE so the handler won't branch to KVM, and clear
+ * MSR_RI in r12 ([H]SRR1) so the handler won't try to return.
+ */
+ li r10,KVM_GUEST_MODE_NONE
+ stb r10,HSTATE_IN_GUEST(r13)
+ li r10,MSR_RI
+ andc r12,r12,r10
+
+ /*
+ * Clean up guest registers to give host a chance to run.
+ */
+ li r10,0
+ mtspr SPRN_AMR,r10
+ mtspr SPRN_IAMR,r10
+ mtspr SPRN_CIABR,r10
+ mtspr SPRN_DAWRX0,r10
+BEGIN_FTR_SECTION
+ mtspr SPRN_DAWRX1,r10
+END_FTR_SECTION_IFSET(CPU_FTR_DAWR1)
+ mtspr SPRN_PID,r10
+
+ /*
+ * Switch to host MMU mode
+ */
+ ld r10, HSTATE_KVM_VCPU(r13)
+ ld r10, VCPU_KVM(r10)
+ lwz r10, KVM_HOST_LPID(r10)
+ mtspr SPRN_LPID,r10
+
+ ld r10, HSTATE_KVM_VCPU(r13)
+ ld r10, VCPU_KVM(r10)
+ ld r10, KVM_HOST_LPCR(r10)
+ mtspr SPRN_LPCR,r10
+
+ /*
+ * Go back to interrupt handler
+ */
+ ld r10,HSTATE_SCRATCH0(r13)
+ cmpwi r10,BOOK3S_INTERRUPT_MACHINE_CHECK
+ beq machine_check_common
+
+ ld r10,HSTATE_SCRATCH0(r13)
+ cmpwi r10,BOOK3S_INTERRUPT_SYSTEM_RESET
+ beq system_reset_common
+
+ b .
@@ -1431,6 +1431,8 @@ static int kvmppc_handle_exit_hv(struct kvm_vcpu *vcpu,*/caseBOOK3S_INTERRUPT_H_DATA_STORAGE:r=RESUME_PAGE_FAULT;+if(vcpu->arch.fault_dsisr==HDSISR_CANARY)+r=RESUME_GUEST;/* Just retry if it's the canary */break;caseBOOK3S_INTERRUPT_H_INST_STORAGE:vcpu->arch.fault_dar=kvmppc_get_pc(vcpu);
@@ -3698,8 +3702,6 @@ static int kvmhv_p9_guest_entry(struct kvm_vcpu *vcpu, u64 time_limit,if(next_timer<time_limit)time_limit=next_timer;-vcpu->arch.ceded=0;-kvmhv_save_host_pmu();/* saves it to PACA kvm_hstate */kvmppc_subcore_enter_guest();
@@ -3826,9 +3828,10 @@ static int kvmhv_p9_guest_entry(struct kvm_vcpu *vcpu, u64 time_limit,}}kvmppc_xive_pull_vcpu(vcpu);++vcpu->arch.slb_max=0;}-vcpu->arch.slb_max=0;dec=mfspr(SPRN_DEC);if(!(lpcr&LPCR_LD))/* Sign extend if not using large decrementer */dec=(s32)dec;
@@ -0,0 +1,223 @@+// SPDX-License-Identifier: GPL-2.0-only+#include<linux/kernel.h>+#include<linux/kvm_host.h>+#include<asm/asm-prototypes.h>+#include<asm/dbell.h>+#include<asm/kvm_ppc.h>++#ifdef CONFIG_KVM_BOOK3S_HV_EXIT_TIMING+staticvoid__start_timing(structkvm_vcpu*vcpu,structkvmhv_tb_accumulator*next)+{+structkvmppc_vcore*vc=vcpu->arch.vcore;+u64tb=mftb()-vc->tb_offset_applied;++vcpu->arch.cur_activity=next;+vcpu->arch.cur_tb_start=tb;+}++staticvoid__accumulate_time(structkvm_vcpu*vcpu,structkvmhv_tb_accumulator*next)+{+structkvmppc_vcore*vc=vcpu->arch.vcore;+structkvmhv_tb_accumulator*curr;+u64tb=mftb()-vc->tb_offset_applied;+u64prev_tb;+u64delta;+u64seq;++curr=vcpu->arch.cur_activity;+vcpu->arch.cur_activity=next;+prev_tb=vcpu->arch.cur_tb_start;+vcpu->arch.cur_tb_start=tb;++if(!curr)+return;++delta=tb-prev_tb;++seq=curr->seqcount;+curr->seqcount=seq+1;+smp_wmb();+curr->tb_total+=delta;+if(seq==0||delta<curr->tb_min)+curr->tb_min=delta;+if(delta>curr->tb_max)+curr->tb_max=delta;+smp_wmb();+curr->seqcount=seq+2;+}++#define start_timing(vcpu, next) __start_timing(vcpu, next)+#define end_timing(vcpu) __start_timing(vcpu, NULL)+#define accumulate_time(vcpu, next) __accumulate_time(vcpu, next)+#else+#define start_timing(vcpu, next) do {} while (0)+#define end_timing(vcpu) do {} while (0)+#define accumulate_time(vcpu, next) do {} while (0)+#endif++staticinlinevoidmfslb(unsignedintidx,u64*slbee,u64*slbev)+{+asmvolatile("slbmfev %0,%1":"=r"(*slbev):"r"(idx));+asmvolatile("slbmfee %0,%1":"=r"(*slbee):"r"(idx));+}++staticinlinevoidmtslb(unsignedintidx,u64slbee,u64slbev)+{+BUG_ON((slbee&0xfff)!=idx);++asmvolatile("slbmte %0,%1"::"r"(slbev),"r"(slbee));+}++staticinlinevoidslb_invalidate(unsignedintih)+{+asmvolatile("slbia %0"::"i"(ih));+}
This one is not used.
+
+/*
+ * Malicious or buggy radix guests may have inserted SLB entries
+ * (only 0..3 because radix always runs with UPRT=1), so these must
+ * be cleared here to avoid side-channels. slbmte is used rather
+ * than slbia, as it won't clear cached translations.
+ */
+static void radix_clear_slb(void)
+{
+ u64 slbee, slbev;
+ int i;
+
+ for (i = 0; i < 4; i++) {
+ mfslb(i, &slbee, &slbev);
+ if (unlikely(slbee || slbev)) {
+ slbee = i;
+ slbev = 0;
+ mtslb(i, slbee, slbev);
+ }
+ }
+}
+
+int __kvmhv_vcpu_entry_p9(struct kvm_vcpu *vcpu)
+{
+ u64 *exsave;
+ unsigned long msr = mfmsr();
+ int trap;
+
+ start_timing(vcpu, &vcpu->arch.rm_entry);
+
+ vcpu->arch.ceded = 0;
+
+ WARN_ON_ONCE(vcpu->arch.shregs.msr & MSR_HV);
+ WARN_ON_ONCE(!(vcpu->arch.shregs.msr & MSR_ME));
+
+ mtspr(SPRN_HSRR0, vcpu->arch.regs.nip);
+ mtspr(SPRN_HSRR1, (vcpu->arch.shregs.msr & ~MSR_HV) | MSR_ME);
+
+ /*
+ * On POWER9 DD2.1 and below, sometimes on a Hypervisor Data Storage
+ * Interrupt (HDSI) the HDSISR is not be updated at all.
+ *
+ * To work around this we put a canary value into the HDSISR before
+ * returning to a guest and then check for this canary when we take a
+ * HDSI. If we find the canary on a HDSI, we know the hardware didn't
+ * update the HDSISR. In this case we return to the guest to retake the
+ * HDSI which should correctly update the HDSISR the second time HDSI
+ * entry.
+ *
+ * Just do this on all p9 processors for now.
+ */
+ mtspr(SPRN_HDSISR, HDSISR_CANARY);
+
+ accumulate_time(vcpu, &vcpu->arch.guest_time);
+
+ local_paca->kvm_hstate.in_guest = KVM_GUEST_MODE_GUEST_HV_FAST;
+ kvmppc_p9_enter_guest(vcpu);
+ // Radix host and guest means host never runs with guest MMU state
+ local_paca->kvm_hstate.in_guest = KVM_GUEST_MODE_NONE;
+
+ accumulate_time(vcpu, &vcpu->arch.rm_intr);
+
+ /* Get these from r11/12 and paca exsave */
+ vcpu->arch.shregs.srr0 = mfspr(SPRN_SRR0);
+ vcpu->arch.shregs.srr1 = mfspr(SPRN_SRR1);
+ vcpu->arch.shregs.dar = mfspr(SPRN_DAR);
+ vcpu->arch.shregs.dsisr = mfspr(SPRN_DSISR);
+
+ /* 0x2 bit for HSRR is only used by PR and P7/8 HV paths, clear it */
+ trap = local_paca->kvm_hstate.scratch0 & ~0x2;
+ if (likely(trap > BOOK3S_INTERRUPT_MACHINE_CHECK)) {
+ exsave = local_paca->exgen;
+ } else if (trap == BOOK3S_INTERRUPT_SYSTEM_RESET) {
+ exsave = local_paca->exnmi;
+ } else { /* trap == 0x200 */
+ exsave = local_paca->exmc;
+ }
+
+ vcpu->arch.regs.gpr[1] = local_paca->kvm_hstate.scratch1;
+ vcpu->arch.regs.gpr[3] = local_paca->kvm_hstate.scratch2;
+ vcpu->arch.regs.gpr[9] = exsave[EX_R9/sizeof(u64)];
+ vcpu->arch.regs.gpr[10] = exsave[EX_R10/sizeof(u64)];
+ vcpu->arch.regs.gpr[11] = exsave[EX_R11/sizeof(u64)];
+ vcpu->arch.regs.gpr[12] = exsave[EX_R12/sizeof(u64)];
+ vcpu->arch.regs.gpr[13] = exsave[EX_R13/sizeof(u64)];
+ vcpu->arch.ppr = exsave[EX_PPR/sizeof(u64)];
+ vcpu->arch.cfar = exsave[EX_CFAR/sizeof(u64)];
+ vcpu->arch.regs.ctr = exsave[EX_CTR/sizeof(u64)];
+
+ vcpu->arch.last_inst = KVM_INST_FETCH_FAILED;
+
+ if (unlikely(trap == BOOK3S_INTERRUPT_MACHINE_CHECK)) {
+ vcpu->arch.fault_dar = exsave[EX_DAR/sizeof(u64)];
+ vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];
+ kvmppc_realmode_machine_check(vcpu);
+
+ } else if (unlikely(trap == BOOK3S_INTERRUPT_HMI)) {
+ kvmppc_realmode_hmi_handler();
+
+ } else if (trap == BOOK3S_INTERRUPT_H_EMUL_ASSIST) {
+ vcpu->arch.emul_inst = mfspr(SPRN_HEIR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_DATA_STORAGE) {
+ vcpu->arch.fault_dar = exsave[EX_DAR/sizeof(u64)];
+ vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];
+ vcpu->arch.fault_gpa = mfspr(SPRN_ASDR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_INST_STORAGE) {
+ vcpu->arch.fault_gpa = mfspr(SPRN_ASDR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_FAC_UNAVAIL) {
+ vcpu->arch.hfscr = mfspr(SPRN_HFSCR);
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ /*
+ * Softpatch interrupt for transactional memory emulation cases
+ * on POWER9 DD2.2. This is early in the guest exit path - we
+ * haven't saved registers or done a treclaim yet.
+ */
+ } else if (trap == BOOK3S_INTERRUPT_HV_SOFTPATCH) {
+ vcpu->arch.emul_inst = mfspr(SPRN_HEIR);
+
+ /*
+ * The cases we want to handle here are those where the guest
+ * is in real suspend mode and is trying to transition to
+ * transactional mode.
+ */
+ if (local_paca->kvm_hstate.fake_suspend &&
+ (vcpu->arch.shregs.msr & MSR_TS_S)) {
+ if (kvmhv_p9_tm_emulation_early(vcpu)) {
+ /* Prevent it being handled again. */
+ trap = 0;
+ }
+ }
+#endif
+ }
+
+ radix_clear_slb();
+
+ __mtmsrd(msr, 0);
The asm code only sets RI but this potentially sets more bits including
MSR_EE, is it expected to be 0 when __kvmhv_vcpu_entry_p9() is called?
+ mtspr(SPRN_CTRLT, 1);
What is this for? ISA does not shed much light:
===
63 RUN This bit controls an external I/O pin.
===
+
+ accumulate_time(vcpu, &vcpu->arch.rm_exit);
This should not compile without CONFIG_KVM_BOOK3S_HV_EXIT_TIMING.
+
+ end_timing(vcpu);
+
+ return trap;
The asm does "For hash guest, read the guest SLB and save it away", this
code does not. Is this new fast-path-in-c only for radix-on-radix or
hash VMs are supported too?
@@ -1135,97 +1128,6 @@ ret_to_ultra:ldr4,VCPU_GPR(R4)(r4)sc2-/*-*EntertheguestonaP9orlatersystemwherewehaveexactly-*onevcpupervcoreandwedon't need to go to real mode-*(whichimpliesthathostandguestarebothusingradixMMUmode).-*r3=vcpupointer-*MostSPRsandalltheVSRshavebeenloadedalready.-*/-_GLOBAL(__kvmhv_vcpu_entry_p9)-EXPORT_SYMBOL_GPL(__kvmhv_vcpu_entry_p9)-mflrr0-stdr0,PPC_LR_STKOFF(r1)-stdur1,-SFS(r1)--lir0,1-stwr0,STACK_SLOT_SHORT_PATH(r1)--stdr3,HSTATE_KVM_VCPU(r13)-mfcrr4-stwr4,SFS+8(r1)--stdr1,HSTATE_HOST_R1(r13)--reg=14-.rept18-stdreg,STACK_SLOT_NVGPRS+((reg-14)*8)(r1)-reg=reg+1-.endr--reg=14-.rept18-ldreg,__VCPU_GPR(reg)(r3)-reg=reg+1-.endr--mfmsrr10-stdr10,HSTATE_HOST_MSR(r13)--mrr4,r3-bfast_guest_entry_c-guest_exit_short_path:-/*-*MaliciousorbuggyradixguestsmayhaveinsertedSLBentries-*(only0..3becauseradixalwaysrunswithUPRT=1),sothesemust-*beclearedheretoavoidside-channels.slbmteisusedrather-*thanslbia,asitwon't clear cached translations.-*/-lir0,0-slbmter0,r0-lir4,1-slbmter0,r4-lir4,2-slbmter0,r4-lir4,3-slbmter0,r4--lir0,KVM_GUEST_MODE_NONE-stbr0,HSTATE_IN_GUEST(r13)--reg=14-.rept18-stdreg,__VCPU_GPR(reg)(r9)-reg=reg+1-.endr--reg=14-.rept18-ldreg,STACK_SLOT_NVGPRS+((reg-14)*8)(r1)-reg=reg+1-.endr--lwzr4,SFS+8(r1)-mtcrr4--mrr3,r12/*trapnumber*/--addir1,r1,SFS-ldr0,PPC_LR_STKOFF(r1)-mtlrr0--/*Ifweareinrealmode,doarfidtogetbacktothecaller*/-mfmsrr4-andi.r5,r4,MSR_IR-bnelr-rldiclr5,r4,64-MSR_TS_S_LG,62/*extractTSfield*/-mtsprSPRN_SRR0,r0-ldr10,HSTATE_HOST_MSR(r13)-rldimir10,r5,MSR_TS_S_LG,63-MSR_TS_T_LG-mtsprSPRN_SRR1,r10-RFI_TO_KERNEL-b.-secondary_too_late:lir12,0stwr12,STACK_SLOT_TRAP(r1)
From: Paul Mackerras <hidden> Date: 2021-04-01 05:42:17
On Tue, Mar 23, 2021 at 11:02:34AM +1000, Nicholas Piggin wrote:
System calls / hcalls have a different calling convention than
other interrupts, so there is code in the KVMTEST to massage these
into the same form as other interrupt handlers.
Move this work into the KVM hcall handler. This means teaching KVM
a little more about the low level interrupt handler setup, PACA save
areas, etc., although that's not obviously worse than the current
approach of coming up with an entirely different interrupt register
/ save convention.
[snip]
quoted hunk
@@ -1964,29 +1948,8 @@ EXC_VIRT_END(system_call, 0x4c00, 0x100) #ifdef CONFIG_KVM_BOOK3S_64_HANDLER TRAMP_REAL_BEGIN(system_call_kvm)- /*- * This is a hcall, so register convention is as above, with these- * differences:
I haven't checked all the code changes in detail yet, but this comment
at least is slightly misleading, since under PR KVM, system calls (to
the guest kernel) and hypercalls both come through this path.
Paul.
From: Paul Mackerras <hidden> Date: 2021-04-01 05:42:40
On Tue, Mar 23, 2021 at 11:02:31AM +1000, Nicholas Piggin wrote:
Rather than bifurcate the call depending on whether or not HV is
possible, and have the HV entry test for PR, just make a single
common point which does the demultiplexing. This makes it simpler
to add another type of exit handler.
Reviewed-by: Daniel Axtens <redacted>
Reviewed-by: Fabiano Rosas <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
From: Paul Mackerras <hidden> Date: 2021-04-01 05:43:02
On Tue, Mar 23, 2021 at 11:02:32AM +1000, Nicholas Piggin wrote:
Move the GUEST_MODE_SKIP logic into KVM code. This is quite a KVM
internal detail that has no real need to be in common handlers.
Also add a comment explaining why this thing exists.
It's a bit hard to see without more context, but I think that in the
PR-only case (CONFIG_KVM_BOOK3S_HV_POSSIBLE undefined), this will
corrupt R9. You need to restore R9 before the unconditional branch to
kvmppc_interrupt_pr. (I realize this code gets modified further, but
I'd rather not break bisection.)
Paul.
From: Nicholas Piggin <npiggin@gmail.com> Date: 2021-04-01 09:33:11
Excerpts from Paul Mackerras's message of March 31, 2021 2:08 pm:
On Tue, Mar 23, 2021 at 11:02:21AM +1000, Nicholas Piggin wrote:
quoted
Guest LPCR depends on hardware type, and future changes will add
restrictions based on errata and guest MMU mode. Move this logic
to a common function and use it for the cases where the guest
wants to update its LPCR (or the LPCR of a nested guest).
This change seems unnecessary, since kvmppc_update_lpcr is called only
to update MMU configuration bits, not as a result of any action by
userspace or a nested hypervisor. It's also beyond the scope of what
was mentioned in the commit message.
I didn't think it was outside the spirit of the patch, but yes
only the guest update LPCR case was enumerated. Would it be more
consistent to add it to the changelog and leave it in here or would
you prefer it left out until there is a real use?
The intention is a single location to add some of these things
(handwaving: say tlbie doesn't work on some chip and we want to
emulate it for old guests we could clear GTSE).
Thanks,
Nick
From: Nicholas Piggin <npiggin@gmail.com> Date: 2021-04-01 09:48:25
Excerpts from Paul Mackerras's message of March 31, 2021 2:54 pm:
On Tue, Mar 23, 2021 at 11:02:28AM +1000, Nicholas Piggin wrote:
quoted
The code being executed in KVM_GUEST_MODE_SKIP is hypervisor code with
MSR[IR]=0, so the faults of concern are the d-side ones caused by access
to guest context by the hypervisor.
Instruction breakpoint interrupts are not a concern here. It's unlikely
any good would come of causing breaks in this code, but skipping the
instruction that caused it won't help matters (e.g., skip the mtmsr that
sets MSR[DR]=0 or clears KVM_GUEST_MODE_SKIP).
Reviewed-by: Daniel Axtens <redacted>
Reviewed-by: Fabiano Rosas <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
It might be worth noting in the commit message that the 0x1300
interrupt was dropped from the architecture a long time ago and is not
generated by P7, P8, P9 or P10.
From: Nicholas Piggin <npiggin@gmail.com> Date: 2021-04-01 09:49:57
Excerpts from Paul Mackerras's message of March 31, 2021 2:58 pm:
On Tue, Mar 23, 2021 at 11:02:30AM +1000, Nicholas Piggin wrote:
quoted
Rather than clear the HV bit from the MSR at guest entry, make it clear
that the hypervisor does not allow the guest to set the bit.
The HV clear is kept in guest entry for now, but a future patch will
warn if it's not present.
Will warn if it *is* present, surely?
Just making sure you were awake, definitely wasn't a copy-paste bug...
Thanks,
Nick
quoted
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
From: Nicholas Piggin <npiggin@gmail.com> Date: 2021-04-01 09:53:58
Excerpts from Paul Mackerras's message of April 1, 2021 3:32 pm:
On Tue, Mar 23, 2021 at 11:02:32AM +1000, Nicholas Piggin wrote:
quoted
Move the GUEST_MODE_SKIP logic into KVM code. This is quite a KVM
internal detail that has no real need to be in common handlers.
Also add a comment explaining why this thing exists.
It's a bit hard to see without more context, but I think that in the
PR-only case (CONFIG_KVM_BOOK3S_HV_POSSIBLE undefined), this will
corrupt R9. You need to restore R9 before the unconditional branch to
kvmppc_interrupt_pr. (I realize this code gets modified further, but
I'd rather not break bisection.)
From: Nicholas Piggin <npiggin@gmail.com> Date: 2021-04-01 10:03:47
Excerpts from Paul Mackerras's message of April 1, 2021 3:41 pm:
On Tue, Mar 23, 2021 at 11:02:34AM +1000, Nicholas Piggin wrote:
quoted
System calls / hcalls have a different calling convention than
other interrupts, so there is code in the KVMTEST to massage these
into the same form as other interrupt handlers.
Move this work into the KVM hcall handler. This means teaching KVM
a little more about the low level interrupt handler setup, PACA save
areas, etc., although that's not obviously worse than the current
approach of coming up with an entirely different interrupt register
/ save convention.
[snip]
quoted
@@ -1964,29 +1948,8 @@ EXC_VIRT_END(system_call, 0x4c00, 0x100) #ifdef CONFIG_KVM_BOOK3S_64_HANDLER TRAMP_REAL_BEGIN(system_call_kvm)- /*- * This is a hcall, so register convention is as above, with these- * differences:
I haven't checked all the code changes in detail yet, but this comment
at least is slightly misleading, since under PR KVM, system calls (to
the guest kernel) and hypercalls both come through this path.
Yeah good point, I'll update the comment at its destination.
Thanks,
Nick
From: Nicholas Piggin <npiggin@gmail.com> Date: 2021-04-01 10:14:54
Excerpts from Alexey Kardashevskiy's message of March 26, 2021 12:05 pm:
quoted hunk
On 23/03/2021 12:02, Nicholas Piggin wrote:
quoted
On processors that don't suppress the HDEC exceptions when LPCR[HDICE]=0,
this could help reduce needless guest exits due to leftover exceptions on
entering the guest.
Reviewed-by: Alexey Kardashevskiy <redacted>
Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
ERROR: modpost: "decrementer_max" [arch/powerpc/kvm/kvm-hv.ko] undefined!
need this:
From: Nicholas Piggin <npiggin@gmail.com> Date: 2021-04-01 10:36:32
Excerpts from Alexey Kardashevskiy's message of April 1, 2021 3:30 pm:
On 3/23/21 12:02 PM, Nicholas Piggin wrote:
quoted
Almost all logic is moved to C, by introducing a new in_guest mode that
selects and branches very early in the interrupt handler to the P9 exit
code.
[...]
quoted
+/*
+ * kvmppc_p9_exit_hcall and kvmppc_p9_exit_interrupt are branched to from
+ * above if the interrupt was taken for a guest that was entered via
+ * kvmppc_p9_enter_guest().
+ *
+ * This code recovers the host stack and vcpu pointer, saves all GPRs and
+ * CR, LR, CTR, XER as well as guest MSR and NIA into the VCPU, then re-
+ * establishes the host stack and registers to return from the
+ * kvmppc_p9_enter_guest() function.
What does "this code" refer to? If it is the asm below, then it does not
save CTR, it is in the c code. Otherwise it is confusing (to me) :)
It gets used in a later patch, I guess I should move it there.
[...]
quoted
+int __kvmhv_vcpu_entry_p9(struct kvm_vcpu *vcpu)
+{
+ u64 *exsave;
+ unsigned long msr = mfmsr();
+ int trap;
+
+ start_timing(vcpu, &vcpu->arch.rm_entry);
+
+ vcpu->arch.ceded = 0;
+
+ WARN_ON_ONCE(vcpu->arch.shregs.msr & MSR_HV);
+ WARN_ON_ONCE(!(vcpu->arch.shregs.msr & MSR_ME));
+
+ mtspr(SPRN_HSRR0, vcpu->arch.regs.nip);
+ mtspr(SPRN_HSRR1, (vcpu->arch.shregs.msr & ~MSR_HV) | MSR_ME);
+
+ /*
+ * On POWER9 DD2.1 and below, sometimes on a Hypervisor Data Storage
+ * Interrupt (HDSI) the HDSISR is not be updated at all.
+ *
+ * To work around this we put a canary value into the HDSISR before
+ * returning to a guest and then check for this canary when we take a
+ * HDSI. If we find the canary on a HDSI, we know the hardware didn't
+ * update the HDSISR. In this case we return to the guest to retake the
+ * HDSI which should correctly update the HDSISR the second time HDSI
+ * entry.
+ *
+ * Just do this on all p9 processors for now.
+ */
+ mtspr(SPRN_HDSISR, HDSISR_CANARY);
+
+ accumulate_time(vcpu, &vcpu->arch.guest_time);
+
+ local_paca->kvm_hstate.in_guest = KVM_GUEST_MODE_GUEST_HV_FAST;
+ kvmppc_p9_enter_guest(vcpu);
+ // Radix host and guest means host never runs with guest MMU state
+ local_paca->kvm_hstate.in_guest = KVM_GUEST_MODE_NONE;
+
+ accumulate_time(vcpu, &vcpu->arch.rm_intr);
+
+ /* Get these from r11/12 and paca exsave */
+ vcpu->arch.shregs.srr0 = mfspr(SPRN_SRR0);
+ vcpu->arch.shregs.srr1 = mfspr(SPRN_SRR1);
+ vcpu->arch.shregs.dar = mfspr(SPRN_DAR);
+ vcpu->arch.shregs.dsisr = mfspr(SPRN_DSISR);
+
+ /* 0x2 bit for HSRR is only used by PR and P7/8 HV paths, clear it */
+ trap = local_paca->kvm_hstate.scratch0 & ~0x2;
+ if (likely(trap > BOOK3S_INTERRUPT_MACHINE_CHECK)) {
+ exsave = local_paca->exgen;
+ } else if (trap == BOOK3S_INTERRUPT_SYSTEM_RESET) {
+ exsave = local_paca->exnmi;
+ } else { /* trap == 0x200 */
+ exsave = local_paca->exmc;
+ }
+
+ vcpu->arch.regs.gpr[1] = local_paca->kvm_hstate.scratch1;
+ vcpu->arch.regs.gpr[3] = local_paca->kvm_hstate.scratch2;
+ vcpu->arch.regs.gpr[9] = exsave[EX_R9/sizeof(u64)];
+ vcpu->arch.regs.gpr[10] = exsave[EX_R10/sizeof(u64)];
+ vcpu->arch.regs.gpr[11] = exsave[EX_R11/sizeof(u64)];
+ vcpu->arch.regs.gpr[12] = exsave[EX_R12/sizeof(u64)];
+ vcpu->arch.regs.gpr[13] = exsave[EX_R13/sizeof(u64)];
+ vcpu->arch.ppr = exsave[EX_PPR/sizeof(u64)];
+ vcpu->arch.cfar = exsave[EX_CFAR/sizeof(u64)];
+ vcpu->arch.regs.ctr = exsave[EX_CTR/sizeof(u64)];
+
+ vcpu->arch.last_inst = KVM_INST_FETCH_FAILED;
+
+ if (unlikely(trap == BOOK3S_INTERRUPT_MACHINE_CHECK)) {
+ vcpu->arch.fault_dar = exsave[EX_DAR/sizeof(u64)];
+ vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];
+ kvmppc_realmode_machine_check(vcpu);
+
+ } else if (unlikely(trap == BOOK3S_INTERRUPT_HMI)) {
+ kvmppc_realmode_hmi_handler();
+
+ } else if (trap == BOOK3S_INTERRUPT_H_EMUL_ASSIST) {
+ vcpu->arch.emul_inst = mfspr(SPRN_HEIR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_DATA_STORAGE) {
+ vcpu->arch.fault_dar = exsave[EX_DAR/sizeof(u64)];
+ vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];
+ vcpu->arch.fault_gpa = mfspr(SPRN_ASDR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_INST_STORAGE) {
+ vcpu->arch.fault_gpa = mfspr(SPRN_ASDR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_FAC_UNAVAIL) {
+ vcpu->arch.hfscr = mfspr(SPRN_HFSCR);
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ /*
+ * Softpatch interrupt for transactional memory emulation cases
+ * on POWER9 DD2.2. This is early in the guest exit path - we
+ * haven't saved registers or done a treclaim yet.
+ */
+ } else if (trap == BOOK3S_INTERRUPT_HV_SOFTPATCH) {
+ vcpu->arch.emul_inst = mfspr(SPRN_HEIR);
+
+ /*
+ * The cases we want to handle here are those where the guest
+ * is in real suspend mode and is trying to transition to
+ * transactional mode.
+ */
+ if (local_paca->kvm_hstate.fake_suspend &&
+ (vcpu->arch.shregs.msr & MSR_TS_S)) {
+ if (kvmhv_p9_tm_emulation_early(vcpu)) {
+ /* Prevent it being handled again. */
+ trap = 0;
+ }
+ }
+#endif
+ }
+
+ radix_clear_slb();
+
+ __mtmsrd(msr, 0);
The asm code only sets RI but this potentially sets more bits including
MSR_EE, is it expected to be 0 when __kvmhv_vcpu_entry_p9() is called?
Yes.
quoted
+ mtspr(SPRN_CTRLT, 1);
What is this for? ISA does not shed much light:
===
63 RUN This bit controls an external I/O pin.
===
I don't think it even does that these days. It interacts with the PMU.
I was looking whether it's feasible to move it into PMU code entirely,
but apparently some tool or something might sample it. I'm a bit
suspicious about that because an untrusted guest could be running and
claim not to so I don't know what said tool really achieves, but I'll
go through that fight another day.
But KVM has to set it to 1 at exit because Linux host has it set to 1
except in CPU idle.
quoted
+
+ accumulate_time(vcpu, &vcpu->arch.rm_exit);
This should not compile without CONFIG_KVM_BOOK3S_HV_EXIT_TIMING.
It has an ifdef wrapper so it should work (it does on my local tree
which is slightly newer than what you have but I don't think I fixed
anything around this recently).
quoted
+
+ end_timing(vcpu);
+
+ return trap;
The asm does "For hash guest, read the guest SLB and save it away", this
code does not. Is this new fast-path-in-c only for radix-on-radix or
hash VMs are supported too?
That asm code does not run for "guest_exit_short_path" case (aka the
p9 path aka the fast path).
Upstream code only supports radix host and radix guest in this path.
The old path supports hash and radix. That's unchanged with this patch.
After the series, the new path supports all P9 modes (hash/hash,
radix/radix, and radix/hash), and the old path supports P7 and P8 only.
Thanks,
Nick
From: Nicholas Piggin <npiggin@gmail.com> Date: 2021-04-01 13:28:43
Excerpts from Nicholas Piggin's message of April 1, 2021 7:32 pm:
Excerpts from Paul Mackerras's message of March 31, 2021 2:08 pm:
quoted
On Tue, Mar 23, 2021 at 11:02:21AM +1000, Nicholas Piggin wrote:
quoted
Guest LPCR depends on hardware type, and future changes will add
restrictions based on errata and guest MMU mode. Move this logic
to a common function and use it for the cases where the guest
wants to update its LPCR (or the LPCR of a nested guest).
This change seems unnecessary, since kvmppc_update_lpcr is called only
to update MMU configuration bits, not as a result of any action by
userspace or a nested hypervisor. It's also beyond the scope of what
was mentioned in the commit message.
I didn't think it was outside the spirit of the patch, but yes
only the guest update LPCR case was enumerated. Would it be more
consistent to add it to the changelog and leave it in here or would
you prefer it left out until there is a real use?
On second thoughts, I already left at least one other place without
such a check, so I now tend to agree with you. But I instead added
a test that just ensures the host is not out of synch with itself in
terms of what it can set the LPCR to.
Thanks,
Nick
Excerpts from Alexey Kardashevskiy's message of April 1, 2021 3:30 pm:
quoted
On 3/23/21 12:02 PM, Nicholas Piggin wrote:
quoted
Almost all logic is moved to C, by introducing a new in_guest mode that
selects and branches very early in the interrupt handler to the P9 exit
code.
[...]
quoted
quoted
+/*
+ * kvmppc_p9_exit_hcall and kvmppc_p9_exit_interrupt are branched to from
+ * above if the interrupt was taken for a guest that was entered via
+ * kvmppc_p9_enter_guest().
+ *
+ * This code recovers the host stack and vcpu pointer, saves all GPRs and
+ * CR, LR, CTR, XER as well as guest MSR and NIA into the VCPU, then re-
+ * establishes the host stack and registers to return from the
+ * kvmppc_p9_enter_guest() function.
What does "this code" refer to? If it is the asm below, then it does not
save CTR, it is in the c code. Otherwise it is confusing (to me) :)
It gets used in a later patch, I guess I should move it there.
[...]
quoted
quoted
+int __kvmhv_vcpu_entry_p9(struct kvm_vcpu *vcpu)
+{
+ u64 *exsave;
+ unsigned long msr = mfmsr();
+ int trap;
+
+ start_timing(vcpu, &vcpu->arch.rm_entry);
+
+ vcpu->arch.ceded = 0;
+
+ WARN_ON_ONCE(vcpu->arch.shregs.msr & MSR_HV);
+ WARN_ON_ONCE(!(vcpu->arch.shregs.msr & MSR_ME));
+
+ mtspr(SPRN_HSRR0, vcpu->arch.regs.nip);
+ mtspr(SPRN_HSRR1, (vcpu->arch.shregs.msr & ~MSR_HV) | MSR_ME);
+
+ /*
+ * On POWER9 DD2.1 and below, sometimes on a Hypervisor Data Storage
+ * Interrupt (HDSI) the HDSISR is not be updated at all.
+ *
+ * To work around this we put a canary value into the HDSISR before
+ * returning to a guest and then check for this canary when we take a
+ * HDSI. If we find the canary on a HDSI, we know the hardware didn't
+ * update the HDSISR. In this case we return to the guest to retake the
+ * HDSI which should correctly update the HDSISR the second time HDSI
+ * entry.
+ *
+ * Just do this on all p9 processors for now.
+ */
+ mtspr(SPRN_HDSISR, HDSISR_CANARY);
+
+ accumulate_time(vcpu, &vcpu->arch.guest_time);
+
+ local_paca->kvm_hstate.in_guest = KVM_GUEST_MODE_GUEST_HV_FAST;
+ kvmppc_p9_enter_guest(vcpu);
+ // Radix host and guest means host never runs with guest MMU state
+ local_paca->kvm_hstate.in_guest = KVM_GUEST_MODE_NONE;
+
+ accumulate_time(vcpu, &vcpu->arch.rm_intr);
+
+ /* Get these from r11/12 and paca exsave */
+ vcpu->arch.shregs.srr0 = mfspr(SPRN_SRR0);
+ vcpu->arch.shregs.srr1 = mfspr(SPRN_SRR1);
+ vcpu->arch.shregs.dar = mfspr(SPRN_DAR);
+ vcpu->arch.shregs.dsisr = mfspr(SPRN_DSISR);
+
+ /* 0x2 bit for HSRR is only used by PR and P7/8 HV paths, clear it */
+ trap = local_paca->kvm_hstate.scratch0 & ~0x2;
+ if (likely(trap > BOOK3S_INTERRUPT_MACHINE_CHECK)) {
+ exsave = local_paca->exgen;
+ } else if (trap == BOOK3S_INTERRUPT_SYSTEM_RESET) {
+ exsave = local_paca->exnmi;
+ } else { /* trap == 0x200 */
+ exsave = local_paca->exmc;
+ }
+
+ vcpu->arch.regs.gpr[1] = local_paca->kvm_hstate.scratch1;
+ vcpu->arch.regs.gpr[3] = local_paca->kvm_hstate.scratch2;
+ vcpu->arch.regs.gpr[9] = exsave[EX_R9/sizeof(u64)];
+ vcpu->arch.regs.gpr[10] = exsave[EX_R10/sizeof(u64)];
+ vcpu->arch.regs.gpr[11] = exsave[EX_R11/sizeof(u64)];
+ vcpu->arch.regs.gpr[12] = exsave[EX_R12/sizeof(u64)];
+ vcpu->arch.regs.gpr[13] = exsave[EX_R13/sizeof(u64)];
+ vcpu->arch.ppr = exsave[EX_PPR/sizeof(u64)];
+ vcpu->arch.cfar = exsave[EX_CFAR/sizeof(u64)];
+ vcpu->arch.regs.ctr = exsave[EX_CTR/sizeof(u64)];
+
+ vcpu->arch.last_inst = KVM_INST_FETCH_FAILED;
+
+ if (unlikely(trap == BOOK3S_INTERRUPT_MACHINE_CHECK)) {
+ vcpu->arch.fault_dar = exsave[EX_DAR/sizeof(u64)];
+ vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];
+ kvmppc_realmode_machine_check(vcpu);
+
+ } else if (unlikely(trap == BOOK3S_INTERRUPT_HMI)) {
+ kvmppc_realmode_hmi_handler();
+
+ } else if (trap == BOOK3S_INTERRUPT_H_EMUL_ASSIST) {
+ vcpu->arch.emul_inst = mfspr(SPRN_HEIR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_DATA_STORAGE) {
+ vcpu->arch.fault_dar = exsave[EX_DAR/sizeof(u64)];
+ vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];
+ vcpu->arch.fault_gpa = mfspr(SPRN_ASDR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_INST_STORAGE) {
+ vcpu->arch.fault_gpa = mfspr(SPRN_ASDR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_FAC_UNAVAIL) {
+ vcpu->arch.hfscr = mfspr(SPRN_HFSCR);
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ /*
+ * Softpatch interrupt for transactional memory emulation cases
+ * on POWER9 DD2.2. This is early in the guest exit path - we
+ * haven't saved registers or done a treclaim yet.
+ */
+ } else if (trap == BOOK3S_INTERRUPT_HV_SOFTPATCH) {
+ vcpu->arch.emul_inst = mfspr(SPRN_HEIR);
+
+ /*
+ * The cases we want to handle here are those where the guest
+ * is in real suspend mode and is trying to transition to
+ * transactional mode.
+ */
+ if (local_paca->kvm_hstate.fake_suspend &&
+ (vcpu->arch.shregs.msr & MSR_TS_S)) {
+ if (kvmhv_p9_tm_emulation_early(vcpu)) {
+ /* Prevent it being handled again. */
+ trap = 0;
+ }
+ }
+#endif
+ }
+
+ radix_clear_slb();
+
+ __mtmsrd(msr, 0);
The asm code only sets RI but this potentially sets more bits including
MSR_EE, is it expected to be 0 when __kvmhv_vcpu_entry_p9() is called?
Yes.
quoted
quoted
+ mtspr(SPRN_CTRLT, 1);
What is this for? ISA does not shed much light:
===
63 RUN This bit controls an external I/O pin.
===
I don't think it even does that these days. It interacts with the PMU.
I was looking whether it's feasible to move it into PMU code entirely,
but apparently some tool or something might sample it. I'm a bit
suspicious about that because an untrusted guest could be running and
claim not to so I don't know what said tool really achieves, but I'll
go through that fight another day.
But KVM has to set it to 1 at exit because Linux host has it set to 1
except in CPU idle.
It this CTRLT setting a new thing or the asm does it too? I could not
spot it.
quoted
quoted
+
+ accumulate_time(vcpu, &vcpu->arch.rm_exit);
This should not compile without CONFIG_KVM_BOOK3S_HV_EXIT_TIMING.
It has an ifdef wrapper so it should work (it does on my local tree
which is slightly newer than what you have but I don't think I fixed
anything around this recently).
You are absolutely right, my bad.
quoted
quoted
+
+ end_timing(vcpu);
+
+ return trap;
The asm does "For hash guest, read the guest SLB and save it away", this
code does not. Is this new fast-path-in-c only for radix-on-radix or
hash VMs are supported too?
That asm code does not run for "guest_exit_short_path" case (aka the
p9 path aka the fast path).
Upstream code only supports radix host and radix guest in this path.
The old path supports hash and radix. That's unchanged with this patch.
After the series, the new path supports all P9 modes (hash/hash,
radix/radix, and radix/hash), and the old path supports P7 and P8 only.
Thanks for clarification. Besides that CTRLT, I checked if the new c
code matches the old asm code (which made diving into ISA incredible fun
:) ) so fwiw
Reviewed-by: Alexey Kardashevskiy <redacted>
I'd really like to see longer commit logs clarifying all intended
changes but it is probably just me.
From: Nicholas Piggin <npiggin@gmail.com> Date: 2021-04-02 07:59:27
Excerpts from Alexey Kardashevskiy's message of April 2, 2021 2:36 pm:
On 01/04/2021 21:35, Nicholas Piggin wrote:
quoted
Excerpts from Alexey Kardashevskiy's message of April 1, 2021 3:30 pm:
quoted
On 3/23/21 12:02 PM, Nicholas Piggin wrote:
quoted
Almost all logic is moved to C, by introducing a new in_guest mode that
selects and branches very early in the interrupt handler to the P9 exit
code.
[...]
quoted
quoted
+/*
+ * kvmppc_p9_exit_hcall and kvmppc_p9_exit_interrupt are branched to from
+ * above if the interrupt was taken for a guest that was entered via
+ * kvmppc_p9_enter_guest().
+ *
+ * This code recovers the host stack and vcpu pointer, saves all GPRs and
+ * CR, LR, CTR, XER as well as guest MSR and NIA into the VCPU, then re-
+ * establishes the host stack and registers to return from the
+ * kvmppc_p9_enter_guest() function.
What does "this code" refer to? If it is the asm below, then it does not
save CTR, it is in the c code. Otherwise it is confusing (to me) :)
It gets used in a later patch, I guess I should move it there.
[...]
quoted
quoted
+int __kvmhv_vcpu_entry_p9(struct kvm_vcpu *vcpu)
+{
+ u64 *exsave;
+ unsigned long msr = mfmsr();
+ int trap;
+
+ start_timing(vcpu, &vcpu->arch.rm_entry);
+
+ vcpu->arch.ceded = 0;
+
+ WARN_ON_ONCE(vcpu->arch.shregs.msr & MSR_HV);
+ WARN_ON_ONCE(!(vcpu->arch.shregs.msr & MSR_ME));
+
+ mtspr(SPRN_HSRR0, vcpu->arch.regs.nip);
+ mtspr(SPRN_HSRR1, (vcpu->arch.shregs.msr & ~MSR_HV) | MSR_ME);
+
+ /*
+ * On POWER9 DD2.1 and below, sometimes on a Hypervisor Data Storage
+ * Interrupt (HDSI) the HDSISR is not be updated at all.
+ *
+ * To work around this we put a canary value into the HDSISR before
+ * returning to a guest and then check for this canary when we take a
+ * HDSI. If we find the canary on a HDSI, we know the hardware didn't
+ * update the HDSISR. In this case we return to the guest to retake the
+ * HDSI which should correctly update the HDSISR the second time HDSI
+ * entry.
+ *
+ * Just do this on all p9 processors for now.
+ */
+ mtspr(SPRN_HDSISR, HDSISR_CANARY);
+
+ accumulate_time(vcpu, &vcpu->arch.guest_time);
+
+ local_paca->kvm_hstate.in_guest = KVM_GUEST_MODE_GUEST_HV_FAST;
+ kvmppc_p9_enter_guest(vcpu);
+ // Radix host and guest means host never runs with guest MMU state
+ local_paca->kvm_hstate.in_guest = KVM_GUEST_MODE_NONE;
+
+ accumulate_time(vcpu, &vcpu->arch.rm_intr);
+
+ /* Get these from r11/12 and paca exsave */
+ vcpu->arch.shregs.srr0 = mfspr(SPRN_SRR0);
+ vcpu->arch.shregs.srr1 = mfspr(SPRN_SRR1);
+ vcpu->arch.shregs.dar = mfspr(SPRN_DAR);
+ vcpu->arch.shregs.dsisr = mfspr(SPRN_DSISR);
+
+ /* 0x2 bit for HSRR is only used by PR and P7/8 HV paths, clear it */
+ trap = local_paca->kvm_hstate.scratch0 & ~0x2;
+ if (likely(trap > BOOK3S_INTERRUPT_MACHINE_CHECK)) {
+ exsave = local_paca->exgen;
+ } else if (trap == BOOK3S_INTERRUPT_SYSTEM_RESET) {
+ exsave = local_paca->exnmi;
+ } else { /* trap == 0x200 */
+ exsave = local_paca->exmc;
+ }
+
+ vcpu->arch.regs.gpr[1] = local_paca->kvm_hstate.scratch1;
+ vcpu->arch.regs.gpr[3] = local_paca->kvm_hstate.scratch2;
+ vcpu->arch.regs.gpr[9] = exsave[EX_R9/sizeof(u64)];
+ vcpu->arch.regs.gpr[10] = exsave[EX_R10/sizeof(u64)];
+ vcpu->arch.regs.gpr[11] = exsave[EX_R11/sizeof(u64)];
+ vcpu->arch.regs.gpr[12] = exsave[EX_R12/sizeof(u64)];
+ vcpu->arch.regs.gpr[13] = exsave[EX_R13/sizeof(u64)];
+ vcpu->arch.ppr = exsave[EX_PPR/sizeof(u64)];
+ vcpu->arch.cfar = exsave[EX_CFAR/sizeof(u64)];
+ vcpu->arch.regs.ctr = exsave[EX_CTR/sizeof(u64)];
+
+ vcpu->arch.last_inst = KVM_INST_FETCH_FAILED;
+
+ if (unlikely(trap == BOOK3S_INTERRUPT_MACHINE_CHECK)) {
+ vcpu->arch.fault_dar = exsave[EX_DAR/sizeof(u64)];
+ vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];
+ kvmppc_realmode_machine_check(vcpu);
+
+ } else if (unlikely(trap == BOOK3S_INTERRUPT_HMI)) {
+ kvmppc_realmode_hmi_handler();
+
+ } else if (trap == BOOK3S_INTERRUPT_H_EMUL_ASSIST) {
+ vcpu->arch.emul_inst = mfspr(SPRN_HEIR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_DATA_STORAGE) {
+ vcpu->arch.fault_dar = exsave[EX_DAR/sizeof(u64)];
+ vcpu->arch.fault_dsisr = exsave[EX_DSISR/sizeof(u64)];
+ vcpu->arch.fault_gpa = mfspr(SPRN_ASDR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_INST_STORAGE) {
+ vcpu->arch.fault_gpa = mfspr(SPRN_ASDR);
+
+ } else if (trap == BOOK3S_INTERRUPT_H_FAC_UNAVAIL) {
+ vcpu->arch.hfscr = mfspr(SPRN_HFSCR);
+
+#ifdef CONFIG_PPC_TRANSACTIONAL_MEM
+ /*
+ * Softpatch interrupt for transactional memory emulation cases
+ * on POWER9 DD2.2. This is early in the guest exit path - we
+ * haven't saved registers or done a treclaim yet.
+ */
+ } else if (trap == BOOK3S_INTERRUPT_HV_SOFTPATCH) {
+ vcpu->arch.emul_inst = mfspr(SPRN_HEIR);
+
+ /*
+ * The cases we want to handle here are those where the guest
+ * is in real suspend mode and is trying to transition to
+ * transactional mode.
+ */
+ if (local_paca->kvm_hstate.fake_suspend &&
+ (vcpu->arch.shregs.msr & MSR_TS_S)) {
+ if (kvmhv_p9_tm_emulation_early(vcpu)) {
+ /* Prevent it being handled again. */
+ trap = 0;
+ }
+ }
+#endif
+ }
+
+ radix_clear_slb();
+
+ __mtmsrd(msr, 0);
The asm code only sets RI but this potentially sets more bits including
MSR_EE, is it expected to be 0 when __kvmhv_vcpu_entry_p9() is called?
Yes.
quoted
quoted
+ mtspr(SPRN_CTRLT, 1);
What is this for? ISA does not shed much light:
===
63 RUN This bit controls an external I/O pin.
===
I don't think it even does that these days. It interacts with the PMU.
I was looking whether it's feasible to move it into PMU code entirely,
but apparently some tool or something might sample it. I'm a bit
suspicious about that because an untrusted guest could be running and
claim not to so I don't know what said tool really achieves, but I'll
go through that fight another day.
But KVM has to set it to 1 at exit because Linux host has it set to 1
except in CPU idle.
It this CTRLT setting a new thing or the asm does it too? I could not
spot it.
It's quite old actually. Earlier processors (maybe POWER6) you had to
even read-modify-write but new ones you can just store 1:
Guest exit:
/* Save guest CTRL register, set runlatch to 1 */
mfspr r6,SPRN_CTRLF
stw r6,VCPU_CTRL(r9)
andi. r0,r6,1
bne 4f
ori r6,r6,1
mtspr SPRN_CTRLT,r6
4:
entry:
/* Restore state of CTRL run bit; assume 1 on entry */
lwz r5,VCPU_CTRL(r4)
andi. r5,r5,1
bne 4f
mfspr r6,SPRN_CTRLF
clrrdi r6,r6,1
mtspr SPRN_CTRLT,r6
It used to light an indicator on the front of the system once upon a
time and I think on some processors (Cell maybe?) it actually controlled
SMT threads in some way. But certainly in P9 it does almost nothing and
we'll probably try to phase it out.
quoted
quoted
The asm does "For hash guest, read the guest SLB and save it away", this
code does not. Is this new fast-path-in-c only for radix-on-radix or
hash VMs are supported too?
That asm code does not run for "guest_exit_short_path" case (aka the
p9 path aka the fast path).
Upstream code only supports radix host and radix guest in this path.
The old path supports hash and radix. That's unchanged with this patch.
After the series, the new path supports all P9 modes (hash/hash,
radix/radix, and radix/hash), and the old path supports P7 and P8 only.
Thanks for clarification. Besides that CTRLT, I checked if the new c
code matches the old asm code (which made diving into ISA incredible fun
:) ) so fwiw
Reviewed-by: Alexey Kardashevskiy <redacted>
Thanks for reviewing.
I'd really like to see longer commit logs clarifying all intended
changes but it is probably just me.
I'm not sure what the best balance is, at some point code is a more
precise description. For this particular patch I probably do need to go
over the changelog again and try to make sure it makes sense and covers
things. If you have specifics that are missing or changes you'd like
I would definitely consider them.
Thanks,
Nick