/* See COPYRIGHT for copyright information. */ #include #include ################################################################### # entry point for APs ################################################################### # Each non-boot CPU ("AP") is started up in response to a STARTUP # IPI from the boot CPU. Section B.4.2 of the Multi-Processor # Specification says that the AP will start in real mode with CS:IP # set to XY00:0000, where XY is an 8-bit value sent with the # STARTUP. Thus this code must start at a 4096-byte boundary. # # Because this code sets DS to zero, it must run from an address in # the low 2^16 bytes of physical memory. # # boot_aps() (in init.c) copies this code to MPENTRY_PADDR (which # satisfies the above restrictions). Then, for each AP, it stores the # address of the pre-allocated per-core stack in mpentry_kstack, sends # the STARTUP IPI, and waits for this code to acknowledge that it has # started (which happens in mp_main in init.c). # # This code is similar to boot/boot.S except that # - it does not need to enable A20 # - it uses MPBOOTPHYS to calculate absolute addresses of its # symbols, rather than relying on the linker to fill them #define RELOC(x) ((x) - KERNBASE) #define MPBOOTPHYS(s) ((s) - mpentry_start + MPENTRY_PADDR) .set PROT_MODE_CSEG, 0x8 # kernel code segment selector .set PROT_MODE_DSEG, 0x10 # kernel data segment selector .code16 .globl mpentry_start mpentry_start: cli xorw %ax, %ax movw %ax, %ds movw %ax, %es movw %ax, %ss lgdt MPBOOTPHYS(gdtdesc) movl %cr0, %eax orl $CR0_PE, %eax movl %eax, %cr0 ljmpl $(PROT_MODE_CSEG), $(MPBOOTPHYS(start32)) .code32 start32: movw $(PROT_MODE_DSEG), %ax movw %ax, %ds movw %ax, %es movw %ax, %ss movw $0, %ax movw %ax, %fs movw %ax, %gs # Set up initial page table. We cannot use kern_pgdir yet because # we are still running at a low EIP. movl $(RELOC(entry_pgdir)), %eax movl %eax, %cr3 # Turn on paging. movl %cr0, %eax orl $(CR0_PE|CR0_PG|CR0_WP), %eax movl %eax, %cr0 # Switch to the per-cpu stack allocated in boot_aps() movl mpentry_kstack, %esp movl $0x0, %ebp # nuke frame pointer # Call mp_main(). (Exercise for the reader: why the indirect call?) movl $mp_main, %eax call *%eax # If mp_main returns (it shouldn't), loop. spin: jmp spin # Bootstrap GDT .p2align 2 # force 4 byte alignment gdt: SEG_NULL # null seg SEG(STA_X|STA_R, 0x0, 0xffffffff) # code seg SEG(STA_W, 0x0, 0xffffffff) # data seg gdtdesc: .word 0x17 # sizeof(gdt) - 1 .long MPBOOTPHYS(gdt) # address gdt .globl mpentry_end mpentry_end: nop