2012-05-21 19:50:07 -07:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2005-04-16 15:20:36 -07:00
# include <linux/module.h>
2006-12-06 20:40:06 -08:00
# include <linux/reboot.h>
2008-01-30 13:32:51 +01:00
# include <linux/init.h>
# include <linux/pm.h>
# include <linux/efi.h>
2009-08-03 22:47:32 +10:00
# include <linux/dmi.h>
2009-10-07 17:09:06 +04:00
# include <linux/sched.h>
2009-09-01 18:25:07 -07:00
# include <linux/tboot.h>
2011-03-25 15:20:14 +01:00
# include <linux/delay.h>
2008-01-30 13:32:51 +01:00
# include <acpi/reboot.h>
# include <asm/io.h>
2005-04-16 15:20:36 -07:00
# include <asm/apic.h>
2005-09-03 15:56:38 -07:00
# include <asm/desc.h>
2008-01-30 13:32:51 +01:00
# include <asm/hpet.h>
2008-03-17 16:37:13 -07:00
# include <asm/pgtable.h>
2008-04-28 03:15:59 +04:00
# include <asm/proto.h>
2007-05-02 19:27:06 +02:00
# include <asm/reboot_fixups.h>
2007-05-02 19:27:11 +02:00
# include <asm/reboot.h>
2008-12-27 18:32:28 +05:30
# include <asm/pci_x86.h>
2008-11-17 19:03:24 -02:00
# include <asm/virtext.h>
2009-01-07 21:35:48 +05:30
# include <asm/cpu.h>
2011-01-06 16:18:50 -05:00
# include <asm/nmi.h>
2012-06-16 21:47:37 -07:00
# include <asm/smp.h>
2005-04-16 15:20:36 -07:00
2012-06-16 21:47:37 -07:00
# include <linux/ctype.h>
# include <linux/mc146818rtc.h>
# include <asm/realmode.h>
# include <asm/x86_init.h>
2008-01-30 13:32:51 +01:00
2005-04-16 15:20:36 -07:00
/*
* Power off function , if any
*/
void ( * pm_power_off ) ( void ) ;
2005-06-23 00:08:33 -07:00
EXPORT_SYMBOL ( pm_power_off ) ;
2005-04-16 15:20:36 -07:00
2008-05-12 15:43:38 +02:00
static const struct desc_ptr no_idt = { } ;
2008-01-30 13:32:51 +01:00
2012-02-01 10:06:34 -05:00
/*
* This is set if we need to go through the ' emergency ' path .
2008-11-17 19:03:24 -02:00
* When machine_emergency_restart ( ) is called , we may be on
* an inconsistent state and won ' t be able to do a clean cleanup
*/
static int reboot_emergency ;
2008-11-11 16:19:48 -08:00
/* This is set by the PCI code if either type 1 or type 2 PCI is detected */
bool port_cf9_safe = false ;
2005-04-16 15:20:36 -07:00
/*
* Reboot options and system auto - detection code provided by
* Dell Inc . so their systems " just work " . : - )
*/
/*
2011-12-05 16:53:53 +03:00
* Some machines require the " reboot=b " or " reboot=k " commandline options ,
2008-01-30 13:32:51 +01:00
* this quirk makes that automatic .
2005-04-16 15:20:36 -07:00
*/
2007-10-03 15:15:40 -04:00
static int __init set_bios_reboot ( const struct dmi_system_id * d )
2005-04-16 15:20:36 -07:00
{
2008-01-30 13:32:51 +01:00
if ( reboot_type ! = BOOT_BIOS ) {
reboot_type = BOOT_BIOS ;
2012-05-21 19:50:07 -07:00
pr_info ( " %s series board detected. Selecting %s-method for reboots. \n " ,
" BIOS " , d - > ident ) ;
2005-04-16 15:20:36 -07:00
}
return 0 ;
}
2012-06-16 21:47:37 -07:00
void __noreturn machine_real_restart ( unsigned int type )
2012-02-01 10:05:00 -05:00
{
local_irq_disable ( ) ;
2012-02-01 10:06:34 -05:00
/*
* Write zero to CMOS register number 0x0f , which the BIOS POST
* routine will recognize as telling it to do a proper reboot . ( Well
* that ' s what this book in front of me says - - it may only apply to
* the Phoenix BIOS though , it ' s not clear ) . At the same time ,
* disable NMIs by setting the top bit in the CMOS address register ,
* as we ' re about to do peculiar things to the CPU . I ' m not sure if
* ` outb_p ' is needed instead of just ` outb ' . Use it to be on the
* safe side . ( Yes , CMOS_WRITE does outb_p ' s . - Paul G . )
2012-02-01 10:05:00 -05:00
*/
spin_lock ( & rtc_lock ) ;
CMOS_WRITE ( 0x00 , 0x8f ) ;
spin_unlock ( & rtc_lock ) ;
/*
* Switch back to the initial page table .
*/
2012-06-16 21:47:37 -07:00
# ifdef CONFIG_X86_32
2012-02-01 10:05:00 -05:00
load_cr3 ( initial_page_table ) ;
2012-06-16 21:47:37 -07:00
# else
write_cr3 ( real_mode_header - > trampoline_pgd ) ;
# endif
2012-02-01 10:05:00 -05:00
/* Jump to the identity-mapped low memory code */
2012-06-16 21:47:37 -07:00
# ifdef CONFIG_X86_32
asm volatile ( " jmpl *%0 " : :
" rm " ( real_mode_header - > machine_real_restart_asm ) ,
" a " ( type ) ) ;
# else
asm volatile ( " ljmpl *%0 " : :
" m " ( real_mode_header - > machine_real_restart_asm ) ,
" D " ( type ) ) ;
# endif
unreachable ( ) ;
2012-02-01 10:05:00 -05:00
}
# ifdef CONFIG_APM_MODULE
EXPORT_SYMBOL ( machine_real_restart ) ;
# endif
/*
* Some Apple MacBook and MacBookPro ' s needs reboot = p to be able to reboot
*/
static int __init set_pci_reboot ( const struct dmi_system_id * d )
{
if ( reboot_type ! = BOOT_CF9 ) {
reboot_type = BOOT_CF9 ;
2012-05-21 19:50:07 -07:00
pr_info ( " %s series board detected. Selecting %s-method for reboots. \n " ,
" PCI " , d - > ident ) ;
2012-02-01 10:05:00 -05:00
}
return 0 ;
}
2011-12-05 16:53:53 +03:00
static int __init set_kbd_reboot ( const struct dmi_system_id * d )
{
if ( reboot_type ! = BOOT_KBD ) {
reboot_type = BOOT_KBD ;
2012-05-21 19:50:07 -07:00
pr_info ( " %s series board detected. Selecting %s-method for reboot. \n " ,
" KBD " , d - > ident ) ;
2011-12-05 16:53:53 +03:00
}
return 0 ;
}
2012-02-01 10:06:34 -05:00
/*
2012-06-16 21:47:37 -07:00
* This is a single dmi_table handling all reboot quirks .
2012-02-01 10:05:00 -05:00
*/
2005-04-16 15:20:36 -07:00
static struct dmi_system_id __initdata reboot_dmi_table [ ] = {
2007-06-01 00:46:40 -07:00
{ /* Handle problems with rebooting on Dell E520's */
. callback = set_bios_reboot ,
. ident = " Dell E520 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Dell DM061 " ) ,
} ,
} ,
2005-04-16 15:20:36 -07:00
{ /* Handle problems with rebooting on Dell 1300's */
2005-06-25 14:57:55 -07:00
. callback = set_bios_reboot ,
2005-04-16 15:20:36 -07:00
. ident = " Dell PowerEdge 1300 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Computer Corporation " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " PowerEdge 1300/ " ) ,
} ,
} ,
{ /* Handle problems with rebooting on Dell 300's */
. callback = set_bios_reboot ,
. ident = " Dell PowerEdge 300 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Computer Corporation " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " PowerEdge 300/ " ) ,
} ,
} ,
2012-02-01 10:06:34 -05:00
{ /* Handle problems with rebooting on Dell Optiplex 745's SFF */
2007-07-21 17:11:11 +02:00
. callback = set_bios_reboot ,
. ident = " Dell OptiPlex 745 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " OptiPlex 745 " ) ,
} ,
} ,
2012-02-01 10:06:34 -05:00
{ /* Handle problems with rebooting on Dell Optiplex 745's DFF */
2008-03-04 15:05:41 -08:00
. callback = set_bios_reboot ,
. ident = " Dell OptiPlex 745 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " OptiPlex 745 " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " 0MM599 " ) ,
} ,
} ,
2012-02-01 10:06:34 -05:00
{ /* Handle problems with rebooting on Dell Optiplex 745 with 0KW626 */
2008-03-12 16:27:56 +01:00
. callback = set_bios_reboot ,
. ident = " Dell OptiPlex 745 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " OptiPlex 745 " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " 0KW626 " ) ,
} ,
} ,
2012-02-01 10:06:34 -05:00
{ /* Handle problems with rebooting on Dell Optiplex 330 with 0KP561 */
2008-11-14 00:55:51 -06:00
. callback = set_bios_reboot ,
. ident = " Dell OptiPlex 330 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " OptiPlex 330 " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " 0KP561 " ) ,
} ,
} ,
2012-02-01 10:06:34 -05:00
{ /* Handle problems with rebooting on Dell Optiplex 360 with 0T656F */
2009-06-05 12:02:38 +02:00
. callback = set_bios_reboot ,
. ident = " Dell OptiPlex 360 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " OptiPlex 360 " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " 0T656F " ) ,
} ,
} ,
2012-02-01 10:06:34 -05:00
{ /* Handle problems with rebooting on Dell OptiPlex 760 with 0G919G */
2010-01-27 15:29:18 -08:00
. callback = set_bios_reboot ,
. ident = " Dell OptiPlex 760 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " OptiPlex 760 " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " 0G919G " ) ,
} ,
} ,
2005-04-16 15:20:36 -07:00
{ /* Handle problems with rebooting on Dell 2400's */
. callback = set_bios_reboot ,
. ident = " Dell PowerEdge 2400 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Computer Corporation " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " PowerEdge 2400 " ) ,
} ,
} ,
2008-07-17 13:50:15 +02:00
{ /* Handle problems with rebooting on Dell T5400's */
. callback = set_bios_reboot ,
. ident = " Dell Precision T5400 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Precision WorkStation T5400 " ) ,
} ,
} ,
2010-06-19 16:32:25 +03:00
{ /* Handle problems with rebooting on Dell T7400's */
. callback = set_bios_reboot ,
. ident = " Dell Precision T7400 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Precision WorkStation T7400 " ) ,
} ,
} ,
2006-01-06 00:12:20 -08:00
{ /* Handle problems with rebooting on HP laptops */
2005-11-29 19:34:35 -08:00
. callback = set_bios_reboot ,
2006-01-06 00:12:20 -08:00
. ident = " HP Compaq Laptop " ,
2005-11-29 19:34:35 -08:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Hewlett-Packard " ) ,
2006-01-06 00:12:20 -08:00
DMI_MATCH ( DMI_PRODUCT_NAME , " HP Compaq " ) ,
2005-11-29 19:34:35 -08:00
} ,
} ,
2009-03-04 11:53:00 -08:00
{ /* Handle problems with rebooting on Dell XPS710 */
. callback = set_bios_reboot ,
. ident = " Dell XPS710 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Dell XPS710 " ) ,
} ,
} ,
2009-03-26 20:45:28 +00:00
{ /* Handle problems with rebooting on Dell DXP061 */
. callback = set_bios_reboot ,
. ident = " Dell DXP061 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Dell DXP061 " ) ,
} ,
} ,
2009-05-22 11:35:50 +08:00
{ /* Handle problems with rebooting on Sony VGN-Z540N */
. callback = set_bios_reboot ,
. ident = " Sony VGN-Z540N " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Sony Corporation " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " VGN-Z540N " ) ,
} ,
} ,
2012-02-01 10:06:34 -05:00
{ /* Handle problems with rebooting on ASUS P4S800 */
2009-12-04 15:42:22 -08:00
. callback = set_bios_reboot ,
. ident = " ASUS P4S800 " ,
. matches = {
DMI_MATCH ( DMI_BOARD_VENDOR , " ASUSTeK Computer INC. " ) ,
DMI_MATCH ( DMI_BOARD_NAME , " P4S800 " ) ,
} ,
} ,
2012-02-01 10:05:00 -05:00
2012-02-01 10:06:34 -05:00
{ /* Handle reboot issue on Acer Aspire one */
2011-12-05 16:53:53 +03:00
. callback = set_kbd_reboot ,
2011-07-06 10:56:30 +10:00
. ident = " Acer Aspire One A110 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Acer " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " AOA110 " ) ,
} ,
} ,
2009-08-11 03:34:40 +09:00
{ /* Handle problems with rebooting on Apple MacBook5 */
2009-08-03 22:47:32 +10:00
. callback = set_pci_reboot ,
2009-08-11 03:34:40 +09:00
. ident = " Apple MacBook5 " ,
2009-08-03 22:47:32 +10:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Apple Inc. " ) ,
2009-08-11 03:34:40 +09:00
DMI_MATCH ( DMI_PRODUCT_NAME , " MacBook5 " ) ,
2009-08-03 22:47:32 +10:00
} ,
} ,
2009-08-11 03:34:40 +09:00
{ /* Handle problems with rebooting on Apple MacBookPro5 */
2009-08-04 19:39:31 +03:00
. callback = set_pci_reboot ,
2009-08-11 03:34:40 +09:00
. ident = " Apple MacBookPro5 " ,
2009-08-04 19:39:31 +03:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Apple Inc. " ) ,
2009-08-11 03:34:40 +09:00
DMI_MATCH ( DMI_PRODUCT_NAME , " MacBookPro5 " ) ,
2009-08-04 19:39:31 +03:00
} ,
} ,
2009-11-02 11:51:11 +01:00
{ /* Handle problems with rebooting on Apple Macmini3,1 */
. callback = set_pci_reboot ,
. ident = " Apple Macmini3,1 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Apple Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Macmini3,1 " ) ,
} ,
} ,
2010-02-16 15:17:29 -08:00
{ /* Handle problems with rebooting on the iMac9,1. */
. callback = set_pci_reboot ,
. ident = " Apple iMac9,1 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Apple Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " iMac9,1 " ) ,
} ,
} ,
2011-06-28 15:57:31 +02:00
{ /* Handle problems with rebooting on the Latitude E6320. */
. callback = set_pci_reboot ,
. ident = " Dell Latitude E6320 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Latitude E6320 " ) ,
} ,
} ,
2011-05-13 09:04:59 +08:00
{ /* Handle problems with rebooting on the Latitude E5420. */
. callback = set_pci_reboot ,
. ident = " Dell Latitude E5420 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Latitude E5420 " ) ,
} ,
} ,
2011-07-21 11:22:21 -07:00
{ /* Handle problems with rebooting on the Latitude E6420. */
. callback = set_pci_reboot ,
. ident = " Dell Latitude E6420 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Latitude E6420 " ) ,
} ,
} ,
2011-11-16 00:19:51 +01:00
{ /* Handle problems with rebooting on the OptiPlex 990. */
. callback = set_pci_reboot ,
. ident = " Dell OptiPlex 990 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " OptiPlex 990 " ) ,
} ,
} ,
2012-02-20 14:20:06 +08:00
{ /* Handle problems with rebooting on the Precision M6600. */
. callback = set_pci_reboot ,
2013-09-24 20:13:44 -04:00
. ident = " Dell Precision M6600 " ,
2012-02-20 14:20:06 +08:00
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " Precision M6600 " ) ,
} ,
} ,
2013-09-20 15:59:07 -07:00
{ /* Handle problems with rebooting on the Dell PowerEdge C6100. */
. callback = set_pci_reboot ,
. ident = " Dell PowerEdge C6100 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell Inc. " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " C6100 " ) ,
} ,
} ,
{ /* Some C6100 machines were shipped with vendor being 'Dell'. */
. callback = set_pci_reboot ,
. ident = " Dell PowerEdge C6100 " ,
. matches = {
DMI_MATCH ( DMI_SYS_VENDOR , " Dell " ) ,
DMI_MATCH ( DMI_PRODUCT_NAME , " C6100 " ) ,
} ,
} ,
2009-08-03 22:47:32 +10:00
{ }
} ;
2012-02-01 10:05:00 -05:00
static int __init reboot_init ( void )
2009-08-03 22:47:32 +10:00
{
2012-02-01 10:06:34 -05:00
/*
* Only do the DMI check if reboot_type hasn ' t been overridden
2012-01-29 14:17:22 -05:00
* on the command line
*/
2012-02-01 10:06:34 -05:00
if ( reboot_default )
2012-02-01 10:05:00 -05:00
dmi_check_system ( reboot_dmi_table ) ;
2009-08-03 22:47:32 +10:00
return 0 ;
}
2012-02-01 10:05:00 -05:00
core_initcall ( reboot_init ) ;
2009-08-03 22:47:32 +10:00
2008-01-30 13:32:51 +01:00
static inline void kb_wait ( void )
{
int i ;
2008-01-30 13:33:25 +01:00
for ( i = 0 ; i < 0x10000 ; i + + ) {
if ( ( inb ( 0x64 ) & 0x02 ) = = 0 )
2008-01-30 13:32:51 +01:00
break ;
2008-01-30 13:33:25 +01:00
udelay ( 2 ) ;
}
2008-01-30 13:32:51 +01:00
}
2011-09-30 15:06:21 -04:00
static void vmxoff_nmi ( int cpu , struct pt_regs * regs )
2008-11-17 19:03:24 -02:00
{
cpu_emergency_vmxoff ( ) ;
}
2012-02-01 10:06:34 -05:00
/* Use NMIs as IPIs to tell all CPUs to disable virtualization */
2008-11-17 19:03:24 -02:00
static void emergency_vmx_disable_all ( void )
{
/* Just make sure we won't change CPUs while doing this */
local_irq_disable ( ) ;
2012-02-01 10:06:34 -05:00
/*
* We need to disable VMX on all CPUs before rebooting , otherwise
2008-11-17 19:03:24 -02:00
* we risk hanging up the machine , because the CPU ignore INIT
* signals when VMX is enabled .
*
* We can ' t take any locks and we may be on an inconsistent
* state , so we use NMIs as IPIs to tell the other CPUs to disable
* VMX and halt .
*
* For safety , we will avoid running the nmi_shootdown_cpus ( )
* stuff unnecessarily , but we don ' t have a way to check
* if other CPUs have VMX enabled . So we will call it only if the
* CPU we are running on has VMX enabled .
*
* We will miss cases where VMX is not enabled on all CPUs . This
* shouldn ' t do much harm because KVM always enable VMX on all
* CPUs anyway . But we can miss it on the small window where KVM
* is still enabling VMX .
*/
if ( cpu_has_vmx ( ) & & cpu_vmx_enabled ( ) ) {
2012-02-01 10:06:34 -05:00
/* Disable VMX on this CPU. */
2008-11-17 19:03:24 -02:00
cpu_vmxoff ( ) ;
/* Halt and disable VMX on the other CPUs */
nmi_shootdown_cpus ( vmxoff_nmi ) ;
}
}
2008-03-06 18:29:43 +01:00
void __attribute__ ( ( weak ) ) mach_reboot_fixups ( void )
{
}
2011-04-04 13:55:05 -04:00
/*
* Windows compatible x86 hardware expects the following on reboot :
*
* 1 ) If the FADT has the ACPI reboot register flag set , try it
* 2 ) If still alive , write to the keyboard controller
* 3 ) If still alive , write to the ACPI reboot register again
* 4 ) If still alive , write to the keyboard controller again
*
* If the machine is still alive at this stage , it gives up . We default to
* following the same pattern , except that if we ' re still alive after ( 4 ) we ' ll
* try to force a triple fault and then cycle between hitting the keyboard
* controller and doing that
*/
2008-02-12 23:37:48 +00:00
static void native_machine_emergency_restart ( void )
2005-04-16 15:20:36 -07:00
{
2008-01-30 13:32:51 +01:00
int i ;
2011-04-04 13:55:05 -04:00
int attempt = 0 ;
int orig_reboot_type = reboot_type ;
2013-07-08 16:01:35 -07:00
unsigned short mode ;
2008-01-30 13:32:51 +01:00
2008-11-17 19:03:24 -02:00
if ( reboot_emergency )
emergency_vmx_disable_all ( ) ;
2009-06-30 19:31:02 -07:00
tboot_shutdown ( TB_SHUTDOWN_REBOOT ) ;
2008-01-30 13:32:51 +01:00
/* Tell the BIOS if we want cold or warm reboot */
2013-07-08 16:01:35 -07:00
mode = reboot_mode = = REBOOT_WARM ? 0x1234 : 0 ;
* ( ( unsigned short * ) __va ( 0x472 ) ) = mode ;
2008-01-30 13:32:51 +01:00
for ( ; ; ) {
/* Could also try the reset bit in the Hammer NB */
switch ( reboot_type ) {
case BOOT_KBD :
2012-02-01 10:06:34 -05:00
mach_reboot_fixups ( ) ; /* For board specific fixups */
2008-03-06 18:29:43 +01:00
2008-01-30 13:32:51 +01:00
for ( i = 0 ; i < 10 ; i + + ) {
kb_wait ( ) ;
udelay ( 50 ) ;
2012-02-01 10:06:34 -05:00
outb ( 0xfe , 0x64 ) ; /* Pulse reset low */
2008-01-30 13:32:51 +01:00
udelay ( 50 ) ;
}
2011-04-04 13:55:05 -04:00
if ( attempt = = 0 & & orig_reboot_type = = BOOT_ACPI ) {
attempt = 1 ;
reboot_type = BOOT_ACPI ;
} else {
reboot_type = BOOT_TRIPLE ;
}
break ;
2008-01-30 13:32:51 +01:00
case BOOT_TRIPLE :
2008-05-12 15:43:38 +02:00
load_idt ( & no_idt ) ;
2008-01-30 13:32:51 +01:00
__asm__ __volatile__ ( " int3 " ) ;
reboot_type = BOOT_KBD ;
break ;
case BOOT_BIOS :
2011-02-14 18:36:03 -08:00
machine_real_restart ( MRR_BIOS ) ;
2008-01-30 13:32:51 +01:00
reboot_type = BOOT_KBD ;
break ;
case BOOT_ACPI :
acpi_reboot ( ) ;
reboot_type = BOOT_KBD ;
break ;
case BOOT_EFI :
2012-11-14 09:42:35 +00:00
if ( efi_enabled ( EFI_RUNTIME_SERVICES ) )
2013-07-08 16:01:35 -07:00
efi . reset_system ( reboot_mode = = REBOOT_WARM ?
2008-11-11 16:19:48 -08:00
EFI_RESET_WARM :
EFI_RESET_COLD ,
2008-01-30 13:32:51 +01:00
EFI_SUCCESS , 0 , NULL ) ;
2008-11-24 00:50:09 -08:00
reboot_type = BOOT_KBD ;
2008-11-11 16:19:48 -08:00
break ;
2008-01-30 13:32:51 +01:00
2008-11-11 16:19:48 -08:00
case BOOT_CF9 :
port_cf9_safe = true ;
2012-02-01 10:06:34 -05:00
/* Fall through */
2008-01-30 13:32:51 +01:00
2008-11-11 16:19:48 -08:00
case BOOT_CF9_COND :
if ( port_cf9_safe ) {
u8 cf9 = inb ( 0xcf9 ) & ~ 6 ;
outb ( cf9 | 2 , 0xcf9 ) ; /* Request hard reset */
udelay ( 50 ) ;
outb ( cf9 | 6 , 0xcf9 ) ; /* Actually do the reset */
udelay ( 50 ) ;
}
2008-01-30 13:32:51 +01:00
reboot_type = BOOT_KBD ;
break ;
}
}
}
2008-03-17 16:08:39 -03:00
void native_machine_shutdown ( void )
2008-01-30 13:32:51 +01:00
{
/* Stop the cpus and apics */
2005-04-16 15:20:36 -07:00
# ifdef CONFIG_SMP
2012-02-01 10:06:34 -05:00
/*
2013-07-08 16:01:42 -07:00
* Stop all of the others . Also disable the local irq to
* not receive the per - cpu timer interrupt which may trigger
* scheduler ' s load balance .
2005-04-16 15:20:36 -07:00
*/
2012-05-30 23:15:41 +08:00
local_irq_disable ( ) ;
2010-10-11 14:37:08 -07:00
stop_other_cpus ( ) ;
2008-01-30 13:32:51 +01:00
# endif
2005-04-16 15:20:36 -07:00
lapic_shutdown ( ) ;
# ifdef CONFIG_X86_IO_APIC
disable_IO_APIC ( ) ;
# endif
2008-01-30 13:32:51 +01:00
2007-12-03 17:17:10 +01:00
# ifdef CONFIG_HPET_TIMER
hpet_disable ( ) ;
# endif
2005-06-25 14:57:55 -07:00
2008-01-30 13:32:51 +01:00
# ifdef CONFIG_X86_64
2009-10-27 16:34:44 +09:00
x86_platform . iommu_shutdown ( ) ;
2008-01-30 13:32:51 +01:00
# endif
2007-05-02 19:27:06 +02:00
}
2008-11-17 19:03:24 -02:00
static void __machine_emergency_restart ( int emergency )
{
reboot_emergency = emergency ;
machine_ops . emergency_restart ( ) ;
}
2008-02-12 23:37:48 +00:00
static void native_machine_restart ( char * __unused )
2005-06-25 14:57:55 -07:00
{
2012-05-21 19:50:07 -07:00
pr_notice ( " machine restart \n " ) ;
2005-04-16 15:20:36 -07:00
2008-01-30 13:32:51 +01:00
if ( ! reboot_force )
machine_shutdown ( ) ;
2008-11-17 19:03:24 -02:00
__machine_emergency_restart ( 0 ) ;
2005-07-26 11:41:26 -06:00
}
2008-02-12 23:37:48 +00:00
static void native_machine_halt ( void )
2005-04-16 15:20:36 -07:00
{
2012-02-01 10:06:34 -05:00
/* Stop other cpus and apics */
2008-11-11 14:33:44 +01:00
machine_shutdown ( ) ;
2009-06-30 19:31:02 -07:00
tboot_shutdown ( TB_SHUTDOWN_HALT ) ;
2008-11-11 14:33:44 +01:00
stop_this_cpu ( NULL ) ;
2005-04-16 15:20:36 -07:00
}
2008-02-12 23:37:48 +00:00
static void native_machine_power_off ( void )
2005-04-16 15:20:36 -07:00
{
2006-01-11 22:43:12 +01:00
if ( pm_power_off ) {
2008-01-30 13:32:51 +01:00
if ( ! reboot_force )
machine_shutdown ( ) ;
2005-04-16 15:20:36 -07:00
pm_power_off ( ) ;
2006-01-11 22:43:12 +01:00
}
2012-02-01 10:06:34 -05:00
/* A fallback in case there is no PM info available */
2009-06-30 19:31:02 -07:00
tboot_shutdown ( TB_SHUTDOWN_HALT ) ;
2005-04-16 15:20:36 -07:00
}
2007-05-02 19:27:11 +02:00
struct machine_ops machine_ops = {
2008-02-12 23:37:48 +00:00
. power_off = native_machine_power_off ,
. shutdown = native_machine_shutdown ,
. emergency_restart = native_machine_emergency_restart ,
. restart = native_machine_restart ,
2008-03-17 16:08:38 -03:00
. halt = native_machine_halt ,
# ifdef CONFIG_KEXEC
. crash_shutdown = native_machine_crash_shutdown ,
# endif
2007-05-02 19:27:11 +02:00
} ;
2008-02-12 23:37:48 +00:00
void machine_power_off ( void )
{
machine_ops . power_off ( ) ;
}
void machine_shutdown ( void )
{
machine_ops . shutdown ( ) ;
}
void machine_emergency_restart ( void )
{
2008-11-17 19:03:24 -02:00
__machine_emergency_restart ( 1 ) ;
2008-02-12 23:37:48 +00:00
}
void machine_restart ( char * cmd )
{
machine_ops . restart ( cmd ) ;
}
void machine_halt ( void )
{
machine_ops . halt ( ) ;
}
2008-03-17 16:08:38 -03:00
# ifdef CONFIG_KEXEC
void machine_crash_shutdown ( struct pt_regs * regs )
{
machine_ops . crash_shutdown ( regs ) ;
}
# endif
2008-11-12 11:34:42 -02:00
2008-11-12 11:34:43 -02:00
# if defined(CONFIG_SMP)
2008-11-12 11:34:42 -02:00
/* This keeps a track of which one is crashing cpu. */
static int crashing_cpu ;
static nmi_shootdown_cb shootdown_callback ;
static atomic_t waiting_for_crash_ipi ;
2011-09-30 15:06:21 -04:00
static int crash_nmi_callback ( unsigned int val , struct pt_regs * regs )
2008-11-12 11:34:42 -02:00
{
int cpu ;
cpu = raw_smp_processor_id ( ) ;
2012-02-01 10:06:34 -05:00
/*
* Don ' t do anything if this handler is invoked on crashing cpu .
2008-11-12 11:34:42 -02:00
* Otherwise , system will completely hang . Crashing cpu can get
* an NMI if system was initially booted with nmi_watchdog parameter .
*/
if ( cpu = = crashing_cpu )
2011-09-30 15:06:21 -04:00
return NMI_HANDLED ;
2008-11-12 11:34:42 -02:00
local_irq_disable ( ) ;
2011-09-30 15:06:21 -04:00
shootdown_callback ( cpu , regs ) ;
2008-11-12 11:34:42 -02:00
atomic_dec ( & waiting_for_crash_ipi ) ;
/* Assume hlt works */
halt ( ) ;
for ( ; ; )
cpu_relax ( ) ;
2011-09-30 15:06:21 -04:00
return NMI_HANDLED ;
2008-11-12 11:34:42 -02:00
}
static void smp_send_nmi_allbutself ( void )
{
2009-01-28 15:42:24 +01:00
apic - > send_IPI_allbutself ( NMI_VECTOR ) ;
2008-11-12 11:34:42 -02:00
}
2012-02-01 10:06:34 -05:00
/*
* Halt all other CPUs , calling the specified function on each of them
2008-11-12 11:34:43 -02:00
*
* This function can be used to halt all other CPUs on crash
* or emergency reboot time . The function passed as parameter
* will be called inside a NMI handler on all CPUs .
*/
2008-11-12 11:34:42 -02:00
void nmi_shootdown_cpus ( nmi_shootdown_cb callback )
{
unsigned long msecs ;
2008-11-12 11:34:44 -02:00
local_irq_disable ( ) ;
2008-11-12 11:34:42 -02:00
2012-02-01 10:06:34 -05:00
/* Make a note of crashing cpu. Will be used in NMI callback. */
2008-11-12 11:34:42 -02:00
crashing_cpu = safe_smp_processor_id ( ) ;
shootdown_callback = callback ;
atomic_set ( & waiting_for_crash_ipi , num_online_cpus ( ) - 1 ) ;
/* Would it be better to replace the trap vector here? */
2011-09-30 15:06:21 -04:00
if ( register_nmi_handler ( NMI_LOCAL , crash_nmi_callback ,
NMI_FLAG_FIRST , " crash " ) )
2012-02-01 10:06:34 -05:00
return ; /* Return what? */
/*
* Ensure the new callback function is set before sending
2008-11-12 11:34:42 -02:00
* out the NMI
*/
wmb ( ) ;
smp_send_nmi_allbutself ( ) ;
msecs = 1000 ; /* Wait at most a second for the other cpus to stop */
while ( ( atomic_read ( & waiting_for_crash_ipi ) > 0 ) & & msecs ) {
mdelay ( 1 ) ;
msecs - - ;
}
/* Leave the nmi callback set */
}
2008-11-12 11:34:43 -02:00
# else /* !CONFIG_SMP */
void nmi_shootdown_cpus ( nmi_shootdown_cb callback )
{
/* No other CPUs to shoot down */
}
2008-11-12 11:34:42 -02:00
# endif