| 1 | |
| 2 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
| 3 |
|
| 4 | #include <linux/export.h>
|
| 5 | #include <linux/reboot.h>
|
| 6 | #include <linux/init.h>
|
| 7 | #include <linux/pm.h>
|
| 8 | #include <linux/efi.h>
|
| 9 | #include <linux/dmi.h>
|
| 10 | #include <linux/sched.h>
|
| 11 | #include <linux/tboot.h>
|
| 12 | #include <linux/delay.h>
|
| 13 | #include <linux/objtool.h>
|
| 14 | #include <linux/pgtable.h>
|
| 15 | #include <linux/kexec.h>
|
| 16 | #include <linux/kvm_types.h>
|
| 17 | #include <acpi/reboot.h>
|
| 18 | #include <asm/io.h>
|
| 19 | #include <asm/apic.h>
|
| 20 | #include <asm/io_apic.h>
|
| 21 | #include <asm/desc.h>
|
| 22 | #include <asm/hpet.h>
|
| 23 | #include <asm/proto.h>
|
| 24 | #include <asm/reboot_fixups.h>
|
| 25 | #include <asm/reboot.h>
|
| 26 | #include <asm/pci_x86.h>
|
| 27 | #include <asm/cpu.h>
|
| 28 | #include <asm/nmi.h>
|
| 29 | #include <asm/smp.h>
|
| 30 | #include <asm/virt.h>
|
| 31 |
|
| 32 | #include <linux/ctype.h>
|
| 33 | #include <linux/mc146818rtc.h>
|
| 34 | #include <asm/realmode.h>
|
| 35 | #include <asm/x86_init.h>
|
| 36 | #include <asm/efi.h>
|
| 37 |
|
| 38 | |
| 39 | * Power off function, if any
|
| 40 |
|
| 41 | void (*pm_power_off)(void);
|
| 42 | EXPORT_SYMBOL(pm_power_off);
|
| 43 |
|
| 44 | |
| 45 | * This is set if we need to go through the 'emergency' path.
|
| 46 | * When machine_emergency_restart() is called, we may be on
|
| 47 | * an inconsistent state and won't be able to do a clean cleanup
|
| 48 |
|
| 49 | static int reboot_emergency;
|
| 50 |
|
| 51 |
|
| 52 | bool port_cf9_safe = false;
|
| 53 |
|
| 54 | |
| 55 | * Reboot options and system auto-detection code provided by
|
| 56 | * Dell Inc. so their systems "just work". :-)
|
| 57 |
|
| 58 |
|
| 59 | |
| 60 | * Some machines require the "reboot=a" commandline options
|
| 61 |
|
| 62 | static int __init set_acpi_reboot(const struct dmi_system_id *d)
|
| 63 | {
|
| 64 | if (reboot_type != BOOT_ACPI) {
|
| 65 | reboot_type = BOOT_ACPI;
|
| 66 | pr_info("%s series board detected. Selecting %s-method for reboots.\n",
|
| 67 | d->ident, "ACPI");
|
| 68 | }
|
| 69 | return 0;
|
| 70 | }
|
| 71 |
|
| 72 | |
| 73 | * Some machines require the "reboot=b" or "reboot=k" commandline options,
|
| 74 | * this quirk makes that automatic.
|
| 75 |
|
| 76 | static int __init set_bios_reboot(const struct dmi_system_id *d)
|
| 77 | {
|
| 78 | if (reboot_type != BOOT_BIOS) {
|
| 79 | reboot_type = BOOT_BIOS;
|
| 80 | pr_info("%s series board detected. Selecting %s-method for reboots.\n",
|
| 81 | d->ident, "BIOS");
|
| 82 | }
|
| 83 | return 0;
|
| 84 | }
|
| 85 |
|
| 86 | |
| 87 | * Some machines don't handle the default ACPI reboot method and
|
| 88 | * require the EFI reboot method:
|
| 89 |
|
| 90 | static int __init set_efi_reboot(const struct dmi_system_id *d)
|
| 91 | {
|
| 92 | if (reboot_type != BOOT_EFI && !efi_runtime_disabled()) {
|
| 93 | reboot_type = BOOT_EFI;
|
| 94 | pr_info("%s series board detected. Selecting EFI-method for reboot.\n", d->ident);
|
| 95 | }
|
| 96 | return 0;
|
| 97 | }
|
| 98 |
|
| 99 | void __noreturn machine_real_restart(unsigned int type)
|
| 100 | {
|
| 101 | local_irq_disable();
|
| 102 |
|
| 103 | |
| 104 | * Write zero to CMOS register number 0x0f, which the BIOS POST
|
| 105 | * routine will recognize as telling it to do a proper reboot. (Well
|
| 106 | * that's what this book in front of me says -- it may only apply to
|
| 107 | * the Phoenix BIOS though, it's not clear). At the same time,
|
| 108 | * disable NMIs by setting the top bit in the CMOS address register,
|
| 109 | * as we're about to do peculiar things to the CPU. I'm not sure if
|
| 110 | * `outb_p' is needed instead of just `outb'. Use it to be on the
|
| 111 | * safe side. (Yes, CMOS_WRITE does outb_p's. - Paul G.)
|
| 112 |
|
| 113 | spin_lock(&rtc_lock);
|
| 114 | CMOS_WRITE(0x00, 0x8f);
|
| 115 | spin_unlock(&rtc_lock);
|
| 116 |
|
| 117 | |
| 118 | * Switch to the trampoline page table.
|
| 119 |
|
| 120 | load_trampoline_pgtable();
|
| 121 |
|
| 122 |
|
| 123 | #ifdef CONFIG_X86_32
|
| 124 | asm volatile("jmpl *%0" : :
|
| 125 | "rm" (real_mode_header->machine_real_restart_asm),
|
| 126 | "a" (type));
|
| 127 | #else
|
| 128 | asm volatile("ljmpl *%0" : :
|
| 129 | "m" (real_mode_header->machine_real_restart_asm),
|
| 130 | "D" (type));
|
| 131 | #endif
|
| 132 | unreachable();
|
| 133 | }
|
| 134 | #ifdef CONFIG_APM_MODULE
|
| 135 | EXPORT_SYMBOL(machine_real_restart);
|
| 136 | #endif
|
| 137 | STACK_FRAME_NON_STANDARD(machine_real_restart);
|
| 138 |
|
| 139 | |
| 140 | * Some Apple MacBook and MacBookPro's needs reboot=p to be able to reboot
|
| 141 |
|
| 142 | static int __init set_pci_reboot(const struct dmi_system_id *d)
|
| 143 | {
|
| 144 | if (reboot_type != BOOT_CF9_FORCE) {
|
| 145 | reboot_type = BOOT_CF9_FORCE;
|
| 146 | pr_info("%s series board detected. Selecting %s-method for reboots.\n",
|
| 147 | d->ident, "PCI");
|
| 148 | }
|
| 149 | return 0;
|
| 150 | }
|
| 151 |
|
| 152 | static int __init set_kbd_reboot(const struct dmi_system_id *d)
|
| 153 | {
|
| 154 | if (reboot_type != BOOT_KBD) {
|
| 155 | reboot_type = BOOT_KBD;
|
| 156 | pr_info("%s series board detected. Selecting %s-method for reboot.\n",
|
| 157 | d->ident, "KBD");
|
| 158 | }
|
| 159 | return 0;
|
| 160 | }
|
| 161 |
|
| 162 | |
| 163 | * This is a single dmi_table handling all reboot quirks.
|
| 164 |
|
| 165 | static const struct dmi_system_id reboot_dmi_table[] __initconst = {
|
| 166 |
|
| 167 |
|
| 168 | {
|
| 169 | .callback = set_kbd_reboot,
|
| 170 | .ident = "Acer Aspire One A110",
|
| 171 | .matches = {
|
| 172 | DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
| 173 | DMI_MATCH(DMI_PRODUCT_NAME, "AOA110"),
|
| 174 | },
|
| 175 | },
|
| 176 | {
|
| 177 | .callback = set_efi_reboot,
|
| 178 | .ident = "Acer TravelMate X514-51T",
|
| 179 | .matches = {
|
| 180 | DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
|
| 181 | DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate X514-51T"),
|
| 182 | },
|
| 183 | },
|
| 184 |
|
| 185 |
|
| 186 | {
|
| 187 | .callback = set_pci_reboot,
|
| 188 | .ident = "Apple MacBook5",
|
| 189 | .matches = {
|
| 190 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
|
| 191 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5"),
|
| 192 | },
|
| 193 | },
|
| 194 | {
|
| 195 | .callback = set_pci_reboot,
|
| 196 | .ident = "Apple MacBook6,1",
|
| 197 | .matches = {
|
| 198 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
|
| 199 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBook6,1"),
|
| 200 | },
|
| 201 | },
|
| 202 | {
|
| 203 | .callback = set_pci_reboot,
|
| 204 | .ident = "Apple MacBookPro5",
|
| 205 | .matches = {
|
| 206 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
|
| 207 | DMI_MATCH(DMI_PRODUCT_NAME, "MacBookPro5"),
|
| 208 | },
|
| 209 | },
|
| 210 | {
|
| 211 | .callback = set_pci_reboot,
|
| 212 | .ident = "Apple Macmini3,1",
|
| 213 | .matches = {
|
| 214 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
|
| 215 | DMI_MATCH(DMI_PRODUCT_NAME, "Macmini3,1"),
|
| 216 | },
|
| 217 | },
|
| 218 | {
|
| 219 | .callback = set_pci_reboot,
|
| 220 | .ident = "Apple iMac9,1",
|
| 221 | .matches = {
|
| 222 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
|
| 223 | DMI_MATCH(DMI_PRODUCT_NAME, "iMac9,1"),
|
| 224 | },
|
| 225 | },
|
| 226 | {
|
| 227 | .callback = set_pci_reboot,
|
| 228 | .ident = "Apple iMac10,1",
|
| 229 | .matches = {
|
| 230 | DMI_MATCH(DMI_SYS_VENDOR, "Apple Inc."),
|
| 231 | DMI_MATCH(DMI_PRODUCT_NAME, "iMac10,1"),
|
| 232 | },
|
| 233 | },
|
| 234 |
|
| 235 |
|
| 236 | {
|
| 237 | .callback = set_pci_reboot,
|
| 238 | .ident = "ASRock Q1900DC-ITX",
|
| 239 | .matches = {
|
| 240 | DMI_MATCH(DMI_BOARD_VENDOR, "ASRock"),
|
| 241 | DMI_MATCH(DMI_BOARD_NAME, "Q1900DC-ITX"),
|
| 242 | },
|
| 243 | },
|
| 244 |
|
| 245 |
|
| 246 | {
|
| 247 | .callback = set_bios_reboot,
|
| 248 | .ident = "ASUS P4S800",
|
| 249 | .matches = {
|
| 250 | DMI_MATCH(DMI_BOARD_VENDOR, "ASUSTeK Computer INC."),
|
| 251 | DMI_MATCH(DMI_BOARD_NAME, "P4S800"),
|
| 252 | },
|
| 253 | },
|
| 254 | {
|
| 255 | .callback = set_acpi_reboot,
|
| 256 | .ident = "ASUS EeeBook X205TA",
|
| 257 | .matches = {
|
| 258 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
| 259 | DMI_MATCH(DMI_PRODUCT_NAME, "X205TA"),
|
| 260 | },
|
| 261 | },
|
| 262 | {
|
| 263 | .callback = set_acpi_reboot,
|
| 264 | .ident = "ASUS EeeBook X205TAW",
|
| 265 | .matches = {
|
| 266 | DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
|
| 267 | DMI_MATCH(DMI_PRODUCT_NAME, "X205TAW"),
|
| 268 | },
|
| 269 | },
|
| 270 |
|
| 271 |
|
| 272 | {
|
| 273 | .callback = set_pci_reboot,
|
| 274 | .ident = "Certec BPC600",
|
| 275 | .matches = {
|
| 276 | DMI_MATCH(DMI_SYS_VENDOR, "Certec"),
|
| 277 | DMI_MATCH(DMI_PRODUCT_NAME, "BPC600"),
|
| 278 | },
|
| 279 | },
|
| 280 |
|
| 281 |
|
| 282 | {
|
| 283 | .callback = set_bios_reboot,
|
| 284 | .ident = "Dell DXP061",
|
| 285 | .matches = {
|
| 286 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 287 | DMI_MATCH(DMI_PRODUCT_NAME, "Dell DXP061"),
|
| 288 | },
|
| 289 | },
|
| 290 | {
|
| 291 | .callback = set_bios_reboot,
|
| 292 | .ident = "Dell E520",
|
| 293 | .matches = {
|
| 294 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 295 | DMI_MATCH(DMI_PRODUCT_NAME, "Dell DM061"),
|
| 296 | },
|
| 297 | },
|
| 298 | {
|
| 299 | .callback = set_pci_reboot,
|
| 300 | .ident = "Dell Latitude E5410",
|
| 301 | .matches = {
|
| 302 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 303 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E5410"),
|
| 304 | },
|
| 305 | },
|
| 306 | {
|
| 307 | .callback = set_pci_reboot,
|
| 308 | .ident = "Dell Latitude E5420",
|
| 309 | .matches = {
|
| 310 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 311 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E5420"),
|
| 312 | },
|
| 313 | },
|
| 314 | {
|
| 315 | .callback = set_pci_reboot,
|
| 316 | .ident = "Dell Latitude E6320",
|
| 317 | .matches = {
|
| 318 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 319 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6320"),
|
| 320 | },
|
| 321 | },
|
| 322 | {
|
| 323 | .callback = set_pci_reboot,
|
| 324 | .ident = "Dell Latitude E6420",
|
| 325 | .matches = {
|
| 326 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 327 | DMI_MATCH(DMI_PRODUCT_NAME, "Latitude E6420"),
|
| 328 | },
|
| 329 | },
|
| 330 | {
|
| 331 | .callback = set_bios_reboot,
|
| 332 | .ident = "Dell OptiPlex 330",
|
| 333 | .matches = {
|
| 334 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 335 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 330"),
|
| 336 | DMI_MATCH(DMI_BOARD_NAME, "0KP561"),
|
| 337 | },
|
| 338 | },
|
| 339 | {
|
| 340 | .callback = set_bios_reboot,
|
| 341 | .ident = "Dell OptiPlex 360",
|
| 342 | .matches = {
|
| 343 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 344 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 360"),
|
| 345 | DMI_MATCH(DMI_BOARD_NAME, "0T656F"),
|
| 346 | },
|
| 347 | },
|
| 348 | {
|
| 349 | .callback = set_bios_reboot,
|
| 350 | .ident = "Dell OptiPlex 745",
|
| 351 | .matches = {
|
| 352 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 353 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
|
| 354 | },
|
| 355 | },
|
| 356 | {
|
| 357 | .callback = set_bios_reboot,
|
| 358 | .ident = "Dell OptiPlex 745",
|
| 359 | .matches = {
|
| 360 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 361 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
|
| 362 | DMI_MATCH(DMI_BOARD_NAME, "0MM599"),
|
| 363 | },
|
| 364 | },
|
| 365 | {
|
| 366 | .callback = set_bios_reboot,
|
| 367 | .ident = "Dell OptiPlex 745",
|
| 368 | .matches = {
|
| 369 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 370 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 745"),
|
| 371 | DMI_MATCH(DMI_BOARD_NAME, "0KW626"),
|
| 372 | },
|
| 373 | },
|
| 374 | {
|
| 375 | .callback = set_bios_reboot,
|
| 376 | .ident = "Dell OptiPlex 760",
|
| 377 | .matches = {
|
| 378 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 379 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 760"),
|
| 380 | DMI_MATCH(DMI_BOARD_NAME, "0G919G"),
|
| 381 | },
|
| 382 | },
|
| 383 | {
|
| 384 | .callback = set_pci_reboot,
|
| 385 | .ident = "Dell OptiPlex 990 BIOS A0x",
|
| 386 | .matches = {
|
| 387 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 388 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 990"),
|
| 389 | DMI_MATCH(DMI_BIOS_VERSION, "A0"),
|
| 390 | },
|
| 391 | },
|
| 392 | {
|
| 393 | .callback = set_bios_reboot,
|
| 394 | .ident = "Dell PowerEdge 300",
|
| 395 | .matches = {
|
| 396 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
|
| 397 | DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 300/"),
|
| 398 | },
|
| 399 | },
|
| 400 | {
|
| 401 | .callback = set_bios_reboot,
|
| 402 | .ident = "Dell PowerEdge 1300",
|
| 403 | .matches = {
|
| 404 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
|
| 405 | DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 1300/"),
|
| 406 | },
|
| 407 | },
|
| 408 | {
|
| 409 | .callback = set_bios_reboot,
|
| 410 | .ident = "Dell PowerEdge 2400",
|
| 411 | .matches = {
|
| 412 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Computer Corporation"),
|
| 413 | DMI_MATCH(DMI_PRODUCT_NAME, "PowerEdge 2400"),
|
| 414 | },
|
| 415 | },
|
| 416 | {
|
| 417 | .callback = set_pci_reboot,
|
| 418 | .ident = "Dell PowerEdge C6100",
|
| 419 | .matches = {
|
| 420 | DMI_MATCH(DMI_SYS_VENDOR, "Dell"),
|
| 421 | DMI_MATCH(DMI_PRODUCT_NAME, "C6100"),
|
| 422 | },
|
| 423 | },
|
| 424 | {
|
| 425 | .callback = set_pci_reboot,
|
| 426 | .ident = "Dell Precision M6600",
|
| 427 | .matches = {
|
| 428 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 429 | DMI_MATCH(DMI_PRODUCT_NAME, "Precision M6600"),
|
| 430 | },
|
| 431 | },
|
| 432 | {
|
| 433 | .callback = set_bios_reboot,
|
| 434 | .ident = "Dell Precision T5400",
|
| 435 | .matches = {
|
| 436 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 437 | DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T5400"),
|
| 438 | },
|
| 439 | },
|
| 440 | {
|
| 441 | .callback = set_bios_reboot,
|
| 442 | .ident = "Dell Precision T7400",
|
| 443 | .matches = {
|
| 444 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 445 | DMI_MATCH(DMI_PRODUCT_NAME, "Precision WorkStation T7400"),
|
| 446 | },
|
| 447 | },
|
| 448 | {
|
| 449 | .callback = set_bios_reboot,
|
| 450 | .ident = "Dell XPS710",
|
| 451 | .matches = {
|
| 452 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 453 | DMI_MATCH(DMI_PRODUCT_NAME, "Dell XPS710"),
|
| 454 | },
|
| 455 | },
|
| 456 | {
|
| 457 | .callback = set_acpi_reboot,
|
| 458 | .ident = "Dell OptiPlex 7450 AIO",
|
| 459 | .matches = {
|
| 460 | DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
| 461 | DMI_MATCH(DMI_PRODUCT_NAME, "OptiPlex 7450 AIO"),
|
| 462 | },
|
| 463 | },
|
| 464 |
|
| 465 |
|
| 466 | {
|
| 467 | .callback = set_bios_reboot,
|
| 468 | .ident = "HP Compaq Laptop",
|
| 469 | .matches = {
|
| 470 | DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
|
| 471 | DMI_MATCH(DMI_PRODUCT_NAME, "HP Compaq"),
|
| 472 | },
|
| 473 | },
|
| 474 |
|
| 475 | {
|
| 476 | .callback = set_pci_reboot,
|
| 477 | .ident = "Zotac ZBOX CI327 nano",
|
| 478 | .matches = {
|
| 479 | DMI_MATCH(DMI_SYS_VENDOR, "NA"),
|
| 480 | DMI_MATCH(DMI_PRODUCT_NAME, "ZBOX-CI327NANO-GS-01"),
|
| 481 | },
|
| 482 | },
|
| 483 |
|
| 484 |
|
| 485 | {
|
| 486 | .callback = set_bios_reboot,
|
| 487 | .ident = "Sony VGN-Z540N",
|
| 488 | .matches = {
|
| 489 | DMI_MATCH(DMI_SYS_VENDOR, "Sony Corporation"),
|
| 490 | DMI_MATCH(DMI_PRODUCT_NAME, "VGN-Z540N"),
|
| 491 | },
|
| 492 | },
|
| 493 |
|
| 494 | { }
|
| 495 | };
|
| 496 |
|
| 497 | static int __init reboot_init(void)
|
| 498 | {
|
| 499 | int rv;
|
| 500 |
|
| 501 | |
| 502 | * Only do the DMI check if reboot_type hasn't been overridden
|
| 503 | * on the command line
|
| 504 |
|
| 505 | if (!reboot_default)
|
| 506 | return 0;
|
| 507 |
|
| 508 | |
| 509 | * The DMI quirks table takes precedence. If no quirks entry
|
| 510 | * matches and the ACPI Hardware Reduced bit is set and EFI
|
| 511 | * runtime services are enabled, force EFI reboot.
|
| 512 |
|
| 513 | rv = dmi_check_system(reboot_dmi_table);
|
| 514 |
|
| 515 | if (!rv && efi_reboot_required() && !efi_runtime_disabled())
|
| 516 | reboot_type = BOOT_EFI;
|
| 517 |
|
| 518 | return 0;
|
| 519 | }
|
| 520 | core_initcall(reboot_init);
|
| 521 |
|
| 522 | static inline void kb_wait(void)
|
| 523 | {
|
| 524 | int i;
|
| 525 |
|
| 526 | for (i = 0; i < 0x10000; i++) {
|
| 527 | if ((inb(0x64) & 0x02) == 0)
|
| 528 | break;
|
| 529 | udelay(2);
|
| 530 | }
|
| 531 | }
|
| 532 |
|
| 533 | static inline void nmi_shootdown_cpus_on_restart(void);
|
| 534 |
|
| 535 | #if IS_ENABLED(CONFIG_KVM_X86)
|
| 536 | static void emergency_reboot_disable_virtualization(void)
|
| 537 | {
|
| 538 | local_irq_disable();
|
| 539 |
|
| 540 | |
| 541 | * Disable virtualization on all CPUs before rebooting to avoid hanging
|
| 542 | * the system, as VMX and SVM block INIT when running in the host.
|
| 543 | *
|
| 544 | * We can't take any locks and we may be on an inconsistent state, so
|
| 545 | * use NMIs as IPIs to tell the other CPUs to disable VMX/SVM and halt.
|
| 546 | *
|
| 547 | * Safely force _this_ CPU out of VMX/SVM operation, and if necessary,
|
| 548 | * blast NMIs to force other CPUs out of VMX/SVM as well.k
|
| 549 |
|
| 550 | if (!x86_virt_emergency_disable_virtualization_cpu())
|
| 551 | nmi_shootdown_cpus_on_restart();
|
| 552 | }
|
| 553 | #else
|
| 554 | static void emergency_reboot_disable_virtualization(void) { }
|
| 555 | #endif
|
| 556 |
|
| 557 | void __attribute__((weak)) mach_reboot_fixups(void)
|
| 558 | {
|
| 559 | }
|
| 560 |
|
| 561 | |
| 562 | * To the best of our knowledge Windows compatible x86 hardware expects
|
| 563 | * the following on reboot:
|
| 564 | *
|
| 565 | * 1) If the FADT has the ACPI reboot register flag set, try it
|
| 566 | * 2) If still alive, write to the keyboard controller
|
| 567 | * 3) If still alive, write to the ACPI reboot register again
|
| 568 | * 4) If still alive, write to the keyboard controller again
|
| 569 | * 5) If still alive, call the EFI runtime service to reboot
|
| 570 | * 6) If no EFI runtime service, call the BIOS to do a reboot
|
| 571 | *
|
| 572 | * We default to following the same pattern. We also have
|
| 573 | * two other reboot methods: 'triple fault' and 'PCI', which
|
| 574 | * can be triggered via the reboot= kernel boot option or
|
| 575 | * via quirks.
|
| 576 | *
|
| 577 | * This means that this function can never return, it can misbehave
|
| 578 | * by not rebooting properly and hanging.
|
| 579 |
|
| 580 | static void native_machine_emergency_restart(void)
|
| 581 | {
|
| 582 | int i;
|
| 583 | int attempt = 0;
|
| 584 | int orig_reboot_type = reboot_type;
|
| 585 | unsigned short mode;
|
| 586 |
|
| 587 | if (reboot_emergency)
|
| 588 | emergency_reboot_disable_virtualization();
|
| 589 |
|
| 590 | tboot_shutdown(TB_SHUTDOWN_REBOOT);
|
| 591 |
|
| 592 |
|
| 593 | mode = reboot_mode == REBOOT_WARM ? 0x1234 : 0;
|
| 594 | *((unsigned short *)__va(0x472)) = mode;
|
| 595 |
|
| 596 | |
| 597 | * If an EFI capsule has been registered with the firmware then
|
| 598 | * override the reboot= parameter.
|
| 599 |
|
| 600 | if (efi_capsule_pending(NULL)) {
|
| 601 | pr_info("EFI capsule is pending, forcing EFI reboot.\n");
|
| 602 | reboot_type = BOOT_EFI;
|
| 603 | }
|
| 604 |
|
| 605 | for (;;) {
|
| 606 |
|
| 607 | switch (reboot_type) {
|
| 608 | case BOOT_ACPI:
|
| 609 | acpi_reboot();
|
| 610 | reboot_type = BOOT_KBD;
|
| 611 | break;
|
| 612 |
|
| 613 | case BOOT_KBD:
|
| 614 | mach_reboot_fixups();
|
| 615 |
|
| 616 | for (i = 0; i < 10; i++) {
|
| 617 | kb_wait();
|
| 618 | udelay(50);
|
| 619 | outb(0xfe, 0x64);
|
| 620 | udelay(50);
|
| 621 | }
|
| 622 | if (attempt == 0 && orig_reboot_type == BOOT_ACPI) {
|
| 623 | attempt = 1;
|
| 624 | reboot_type = BOOT_ACPI;
|
| 625 | } else {
|
| 626 | reboot_type = BOOT_EFI;
|
| 627 | }
|
| 628 | break;
|
| 629 |
|
| 630 | case BOOT_EFI:
|
| 631 | efi_reboot(reboot_mode, NULL);
|
| 632 | reboot_type = BOOT_BIOS;
|
| 633 | break;
|
| 634 |
|
| 635 | case BOOT_BIOS:
|
| 636 | machine_real_restart(MRR_BIOS);
|
| 637 |
|
| 638 |
|
| 639 | reboot_type = BOOT_CF9_SAFE;
|
| 640 | break;
|
| 641 |
|
| 642 | case BOOT_CF9_FORCE:
|
| 643 | port_cf9_safe = true;
|
| 644 | fallthrough;
|
| 645 |
|
| 646 | case BOOT_CF9_SAFE:
|
| 647 | if (port_cf9_safe) {
|
| 648 | u8 reboot_code = reboot_mode == REBOOT_WARM ? 0x06 : 0x0E;
|
| 649 | u8 cf9 = inb(0xcf9) & ~reboot_code;
|
| 650 | outb(cf9|2, 0xcf9);
|
| 651 | udelay(50);
|
| 652 |
|
| 653 | outb(cf9|reboot_code, 0xcf9);
|
| 654 | udelay(50);
|
| 655 | }
|
| 656 | reboot_type = BOOT_TRIPLE;
|
| 657 | break;
|
| 658 |
|
| 659 | case BOOT_TRIPLE:
|
| 660 | idt_invalidate();
|
| 661 | __asm__ __volatile__("int3");
|
| 662 |
|
| 663 |
|
| 664 | reboot_type = BOOT_KBD;
|
| 665 | break;
|
| 666 | }
|
| 667 | }
|
| 668 | }
|
| 669 |
|
| 670 | void native_machine_shutdown(void)
|
| 671 | {
|
| 672 | |
| 673 | * Call enc_kexec_begin() while all CPUs are still active and
|
| 674 | * interrupts are enabled. This will allow all in-flight memory
|
| 675 | * conversions to finish cleanly.
|
| 676 |
|
| 677 | if (kexec_in_progress)
|
| 678 | x86_platform.guest.enc_kexec_begin();
|
| 679 |
|
| 680 |
|
| 681 | #ifdef CONFIG_X86_IO_APIC
|
| 682 | |
| 683 | * Disabling IO APIC before local APIC is a workaround for
|
| 684 | * erratum AVR31 in "Intel Atom Processor C2000 Product Family
|
| 685 | * Specification Update". In this situation, interrupts that target
|
| 686 | * a Logical Processor whose Local APIC is either in the process of
|
| 687 | * being hardware disabled or software disabled are neither delivered
|
| 688 | * nor discarded. When this erratum occurs, the processor may hang.
|
| 689 | *
|
| 690 | * Even without the erratum, it still makes sense to quiet IO APIC
|
| 691 | * before disabling Local APIC.
|
| 692 |
|
| 693 | clear_IO_APIC();
|
| 694 | #endif
|
| 695 |
|
| 696 | #ifdef CONFIG_SMP
|
| 697 | |
| 698 | * Stop all of the others. Also disable the local irq to
|
| 699 | * not receive the per-cpu timer interrupt which may trigger
|
| 700 | * scheduler's load balance.
|
| 701 |
|
| 702 | local_irq_disable();
|
| 703 | stop_other_cpus();
|
| 704 | #endif
|
| 705 |
|
| 706 | lapic_shutdown();
|
| 707 | restore_boot_irq_mode();
|
| 708 |
|
| 709 | #ifdef CONFIG_HPET_TIMER
|
| 710 | hpet_disable();
|
| 711 | #endif
|
| 712 |
|
| 713 | #ifdef CONFIG_X86_64
|
| 714 | x86_platform.iommu_shutdown();
|
| 715 | #endif
|
| 716 |
|
| 717 | if (kexec_in_progress)
|
| 718 | x86_platform.guest.enc_kexec_finish();
|
| 719 | }
|
| 720 |
|
| 721 | static void __machine_emergency_restart(int emergency)
|
| 722 | {
|
| 723 | reboot_emergency = emergency;
|
| 724 | machine_ops.emergency_restart();
|
| 725 | }
|
| 726 |
|
| 727 | static void native_machine_restart(char *command)
|
| 728 | {
|
| 729 | pr_notice("machine restart\n");
|
| 730 |
|
| 731 | if (!reboot_force)
|
| 732 | machine_shutdown();
|
| 733 |
|
| 734 | do_kernel_restart(command);
|
| 735 |
|
| 736 | __machine_emergency_restart(0);
|
| 737 | }
|
| 738 |
|
| 739 | static void native_machine_halt(void)
|
| 740 | {
|
| 741 |
|
| 742 | machine_shutdown();
|
| 743 |
|
| 744 | tboot_shutdown(TB_SHUTDOWN_HALT);
|
| 745 |
|
| 746 | stop_this_cpu(NULL);
|
| 747 | }
|
| 748 |
|
| 749 | static void native_machine_power_off(void)
|
| 750 | {
|
| 751 | if (kernel_can_power_off()) {
|
| 752 | if (!reboot_force)
|
| 753 | machine_shutdown();
|
| 754 | do_kernel_power_off();
|
| 755 | }
|
| 756 |
|
| 757 | tboot_shutdown(TB_SHUTDOWN_HALT);
|
| 758 | }
|
| 759 |
|
| 760 | struct machine_ops machine_ops __ro_after_init = {
|
| 761 | .power_off = native_machine_power_off,
|
| 762 | .shutdown = native_machine_shutdown,
|
| 763 | .emergency_restart = native_machine_emergency_restart,
|
| 764 | .restart = native_machine_restart,
|
| 765 | .halt = native_machine_halt,
|
| 766 | #ifdef CONFIG_CRASH_DUMP
|
| 767 | .crash_shutdown = native_machine_crash_shutdown,
|
| 768 | #endif
|
| 769 | };
|
| 770 |
|
| 771 | void machine_power_off(void)
|
| 772 | {
|
| 773 | machine_ops.power_off();
|
| 774 | }
|
| 775 |
|
| 776 | void machine_shutdown(void)
|
| 777 | {
|
| 778 | machine_ops.shutdown();
|
| 779 | }
|
| 780 |
|
| 781 | void machine_emergency_restart(void)
|
| 782 | {
|
| 783 | __machine_emergency_restart(1);
|
| 784 | }
|
| 785 |
|
| 786 | void machine_restart(char *cmd)
|
| 787 | {
|
| 788 | machine_ops.restart(cmd);
|
| 789 | }
|
| 790 |
|
| 791 | void machine_halt(void)
|
| 792 | {
|
| 793 | machine_ops.halt();
|
| 794 | }
|
| 795 |
|
| 796 | #ifdef CONFIG_CRASH_DUMP
|
| 797 | void machine_crash_shutdown(struct pt_regs *regs)
|
| 798 | {
|
| 799 | machine_ops.crash_shutdown(regs);
|
| 800 | }
|
| 801 | #endif
|
| 802 |
|
| 803 |
|
| 804 | int crashing_cpu = -1;
|
| 805 |
|
| 806 | #if defined(CONFIG_SMP)
|
| 807 |
|
| 808 | static nmi_shootdown_cb shootdown_callback;
|
| 809 |
|
| 810 | static atomic_t waiting_for_crash_ipi;
|
| 811 | static int crash_ipi_issued;
|
| 812 |
|
| 813 | static int crash_nmi_callback(unsigned int val, struct pt_regs *regs)
|
| 814 | {
|
| 815 | int cpu;
|
| 816 |
|
| 817 | cpu = raw_smp_processor_id();
|
| 818 |
|
| 819 | |
| 820 | * Don't do anything if this handler is invoked on crashing cpu.
|
| 821 | * Otherwise, system will completely hang. Crashing cpu can get
|
| 822 | * an NMI if system was initially booted with nmi_watchdog parameter.
|
| 823 |
|
| 824 | if (cpu == crashing_cpu)
|
| 825 | return NMI_HANDLED;
|
| 826 | local_irq_disable();
|
| 827 |
|
| 828 | if (shootdown_callback)
|
| 829 | shootdown_callback(cpu, regs);
|
| 830 |
|
| 831 | |
| 832 | * Disable virtualization, as both VMX and SVM can block INIT and thus
|
| 833 | * prevent AP bringup, e.g. in a kdump kernel or in firmware.
|
| 834 |
|
| 835 | x86_virt_emergency_disable_virtualization_cpu();
|
| 836 |
|
| 837 | atomic_dec(&waiting_for_crash_ipi);
|
| 838 |
|
| 839 | if (smp_ops.stop_this_cpu) {
|
| 840 | smp_ops.stop_this_cpu();
|
| 841 | BUG();
|
| 842 | }
|
| 843 |
|
| 844 |
|
| 845 | halt();
|
| 846 | for (;;)
|
| 847 | cpu_relax();
|
| 848 |
|
| 849 | return NMI_HANDLED;
|
| 850 | }
|
| 851 |
|
| 852 | |
| 853 | * nmi_shootdown_cpus - Stop other CPUs via NMI
|
| 854 | * @callback: Optional callback to be invoked from the NMI handler
|
| 855 | *
|
| 856 | * The NMI handler on the remote CPUs invokes @callback, if not
|
| 857 | * NULL, first and then disables virtualization to ensure that
|
| 858 | * INIT is recognized during reboot.
|
| 859 | *
|
| 860 | * nmi_shootdown_cpus() can only be invoked once. After the first
|
| 861 | * invocation all other CPUs are stuck in crash_nmi_callback() and
|
| 862 | * cannot respond to a second NMI.
|
| 863 |
|
| 864 | void nmi_shootdown_cpus(nmi_shootdown_cb callback)
|
| 865 | {
|
| 866 | unsigned long msecs;
|
| 867 |
|
| 868 | local_irq_disable();
|
| 869 |
|
| 870 | |
| 871 | * Avoid certain doom if a shootdown already occurred; re-registering
|
| 872 | * the NMI handler will cause list corruption, modifying the callback
|
| 873 | * will do who knows what, etc...
|
| 874 |
|
| 875 | if (WARN_ON_ONCE(crash_ipi_issued))
|
| 876 | return;
|
| 877 |
|
| 878 |
|
| 879 | crashing_cpu = smp_processor_id();
|
| 880 |
|
| 881 | shootdown_callback = callback;
|
| 882 |
|
| 883 | atomic_set(&waiting_for_crash_ipi, num_online_cpus() - 1);
|
| 884 |
|
| 885 | |
| 886 | * Set emergency handler to preempt other handlers.
|
| 887 |
|
| 888 | set_emergency_nmi_handler(NMI_LOCAL, crash_nmi_callback);
|
| 889 |
|
| 890 | apic_send_IPI_allbutself(NMI_VECTOR);
|
| 891 |
|
| 892 |
|
| 893 | WRITE_ONCE(crash_ipi_issued, 1);
|
| 894 |
|
| 895 | msecs = 1000;
|
| 896 | while ((atomic_read(&waiting_for_crash_ipi) > 0) && msecs) {
|
| 897 | mdelay(1);
|
| 898 | msecs--;
|
| 899 | }
|
| 900 |
|
| 901 | |
| 902 | * Leave the nmi callback set, shootdown is a one-time thing. Clearing
|
| 903 | * the callback could result in a NULL pointer dereference if a CPU
|
| 904 | * (finally) responds after the timeout expires.
|
| 905 |
|
| 906 | }
|
| 907 |
|
| 908 | static inline void nmi_shootdown_cpus_on_restart(void)
|
| 909 | {
|
| 910 | if (!crash_ipi_issued)
|
| 911 | nmi_shootdown_cpus(NULL);
|
| 912 | }
|
| 913 |
|
| 914 | |
| 915 | * Check if the crash dumping IPI got issued and if so, call its callback
|
| 916 | * directly. This function is used when we have already been in NMI handler.
|
| 917 | * It doesn't return.
|
| 918 |
|
| 919 | void run_crash_ipi_callback(struct pt_regs *regs)
|
| 920 | {
|
| 921 | if (crash_ipi_issued)
|
| 922 | crash_nmi_callback(0, regs);
|
| 923 | }
|
| 924 |
|
| 925 |
|
| 926 | void __noreturn nmi_panic_self_stop(struct pt_regs *regs)
|
| 927 | {
|
| 928 | while (1) {
|
| 929 |
|
| 930 | run_crash_ipi_callback(regs);
|
| 931 | cpu_relax();
|
| 932 | }
|
| 933 | }
|
| 934 |
|
| 935 | #else
|
| 936 | void nmi_shootdown_cpus(nmi_shootdown_cb callback)
|
| 937 | {
|
| 938 |
|
| 939 | }
|
| 940 |
|
| 941 | static inline void nmi_shootdown_cpus_on_restart(void) { }
|
| 942 |
|
| 943 | void run_crash_ipi_callback(struct pt_regs *regs)
|
| 944 | {
|
| 945 | }
|
| 946 | #endif
|