1#include <linux/module.h>
2#include <linux/reboot.h>
3#include <linux/init.h>
4#include <linux/pm.h>
5#include <linux/efi.h>
6#include <linux/dmi.h>
7#include <linux/sched.h>
8#include <linux/tboot.h>
9#include <acpi/reboot.h>
10#include <asm/io.h>
11#include <asm/apic.h>
12#include <asm/desc.h>
13#include <asm/hpet.h>
14#include <asm/pgtable.h>
15#include <asm/proto.h>
16#include <asm/reboot_fixups.h>
17#include <asm/reboot.h>
18#include <asm/pci_x86.h>
19#include <asm/virtext.h>
20#include <asm/cpu.h>
21
22#ifdef CONFIG_X86_32
23# include <linux/ctype.h>
24# include <linux/mc146818rtc.h>
25#else
26# include <asm/iommu.h>
27#endif
28
29
30
31
32void (*pm_power_off)(void);
33EXPORT_SYMBOL(pm_power_off);
34
35static const struct desc_ptr no_idt = {};
36static int reboot_mode;
37enum reboot_type reboot_type = BOOT_KBD;
38int reboot_force;
39
40#if defined(CONFIG_X86_32) && defined(CONFIG_SMP)
41static int reboot_cpu = -1;
42#endif
43
44
45
46
47
48static int reboot_emergency;
49
50
51bool port_cf9_safe = false;
52
53
54
55
56
57
58
59
60
61
62
63
64
65static int __init reboot_setup(char *str)
66{
67 for (;;) {
68 switch (*str) {
69 case 'w':
70 reboot_mode = 0x1234;
71 break;
72
73 case 'c':
74 reboot_mode = 0;
75 break;
76
77#ifdef CONFIG_X86_32
78#ifdef CONFIG_SMP
79 case 's':
80 if (isdigit(*(str+1))) {
81 reboot_cpu = (int) (*(str+1) - '0');
82 if (isdigit(*(str+2)))
83 reboot_cpu = reboot_cpu*10 + (int)(*(str+2) - '0');
84 }
85
86
87
88 break;
89#endif
90
91 case 'b':
92#endif
93 case 'a':
94 case 'k':
95 case 't':
96 case 'e':
97 case 'p':
98 reboot_type = *str;
99 break;
100
101 case 'f':
102 reboot_force = 1;
103 break;
104 }
105
106 str = strchr(str, ',');
107 if (str)
108 str++;
109 else
110 break;
111 }
112 return 1;
113}
114
115__setup("reboot=", reboot_setup);
116
117
118#ifdef CONFIG_X86_32
119
120
121
122
123
124
125
126
127
128static int __init set_bios_reboot(const struct dmi_system_id *d)
129{
130 if (reboot_type != BOOT_BIOS) {
131 reboot_type = BOOT_BIOS;
132 printk(KERN_INFO "%s series board detected. Selecting BIOS-method for reboots.\n", d->ident);
133 }
134 return 0;
135}
136
137static struct dmi_system_id __initdata reboot_dmi_table[] = {
138 {
139 .callback = set_bios_reboot,
140 .ident = "Dell E520",
141 .matches = {
142 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
143 DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM061"),
144 },
145 },
146 {
147 .callback = set_bios_reboot,
148 .ident = "Dell PowerEdge 1300",
149 .matches = {
150 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
151 DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"),
152 },
153 },
154 {
155 .callback = set_bios_reboot,
156 .ident = "Dell PowerEdge 300",
157 .matches = {
158 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
159 DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
160 },
161 },
162 {
163 .callback = set_bios_reboot,
164 .ident = "Dell OptiPlex 745",
165 .matches = {
166 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
167 DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
168 },
169 },
170 {
171 .callback = set_bios_reboot,
172 .ident = "Dell OptiPlex 745",
173 .matches = {
174 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
175 DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
176 DMI_MATCH(DMI_BOARD_NAME, "0MM599"),
177 },
178 },
179 {
180 .callback = set_bios_reboot,
181 .ident = "Dell OptiPlex 745",
182 .matches = {
183 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
184 DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
185 DMI_MATCH(DMI_BOARD_NAME, "0KW626"),
186 },
187 },
188 {
189 .callback = set_bios_reboot,
190 .ident = "Dell OptiPlex 330",
191 .matches = {
192 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
193 DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 330"),
194 DMI_MATCH(DMI_BOARD_NAME, "0KP561"),
195 },
196 },
197 {
198 .callback = set_bios_reboot,
199 .ident = "Dell OptiPlex 360",
200 .matches = {
201 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
202 DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 360"),
203 DMI_MATCH(DMI_BOARD_NAME, "0T656F"),
204 },
205 },
206 {
207 .callback = set_bios_reboot,
208 .ident = "Dell PowerEdge 2400",
209 .matches = {
210 DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
211 DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"),
212 },
213 },
214 {
215 .callback = set_bios_reboot,
216 .ident = "Dell Precision T5400",
217 .matches = {
218 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
219 DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T5400"),
220 },
221 },
222 {
223 .callback = set_bios_reboot,
224 .ident = "HP Compaq Laptop",
225 .matches = {
226 DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
227 DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq"),
228 },
229 },
230 {
231 .callback = set_bios_reboot,
232 .ident = "Dell XPS710",
233 .matches = {
234 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
235 DMI_MATCH(DMI_PRODUCT_NAME, "Dell XPS710"),
236 },
237 },
238 {
239 .callback = set_bios_reboot,
240 .ident = "Dell DXP061",
241 .matches = {
242 DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
243 DMI_MATCH(DMI_PRODUCT_NAME, "Dell DXP061"),
244 },
245 },
246 {
247 .callback = set_bios_reboot,
248 .ident = "Sony VGN-Z540N",
249 .matches = {
250 DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
251 DMI_MATCH(DMI_PRODUCT_NAME, "VGN-Z540N"),
252 },
253 },
254 {
255 .callback = set_bios_reboot,
256 .ident = "CompuLab SBC-FITPC2",
257 .matches = {
258 DMI_MATCH(DMI_SYS_VENDOR, "CompuLab"),
259 DMI_MATCH(DMI_PRODUCT_NAME, "SBC-FITPC2"),
260 },
261 },
262 { }
263};
264
265static int __init reboot_init(void)
266{
267 dmi_check_system(reboot_dmi_table);
268 return 0;
269}
270core_initcall(reboot_init);
271
272
273
274
275
276
277
278static const unsigned long long
279real_mode_gdt_entries [3] =
280{
281 0x0000000000000000ULL,
282 0x00009b000000ffffULL,
283 0x000093000100ffffULL
284};
285
286static const struct desc_ptr
287real_mode_gdt = { sizeof (real_mode_gdt_entries) - 1, (long)real_mode_gdt_entries },
288real_mode_idt = { 0x3ff, 0 };
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308static const unsigned char real_mode_switch [] =
309{
310 0x66, 0x0f, 0x20, 0xc0,
311 0x66, 0x83, 0xe0, 0x11,
312 0x66, 0x0d, 0x00, 0x00, 0x00, 0x60,
313 0x66, 0x0f, 0x22, 0xc0,
314 0x66, 0x0f, 0x22, 0xd8,
315 0x66, 0x0f, 0x20, 0xc3,
316 0x66, 0x81, 0xe3, 0x00, 0x00, 0x00, 0x60,
317 0x74, 0x02,
318 0x0f, 0x09,
319 0x24, 0x10,
320 0x66, 0x0f, 0x22, 0xc0
321};
322static const unsigned char jump_to_bios [] =
323{
324 0xea, 0x00, 0x00, 0xff, 0xff
325};
326
327
328
329
330
331
332void machine_real_restart(const unsigned char *code, int length)
333{
334 local_irq_disable();
335
336
337
338
339
340
341
342
343
344
345 spin_lock(&rtc_lock);
346 CMOS_WRITE(0x00, 0x8f);
347 spin_unlock(&rtc_lock);
348
349
350
351
352 memcpy(swapper_pg_dir, swapper_pg_dir + KERNEL_PGD_BOUNDARY,
353 sizeof(swapper_pg_dir [0]) * KERNEL_PGD_PTRS);
354
355
356
357
358 load_cr3(swapper_pg_dir);
359
360
361
362
363
364
365 *((unsigned short *)0x472) = reboot_mode;
366
367
368
369
370
371
372 memcpy((void *)(0x1000 - sizeof(real_mode_switch) - 100),
373 real_mode_switch, sizeof (real_mode_switch));
374 memcpy((void *)(0x1000 - 100), code, length);
375
376
377 load_idt(&real_mode_idt);
378
379
380
381
382 load_gdt(&real_mode_gdt);
383
384
385
386
387
388
389 __asm__ __volatile__ ("movl $0x0010,%%eax\n"
390 "\tmovl %%eax,%%ds\n"
391 "\tmovl %%eax,%%es\n"
392 "\tmovl %%eax,%%fs\n"
393 "\tmovl %%eax,%%gs\n"
394 "\tmovl %%eax,%%ss" : : : "eax");
395
396
397
398
399 __asm__ __volatile__ ("ljmp $0x0008,%0"
400 :
401 : "i" ((void *)(0x1000 - sizeof (real_mode_switch) - 100)));
402}
403#ifdef CONFIG_APM_MODULE
404EXPORT_SYMBOL(machine_real_restart);
405#endif
406
407#endif
408
409
410
411
412static int __init set_pci_reboot(const struct dmi_system_id *d)
413{
414 if (reboot_type != BOOT_CF9) {
415 reboot_type = BOOT_CF9;
416 printk(KERN_INFO "%s series board detected. "
417 "Selecting PCI-method for reboots.\n", d->ident);
418 }
419 return 0;
420}
421
422static struct dmi_system_id __initdata pci_reboot_dmi_table[] = {
423 {
424 .callback = set_pci_reboot,
425 .ident = "Apple MacBook5",
426 .matches = {
427 DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
428 DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5"),
429 },
430 },
431 {
432 .callback = set_pci_reboot,
433 .ident = "Apple MacBookPro5",
434 .matches = {
435 DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
436 DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5"),
437 },
438 },
439 {
440 .callback = set_pci_reboot,
441 .ident = "Apple Macmini3,1",
442 .matches = {
443 DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
444 DMI_MATCH(DMI_PRODUCT_NAME, "Macmini3,1"),
445 },
446 },
447 { }
448};
449
450static int __init pci_reboot_init(void)
451{
452 dmi_check_system(pci_reboot_dmi_table);
453 return 0;
454}
455core_initcall(pci_reboot_init);
456
457static inline void kb_wait(void)
458{
459 int i;
460
461 for (i = 0; i < 0x10000; i++) {
462 if ((inb(0x64) & 0x02) == 0)
463 break;
464 udelay(2);
465 }
466}
467
468static void vmxoff_nmi(int cpu, struct die_args *args)
469{
470 cpu_emergency_vmxoff();
471}
472
473
474
475static void emergency_vmx_disable_all(void)
476{
477
478 local_irq_disable();
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498 if (cpu_has_vmx() && cpu_vmx_enabled()) {
499
500
501 cpu_vmxoff();
502
503
504 nmi_shootdown_cpus(vmxoff_nmi);
505
506 }
507}
508
509
510void __attribute__((weak)) mach_reboot_fixups(void)
511{
512}
513
514static void native_machine_emergency_restart(void)
515{
516 int i;
517
518 if (reboot_emergency)
519 emergency_vmx_disable_all();
520
521 tboot_shutdown(TB_SHUTDOWN_REBOOT);
522
523
524 *((unsigned short *)__va(0x472)) = reboot_mode;
525
526 for (;;) {
527
528 switch (reboot_type) {
529 case BOOT_KBD:
530 mach_reboot_fixups();
531
532 for (i = 0; i < 10; i++) {
533 kb_wait();
534 udelay(50);
535 outb(0xfe, 0x64);
536 udelay(50);
537 }
538
539 case BOOT_TRIPLE:
540 load_idt(&no_idt);
541 __asm__ __volatile__("int3");
542
543 reboot_type = BOOT_KBD;
544 break;
545
546#ifdef CONFIG_X86_32
547 case BOOT_BIOS:
548 machine_real_restart(jump_to_bios, sizeof(jump_to_bios));
549
550 reboot_type = BOOT_KBD;
551 break;
552#endif
553
554 case BOOT_ACPI:
555 acpi_reboot();
556 reboot_type = BOOT_KBD;
557 break;
558
559 case BOOT_EFI:
560 if (efi_enabled)
561 efi.reset_system(reboot_mode ?
562 EFI_RESET_WARM :
563 EFI_RESET_COLD,
564 EFI_SUCCESS, 0, NULL);
565 reboot_type = BOOT_KBD;
566 break;
567
568 case BOOT_CF9:
569 port_cf9_safe = true;
570
571
572 case BOOT_CF9_COND:
573 if (port_cf9_safe) {
574 u8 cf9 = inb(0xcf9) & ~6;
575 outb(cf9|2, 0xcf9);
576 udelay(50);
577 outb(cf9|6, 0xcf9);
578 udelay(50);
579 }
580 reboot_type = BOOT_KBD;
581 break;
582 }
583 }
584}
585
586void native_machine_shutdown(void)
587{
588
589#ifdef CONFIG_SMP
590
591
592 int reboot_cpu_id = 0;
593
594#ifdef CONFIG_X86_32
595
596 if ((reboot_cpu != -1) && (reboot_cpu < nr_cpu_ids) &&
597 cpu_online(reboot_cpu))
598 reboot_cpu_id = reboot_cpu;
599#endif
600
601
602 if (!cpu_online(reboot_cpu_id))
603 reboot_cpu_id = smp_processor_id();
604
605
606 set_cpus_allowed_ptr(current, cpumask_of(reboot_cpu_id));
607
608
609
610
611 smp_send_stop();
612#endif
613
614 lapic_shutdown();
615
616#ifdef CONFIG_X86_IO_APIC
617 disable_IO_APIC();
618#endif
619
620#ifdef CONFIG_HPET_TIMER
621 hpet_disable();
622#endif
623
624#ifdef CONFIG_X86_64
625 pci_iommu_shutdown();
626#endif
627}
628
629static void __machine_emergency_restart(int emergency)
630{
631 reboot_emergency = emergency;
632 machine_ops.emergency_restart();
633}
634
635static void native_machine_restart(char *__unused)
636{
637 printk("machine restart\n");
638
639 if (!reboot_force)
640 machine_shutdown();
641 __machine_emergency_restart(0);
642}
643
644static void native_machine_halt(void)
645{
646
647 machine_shutdown();
648
649 tboot_shutdown(TB_SHUTDOWN_HALT);
650
651
652 stop_this_cpu(NULL);
653}
654
655static void native_machine_power_off(void)
656{
657 if (pm_power_off) {
658 if (!reboot_force)
659 machine_shutdown();
660 pm_power_off();
661 }
662
663 tboot_shutdown(TB_SHUTDOWN_HALT);
664}
665
666struct machine_ops machine_ops = {
667 .power_off = native_machine_power_off,
668 .shutdown = native_machine_shutdown,
669 .emergency_restart = native_machine_emergency_restart,
670 .restart = native_machine_restart,
671 .halt = native_machine_halt,
672#ifdef CONFIG_KEXEC
673 .crash_shutdown = native_machine_crash_shutdown,
674#endif
675};
676
677void machine_power_off(void)
678{
679 machine_ops.power_off();
680}
681
682void machine_shutdown(void)
683{
684 machine_ops.shutdown();
685}
686
687void machine_emergency_restart(void)
688{
689 __machine_emergency_restart(1);
690}
691
692void machine_restart(char *cmd)
693{
694 machine_ops.restart(cmd);
695}
696
697void machine_halt(void)
698{
699 machine_ops.halt();
700}
701
702#ifdef CONFIG_KEXEC
703void machine_crash_shutdown(struct pt_regs *regs)
704{
705 machine_ops.crash_shutdown(regs);
706}
707#endif
708
709
710#if defined(CONFIG_SMP)
711
712
713static int crashing_cpu;
714static nmi_shootdown_cb shootdown_callback;
715
716static atomic_t waiting_for_crash_ipi;
717
718static int crash_nmi_callback(struct notifier_block *self,
719 unsigned long val, void *data)
720{
721 int cpu;
722
723 if (val != DIE_NMI_IPI)
724 return NOTIFY_OK;
725
726 cpu = raw_smp_processor_id();
727
728
729
730
731
732 if (cpu == crashing_cpu)
733 return NOTIFY_STOP;
734 local_irq_disable();
735
736 shootdown_callback(cpu, (struct die_args *)data);
737
738 atomic_dec(&waiting_for_crash_ipi);
739
740 halt();
741 for (;;)
742 cpu_relax();
743
744 return 1;
745}
746
747static void smp_send_nmi_allbutself(void)
748{
749 apic->send_IPI_allbutself(NMI_VECTOR);
750}
751
752static struct notifier_block crash_nmi_nb = {
753 .notifier_call = crash_nmi_callback,
754};
755
756
757
758
759
760
761
762void nmi_shootdown_cpus(nmi_shootdown_cb callback)
763{
764 unsigned long msecs;
765 local_irq_disable();
766
767
768 crashing_cpu = safe_smp_processor_id();
769
770 shootdown_callback = callback;
771
772 atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
773
774 if (register_die_notifier(&crash_nmi_nb))
775 return;
776
777
778
779 wmb();
780
781 smp_send_nmi_allbutself();
782
783 msecs = 1000;
784 while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
785 mdelay(1);
786 msecs--;
787 }
788
789
790}
791#else
792void nmi_shootdown_cpus(nmi_shootdown_cb callback)
793{
794
795}
796#endif
797