arm/arm64: fixes and updates
Subsystems (2)
Hi Paolo,
Happy New Year and please pull.
Thanks,
drew
The following changes since commit 2c6589bc4e8bb0299cf4e8af84fa05cbbb30952d:
Update AMD instructions to conform to LLVM assembler (2019-12-18 18:31:17 +0100)
are available in the Git repository at:
https://github.com/rhdrjones/kvm-unit-tests arm/queue
for you to fetch changes up to ada4c643a2332c408a262a749b0365a1f970084c:
arm: cstart64.S: Remove icache invalidation from asm_mmu_enable (2020-01-06 10:30:43 +0100)
----------------------------------------------------------------
Alexandru Elisei (13):
lib: arm/arm64: Remove unnecessary dcache maintenance operations
lib: arm: Add proper data synchronization barriers for TLBIs
lib: Add WRITE_ONCE and READ_ONCE implementations in compiler.h
lib: arm/arm64: Use WRITE_ONCE to update the translation tables
lib: arm/arm64: Remove unused CPU_OFF parameter
lib: arm/arm64: Add missing include for alloc_page.h in pgtable.h
lib: arm: Implement flush_tlb_all
lib: arm/arm64: Teach mmu_clear_user about block mappings
arm64: timer: Write to ICENABLER to disable timer IRQ
lib: arm/arm64: Refuse to disable the MMU with non-identity stack pointer
arm: cstart64.S: Downgrade TLBI to non-shareable in asm_mmu_enable arm/arm64: Invalidate TLB before enabling MMU
arm: cstart64.S: Remove icache invalidation from asm_mmu_enable
Andrew Jones (2):
arm: Enable the VFP
arm/arm64: PL031: Fix check_rtc_irq
Chen Qun (1):
arm: Add missing test name prefix for pl031 and spinlock
Zeng Tao (1):
devicetree: Fix the dt_for_each_cpu_node
.gitlab-ci.yml | 2 +- arm/Makefile.arm | 2 +- arm/cache.c | 3 +- arm/cstart.S | 25 +++++++++++-- arm/cstart64.S | 5 ++- arm/pl031.c | 5 +-- arm/spinlock-test.c | 1 + arm/timer.c | 22 ++++++------ lib/arm/asm/gic-v3.h | 1 + lib/arm/asm/gic.h | 1 + lib/arm/asm/mmu-api.h | 2 +- lib/arm/asm/mmu.h | 18 ++++++---- lib/arm/asm/pgtable-hwdef.h | 11 ++++++ lib/arm/asm/pgtable.h | 20 ++++++++--- lib/arm/mmu.c | 60 ++++++++++++++++++------------- lib/arm/psci.c | 4 +-- lib/arm64/asm/pgtable-hwdef.h | 3 ++ lib/arm64/asm/pgtable.h | 15 ++++++-- lib/devicetree.c | 2 +- lib/linux/compiler.h | 83 +++++++++++++++++++++++++++++++++++++++++++ 20 files changed, 221 insertions(+), 64 deletions(-) create mode 100644 lib/linux/compiler.h
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 01/17] arm: Add missing test name prefix for pl031 and spinlock
From: Chen Qun <[email protected]>
pl031 and spinlock testcase without prefix, when running the unit tests in TAP mode (./run_tests.sh -t), it is difficult to the test results.
The test results:
ok 13 - Periph/PCell IDs match
ok 14 - R/O fields are R/O
ok 15 - RTC ticks at 1HZ
ok 16 - RTC IRQ not pending yet
...
ok 24 - RTC IRQ not pending anymore
ok 25 - CPU1: Done - Errors: 0
ok 26 - CPU0: Done - Errors: 0
It should be like this:
ok 13 - pl031: Periph/PCell IDs match
ok 14 - pl031: R/O fields are R/O
ok 15 - pl031: RTC ticks at 1HZ
ok 16 - pl031: RTC IRQ not pending yet
...
ok 24 - pl031: RTC IRQ not pending anymore
ok 25 - spinlock: CPU0: Done - Errors: 0
ok 26 - spinlock: CPU1: Done - Errors: 0
--- arm/pl031.c | 1 + arm/spinlock-test.c | 1 + 2 files changed, 2 insertions(+)
arm/pl031.c +1 -0
@@ -252,6 +252,7 @@ int main(int argc, char **argv)return 0 ; } +report_prefix_push ("pl031" );report (!check_id (),"Periph/PCell IDs match" );report (!check_ro (),"R/O fields are R/O" );report (!check_rtc_freq (),"RTC ticks at 1HZ" );
arm/spinlock-test.c +1 -0
@@ -72,6 +72,7 @@ static void test_spinlock(void *data __unused)int main (int argc,char **argv) { +report_prefix_push ("spinlock" );if (argc >1 &&strcmp (argv[1 ],"bad" ) !=0 ) { lock_ops.lock = gcc_builtin_lock; lock_ops.unlock = gcc_builtin_unlock;
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 02/17] arm: Enable the VFP
In reply to Andrew JonesVariable argument macros frequently depend on floating point registers. Indeed we needed to enable the VFP for arm64 since its introduction in order to use printf and the like. Somehow we didn't need to do that for arm32 until recently when compiling with GCC 9.
--- .gitlab-ci.yml | 2 +- arm/Makefile.arm | 2 +- arm/cstart.S | 14 +++++++++++++- 3 files changed, 15 insertions(+), 3 deletions(-)
.gitlab-ci.yml +1 -1
@@ -17,7 +17,7 @@ build-aarch64: build-arm: script: - - dnf install -y qemu-system-arm gcc-arm-linux-gnu-8.2.1-1.fc30.2 + - dnf install -y qemu-system-arm gcc-arm-linux-gnu - ./configure --arch=arm --cross-prefix=arm-linux-gnu- - make -j2 - ACCEL=tcg ./run_tests.sh
arm/Makefile.arm +1 -1
@@ -5,7 +5,7 @@ # bits = 32 ldarch = elf32-littlearm -machine = -marm +machine = -marm -mfpu=vfp # stack.o relies on frame pointers. KEEP_FRAME_POINTER := y
arm/cstart.S +13 -1
@@ -50,10 +50,11 @@ start: mov r0, r2 push {r0-r1} - /* set up vector table and mode stacks */ + /* set up vector table, mode stacks, and enable the VFP */ mov r0, lr @ lr is stack top (see above), @ which is the exception stacks base bl exceptions_init + bl enable_vfp /* complete setup */ pop {r0-r1}
@@ -100,6 +101,16 @@ exceptions_init: isb mov pc, lr +enable_vfp: + /* Enable full access to CP10 and CP11: */ + mov r0, #(3 << 22 | 3 << 20) + mcr p15, 0, r0, c1, c0, 2 + isb + /* Set the FPEXC.EN bit to enable Advanced SIMD and VFP: */ + mov r0, #(1 << 30) + vmsr fpexc, r0 + mov pc, lr + .text .global get_mmu_off
@@ -130,6 +141,7 @@ secondary_entry: ldr r0, [r1] mov sp, r0 bl exceptions_init + bl enable_vfp /* finish init in C code */ bl secondary_cinit
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 03/17] arm/arm64: PL031: Fix check_rtc_irq
In reply to Andrew JonesSince QEMU commit 83ad95957c7e ("pl031: Expose RTCICR as proper WC register") the PL031 test gets into an infinite loop. Now we must write bit zero of RTCICR to clear the IRQ status. Before, writing anything to RTCICR would work. As '1' is a member of 'anything' writing it should work for old QEMU as well.
--- arm/pl031.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arm/pl031.c +2 -2
@@ -143,8 +143,8 @@ static void irq_handler(struct pt_regs *regs)report (readl (&pl031->ris) ==1 ," RTC RIS == 1" );report (readl (&pl031->mis) ==1 ," RTC MIS == 1" ); -/* Writing any value should clear IRQ status */ -writel (0x80000000ULL , &pl031->icr); +/* Writing one to bit zero should clear IRQ status */ +writel (1 , &pl031->icr);report (readl (&pl031->ris) ==0 ," RTC RIS == 0" );report (readl (&pl031->mis) ==0 ," RTC MIS == 0" );
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 05/17] lib: arm/arm64: Remove unnecessary dcache maintenance operations
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
On ARMv7 with multiprocessing extensions (which are mandated by the virtualization extensions [1]), and on ARMv8, translation table walks are coherent [2, 3], which means that no dcache maintenance operations are required when changing the tables. Remove the maintenance operations so that we do only the minimum required to ensure correctness.
Translation table walks are coherent if the memory where the tables themselves reside have the same shareability and cacheability attributes as the translation table walks. For ARMv8, this is already the case, and it is only a matter of removing the cache operations.
However, for ARMv7, translation table walks were being configured as Non-shareable (TTBCR.SH0 = 0b00) and Non-cacheable (TTBCR.{I,O}RGN0 = 0b00). Fix that by marking them as Inner Shareable, Normal memory, Inner and Outer Write-Back Write-Allocate Cacheable.
Because translation table walks are now coherent on arm, replace the TLBIMVAA operation with TLBIMVAAIS in flush_tlb_page, which acts on the Inner Shareable domain instead of being private to the PE.
The functions that update the translation table are called when the MMU is off, or to modify permissions, in the case of the cache test, so break-before-make is not necessary.
[1] ARM DDI 0406C.d, section B1.7
[2] ARM DDI 0406C.d, section B3.3.1
[3] ARM DDI 0487E.a, section D13.2.72
[4] ARM DDI 0487E.a, section K11.5.3
--- arm/cstart.S | 7 +++++-- lib/arm/asm/mmu.h | 4 ++-- lib/arm/asm/pgtable-hwdef.h | 8 ++++++++ lib/arm/mmu.c | 14 +------------- 4 files changed, 16 insertions(+), 17 deletions(-)
arm/cstart.S +5 -2
@@ -9,6 +9,7 @@ #include <auxinfo.h> #include <asm/thread_info.h> #include <asm/asm-offsets.h> +#include <asm/pgtable-hwdef.h> #include <asm/ptrace.h> #include <asm/sysreg.h>
@@ -166,9 +167,11 @@ halt: .globl asm_mmu_enable asm_mmu_enable: /* TTBCR */ - mrc p15, 0, r2, c2, c0, 2 - orr r2, #(1 << 31) @ TTB_EAE + ldr r2, =(TTBCR_EAE | \ + TTBCR_SH0_SHARED | \ + TTBCR_IRGN0_WBWA | TTBCR_ORGN0_WBWA) mcr p15, 0, r2, c2, c0, 2 + isb /* MAIR */ ldr r2, =PRRR
lib/arm/asm/mmu.h +2 -2
@@ -31,8 +31,8 @@ static inline void flush_tlb_all(void)static inline void flush_tlb_page (unsigned long vaddr) { -/* TLBIMVAA */ - asmvolatile ("mcr p15, 0, %0, c8, c7, 3" ::"r" (vaddr)); +/* TLBIMVAAIS */ + asmvolatile ("mcr p15, 0, %0, c8, c3, 3" ::"r" (vaddr));dsb ();isb (); }
lib/arm/asm/pgtable-hwdef.h +8 -0
@@ -108,4 +108,12 @@#define PHYS_MASK_SHIFT (40)#define PHYS_MASK ((_AC(1, ULL) << PHYS_MASK_SHIFT) - 1) +#define TTBCR_IRGN0_WBWA (_AC(1, UL) << 8) +#define TTBCR_ORGN0_WBWA (_AC(1, UL) << 10) +#define TTBCR_SH0_SHARED (_AC(3, UL) << 12) +#define TTBCR_IRGN1_WBWA (_AC(1, UL) << 24) +#define TTBCR_ORGN1_WBWA (_AC(1, UL) << 26) +#define TTBCR_SH1_SHARED (_AC(3, UL) << 28) +#define TTBCR_EAE (_AC(1, UL) << 31) +#endif /* _ASMARM_PGTABLE_HWDEF_H_ */
lib/arm/mmu.c +1 -13
@@ -73,17 +73,6 @@ void mmu_disable(void)asm_mmu_disable (); } -static void flush_entry (pgd_t *pgtable,uintptr_t vaddr) -{ -pgd_t *pgd =pgd_offset (pgtable, vaddr); -pmd_t *pmd =pmd_offset (pgd, vaddr); - -flush_dcache_addr ((ulong )pgd); -flush_dcache_addr ((ulong )pmd); -flush_dcache_addr ((ulong )pte_offset (pmd, vaddr)); -flush_tlb_page (vaddr); -} -static pteval_t *get_pte (pgd_t *pgtable,uintptr_t vaddr) {pgd_t *pgd =pgd_offset (pgtable, vaddr);
@@ -98,7 +87,7 @@ static pteval_t *install_pte(pgd_t *pgtable, uintptr_t vaddr, pteval_t pte)pteval_t *p_pte =get_pte (pgtable, vaddr); *p_pte = pte; -flush_entry (pgtable, vaddr); +flush_tlb_page (vaddr);return p_pte; }
@@ -148,7 +137,6 @@ void mmu_set_range_sect(pgd_t *pgtable, uintptr_t virt_offset,pgd_val (*pgd) = paddr;pgd_val (*pgd) |=PMD_TYPE_SECT |PMD_SECT_AF |PMD_SECT_S ;pgd_val (*pgd) |=pgprot_val (prot); -flush_dcache_addr ((ulong )pgd);flush_tlb_page (vaddr); } }
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 06/17] lib: arm: Add proper data synchronization barriers for TLBIs
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
We need to issue a DSB before doing TLB invalidation to make sure that the table walker sees the new VA mapping after the TLBI finishes. For flush_tlb_page, we do a DSB ISHST (synchronization barrier for writes in the Inner Shareable domain) because translation table walks are now coherent for arm. For local_flush_tlb_all, we only need to affect the Non-shareable domain, and we do a DSB NSHST. We need a synchronization barrier here, and not a memory ordering barrier, because a table walk is not a memory operation and therefore not affected by the DMB.
For the same reasons, we downgrade the full system DSB after the TLBI to a DSB ISH (synchronization barrier for reads and writes in the Inner Shareable domain), and, respectively, DSB NSH (in the Non-shareable domain).
With these two changes, our TLB maintenance functions now match what Linux does in __flush_tlb_kernel_page, and, respectively, in local_flush_tlb_all.
A similar change was implemented in Linux commit 62cbbc42e001 ("ARM: tlb:
reduce scope of barrier domains for TLB invalidation").
--- lib/arm/asm/mmu.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-)
lib/arm/asm/mmu.h +4 -2
@@ -17,9 +17,10 @@static inline void local_flush_tlb_all (void ) { +dsb (nshst);/* TLBIALL */ asmvolatile ("mcr p15, 0, %0, c8, c7, 0" ::"r" (0 )); -dsb (); +dsb (nsh);isb (); }
@@ -31,9 +32,10 @@ static inline void flush_tlb_all(void)static inline void flush_tlb_page (unsigned long vaddr) { +dsb (ishst);/* TLBIMVAAIS */ asmvolatile ("mcr p15, 0, %0, c8, c3, 3" ::"r" (vaddr)); -dsb (); +dsb (ish);isb (); }
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 07/17] lib: Add WRITE_ONCE and READ_ONCE implementations in compiler.h
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
Add the WRITE_ONCE and READ_ONCE macros which are used to prevent the compiler from optimizing a store or a load, respectively, into something else.
--- lib/linux/compiler.h | 83 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 lib/linux/compiler.h
lib/linux/compiler.h new file +83 -0
@@ -0,0 +1,83 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* +* Taken from Linux commit 219d54332a09 ("Linux 5.4"), from the file +* tools/include/linux/compiler.h, with minor changes. +*/ +#ifndef __LINUX_COMPILER_H +#define __LINUX_COMPILER_H + +#ifndef __ASSEMBLY__ + +#include <stdint.h> + +#define barrier () asm volatile("" : : : "memory") + +#define __always_inline inline __attribute__((always_inline)) + +static __always_inline void __read_once_size(const volatile void *p, void *res, int size) +{ +switch (size) { +case 1 : *(uint8_t *)res = *(volatile uint8_t *)p;break ; +case 2 : *(uint16_t *)res = *(volatile uint16_t *)p;break ; +case 4 : *(uint32_t *)res = *(volatile uint32_t *)p;break ; +case 8 : *(uint64_t *)res = *(volatile uint64_t *)p;break ; +default : +barrier (); +__builtin_memcpy ((void *)res, (const void *)p, size); +barrier (); + } +} + +/* +* Prevent the compiler from merging or refetching reads or writes. The +* compiler is also forbidden from reordering successive instances of +* READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some +* particular ordering. One way to make the compiler aware of ordering is to +* put the two invocations of READ_ONCE or WRITE_ONCE in different C +* statements. +* +* These two macros will also work on aggregate data types like structs or +* unions. If the size of the accessed data type exceeds the word size of +* the machine (e.g., 32 bits or 64 bits) READ_ONCE() and WRITE_ONCE() will +* fall back to memcpy and print a compile-time warning. +* +* Their two major use cases are: (1) Mediating communication between +* process-level code and irq/NMI handlers, all running on the same CPU, +* and (2) Ensuring that the compiler does not fold, spindle, or otherwise +* mutilate accesses that either do not require ordering or that interact +* with an explicit memory barrier or atomic instruction that provides the +* required ordering. +*/ + +#define READ_ONCE (x) \ +({ \ + union { typeof(x) __val; char __c[1]; } __u = \ + { .__c = { 0 } }; \ + __read_once_size(&(x), __u.__c, sizeof(x)); \ + __u.__val; \ +}) + +static __always_inline void __write_once_size(volatile void *p, void *res, int size) +{ +switch (size) { +case 1 : *(volatile uint8_t *) p = *(uint8_t *) res;break ; +case 2 : *(volatile uint16_t *) p = *(uint16_t *) res;break ; +case 4 : *(volatile uint32_t *) p = *(uint32_t *) res;break ; +case 8 : *(volatile uint64_t *) p = *(uint64_t *) res;break ; +default : +barrier (); +__builtin_memcpy ((void *)p, (const void *)res, size); +barrier (); + } +} + +#define WRITE_ONCE (x, val) \ +({ \ + union { typeof(x) __val; char __c[1]; } __u = \ + { .__val = (val) }; \ + __write_once_size(&(x), __u.__c, sizeof(x)); \ + __u.__val; \ +}) + +#endif /* !__ASSEMBLY__ */ +#endif /* !__LINUX_COMPILER_H */
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 08/17] lib: arm/arm64: Use WRITE_ONCE to update the translation tables
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
Use WRITE_ONCE to prevent store tearing when updating an entry in the translation tables. Without WRITE_ONCE, the compiler, even though it is unlikely, can emit several stores when changing the table, and we might end up with bogus TLB entries.
It's worth noting that the existing code is mostly fine without any changes because the translation tables are updated in one of the following situations:
- When the tables are being created with the MMU off, which means no TLB caching is being performed.
- When new page table entries are added as a result of vmalloc'ing a stack for a secondary CPU, which doesn't happen very often.
- When clearing the PTE_USER bit for the cache test, and store tearing has no effect on the table walker because there are no intermediate values between bit values 0 and 1. We still use WRITE_ONCE in this case for consistency.
However, the functions are global and there is nothing preventing someone from writing a test that uses them in a different scenario. Let's make sure that when that happens, there will be no breakage once in a blue moon.
--- lib/arm/asm/pgtable.h | 12 ++++++++---- lib/arm/mmu.c | 19 +++++++++++++------ lib/arm64/asm/pgtable.h | 7 +++++-- 3 files changed, 26 insertions(+), 12 deletions(-)
lib/arm/asm/pgtable.h +8 -4
@@ -19,6 +19,8 @@* because we always allocate their pages with alloc_page(), and * alloc_page() always returns identity mapped pages. */ +#include <linux/compiler.h> +#define pgtable_va (x) ((void *)(unsigned long)(x))#define pgtable_pa (x) ((unsigned long)(x))
@@ -58,8 +60,9 @@ static inline pmd_t *pmd_alloc_one(void)static inline pmd_t *pmd_alloc (pgd_t *pgd,unsigned long addr) {if (pgd_none (*pgd)) { -pmd_t *pmd =pmd_alloc_one (); -pgd_val (*pgd) =pgtable_pa (pmd) |PMD_TYPE_TABLE ; +pgd_t entry; +pgd_val (entry) =pgtable_pa (pmd_alloc_one ()) |PMD_TYPE_TABLE ; +WRITE_ONCE (*pgd, entry); }return pmd_offset (pgd, addr); }
@@ -84,8 +87,9 @@ static inline pte_t *pte_alloc_one(void)static inline pte_t *pte_alloc (pmd_t *pmd,unsigned long addr) {if (pmd_none (*pmd)) { -pte_t *pte =pte_alloc_one (); -pmd_val (*pmd) =pgtable_pa (pte) |PMD_TYPE_TABLE ; +pmd_t entry; +pmd_val (entry) =pgtable_pa (pte_alloc_one ()) |PMD_TYPE_TABLE ; +WRITE_ONCE (*pmd, entry); }return pte_offset (pmd, addr); }
lib/arm/mmu.c +13 -6
@@ -17,6 +17,8 @@#include <asm/pgtable-hwdef.h> #include <asm/pgtable.h> +#include <linux/compiler.h> +extern unsigned long etext;pgd_t *mmu_idmap;
@@ -86,7 +88,7 @@ static pteval_t *install_pte(pgd_t *pgtable, uintptr_t vaddr, pteval_t pte) {pteval_t *p_pte =get_pte (pgtable, vaddr); - *p_pte = pte; +WRITE_ONCE (*p_pte, pte);flush_tlb_page (vaddr);return p_pte; }
@@ -131,12 +133,15 @@ void mmu_set_range_sect(pgd_t *pgtable, uintptr_t virt_offset,phys_addr_t paddr = phys_start &PGDIR_MASK ;uintptr_t vaddr = virt_offset &PGDIR_MASK ;uintptr_t virt_end = phys_end - paddr + vaddr; +pgd_t *pgd; +pgd_t entry;for (; vaddr < virt_end; vaddr +=PGDIR_SIZE , paddr +=PGDIR_SIZE ) { -pgd_t *pgd =pgd_offset (pgtable, vaddr); -pgd_val (*pgd) = paddr; -pgd_val (*pgd) |=PMD_TYPE_SECT |PMD_SECT_AF |PMD_SECT_S ; -pgd_val (*pgd) |=pgprot_val (prot); +pgd_val (entry) = paddr; +pgd_val (entry) |=PMD_TYPE_SECT |PMD_SECT_AF |PMD_SECT_S ; +pgd_val (entry) |=pgprot_val (prot); + pgd =pgd_offset (pgtable, vaddr); +WRITE_ONCE (*pgd, entry);flush_tlb_page (vaddr); } }
@@ -210,6 +215,7 @@ void mmu_clear_user(unsigned long vaddr) {pgd_t *pgtable;pteval_t *pte; +pteval_t entry;if (!mmu_enabled ())return ;
@@ -217,6 +223,7 @@ void mmu_clear_user(unsigned long vaddr) pgtable =current_thread_info ()->pgtable; pte =get_pte (pgtable, vaddr); - *pte &= ~PTE_USER ; + entry = *pte & ~PTE_USER ; +WRITE_ONCE (*pte, entry);flush_tlb_page (vaddr); }
lib/arm64/asm/pgtable.h +5 -2
@@ -18,6 +18,8 @@#include <asm/page.h> #include <asm/pgtable-hwdef.h> +#include <linux/compiler.h> +/* * We can convert va <=> pa page table addresses with simple casts * because we always allocate their pages with alloc_page(), and
@@ -66,8 +68,9 @@ static inline pte_t *pte_alloc_one(void)static inline pte_t *pte_alloc (pmd_t *pmd,unsigned long addr) {if (pmd_none (*pmd)) { -pte_t *pte =pte_alloc_one (); -pmd_val (*pmd) =pgtable_pa (pte) |PMD_TYPE_TABLE ; +pmd_t entry; +pmd_val (entry) =pgtable_pa (pte_alloc_one ()) |PMD_TYPE_TABLE ; +WRITE_ONCE (*pmd, entry); }return pte_offset (pmd, addr); }
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 04/17] devicetree: Fix the dt_for_each_cpu_node
In reply to Andrew JonesFrom: Zeng Tao <[email protected]>
If the /cpus node contains nodes other than /cpus/cpu*, for example:
/cpus/cpu-map/. The test will issue an unexpected assert error as follow:
[root@localhost]# ./arm-run arm/spinlock-test.flat
qemu-system-aarch64 -nodefaults -machine virt,gic-version=host,accel=kvm
-cpu host -device virtio-serial-device -device virtconsole,chardev=ctd
-chardev testdev,id=ctd -device pci-testdev -display none -serial stdio
-kernel arm/spinlock-test.flat # -initrd /tmp/tmp.mwPLiF4EWm
lib/arm/setup.c:64: assert failed: ret == 0
STACK:In this patch, ignore the non-cpu subnodes instead of return an error.
--- lib/devicetree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
lib/devicetree.c +1 -1
@@ -225,7 +225,7 @@ int dt_for_each_cpu_node(void (*func)(int fdtnode, u64 regval, void *info), prop =fdt_get_property (fdt, cpu,"device_type" , &len);if (prop ==NULL ) -return len; +continue ;if (len !=4 ||strcmp ((char *)prop->data,"cpu" ))continue ;
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 09/17] lib: arm/arm64: Remove unused CPU_OFF parameter
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
The first version of PSCI required an argument for CPU_OFF, the power_state argument, which was removed in version 0.2 of the specification [1]. kvm-unit-tests supports PSCI 0.2, and KVM ignores any CPU_OFF parameters, so let's remove the PSCI_POWER_STATE_TYPE_POWER_DOWN parameter.
[1] ARM DEN 0022D, section 7.3.
--- lib/arm/psci.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
lib/arm/psci.c +1 -3
@@ -40,11 +40,9 @@ int cpu_psci_cpu_boot(unsigned int cpu)return err; } -#define PSCI_POWER_STATE_TYPE_POWER_DOWN (1U << 16)void cpu_psci_cpu_die (void ) { -int err =psci_invoke (PSCI_0_2_FN_CPU_OFF , -PSCI_POWER_STATE_TYPE_POWER_DOWN ,0 ,0 ); +int err =psci_invoke (PSCI_0_2_FN_CPU_OFF ,0 ,0 ,0 );printf ("CPU%d unable to power off (error = %d)\n" ,smp_processor_id (), err); }
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 11/17] lib: arm: Implement flush_tlb_all
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
flush_tlb_all performs a TLBIALL, which invalidates the entire TLB and affects only the executing PE; translation table walks are now Inner Shareable, so execute a TLBIALLIS (invalidate TLB Inner Shareable) instead. TLBIALLIS is the equivalent of TLBIALL [1] when the multiprocessing extensions are implemented, which are mandated by the virtualization extensions.
Also add the necessary barriers to tlb_flush_all and a comment to flush_dcache_addr stating what instruction is uses (unsurprisingly, it's DCCIMVAC, which does a dcache clean and invalidate by VA to PoC).
[1] ARM DDI 0406C.d, section B3.10.6
--- lib/arm/asm/mmu.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-)
lib/arm/asm/mmu.h +6 -2
@@ -26,8 +26,11 @@ static inline void local_flush_tlb_all(void)static inline void flush_tlb_all (void ) { -//TODO -local_flush_tlb_all (); +dsb (ishst); +/* TLBIALLIS */ + asmvolatile ("mcr p15, 0, %0, c8, c3, 0" ::"r" (0 )); +dsb (ish); +isb (); }static inline void flush_tlb_page (unsigned long vaddr)
@@ -41,6 +44,7 @@ static inline void flush_tlb_page(unsigned long vaddr)static inline void flush_dcache_addr (unsigned long vaddr) { +/* DCCIMVAC */ asmvolatile ("mcr p15, 0, %0, c7, c14, 1" ::"r" (vaddr)); }
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 12/17] lib: arm/arm64: Teach mmu_clear_user about block mappings
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
kvm-unit-tests uses block mappings, so let's expand the mmu_clear_user function to handle those as well.
Now that the function knows about block mappings, we cannot simply assume that if an address isn't mapped we can map it as a regular page. Change the semantics of the function to fail quite loudly if the address isn't mapped, and shift the burden on the caller to map the address as a page or block mapping before calling mmu_clear_user.
Also make mmu_clear_user more flexible by adding a pgtable parameter, instead of assuming that the change always applies to the current translation tables.
--- arm/cache.c | 3 ++- lib/arm/asm/mmu-api.h | 2 +- lib/arm/asm/pgtable-hwdef.h | 3 +++ lib/arm/asm/pgtable.h | 7 +++++++ lib/arm/mmu.c | 26 +++++++++++++++++++------- lib/arm64/asm/pgtable-hwdef.h | 3 +++ lib/arm64/asm/pgtable.h | 7 +++++++ 7 files changed, 42 insertions(+), 9 deletions(-)
arm/cache.c +2 -1
@@ -2,6 +2,7 @@#include <alloc_page.h> #include <asm/mmu.h> #include <asm/processor.h> +#include <asm/thread_info.h> #define NTIMES (1 << 16)
@@ -47,7 +48,7 @@ static void check_code_generation(bool dcache_clean, bool icache_inval)bool success;/* Make sure we can execute from a writable page */ -mmu_clear_user ((unsigned long )code); +mmu_clear_user (current_thread_info ()->pgtable, (unsigned long )code); sctlr =read_sysreg (sctlr_el1);if (sctlr &SCTLR_EL1_WXN ) {
lib/arm/asm/mmu-api.h +1 -1
@@ -22,5 +22,5 @@ extern void mmu_set_range_sect(pgd_t *pgtable, uintptr_t virt_offset,extern void mmu_set_range_ptes (pgd_t *pgtable,uintptr_t virt_offset,phys_addr_t phys_start,phys_addr_t phys_end,pgprot_t prot); -extern void mmu_clear_user (unsigned long vaddr); +extern void mmu_clear_user (pgd_t *pgtable,unsigned long vaddr);#endif
lib/arm/asm/pgtable-hwdef.h +3 -0
@@ -14,6 +14,8 @@#define PGDIR_SIZE (_AC(1,UL) << PGDIR_SHIFT)#define PGDIR_MASK (~((1 << PGDIR_SHIFT) - 1)) +#define PGD_VALID (_AT(pgdval_t, 1) << 0) +#define PTRS_PER_PTE 512#define PTRS_PER_PMD 512
@@ -54,6 +56,7 @@#define PMD_TYPE_FAULT (_AT(pmdval_t, 0) << 0)#define PMD_TYPE_TABLE (_AT(pmdval_t, 3) << 0)#define PMD_TYPE_SECT (_AT(pmdval_t, 1) << 0) +#define PMD_SECT_VALID (_AT(pmdval_t, 1) << 0)#define PMD_TABLE_BIT (_AT(pmdval_t, 1) << 1)#define PMD_BIT4 (_AT(pmdval_t, 0))#define PMD_DOMAIN (x) (_AT(pmdval_t, 0))
lib/arm/asm/pgtable.h +7 -0
@@ -29,6 +29,13 @@#define pmd_none (pmd) (!pmd_val(pmd))#define pte_none (pte) (!pte_val(pte)) +#define pgd_valid (pgd) (pgd_val(pgd) & PGD_VALID) +#define pmd_valid (pmd) (pmd_val(pmd) & PMD_SECT_VALID) +#define pte_valid (pte) (pte_val(pte) & L_PTE_VALID) + +#define pmd_huge (pmd) \ + ((pmd_val(pmd) & PMD_TYPE_MASK) == PMD_TYPE_SECT) +#define pgd_index (addr) \ (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))#define pgd_offset (pgtable, addr) ((pgtable) + pgd_index(addr))
lib/arm/mmu.c +19 -7
@@ -211,19 +211,31 @@ unsigned long __phys_to_virt(phys_addr_t addr)return addr; } -void mmu_clear_user (unsigned long vaddr) +void mmu_clear_user (pgd_t *pgtable,unsigned long vaddr) { -pgd_t *pgtable; -pteval_t *pte; -pteval_t entry; +pgd_t *pgd; +pmd_t *pmd; +pte_t *pte;if (!mmu_enabled ())return ; - pgtable =current_thread_info ()->pgtable; - pte =get_pte (pgtable, vaddr); + pgd =pgd_offset (pgtable, vaddr); +assert (pgd_valid (*pgd)); + pmd =pmd_offset (pgd, vaddr); +assert (pmd_valid (*pmd)); + +if (pmd_huge (*pmd)) { +pmd_t entry =__pmd (pmd_val (*pmd) & ~PMD_SECT_USER ); +WRITE_ONCE (*pmd, entry); + goto out_flush_tlb; + } - entry = *pte & ~PTE_USER ; + pte =pte_offset (pmd, vaddr); +assert (pte_valid (*pte)); +pte_t entry =__pte (pte_val (*pte) & ~PTE_USER );WRITE_ONCE (*pte, entry); + +out_flush_tlb:flush_tlb_page (vaddr); }
lib/arm64/asm/pgtable-hwdef.h +3 -0
@@ -22,6 +22,8 @@#define PGDIR_MASK (~(PGDIR_SIZE-1))#define PTRS_PER_PGD (1 << (VA_BITS - PGDIR_SHIFT)) +#define PGD_VALID (_AT(pgdval_t, 1) << 0) +/* From include/asm-generic/pgtable-nopmd.h */ #define PMD_SHIFT PGDIR_SHIFT#define PTRS_PER_PMD 1
@@ -71,6 +73,7 @@#define PTE_TYPE_MASK (_AT(pteval_t, 3) << 0)#define PTE_TYPE_FAULT (_AT(pteval_t, 0) << 0)#define PTE_TYPE_PAGE (_AT(pteval_t, 3) << 0) +#define PTE_VALID (_AT(pteval_t, 1) << 0)#define PTE_TABLE_BIT (_AT(pteval_t, 1) << 1)#define PTE_USER (_AT(pteval_t, 1) << 6)/* AP[1] */ #define PTE_RDONLY (_AT(pteval_t, 1) << 7)/* AP[2] */
lib/arm64/asm/pgtable.h +7 -0
@@ -33,6 +33,13 @@#define pmd_none (pmd) (!pmd_val(pmd))#define pte_none (pte) (!pte_val(pte)) +#define pgd_valid (pgd) (pgd_val(pgd) & PGD_VALID) +#define pmd_valid (pmd) (pmd_val(pmd) & PMD_SECT_VALID) +#define pte_valid (pte) (pte_val(pte) & PTE_VALID) + +#define pmd_huge (pmd) \ + ((pmd_val(pmd) & PMD_TYPE_MASK) == PMD_TYPE_SECT) +#define pgd_index (addr) \ (((addr) >> PGDIR_SHIFT) & (PTRS_PER_PGD - 1))#define pgd_offset (pgtable, addr) ((pgtable) + pgd_index(addr))
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 13/17] arm64: timer: Write to ICENABLER to disable timer IRQ
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
According the Generic Interrupt Controller versions 2, 3 and 4 architecture specifications, a write of 0 to the GIC{D,R}_ISENABLER{,0} registers is ignored; this is also how KVM emulates the corresponding register. Write instead to the ICENABLER register when disabling the timer interrupt.
Note that fortunately for us, the timer test was still working as intended because KVM does the sensible thing and all interrupts are disabled by default when creating a VM.
--- arm/timer.c | 22 +++++++++++----------- lib/arm/asm/gic-v3.h | 1 + lib/arm/asm/gic.h | 1 + 3 files changed, 13 insertions(+), 11 deletions(-)
arm/timer.c +11 -11
@@ -17,6 +17,9 @@#define ARCH_TIMER_CTL_ISTATUS (1 << 2)static void *gic_ispendr; +static void *gic_isenabler; +static void *gic_icenabler; +static bool ptimer_unsupported;static void ptimer_unsupported_handler (struct pt_regs *regs,unsigned int esr)
@@ -132,19 +135,12 @@ static struct timer_info ptimer_info = {static void set_timer_irq_enabled (struct timer_info *info,bool enabled) { -u32 val =0 ; +u32 val =1 <<PPI (info->irq);if (enabled) - val =1 <<PPI (info->irq); - -switch (gic_version ()) { -case 2 : -writel (val,gicv2_dist_base () +GICD_ISENABLER +0 ); -break ; -case 3 : -writel (val,gicv3_sgi_base () +GICR_ISENABLER0 ); -break ; - } +writel (val, gic_isenabler); +else +writel (val, gic_icenabler); }static void irq_handler (struct pt_regs *regs)
@@ -306,9 +302,13 @@ static void test_init(void)switch (gic_version ()) {case 2 : gic_ispendr =gicv2_dist_base () +GICD_ISPENDR ; + gic_isenabler =gicv2_dist_base () +GICD_ISENABLER ; + gic_icenabler =gicv2_dist_base () +GICD_ICENABLER ;break ;case 3 : gic_ispendr =gicv3_sgi_base () +GICD_ISPENDR ; + gic_isenabler =gicv3_sgi_base () +GICR_ISENABLER0 ; + gic_icenabler =gicv3_sgi_base () +GICR_ICENABLER0 ;break ; }
lib/arm/asm/gic-v3.h +1 -0
@@ -31,6 +31,7 @@/* Re-Distributor registers, offsets from SGI_base */ #define GICR_IGROUPR0 GICD_IGROUPR#define GICR_ISENABLER0 GICD_ISENABLER +#define GICR_ICENABLER0 GICD_ICENABLER#define GICR_IPRIORITYR0 GICD_IPRIORITYR#define ICC_SGI1R_AFFINITY_1_SHIFT 16
lib/arm/asm/gic.h +1 -0
@@ -15,6 +15,7 @@#define GICD_IIDR 0x0008#define GICD_IGROUPR 0x0080#define GICD_ISENABLER 0x0100 +#define GICD_ICENABLER 0x0180#define GICD_ISPENDR 0x0200#define GICD_ICPENDR 0x0280#define GICD_ISACTIVER 0x0300
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 17/17] arm: cstart64.S: Remove icache invalidation from asm_mmu_enable
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
According to the ARM ARM [1]:
"In Armv8, any permitted instruction cache implementation can be described as implementing the IVIPT Extension to the Arm architecture.
The formal definition of the Arm IVIPT Extension is that it reduces the instruction cache maintenance requirement to the following condition:
Instruction cache maintenance is required only after writing new data to a PA that holds an instruction".
We never patch instructions in the boot path, so remove the icache invalidation from asm_mmu_enable. Tests that modify instructions (like the cache test) should have their own icache maintenance operations.
[1] ARM DDI 0487E.a, section D5.11.2 "Instruction caches"
--- arm/cstart64.S | 1 - 1 file changed, 1 deletion(-)
arm/cstart64.S +0 -1
@@ -159,7 +159,6 @@ halt: .globl asm_mmu_enable asm_mmu_enable: - ic iallu // I+BTB cache invalidate tlbi vmalle1 // invalidate I + D TLBs dsb nsh
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 14/17] lib: arm/arm64: Refuse to disable the MMU with non-identity stack pointer
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
When the MMU is off, all addresses are physical addresses. If the stack pointer is not an identity mapped address (the virtual address is not the same as the physical address), then we end up trying to access an invalid memory region. This can happen if we call mmu_disable from a secondary CPU, which has its stack allocated from the vmalloc region.
--- lib/arm/mmu.c | 4 ++++ 1 file changed, 4 insertions(+)
lib/arm/mmu.c +4 -0
@@ -68,8 +68,12 @@ void mmu_enable(pgd_t *pgtable)extern void asm_mmu_disable (void );void mmu_disable (void ) { +unsigned long sp = current_stack_pointer;int cpu =current_thread_info ()->cpu; +assert_msg (__virt_to_phys (sp) == sp, +"Attempting to disable MMU with non-identity mapped stack" ); +mmu_mark_disabled (cpu);asm_mmu_disable ();
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 15/17] arm: cstart64.S: Downgrade TLBI to non-shareable in asm_mmu_enable
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
There's really no need to invalidate the TLB entries for all CPUs when enabling the MMU for the current CPU, so use the non-shareable version of the TLBI operation (and downgrade the DSB accordingly).
--- arm/cstart64.S | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
arm/cstart64.S +2 -2
@@ -160,8 +160,8 @@ halt: .globl asm_mmu_enable asm_mmu_enable: ic iallu // I+BTB cache invalidate - tlbi vmalle1is // invalidate I + D TLBs - dsb ish + tlbi vmalle1 // invalidate I + D TLBs + dsb nsh /* TCR */ ldr x1, =TCR_TxSZ(VA_BITS) | \
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 16/17] arm/arm64: Invalidate TLB before enabling MMU
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
Let's invalidate the TLB before enabling the MMU, not after, so we don't accidently use a stale TLB mapping. For arm, we add a TLBIALL operation, which applies only to the PE that executed the instruction [1]. For arm64, we already do that in asm_mmu_enable.
We now find ourselves in a situation where we issue an extra invalidation after asm_mmu_enable returns. Remove this redundant call to tlb_flush_all.
[1] ARM DDI 0406C.d, section B3.10.6
--- arm/cstart.S | 4 ++++ lib/arm/mmu.c | 1 - 2 files changed, 4 insertions(+), 1 deletion(-)
arm/cstart.S +4 -0
@@ -166,6 +166,10 @@ halt: .equ NMRR, 0xff000004 @ MAIR1 (from Linux kernel) .globl asm_mmu_enable asm_mmu_enable: + /* TLBIALL */ + mcr p15, 0, r2, c8, c7, 0 + dsb nsh + /* TTBCR */ ldr r2, =(TTBCR_EAE | \ TTBCR_SH0_SHARED | \
lib/arm/mmu.c +0 -1
@@ -59,7 +59,6 @@ void mmu_enable(pgd_t *pgtable)struct thread_info *info =current_thread_info ();asm_mmu_enable (__pa (pgtable)); -flush_tlb_all (); info->pgtable = pgtable;mmu_mark_enabled (info->cpu);
Signature · 1 line
2.21.0
[PULL kvm-unit-tests 10/17] lib: arm/arm64: Add missing include for alloc_page.h in pgtable.h
In reply to Andrew JonesFrom: Alexandru Elisei <[email protected]>
pgtable.h is used only by mmu.c, where it is included after alloc_page.h.
--- lib/arm/asm/pgtable.h | 1 + lib/arm64/asm/pgtable.h | 1 + 2 files changed, 2 insertions(+)
lib/arm/asm/pgtable.h +1 -0
@@ -13,6 +13,7 @@* * This work is licensed under the terms of the GNU GPL, version 2. */ +#include <alloc_page.h> /* * We can convert va <=> pa page table addresses with simple casts
lib/arm64/asm/pgtable.h +1 -0
@@ -14,6 +14,7 @@* This work is licensed under the terms of the GNU GPL, version 2. */ #include <alloc.h> +#include <alloc_page.h> #include <asm/setup.h> #include <asm/page.h> #include <asm/pgtable-hwdef.h>
Signature · 1 line
2.21.0
Paolo Bonzini
maintainerQuoted 1 line · Andrew Jones
Pulled, thanks.
Paolo