| 1 | /* |
| 2 | * Verify the COMMPAGE emulation |
| 3 | * |
| 4 | * The ARM commpage is a set of user space helper functions provided |
| 5 | * by the kernel in an effort to ease portability of user space code |
| 6 | * between different CPUs with potentially different capabilities. It |
| 7 | * is a 32 bit invention and similar to the vdso segment in many ways. |
| 8 | * |
| 9 | * The ABI is documented in the Linux kernel: |
| 10 | * Documentation/arm/kernel_userspace_helpers.rst |
| 11 | * |
| 12 | * Copyright (c) 2020 Linaro Ltd |
| 13 | * |
| 14 | * SPDX-License-Identifier: GPL-2.0-or-later |
| 15 | */ |
| 16 | |
| 17 | #include <stdlib.h> |
| 18 | #include <stdio.h> |
| 19 | #include <stdint.h> |
| 20 | |
| 21 | #define ARM_COMMPAGE (0xffff0f00u) |
| 22 | #define ARM_KUSER_VERSION (*(int32_t *)(ARM_COMMPAGE + 0xfc)) |
| 23 | typedef void * (get_tls_fn)(void); |
| 24 | #define ARM_KUSER_GET_TLS (*(get_tls_fn *)(ARM_COMMPAGE + 0xe0)) |
| 25 | typedef int (cmpxchg_fn)(int oldval, int newval, volatile int *ptr); |
| 26 | #define ARM_KUSER_CMPXCHG (*(cmpxchg_fn *)(ARM_COMMPAGE + 0xc0)) |
| 27 | typedef void (dmb_fn)(void); |
| 28 | #define ARM_KUSER_DMB (*(dmb_fn *)(ARM_COMMPAGE + 0xa0)) |
| 29 | typedef int (cmpxchg64_fn)(const int64_t *oldval, |
| 30 | const int64_t *newval, |
| 31 | volatile int64_t *ptr); |
| 32 | #define ARM_KUSER_CMPXCHG64 (*(cmpxchg64_fn *)(ARM_COMMPAGE + 0x60)) |
| 33 | |
| 34 | #define fail_unless(x) \ |
| 35 | do { \ |
| 36 | if (!(x)) { \ |
| 37 | fprintf(stderr, "FAILED at %s:%d\n", __FILE__, __LINE__); \ |
| 38 | exit(EXIT_FAILURE); \ |
| 39 | } \ |
| 40 | } while (0) |
| 41 | |
| 42 | |
| 43 | int main(int argc, char *argv[argc]) |
| 44 | { |
| 45 | void *kuser_tls; |
| 46 | int val = 1; |
| 47 | const int64_t oldval = 1, newval = 2; |
| 48 | int64_t val64 = 1; |
| 49 | |
| 50 | fail_unless(ARM_KUSER_VERSION == 0x5); |
| 51 | kuser_tls = ARM_KUSER_GET_TLS(); |
| 52 | printf("TLS = %p\n", kuser_tls); |
| 53 | fail_unless(kuser_tls != 0); |
| 54 | fail_unless(ARM_KUSER_CMPXCHG(1, 2, &val) == 0); |
| 55 | printf("val = %d\n", val); |
| 56 | /* this is a crash test, not checking an actual barrier occurs */ |
| 57 | ARM_KUSER_DMB(); |
| 58 | fail_unless(ARM_KUSER_CMPXCHG64(&oldval, &newval, &val64) == 0); |
| 59 | printf("val64 = %lld\n", val64); |
| 60 | return 0; |
| 61 | } |