diff options
author | Marc Zyngier <marc.zyngier@arm.com> | 2018-04-06 07:27:28 -0400 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2018-07-09 06:37:41 -0400 |
commit | e48d53a91f6e90873e21a5ca5e8c0d7a9f8936a4 (patch) | |
tree | 8226aa3bd5ae8eefb39ad90d66fff26917bd747b | |
parent | 1e4b044d22517cae7047c99038abb444423243ca (diff) |
arm64: KVM: Add support for Stage-2 control of memory types and cacheability
Up to ARMv8.3, the combinaison of Stage-1 and Stage-2 attributes
results in the strongest attribute of the two stages. This means
that the hypervisor has to perform quite a lot of cache maintenance
just in case the guest has some non-cacheable mappings around.
ARMv8.4 solves this problem by offering a different mode (FWB) where
Stage-2 has total control over the memory attribute (this is limited
to systems where both I/O and instruction fetches are coherent with
the dcache). This is achieved by having a different set of memory
attributes in the page tables, and a new bit set in HCR_EL2.
On such a system, we can then safely sidestep any form of dcache
management.
Acked-by: Catalin Marinas <catalin.marinas@arm.com>
Reviewed-by: Christoffer Dall <christoffer.dall@arm.com>
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r-- | arch/arm64/include/asm/cpucaps.h | 3 | ||||
-rw-r--r-- | arch/arm64/include/asm/kvm_arm.h | 1 | ||||
-rw-r--r-- | arch/arm64/include/asm/kvm_emulate.h | 2 | ||||
-rw-r--r-- | arch/arm64/include/asm/kvm_mmu.h | 27 | ||||
-rw-r--r-- | arch/arm64/include/asm/memory.h | 7 | ||||
-rw-r--r-- | arch/arm64/include/asm/pgtable-prot.h | 14 | ||||
-rw-r--r-- | arch/arm64/include/asm/sysreg.h | 1 | ||||
-rw-r--r-- | arch/arm64/kernel/cpufeature.c | 20 | ||||
-rw-r--r-- | virt/kvm/arm/mmu.c | 4 |
9 files changed, 70 insertions, 9 deletions
diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h index 8a699c708fc9..ed84d6536830 100644 --- a/arch/arm64/include/asm/cpucaps.h +++ b/arch/arm64/include/asm/cpucaps.h | |||
@@ -49,7 +49,8 @@ | |||
49 | #define ARM64_HAS_CACHE_DIC 28 | 49 | #define ARM64_HAS_CACHE_DIC 28 |
50 | #define ARM64_HW_DBM 29 | 50 | #define ARM64_HW_DBM 29 |
51 | #define ARM64_SSBD 30 | 51 | #define ARM64_SSBD 30 |
52 | #define ARM64_HAS_STAGE2_FWB 31 | ||
52 | 53 | ||
53 | #define ARM64_NCAPS 31 | 54 | #define ARM64_NCAPS 32 |
54 | 55 | ||
55 | #endif /* __ASM_CPUCAPS_H */ | 56 | #endif /* __ASM_CPUCAPS_H */ |
diff --git a/arch/arm64/include/asm/kvm_arm.h b/arch/arm64/include/asm/kvm_arm.h index 6dd285e979c9..aa45df752a16 100644 --- a/arch/arm64/include/asm/kvm_arm.h +++ b/arch/arm64/include/asm/kvm_arm.h | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <asm/types.h> | 23 | #include <asm/types.h> |
24 | 24 | ||
25 | /* Hyp Configuration Register (HCR) bits */ | 25 | /* Hyp Configuration Register (HCR) bits */ |
26 | #define HCR_FWB (UL(1) << 46) | ||
26 | #define HCR_TEA (UL(1) << 37) | 27 | #define HCR_TEA (UL(1) << 37) |
27 | #define HCR_TERR (UL(1) << 36) | 28 | #define HCR_TERR (UL(1) << 36) |
28 | #define HCR_TLOR (UL(1) << 35) | 29 | #define HCR_TLOR (UL(1) << 35) |
diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 1dab3a984608..dd98fdf33d99 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h | |||
@@ -63,6 +63,8 @@ static inline void vcpu_reset_hcr(struct kvm_vcpu *vcpu) | |||
63 | /* trap error record accesses */ | 63 | /* trap error record accesses */ |
64 | vcpu->arch.hcr_el2 |= HCR_TERR; | 64 | vcpu->arch.hcr_el2 |= HCR_TERR; |
65 | } | 65 | } |
66 | if (cpus_have_const_cap(ARM64_HAS_STAGE2_FWB)) | ||
67 | vcpu->arch.hcr_el2 |= HCR_FWB; | ||
66 | 68 | ||
67 | if (test_bit(KVM_ARM_VCPU_EL1_32BIT, vcpu->arch.features)) | 69 | if (test_bit(KVM_ARM_VCPU_EL1_32BIT, vcpu->arch.features)) |
68 | vcpu->arch.hcr_el2 &= ~HCR_RW; | 70 | vcpu->arch.hcr_el2 &= ~HCR_RW; |
diff --git a/arch/arm64/include/asm/kvm_mmu.h b/arch/arm64/include/asm/kvm_mmu.h index fb9a7127bb75..bac9f016736b 100644 --- a/arch/arm64/include/asm/kvm_mmu.h +++ b/arch/arm64/include/asm/kvm_mmu.h | |||
@@ -267,6 +267,15 @@ static inline void __clean_dcache_guest_page(kvm_pfn_t pfn, unsigned long size) | |||
267 | { | 267 | { |
268 | void *va = page_address(pfn_to_page(pfn)); | 268 | void *va = page_address(pfn_to_page(pfn)); |
269 | 269 | ||
270 | /* | ||
271 | * With FWB, we ensure that the guest always accesses memory using | ||
272 | * cacheable attributes, and we don't have to clean to PoC when | ||
273 | * faulting in pages. Furthermore, FWB implies IDC, so cleaning to | ||
274 | * PoU is not required either in this case. | ||
275 | */ | ||
276 | if (cpus_have_const_cap(ARM64_HAS_STAGE2_FWB)) | ||
277 | return; | ||
278 | |||
270 | kvm_flush_dcache_to_poc(va, size); | 279 | kvm_flush_dcache_to_poc(va, size); |
271 | } | 280 | } |
272 | 281 | ||
@@ -287,20 +296,26 @@ static inline void __invalidate_icache_guest_page(kvm_pfn_t pfn, | |||
287 | 296 | ||
288 | static inline void __kvm_flush_dcache_pte(pte_t pte) | 297 | static inline void __kvm_flush_dcache_pte(pte_t pte) |
289 | { | 298 | { |
290 | struct page *page = pte_page(pte); | 299 | if (!cpus_have_const_cap(ARM64_HAS_STAGE2_FWB)) { |
291 | kvm_flush_dcache_to_poc(page_address(page), PAGE_SIZE); | 300 | struct page *page = pte_page(pte); |
301 | kvm_flush_dcache_to_poc(page_address(page), PAGE_SIZE); | ||
302 | } | ||
292 | } | 303 | } |
293 | 304 | ||
294 | static inline void __kvm_flush_dcache_pmd(pmd_t pmd) | 305 | static inline void __kvm_flush_dcache_pmd(pmd_t pmd) |
295 | { | 306 | { |
296 | struct page *page = pmd_page(pmd); | 307 | if (!cpus_have_const_cap(ARM64_HAS_STAGE2_FWB)) { |
297 | kvm_flush_dcache_to_poc(page_address(page), PMD_SIZE); | 308 | struct page *page = pmd_page(pmd); |
309 | kvm_flush_dcache_to_poc(page_address(page), PMD_SIZE); | ||
310 | } | ||
298 | } | 311 | } |
299 | 312 | ||
300 | static inline void __kvm_flush_dcache_pud(pud_t pud) | 313 | static inline void __kvm_flush_dcache_pud(pud_t pud) |
301 | { | 314 | { |
302 | struct page *page = pud_page(pud); | 315 | if (!cpus_have_const_cap(ARM64_HAS_STAGE2_FWB)) { |
303 | kvm_flush_dcache_to_poc(page_address(page), PUD_SIZE); | 316 | struct page *page = pud_page(pud); |
317 | kvm_flush_dcache_to_poc(page_address(page), PUD_SIZE); | ||
318 | } | ||
304 | } | 319 | } |
305 | 320 | ||
306 | #define kvm_virt_to_phys(x) __pa_symbol(x) | 321 | #define kvm_virt_to_phys(x) __pa_symbol(x) |
diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 49d99214f43c..b96442960aea 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h | |||
@@ -155,6 +155,13 @@ | |||
155 | #define MT_S2_NORMAL 0xf | 155 | #define MT_S2_NORMAL 0xf |
156 | #define MT_S2_DEVICE_nGnRE 0x1 | 156 | #define MT_S2_DEVICE_nGnRE 0x1 |
157 | 157 | ||
158 | /* | ||
159 | * Memory types for Stage-2 translation when ID_AA64MMFR2_EL1.FWB is 0001 | ||
160 | * Stage-2 enforces Normal-WB and Device-nGnRE | ||
161 | */ | ||
162 | #define MT_S2_FWB_NORMAL 6 | ||
163 | #define MT_S2_FWB_DEVICE_nGnRE 1 | ||
164 | |||
158 | #ifdef CONFIG_ARM64_4K_PAGES | 165 | #ifdef CONFIG_ARM64_4K_PAGES |
159 | #define IOREMAP_MAX_ORDER (PUD_SHIFT) | 166 | #define IOREMAP_MAX_ORDER (PUD_SHIFT) |
160 | #else | 167 | #else |
diff --git a/arch/arm64/include/asm/pgtable-prot.h b/arch/arm64/include/asm/pgtable-prot.h index 108ecad7acc5..c66c3047400e 100644 --- a/arch/arm64/include/asm/pgtable-prot.h +++ b/arch/arm64/include/asm/pgtable-prot.h | |||
@@ -67,8 +67,18 @@ | |||
67 | #define PAGE_HYP_RO __pgprot(_HYP_PAGE_DEFAULT | PTE_HYP | PTE_RDONLY | PTE_HYP_XN) | 67 | #define PAGE_HYP_RO __pgprot(_HYP_PAGE_DEFAULT | PTE_HYP | PTE_RDONLY | PTE_HYP_XN) |
68 | #define PAGE_HYP_DEVICE __pgprot(PROT_DEVICE_nGnRE | PTE_HYP) | 68 | #define PAGE_HYP_DEVICE __pgprot(PROT_DEVICE_nGnRE | PTE_HYP) |
69 | 69 | ||
70 | #define PAGE_S2 __pgprot(_PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_NORMAL) | PTE_S2_RDONLY | PTE_S2_XN) | 70 | #define PAGE_S2_MEMATTR(attr) \ |
71 | #define PAGE_S2_DEVICE __pgprot(_PROT_DEFAULT | PTE_S2_MEMATTR(MT_S2_DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_S2_XN) | 71 | ({ \ |
72 | u64 __val; \ | ||
73 | if (cpus_have_const_cap(ARM64_HAS_STAGE2_FWB)) \ | ||
74 | __val = PTE_S2_MEMATTR(MT_S2_FWB_ ## attr); \ | ||
75 | else \ | ||
76 | __val = PTE_S2_MEMATTR(MT_S2_ ## attr); \ | ||
77 | __val; \ | ||
78 | }) | ||
79 | |||
80 | #define PAGE_S2 __pgprot(_PROT_DEFAULT | PAGE_S2_MEMATTR(NORMAL) | PTE_S2_RDONLY | PTE_S2_XN) | ||
81 | #define PAGE_S2_DEVICE __pgprot(_PROT_DEFAULT | PAGE_S2_MEMATTR(DEVICE_nGnRE) | PTE_S2_RDONLY | PTE_S2_XN) | ||
72 | 82 | ||
73 | #define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN) | 83 | #define PAGE_NONE __pgprot(((_PAGE_DEFAULT) & ~PTE_VALID) | PTE_PROT_NONE | PTE_RDONLY | PTE_NG | PTE_PXN | PTE_UXN) |
74 | #define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE) | 84 | #define PAGE_SHARED __pgprot(_PAGE_DEFAULT | PTE_USER | PTE_NG | PTE_PXN | PTE_UXN | PTE_WRITE) |
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h index a8f84812c6e8..98af0b37fb31 100644 --- a/arch/arm64/include/asm/sysreg.h +++ b/arch/arm64/include/asm/sysreg.h | |||
@@ -576,6 +576,7 @@ | |||
576 | #define ID_AA64MMFR1_VMIDBITS_16 2 | 576 | #define ID_AA64MMFR1_VMIDBITS_16 2 |
577 | 577 | ||
578 | /* id_aa64mmfr2 */ | 578 | /* id_aa64mmfr2 */ |
579 | #define ID_AA64MMFR2_FWB_SHIFT 40 | ||
579 | #define ID_AA64MMFR2_AT_SHIFT 32 | 580 | #define ID_AA64MMFR2_AT_SHIFT 32 |
580 | #define ID_AA64MMFR2_LVA_SHIFT 16 | 581 | #define ID_AA64MMFR2_LVA_SHIFT 16 |
581 | #define ID_AA64MMFR2_IESB_SHIFT 12 | 582 | #define ID_AA64MMFR2_IESB_SHIFT 12 |
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c index f24892a40d2c..d58d1f0abe16 100644 --- a/arch/arm64/kernel/cpufeature.c +++ b/arch/arm64/kernel/cpufeature.c | |||
@@ -192,6 +192,7 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr1[] = { | |||
192 | }; | 192 | }; |
193 | 193 | ||
194 | static const struct arm64_ftr_bits ftr_id_aa64mmfr2[] = { | 194 | static const struct arm64_ftr_bits ftr_id_aa64mmfr2[] = { |
195 | ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_FWB_SHIFT, 4, 0), | ||
195 | ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_AT_SHIFT, 4, 0), | 196 | ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_AT_SHIFT, 4, 0), |
196 | ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_LVA_SHIFT, 4, 0), | 197 | ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_LVA_SHIFT, 4, 0), |
197 | ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_IESB_SHIFT, 4, 0), | 198 | ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR2_IESB_SHIFT, 4, 0), |
@@ -1026,6 +1027,14 @@ static void cpu_copy_el2regs(const struct arm64_cpu_capabilities *__unused) | |||
1026 | } | 1027 | } |
1027 | #endif | 1028 | #endif |
1028 | 1029 | ||
1030 | static void cpu_has_fwb(const struct arm64_cpu_capabilities *__unused) | ||
1031 | { | ||
1032 | u64 val = read_sysreg_s(SYS_CLIDR_EL1); | ||
1033 | |||
1034 | /* Check that CLIDR_EL1.LOU{U,IS} are both 0 */ | ||
1035 | WARN_ON(val & (7 << 27 | 7 << 21)); | ||
1036 | } | ||
1037 | |||
1029 | static const struct arm64_cpu_capabilities arm64_features[] = { | 1038 | static const struct arm64_cpu_capabilities arm64_features[] = { |
1030 | { | 1039 | { |
1031 | .desc = "GIC system register CPU interface", | 1040 | .desc = "GIC system register CPU interface", |
@@ -1182,6 +1191,17 @@ static const struct arm64_cpu_capabilities arm64_features[] = { | |||
1182 | .type = ARM64_CPUCAP_SYSTEM_FEATURE, | 1191 | .type = ARM64_CPUCAP_SYSTEM_FEATURE, |
1183 | .matches = has_cache_dic, | 1192 | .matches = has_cache_dic, |
1184 | }, | 1193 | }, |
1194 | { | ||
1195 | .desc = "Stage-2 Force Write-Back", | ||
1196 | .type = ARM64_CPUCAP_SYSTEM_FEATURE, | ||
1197 | .capability = ARM64_HAS_STAGE2_FWB, | ||
1198 | .sys_reg = SYS_ID_AA64MMFR2_EL1, | ||
1199 | .sign = FTR_UNSIGNED, | ||
1200 | .field_pos = ID_AA64MMFR2_FWB_SHIFT, | ||
1201 | .min_field_value = 1, | ||
1202 | .matches = has_cpuid_feature, | ||
1203 | .cpu_enable = cpu_has_fwb, | ||
1204 | }, | ||
1185 | #ifdef CONFIG_ARM64_HW_AFDBM | 1205 | #ifdef CONFIG_ARM64_HW_AFDBM |
1186 | { | 1206 | { |
1187 | /* | 1207 | /* |
diff --git a/virt/kvm/arm/mmu.c b/virt/kvm/arm/mmu.c index 1d90d79706bd..ea7314296ad1 100644 --- a/virt/kvm/arm/mmu.c +++ b/virt/kvm/arm/mmu.c | |||
@@ -196,6 +196,10 @@ static void clear_stage2_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr | |||
196 | * This is why right after unmapping a page/section and invalidating | 196 | * This is why right after unmapping a page/section and invalidating |
197 | * the corresponding TLBs, we call kvm_flush_dcache_p*() to make sure | 197 | * the corresponding TLBs, we call kvm_flush_dcache_p*() to make sure |
198 | * the IO subsystem will never hit in the cache. | 198 | * the IO subsystem will never hit in the cache. |
199 | * | ||
200 | * This is all avoided on systems that have ARM64_HAS_STAGE2_FWB, as | ||
201 | * we then fully enforce cacheability of RAM, no matter what the guest | ||
202 | * does. | ||
199 | */ | 203 | */ |
200 | static void unmap_stage2_ptes(struct kvm *kvm, pmd_t *pmd, | 204 | static void unmap_stage2_ptes(struct kvm *kvm, pmd_t *pmd, |
201 | phys_addr_t addr, phys_addr_t end) | 205 | phys_addr_t addr, phys_addr_t end) |