Path: blob/master/tools/testing/selftests/arm64/abi/tpidr2.c
51433 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 syscall(__NR_clone, clone_flags, newsp, parent_tidptr, tls, child_tidptr);131}132133#define __STACK_SIZE (8 * 1024 * 1024)134135/*136* If we clone with CLONE_VM then the value in the parent should137* be unchanged and the child should start with zero and be able to138* set its own value.139*/140static int write_clone_read(void)141{142int parent_tid, child_tid;143pid_t parent, waiting;144int ret, status;145void *stack;146147parent = getpid();148set_tpidr2(parent);149150stack = malloc(__STACK_SIZE);151if (!stack) {152ksft_print_msg("malloc() failed\n");153return 0;154}155156ret = sys_clone(CLONE_VM, (unsigned long)stack + __STACK_SIZE,157&parent_tid, 0, &child_tid);158if (ret == -1) {159ksft_print_msg("clone() failed: %d\n", errno);160return 0;161}162163if (ret == 0) {164/* In child */165if (get_tpidr2() != 0) {166ksft_print_msg("TPIDR2 non-zero in child: %llx\n",167get_tpidr2());168exit(0);169}170171if (gettid() == 0)172ksft_print_msg("Child TID==0\n");173set_tpidr2(gettid());174if (get_tpidr2() == gettid()) {175exit(1);176} else {177ksft_print_msg("Failed to set TPIDR2 in child\n");178exit(0);179}180}181182for (;;) {183waiting = waitpid(ret, &status, __WCLONE);184185if (waiting < 0) {186if (errno == EINTR)187continue;188ksft_print_msg("waitpid() failed: %d\n", errno);189return 0;190}191if (waiting != ret) {192ksft_print_msg("waitpid() returned wrong PID %d\n",193waiting);194return 0;195}196197if (!WIFEXITED(status)) {198ksft_print_msg("child did not exit\n");199return 0;200}201202if (parent != get_tpidr2()) {203ksft_print_msg("TPIDR2 corrupted in parent\n");204return 0;205}206207return WEXITSTATUS(status);208}209}210211int main(int argc, char **argv)212{213int ret;214215ksft_print_header();216ksft_set_plan(5);217218ksft_print_msg("PID: %d\n", getpid());219220/*221* This test is run with nolibc which doesn't support hwcap and222* it's probably disproportionate to implement so instead check223* for the default vector length configuration in /proc.224*/225ret = open("/proc/sys/abi/sme_default_vector_length", O_RDONLY, 0);226if (ret >= 0) {227ksft_test_result(default_value(), "default_value\n");228ksft_test_result(write_read(), "write_read\n");229ksft_test_result(write_sleep_read(), "write_sleep_read\n");230ksft_test_result(write_fork_read(), "write_fork_read\n");231ksft_test_result(write_clone_read(), "write_clone_read\n");232233} else {234ksft_print_msg("SME support not present\n");235236ksft_test_result_skip("default_value\n");237ksft_test_result_skip("write_read\n");238ksft_test_result_skip("write_sleep_read\n");239ksft_test_result_skip("write_fork_read\n");240ksft_test_result_skip("write_clone_read\n");241}242243ksft_finished();244}245246247