dell_flash_unlock.c 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /* SPDX-License-Identifier: MIT */
  2. /* SPDX-FileCopyrightText: 2023 Nicholas Chin */
  3. #include <sys/mman.h>
  4. #include <err.h>
  5. #include <errno.h>
  6. #include <fcntl.h>
  7. #include <stdint.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <unistd.h>
  11. #include "accessors.h"
  12. int get_fdo_status(void);
  13. int check_lpc_decode(void);
  14. void ec_set_fdo(void);
  15. void write_ec_reg(uint8_t index, uint8_t data);
  16. void send_ec_cmd(uint8_t cmd);
  17. int wait_ec(void);
  18. int check_bios_write_en(void);
  19. int set_gbl_smi_en(int enable);
  20. int get_gbl_smi_en(void);
  21. #define EC_INDEX 0x910
  22. #define EC_DATA 0x911
  23. #define EC_ENABLE_FDO 2
  24. #define LPC_DEV PCI_DEV(0, 0x1f, 0)
  25. #define RCBA_MMIO_LEN 0x4000
  26. /* Register offsets */
  27. #define SPIBAR 0x3800
  28. #define HSFS_REG 0x04
  29. #define SMI_EN_REG 0x30
  30. volatile uint8_t *rcba_mmio;
  31. uint16_t pmbase;
  32. int
  33. main(int argc, char *argv[])
  34. {
  35. int devmemfd;
  36. (void)argc;
  37. (void)argv;
  38. if (sys_iopl(3) == -1)
  39. err(errno, "Could not access IO ports");
  40. if ((devmemfd = open("/dev/mem", O_RDONLY)) == -1)
  41. err(errno, "/dev/mem");
  42. /* Read RCBA and PMBASE from the LPC config registers */
  43. long int rcba = pci_read_32(LPC_DEV, 0xf0) & 0xffffc000;
  44. pmbase = pci_read_32(LPC_DEV, 0x40) & 0xff80;
  45. /* FDO pin-strap status bit is in RCBA mmio space */
  46. rcba_mmio = mmap(0, RCBA_MMIO_LEN, PROT_READ, MAP_SHARED, devmemfd,
  47. rcba);
  48. if (rcba_mmio == MAP_FAILED)
  49. err(errno, "Could not map RCBA");
  50. if (get_fdo_status() == 1) { /* Descriptor not overridden */
  51. if (check_lpc_decode() == -1)
  52. err(errno = ECANCELED, "Can't forward I/O to LPC");
  53. printf("Sending FDO override command to EC:\n");
  54. ec_set_fdo();
  55. printf("Flash Descriptor Override enabled.\n"
  56. "Shut down (don't reboot) now.\n\n"
  57. "The EC may auto-boot on some systems; if not then "
  58. "manually power on.\n When the system boots rerun "
  59. "this utility to finish unlocking.\n");
  60. } else if (check_bios_write_en() == 0) {
  61. /* SMI locks in place, try disabling SMIs to bypass them */
  62. if (set_gbl_smi_en(0)) {
  63. printf("SMIs disabled. Internal flashing should work "
  64. "now.\n After flashing, re-run this utility "
  65. "to enable SMIs.\n (shutdown is buggy when "
  66. "SMIs are disabled)\n");
  67. } else {
  68. err(errno = ECANCELED, "Could not disable SMIs!");
  69. }
  70. } else { /* SMI locks not in place or bypassed */
  71. if (get_gbl_smi_en()) {
  72. /* SMIs are still enabled, assume this is an Exx10
  73. * or newer which don't need the SMM bypass */
  74. printf("Flash is unlocked.\n"
  75. "Internal flashing should work.\n");
  76. } else {
  77. /* SMIs disabled, assume this is an Exx00 after
  78. * unlocking and flashing */
  79. set_gbl_smi_en(1);
  80. printf("SMIs enabled.\n"
  81. "You can now shutdown the system.\n");
  82. }
  83. }
  84. sys_iopl(0);
  85. return errno;
  86. }
  87. int
  88. get_fdo_status(void)
  89. {
  90. return (*(uint16_t*)(rcba_mmio + SPIBAR + HSFS_REG) >> 13) & 1;
  91. }
  92. int
  93. check_lpc_decode(void)
  94. {
  95. /* Check that at a Generic Decode Range Register is set up to
  96. * forward I/O ports 0x910 and 0x911 over LPC for the EC */
  97. int i = 0;
  98. int gen_dec_free = -1;
  99. for (; i < 4; i++) {
  100. uint32_t reg_val = pci_read_32(LPC_DEV, 0x84 + 4*i);
  101. uint16_t base_addr = reg_val & 0xfffc;
  102. uint16_t mask = ((reg_val >> 16) & 0xfffc) | 0x3;
  103. /* Bit 0 is the enable for each decode range. If disabled, note
  104. * this register as available to add our own range decode */
  105. if ((reg_val & 1) == 0)
  106. gen_dec_free = i;
  107. /* Check if the current range register matches port 0x910.
  108. * 0x911 doesn't need to be checked as the LPC bridge only
  109. * decodes at the dword level, and thus a check is redundant */
  110. if ((0x910 & ~mask) == base_addr) {
  111. return 0;
  112. }
  113. }
  114. /* No matching range found, try setting a range in a free register */
  115. if (gen_dec_free != -1) {
  116. /* Set up an I/O decode range from 0x910-0x913 */
  117. pci_write_32(LPC_DEV, 0x84 + 4 * gen_dec_free, 0x911);
  118. return 0;
  119. } else {
  120. return -1;
  121. }
  122. }
  123. void
  124. ec_set_fdo(void)
  125. {
  126. /* EC FDO command arguments for reference:
  127. * 0 = Query EC FDO status
  128. * 2 = Enable FDO for next boot
  129. * 3 = Disable FDO for next boot */
  130. write_ec_reg(0x12, EC_ENABLE_FDO);
  131. send_ec_cmd(0xb8);
  132. }
  133. void
  134. write_ec_reg(uint8_t index, uint8_t data)
  135. {
  136. sys_outb(EC_INDEX, index);
  137. sys_outb(EC_DATA, data);
  138. }
  139. void
  140. send_ec_cmd(uint8_t cmd)
  141. {
  142. sys_outb(EC_INDEX, 0);
  143. sys_outb(EC_DATA, cmd);
  144. if (wait_ec() == -1)
  145. err(errno = ECANCELED, "Timeout while waiting for EC!");
  146. }
  147. int
  148. wait_ec(void)
  149. {
  150. uint8_t busy;
  151. int timeout = 1000;
  152. do {
  153. sys_outb(EC_INDEX, 0);
  154. busy = sys_inb(EC_DATA);
  155. timeout--;
  156. usleep(1000);
  157. } while (busy && timeout > 0);
  158. return timeout > 0 ? 0 : -1;
  159. }
  160. int
  161. check_bios_write_en(void)
  162. {
  163. uint8_t bios_cntl = pci_read_32(LPC_DEV, 0xdc) & 0xff;
  164. /* Bit 5 = SMM BIOS Write Protect Disable (SMM_BWP)
  165. * Bit 1 = BIOS Lock Enable (BLE)
  166. * If both are 0, then there's no write protection */
  167. if ((bios_cntl & 0x22) == 0)
  168. return 1;
  169. /* SMM protection is enabled, but try enabling writes
  170. * anyway in case the vendor SMM code doesn't reset it */
  171. pci_write_32(LPC_DEV, 0xdc, bios_cntl | 0x1);
  172. return pci_read_32(LPC_DEV, 0xdc) & 0x1;
  173. }
  174. int
  175. set_gbl_smi_en(int enable)
  176. {
  177. uint32_t smi_en = sys_inl(pmbase + SMI_EN_REG);
  178. if (enable) {
  179. smi_en |= 1;
  180. } else {
  181. smi_en &= ~1;
  182. }
  183. sys_outl(pmbase + SMI_EN_REG, smi_en);
  184. return (get_gbl_smi_en() == enable);
  185. }
  186. int
  187. get_gbl_smi_en(void)
  188. {
  189. return sys_inl(pmbase + SMI_EN_REG) & 1;
  190. }