// SPDX-License-Identifier: GPL-2.0-only // Copyright (C) 2021-2 ARM Limited. // Original author: Mark Brown <broonie@kernel.org> // // Scalable Matrix Extension ZT context switch test // Repeatedly writes unique test patterns into ZT0 // and reads them back to verify integrity. #include <asm/unistd.h> #include "assembler.h" #include "asm-offsets.h" #include "sme-inst.h" .arch_extension sve #define ZT_SZ 512 #define ZT_B (ZT_SZ / 8) // Declare some storage space to shadow ZT register contents and a // scratch buffer. .pushsection .text .data .align 4 ztref: .space ZT_B scratch: .space ZT_B .popsection // Generate a test pattern for storage in ZT // x0: pid // x1: generation // These values are used to construct a 32-bit pattern that is repeated in the // scratch buffer as many times as will fit: // bits 31:24 generation number (increments once per test_loop) // bits 23: 8 pid // bits 7: 0 32-bit lane index function pattern mov w3, wzr bfi w3, w0, #8, #16 // PID bfi w3, w1, #24, #8 // Generation ldr x0, =scratch mov w1, #ZT_B / 4 0: str w3, [x0], #4 add w3, w3, #1 // Lane subs w1, w1, #1 b.ne 0b ret endfunction // Set up test pattern in a ZT horizontal vector // x0: pid // x1: generation function setup_zt mov x4, x30 bl pattern // Get pattern in scratch buffer ldr x0, =ztref ldr x1, =scratch mov x2, #ZT_B bl memcpy ldr x0, =ztref _ldr_zt 0 // load zt0 from pointer x0 ret x4 endfunction // Trivial memory compare: compare x2 bytes starting at address x0 with // bytes starting at address x1. // Returns only if all bytes match; otherwise, the program is aborted. // Clobbers x0-x5. function memcmp cbz x2, 2f stp x0, x1, [sp, #-0x20]! str x2, [sp, #0x10] mov x5, #0 0: ldrb w3, [x0, x5] ldrb w4, [x1, x5] add x5, x5, #1 cmp w3, w4 b.ne 1f subs x2, x2, #1 b.ne 0b 1: ldr x2, [sp, #0x10] ldp x0, x1, [sp], #0x20 b.ne barf 2: ret endfunction // Verify that a ZT vector matches its shadow in memory, else abort // Clobbers x0-x3 function check_zt mov x3, x30 ldr x0, =scratch // Poison scratch mov x1, #ZT_B bl memfill_ae ldr x0, =scratch _str_zt 0 ldr x0, =ztref ldr x1, =scratch mov x2, #ZT_B mov x30, x3 b memcmp endfunction // Any SME register modified here can cause corruption in the main // thread -- but *only* the locations modified here. function irritator_handler // Increment the irritation signal count (x23): ldr x0, [x2, #ucontext_regs + 8 * 23] add x0, x0, #1 str x0, [x2, #ucontext_regs + 8 * 23] // Corrupt some random ZT data #if 0 adr x0, .text + (irritator_handler - .text) / 16 * 16 movi v0.8b, #1 movi v9.16b, #2 movi v31.8b, #3 #endif ret endfunction function tickle_handler // Increment the signal count (x23): ldr x0, [x2, #ucontext_regs + 8 * 23] add x0, x0, #1 str x0, [x2, #ucontext_regs + 8 * 23] ret endfunction function terminate_handler mov w21, w0 mov x20, x2 puts "Terminated by signal " mov w0, w21 bl putdec puts ", no error, iterations=" ldr x0, [x20, #ucontext_regs + 8 * 22] bl putdec puts ", signals=" ldr x0, [x20, #ucontext_regs + 8 * 23] bl putdecn mov x0, #0 mov x8, #__NR_exit svc #0 endfunction // w0: signal number // x1: sa_action // w2: sa_flags // Clobbers x0-x6,x8 function setsignal str x30, [sp, #-((sa_sz + 15) / 16 * 16 + 16)]! mov w4, w0 mov x5, x1 mov w6, w2 add x0, sp, #16 mov x1, #sa_sz bl memclr mov w0, w4 add x1, sp, #16 str w6, [x1, #sa_flags] str x5, [x1, #sa_handler] mov x2, #0 mov x3, #sa_mask_sz mov x8, #__NR_rt_sigaction svc #0 cbz w0, 1f puts "sigaction failure\n" b .Labort 1: ldr x30, [sp], #((sa_sz + 15) / 16 * 16 + 16) ret endfunction // Main program entry point .globl _start function _start mov x23, #0 // signal count mov w0, #SIGINT adr x1, terminate_handler mov w2, #SA_SIGINFO bl setsignal mov w0, #SIGTERM adr x1, terminate_handler mov w2, #SA_SIGINFO bl setsignal mov w0, #SIGUSR1 adr x1, irritator_handler mov w2, #SA_SIGINFO orr w2, w2, #SA_NODEFER bl setsignal mov w0, #SIGUSR2 adr x1, tickle_handler mov w2, #SA_SIGINFO orr w2, w2, #SA_NODEFER bl setsignal smstart_za // Obtain our PID, to ensure test pattern uniqueness between processes mov x8, #__NR_getpid svc #0 mov x20, x0 puts "PID:\t" mov x0, x20 bl putdecn mov x22, #0 // generation number, increments per iteration .Ltest_loop: mov x0, x20 mov x1, x22 bl setup_zt mov x8, #__NR_sched_yield // Encourage preemption svc #0 mrs x0, S3_3_C4_C2_2 // SVCR should have ZA=1,SM=0 and x1, x0, #3 cmp x1, #2 b.ne svcr_barf bl check_zt add x22, x22, #1 // Everything still working b .Ltest_loop .Labort: mov x0, #0 mov x1, #SIGABRT mov x8, #__NR_kill svc #0 endfunction function barf // fpsimd.c acitivty log dump hack // ldr w0, =0xdeadc0de // mov w8, #__NR_exit // svc #0 // end hack smstop mov x10, x0 // expected data mov x11, x1 // actual data mov x12, x2 // data size puts "Mismatch: PID=" mov x0, x20 bl putdec puts ", iteration=" mov x0, x22 bl putdec puts "\tExpected [" mov x0, x10 mov x1, x12 bl dumphex puts "]\n\tGot [" mov x0, x11 mov x1, x12 bl dumphex puts "]\n" mov x8, #__NR_getpid svc #0 // fpsimd.c acitivty log dump hack // ldr w0, =0xdeadc0de // mov w8, #__NR_exit // svc #0 // ^ end of hack mov x1, #SIGABRT mov x8, #__NR_kill svc #0 // mov x8, #__NR_exit // mov x1, #1 // svc #0 endfunction function svcr_barf mov x10, x0 puts "Bad SVCR: " mov x0, x10 bl putdecn mov x8, #__NR_exit mov x1, #1 svc #0 endfunction