Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 1 | // Support for enabling/disabling BIOS ram shadowing. |
| 2 | // |
Kevin O'Connor | 5bd01de | 2010-09-13 21:19:27 -0400 | [diff] [blame] | 3 | // Copyright (C) 2008-2010 Kevin O'Connor <kevin@koconnor.net> |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 4 | // Copyright (C) 2006 Fabrice Bellard |
| 5 | // |
Kevin O'Connor | b1b7c2a | 2009-01-15 20:52:58 -0500 | [diff] [blame] | 6 | // This file may be distributed under the terms of the GNU LGPLv3 license. |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 7 | |
Kevin O'Connor | 9521e26 | 2008-07-04 13:04:29 -0400 | [diff] [blame] | 8 | #include "config.h" // CONFIG_* |
Kevin O'Connor | 2d2fa31 | 2013-09-14 21:55:26 -0400 | [diff] [blame] | 9 | #include "dev-q35.h" // PCI_VENDOR_ID_INTEL |
Paolo Bonzini | 40d0312 | 2014-05-15 13:22:26 +0200 | [diff] [blame] | 10 | #include "dev-piix.h" // I440FX_PAM0 |
Kevin O'Connor | 2d2fa31 | 2013-09-14 21:55:26 -0400 | [diff] [blame] | 11 | #include "hw/pci.h" // pci_config_writeb |
Kevin O'Connor | 5d369d8 | 2013-09-02 20:48:46 -0400 | [diff] [blame] | 12 | #include "hw/pci_ids.h" // PCI_VENDOR_ID_INTEL |
| 13 | #include "hw/pci_regs.h" // PCI_VENDOR_ID |
Kevin O'Connor | 9dea590 | 2013-09-14 20:23:54 -0400 | [diff] [blame] | 14 | #include "malloc.h" // rom_get_last |
Kevin O'Connor | 2d2fa31 | 2013-09-14 21:55:26 -0400 | [diff] [blame] | 15 | #include "output.h" // dprintf |
| 16 | #include "paravirt.h" // runningOnXen |
Kevin O'Connor | fa9c66a | 2013-09-14 19:10:40 -0400 | [diff] [blame] | 17 | #include "string.h" // memset |
Kevin O'Connor | 2d2fa31 | 2013-09-14 21:55:26 -0400 | [diff] [blame] | 18 | #include "util.h" // make_bios_writable |
Kevin O'Connor | b9c6a96 | 2013-09-14 13:01:30 -0400 | [diff] [blame] | 19 | #include "x86.h" // wbinvd |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 20 | |
Kevin O'Connor | 3528496 | 2009-07-24 21:49:26 -0400 | [diff] [blame] | 21 | // On the emulators, the bios at 0xf0000 is also at 0xffff0000 |
Kevin O'Connor | 5bd01de | 2010-09-13 21:19:27 -0400 | [diff] [blame] | 22 | #define BIOS_SRC_OFFSET 0xfff00000 |
Kevin O'Connor | 3528496 | 2009-07-24 21:49:26 -0400 | [diff] [blame] | 23 | |
Kevin O'Connor | d449a11 | 2016-04-01 15:45:29 -0400 | [diff] [blame] | 24 | union pamdata_u { |
| 25 | u8 data8[8]; |
| 26 | u32 data32[2]; |
| 27 | }; |
| 28 | |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 29 | // Enable shadowing and copy bios. |
| 30 | static void |
Isaku Yamahata | 08328e7 | 2010-07-20 16:50:45 +0900 | [diff] [blame] | 31 | __make_bios_writable_intel(u16 bdf, u32 pam0) |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 32 | { |
Kevin O'Connor | d449a11 | 2016-04-01 15:45:29 -0400 | [diff] [blame] | 33 | // Read in current PAM settings from pci config space |
| 34 | union pamdata_u pamdata; |
| 35 | pamdata.data32[0] = pci_config_readl(bdf, ALIGN_DOWN(pam0, 4)); |
| 36 | pamdata.data32[1] = pci_config_readl(bdf, ALIGN_DOWN(pam0, 4) + 4); |
| 37 | u8 *pam = &pamdata.data8[pam0 & 0x03]; |
| 38 | |
Kevin O'Connor | e773930 | 2009-07-26 19:16:09 -0400 | [diff] [blame] | 39 | // Make ram from 0xc0000-0xf0000 writable |
Kevin O'Connor | e773930 | 2009-07-26 19:16:09 -0400 | [diff] [blame] | 40 | int i; |
Kevin O'Connor | d449a11 | 2016-04-01 15:45:29 -0400 | [diff] [blame] | 41 | for (i=0; i<6; i++) |
| 42 | pam[i + 1] = 0x33; |
Kevin O'Connor | e773930 | 2009-07-26 19:16:09 -0400 | [diff] [blame] | 43 | |
Kevin O'Connor | 5b8f809 | 2009-09-20 19:47:45 -0400 | [diff] [blame] | 44 | // Make ram from 0xf0000-0x100000 writable |
Kevin O'Connor | d449a11 | 2016-04-01 15:45:29 -0400 | [diff] [blame] | 45 | int ram_present = pam[0] & 0x10; |
| 46 | pam[0] = 0x30; |
Kevin O'Connor | 5b8f809 | 2009-09-20 19:47:45 -0400 | [diff] [blame] | 47 | |
Kevin O'Connor | d449a11 | 2016-04-01 15:45:29 -0400 | [diff] [blame] | 48 | // Write PAM settings back to pci config space |
| 49 | pci_config_writel(bdf, ALIGN_DOWN(pam0, 4), pamdata.data32[0]); |
| 50 | pci_config_writel(bdf, ALIGN_DOWN(pam0, 4) + 4, pamdata.data32[1]); |
| 51 | |
| 52 | if (!ram_present) |
| 53 | // Copy bios. |
| 54 | memcpy(VSYMBOL(code32flat_start) |
| 55 | , VSYMBOL(code32flat_start) + BIOS_SRC_OFFSET |
| 56 | , SYMBOL(code32flat_end) - SYMBOL(code32flat_start)); |
Kevin O'Connor | 5b8f809 | 2009-09-20 19:47:45 -0400 | [diff] [blame] | 57 | } |
| 58 | |
Kevin O'Connor | 6e4583c | 2011-06-19 10:09:26 -0400 | [diff] [blame] | 59 | static void |
Isaku Yamahata | 08328e7 | 2010-07-20 16:50:45 +0900 | [diff] [blame] | 60 | make_bios_writable_intel(u16 bdf, u32 pam0) |
| 61 | { |
| 62 | int reg = pci_config_readb(bdf, pam0); |
| 63 | if (!(reg & 0x10)) { |
| 64 | // QEMU doesn't fully implement the piix shadow capabilities - |
| 65 | // if ram isn't backing the bios segment when shadowing is |
Stefan Weil | 6bcacf7 | 2015-10-02 08:46:40 +0200 | [diff] [blame] | 66 | // disabled, the code itself won't be in memory. So, run the |
Isaku Yamahata | 08328e7 | 2010-07-20 16:50:45 +0900 | [diff] [blame] | 67 | // code from the high-memory flash location. |
Kevin O'Connor | 5bd01de | 2010-09-13 21:19:27 -0400 | [diff] [blame] | 68 | u32 pos = (u32)__make_bios_writable_intel + BIOS_SRC_OFFSET; |
Isaku Yamahata | 08328e7 | 2010-07-20 16:50:45 +0900 | [diff] [blame] | 69 | void (*func)(u16 bdf, u32 pam0) = (void*)pos; |
| 70 | func(bdf, pam0); |
| 71 | return; |
| 72 | } |
| 73 | // Ram already present - just enable writes |
| 74 | __make_bios_writable_intel(bdf, pam0); |
| 75 | } |
| 76 | |
Kevin O'Connor | 6e4583c | 2011-06-19 10:09:26 -0400 | [diff] [blame] | 77 | static void |
Isaku Yamahata | 08328e7 | 2010-07-20 16:50:45 +0900 | [diff] [blame] | 78 | make_bios_readonly_intel(u16 bdf, u32 pam0) |
| 79 | { |
| 80 | // Flush any pending writes before locking memory. |
| 81 | wbinvd(); |
| 82 | |
Kevin O'Connor | d449a11 | 2016-04-01 15:45:29 -0400 | [diff] [blame] | 83 | // Read in current PAM settings from pci config space |
| 84 | union pamdata_u pamdata; |
| 85 | pamdata.data32[0] = pci_config_readl(bdf, ALIGN_DOWN(pam0, 4)); |
| 86 | pamdata.data32[1] = pci_config_readl(bdf, ALIGN_DOWN(pam0, 4) + 4); |
| 87 | u8 *pam = &pamdata.data8[pam0 & 0x03]; |
| 88 | |
Isaku Yamahata | 08328e7 | 2010-07-20 16:50:45 +0900 | [diff] [blame] | 89 | // Write protect roms from 0xc0000-0xf0000 |
Kevin O'Connor | 2b0fb8c | 2013-08-07 23:03:47 -0400 | [diff] [blame] | 90 | u32 romlast = BUILD_BIOS_ADDR, rommax = BUILD_BIOS_ADDR; |
Kevin O'Connor | c98424c | 2013-07-21 16:38:18 -0400 | [diff] [blame] | 91 | if (CONFIG_WRITABLE_UPPERMEMORY) |
| 92 | romlast = rom_get_last(); |
Kevin O'Connor | 2b0fb8c | 2013-08-07 23:03:47 -0400 | [diff] [blame] | 93 | if (CONFIG_MALLOC_UPPERMEMORY) |
| 94 | rommax = rom_get_max(); |
Isaku Yamahata | 08328e7 | 2010-07-20 16:50:45 +0900 | [diff] [blame] | 95 | int i; |
| 96 | for (i=0; i<6; i++) { |
| 97 | u32 mem = BUILD_ROM_START + i * 32*1024; |
Kevin O'Connor | c98424c | 2013-07-21 16:38:18 -0400 | [diff] [blame] | 98 | if (romlast < mem + 16*1024 || rommax < mem + 32*1024) { |
| 99 | if (romlast >= mem && rommax >= mem + 16*1024) |
Kevin O'Connor | d449a11 | 2016-04-01 15:45:29 -0400 | [diff] [blame] | 100 | pam[i + 1] = 0x31; |
Isaku Yamahata | 08328e7 | 2010-07-20 16:50:45 +0900 | [diff] [blame] | 101 | break; |
| 102 | } |
Kevin O'Connor | d449a11 | 2016-04-01 15:45:29 -0400 | [diff] [blame] | 103 | pam[i + 1] = 0x11; |
Isaku Yamahata | 08328e7 | 2010-07-20 16:50:45 +0900 | [diff] [blame] | 104 | } |
| 105 | |
| 106 | // Write protect 0xf0000-0x100000 |
Kevin O'Connor | d449a11 | 2016-04-01 15:45:29 -0400 | [diff] [blame] | 107 | pam[0] = 0x10; |
| 108 | |
| 109 | // Write PAM settings back to pci config space |
| 110 | pci_config_writel(bdf, ALIGN_DOWN(pam0, 4), pamdata.data32[0]); |
| 111 | pci_config_writel(bdf, ALIGN_DOWN(pam0, 4) + 4, pamdata.data32[1]); |
Isaku Yamahata | 08328e7 | 2010-07-20 16:50:45 +0900 | [diff] [blame] | 112 | } |
| 113 | |
Kevin O'Connor | 96d4c43 | 2013-03-08 19:31:14 -0500 | [diff] [blame] | 114 | static int ShadowBDF = -1; |
Kevin O'Connor | 6e4583c | 2011-06-19 10:09:26 -0400 | [diff] [blame] | 115 | |
Kevin O'Connor | 5b8f809 | 2009-09-20 19:47:45 -0400 | [diff] [blame] | 116 | // Make the 0xc0000-0x100000 area read/writable. |
| 117 | void |
Kevin O'Connor | 1ca05b0 | 2010-01-03 17:43:37 -0500 | [diff] [blame] | 118 | make_bios_writable(void) |
Kevin O'Connor | 5b8f809 | 2009-09-20 19:47:45 -0400 | [diff] [blame] | 119 | { |
Kevin O'Connor | 897fb11 | 2013-02-07 23:32:48 -0500 | [diff] [blame] | 120 | if (!CONFIG_QEMU || runningOnXen()) |
Kevin O'Connor | 5b8f809 | 2009-09-20 19:47:45 -0400 | [diff] [blame] | 121 | return; |
| 122 | |
| 123 | dprintf(3, "enabling shadow ram\n"); |
| 124 | |
Kevin O'Connor | faf6a4e | 2011-06-21 21:22:01 -0400 | [diff] [blame] | 125 | // At this point, statically allocated variables can't be written, |
| 126 | // so do this search manually. |
Kevin O'Connor | 2b333e4 | 2011-07-02 14:49:41 -0400 | [diff] [blame] | 127 | int bdf; |
| 128 | foreachbdf(bdf, 0) { |
Kevin O'Connor | faf6a4e | 2011-06-21 21:22:01 -0400 | [diff] [blame] | 129 | u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID); |
| 130 | u16 vendor = vendev & 0xffff, device = vendev >> 16; |
| 131 | if (vendor == PCI_VENDOR_ID_INTEL |
| 132 | && device == PCI_DEVICE_ID_INTEL_82441) { |
| 133 | make_bios_writable_intel(bdf, I440FX_PAM0); |
Kevin O'Connor | a48f602 | 2016-01-12 14:22:33 -0500 | [diff] [blame] | 134 | code_mutable_preinit(); |
Kevin O'Connor | 96d4c43 | 2013-03-08 19:31:14 -0500 | [diff] [blame] | 135 | ShadowBDF = bdf; |
Kevin O'Connor | faf6a4e | 2011-06-21 21:22:01 -0400 | [diff] [blame] | 136 | return; |
| 137 | } |
Isaku Yamahata | 72a590e | 2012-11-28 10:17:33 +0100 | [diff] [blame] | 138 | if (vendor == PCI_VENDOR_ID_INTEL |
| 139 | && device == PCI_DEVICE_ID_INTEL_Q35_MCH) { |
| 140 | make_bios_writable_intel(bdf, Q35_HOST_BRIDGE_PAM0); |
Kevin O'Connor | a48f602 | 2016-01-12 14:22:33 -0500 | [diff] [blame] | 141 | code_mutable_preinit(); |
Kevin O'Connor | 96d4c43 | 2013-03-08 19:31:14 -0500 | [diff] [blame] | 142 | ShadowBDF = bdf; |
Isaku Yamahata | 72a590e | 2012-11-28 10:17:33 +0100 | [diff] [blame] | 143 | return; |
| 144 | } |
Kevin O'Connor | 3528496 | 2009-07-24 21:49:26 -0400 | [diff] [blame] | 145 | } |
Kevin O'Connor | faf6a4e | 2011-06-21 21:22:01 -0400 | [diff] [blame] | 146 | dprintf(1, "Unable to unlock ram - bridge not found\n"); |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 147 | } |
| 148 | |
| 149 | // Make the BIOS code segment area (0xf0000) read-only. |
| 150 | void |
Kevin O'Connor | 1ca05b0 | 2010-01-03 17:43:37 -0500 | [diff] [blame] | 151 | make_bios_readonly(void) |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 152 | { |
Kevin O'Connor | 897fb11 | 2013-02-07 23:32:48 -0500 | [diff] [blame] | 153 | if (!CONFIG_QEMU || runningOnXen()) |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 154 | return; |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 155 | dprintf(3, "locking shadow ram\n"); |
Kevin O'Connor | 96d4c43 | 2013-03-08 19:31:14 -0500 | [diff] [blame] | 156 | |
| 157 | if (ShadowBDF < 0) { |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 158 | dprintf(1, "Unable to lock ram - bridge not found\n"); |
Kevin O'Connor | 96d4c43 | 2013-03-08 19:31:14 -0500 | [diff] [blame] | 159 | return; |
| 160 | } |
| 161 | |
| 162 | u16 device = pci_config_readw(ShadowBDF, PCI_DEVICE_ID); |
| 163 | if (device == PCI_DEVICE_ID_INTEL_82441) |
| 164 | make_bios_readonly_intel(ShadowBDF, I440FX_PAM0); |
| 165 | else |
| 166 | make_bios_readonly_intel(ShadowBDF, Q35_HOST_BRIDGE_PAM0); |
Kevin O'Connor | da4a648 | 2008-06-08 13:48:06 -0400 | [diff] [blame] | 167 | } |
Kevin O'Connor | 244caf8 | 2010-09-15 21:48:16 -0400 | [diff] [blame] | 168 | |
| 169 | void |
Kevin O'Connor | c68aff5 | 2017-03-03 10:48:45 -0500 | [diff] [blame] | 170 | qemu_reboot(void) |
Kevin O'Connor | 244caf8 | 2010-09-15 21:48:16 -0400 | [diff] [blame] | 171 | { |
Kevin O'Connor | 897fb11 | 2013-02-07 23:32:48 -0500 | [diff] [blame] | 172 | if (!CONFIG_QEMU || runningOnXen()) |
Kevin O'Connor | 244caf8 | 2010-09-15 21:48:16 -0400 | [diff] [blame] | 173 | return; |
| 174 | // QEMU doesn't map 0xc0000-0xfffff back to the original rom on a |
| 175 | // reset, so do that manually before invoking a hard reset. |
Kevin O'Connor | b837e68 | 2015-11-09 15:00:19 -0500 | [diff] [blame] | 176 | void *cstart = VSYMBOL(code32flat_start), *cend = VSYMBOL(code32flat_end); |
| 177 | void *hrp = &HaveRunPost; |
| 178 | if (readl(hrp + BIOS_SRC_OFFSET)) { |
Kevin O'Connor | 42812e0 | 2018-02-22 20:29:27 -0500 | [diff] [blame^] | 179 | // There isn't a pristine copy of the BIOS at 0xffff0000 to copy |
| 180 | if (HaveRunPost == 3) { |
| 181 | // In a reboot loop. Try to shutdown the machine instead. |
| 182 | dprintf(1, "Unable to hard-reboot machine - attempting shutdown.\n"); |
| 183 | apm_shutdown(); |
| 184 | } |
| 185 | make_bios_writable(); |
| 186 | HaveRunPost = 3; |
| 187 | } else { |
| 188 | // Copy the BIOS making sure to only reset HaveRunPost at end |
| 189 | make_bios_writable(); |
| 190 | memcpy(cstart, cstart + BIOS_SRC_OFFSET, hrp - cstart); |
| 191 | memcpy(hrp + 4, hrp + 4 + BIOS_SRC_OFFSET, cend - (hrp + 4)); |
| 192 | barrier(); |
| 193 | HaveRunPost = 0; |
| 194 | barrier(); |
Kevin O'Connor | b837e68 | 2015-11-09 15:00:19 -0500 | [diff] [blame] | 195 | } |
Kevin O'Connor | c68aff5 | 2017-03-03 10:48:45 -0500 | [diff] [blame] | 196 | |
| 197 | // Request a QEMU system reset. Do the reset in this function as |
| 198 | // the BIOS code was overwritten above and not all BIOS |
| 199 | // functionality may be available. |
| 200 | |
| 201 | // Attempt PCI style reset |
| 202 | outb(0x02, PORT_PCI_REBOOT); |
| 203 | outb(0x06, PORT_PCI_REBOOT); |
| 204 | |
| 205 | // Next try triple faulting the CPU to force a reset |
| 206 | asm volatile("int3"); |
Kevin O'Connor | 244caf8 | 2010-09-15 21:48:16 -0400 | [diff] [blame] | 207 | } |