Message ID | 20240207204652.22954-2-ankita@nvidia.com (mailing list archive) |
---|---|
State | New |
Headers | show |
Series | kvm: arm64: allow the VM to select DEVICE_* and NORMAL_NC for IO memory | expand |
On Thu, Feb 08, 2024 at 02:16:49AM +0530, ankita@nvidia.com wrote: > diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c > index c651df904fe3..2a893724ee9b 100644 > --- a/arch/arm64/kvm/hyp/pgtable.c > +++ b/arch/arm64/kvm/hyp/pgtable.c > @@ -717,15 +717,28 @@ void kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, > static int stage2_set_prot_attr(struct kvm_pgtable *pgt, enum kvm_pgtable_prot prot, > kvm_pte_t *ptep) > { > - bool device = prot & KVM_PGTABLE_PROT_DEVICE; > - kvm_pte_t attr = device ? KVM_S2_MEMATTR(pgt, DEVICE_nGnRE) : > - KVM_S2_MEMATTR(pgt, NORMAL); > + kvm_pte_t attr; > u32 sh = KVM_PTE_LEAF_ATTR_LO_S2_SH_IS; > > + switch (prot & (KVM_PGTABLE_PROT_DEVICE | > + KVM_PGTABLE_PROT_NORMAL_NC)) { > + case 0: > + attr = KVM_S2_MEMATTR(pgt, NORMAL); > + break; > + case KVM_PGTABLE_PROT_DEVICE: > + if (prot & KVM_PGTABLE_PROT_X) > + return -EINVAL; > + attr = KVM_S2_MEMATTR(pgt, DEVICE_nGnRE); > + break; > + case KVM_PGTABLE_PROT_NORMAL_NC: > + attr = KVM_S2_MEMATTR(pgt, NORMAL_NC); > + break; Does it make sense to allow executable here as well? I don't think it's harmful but not sure there's a use-case for it either. > + default: > + WARN_ON_ONCE(1); Return -EINVAL?
On Thu, Feb 08, 2024 at 02:16:49AM +0530, ankita@nvidia.com wrote: > diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c > index c651df904fe3..2a893724ee9b 100644 > --- a/arch/arm64/kvm/hyp/pgtable.c > +++ b/arch/arm64/kvm/hyp/pgtable.c > @@ -717,15 +717,28 @@ void kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, > static int stage2_set_prot_attr(struct kvm_pgtable *pgt, enum kvm_pgtable_prot prot, > kvm_pte_t *ptep) > { > - bool device = prot & KVM_PGTABLE_PROT_DEVICE; > - kvm_pte_t attr = device ? KVM_S2_MEMATTR(pgt, DEVICE_nGnRE) : > - KVM_S2_MEMATTR(pgt, NORMAL); > + kvm_pte_t attr; > u32 sh = KVM_PTE_LEAF_ATTR_LO_S2_SH_IS; > > + switch (prot & (KVM_PGTABLE_PROT_DEVICE | > + KVM_PGTABLE_PROT_NORMAL_NC)) { > + case 0: > + attr = KVM_S2_MEMATTR(pgt, NORMAL); > + break; > + case KVM_PGTABLE_PROT_DEVICE: > + if (prot & KVM_PGTABLE_PROT_X) > + return -EINVAL; > + attr = KVM_S2_MEMATTR(pgt, DEVICE_nGnRE); > + break; > + case KVM_PGTABLE_PROT_NORMAL_NC: > + attr = KVM_S2_MEMATTR(pgt, NORMAL_NC); > + break; > + default: > + WARN_ON_ONCE(1); > + } Cosmetic nit, but I'd find this a little easier to read if the normal case was the default (i.e. drop 'case 0') and we returned an error for DEVICE | NC. Will
On Thu, Feb 08, 2024 at 01:00:59PM +0000, Catalin Marinas wrote: > On Thu, Feb 08, 2024 at 02:16:49AM +0530, ankita@nvidia.com wrote: > > diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c > > index c651df904fe3..2a893724ee9b 100644 > > --- a/arch/arm64/kvm/hyp/pgtable.c > > +++ b/arch/arm64/kvm/hyp/pgtable.c > > @@ -717,15 +717,28 @@ void kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, > > static int stage2_set_prot_attr(struct kvm_pgtable *pgt, enum kvm_pgtable_prot prot, > > kvm_pte_t *ptep) > > { > > - bool device = prot & KVM_PGTABLE_PROT_DEVICE; > > - kvm_pte_t attr = device ? KVM_S2_MEMATTR(pgt, DEVICE_nGnRE) : > > - KVM_S2_MEMATTR(pgt, NORMAL); > > + kvm_pte_t attr; > > u32 sh = KVM_PTE_LEAF_ATTR_LO_S2_SH_IS; > > > > + switch (prot & (KVM_PGTABLE_PROT_DEVICE | > > + KVM_PGTABLE_PROT_NORMAL_NC)) { > > + case 0: > > + attr = KVM_S2_MEMATTR(pgt, NORMAL); > > + break; > > + case KVM_PGTABLE_PROT_DEVICE: > > + if (prot & KVM_PGTABLE_PROT_X) > > + return -EINVAL; > > + attr = KVM_S2_MEMATTR(pgt, DEVICE_nGnRE); > > + break; > > + case KVM_PGTABLE_PROT_NORMAL_NC: > > + attr = KVM_S2_MEMATTR(pgt, NORMAL_NC); > > + break; > > Does it make sense to allow executable here as well? I don't think it's > harmful but not sure there's a use-case for it either. Ah, we should just return EINVAL for that too. I get that the memory attribute itself is not problematic, but since we're only using this thing for MMIO it'd be a rather massive bug in KVM... We reject attempts to do this earlier in user_mem_abort(). If, for some reason, we wanted to do Normal-NC actual memory then we would need to make sure that KVM does the appropriate cache maintenance at map / unmap.
>> + default: >> + WARN_ON_ONCE(1); > > Return -EINVAL? Sure. >> > + case KVM_PGTABLE_PROT_NORMAL_NC: >> > + attr = KVM_S2_MEMATTR(pgt, NORMAL_NC); >> > + break; >> >> Does it make sense to allow executable here as well? I don't think it's >> harmful but not sure there's a use-case for it either. > > Ah, we should just return EINVAL for that too. > > I get that the memory attribute itself is not problematic, but since > we're only using this thing for MMIO it'd be a rather massive > bug in KVM... We reject attempts to do this earlier in user_mem_abort(). Ack, will change to test executable and return -EINVAL in that case.
>> >> + switch (prot & (KVM_PGTABLE_PROT_DEVICE | >> + KVM_PGTABLE_PROT_NORMAL_NC)) { >> + case 0: >> + attr = KVM_S2_MEMATTR(pgt, NORMAL); >> + break; >> + case KVM_PGTABLE_PROT_DEVICE: >> + if (prot & KVM_PGTABLE_PROT_X) >> + return -EINVAL; >> + attr = KVM_S2_MEMATTR(pgt, DEVICE_nGnRE); >> + break; >> + case KVM_PGTABLE_PROT_NORMAL_NC: >> + attr = KVM_S2_MEMATTR(pgt, NORMAL_NC); >> + break; >> + default: >> + WARN_ON_ONCE(1); >> + } > > Cosmetic nit, but I'd find this a little easier to read if the normal > case was the default (i.e. drop 'case 0') and we returned an error for > DEVICE | NC. Makes sense, will update the logic accordingly.
diff --git a/arch/arm64/include/asm/kvm_pgtable.h b/arch/arm64/include/asm/kvm_pgtable.h index cfdf40f734b1..19278dfe7978 100644 --- a/arch/arm64/include/asm/kvm_pgtable.h +++ b/arch/arm64/include/asm/kvm_pgtable.h @@ -197,6 +197,7 @@ enum kvm_pgtable_stage2_flags { * @KVM_PGTABLE_PROT_W: Write permission. * @KVM_PGTABLE_PROT_R: Read permission. * @KVM_PGTABLE_PROT_DEVICE: Device attributes. + * @KVM_PGTABLE_PROT_NORMAL_NC: Normal noncacheable attributes. * @KVM_PGTABLE_PROT_SW0: Software bit 0. * @KVM_PGTABLE_PROT_SW1: Software bit 1. * @KVM_PGTABLE_PROT_SW2: Software bit 2. @@ -208,6 +209,7 @@ enum kvm_pgtable_prot { KVM_PGTABLE_PROT_R = BIT(2), KVM_PGTABLE_PROT_DEVICE = BIT(3), + KVM_PGTABLE_PROT_NORMAL_NC = BIT(4), KVM_PGTABLE_PROT_SW0 = BIT(55), KVM_PGTABLE_PROT_SW1 = BIT(56), diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index d82305ab420f..449ca2ff1df6 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -173,6 +173,7 @@ * Memory types for Stage-2 translation */ #define MT_S2_NORMAL 0xf +#define MT_S2_NORMAL_NC 0x5 #define MT_S2_DEVICE_nGnRE 0x1 /* @@ -180,6 +181,7 @@ * Stage-2 enforces Normal-WB and Device-nGnRE */ #define MT_S2_FWB_NORMAL 6 +#define MT_S2_FWB_NORMAL_NC 5 #define MT_S2_FWB_DEVICE_nGnRE 1 #ifdef CONFIG_ARM64_4K_PAGES diff --git a/arch/arm64/kvm/hyp/pgtable.c b/arch/arm64/kvm/hyp/pgtable.c index c651df904fe3..2a893724ee9b 100644 --- a/arch/arm64/kvm/hyp/pgtable.c +++ b/arch/arm64/kvm/hyp/pgtable.c @@ -717,15 +717,28 @@ void kvm_tlb_flush_vmid_range(struct kvm_s2_mmu *mmu, static int stage2_set_prot_attr(struct kvm_pgtable *pgt, enum kvm_pgtable_prot prot, kvm_pte_t *ptep) { - bool device = prot & KVM_PGTABLE_PROT_DEVICE; - kvm_pte_t attr = device ? KVM_S2_MEMATTR(pgt, DEVICE_nGnRE) : - KVM_S2_MEMATTR(pgt, NORMAL); + kvm_pte_t attr; u32 sh = KVM_PTE_LEAF_ATTR_LO_S2_SH_IS; + switch (prot & (KVM_PGTABLE_PROT_DEVICE | + KVM_PGTABLE_PROT_NORMAL_NC)) { + case 0: + attr = KVM_S2_MEMATTR(pgt, NORMAL); + break; + case KVM_PGTABLE_PROT_DEVICE: + if (prot & KVM_PGTABLE_PROT_X) + return -EINVAL; + attr = KVM_S2_MEMATTR(pgt, DEVICE_nGnRE); + break; + case KVM_PGTABLE_PROT_NORMAL_NC: + attr = KVM_S2_MEMATTR(pgt, NORMAL_NC); + break; + default: + WARN_ON_ONCE(1); + } + if (!(prot & KVM_PGTABLE_PROT_X)) attr |= KVM_PTE_LEAF_ATTR_HI_S2_XN; - else if (device) - return -EINVAL; if (prot & KVM_PGTABLE_PROT_R) attr |= KVM_PTE_LEAF_ATTR_LO_S2_S2AP_R;