On 12/04/16 14:38, Oliver O'Halloran wrote:
> Power ISAv3 extends the width of the decrementer register from 32 bits.
> The enlarged register width is implementation dependent, but reads from
> these registers are automatically sign extended to produce a 64 bit output
> when operating in large mode. The HDEC always operates in large mode
> while the DEC register can be operated in 32bit mode or large mode
> depending on the setting of the LPCR.LD bit.
> 
> Currently the hypervisor assumes that reads from the DEC and HDEC register
> produce a 32 bit result which it sign extends to 64 bits using the extsw
> instruction. This behaviour can result in the guest DEC register value
> being corrupted by the hypervisor when the guest is operating in LD mode since
> the results of the extsw instruction only depends on the value of bit
> 31 in the register to be sign extended.
> 
> This patch adds the GET_DEC() and GET_HDEC() assembly macros for reading
> from the decrementer registers. These macros will return the current
> decrementer value as a 64 bit quantity regardless of the Host CPU or
> guest decrementer operating mode. Additionally this patch corrects several
> uses of decrementer values that assume a 32 bit register width.
> 
> Signed-off-by: Oliver O'Halloran <ooh...@gmail.com>
> Cc: Paul Mackerras <pau...@samba.org>
> ---
>  arch/powerpc/include/asm/exception-64s.h | 22 ++++++++++++++++++
>  arch/powerpc/include/asm/kvm_host.h      |  2 +-
>  arch/powerpc/include/asm/kvm_ppc.h       |  2 +-
>  arch/powerpc/include/uapi/asm/kvm.h      |  2 +-
>  arch/powerpc/kernel/exceptions-64s.S     |  9 +++++++-
>  arch/powerpc/kvm/book3s_hv_interrupts.S  |  3 +--
>  arch/powerpc/kvm/book3s_hv_rmhandlers.S  | 38 
> ++++++++++++++++++--------------
>  arch/powerpc/kvm/emulate.c               |  4 ++--
>  8 files changed, 57 insertions(+), 25 deletions(-)
> 
> diff --git a/arch/powerpc/include/asm/exception-64s.h 
> b/arch/powerpc/include/asm/exception-64s.h
> index 93ae809fe5ea..d922f76c682d 100644
> --- a/arch/powerpc/include/asm/exception-64s.h
> +++ b/arch/powerpc/include/asm/exception-64s.h
> @@ -545,4 +545,26 @@ END_FTR_SECTION_IFSET(CPU_FTR_CAN_NAP)
>  #define FINISH_NAP
>  #endif
>  
> +/* these ensure that we always get a 64bit value from the
> + * decrementer register. */
Comment style does not match recommended style

