/* -*- linux-c -*- ------------------------------------------------------- *1*2* Copyright (C) 1991, 1992 Linus Torvalds3* Copyright 2007 rPath, Inc. - All Rights Reserved4*5* This file is part of the Linux kernel, and is made available under6* the terms of the GNU General Public License version 2.7*8* ----------------------------------------------------------------------- */910/*11* Prepare the machine for transition to protected mode.12*/1314#include "boot.h"15#include <asm/segment.h>1617/*18* Invoke the realmode switch hook if present; otherwise19* disable all interrupts.20*/21static void realmode_switch_hook(void)22{23if (boot_params.hdr.realmode_swtch) {24asm volatile("lcallw *%0"25: : "m" (boot_params.hdr.realmode_swtch)26: "eax", "ebx", "ecx", "edx");27} else {28asm volatile("cli");29outb(0x80, 0x70); /* Disable NMI */30io_delay();31}32}3334/*35* Disable all interrupts at the legacy PIC.36*/37static void mask_all_interrupts(void)38{39outb(0xff, 0xa1); /* Mask all interrupts on the secondary PIC */40io_delay();41outb(0xfb, 0x21); /* Mask all but cascade on the primary PIC */42io_delay();43}4445/*46* Reset IGNNE# if asserted in the FPU.47*/48static void reset_coprocessor(void)49{50outb(0, 0xf0);51io_delay();52outb(0, 0xf1);53io_delay();54}5556/*57* Set up the GDT58*/5960struct gdt_ptr {61u16 len;62u32 ptr;63} __attribute__((packed));6465static void setup_gdt(void)66{67/* There are machines which are known to not boot with the GDT68being 8-byte unaligned. Intel recommends 16 byte alignment. */69static const u64 boot_gdt[] __attribute__((aligned(16))) = {70/* CS: code, read/execute, 4 GB, base 0 */71[GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff),72/* DS: data, read/write, 4 GB, base 0 */73[GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff),74/* TSS: 32-bit tss, 104 bytes, base 4096 */75/* We only have a TSS here to keep Intel VT happy;76we don't actually use it for anything. */77[GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(0x0089, 4096, 103),78};79/* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead80of the gdt_ptr contents. Thus, make it static so it will81stay in memory, at least long enough that we switch to the82proper kernel GDT. */83static struct gdt_ptr gdt;8485gdt.len = sizeof(boot_gdt)-1;86gdt.ptr = (u32)&boot_gdt + (ds() << 4);8788asm volatile("lgdtl %0" : : "m" (gdt));89}9091/*92* Set up the IDT93*/94static void setup_idt(void)95{96static const struct gdt_ptr null_idt = {0, 0};97asm volatile("lidtl %0" : : "m" (null_idt));98}99100/*101* Actual invocation sequence102*/103void go_to_protected_mode(void)104{105/* Hook before leaving real mode, also disables interrupts */106realmode_switch_hook();107108/* Enable the A20 gate */109if (enable_a20()) {110puts("A20 gate not responding, unable to boot...\n");111die();112}113114/* Reset coprocessor (IGNNE#) */115reset_coprocessor();116117/* Mask all interrupts in the PIC */118mask_all_interrupts();119120/* Actual transition to protected mode... */121setup_idt();122setup_gdt();123protected_mode_jump(boot_params.hdr.code32_start,124(u32)&boot_params + (ds() << 4));125}126127128