root/tools/testing/selftests/arm64/gcs/gcs-stress-thread.S
// Program that loops for ever doing lots of recursions and system calls,
// intended to be used as part of a stress test for GCS context switching.
//
// Copyright 2015-2023 Arm Ltd

#include <asm/unistd.h>

#define sa_sz 32
#define sa_flags 8
#define sa_handler 0
#define sa_mask_sz 8

#define si_code 8

#define SIGINT 2
#define SIGABRT 6
#define SIGUSR1 10
#define SIGSEGV 11
#define SIGUSR2 12
#define SIGTERM 15
#define SEGV_CPERR 10

#define SA_NODEFER 1073741824
#define SA_SIGINFO 4
#define ucontext_regs 184

#define PR_SET_SHADOW_STACK_STATUS      75
# define PR_SHADOW_STACK_ENABLE         (1UL << 0)

#define GCSPR_EL0 S3_3_C2_C5_1

.macro function name
        .macro endfunction
                .type \name, @function
                .purgem endfunction
        .endm
\name:
.endm

// Print a single character x0 to stdout
// Clobbers x0-x2,x8
function putc
        str     x0, [sp, #-16]!

        mov     x0, #1                  // STDOUT_FILENO
        mov     x1, sp
        mov     x2, #1
        mov     x8, #__NR_write
        svc     #0

        add     sp, sp, #16
        ret
endfunction
.globl  putc

// Print a NUL-terminated string starting at address x0 to stdout
// Clobbers x0-x3,x8
function puts
        mov     x1, x0

        mov     x2, #0
0:      ldrb    w3, [x0], #1
        cbz     w3, 1f
        add     x2, x2, #1
        b       0b

1:      mov     w0, #1                  // STDOUT_FILENO
        mov     x8, #__NR_write
        svc     #0

        ret
endfunction
.globl  puts

// Utility macro to print a literal string
// Clobbers x0-x4,x8
.macro puts string
        .pushsection .rodata.str1.1, "aMS", @progbits, 1
.L__puts_literal\@: .string "\string"
        .popsection

        ldr     x0, =.L__puts_literal\@
        bl      puts
.endm

// Print an unsigned decimal number x0 to stdout
// Clobbers x0-x4,x8
function putdec
        mov     x1, sp
        str     x30, [sp, #-32]!        // Result can't be > 20 digits

        mov     x2, #0
        strb    w2, [x1, #-1]!          // Write the NUL terminator

        mov     x2, #10
0:      udiv    x3, x0, x2              // div-mod loop to generate the digits
        msub    x0, x3, x2, x0
        add     w0, w0, #'0'
        strb    w0, [x1, #-1]!
        mov     x0, x3
        cbnz    x3, 0b

        ldrb    w0, [x1]
        cbnz    w0, 1f
        mov     w0, #'0'                // Print "0" for 0, not ""
        strb    w0, [x1, #-1]!

1:      mov     x0, x1
        bl      puts

        ldr     x30, [sp], #32
        ret
endfunction
.globl  putdec

// Print an unsigned decimal number x0 to stdout, followed by a newline
// Clobbers x0-x5,x8
function putdecn
        mov     x5, x30

        bl      putdec
        mov     x0, #'\n'
        bl      putc

        ret     x5
endfunction
.globl  putdecn

// Fill x1 bytes starting at x0 with 0.
// Clobbers x1, x2.
function memclr
        mov     w2, #0
endfunction
.globl  memclr
        // fall through to memfill

// Trivial memory fill: fill x1 bytes starting at address x0 with byte w2
// Clobbers x1
function memfill
        cmp     x1, #0
        b.eq    1f

0:      strb    w2, [x0], #1
        subs    x1, x1, #1
        b.ne    0b

1:      ret
endfunction
.globl  memfill

// 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       abort

1:      ldr     x30, [sp], #((sa_sz + 15) / 16 * 16 + 16)
        ret
endfunction


function tickle_handler
        // Perhaps collect GCSPR_EL0 here in future?
        ret
endfunction

function terminate_handler
        mov     w21, w0
        mov     x20, x2

        puts    "Terminated by signal "
        mov     w0, w21
        bl      putdec
        puts    ", no error\n"

        mov     x0, #0
        mov     x8, #__NR_exit
        svc     #0
endfunction

function segv_handler
        // stash the siginfo_t *
        mov     x20, x1

        // Disable GCS, we don't want additional faults logging things
        mov     x0, PR_SET_SHADOW_STACK_STATUS
        mov     x1, xzr
        mov     x2, xzr
        mov     x3, xzr
        mov     x4, xzr
        mov     x5, xzr
        mov     x8, #__NR_prctl
        svc     #0

        puts    "Got SIGSEGV code "

        ldr     x21, [x20, #si_code]
        mov     x0, x21
        bl      putdec

        // GCS faults should have si_code SEGV_CPERR
        cmp     x21, #SEGV_CPERR
        bne     1f

        puts    " (GCS violation)"
1:
        mov     x0, '\n'
        bl      putc
        b       abort
endfunction

// Recurse x20 times
.macro recurse id
function recurse\id
        stp     x29, x30, [sp, #-16]!
        mov     x29, sp

        cmp     x20, 0
        beq     1f
        sub     x20, x20, 1
        bl      recurse\id

1:
        ldp     x29, x30, [sp], #16

        // Do a syscall immediately prior to returning to try to provoke
        // scheduling and migration at a point where coherency issues
        // might trigger.
        mov     x8, #__NR_getpid
        svc     #0

        ret
endfunction
.endm

// Generate and use two copies so we're changing the GCS contents
recurse 1
recurse 2

.globl _start
function _start
        // Run with GCS
        mov     x0, PR_SET_SHADOW_STACK_STATUS
        mov     x1, PR_SHADOW_STACK_ENABLE
        mov     x2, xzr
        mov     x3, xzr
        mov     x4, xzr
        mov     x5, xzr
        mov     x8, #__NR_prctl
        svc     #0
        cbz     x0, 1f
        puts    "Failed to enable GCS\n"
        b       abort
1:

        mov     w0, #SIGTERM
        adr     x1, terminate_handler
        mov     w2, #SA_SIGINFO
        bl      setsignal

        mov     w0, #SIGUSR1
        adr     x1, tickle_handler
        mov     w2, #SA_SIGINFO
        orr     w2, w2, #SA_NODEFER
        bl      setsignal

        mov     w0, #SIGSEGV
        adr     x1, segv_handler
        mov     w2, #SA_SIGINFO
        orr     w2, w2, #SA_NODEFER
        bl      setsignal

        puts    "Running\n"

loop:
        // Small recursion depth so we're frequently flipping between
        // the two recursors and changing what's on the stack
        mov     x20, #5
        bl      recurse1
        mov     x20, #5
        bl      recurse2
        b       loop
endfunction

abort:
        mov     x0, #255
        mov     x8, #__NR_exit
        svc     #0