Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1756812AbcCUQQV (ORCPT ); Mon, 21 Mar 2016 12:16:21 -0400 Received: from mga02.intel.com ([134.134.136.20]:20008 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751521AbcCUQQS (ORCPT ); Mon, 21 Mar 2016 12:16:18 -0400 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.24,372,1455004800"; d="scan'208";a="928698046" From: Andi Kleen To: x86@kernel.org Cc: luto@amacapital.net, linux-kernel@vger.kernel.org, Andi Kleen Subject: [PATCH 7/9] x86: Add self test code for fsgsbase Date: Mon, 21 Mar 2016 09:16:07 -0700 Message-Id: <1458576969-13309-8-git-send-email-andi@firstfloor.org> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1458576969-13309-1-git-send-email-andi@firstfloor.org> References: <1458576969-13309-1-git-send-email-andi@firstfloor.org> Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 4928 Lines: 189 From: Andi Kleen Add a simple tester. By default it runs 10000 iterations, but can also run forever with tfsgs_64 0 Signed-off-by: Andi Kleen --- tools/testing/selftests/x86/Makefile | 3 +- tools/testing/selftests/x86/tfsgs.c | 151 +++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 tools/testing/selftests/x86/tfsgs.c diff --git a/tools/testing/selftests/x86/Makefile b/tools/testing/selftests/x86/Makefile index d5ce7d7..e4a3ef9 100644 --- a/tools/testing/selftests/x86/Makefile +++ b/tools/testing/selftests/x86/Makefile @@ -9,11 +9,12 @@ TARGETS_C_BOTHBITS := single_step_syscall sysret_ss_attrs syscall_nt ptrace_sysc TARGETS_C_32BIT_ONLY := entry_from_vm86 syscall_arg_fault test_syscall_vdso unwind_vdso \ test_FCMOV test_FCOMI test_FISTTP \ vdso_restorer +TARGETS_C_64BIT_ONLY := tfsgs TARGETS_C_32BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_32BIT_ONLY) TARGETS_C_64BIT_ALL := $(TARGETS_C_BOTHBITS) $(TARGETS_C_64BIT_ONLY) BINARIES_32 := $(TARGETS_C_32BIT_ALL:%=%_32) -BINARIES_64 := $(TARGETS_C_64BIT_ALL:%=%_64) +BINARIES_64 := $(TARGETS_C_64BIT_ALL:%=%_64) $(TARGETS_C_64BIT_ONLY:%=%_64) CFLAGS := -O2 -g -std=gnu99 -pthread -Wall diff --git a/tools/testing/selftests/x86/tfsgs.c b/tools/testing/selftests/x86/tfsgs.c new file mode 100644 index 0000000..15bb472 --- /dev/null +++ b/tools/testing/selftests/x86/tfsgs.c @@ -0,0 +1,151 @@ +/* Test kernel RD/WR FS/GS BASE support + * Run tfsgs 0 to run forever, otherwise iterations (default 10000) + * For stress testing run many in parallel to test context switching too + * + * This program destroys TLS, which means most of normal glibc + * doesn't work. So it uses its own libc replacement. + * + * It also breaks some versions of gdb + * (workaround available in https://sourceware.org/bugzilla/show_bug.cgi?id=19684) + */ +#include +#include +#include +#include +#include +#include +#include + +#ifndef __always_inline +#define __always_inline inline __attribute__((always_inline)) +#endif + +static __always_inline unsigned long rdgsbase(void) +{ + unsigned long gs; + asm volatile(".byte 0xf3,0x48,0x0f,0xae,0xc8 # rdgsbaseq %%rax" + : "=a" (gs) + :: "memory"); + return gs; +} + +static __always_inline unsigned long rdfsbase(void) +{ + unsigned long fs; + asm volatile(".byte 0xf3,0x48,0x0f,0xae,0xc0 # rdfsbaseq %%rax" + : "=a" (fs) + :: "memory"); + return fs; +} + +static __always_inline void wrgsbase(unsigned long gs) +{ + asm volatile(".byte 0xf3,0x48,0x0f,0xae,0xd8 # wrgsbaseq %%rax" + :: "a" (gs) + : "memory"); +} + +static __always_inline void wrfsbase(unsigned long fs) +{ + asm volatile(".byte 0xf3,0x48,0x0f,0xae,0xd0 # wrfsbaseq %%rax" + :: "a" (fs) + : "memory"); +} + +/* Custom assert because we can't access errno with changed fs */ + +int my_strlen(char *s) +{ + int len = 0; + while (*s++) + len++; + return len; +} + +int arch_prctl(int cmd, unsigned long arg) +{ + int ret; + asm volatile("syscall" : "=a" (ret) + : "0" (__NR_arch_prctl), "D" (cmd), "S" (arg) + : "memory", "rcx", "r11"); + return ret; +} + +__attribute__((noinline)) void my_assert(int flag, char *msg) +{ + if (!flag) { + int ret; + asm volatile("syscall" + : "=a" (ret) + : "0" (__NR_write), + "D" (2), "S" (msg), + "d" (my_strlen(msg)) + : "memory", "rcx", "r11"); + *(int *)0 = 0; + } +} + +long iter = 10000; + +#ifndef bit_FSGSBASE +#define bit_FSGSBASE 1 +#endif + +/* Will be eventually in asm/hwcap.h */ +#define HWCAP2_FSGSBASE (1 << 0) + +unsigned long nfs, ngs, x; + +int main(int ac, char **av) +{ + long i; + unsigned a, b, c, d; + + if (__get_cpuid_max(0, NULL) < 7) + exit(0); + __cpuid_count(7, 0, a, b, c, d); + if (!(b & bit_FSGSBASE)) + exit(0); + + /* Kernel support? */ + if (!(getauxval(AT_HWCAP2) & HWCAP2_FSGSBASE)) + exit(0); + + if (av[1]) + iter = strtoul(av[1], NULL, 0); + + srandom(1); + unsigned long count = random(); + unsigned long orig_fs = rdfsbase(); + for (i = 0; i < iter || iter == 0; i++) { + unsigned long x = count++; + x = ((long)(x << 16)) >> 16; /* sign extend 48->64 */ + + wrgsbase(x); + wrfsbase(x); + + int i; + for (i = 0; i < 1000; i++) + asm volatile("pause" ::: "memory"); + + ngs = rdgsbase(); + nfs = rdfsbase(); + + my_assert(ngs == x, "gs check 1 failed\n"); + my_assert(nfs == x, "fs check 1 failed\n"); + + unsigned long n; + const unsigned long MASK = 0x7fffffffffff; + arch_prctl(ARCH_SET_FS, (x + 1) & MASK); + arch_prctl(ARCH_SET_GS, (x - 1) & MASK); + n = rdfsbase(); + my_assert(n == ((x + 1) & MASK), "fs check 2 failed\n"); + + for (i = 0; i < 1000; i++) + asm volatile("pause" ::: "memory"); + + n = rdgsbase(); + my_assert(n == ((x - 1) & MASK), "gs check 2 failed\n"); + } + wrfsbase(orig_fs); +} -- 2.5.5