Properly protect the startup stack with a mutex.

Because we start the APs sequentially, it is unlikely they will coincide
for the brief period that they use the temporary startup stack, but we
should guard against it. This allows us to remove the mutex around the
restart of each AP when relocating, which should improve test times.
This commit is contained in:
Martin Whitaker 2022-01-31 21:54:24 +00:00
parent 17093a96f9
commit 4100a44b12
5 changed files with 52 additions and 23 deletions

View File

@ -126,9 +126,10 @@ static void run_at(uintptr_t addr, int my_pcpu)
} }
BARRIER(true); BARRIER(true);
// We use a lock to ensure that only one CPU at a time jumps to #ifndef __x86_64__
// the new code. Some of the startup stuff is not thread safe! // The 32-bit startup code needs to know where it is located.
spin_lock(start_mutex); __asm__ __volatile__("movl %0, %%edi" : : "r" (new_start_addr));
#endif
goto *new_start_addr; goto *new_start_addr;
} }
@ -384,9 +385,6 @@ void main(void)
} else { } else {
pcpu_state[my_pcpu] = CPU_STATE_RUNNING; pcpu_state[my_pcpu] = CPU_STATE_RUNNING;
} }
} else {
// Release the lock taken in run_at().
spin_unlock(start_mutex);
} }
BARRIER(true); BARRIER(true);
init_state = 2; init_state = 2;

View File

@ -49,6 +49,8 @@
extern uint8_t _start[]; extern uint8_t _start[];
extern uint8_t startup32[];
extern uint8_t startup[]; extern uint8_t startup[];
extern uint64_t pml4[]; extern uint64_t pml4[];

View File

