summaryrefslogtreecommitdiff
path: root/util/e6400-flash-unlock
diff options
context:
space:
mode:
authorNicholas Chin <nic.c3.14@gmail.com>2023-10-12 17:57:06 -0600
committerNicholas Chin <nic.c3.14@gmail.com>2023-10-12 17:57:06 -0600
commit5d6946c42cf8faa64f609155cf3b121ff8d78474 (patch)
tree184a97c42535cd0547cd8bcc44b7e7be670f3d85 /util/e6400-flash-unlock
parent42068f7ce11b88e13b4bf3f2d2290bc4d16d60b7 (diff)
util/e6400-flash-unlock: Rename to dell-flash-unlock
This more accurately describes the scope of the utility. Signed-off-by: Nicholas Chin <nic.c3.14@gmail.com>
Diffstat (limited to 'util/e6400-flash-unlock')
-rw-r--r--util/e6400-flash-unlock/COPYING19
-rw-r--r--util/e6400-flash-unlock/Makefile15
-rw-r--r--util/e6400-flash-unlock/README.md102
-rw-r--r--util/e6400-flash-unlock/accessors.c91
-rw-r--r--util/e6400-flash-unlock/accessors.h17
-rw-r--r--util/e6400-flash-unlock/e6400_flash_unlock.c217
6 files changed, 0 insertions, 461 deletions
diff --git a/util/e6400-flash-unlock/COPYING b/util/e6400-flash-unlock/COPYING
deleted file mode 100644
index bf82341..0000000
--- a/util/e6400-flash-unlock/COPYING
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2023 Nicholas Chin
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/util/e6400-flash-unlock/Makefile b/util/e6400-flash-unlock/Makefile
deleted file mode 100644
index 41a9048..0000000
--- a/util/e6400-flash-unlock/Makefile
+++ /dev/null
@@ -1,15 +0,0 @@
-# SPDX-License-Identifier: MIT
-# SPDX-FileCopyrightText: 2023 Nicholas Chin
-
-CC=cc
-CFLAGS=-Wall -Wextra -Werror -O2 -pedantic
-ifeq ($(shell uname), OpenBSD)
- CFLAGS += -l$(shell uname -p)
-endif
-SRCS=e6400_flash_unlock.c accessors.c
-
-all: $(SRCS) accessors.h
- $(CC) $(CFLAGS) $(SRCS) -o e6400_flash_unlock
-
-clean:
- rm -f e6400_flash_unlock
diff --git a/util/e6400-flash-unlock/README.md b/util/e6400-flash-unlock/README.md
deleted file mode 100644
index bc9dd9f..0000000
--- a/util/e6400-flash-unlock/README.md
+++ /dev/null
@@ -1,102 +0,0 @@
-# Dell Laptop Internal Flashing
-
-This utility allows you to use flashrom's internal programmer to program the
-entire BIOS flash chip from software while still running the original Dell
-BIOS, which normally restricts software writes to the flash chip. It seems like
-this works on any Dell laptop that has an EC similar to the SMSC MEC5035 on the
-E6400, which mainly seem to be the Latitude and Precision lines starting from
-around 2008 (E6400 era).
-
-## TL;DR
-Run `make` to compile the utility, and then run `sudo ./e6400_flash_unlock` and
-follow the directions it outputs.
-
-## Confirmed supported devices
-- Latitude E6400
-- Latitude E6410
-- Latitude E4310
-- Latitude E6430
-- Precision M6800
-
-It is likely that any other Latitude/Precision laptops from the same era as
-devices specifically mentioned in the above list will work as Dell seems to use
-the same ECs in one generation.
-
-## Detailed device specific behavior
-- On GM45 era laptops, the expected behavior is that you will run the utility
- for the first time, which will tell the EC to set the descriptor override on
- the next boot. Then you will need to shut down the system, after which the
- system will automatically boot up. You should then re-run the utility to
- disable SMM, after which you can run flashrom. Finally, you should run the
- utility a third time to reenable SMM so that shutdown works properly
- afterwards.
-- On 1st Generation Intel Core systems such as the E6410 and newer, run the
- utility and shutdown in the same way as the E6400. However, it seems like the
- EC no longer automatically boots the system. In this case you should manually
- power it on. It also seems that the firmware does not set the BIOS Lock bit
- when the descriptor override is set, making the 2nd run after the reboot
- technically unnecessary. There is no harm in rerunning it though, as the
- utility can detect when the flash is unlocked and perform the correct steps
- as necessary.
-
-## How it works
-There are several ways the firmware can protect itself from being overwritten.
-One way is the Intel Flash Descriptor (IFD) permissions. On Intel systems, the
-flash image is divided into several regions such as the IFD itself, Gigabit
-Ethernet (GBE) non-volative memory, Management Engine (ME) firmware, Platform
-Data (PD), and the BIOS. The IFD contains a section which specifies the
-read/write permissions for each SPI controller (such as the host system) and
-each region of the flash, which are enforced by the chipset.
-
-On the Latitude E6400, the host has read-only access to the IFD, no access to
-the ME region, and read-write access to the PD, GBE, and BIOS regions. In order
-for flashrom to write to the entire flash internally, the host needs full
-permissions to all of these regions. Since the IFD is read only, we cannot
-change these permissions unless we directly access the chip using an external
-programmer, which defeats the purpose of internal flashing.
-
-However, Intel chipsets have a pin strap that allows the flash descriptor
-permissions to be overridden depending on the value of the pin at power on,
-granting RW permissions to all regions. On the ICH9M chipset on the E6400, this
-pin is HDA\_DOCK\_EN/GPIO33, which will enable the override if it is sampled
-low. This pin happens to be connected to a GPIO controlled by the Embedded
-Controller (EC), a small microcontroller on the board which handles things like
-the keyboard, touchpad, LEDs, and other system level tasks. Software can send a
-certain command to the EC, which tells it to pull GPIO33 low on the next boot.
-
-Although we now have full access according to the IFD permissions, we still
-cannot flash the whole chip, due to another protection the firmware uses.
-Before software can update the BIOS, it must change the BIOS Write Enable
-(BIOSWE) bit in the chipset from 0 to 1. However, if the BIOS Lock Enable (BLE)
-bit is also set to 1, then changing the BIOSWE bit triggers a System Management
-Interrupt (SMI). This causes the processor to enter System Management Mode
-(SMM), a highly privileged x86 execution state which operates transparently to
-the operating system. The code that SMM runs is provided by the BIOS, which
-checks the BIOSWE bit and sets it back to 0 before returning control to the OS.
-This feature is intended to only allow SMM code to update the system firmware.
-As the switch to SMM suspends the execution of the OS, it appears to the OS
-that the BIOSWE bit was never set to 1. Unfortunately, the BLE bit cannot be
-set back to 0 once it is set to 1, so this functionality cannot be disabled
-after it is first enabled by the BIOS.
-
-Older versions of the E6400 BIOS did not set the BLE bit, allowing flashrom to
-flash the entire flash chip internally after only setting the descriptor
-override. However, more recent versions do set it, so we may have hit a dead
-end unless we force downgrade to an older version (though there is a more
-convenient method, as we are about to see).
-
-What if there was a way to sidestep the BIOS Lock entirely? As it turns out,
-there is, and it's called the Global SMI Enable (GBL\_SMI\_EN) bit. If it's set
-to 1, then the chipset will generate SMIs, such as when we change BIOSWE with
-BLE set. If it's 0, then no SMI will be generated, even with the BLE bit set.
-On the E6400, GBL\_SMI\_EN is set to 1, and it can be changed back to 0, unlike
-the BLE bit. But there still might be one bit in the way, the SMI\_LOCK bit,
-which prevents modifications to GBL\_SMI\_EN when SMI\_LOCK is 1. Like the BLE
-bit, it cannot be changed back to 0 once it set to 1. But we are in luck, as
-the vendor E6400 BIOS leaves SMI\_LOCK unset at 0, allowing us to clear
-GBL\_SMI\_EN and disable SMIs, bypassing the BIOS Lock protections.
-
-There are other possible protection mechanisms that the firmware can utilize,
-such as Protected Range Register settings, which apply access permissions to
-address ranges of the flash, similar to the IFD. However, the E6400 vendor
-firmware does not utilize these, so they will not be discussed.
diff --git a/util/e6400-flash-unlock/accessors.c b/util/e6400-flash-unlock/accessors.c
deleted file mode 100644
index 6fca281..0000000
--- a/util/e6400-flash-unlock/accessors.c
+++ /dev/null
@@ -1,91 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* SPDX-FileCopyrightText: 2023 Nicholas Chin */
-
-#if defined(__linux__)
-#include <sys/io.h>
-#endif
-
-#if defined(__OpenBSD__)
-#include <machine/sysarch.h>
-#include <sys/types.h>
-#if defined(__amd64__)
-#include <amd64/pio.h>
-#elif defined(__i386__)
-#include <i386/pio.h>
-#endif /* __i386__ */
-#endif /* __OpenBSD__ */
-
-#include <errno.h>
-
-#include "accessors.h"
-
-uint32_t
-pci_read_32(uint32_t dev, uint8_t reg)
-{
- sys_outl(PCI_CFG_ADDR, dev | reg);
- return sys_inl(PCI_CFG_DATA);
-}
-
-void
-pci_write_32(uint32_t dev, uint8_t reg, uint32_t value)
-{
- sys_outl(PCI_CFG_ADDR, dev | reg);
- sys_outl(PCI_CFG_DATA, value);
-}
-
-void
-sys_outb(unsigned int port, uint8_t data)
-{
- #if defined(__linux__)
- outb(data, port);
- #endif
- #if defined(__OpenBSD__)
- outb(port, data);
- #endif
-}
-
-void
-sys_outl(unsigned int port, uint32_t data)
-{
- #if defined(__linux__)
- outl(data, port);
- #endif
- #if defined(__OpenBSD__)
- outl(port, data);
- #endif
-}
-
-uint8_t
-sys_inb(unsigned int port)
-{
- #if defined(__linux__) || defined (__OpenBSD__)
- return inb(port);
- #endif
- return 0;
-}
-
-uint32_t
-sys_inl(unsigned int port)
-{
- #if defined(__linux__) || defined (__OpenBSD__)
- return inl(port);
- #endif
- return 0;
-}
-
-int
-sys_iopl(int level)
-{
-#if defined(__linux__)
- return iopl(level);
-#endif
-#if defined(__OpenBSD__)
-#if defined(__i386__)
- return i386_iopl(level);
-#elif defined(__amd64__)
- return amd64_iopl(level);
-#endif /* __amd64__ */
-#endif /* __OpenBSD__ */
- errno = ENOSYS;
- return -1;
-}
diff --git a/util/e6400-flash-unlock/accessors.h b/util/e6400-flash-unlock/accessors.h
deleted file mode 100644
index a19f215..0000000
--- a/util/e6400-flash-unlock/accessors.h
+++ /dev/null
@@ -1,17 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* SPDX-FileCopyrightText: 2023 Nicholas Chin */
-
-#include <stdint.h>
-
-#define PCI_CFG_ADDR 0xcf8
-#define PCI_CFG_DATA 0xcfc
-#define PCI_DEV(bus, dev, func) (1u << 31 | bus << 16 | dev << 11 | func << 8)
-
-uint32_t pci_read_32(uint32_t dev, uint8_t reg);
-void pci_write_32(uint32_t dev, uint8_t reg, uint32_t value);
-
-int sys_iopl(int level);
-void sys_outb(unsigned int port, uint8_t data);
-void sys_outl(unsigned int port, uint32_t data);
-uint8_t sys_inb(unsigned int port);
-uint32_t sys_inl(unsigned int port);
diff --git a/util/e6400-flash-unlock/e6400_flash_unlock.c b/util/e6400-flash-unlock/e6400_flash_unlock.c
deleted file mode 100644
index 174a1c9..0000000
--- a/util/e6400-flash-unlock/e6400_flash_unlock.c
+++ /dev/null
@@ -1,217 +0,0 @@
-/* SPDX-License-Identifier: MIT */
-/* SPDX-FileCopyrightText: 2023 Nicholas Chin */
-
-#include <sys/mman.h>
-
-#include <err.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-
-#include "accessors.h"
-
-int get_fdo_status(void);
-int check_lpc_decode(void);
-void ec_set_fdo();
-void write_ec_reg(uint8_t index, uint8_t data);
-void send_ec_cmd(uint8_t cmd);
-int wait_ec(void);
-int check_bios_write_en(void);
-int set_gbl_smi_en(int enable);
-int get_gbl_smi_en(void);
-
-#define EC_INDEX 0x910
-#define EC_DATA 0x911
-#define EC_ENABLE_FDO 2
-
-#define LPC_DEV PCI_DEV(0, 0x1f, 0)
-
-#define RCBA_MMIO_LEN 0x4000
-
-/* Register offsets */
-#define SPIBAR 0x3800
-#define HSFS_REG 0x04
-#define SMI_EN_REG 0x30
-
-volatile uint8_t *rcba_mmio;
-uint16_t pmbase;
-
-int
-main(int argc, char *argv[])
-{
- int devmemfd;
- (void)argc;
- (void)argv;
-
- if (sys_iopl(3) == -1)
- err(errno, "Could not access IO ports");
- if ((devmemfd = open("/dev/mem", O_RDONLY)) == -1)
- err(errno, "/dev/mem");
-
- /* Read RCBA and PMBASE from the LPC config registers */
- long int rcba = pci_read_32(LPC_DEV, 0xf0) & 0xffffc000;
- pmbase = pci_read_32(LPC_DEV, 0x40) & 0xff80;
-
- /* FDO pin-strap status bit is in RCBA mmio space */
- rcba_mmio = mmap(0, RCBA_MMIO_LEN, PROT_READ, MAP_SHARED, devmemfd,
- rcba);
- if (rcba_mmio == MAP_FAILED)
- err(errno, "Could not map RCBA");
-
- if (get_fdo_status() == 1) { /* Descriptor not overridden */
- if (check_lpc_decode() == -1)
- err(errno = ECANCELED, "Can't forward I/O to LPC");
-
- printf("Sending FDO override command to EC:\n");
- ec_set_fdo();
- printf("Flash Descriptor Override enabled.\n"
- "Shut down (don't reboot) now.\n\n"
- "The EC may auto-boot on some systems; if not then "
- "manually power on.\n When the system boots rerun "
- "this utility to finish unlocking.\n");
- } else if (check_bios_write_en() == 0) {
- /* SMI locks in place, try disabling SMIs to bypass them */
- if (set_gbl_smi_en(0)) {
- printf("SMIs disabled. Internal flashing should work "
- "now.\n After flashing, re-run this utility "
- "to enable SMIs.\n (shutdown is buggy when "
- "SMIs are disabled)\n");
- } else {
- err(errno = ECANCELED, "Could not disable SMIs!");
- }
- } else { /* SMI locks not in place or bypassed */
- if (get_gbl_smi_en()) {
- /* SMIs are still enabled, assume this is an Exx10
- * or newer which don't need the SMM bypass */
- printf("Flash is unlocked.\n"
- "Internal flashing should work.\n");
- } else {
- /* SMIs disabled, assume this is an Exx00 after
- * unlocking and flashing */
- set_gbl_smi_en(1);
- printf("SMIs enabled.\n"
- "You can now shutdown the system.\n");
- }
- }
- return errno;
-}
-
-int
-get_fdo_status(void)
-{
- return (*(uint16_t*)(rcba_mmio + SPIBAR + HSFS_REG) >> 13) & 1;
-}
-
-int
-check_lpc_decode(void)
-{
- /* Check that at a Generic Decode Range Register is set up to
- * forward I/O ports 0x910 and 0x911 over LPC for the EC */
- int i = 0;
- int gen_dec_free = -1;
- for (; i < 4; i++) {
- uint32_t reg_val = pci_read_32(LPC_DEV, 0x84 + 4*i);
- uint16_t base_addr = reg_val & 0xfffc;
- uint16_t mask = ((reg_val >> 16) & 0xfffc) | 0x3;
-
- /* Bit 0 is the enable for each decode range. If disabled, note
- * this register as available to add our own range decode */
- if ((reg_val & 1) == 0)
- gen_dec_free = i;
-
- /* Check if the current range register matches port 0x910.
- * 0x911 doesn't need to be checked as the LPC bridge only
- * decodes at the dword level, and thus a check is redundant */
- if ((0x910 & ~mask) == base_addr) {
- return 0;
- }
- }
-
- /* No matching range found, try setting a range in a free register */
- if (gen_dec_free != -1) {
- /* Set up an I/O decode range from 0x910-0x913 */
- pci_write_32(LPC_DEV, 0x84 + 4 * gen_dec_free, 0x911);
- return 0;
- } else {
- return -1;
- }
-}
-
-void
-ec_set_fdo()
-{
- /* EC FDO command arguments for reference:
- * 0 = Query EC FDO status
- * 2 = Enable FDO for next boot
- * 3 = Disable FDO for next boot */
- write_ec_reg(0x12, EC_ENABLE_FDO);
- send_ec_cmd(0xb8);
-}
-
-void
-write_ec_reg(uint8_t index, uint8_t data)
-{
- sys_outb(EC_INDEX, index);
- sys_outb(EC_DATA, data);
-}
-
-void
-send_ec_cmd(uint8_t cmd)
-{
- sys_outb(EC_INDEX, 0);
- sys_outb(EC_DATA, cmd);
- if (wait_ec() == -1)
- err(errno = ECANCELED, "Timeout while waiting for EC!");
-}
-
-int
-wait_ec(void)
-{
- uint8_t busy;
- int timeout = 1000;
- do {
- sys_outb(EC_INDEX, 0);
- busy = sys_inb(EC_DATA);
- timeout--;
- usleep(1000);
- } while (busy && timeout > 0);
- return timeout > 0 ? 0 : -1;
-}
-
-int
-check_bios_write_en(void)
-{
- uint8_t bios_cntl = pci_read_32(LPC_DEV, 0xdc) & 0xff;
- /* Bit 5 = SMM BIOS Write Protect Disable (SMM_BWP)
- * Bit 1 = BIOS Lock Enable (BLE)
- * If both are 0, then there's no write protection */
- if ((bios_cntl & 0x22) == 0)
- return 1;
-
- /* SMM protection is enabled, but try enabling writes
- * anyway in case the vendor SMM code doesn't reset it */
- pci_write_32(LPC_DEV, 0xdc, bios_cntl | 0x1);
- return pci_read_32(LPC_DEV, 0xdc) & 0x1;
-}
-
-int
-set_gbl_smi_en(int enable)
-{
- uint32_t smi_en = sys_inl(pmbase + SMI_EN_REG);
- if (enable) {
- smi_en |= 1;
- } else {
- smi_en &= ~1;
- }
- sys_outl(pmbase + SMI_EN_REG, smi_en);
- return (get_gbl_smi_en() == enable);
-}
-
-int
-get_gbl_smi_en(void)
-{
- return sys_inl(pmbase + SMI_EN_REG) & 1;
-}