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