Commit 9ad75a09 authored by Andy Lutomirski's avatar Andy Lutomirski Committed by Thomas Gleixner

selftests/x86/fsgsbase: Test RD/WRGSBASE

This validates that GS and GSBASE are independently preserved across
context switches.

[ chang: Use FSGSBASE instructions directly instead of .byte ]
Signed-off-by: default avatarAndy Lutomirski <luto@kernel.org>
Signed-off-by: default avatarChang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: default avatarThomas Gleixner <tglx@linutronix.de>
Reviewed-by: default avatarAndi Kleen <ak@linux.intel.com>
Cc: Ravi Shankar <ravi.v.shankar@intel.com>
Cc: H. Peter Anvin <hpa@zytor.com>
Link: https://lkml.kernel.org/r/1557309753-24073-15-git-send-email-chang.seok.bae@intel.com
parent 5bf0cab6
...@@ -26,6 +26,7 @@ ...@@ -26,6 +26,7 @@
#include <stddef.h> #include <stddef.h>
#include <sys/ptrace.h> #include <sys/ptrace.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <setjmp.h>
#ifndef __x86_64__ #ifndef __x86_64__
# error This test is 64-bit only # error This test is 64-bit only
...@@ -74,6 +75,43 @@ static void sigsegv(int sig, siginfo_t *si, void *ctx_void) ...@@ -74,6 +75,43 @@ static void sigsegv(int sig, siginfo_t *si, void *ctx_void)
} }
static jmp_buf jmpbuf;
static void sigill(int sig, siginfo_t *si, void *ctx_void)
{
siglongjmp(jmpbuf, 1);
}
static bool have_fsgsbase;
static inline unsigned long rdgsbase(void)
{
unsigned long gsbase;
asm volatile("rdgsbase %0" : "=r" (gsbase) :: "memory");
return gsbase;
}
static inline unsigned long rdfsbase(void)
{
unsigned long fsbase;
asm volatile("rdfsbase %0" : "=r" (fsbase) :: "memory");
return fsbase;
}
static inline void wrgsbase(unsigned long gsbase)
{
asm volatile("wrgsbase %0" :: "r" (gsbase) : "memory");
}
static inline void wrfsbase(unsigned long fsbase)
{
asm volatile("wrfsbase %0" :: "r" (fsbase) : "memory");
}
enum which_base { FS, GS }; enum which_base { FS, GS };
static unsigned long read_base(enum which_base which) static unsigned long read_base(enum which_base which)
...@@ -202,14 +240,16 @@ static void do_remote_base() ...@@ -202,14 +240,16 @@ static void do_remote_base()
to_set, hard_zero ? " and clear gs" : "", sel); to_set, hard_zero ? " and clear gs" : "", sel);
} }
void do_unexpected_base(void) static __thread int set_thread_area_entry_number = -1;
static void do_unexpected_base(void)
{ {
/* /*
* The goal here is to try to arrange for GS == 0, GSBASE != * The goal here is to try to arrange for GS == 0, GSBASE !=
* 0, and for the the kernel the think that GSBASE == 0. * 0, and for the the kernel the think that GSBASE == 0.
* *
* To make the test as reliable as possible, this uses * To make the test as reliable as possible, this uses
* explicit descriptorss. (This is not the only way. This * explicit descriptors. (This is not the only way. This
* could use ARCH_SET_GS with a low, nonzero base, but the * could use ARCH_SET_GS with a low, nonzero base, but the
* relevant side effect of ARCH_SET_GS could change.) * relevant side effect of ARCH_SET_GS could change.)
*/ */
...@@ -242,7 +282,7 @@ void do_unexpected_base(void) ...@@ -242,7 +282,7 @@ void do_unexpected_base(void)
MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0); MAP_PRIVATE | MAP_ANONYMOUS | MAP_32BIT, -1, 0);
memcpy(low_desc, &desc, sizeof(desc)); memcpy(low_desc, &desc, sizeof(desc));
low_desc->entry_number = -1; low_desc->entry_number = set_thread_area_entry_number;
/* 32-bit set_thread_area */ /* 32-bit set_thread_area */
long ret; long ret;
...@@ -257,6 +297,8 @@ void do_unexpected_base(void) ...@@ -257,6 +297,8 @@ void do_unexpected_base(void)
return; return;
} }
printf("\tother thread: using GDT slot %d\n", desc.entry_number); printf("\tother thread: using GDT slot %d\n", desc.entry_number);
set_thread_area_entry_number = desc.entry_number;
asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)((desc.entry_number << 3) | 0x3))); asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)((desc.entry_number << 3) | 0x3)));
} }
...@@ -268,6 +310,34 @@ void do_unexpected_base(void) ...@@ -268,6 +310,34 @@ void do_unexpected_base(void)
asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0)); asm volatile ("mov %0, %%gs" : : "rm" ((unsigned short)0));
} }
void test_wrbase(unsigned short index, unsigned long base)
{
unsigned short newindex;
unsigned long newbase;
printf("[RUN]\tGS = 0x%hx, GSBASE = 0x%lx\n", index, base);
asm volatile ("mov %0, %%gs" : : "rm" (index));
wrgsbase(base);
remote_base = 0;
ftx = 1;
syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
while (ftx != 0)
syscall(SYS_futex, &ftx, FUTEX_WAIT, 1, NULL, NULL, 0);
asm volatile ("mov %%gs, %0" : "=rm" (newindex));
newbase = rdgsbase();
if (newindex == index && newbase == base) {
printf("[OK]\tIndex and base were preserved\n");
} else {
printf("[FAIL]\tAfter switch, GS = 0x%hx and GSBASE = 0x%lx\n",
newindex, newbase);
nerrs++;
}
}
static void *threadproc(void *ctx) static void *threadproc(void *ctx)
{ {
while (1) { while (1) {
...@@ -439,6 +509,17 @@ int main() ...@@ -439,6 +509,17 @@ int main()
{ {
pthread_t thread; pthread_t thread;
/* Probe FSGSBASE */
sethandler(SIGILL, sigill, 0);
if (sigsetjmp(jmpbuf, 1) == 0) {
rdfsbase();
have_fsgsbase = true;
printf("\tFSGSBASE instructions are enabled\n");
} else {
printf("\tFSGSBASE instructions are disabled\n");
}
clearhandler(SIGILL);
sethandler(SIGSEGV, sigsegv, 0); sethandler(SIGSEGV, sigsegv, 0);
check_gs_value(0); check_gs_value(0);
...@@ -485,6 +566,21 @@ int main() ...@@ -485,6 +566,21 @@ int main()
test_unexpected_base(); test_unexpected_base();
if (have_fsgsbase) {
unsigned short ss;
asm volatile ("mov %%ss, %0" : "=rm" (ss));
test_wrbase(0, 0);
test_wrbase(0, 1);
test_wrbase(0, 0x200000000);
test_wrbase(0, 0xffffffffffffffff);
test_wrbase(ss, 0);
test_wrbase(ss, 1);
test_wrbase(ss, 0x200000000);
test_wrbase(ss, 0xffffffffffffffff);
}
ftx = 3; /* Kill the thread. */ ftx = 3; /* Kill the thread. */
syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0); syscall(SYS_futex, &ftx, FUTEX_WAKE, 0, NULL, NULL, 0);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment