Files
lk/arch/x86/64/start.S
Travis Geiselbrecht b7d69d8804 [arch][x86] handle the local apic of the boot cpu not being 0
I have a bulldozer machine here that curiously starts the APIC IDs for
the cpus at 16 and counts up.

This is a problem since the current code assumes that the boot cpu is 0,
and would try to start itself (apic id 16) later because it thought it
was the first secondary. Fix this by re-reading the APIC id on the boot
cpu and patching the percpu structure a bit into boot. Kinda a hack but
avoids having to detect the APIC, find the type of ID to read, etc.

Also means that practically speaking the system is using the full 32bit
APIC IDs if that feature is present, since now the local apic id is
entirely read from the local apic as it should be (if present).

Fixes #475
2025-09-22 20:57:30 -07:00

231 lines
5.9 KiB
ArmAsm

/*
* Copyright (c) 2009 Corey Tabaka
* Copyright (c) 2015 Intel Corporation
* Copyright (c) 2016 Travis Geiselbrecht
*
* Use of this source code is governed by a MIT-style
* license that can be found in the LICENSE file or at
* https://opensource.org/licenses/MIT
*/
#include <lk/asm.h>
#include <arch/x86/descriptor.h>
#include <arch/x86/mmu.h>
#include <hw/multiboot.h>
#define MSR_EFER 0xc0000080
#define EFER_LME 0x00000100
#define PHYS_LOAD_ADDRESS (MEMBASE + KERNEL_LOAD_OFFSET)
#define PHYS_ADDR_DELTA (KERNEL_BASE + KERNEL_LOAD_OFFSET - PHYS_LOAD_ADDRESS)
#define PHYS(x) ((x) - PHYS_ADDR_DELTA)
.section ".text.boot"
.code32
.global _start
_start:
jmp real_start
.align 8
/* flags for multiboot header */
#define MULTIBOOT_HEADER_FLAGS (MULTIBOOT_PAGE_ALIGN | MULTIBOOT_MEMORY_INFO | MULTIBOOT_AOUT_KLUDGE)
.type multiboot_header,STT_OBJECT
multiboot_header:
/* magic */
.int MULTIBOOT_HEADER_MAGIC
/* flags */
.int MULTIBOOT_HEADER_FLAGS
/* checksum */
.int -(MULTIBOOT_HEADER_MAGIC + MULTIBOOT_HEADER_FLAGS)
/* header_addr */
.int PHYS(multiboot_header)
/* load_addr */
.int PHYS(_start)
/* load_end_addr */
.int PHYS(__data_end)
/* bss_end_addr */
.int PHYS(__bss_end)
/* entry_addr */
.int PHYS(real_start)
real_start:
cmpl $MULTIBOOT_BOOTLOADER_MAGIC, %eax
jne 0f
movl %ebx, PHYS(_multiboot_info)
0:
/* load our new gdt by physical pointer */
lgdt PHYS(_gdtr_phys)
/* load our data selectors */
movw $DATA_SELECTOR, %ax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %ss
movw %ax, %gs
movw %ax, %ss
/* load initial stack pointer */
movl $PHYS(_kstack + 4096), %esp
/* far jump to load the CS from our GDT */
pushl $CODE_SELECTOR
pushl $PHYS(.Lfarjump)
lret
.Lfarjump:
/* zero the bss section */
bss_setup:
movl $PHYS(__bss_start), %edi /* starting address of the bss */
movl $PHYS(__bss_end), %ecx /* find the length of the bss in bytes */
subl %edi, %ecx
shrl $2, %ecx /* convert to 32 bit words, since the bss is aligned anyway */
2:
movl $0, (%edi)
addl $4, %edi
loop 2b
paging_setup:
/* Preparing 64 bit paging. We will use 2MB pages covering 1GB
* for initial bootstrap, this page table will be 1 to 1.
*/
/* PAE bit must be enabled for 64 bit paging*/
mov %cr4, %eax
or $(1<<5), %eax
mov %eax, %cr4
/* load the physical pointer to the top level page table */
movl $PHYS(kernel_pml4), %eax
mov %eax, %cr3
/* save it into a global variable that is used by the kernel */
movl %eax, PHYS(kernel_pml4_phys)
/* Long Mode Enabled at this point*/
movl $MSR_EFER ,%ecx
rdmsr
orl $EFER_LME,%eax
wrmsr
/* Setting the First PML4E with a PDP table reference at index 0 */
movl $PHYS(kernel_pdp), %eax
orl $X86_KERNEL_PD_FLAGS, %eax
movl %eax, PHYS(kernel_pml4)
/* Setting the First PDPTE with a Page table reference at index 0 */
movl $PHYS(kernel_pte), %eax
orl $X86_KERNEL_PD_FLAGS, %eax
movl %eax, PHYS(kernel_pdp)
/* point the pml4e at the second high PDP (for -2GB mapping) at index 511 */
movl $PHYS(kernel_pdp_high), %eax
orl $X86_KERNEL_PD_FLAGS, %eax
movl %eax, PHYS(kernel_pml4 + 8*511)
/* point the second pdp at the same low level page table */
movl $PHYS(kernel_pte), %eax
orl $X86_KERNEL_PD_FLAGS, %eax
movl %eax, PHYS(kernel_pdp_high + 8*510)
/* map the first 1GB in this table */
movl $PHYS(kernel_pte), %esi
movl $0x200, %ecx /* 512 entries */
xor %eax, %eax /* start off at address 0 */
0:
mov %eax, %ebx
shll $21, %ebx
orl $X86_KERNEL_PD_LP_FLAGS, %ebx
movl %ebx, (%esi)
addl $8,%esi
inc %eax
loop 0b /* dec ecx and loop while > 0 */
/* physical [0 ... 1GB] is now be mapped to -2GB (0xffffffff80000000)
* using 2MB pages, where the kernel image will reside.
*/
/* set up a linear map of the first 64GB at 0xffffff8000000000 */
movl $PHYS(kernel_linear_map_pdp), %esi
movl $32768, %ecx
xor %eax, %eax
/* loop across these page tables, incrementing the address by 2MB */
0:
mov %eax, %ebx
shll $21, %ebx
orl $X86_KERNEL_PD_LP_FLAGS, %ebx # lower word of the entry
movl %ebx, (%esi)
mov %eax, %ebx
shrl $11, %ebx # upper word of the entry
movl %ebx, 4(%esi)
addl $8,%esi
inc %eax
loop 0b
/* point the high pdp at our linear mapping page tables */
movl $PHYS(kernel_pdp_high), %esi
movl $64, %ecx
movl $PHYS(kernel_linear_map_pdp), %eax
orl $X86_KERNEL_PD_FLAGS, %eax
0:
movl %eax, (%esi)
add $8, %esi
addl $4096, %eax
loop 0b
/* Enabling Paging and from this point we are in 32 bit compatibility mode */
mov %cr0, %eax
btsl $(31), %eax
mov %eax, %cr0
/* Use a far jump to get into 64bit mode */
pushl $CODE_64_SELECTOR
pushl $PHYS(farjump64)
lret
.align 8
.code64
farjump64:
/* branch to our high address */
mov $highaddr, %rax
jmp *%rax
highaddr:
/* load the high kernel stack */
mov $(_kstack + 4096), %rsp
/* reload the gdtr */
lgdt _gdtr
/* zero out the data selectors */
xor %eax, %eax
movw %ax, %ds
movw %ax, %es
movw %ax, %fs
movw %ax, %gs
movw %ax, %ss
/* set up the idt */
call setup_idt
/* set up the percpu data structure pointer for the boot cpu */
/* NOTE: sets the first cpu as APIC id 0 for now, which may not be correct. Fixed later in boot. */
xor %edi, %edi
xor %esi, %esi
call x86_configure_percpu_early
/* call the main module */
call lk_main
0: /* just sit around waiting for interrupts */
hlt /* interrupts will unhalt the processor */
pause
jmp 0b /* so jump back to halt to conserve power */