Path: blob/master/tools/testing/selftests/arm64/abi/tpidr2.c
26292 views
// SPDX-License-Identifier: GPL-2.0-only12#include <linux/sched.h>3#include <linux/wait.h>45#include "kselftest.h"67#define SYS_TPIDR2 "S3_3_C13_C0_5"89#define EXPECTED_TESTS 51011static void set_tpidr2(uint64_t val)12{13asm volatile (14"msr " SYS_TPIDR2 ", %0\n"15:16: "r"(val)17: "cc");18}1920static uint64_t get_tpidr2(void)21{22uint64_t val;2324asm volatile (25"mrs %0, " SYS_TPIDR2 "\n"26: "=r"(val)27:28: "cc");2930return val;31}3233/* Processes should start with TPIDR2 == 0 */34static int default_value(void)35{36return get_tpidr2() == 0;37}3839/* If we set TPIDR2 we should read that value */40static int write_read(void)41{42set_tpidr2(getpid());4344return getpid() == get_tpidr2();45}4647/* If we set a value we should read the same value after scheduling out */48static int write_sleep_read(void)49{50set_tpidr2(getpid());5152msleep(100);5354return getpid() == get_tpidr2();55}5657/*58* If we fork the value in the parent should be unchanged and the59* child should start with the same value and be able to set its own60* value.61*/62static int write_fork_read(void)63{64pid_t newpid, waiting, oldpid;65int status;6667set_tpidr2(getpid());6869oldpid = getpid();70newpid = fork();71if (newpid == 0) {72/* In child */73if (get_tpidr2() != oldpid) {74ksft_print_msg("TPIDR2 changed in child: %llx\n",75get_tpidr2());76exit(0);77}7879set_tpidr2(getpid());80if (get_tpidr2() == getpid()) {81exit(1);82} else {83ksft_print_msg("Failed to set TPIDR2 in child\n");84exit(0);85}86}87if (newpid < 0) {88ksft_print_msg("fork() failed: %d\n", newpid);89return 0;90}9192for (;;) {93waiting = waitpid(newpid, &status, 0);9495if (waiting < 0) {96if (errno == EINTR)97continue;98ksft_print_msg("waitpid() failed: %d\n", errno);99return 0;100}101if (waiting != newpid) {102ksft_print_msg("waitpid() returned wrong PID: %d != %d\n",103waiting, newpid);104return 0;105}106107if (!WIFEXITED(status)) {108ksft_print_msg("child did not exit\n");109return 0;110}111112if (getpid() != get_tpidr2()) {113ksft_print_msg("TPIDR2 corrupted in parent\n");114return 0;115}116117return WEXITSTATUS(status);118}119}120121/*122* sys_clone() has a lot of per architecture variation so just define123* it here rather than adding it to nolibc, plus the raw API is a124* little more convenient for this test.125*/126static int sys_clone(unsigned long clone_flags, unsigned long newsp,127int *parent_tidptr, unsigned long tls,128int *child_tidptr)129{130return my_syscall5(__NR_clone, clone_flags, newsp, parent_tidptr, tls,131child_tidptr);132}133134#define __STACK_SIZE (8 * 1024 * 1024)135136/*137* If we clone with CLONE_VM then the value in the parent should138* be unchanged and the child should start with zero and be able to139* set its own value.140*/141static int write_clone_read(void)142{143int parent_tid, child_tid;144pid_t parent, waiting;145int ret, status;146void *stack;147148parent = getpid();149set_tpidr2(parent);150151stack = malloc(__STACK_SIZE);152if (!stack) {153ksft_print_msg("malloc() failed\n");154return 0;155}156157ret = sys_clone(CLONE_VM, (unsigned long)stack + __STACK_SIZE,158&parent_tid, 0, &child_tid);159if (ret == -1) {160ksft_print_msg("clone() failed: %d\n", errno);161return 0;162}163164if (ret == 0) {165/* In child */166if (get_tpidr2() != 0) {167ksft_print_msg("TPIDR2 non-zero in child: %llx\n",168get_tpidr2());169exit(0);170}171172if (gettid() == 0)173ksft_print_msg("Child TID==0\n");174set_tpidr2(gettid());175if (get_tpidr2() == gettid()) {176exit(1);177} else {178ksft_print_msg("Failed to set TPIDR2 in child\n");179exit(0);180}181}182183for (;;) {184waiting = wait4(ret, &status, __WCLONE, NULL);185186if (waiting < 0) {187if (errno == EINTR)188continue;189ksft_print_msg("wait4() failed: %d\n", errno);190return 0;191}192if (waiting != ret) {193ksft_print_msg("wait4() returned wrong PID %d\n",194waiting);195return 0;196}197198if (!WIFEXITED(status)) {199ksft_print_msg("child did not exit\n");200return 0;201}202203if (parent != get_tpidr2()) {204ksft_print_msg("TPIDR2 corrupted in parent\n");205return 0;206}207208return WEXITSTATUS(status);209}210}211212int main(int argc, char **argv)213{214int ret;215216ksft_print_header();217ksft_set_plan(5);218219ksft_print_msg("PID: %d\n", getpid());220221/*222* This test is run with nolibc which doesn't support hwcap and223* it's probably disproportionate to implement so instead check224* for the default vector length configuration in /proc.225*/226ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0);227if (ret >= 0) {228ksft_test_result(default_value(), "default_value\n");229ksft_test_result(write_read, "write_read\n");230ksft_test_result(write_sleep_read, "write_sleep_read\n");231ksft_test_result(write_fork_read, "write_fork_read\n");232ksft_test_result(write_clone_read, "write_clone_read\n");233234} else {235ksft_print_msg("SME support not present\n");236237ksft_test_result_skip("default_value\n");238ksft_test_result_skip("write_read\n");239ksft_test_result_skip("write_sleep_read\n");240ksft_test_result_skip("write_fork_read\n");241ksft_test_result_skip("write_clone_read\n");242}243244ksft_finished();245}246247248