arch/x86_64: Terrible, awful hackery to bootstrap entry

Because of a historical misunderstanding, by default the ACRN
hypervisor wants to load Zephyr at address 0x1000 and enter the binary
at that same address.  This entry point corresponds to the __start
symbol of the build they were given, which is a 1-cpu non-SMP
configuration.  Unfortunately, when we build with
CONFIG_MP_NUM_CPUS=1, the code in locore.S #if's out the 16 bit entry
point for the auxiliary CPUs at the start of the section.  So in the
build ACRN received, the start address happened to be 0x7000, the same
address we need to launch the AP processors from.

That's right: under ACRN, the SAME ADDRESS used to enter the OS in 32
bit mode needs to be used later to boot CPUs running in 16 bit real
mode!

The solution, such as it is, is to put a 32 bit jump at the entry
address which hops to the 32 bit OS entry code, and then scribble NOP
instructions over that jump once we get there so that the next time we
reach that address (in real mode) we fall through to the correct
entry.

This patch should be considered a temporary workaround.  While it
works on all x86 hardware, it's not really needed.  A much better
solution would be to eliminate the locore linker region entirely
(which causes other headaches) and enter the Zephyr binary in a 32 bit
address somewhere in the contiguous high memory area.  All that locore
is needed for is the 16 bit bootstrap code for SMP processors, which
is ~6 instructions and can be copied in from the kernel at runtime.

Signed-off-by: Andy Ross <andrew.j.ross@intel.com>
This commit is contained in:
Andy Ross 2021-05-14 13:20:04 -07:00 committed by Kumar Gala
commit 5e9c583c24

View file

@ -78,15 +78,41 @@
movq %rax, %cr0
.endm
/* The .locore section begins the page-aligned initialization region
* of low memory. The first address is used as the architectural
* entry point for auxiliary CPUs being brought up (in real mode!)
* via a startup IPI. It's is ALSO used by some loaders (well,
* ACRN...) who hard-coded the address by inspecting _start on a
* non-SMP build.
*
* === OUTRAGEOUS HACK FOLLOWS ===
*
* Therefore it needs to start at OS entry with a 32 bit jump to the
* 32 bit entry point, and gets clobbered later (see the beginning of
* __start32) with NOP bytes such that the next CPU will fall through
* to the 16 bit SMP entry.
*
* We write out the JMP followed by 8 NOPs for simplicity. No i386
* JMP encodes with more than 8 bytes, so we can come back later and
* scribble over it with 8 0x90 bytes (which is the 1-byte NOP) and be
* sure to get all of it without overwriting anything.
*/
.section .locore,"ax"
.code32
.globl __start
__start:
jmp __start32
nop
nop
nop
nop
nop
nop
nop
nop
#if CONFIG_MP_NUM_CPUS > 1
/*
* APs are sent here on startup, in real mode. This
* is first because we want it on a page boundary.
*/
.code16
.global x86_ap_start
x86_ap_start:
@ -133,14 +159,25 @@ unknown_loapic_id:
#endif /* CONFIG_MP_NUM_CPUS > 1 */
.code32
.globl __start
__start:
.globl __start32
__start32:
/*
* kernel execution begins here in 32-bit mode, with flat-mode
* descriptors in all segment registers, interrupts disabled.
* first, let common code do things like detect multiboot info.
*/
/* See note above, re: OUTRAGEOUS HACK */
movl $__start, %ebp
movb $0x90, 0(%ebp)
movb $0x90, 1(%ebp)
movb $0x90, 2(%ebp)
movb $0x90, 3(%ebp)
movb $0x90, 4(%ebp)
movb $0x90, 5(%ebp)
movb $0x90, 6(%ebp)
movb $0x90, 7(%ebp)
wbinvd
lgdt gdt48
lidt idt48