^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /* -*- linux-c -*- ------------------------------------------------------- *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) * Copyright (C) 1991, 1992 Linus Torvalds
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright 2007 rPath, Inc. - All Rights Reserved
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) * ----------------------------------------------------------------------- */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) * Prepare the machine for transition to protected mode.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include "boot.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <asm/segment.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) * Invoke the realmode switch hook if present; otherwise
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) * disable all interrupts.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) static void realmode_switch_hook(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) if (boot_params.hdr.realmode_swtch) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) asm volatile("lcallw *%0"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) : : "m" (boot_params.hdr.realmode_swtch)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) : "eax", "ebx", "ecx", "edx");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) } else {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) asm volatile("cli");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) outb(0x80, 0x70); /* Disable NMI */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) io_delay();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) * Disable all interrupts at the legacy PIC.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) static void mask_all_interrupts(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) outb(0xff, 0xa1); /* Mask all interrupts on the secondary PIC */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39) io_delay();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) outb(0xfb, 0x21); /* Mask all but cascade on the primary PIC */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) io_delay();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) * Reset IGNNE# if asserted in the FPU.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) static void reset_coprocessor(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) outb(0, 0xf0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) io_delay();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) outb(0, 0xf1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) io_delay();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) * Set up the GDT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) struct gdt_ptr {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) u16 len;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) u32 ptr;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) } __attribute__((packed));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) static void setup_gdt(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) /* There are machines which are known to not boot with the GDT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) being 8-byte unaligned. Intel recommends 16 byte alignment. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) static const u64 boot_gdt[] __attribute__((aligned(16))) = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) /* CS: code, read/execute, 4 GB, base 0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) /* DS: data, read/write, 4 GB, base 0 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) /* TSS: 32-bit tss, 104 bytes, base 4096 */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) /* We only have a TSS here to keep Intel VT happy;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) we don't actually use it for anything. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) [GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(0x0089, 4096, 103),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) /* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) of the gdt_ptr contents. Thus, make it static so it will
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) stay in memory, at least long enough that we switch to the
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) proper kernel GDT. */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) static struct gdt_ptr gdt;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) gdt.len = sizeof(boot_gdt)-1;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) gdt.ptr = (u32)&boot_gdt + (ds() << 4);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) asm volatile("lgdtl %0" : : "m" (gdt));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) * Set up the IDT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) static void setup_idt(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) static const struct gdt_ptr null_idt = {0, 0};
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) asm volatile("lidtl %0" : : "m" (null_idt));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) * Actual invocation sequence
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) void go_to_protected_mode(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) /* Hook before leaving real mode, also disables interrupts */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105) realmode_switch_hook();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) /* Enable the A20 gate */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) if (enable_a20()) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) puts("A20 gate not responding, unable to boot...\n");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 110) die();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 111) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 112)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 113) /* Reset coprocessor (IGNNE#) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 114) reset_coprocessor();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 115)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 116) /* Mask all interrupts in the PIC */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 117) mask_all_interrupts();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 118)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 119) /* Actual transition to protected mode... */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 120) setup_idt();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 121) setup_gdt();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 122) protected_mode_jump(boot_params.hdr.code32_start,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 123) (u32)&boot_params + (ds() << 4));
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 124) }