linux/arch/x86/boot/pm.c
<<
>>
Prefs
   1/* -*- linux-c -*- ------------------------------------------------------- *
   2 *
   3 *   Copyright (C) 1991, 1992 Linus Torvalds
   4 *   Copyright 2007 rPath, Inc. - All Rights Reserved
   5 *
   6 *   This file is part of the Linux kernel, and is made available under
   7 *   the terms of the GNU General Public License version 2.
   8 *
   9 * ----------------------------------------------------------------------- */
  10
  11/*
  12 * Prepare the machine for transition to protected mode.
  13 */
  14
  15#include "boot.h"
  16#include <asm/segment.h>
  17
  18/*
  19 * Invoke the realmode switch hook if present; otherwise
  20 * disable all interrupts.
  21 */
  22static void realmode_switch_hook(void)
  23{
  24        if (boot_params.hdr.realmode_swtch) {
  25                asm volatile("lcallw *%0"
  26                             : : "m" (boot_params.hdr.realmode_swtch)
  27                             : "eax", "ebx", "ecx", "edx");
  28        } else {
  29                asm volatile("cli");
  30                outb(0x80, 0x70); /* Disable NMI */
  31                io_delay();
  32        }
  33}
  34
  35/*
  36 * A zImage kernel is loaded at 0x10000 but wants to run at 0x1000.
  37 * A bzImage kernel is loaded and runs at 0x100000.
  38 */
  39static void move_kernel_around(void)
  40{
  41        /* Note: rely on the compile-time option here rather than
  42           the LOADED_HIGH flag.  The Qemu kernel loader unconditionally
  43           sets the loadflags to zero. */
  44#ifndef __BIG_KERNEL__
  45        u16 dst_seg, src_seg;
  46        u32 syssize;
  47
  48        dst_seg =  0x1000 >> 4;
  49        src_seg = 0x10000 >> 4;
  50        syssize = boot_params.hdr.syssize; /* Size in 16-byte paragraphs */
  51
  52        while (syssize) {
  53                int paras  = (syssize >= 0x1000) ? 0x1000 : syssize;
  54                int dwords = paras << 2;
  55
  56                asm volatile("pushw %%es ; "
  57                             "pushw %%ds ; "
  58                             "movw %1,%%es ; "
  59                             "movw %2,%%ds ; "
  60                             "xorw %%di,%%di ; "
  61                             "xorw %%si,%%si ; "
  62                             "rep;movsl ; "
  63                             "popw %%ds ; "
  64                             "popw %%es"
  65                             : "+c" (dwords)
  66                             : "r" (dst_seg), "r" (src_seg)
  67                             : "esi", "edi");
  68
  69                syssize -= paras;
  70                dst_seg += paras;
  71                src_seg += paras;
  72        }
  73#endif
  74}
  75
  76/*
  77 * Disable all interrupts at the legacy PIC.
  78 */
  79static void mask_all_interrupts(void)
  80{
  81        outb(0xff, 0xa1);       /* Mask all interrupts on the secondary PIC */
  82        io_delay();
  83        outb(0xfb, 0x21);       /* Mask all but cascade on the primary PIC */
  84        io_delay();
  85}
  86
  87/*
  88 * Reset IGNNE# if asserted in the FPU.
  89 */
  90static void reset_coprocessor(void)
  91{
  92        outb(0, 0xf0);
  93        io_delay();
  94        outb(0, 0xf1);
  95        io_delay();
  96}
  97
  98/*
  99 * Set up the GDT
 100 */
 101
 102struct gdt_ptr {
 103        u16 len;
 104        u32 ptr;
 105} __attribute__((packed));
 106
 107static void setup_gdt(void)
 108{
 109        /* There are machines which are known to not boot with the GDT
 110           being 8-byte unaligned.  Intel recommends 16 byte alignment. */
 111        static const u64 boot_gdt[] __attribute__((aligned(16))) = {
 112                /* CS: code, read/execute, 4 GB, base 0 */
 113                [GDT_ENTRY_BOOT_CS] = GDT_ENTRY(0xc09b, 0, 0xfffff),
 114                /* DS: data, read/write, 4 GB, base 0 */
 115                [GDT_ENTRY_BOOT_DS] = GDT_ENTRY(0xc093, 0, 0xfffff),
 116                /* TSS: 32-bit tss, 104 bytes, base 4096 */
 117                /* We only have a TSS here to keep Intel VT happy;
 118                   we don't actually use it for anything. */
 119                [GDT_ENTRY_BOOT_TSS] = GDT_ENTRY(0x0089, 4096, 103),
 120        };
 121        /* Xen HVM incorrectly stores a pointer to the gdt_ptr, instead
 122           of the gdt_ptr contents.  Thus, make it static so it will
 123           stay in memory, at least long enough that we switch to the
 124           proper kernel GDT. */
 125        static struct gdt_ptr gdt;
 126
 127        gdt.len = sizeof(boot_gdt)-1;
 128        gdt.ptr = (u32)&boot_gdt + (ds() << 4);
 129
 130        asm volatile("lgdtl %0" : : "m" (gdt));
 131}
 132
 133/*
 134 * Set up the IDT
 135 */
 136static void setup_idt(void)
 137{
 138        static const struct gdt_ptr null_idt = {0, 0};
 139        asm volatile("lidtl %0" : : "m" (null_idt));
 140}
 141
 142/*
 143 * Actual invocation sequence
 144 */
 145void go_to_protected_mode(void)
 146{
 147        /* Hook before leaving real mode, also disables interrupts */
 148        realmode_switch_hook();
 149
 150        /* Move the kernel/setup to their final resting places */
 151        move_kernel_around();
 152
 153        /* Enable the A20 gate */
 154        if (enable_a20()) {
 155                puts("A20 gate not responding, unable to boot...\n");
 156                die();
 157        }
 158
 159        /* Reset coprocessor (IGNNE#) */
 160        reset_coprocessor();
 161
 162        /* Mask all interrupts in the PIC */
 163        mask_all_interrupts();
 164
 165        /* Actual transition to protected mode... */
 166        setup_idt();
 167        setup_gdt();
 168        protected_mode_jump(boot_params.hdr.code32_start,
 169                            (u32)&boot_params + (ds() << 4));
 170}
 171