[PATCH v7 11/18] x86/fsgsbase/64: Introduce the FIND_PERCPU_BASE macro

From: Chang S. Bae
Date: Wed May 08 2019 - 13:04:15 EST


On the paranoid entry, by far, SWAPGS is needed when entering from user
space. A positive GSBASE means it is a user value and a negative GSBASE
means it is a kernel value. When FSGSBASE is enabled, however, user
space can write arbitrary values to GSBASE, so it will break the
existing logic to differentiate kernel and user GSBASE.

So, what the new GSBASE handling needs to do on the entry is:

original_GSBASE = RDGSBASE()
kernel_GSBASE = FIND_PERCPU_BASE()
WRGSBASE(kernel_GSBASE)

The new macro will be useful to retrieve the kernel GSBASE, and will be
used on following patches.

The way used to retrieve per-CPU base is reading the per_cpu_offset
table with a CPU NR as an index. The CPU NR is obtained by the RDPID
instruction, or (if not available) it can be extracted from the limit
field of the CPUNODE entry in GDT.

Also, the GAS-compatible RDPID macro is added, as binutils 2.27 seems
to start to support the instruction. The minimum build requirement for
binutils is the 2.21 version, at this point.

Suggested-by: H. Peter Anvin <hpa@xxxxxxxxx>
Signed-off-by: Chang S. Bae <chang.seok.bae@xxxxxxxxx>
Cc: Andy Lutomirski <luto@xxxxxxxxxx>
Cc: Thomas Gleixner <tglx@xxxxxxxxxxxxx>
Cc: Ingo Molnar <mingo@xxxxxxxxxx>
Cc: Dave Hansen <dave.hansen@xxxxxxxxxxxxxxx>
Cc: Andi Kleen <ak@xxxxxxxxxxxxxxx>
---
arch/x86/entry/calling.h | 34 ++++++++++++++++++++++++++++++++++
arch/x86/include/asm/inst.h | 15 +++++++++++++++
2 files changed, 49 insertions(+)

diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h
index d119729..6cbf793 100644
--- a/arch/x86/entry/calling.h
+++ b/arch/x86/entry/calling.h
@@ -6,6 +6,7 @@
#include <asm/percpu.h>
#include <asm/asm-offsets.h>
#include <asm/processor-flags.h>
+#include <asm/inst.h>

/*

@@ -337,6 +338,39 @@ For 32-bit we have the following conventions - kernel is built with
#endif
.endm

+#ifdef CONFIG_SMP
+
+/*
+ * CPU/node NR is loaded from the limit (size) field of a special segment
+ * descriptor entry in GDT.
+ */
+.macro LOAD_CPU_AND_NODE_SEG_LIMIT reg:req
+ movq $__CPUNODE_SEG, \reg
+ lsl \reg, \reg
+.endm
+
+/*
+ * Fetch the per-CPU GSBASE value for this processor and put it in @reg.
+ * We normally use %gs for accessing per-CPU data, but we are setting up
+ * %gs here and obviously can not use %gs itself to access per-CPU data.
+ */
+.macro GET_PERCPU_BASE reg:req
+ ALTERNATIVE \
+ "LOAD_CPU_AND_NODE_SEG_LIMIT \reg", \
+ "RDPID \reg", \
+ X86_FEATURE_RDPID
+ andq $VDSO_CPUNODE_MASK, \reg
+ movq __per_cpu_offset(, \reg, 8), \reg
+.endm
+
+#else
+
+.macro GET_PERCPU_BASE reg:req
+ movq pcpu_unit_offsets(%rip), \reg
+.endm
+
+#endif /* CONFIG_SMP */
+
.macro READ_MSR_GSBASE save_reg:req
movl $MSR_GS_BASE, %ecx
/* Read MSR specified by %ecx into %edx:%eax */
diff --git a/arch/x86/include/asm/inst.h b/arch/x86/include/asm/inst.h
index f5a796d..d063841 100644
--- a/arch/x86/include/asm/inst.h
+++ b/arch/x86/include/asm/inst.h
@@ -306,6 +306,21 @@
.endif
MODRM 0xc0 movq_r64_xmm_opd1 movq_r64_xmm_opd2
.endm
+
+.macro RDPID opd
+ REG_TYPE rdpid_opd_type \opd
+ .if rdpid_opd_type == REG_TYPE_R64
+ R64_NUM rdpid_opd \opd
+ .else
+ R32_NUM rdpid_opd \opd
+ .endif
+ .byte 0xf3
+ .if rdpid_opd > 7
+ PFX_REX rdpid_opd 0
+ .endif
+ .byte 0x0f, 0xc7
+ MODRM 0xc0 rdpid_opd 0x7
+.endm
#endif

#endif
--
2.7.4