amd64: Set GS.base before calling init_secondary() on APs

KMSAN instrumentation requires thread-local storage to track
initialization state for function parameters and return values.  This
buffer is accessed as part of each function prologue.  It is provided by
the KMSAN runtime, which looks up a pointer in the current thread's
structure.

When KMSAN is configured, init_secondary() is instrumented, but this
means that GS.base must be initialized first, otherwise the runtime
cannot safely access curthread.  Work around this by loading GS.base
before calling init_secondary(), so that the runtime can at least check
curthread == NULL and return a pointer to some dummy storage.  Note that
init_secondary() still must reload GS.base after calling lgdt(), which
loads a selector into %gs, which in turn clears the base register.

Reviewed by:	kib
MFC after:	2 weeks
Sponsored by:	The FreeBSD Foundation
Differential Revision:	https://reviews.freebsd.org/D31336
This commit is contained in:
Mark Johnston 2021-07-29 10:22:37 -04:00
parent e153745083
commit 4b136ef259
2 changed files with 21 additions and 4 deletions

View file

@ -103,6 +103,7 @@ static char *doublefault_stack;
static char *mce_stack;
static char *nmi_stack;
static char *dbg_stack;
void *bootpcpu;
extern u_int mptramp_la57;
extern u_int mptramp_nx;
@ -197,10 +198,8 @@ init_secondary(void)
/* Update microcode before doing anything else. */
ucode_load_ap(cpu);
/* Get per-cpu data and save */
pc = &__pcpu[cpu];
/* prime data page for it to use */
/* Initialize the PCPU area. */
pc = bootpcpu;
pcpu_init(pc, cpu, sizeof(struct pcpu));
dpcpu_init(dpcpu, cpu);
pc->pc_apic_id = cpu_apic_ids[cpu];
@ -431,6 +430,7 @@ start_all_aps(void)
dpcpu = (void *)kmem_malloc_domainset(DOMAINSET_PREF(domain),
DPCPU_SIZE, M_WAITOK | M_ZERO);
bootpcpu = &__pcpu[cpu];
bootSTK = (char *)bootstacks[cpu] +
kstack_pages * PAGE_SIZE - 8;
bootAP = cpu;

View file

@ -260,4 +260,21 @@ bootMP_size:
.p2align 4,0
entry_64:
movq bootSTK, %rsp
/*
* Initialize the segment register used for the PCPU area. The PCPU
* area will be initialized by init_secondary(), but it should be
* accessible before that to support sanitizer instrumentation which
* accesses per-CPU variables.
*
* Note that GS.base is loaded again in init_secondary(). This is not
* redundant: lgdt() loads a selector into %gs and this has the side
* effect of clearing GS.base.
*/
movl $MSR_GSBASE, %ecx
movq bootpcpu, %rax
movq %rax, %rdx
shrq $32, %rdx
wrmsr
jmp init_secondary