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
192 lines
4.8 KiB
ArmAsm
192 lines
4.8 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 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"
|
|
.global _start
|
|
_start:
|
|
jmp real_start
|
|
|
|
.align 4
|
|
|
|
/* 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)
|
|
|
|
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
|
|
|
|
/*We jumped here in protected mode in a code segment that migh not longer
|
|
be valid , do a long jump to our code segment, we use retf instead of
|
|
ljmp to be able to use relative labels */
|
|
pushl $CODE_SELECTOR /*Pushing our code segment */
|
|
pushl $PHYS(.Lfarjump) /*and jump address */
|
|
retf /*This instruction will jump to codesel:farjump */
|
|
|
|
.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:
|
|
#if X86_LEGACY
|
|
/* map the first 16MB 1:1 with 4KB pages and again at 0x8000.0000 */
|
|
|
|
/* set up 4 page tables worth of entries */
|
|
movl $PHYS(kernel_pt), %edi
|
|
movl $1024*4,%ecx
|
|
movl $X86_KERNEL_PT_FLAGS, %eax
|
|
|
|
.Lfill_pt:
|
|
movl %eax, (%edi)
|
|
addl $4, %edi
|
|
addl $4096, %eax
|
|
loop .Lfill_pt
|
|
|
|
/* set up the page dir with 4 entries at 0 and 0x8000.0000 pointing
|
|
* to 4 page tables that will map physical address 0 - 16MB
|
|
*/
|
|
movl $PHYS(kernel_pd), %esi
|
|
movl $PHYS(kernel_pd) + 512*4, %edi
|
|
movl $PHYS(kernel_pt) + X86_KERNEL_PT_FLAGS, %eax
|
|
movl %eax, (%esi)
|
|
movl %eax, (%edi)
|
|
addl $4096, %eax
|
|
movl %eax, 4(%esi)
|
|
movl %eax, 4(%edi)
|
|
addl $4096, %eax
|
|
movl %eax, 8(%esi)
|
|
movl %eax, 8(%edi)
|
|
addl $4096, %eax
|
|
movl %eax, 12(%esi)
|
|
movl %eax, 12(%edi)
|
|
#else
|
|
/* map the first 1GB 1:1 using 4MB pages */
|
|
movl $PHYS(kernel_pd), %esi
|
|
movl $0x100, %ecx
|
|
xor %eax, %eax
|
|
|
|
.Lfill_pd:
|
|
mov %eax, %edx
|
|
orl $X86_KERNEL_PD_LP_FLAGS, %edx
|
|
movl %edx, (%esi)
|
|
addl $4, %esi
|
|
addl $0x00400000, %eax
|
|
loop .Lfill_pd
|
|
|
|
/* map the first 1GB to KERNEL_ASPACE_BASE */
|
|
movl $(PHYS(kernel_pd) + 0x800), %esi
|
|
movl $0x100, %ecx
|
|
xor %eax, %eax
|
|
|
|
.Lfill_pd2:
|
|
mov %eax, %edx
|
|
orl $X86_KERNEL_PD_LP_FLAGS, %edx
|
|
movl %edx, (%esi)
|
|
addl $4, %esi
|
|
addl $0x00400000, %eax
|
|
loop .Lfill_pd2
|
|
|
|
/* enable PSE (4MB pages) */
|
|
mov %cr4, %eax
|
|
orl $(1<<4), %eax
|
|
mov %eax, %cr4
|
|
#endif
|
|
|
|
/* Set PD in CR3 */
|
|
movl $PHYS(kernel_pd), %eax
|
|
mov %eax, %cr3
|
|
|
|
/* save a copy of the address of the kernel page directory */
|
|
movl %eax, PHYS(kernel_pd_phys)
|
|
|
|
/* Enabling Paging and from this point we are in */
|
|
mov %cr0, %eax
|
|
btsl $(31), %eax
|
|
mov %eax, %cr0
|
|
|
|
/* load the high kernel stack */
|
|
movl $(_kstack + 4096), %esp
|
|
|
|
/* reload the high gdtr */
|
|
lgdt PHYS(_gdtr)
|
|
|
|
/* branch to the high address */
|
|
movl $main_lk, %eax
|
|
jmp *%eax
|
|
|
|
main_lk:
|
|
/* 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. */
|
|
pushl $0
|
|
pushl $0
|
|
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 */
|