@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0-only // SPDX-License-Identifier: GPL-2.0-only
// Copyright (C) 2020-2021 Martin Whitaker. // Copyright (C) 2020-2022 Martin Whitaker.
// //
// Derived from Linux 5.6 arch/x86/boot/compressed/eboot.c and extracts // Derived from Linux 5.6 arch/x86/boot/compressed/eboot.c and extracts
// from drivers/firmware/efi/libstub: // from drivers/firmware/efi/libstub:
@ -541,6 +541,8 @@ boot_params_t *efi_setup(efi_handle_t handle, efi_system_table_t *sys_table_arg,
memset(boot_params, 0, sizeof(boot_params_t)); memset(boot_params, 0, sizeof(boot_params_t));
} }
boot_params->code32_start = (uintptr_t)startup32;
status = set_screen_info(boot_params); status = set_screen_info(boot_params);
if (status != EFI_SUCCESS) { if (status != EFI_SUCCESS) {
print_string("set_screen_info() failed\n"); print_string("set_screen_info() failed\n");

View File

@ -31,7 +31,7 @@
startup32: startup32:
cld cld
cli cli
jmp startup jmp first_start
# The Linux 32-bit EFI handover point. # The Linux 32-bit EFI handover point.
@ -62,18 +62,28 @@ efi_handover:
call efi_setup call efi_setup
# Fall though to the shared 32-bit entry point with the boot # Fall though to the shared 32-bit entry point with the boot
# params pointer in %esi. # params pointer in %esi...
movl %eax, %esi movl %eax, %esi
# ...and with the startup address in %edi.
first_start:
movl 0x214(%esi), %ebx # bootparams.code32_start
leal (startup - startup32)(%ebx), %edi
# The 32-bit entry point for AP boot and for restart after relocation. # The 32-bit entry point for AP boot and for restart after relocation.
.globl startup .globl startup
startup: startup:
# Use a temporary stack until we pick the correct one. We can # Use the startup stack until we pick the correct one. We
# safely use the high address, even if we are loaded low. # need to take the mutex to protect our use of the stack.
movl $(HIGH_LOAD_ADDR + startup_stack_top - startup32), %esp leal (startup_stack_mutex - startup)(%edi), %eax
0: lock bts $0, (%eax)
jc 0b
leal (startup_stack_top - startup)(%edi), %esp
# Load the GOT pointer. # Load the GOT pointer.
@ -98,6 +108,10 @@ startup:
leal _end@GOTOFF(%ebx), %esp leal _end@GOTOFF(%ebx), %esp
addl %eax, %esp addl %eax, %esp
# Release the mutex that protects the startup stack.
movl $0, startup_stack_mutex@GOTOFF(%ebx)
# Initialise the GDT descriptor. # Initialise the GDT descriptor.
leal gdt@GOTOFF(%ebx), %eax leal gdt@GOTOFF(%ebx), %eax
@ -495,15 +509,15 @@ ap_trampoline:
movw %cs, %ax movw %cs, %ax
movw %ax, %ds movw %ax, %ds
# Patch the jump address. # Load the startup address and use it to patch the jump address.
movl (ap_startup_addr - ap_trampoline), %ebx movl (ap_startup_addr - ap_trampoline), %edi
movl %ebx, (ap_jump - ap_trampoline + 2) movl %edi, (ap_jump - ap_trampoline + 2)
# Patch and load the GDT descriptor. It should point to the main # Patch and load the GDT descriptor. It should point to the main
# GDT descriptor, which has already been initialised by the BSP. # GDT descriptor, which has already been initialised by the BSP.
movl %ebx, %eax movl %edi, %eax
addl $(gdt - startup), %eax addl $(gdt - startup), %eax
movl %eax, (ap_gdt_descr - ap_trampoline + 2) movl %eax, (ap_gdt_descr - ap_trampoline + 2)
lgdt ap_gdt_descr - ap_trampoline lgdt ap_gdt_descr - ap_trampoline
@ -544,11 +558,15 @@ ap_trampoline_end:
# Variables. # Variables.
.data .data
.align 4
.globl boot_params_addr .globl boot_params_addr
boot_params_addr: boot_params_addr:
.long 0 .long 0
startup_stack_mutex:
.long 0
first_boot: first_boot:
.long 1 .long 1

View File

@ -32,19 +32,18 @@ startup32:
cld cld
cli cli
# Use a temporary stack until we pick the correct one. We can
# safely use the high address, even if we are loaded low.
movl $(HIGH_LOAD_ADDR + startup_stack_top - startup32), %esp
# Get the load address. # Get the load address.
movl 0x214(%esi), %ebx movl 0x214(%esi), %ebx # bootparams.code32_start
# Save the boot params pointer. # Save the boot params pointer.
movl %esi, (boot_params_addr - startup32)(%ebx) movl %esi, (boot_params_addr - startup32)(%ebx)
# Use the startup stack until we pick the correct one.
leal (startup_stack_top - startup32)(%ebx), %esp
# Initialise the pml4 and pdp tables. # Initialise the pml4 and pdp tables.
leal (pml4 - startup32)(%ebx), %ecx leal (pml4 - startup32)(%ebx), %ecx
@ -143,8 +142,11 @@ efi_handover:
.globl startup .globl startup
startup: startup:
# Use a temporary stack until we pick the correct one. # Use the startup stack until we pick the correct one. We
# need to take a mutex to protect our use of the stack.
0: lock bts $0, startup_stack_mutex(%rip)
jc 0b
leaq startup_stack_top(%rip), %rsp leaq startup_stack_top(%rip), %rsp
# Pick the correct stack. The stacks are allocated immediately # Pick the correct stack. The stacks are allocated immediately
@ -158,6 +160,9 @@ startup:
leaq _end(%rip), %rsp leaq _end(%rip), %rsp
addq %rax, %rsp addq %rax, %rsp
# Release the mutex that protects the startup stack.
movl $0, startup_stack_mutex(%rip)
# Initialise the pml4 and pdp tables. # Initialise the pml4 and pdp tables.
leaq pml4(%rip), %rcx leaq pml4(%rip), %rcx
@ -589,11 +594,15 @@ ap_trampoline_end:
# Variables. # Variables.
.data .data
.align 4
.globl boot_params_addr .globl boot_params_addr
boot_params_addr: boot_params_addr:
.quad 0 .quad 0
startup_stack_mutex:
.long 0
first_boot: first_boot:
.long 1 .long 1