| /* |
| * Copyright (c) 2018 Intel Corporation |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| #include "serial.h" |
| #include "x86_64-hw.h" |
| #include "shared-page.h" |
| |
| /* |
| * 16 bit boot stub. This code gets copied into a low memory page and |
| * used as the bootstrap code for SMP processors, which always start |
| * in real mode. It is compiled with gcc's -m16 switch, which is a |
| * wrapper around the assembler's .code16gcc directive which cleverly |
| * takes 32 bit assembly and "fixes" it with appropriate address size |
| * prefixes to run in real mode on a 386. |
| * |
| * It is just code! We have the .text segment and NOTHING ELSE. No |
| * static or global variables can be used, nor const read-only data. |
| * Neither is the linker run, so nothing can be relocated and all |
| * symbolic references need to be to addresses within this file. In |
| * fact, any relocations that do sneak in will be left at zero at |
| * runtime! |
| */ |
| |
| __asm__(" cli\n" |
| " xor %ax, %ax\n" |
| " mov %ax, %ss\n" |
| " mov %ax, %ds\n" |
| " mov $80000, %esp\n" /* FIXME: put stack someplace officiallerish */ |
| " jmp _start16\n"); |
| |
| void _start16(void) |
| { |
| #ifdef XUK_DEBUG |
| serial_putc('1'); serial_putc('6'); serial_putc('\n'); |
| #endif |
| |
| /* First, serialize on a simple spinlock. Note there's a |
| * theoretical flaw here in that we are on a shared stack with the |
| * other CPUs here and we don't *technically* know that "oldlock" |
| * does not get written to the (clobberable!) stack memory. But |
| * in practice the compiler does the right thing here and we spin |
| * in registers until exiting the loop, at which point we are the |
| * only users of the stack, and thus safe. |
| */ |
| int oldlock; |
| |
| do { |
| __asm__ volatile("pause; mov $1, %%eax; xchg %%eax, (%1)" |
| : "=a"(oldlock) : "m"(_shared.smpinit_lock)); |
| } while (oldlock); |
| |
| /* Put a red banner at the top of the screen to announce our |
| * presence |
| */ |
| volatile unsigned short *vga = (unsigned short *)0xb8000; |
| |
| for (int i = 0; i < 240; i++) { |
| vga[i] = 0xcc20; |
| } |
| |
| /* Spin again waiting on the BSP processor to give us a stack. We |
| * won't use it until the entry code of stub32, but we want to |
| * make sure it's there before we jump. |
| */ |
| while (!_shared.smpinit_stack) { |
| } |
| |
| /* Load the GDT the CPU0 already prepared for us */ |
| __asm__ volatile ("lgdtw (%0)\n" : : "r"(_shared.gdt16_addr)); |
| |
| /* Enter protected mode by setting the bottom bit of CR0 */ |
| int cr0; |
| |
| __asm__ volatile ("mov %%cr0, %0\n" : "=r"(cr0)); |
| cr0 |= 1; |
| __asm__ volatile ("mov %0, %%cr0\n" : : "r"(cr0)); |
| |
| /* Set up data and stack segments */ |
| short ds = GDT_SELECTOR(2); |
| |
| __asm__ volatile ("mov %0, %%ds; mov %0, %%ss" : : "r"(ds)); |
| |
| /* Far jump to the 32 bit entry point, passing a cookie in EAX to |
| * tell it what we're doing |
| */ |
| int magic = BOOT_MAGIC_STUB16; |
| |
| __asm__ volatile ("ljmpl $0x8,$0x100000" : : "a"(magic)); |
| |
| while (1) { |
| __asm__("hlt"); |
| } |
| } |