> +
> +#define IS_LD_ENABLED(reg)                 \
> +     mfspr  reg,SPRN_LPCR;              \
> +     andis. reg,reg,(LPCR_LD >> 16);
> +
> +#define GET_DEC(reg)                       \
> +     IS_LD_ENABLED(reg);                \
> +     mfspr reg, SPRN_DEC;               \
> +     bne 99f;                           \
> +     extsw reg, reg;                    \
> +99:
> +
> +/* For CPUs that support it the Hypervisor LD is
> + * always enabled, so this needs to be feature gated */
> +#define GET_HDEC(reg) \
> +     mfspr reg, SPRN_HDEC;           \
> +BEGIN_FTR_SECTION                       \
> +     extsw reg, reg;                 \
> +END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_300)
> +
>  #endif       /* _ASM_POWERPC_EXCEPTION_H */
> diff --git a/arch/powerpc/include/asm/kvm_host.h 
> b/arch/powerpc/include/asm/kvm_host.h
> index d7b343170453..6330d3fca083 100644
> --- a/arch/powerpc/include/asm/kvm_host.h
> +++ b/arch/powerpc/include/asm/kvm_host.h
> @@ -516,7 +516,7 @@ struct kvm_vcpu_arch {
>       ulong mcsrr0;
>       ulong mcsrr1;
>       ulong mcsr;
> -     u32 dec;
> +     u64 dec;
>  #ifdef CONFIG_BOOKE
>       u32 decar;
>  #endif
> diff --git a/arch/powerpc/include/asm/kvm_ppc.h 
> b/arch/powerpc/include/asm/kvm_ppc.h
> index 2544edabe7f3..4de0102930e9 100644
> --- a/arch/powerpc/include/asm/kvm_ppc.h
> +++ b/arch/powerpc/include/asm/kvm_ppc.h
> @@ -94,7 +94,7 @@ extern int kvmppc_emulate_instruction(struct kvm_run *run,
>  extern int kvmppc_emulate_loadstore(struct kvm_vcpu *vcpu);
>  extern int kvmppc_emulate_mmio(struct kvm_run *run, struct kvm_vcpu *vcpu);
>  extern void kvmppc_emulate_dec(struct kvm_vcpu *vcpu);
> -extern u32 kvmppc_get_dec(struct kvm_vcpu *vcpu, u64 tb);
> +extern u64 kvmppc_get_dec(struct kvm_vcpu *vcpu, u64 tb);
>  extern void kvmppc_decrementer_func(struct kvm_vcpu *vcpu);
>  extern int kvmppc_sanity_check(struct kvm_vcpu *vcpu);
>  extern int kvmppc_subarch_vcpu_init(struct kvm_vcpu *vcpu);
> diff --git a/arch/powerpc/include/uapi/asm/kvm.h 
> b/arch/powerpc/include/uapi/asm/kvm.h
> index c93cf35ce379..2dd92e841127 100644
> --- a/arch/powerpc/include/uapi/asm/kvm.h
> +++ b/arch/powerpc/include/uapi/asm/kvm.h
> @@ -215,7 +215,7 @@ struct kvm_sregs {
>                       __u32 tsr;      /* KVM_SREGS_E_UPDATE_TSR */
>                       __u32 tcr;
>                       __u32 decar;
> -                     __u32 dec;      /* KVM_SREGS_E_UPDATE_DEC */
> +                     __u64 dec;      /* KVM_SREGS_E_UPDATE_DEC */
>  
>                       /*
>                        * Userspace can read TB directly, but the
> diff --git a/arch/powerpc/kernel/exceptions-64s.S 
> b/arch/powerpc/kernel/exceptions-64s.S
> index 7716cebf4b8e..984ae894e758 100644
> --- a/arch/powerpc/kernel/exceptions-64s.S
> +++ b/arch/powerpc/kernel/exceptions-64s.S
> @@ -641,7 +641,14 @@ masked_##_H##interrupt:                                  
> \
>       stb     r11,PACAIRQHAPPENED(r13);               \
>       cmpwi   r10,PACA_IRQ_DEC;                       \
>       bne     1f;                                     \
> -     lis     r10,0x7fff;                             \
> +     mfspr   r10,SPRN_LPCR;                          \
> +     andis.  r10,r10,(LPCR_LD >> 16);                \
> +     beq     3f;                                     \
> +     LOAD_REG_ADDR(r10, decrementer_max);            \

I presume this works because all of this is w.r.t. kernel toc
and not necessarily in the kvm module

> +     ld r10,0(r10);                                  \
> +     mtspr   SPRN_DEC,r10;                           \
> +     b       2f;                                     \
> +3:   lis     r10,0x7fff;                             \
>       ori     r10,r10,0xffff;                         \
>       mtspr   SPRN_DEC,r10;                           \
>       b       2f;                                     \
> diff --git a/arch/powerpc/kvm/book3s_hv_interrupts.S 
> b/arch/powerpc/kvm/book3s_hv_interrupts.S
> index 0fdc4a28970b..b408f72385e4 100644
> --- a/arch/powerpc/kvm/book3s_hv_interrupts.S
> +++ b/arch/powerpc/kvm/book3s_hv_interrupts.S
> @@ -121,10 +121,9 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
>        * Put whatever is in the decrementer into the
>        * hypervisor decrementer.
>        */
> -     mfspr   r8,SPRN_DEC
> +     GET_DEC(r8)
>       mftb    r7
>       mtspr   SPRN_HDEC,r8
> -     extsw   r8,r8
>       add     r8,r8,r7
>       std     r8,HSTATE_DECEXP(r13)
>  
> diff --git a/arch/powerpc/kvm/book3s_hv_rmhandlers.S 
> b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> index e571ad277398..718e5581494e 100644
> --- a/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> +++ b/arch/powerpc/kvm/book3s_hv_rmhandlers.S
> @@ -183,6 +183,10 @@ END_FTR_SECTION_IFSET(CPU_FTR_ARCH_207S)
>  kvmppc_primary_no_guest:
>       /* We handle this much like a ceded vcpu */
>       /* put the HDEC into the DEC, since HDEC interrupts don't wake us */
> +
> +     /* XXX: ISA v3 only says the HDEC is at least as large as the DEC, but
> +      *      this code assumes we can fit HDEC in DEC. This is probably
> +      *      not an issue in practice, but... */
>       mfspr   r3, SPRN_HDEC
>       mtspr   SPRN_DEC, r3
>       /*
> @@ -249,9 +253,9 @@ kvm_novcpu_wakeup:
>       bge     kvm_novcpu_exit
>  
>       /* See if our timeslice has expired (HDEC is negative) */
> -     mfspr   r0, SPRN_HDEC
> +     GET_HDEC(r0);
>       li      r12, BOOK3S_INTERRUPT_HV_DECREMENTER
> -     cmpwi   r0, 0
> +     cmpdi   r0, 0
>       blt     kvm_novcpu_exit
>  
>       /* Got an IPI but other vcpus aren't yet exiting, must be a latecomer */
> @@ -340,8 +344,9 @@ kvm_secondary_got_guest:
>       lbz     r4, HSTATE_PTID(r13)
>       cmpwi   r4, 0
>       bne     63f
> -     lis     r6, 0x7fff
> -     ori     r6, r6, 0xffff
> +
> +     LOAD_REG_ADDR(r6, decrementer_max);
> +     ld      r6,0(r6);
>       mtspr   SPRN_HDEC, r6
>       /* and set per-LPAR registers, if doing dynamic micro-threading */
>       ld      r6, HSTATE_SPLIT_MODE(r13)
> @@ -897,7 +902,7 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
>       mftb    r7
>       subf    r3,r7,r8
>       mtspr   SPRN_DEC,r3
> -     stw     r3,VCPU_DEC(r4)
> +     std     r3,VCPU_DEC(r4)
>  
>       ld      r5, VCPU_SPRG0(r4)
>       ld      r6, VCPU_SPRG1(r4)
> @@ -953,8 +958,8 @@ END_FTR_SECTION_IFCLR(CPU_FTR_ARCH_207S)
>       isync
>  
>       /* Check if HDEC expires soon */
> -     mfspr   r3, SPRN_HDEC
> -     cmpwi   r3, 512         /* 1 microsecond */
> +     GET_HDEC(r3)
> +     cmpdi   r3, 512         /* 1 microsecond */
>       blt     hdec_soon
>  
>       ld      r6, VCPU_CTR(r4)
> @@ -990,8 +995,9 @@ deliver_guest_interrupt:
>       beq     5f
>       li      r0, BOOK3S_INTERRUPT_EXTERNAL
>       bne     cr1, 12f
> -     mfspr   r0, SPRN_DEC
> -     cmpwi   r0, 0
> +
> +     GET_DEC(r0)
> +     cmpdi   r0, 0
>       li      r0, BOOK3S_INTERRUPT_DECREMENTER
>       bge     5f
>  
> @@ -1206,8 +1212,8 @@ END_FTR_SECTION_IFSET(CPU_FTR_HAS_PPR)
>       /* See if this is a leftover HDEC interrupt */
>       cmpwi   r12,BOOK3S_INTERRUPT_HV_DECREMENTER
>       bne     2f
> -     mfspr   r3,SPRN_HDEC
> -     cmpwi   r3,0
> +     GET_HDEC(r3);
> +     cmpdi   r3,0
>       mr      r4,r9
>       bge     fast_guest_return
>  2:
> @@ -1326,9 +1332,8 @@ mc_cont:
>       mtspr   SPRN_SPURR,r4
>  
>       /* Save DEC */
> -     mfspr   r5,SPRN_DEC
> +     GET_DEC(r5);
>       mftb    r6
> -     extsw   r5,r5
>       add     r5,r5,r6
>       /* r5 is a guest timebase value here, convert to host TB */
>       ld      r3,HSTATE_KVM_VCORE(r13)
> @@ -2250,15 +2255,14 @@ _GLOBAL(kvmppc_h_cede)                /* r3 = vcpu 
> pointer, r11 = msr, r13 = paca */
>        * no later than the end of our timeslice (HDEC interrupts
>        * don't wake us from nap).
>        */
> -     mfspr   r3, SPRN_DEC
> -     mfspr   r4, SPRN_HDEC
> +     GET_DEC(r3);
> +     GET_HDEC(r4);
>       mftb    r5
> -     cmpw    r3, r4
> +     cmpd    r3, r4
>       ble     67f
>       mtspr   SPRN_DEC, r4
>  67:
>       /* save expiry time of guest decrementer */
> -     extsw   r3, r3
>       add     r3, r3, r5
>       ld      r4, HSTATE_KVM_VCPU(r13)
>       ld      r5, HSTATE_KVM_VCORE(r13)
> diff --git a/arch/powerpc/kvm/emulate.c b/arch/powerpc/kvm/emulate.c
> index 5cc2e7af3a7b..4d26252162b0 100644
> --- a/arch/powerpc/kvm/emulate.c
> +++ b/arch/powerpc/kvm/emulate.c
> @@ -47,7 +47,7 @@ void kvmppc_emulate_dec(struct kvm_vcpu *vcpu)
>       kvmppc_core_dequeue_dec(vcpu);
>  
>       /* POWER4+ triggers a dec interrupt if the value is < 0 */
> -     if (vcpu->arch.dec & 0x80000000) {
> +     if ((s64) vcpu->arch.dec < 0) {
>               kvmppc_core_queue_dec(vcpu);
>               return;
>       }
> @@ -78,7 +78,7 @@ void kvmppc_emulate_dec(struct kvm_vcpu *vcpu)
>       vcpu->arch.dec_jiffies = get_tb();
>  }
>  
> -u32 kvmppc_get_dec(struct kvm_vcpu *vcpu, u64 tb)
> +u64 kvmppc_get_dec(struct kvm_vcpu *vcpu, u64 tb)
>  {
>       u64 jd = tb - vcpu->arch.dec_jiffies;
>  
> 
_______________________________________________
Linuxppc-dev mailing list
Linuxppc-dev@lists.ozlabs.org
https://lists.ozlabs.org/listinfo/linuxppc-dev

Reply via email to