Message ID | 20170320081628.18952-10-khuey@kylehuey.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Hi Kyle, 2017-03-20 16:16 GMT+08:00 Kyle Huey <me@kylehuey.com>: > Test disabling and reenabling the cpuid instruction via the new arch_prctl > ARCH_SET_CPUID, retrieving the current state via ARCH_GET_CPUID, and the > expected behaviors across fork() and exec(). > > Signed-off-by: Kyle Huey <khuey@kylehuey.com> > --- > tools/testing/selftests/x86/Makefile | 2 +- > tools/testing/selftests/x86/cpuid_fault.c | 251 ++++++++++++++++++++++++++++++ I'm not sure why this commit is not merged to upstream. I test 4.14-rc3 w/ this testcase on a haswell client, however I encounter the below splat, any idea? # ./cpuid_fault_64 cpuid() == {d, 756e6547, 6c65746e, 49656e69} arch_prctl(ARCH_GET_CPUID); ARCH_GET_CPUID is unsupported on this kernel. Regards, Wanpeng Li > 2 files changed, 252 insertions(+), 1 deletion(-) > create mode 100644 tools/testing/selftests/x86/cpuid_fault.c > > diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile > index 38e0a9ca5d71..acda4e5fcf25 100644 > --- a/tools/testing/selftests/x86/Makefile > +++ b/tools/testing/selftests/x86/Makefile > @@ -6,7 +6,7 @@ include ../lib.mk > > TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \ > check_initial_reg_state sigreturn ldt_gdt iopl mpx-mini-test ioperm \ > - protection_keys test_vdso > + protection_keys test_vdso cpuid_fault > TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ > test_FCMOV test_FCOMI test_FISTTP \ > vdso_restorer > diff --git a/tools/testing/selftests/x86/cpuid_fault.c b/tools/testing/selftests/x86/cpuid_fault.c > new file mode 100644 > index 000000000000..e3b93c28c655 > --- /dev/null > +++ b/tools/testing/selftests/x86/cpuid_fault.c > @@ -0,0 +1,251 @@ > + > +/* > + * Tests for arch_prctl(ARCH_GET_CPUID, ...) / arch_prctl(ARCH_SET_CPUID, ...) > + * > + * Basic test to test behaviour of ARCH_GET_CPUID and ARCH_SET_CPUID > + */ > + > +#include <stdio.h> > +#include <stdlib.h> > +#include <unistd.h> > +#include <signal.h> > +#include <inttypes.h> > +#include <cpuid.h> > +#include <err.h> > +#include <errno.h> > +#include <sys/wait.h> > + > +#include <sys/prctl.h> > +#include <linux/prctl.h> > + > +/* > +#define ARCH_GET_CPUID 0x1005 > +#define ARCH_SET_CPUID 0x1006 > +#ifdef __x86_64__ > +#define SYS_arch_prctl 158 > +#else > +#define SYS_arch_prctl 384 > +#endif > +*/ > + > +const char *cpuid_names[] = { > + [0] = "[cpuid disabled]", > + [1] = "[cpuid enabled]", > +}; > + > +int arch_prctl(int option, unsigned long arg2) > +{ > + return syscall(SYS_arch_prctl, option, arg2); > +} > + > +int cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, > + unsigned int *edx) > +{ > + return __get_cpuid(0, eax, ebx, ecx, edx); > +} > + > +int do_child_exec_test(int eax, int ebx, int ecx, int edx) > +{ > + int cpuid_val = 0, child = 0, status = 0; > + > + printf("arch_prctl(ARCH_GET_CPUID); "); > + > + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); > + if (cpuid_val < 0) > + errx(1, "ARCH_GET_CPUID fails now, but not before?"); > + > + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); > + if (cpuid_val != 0) > + errx(1, "How did cpuid get re-enabled on fork?"); > + > + child = fork(); > + if (child == 0) { > + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); > + if (cpuid_val < 0) > + errx(1, "ARCH_GET_CPUID fails now, but not before?"); > + > + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); > + if (cpuid_val != 0) > + errx(1, "How did cpuid get re-enabled on fork?"); > + > + printf("exec\n"); > + execl("/proc/self/exe", "cpuid-fault", "-early-return", NULL); > + } > + > + if (child != waitpid(child, &status, 0)) > + errx(1, "waitpid failed!?"); > + > + if (WEXITSTATUS(status) != 0) > + errx(1, "Execed child exited abnormally"); > + > + return 0; > +} > + > +int child_received_signal; > + > +void child_sigsegv_cb(int sig) > +{ > + int cpuid_val = 0; > + > + child_received_signal = 1; > + printf("[ SIG_SEGV ]\n"); > + printf("arch_prctl(ARCH_GET_CPUID); "); > + > + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); > + if (cpuid_val < 0) > + errx(1, "ARCH_GET_CPUID fails now, but not before?"); > + > + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); > + printf("arch_prctl(ARCH_SET_CPUID, 1)\n"); > + if (arch_prctl(ARCH_SET_CPUID, 1) != 0) > + exit(errno); > + > + printf("cpuid() == "); > +} > + > +int do_child_test(void) > +{ > + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; > + > + signal(SIGSEGV, child_sigsegv_cb); > + > + /* the child starts out with cpuid disabled, the signal handler > + * attempts to enable and retry > + */ > + printf("cpuid() == "); > + cpuid(&eax, &ebx, &ecx, &edx); > + printf("{%x, %x, %x, %x}\n", eax, ebx, ecx, edx); > + return child_received_signal ? 0 : 42; > +} > + > +int signal_count; > + > +void sigsegv_cb(int sig) > +{ > + int cpuid_val = 0; > + > + signal_count++; > + printf("[ SIG_SEGV ]\n"); > + printf("arch_prctl(ARCH_GET_CPUID); "); > + > + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); > + if (cpuid_val < 0) > + errx(1, "ARCH_GET_CPUID fails now, but not before?"); > + > + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); > + printf("arch_prctl(ARC_SET_CPUID, 1)\n"); > + if (arch_prctl(ARCH_SET_CPUID, 1) != 0) > + errx(1, "ARCH_SET_CPUID failed!"); > + > + printf("cpuid() == "); > +} > + > +int main(int argc, char **argv) > +{ > + int cpuid_val = 0, child = 0, status = 0; > + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; > + > + signal(SIGSEGV, sigsegv_cb); > + setvbuf(stdout, NULL, _IONBF, 0); > + > + cpuid(&eax, &ebx, &ecx, &edx); > + printf("cpuid() == {%x, %x, %x, %x}\n", eax, ebx, ecx, edx); > + printf("arch_prctl(ARCH_GET_CPUID); "); > + > + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); > + if (cpuid_val < 0) { > + if (errno == EINVAL) { > + printf("ARCH_GET_CPUID is unsupported on this kernel.\n"); > + fflush(stdout); > + exit(0); /* no ARCH_GET_CPUID on this system */ > + } else if (errno == ENODEV) { > + printf("ARCH_GET_CPUID is unsupported on this hardware.\n"); > + fflush(stdout); > + exit(0); /* no ARCH_GET_CPUID on this system */ > + } else { > + errx(errno, "ARCH_GET_CPUID failed unexpectedly!"); > + } > + } > + > + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); > + cpuid(&eax, &ebx, &ecx, &edx); > + printf("cpuid() == {%x, %x, %x, %x}\n", eax, ebx, ecx, edx); > + printf("arch_prctl(ARCH_SET_CPUID, 1)\n"); > + > + if (arch_prctl(ARCH_SET_CPUID, 1) != 0) { > + if (errno == EINVAL) { > + printf("ARCH_SET_CPUID is unsupported on this kernel."); > + exit(0); /* no ARCH_SET_CPUID on this system */ > + } else if (errno == ENODEV) { > + printf("ARCH_SET_CPUID is unsupported on this hardware."); > + exit(0); /* no ARCH_SET_CPUID on this system */ > + } else { > + errx(errno, "ARCH_SET_CPUID failed unexpectedly!"); > + } > + } > + > + > + cpuid(&eax, &ebx, &ecx, &edx); > + printf("cpuid() == {%x, %x, %x, %x}\n", eax, ebx, ecx, edx); > + printf("arch_prctl(ARCH_SET_CPUID, 0)\n"); > + fflush(stdout); > + > + if (arch_prctl(ARCH_SET_CPUID, 0) == -1) > + errx(1, "ARCH_SET_CPUID failed!"); > + > + printf("cpuid() == "); > + eax = ebx = ecx = edx = 0; > + cpuid(&eax, &ebx, &ecx, &edx); > + printf("{%x, %x, %x, %x}\n", eax, ebx, ecx, edx); > + printf("arch_prctl(ARCH_SET_CPUID, 0)\n"); > + > + if (signal_count != 1) > + errx(1, "cpuid didn't fault!"); > + > + if (arch_prctl(ARCH_SET_CPUID, 0) == -1) > + errx(1, "ARCH_SET_CPUID failed!"); > + > + if (argc > 1) > + exit(0); /* Don't run the whole test again if we were execed */ > + > + printf("do_child_test\n"); > + child = fork(); > + if (child == 0) > + return do_child_test(); > + > + if (child != waitpid(child, &status, 0)) > + errx(1, "waitpid failed!?"); > + > + if (WEXITSTATUS(status) != 0) > + errx(1, "Child exited abnormally!"); > + > + /* The child enabling cpuid should not have affected us */ > + printf("cpuid() == "); > + eax = ebx = ecx = edx = 0; > + cpuid(&eax, &ebx, &ecx, &edx); > + printf("{%x, %x, %x, %x}\n", eax, ebx, ecx, edx); > + printf("arch_prctl(ARCH_SET_CPUID, 0)\n"); > + > + if (signal_count != 2) > + errx(1, "cpuid didn't fault!"); > + > + if (arch_prctl(ARCH_SET_CPUID, 0) == -1) > + errx(1, "ARCH_SET_CPUID failed!"); > + > + /* Our ARCH_CPUID_SIGSEGV should not propagate through exec */ > + printf("do_child_exec_test\n"); > + fflush(stdout); > + > + child = fork(); > + if (child == 0) > + return do_child_exec_test(eax, ebx, ecx, edx); > + > + if (child != waitpid(child, &status, 0)) > + errx(1, "waitpid failed!?"); > + > + if (WEXITSTATUS(status) != 0) > + errx(1, "Child exited abnormally!"); > + > + printf("All tests passed!\n"); > + exit(EXIT_SUCCESS); > +} > -- > 2.11.0 >
On Tue, Oct 10, 2017 at 8:35 PM, Wanpeng Li <kernellwp@gmail.com> wrote: > Hi Kyle, > 2017-03-20 16:16 GMT+08:00 Kyle Huey <me@kylehuey.com>: >> Test disabling and reenabling the cpuid instruction via the new arch_prctl >> ARCH_SET_CPUID, retrieving the current state via ARCH_GET_CPUID, and the >> expected behaviors across fork() and exec(). >> >> Signed-off-by: Kyle Huey <khuey@kylehuey.com> >> --- >> tools/testing/selftests/x86/Makefile | 2 +- >> tools/testing/selftests/x86/cpuid_fault.c | 251 ++++++++++++++++++++++++++++++ > > I'm not sure why this commit is not merged to upstream. I test > 4.14-rc3 w/ this testcase on a haswell client, however I encounter the > below splat, any idea? Thanks for pointing out that this never got merged. That's quite disappointing, especially after reviewers insisted I write this test. The failure you're seeing is because the values of ARCH_GET_CPUID and ARCH_SET_CPUID changed and the values hardcoded in the test are no longer accurate. If you set them to the correct values (0x1011 and 0x1012 respectively) the test should pass. - Kyle > # ./cpuid_fault_64 > cpuid() == {d, 756e6547, 6c65746e, 49656e69} > arch_prctl(ARCH_GET_CPUID); ARCH_GET_CPUID is unsupported on this kernel. > > Regards, > Wanpeng Li > >> 2 files changed, 252 insertions(+), 1 deletion(-) >> create mode 100644 tools/testing/selftests/x86/cpuid_fault.c >> >> diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile >> index 38e0a9ca5d71..acda4e5fcf25 100644 >> --- a/tools/testing/selftests/x86/Makefile >> +++ b/tools/testing/selftests/x86/Makefile >> @@ -6,7 +6,7 @@ include ../lib.mk >> >> TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \ >> check_initial_reg_state sigreturn ldt_gdt iopl mpx-mini-test ioperm \ >> - protection_keys test_vdso >> + protection_keys test_vdso cpuid_fault >> TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ >> test_FCMOV test_FCOMI test_FISTTP \ >> vdso_restorer >> diff --git a/tools/testing/selftests/x86/cpuid_fault.c b/tools/testing/selftests/x86/cpuid_fault.c >> new file mode 100644 >> index 000000000000..e3b93c28c655 >> --- /dev/null >> +++ b/tools/testing/selftests/x86/cpuid_fault.c >> @@ -0,0 +1,251 @@ >> + >> +/* >> + * Tests for arch_prctl(ARCH_GET_CPUID, ...) / arch_prctl(ARCH_SET_CPUID, ...) >> + * >> + * Basic test to test behaviour of ARCH_GET_CPUID and ARCH_SET_CPUID >> + */ >> + >> +#include <stdio.h> >> +#include <stdlib.h> >> +#include <unistd.h> >> +#include <signal.h> >> +#include <inttypes.h> >> +#include <cpuid.h> >> +#include <err.h> >> +#include <errno.h> >> +#include <sys/wait.h> >> + >> +#include <sys/prctl.h> >> +#include <linux/prctl.h> >> + >> +/* >> +#define ARCH_GET_CPUID 0x1005 >> +#define ARCH_SET_CPUID 0x1006 >> +#ifdef __x86_64__ >> +#define SYS_arch_prctl 158 >> +#else >> +#define SYS_arch_prctl 384 >> +#endif >> +*/ >> + >> +const char *cpuid_names[] = { >> + [0] = "[cpuid disabled]", >> + [1] = "[cpuid enabled]", >> +}; >> + >> +int arch_prctl(int option, unsigned long arg2) >> +{ >> + return syscall(SYS_arch_prctl, option, arg2); >> +} >> + >> +int cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, >> + unsigned int *edx) >> +{ >> + return __get_cpuid(0, eax, ebx, ecx, edx); >> +} >> + >> +int do_child_exec_test(int eax, int ebx, int ecx, int edx) >> +{ >> + int cpuid_val = 0, child = 0, status = 0; >> + >> + printf("arch_prctl(ARCH_GET_CPUID); "); >> + >> + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); >> + if (cpuid_val < 0) >> + errx(1, "ARCH_GET_CPUID fails now, but not before?"); >> + >> + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); >> + if (cpuid_val != 0) >> + errx(1, "How did cpuid get re-enabled on fork?"); >> + >> + child = fork(); >> + if (child == 0) { >> + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); >> + if (cpuid_val < 0) >> + errx(1, "ARCH_GET_CPUID fails now, but not before?"); >> + >> + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); >> + if (cpuid_val != 0) >> + errx(1, "How did cpuid get re-enabled on fork?"); >> + >> + printf("exec\n"); >> + execl("/proc/self/exe", "cpuid-fault", "-early-return", NULL); >> + } >> + >> + if (child != waitpid(child, &status, 0)) >> + errx(1, "waitpid failed!?"); >> + >> + if (WEXITSTATUS(status) != 0) >> + errx(1, "Execed child exited abnormally"); >> + >> + return 0; >> +} >> + >> +int child_received_signal; >> + >> +void child_sigsegv_cb(int sig) >> +{ >> + int cpuid_val = 0; >> + >> + child_received_signal = 1; >> + printf("[ SIG_SEGV ]\n"); >> + printf("arch_prctl(ARCH_GET_CPUID); "); >> + >> + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); >> + if (cpuid_val < 0) >> + errx(1, "ARCH_GET_CPUID fails now, but not before?"); >> + >> + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); >> + printf("arch_prctl(ARCH_SET_CPUID, 1)\n"); >> + if (arch_prctl(ARCH_SET_CPUID, 1) != 0) >> + exit(errno); >> + >> + printf("cpuid() == "); >> +} >> + >> +int do_child_test(void) >> +{ >> + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; >> + >> + signal(SIGSEGV, child_sigsegv_cb); >> + >> + /* the child starts out with cpuid disabled, the signal handler >> + * attempts to enable and retry >> + */ >> + printf("cpuid() == "); >> + cpuid(&eax, &ebx, &ecx, &edx); >> + printf("{%x, %x, %x, %x}\n", eax, ebx, ecx, edx); >> + return child_received_signal ? 0 : 42; >> +} >> + >> +int signal_count; >> + >> +void sigsegv_cb(int sig) >> +{ >> + int cpuid_val = 0; >> + >> + signal_count++; >> + printf("[ SIG_SEGV ]\n"); >> + printf("arch_prctl(ARCH_GET_CPUID); "); >> + >> + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); >> + if (cpuid_val < 0) >> + errx(1, "ARCH_GET_CPUID fails now, but not before?"); >> + >> + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); >> + printf("arch_prctl(ARC_SET_CPUID, 1)\n"); >> + if (arch_prctl(ARCH_SET_CPUID, 1) != 0) >> + errx(1, "ARCH_SET_CPUID failed!"); >> + >> + printf("cpuid() == "); >> +} >> + >> +int main(int argc, char **argv) >> +{ >> + int cpuid_val = 0, child = 0, status = 0; >> + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; >> + >> + signal(SIGSEGV, sigsegv_cb); >> + setvbuf(stdout, NULL, _IONBF, 0); >> + >> + cpuid(&eax, &ebx, &ecx, &edx); >> + printf("cpuid() == {%x, %x, %x, %x}\n", eax, ebx, ecx, edx); >> + printf("arch_prctl(ARCH_GET_CPUID); "); >> + >> + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); >> + if (cpuid_val < 0) { >> + if (errno == EINVAL) { >> + printf("ARCH_GET_CPUID is unsupported on this kernel.\n"); >> + fflush(stdout); >> + exit(0); /* no ARCH_GET_CPUID on this system */ >> + } else if (errno == ENODEV) { >> + printf("ARCH_GET_CPUID is unsupported on this hardware.\n"); >> + fflush(stdout); >> + exit(0); /* no ARCH_GET_CPUID on this system */ >> + } else { >> + errx(errno, "ARCH_GET_CPUID failed unexpectedly!"); >> + } >> + } >> + >> + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); >> + cpuid(&eax, &ebx, &ecx, &edx); >> + printf("cpuid() == {%x, %x, %x, %x}\n", eax, ebx, ecx, edx); >> + printf("arch_prctl(ARCH_SET_CPUID, 1)\n"); >> + >> + if (arch_prctl(ARCH_SET_CPUID, 1) != 0) { >> + if (errno == EINVAL) { >> + printf("ARCH_SET_CPUID is unsupported on this kernel."); >> + exit(0); /* no ARCH_SET_CPUID on this system */ >> + } else if (errno == ENODEV) { >> + printf("ARCH_SET_CPUID is unsupported on this hardware."); >> + exit(0); /* no ARCH_SET_CPUID on this system */ >> + } else { >> + errx(errno, "ARCH_SET_CPUID failed unexpectedly!"); >> + } >> + } >> + >> + >> + cpuid(&eax, &ebx, &ecx, &edx); >> + printf("cpuid() == {%x, %x, %x, %x}\n", eax, ebx, ecx, edx); >> + printf("arch_prctl(ARCH_SET_CPUID, 0)\n"); >> + fflush(stdout); >> + >> + if (arch_prctl(ARCH_SET_CPUID, 0) == -1) >> + errx(1, "ARCH_SET_CPUID failed!"); >> + >> + printf("cpuid() == "); >> + eax = ebx = ecx = edx = 0; >> + cpuid(&eax, &ebx, &ecx, &edx); >> + printf("{%x, %x, %x, %x}\n", eax, ebx, ecx, edx); >> + printf("arch_prctl(ARCH_SET_CPUID, 0)\n"); >> + >> + if (signal_count != 1) >> + errx(1, "cpuid didn't fault!"); >> + >> + if (arch_prctl(ARCH_SET_CPUID, 0) == -1) >> + errx(1, "ARCH_SET_CPUID failed!"); >> + >> + if (argc > 1) >> + exit(0); /* Don't run the whole test again if we were execed */ >> + >> + printf("do_child_test\n"); >> + child = fork(); >> + if (child == 0) >> + return do_child_test(); >> + >> + if (child != waitpid(child, &status, 0)) >> + errx(1, "waitpid failed!?"); >> + >> + if (WEXITSTATUS(status) != 0) >> + errx(1, "Child exited abnormally!"); >> + >> + /* The child enabling cpuid should not have affected us */ >> + printf("cpuid() == "); >> + eax = ebx = ecx = edx = 0; >> + cpuid(&eax, &ebx, &ecx, &edx); >> + printf("{%x, %x, %x, %x}\n", eax, ebx, ecx, edx); >> + printf("arch_prctl(ARCH_SET_CPUID, 0)\n"); >> + >> + if (signal_count != 2) >> + errx(1, "cpuid didn't fault!"); >> + >> + if (arch_prctl(ARCH_SET_CPUID, 0) == -1) >> + errx(1, "ARCH_SET_CPUID failed!"); >> + >> + /* Our ARCH_CPUID_SIGSEGV should not propagate through exec */ >> + printf("do_child_exec_test\n"); >> + fflush(stdout); >> + >> + child = fork(); >> + if (child == 0) >> + return do_child_exec_test(eax, ebx, ecx, edx); >> + >> + if (child != waitpid(child, &status, 0)) >> + errx(1, "waitpid failed!?"); >> + >> + if (WEXITSTATUS(status) != 0) >> + errx(1, "Child exited abnormally!"); >> + >> + printf("All tests passed!\n"); >> + exit(EXIT_SUCCESS); >> +} >> -- >> 2.11.0 >>
2017-10-11 11:56 GMT+08:00 Kyle Huey <me@kylehuey.com>: > On Tue, Oct 10, 2017 at 8:35 PM, Wanpeng Li <kernellwp@gmail.com> wrote: >> Hi Kyle, >> 2017-03-20 16:16 GMT+08:00 Kyle Huey <me@kylehuey.com>: >>> Test disabling and reenabling the cpuid instruction via the new arch_prctl >>> ARCH_SET_CPUID, retrieving the current state via ARCH_GET_CPUID, and the >>> expected behaviors across fork() and exec(). >>> >>> Signed-off-by: Kyle Huey <khuey@kylehuey.com> >>> --- >>> tools/testing/selftests/x86/Makefile | 2 +- >>> tools/testing/selftests/x86/cpuid_fault.c | 251 ++++++++++++++++++++++++++++++ >> >> I'm not sure why this commit is not merged to upstream. I test >> 4.14-rc3 w/ this testcase on a haswell client, however I encounter the >> below splat, any idea? > > Thanks for pointing out that this never got merged. That's quite > disappointing, especially after reviewers insisted I write this test. > > The failure you're seeing is because the values of ARCH_GET_CPUID and > ARCH_SET_CPUID changed and the values hardcoded in the test are no > longer accurate. If you set them to the correct values (0x1011 and > 0x1012 respectively) the test should pass. It works, thanks Kyle. Regards, Wanpeng Li
On Tue, 10 Oct 2017, Kyle Huey wrote: > On Tue, Oct 10, 2017 at 8:35 PM, Wanpeng Li <kernellwp@gmail.com> wrote: > > Hi Kyle, > > 2017-03-20 16:16 GMT+08:00 Kyle Huey <me@kylehuey.com>: > >> Test disabling and reenabling the cpuid instruction via the new arch_prctl > >> ARCH_SET_CPUID, retrieving the current state via ARCH_GET_CPUID, and the > >> expected behaviors across fork() and exec(). > >> > >> Signed-off-by: Kyle Huey <khuey@kylehuey.com> > >> --- > >> tools/testing/selftests/x86/Makefile | 2 +- > >> tools/testing/selftests/x86/cpuid_fault.c | 251 ++++++++++++++++++++++++++++++ > > > > I'm not sure why this commit is not merged to upstream. I test > > 4.14-rc3 w/ this testcase on a haswell client, however I encounter the > > below splat, any idea? > > Thanks for pointing out that this never got merged. That's quite > disappointing, especially after reviewers insisted I write this test. Sorry about that. I expected it to be picked up by Shuah and I assumed the same happend the other way round. Can you please resubmit? Thanks, tglx
diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index 38e0a9ca5d71..acda4e5fcf25 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -6,7 +6,7 @@ include ../lib.mk TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_syscall test_mremap_vdso \ check_initial_reg_state sigreturn ldt_gdt iopl mpx-mini-test ioperm \ - protection_keys test_vdso + protection_keys test_vdso cpuid_fault TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ test_FCMOV test_FCOMI test_FISTTP \ vdso_restorer diff --git a/tools/testing/selftests/x86/cpuid_fault.c b/tools/testing/selftests/x86/cpuid_fault.c new file mode 100644 index 000000000000..e3b93c28c655 --- /dev/null +++ b/tools/testing/selftests/x86/cpuid_fault.c @@ -0,0 +1,251 @@ + +/* + * Tests for arch_prctl(ARCH_GET_CPUID, ...) / arch_prctl(ARCH_SET_CPUID, ...) + * + * Basic test to test behaviour of ARCH_GET_CPUID and ARCH_SET_CPUID + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <signal.h> +#include <inttypes.h> +#include <cpuid.h> +#include <err.h> +#include <errno.h> +#include <sys/wait.h> + +#include <sys/prctl.h> +#include <linux/prctl.h> + +/* +#define ARCH_GET_CPUID 0x1005 +#define ARCH_SET_CPUID 0x1006 +#ifdef __x86_64__ +#define SYS_arch_prctl 158 +#else +#define SYS_arch_prctl 384 +#endif +*/ + +const char *cpuid_names[] = { + [0] = "[cpuid disabled]", + [1] = "[cpuid enabled]", +}; + +int arch_prctl(int option, unsigned long arg2) +{ + return syscall(SYS_arch_prctl, option, arg2); +} + +int cpuid(unsigned int *eax, unsigned int *ebx, unsigned int *ecx, + unsigned int *edx) +{ + return __get_cpuid(0, eax, ebx, ecx, edx); +} + +int do_child_exec_test(int eax, int ebx, int ecx, int edx) +{ + int cpuid_val = 0, child = 0, status = 0; + + printf("arch_prctl(ARCH_GET_CPUID); "); + + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); + if (cpuid_val < 0) + errx(1, "ARCH_GET_CPUID fails now, but not before?"); + + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); + if (cpuid_val != 0) + errx(1, "How did cpuid get re-enabled on fork?"); + + child = fork(); + if (child == 0) { + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); + if (cpuid_val < 0) + errx(1, "ARCH_GET_CPUID fails now, but not before?"); + + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); + if (cpuid_val != 0) + errx(1, "How did cpuid get re-enabled on fork?"); + + printf("exec\n"); + execl("/proc/self/exe", "cpuid-fault", "-early-return", NULL); + } + + if (child != waitpid(child, &status, 0)) + errx(1, "waitpid failed!?"); + + if (WEXITSTATUS(status) != 0) + errx(1, "Execed child exited abnormally"); + + return 0; +} + +int child_received_signal; + +void child_sigsegv_cb(int sig) +{ + int cpuid_val = 0; + + child_received_signal = 1; + printf("[ SIG_SEGV ]\n"); + printf("arch_prctl(ARCH_GET_CPUID); "); + + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); + if (cpuid_val < 0) + errx(1, "ARCH_GET_CPUID fails now, but not before?"); + + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); + printf("arch_prctl(ARCH_SET_CPUID, 1)\n"); + if (arch_prctl(ARCH_SET_CPUID, 1) != 0) + exit(errno); + + printf("cpuid() == "); +} + +int do_child_test(void) +{ + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; + + signal(SIGSEGV, child_sigsegv_cb); + + /* the child starts out with cpuid disabled, the signal handler + * attempts to enable and retry + */ + printf("cpuid() == "); + cpuid(&eax, &ebx, &ecx, &edx); + printf("{%x, %x, %x, %x}\n", eax, ebx, ecx, edx); + return child_received_signal ? 0 : 42; +} + +int signal_count; + +void sigsegv_cb(int sig) +{ + int cpuid_val = 0; + + signal_count++; + printf("[ SIG_SEGV ]\n"); + printf("arch_prctl(ARCH_GET_CPUID); "); + + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); + if (cpuid_val < 0) + errx(1, "ARCH_GET_CPUID fails now, but not before?"); + + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); + printf("arch_prctl(ARC_SET_CPUID, 1)\n"); + if (arch_prctl(ARCH_SET_CPUID, 1) != 0) + errx(1, "ARCH_SET_CPUID failed!"); + + printf("cpuid() == "); +} + +int main(int argc, char **argv) +{ + int cpuid_val = 0, child = 0, status = 0; + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; + + signal(SIGSEGV, sigsegv_cb); + setvbuf(stdout, NULL, _IONBF, 0); + + cpuid(&eax, &ebx, &ecx, &edx); + printf("cpuid() == {%x, %x, %x, %x}\n", eax, ebx, ecx, edx); + printf("arch_prctl(ARCH_GET_CPUID); "); + + cpuid_val = arch_prctl(ARCH_GET_CPUID, 0); + if (cpuid_val < 0) { + if (errno == EINVAL) { + printf("ARCH_GET_CPUID is unsupported on this kernel.\n"); + fflush(stdout); + exit(0); /* no ARCH_GET_CPUID on this system */ + } else if (errno == ENODEV) { + printf("ARCH_GET_CPUID is unsupported on this hardware.\n"); + fflush(stdout); + exit(0); /* no ARCH_GET_CPUID on this system */ + } else { + errx(errno, "ARCH_GET_CPUID failed unexpectedly!"); + } + } + + printf("cpuid_val == %s\n", cpuid_names[cpuid_val]); + cpuid(&eax, &ebx, &ecx, &edx); + printf("cpuid() == {%x, %x, %x, %x}\n", eax, ebx, ecx, edx); + printf("arch_prctl(ARCH_SET_CPUID, 1)\n"); + + if (arch_prctl(ARCH_SET_CPUID, 1) != 0) { + if (errno == EINVAL) { + printf("ARCH_SET_CPUID is unsupported on this kernel."); + exit(0); /* no ARCH_SET_CPUID on this system */ + } else if (errno == ENODEV) { + printf("ARCH_SET_CPUID is unsupported on this hardware."); + exit(0); /* no ARCH_SET_CPUID on this system */ + } else { + errx(errno, "ARCH_SET_CPUID failed unexpectedly!"); + } + } + + + cpuid(&eax, &ebx, &ecx, &edx); + printf("cpuid() == {%x, %x, %x, %x}\n", eax, ebx, ecx, edx); + printf("arch_prctl(ARCH_SET_CPUID, 0)\n"); + fflush(stdout); + + if (arch_prctl(ARCH_SET_CPUID, 0) == -1) + errx(1, "ARCH_SET_CPUID failed!"); + + printf("cpuid() == "); + eax = ebx = ecx = edx = 0; + cpuid(&eax, &ebx, &ecx, &edx); + printf("{%x, %x, %x, %x}\n", eax, ebx, ecx, edx); + printf("arch_prctl(ARCH_SET_CPUID, 0)\n"); + + if (signal_count != 1) + errx(1, "cpuid didn't fault!"); + + if (arch_prctl(ARCH_SET_CPUID, 0) == -1) + errx(1, "ARCH_SET_CPUID failed!"); + + if (argc > 1) + exit(0); /* Don't run the whole test again if we were execed */ + + printf("do_child_test\n"); + child = fork(); + if (child == 0) + return do_child_test(); + + if (child != waitpid(child, &status, 0)) + errx(1, "waitpid failed!?"); + + if (WEXITSTATUS(status) != 0) + errx(1, "Child exited abnormally!"); + + /* The child enabling cpuid should not have affected us */ + printf("cpuid() == "); + eax = ebx = ecx = edx = 0; + cpuid(&eax, &ebx, &ecx, &edx); + printf("{%x, %x, %x, %x}\n", eax, ebx, ecx, edx); + printf("arch_prctl(ARCH_SET_CPUID, 0)\n"); + + if (signal_count != 2) + errx(1, "cpuid didn't fault!"); + + if (arch_prctl(ARCH_SET_CPUID, 0) == -1) + errx(1, "ARCH_SET_CPUID failed!"); + + /* Our ARCH_CPUID_SIGSEGV should not propagate through exec */ + printf("do_child_exec_test\n"); + fflush(stdout); + + child = fork(); + if (child == 0) + return do_child_exec_test(eax, ebx, ecx, edx); + + if (child != waitpid(child, &status, 0)) + errx(1, "waitpid failed!?"); + + if (WEXITSTATUS(status) != 0) + errx(1, "Child exited abnormally!"); + + printf("All tests passed!\n"); + exit(EXIT_SUCCESS); +}
Test disabling and reenabling the cpuid instruction via the new arch_prctl ARCH_SET_CPUID, retrieving the current state via ARCH_GET_CPUID, and the expected behaviors across fork() and exec(). Signed-off-by: Kyle Huey <khuey@kylehuey.com> --- tools/testing/selftests/x86/Makefile | 2 +- tools/testing/selftests/x86/cpuid_fault.c | 251 ++++++++++++++++++++++++++++++ 2 files changed, 252 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/x86/cpuid_fault.c