linux/arch/i386/kernel/reboot.c
<<
>>
Prefs
   1/*
   2 *  linux/arch/i386/kernel/reboot.c
   3 */
   4
   5#include <linux/mm.h>
   6#include <linux/module.h>
   7#include <linux/delay.h>
   8#include <linux/init.h>
   9#include <linux/interrupt.h>
  10#include <linux/mc146818rtc.h>
  11#include <linux/efi.h>
  12#include <linux/dmi.h>
  13#include <linux/ctype.h>
  14#include <linux/pm.h>
  15#include <linux/reboot.h>
  16#include <asm/uaccess.h>
  17#include <asm/apic.h>
  18#include <asm/desc.h>
  19#include "mach_reboot.h"
  20#include <asm/reboot_fixups.h>
  21#include <asm/reboot.h>
  22
  23/*
  24 * Power off function, if any
  25 */
  26void (*pm_power_off)(void);
  27EXPORT_SYMBOL(pm_power_off);
  28
  29static int reboot_mode;
  30static int reboot_thru_bios;
  31
  32#ifdef CONFIG_SMP
  33static int reboot_cpu = -1;
  34#endif
  35static int __init reboot_setup(char *str)
  36{
  37        while(1) {
  38                switch (*str) {
  39                case 'w': /* "warm" reboot (no memory testing etc) */
  40                        reboot_mode = 0x1234;
  41                        break;
  42                case 'c': /* "cold" reboot (with memory testing etc) */
  43                        reboot_mode = 0x0;
  44                        break;
  45                case 'b': /* "bios" reboot by jumping through the BIOS */
  46                        reboot_thru_bios = 1;
  47                        break;
  48                case 'h': /* "hard" reboot by toggling RESET and/or crashing the CPU */
  49                        reboot_thru_bios = 0;
  50                        break;
  51#ifdef CONFIG_SMP
  52                case 's': /* "smp" reboot by executing reset on BSP or other CPU*/
  53                        if (isdigit(*(str+1))) {
  54                                reboot_cpu = (int) (*(str+1) - '0');
  55                                if (isdigit(*(str+2)))
  56                                        reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0');
  57                        }
  58                                /* we will leave sorting out the final value 
  59                                when we are ready to reboot, since we might not
  60                                have set up boot_cpu_id or smp_num_cpu */
  61                        break;
  62#endif
  63                }
  64                if((str = strchr(str,',')) != NULL)
  65                        str++;
  66                else
  67                        break;
  68        }
  69        return 1;
  70}
  71
  72__setup("reboot=", reboot_setup);
  73
  74/*
  75 * Reboot options and system auto-detection code provided by
  76 * Dell Inc. so their systems "just work". :-)
  77 */
  78
  79/*
  80 * Some machines require the "reboot=b"  commandline option, this quirk makes that automatic.
  81 */
  82static int __init set_bios_reboot(struct dmi_system_id *d)
  83{
  84        if (!reboot_thru_bios) {
  85                reboot_thru_bios = 1;
  86                printk(KERN_INFO "%s series board detected. Selecting BIOS-method for reboots.\n", d->ident);
  87        }
  88        return 0;
  89}
  90
  91static struct dmi_system_id __initdata reboot_dmi_table[] = {
  92        {       /* Handle problems with rebooting on Dell E520's */
  93                .callback = set_bios_reboot,
  94                .ident = "Dell E520",
  95                .matches = {
  96                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
  97                        DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM061"),
  98                },
  99        },
 100        {       /* Handle problems with rebooting on Dell 1300's */
 101                .callback = set_bios_reboot,
 102                .ident = "Dell PowerEdge 1300",
 103                .matches = {
 104                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 105                        DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"),
 106                },
 107        },
 108        {       /* Handle problems with rebooting on Dell 300's */
 109                .callback = set_bios_reboot,
 110                .ident = "Dell PowerEdge 300",
 111                .matches = {
 112                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 113                        DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
 114                },
 115        },
 116        {       /* Handle problems with rebooting on Dell Optiplex 745's SFF*/
 117                .callback = set_bios_reboot,
 118                .ident = "Dell OptiPlex 745",
 119                .matches = {
 120                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
 121                        DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
 122                        DMI_MATCH(DMI_BOARD_NAME, "0WF810"),
 123                },
 124        },
 125        {       /* Handle problems with rebooting on Dell 2400's */
 126                .callback = set_bios_reboot,
 127                .ident = "Dell PowerEdge 2400",
 128                .matches = {
 129                        DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
 130                        DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"),
 131                },
 132        },
 133        {       /* Handle problems with rebooting on HP laptops */
 134                .callback = set_bios_reboot,
 135                .ident = "HP Compaq Laptop",
 136                .matches = {
 137                        DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
 138                        DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq"),
 139                },
 140        },
 141        { }
 142};
 143
 144static int __init reboot_init(void)
 145{
 146        dmi_check_system(reboot_dmi_table);
 147        return 0;
 148}
 149
 150core_initcall(reboot_init);
 151
 152/* The following code and data reboots the machine by switching to real
 153   mode and jumping to the BIOS reset entry point, as if the CPU has
 154   really been reset.  The previous version asked the keyboard
 155   controller to pulse the CPU reset line, which is more thorough, but
 156   doesn't work with at least one type of 486 motherboard.  It is easy
 157   to stop this code working; hence the copious comments. */
 158
 159static unsigned long long
 160real_mode_gdt_entries [3] =
 161{
 162        0x0000000000000000ULL,  /* Null descriptor */
 163        0x00009a000000ffffULL,  /* 16-bit real-mode 64k code at 0x00000000 */
 164        0x000092000100ffffULL   /* 16-bit real-mode 64k data at 0x00000100 */
 165};
 166
 167static struct Xgt_desc_struct
 168real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
 169real_mode_idt = { 0x3ff, 0 },
 170no_idt = { 0, 0 };
 171
 172
 173/* This is 16-bit protected mode code to disable paging and the cache,
 174   switch to real mode and jump to the BIOS reset code.
 175
 176   The instruction that switches to real mode by writing to CR0 must be
 177   followed immediately by a far jump instruction, which set CS to a
 178   valid value for real mode, and flushes the prefetch queue to avoid
 179   running instructions that have already been decoded in protected
 180   mode.
 181
 182   Clears all the flags except ET, especially PG (paging), PE
 183   (protected-mode enable) and TS (task switch for coprocessor state
 184   save).  Flushes the TLB after paging has been disabled.  Sets CD and
 185   NW, to disable the cache on a 486, and invalidates the cache.  This
 186   is more like the state of a 486 after reset.  I don't know if
 187   something else should be done for other chips.
 188
 189   More could be done here to set up the registers as if a CPU reset had
 190   occurred; hopefully real BIOSs don't assume much. */
 191
 192static unsigned char real_mode_switch [] =
 193{
 194        0x66, 0x0f, 0x20, 0xc0,                 /*    movl  %cr0,%eax        */
 195        0x66, 0x83, 0xe0, 0x11,                 /*    andl  $0x00000011,%eax */
 196        0x66, 0x0d, 0x00, 0x00, 0x00, 0x60,     /*    orl   $0x60000000,%eax */
 197        0x66, 0x0f, 0x22, 0xc0,                 /*    movl  %eax,%cr0        */
 198        0x66, 0x0f, 0x22, 0xd8,                 /*    movl  %eax,%cr3        */
 199        0x66, 0x0f, 0x20, 0xc3,                 /*    movl  %cr0,%ebx        */
 200        0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60,       /*    andl  $0x60000000,%ebx */
 201        0x74, 0x02,                             /*    jz    f                */
 202        0x0f, 0x09,                             /*    wbinvd                 */
 203        0x24, 0x10,                             /* f: andb  $0x10,al         */
 204        0x66, 0x0f, 0x22, 0xc0                  /*    movl  %eax,%cr0        */
 205};
 206static unsigned char jump_to_bios [] =
 207{
 208        0xea, 0x00, 0x00, 0xff, 0xff            /*    ljmp  $0xffff,$0x0000  */
 209};
 210
 211/*
 212 * Switch to real mode and then execute the code
 213 * specified by the code and length parameters.
 214 * We assume that length will aways be less that 100!
 215 */
 216void machine_real_restart(unsigned char *code, int length)
 217{
 218        local_irq_disable();
 219
 220        /* Write zero to CMOS register number 0x0f, which the BIOS POST
 221           routine will recognize as telling it to do a proper reboot.  (Well
 222           that's what this book in front of me says -- it may only apply to
 223           the Phoenix BIOS though, it's not clear).  At the same time,
 224           disable NMIs by setting the top bit in the CMOS address register,
 225           as we're about to do peculiar things to the CPU.  I'm not sure if
 226           `outb_p' is needed instead of just `outb'.  Use it to be on the
 227           safe side.  (Yes, CMOS_WRITE does outb_p's. -  Paul G.)
 228         */
 229
 230        spin_lock(&rtc_lock);
 231        CMOS_WRITE(0x00, 0x8f);
 232        spin_unlock(&rtc_lock);
 233
 234        /* Remap the kernel at virtual address zero, as well as offset zero
 235           from the kernel segment.  This assumes the kernel segment starts at
 236           virtual address PAGE_OFFSET. */
 237
 238        memcpy (swapper_pg_dir, swapper_pg_dir + USER_PGD_PTRS,
 239                sizeof (swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
 240
 241        /*
 242         * Use `swapper_pg_dir' as our page directory.
 243         */
 244        load_cr3(swapper_pg_dir);
 245
 246        /* Write 0x1234 to absolute memory location 0x472.  The BIOS reads
 247           this on booting to tell it to "Bypass memory test (also warm
 248           boot)".  This seems like a fairly standard thing that gets set by
 249           REBOOT.COM programs, and the previous reset routine did this
 250           too. */
 251
 252        *((unsigned short *)0x472) = reboot_mode;
 253
 254        /* For the switch to real mode, copy some code to low memory.  It has
 255           to be in the first 64k because it is running in 16-bit mode, and it
 256           has to have the same physical and virtual address, because it turns
 257           off paging.  Copy it near the end of the first page, out of the way
 258           of BIOS variables. */
 259
 260        memcpy ((void *) (0x1000 - sizeof (real_mode_switch) - 100),
 261                real_mode_switch, sizeof (real_mode_switch));
 262        memcpy ((void *) (0x1000 - 100), code, length);
 263
 264        /* Set up the IDT for real mode. */
 265
 266        load_idt(&real_mode_idt);
 267
 268        /* Set up a GDT from which we can load segment descriptors for real
 269           mode.  The GDT is not used in real mode; it is just needed here to
 270           prepare the descriptors. */
 271
 272        load_gdt(&real_mode_gdt);
 273
 274        /* Load the data segment registers, and thus the descriptors ready for
 275           real mode.  The base address of each segment is 0x100, 16 times the
 276           selector value being loaded here.  This is so that the segment
 277           registers don't have to be reloaded after switching to real mode:
 278           the values are consistent for real mode operation already. */
 279
 280        __asm__ __volatile__ ("movl $0x0010,%%eax\n"
 281                                "\tmovl %%eax,%%ds\n"
 282                                "\tmovl %%eax,%%es\n"
 283                                "\tmovl %%eax,%%fs\n"
 284                                "\tmovl %%eax,%%gs\n"
 285                                "\tmovl %%eax,%%ss" : : : "eax");
 286
 287        /* Jump to the 16-bit code that we copied earlier.  It disables paging
 288           and the cache, switches to real mode, and jumps to the BIOS reset
 289           entry point. */
 290
 291        __asm__ __volatile__ ("ljmp $0x0008,%0"
 292                                :
 293                                : "i" ((void *) (0x1000 - sizeof (real_mode_switch) - 100)));
 294}
 295#ifdef CONFIG_APM_MODULE
 296EXPORT_SYMBOL(machine_real_restart);
 297#endif
 298
 299static void native_machine_shutdown(void)
 300{
 301#ifdef CONFIG_SMP
 302        int reboot_cpu_id;
 303
 304        /* The boot cpu is always logical cpu 0 */
 305        reboot_cpu_id = 0;
 306
 307        /* See if there has been given a command line override */
 308        if ((reboot_cpu != -1) && (reboot_cpu < NR_CPUS) &&
 309                cpu_isset(reboot_cpu, cpu_online_map)) {
 310                reboot_cpu_id = reboot_cpu;
 311        }
 312
 313        /* Make certain the cpu I'm rebooting on is online */
 314        if (!cpu_isset(reboot_cpu_id, cpu_online_map)) {
 315                reboot_cpu_id = smp_processor_id();
 316        }
 317
 318        /* Make certain I only run on the appropriate processor */
 319        set_cpus_allowed(current, cpumask_of_cpu(reboot_cpu_id));
 320
 321        /* O.K. Now that I'm on the appropriate processor, stop
 322         * all of the others, and disable their local APICs.
 323         */
 324
 325        smp_send_stop();
 326#endif /* CONFIG_SMP */
 327
 328        lapic_shutdown();
 329
 330#ifdef CONFIG_X86_IO_APIC
 331        disable_IO_APIC();
 332#endif
 333}
 334
 335void __attribute__((weak)) mach_reboot_fixups(void)
 336{
 337}
 338
 339static void native_machine_emergency_restart(void)
 340{
 341        if (!reboot_thru_bios) {
 342                if (efi_enabled) {
 343                        efi.reset_system(EFI_RESET_COLD, EFI_SUCCESS, 0, NULL);
 344                        load_idt(&no_idt);
 345                        __asm__ __volatile__("int3");
 346                }
 347                /* rebooting needs to touch the page at absolute addr 0 */
 348                *((unsigned short *)__va(0x472)) = reboot_mode;
 349                for (;;) {
 350                        mach_reboot_fixups(); /* for board specific fixups */
 351                        mach_reboot();
 352                        /* That didn't work - force a triple fault.. */
 353                        load_idt(&no_idt);
 354                        __asm__ __volatile__("int3");
 355                }
 356        }
 357        if (efi_enabled)
 358                efi.reset_system(EFI_RESET_WARM, EFI_SUCCESS, 0, NULL);
 359
 360        machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
 361}
 362
 363static void native_machine_restart(char * __unused)
 364{
 365        machine_shutdown();
 366        machine_emergency_restart();
 367}
 368
 369static void native_machine_halt(void)
 370{
 371}
 372
 373static void native_machine_power_off(void)
 374{
 375        if (pm_power_off) {
 376                machine_shutdown();
 377                pm_power_off();
 378        }
 379}
 380
 381
 382struct machine_ops machine_ops = {
 383        .power_off = native_machine_power_off,
 384        .shutdown = native_machine_shutdown,
 385        .emergency_restart = native_machine_emergency_restart,
 386        .restart = native_machine_restart,
 387        .halt = native_machine_halt,
 388};
 389
 390void machine_power_off(void)
 391{
 392        machine_ops.power_off();
 393}
 394
 395void machine_shutdown(void)
 396{
 397        machine_ops.shutdown();
 398}
 399
 400void machine_emergency_restart(void)
 401{
 402        machine_ops.emergency_restart();
 403}
 404
 405void machine_restart(char *cmd)
 406{
 407        machine_ops.restart(cmd);
 408}
 409
 410void machine_halt(void)
 411{
 412        machine_ops.halt();
 413}
 414
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.