| # Firmware Write Protection |
| |
| [TOC] |
| |
| This is a somewhat tricky topic since write protection implementations can |
| differ between chips and the hardware write protection has changed over time, |
| so please edit or open a bug if something is not clear. |
| |
| ## Terminology |
| |
| ## RO and RW |
| |
| MCUs running the EC code have read-only (RO) and read-write (RW) firmware. |
| Coming out of reset, the MCU boots into its RO firmware. |
| |
| In the case of the EC, the RO firmware boots the host and asks it verify a hash |
| of the RW firmware (software sync). If the RW firmware is invalid, it is |
| updated from a copy in the host's RW firmware. |
| |
| In the case of the FPMCU, the RO firmware uses the public key embedded in it to |
| validate the signature of the RW firmware. If the RW firmware is invalid it |
| does not jump to the RW firmware. |
| |
| Once the RW firmware is validated, the MCU jumps to it (without rebooting). The |
| RO firmware is locked in the factory and is never changed. The RW firmware can |
| be updated later by pushing a new system firmware containing an updated RW |
| region. |
| |
| Note that both the RO and RW firmware regions are normally protected once write |
| protect has been turned on. |
| |
| In the case of the EC, the RW region is unprotected at MCU boot until it has |
| been verified by the host. The RW region is protected before the Linux kernel |
| is loaded. |
| |
| In the case of the FPMCU, the RW region is protected before jumping the RO |
| firmware jumps to it. |
| |
| ## Hardware Write Protect {#hw_wp} |
| |
| On modern Chrome OS devices, the Cr50 (aka GSC / TPM) provides a "hardware |
| write protect" GPIO that is connected to the AP SPI flash, EC SPI flash, |
| EEPROM, and FPMCU via a [GPIO][write_protect_gpio]. This "hardware write |
| protect" can only be disabled with servo or suzyq (["CCD open"]) and |
| corresponds to [`OverrideWP`] in ccd. Disabling this write protect disables it |
| for everything connected to this signal. |
| |
| In the case of the FPMCU, the hardware write protect GPIO is tied to the STM32 |
| `BOOT0` pin, which is what tells the MCU to enter the STM32 bootloader mode. |
| |
| You may see various references to a [write protect screw in |
| documentation][wp_screw]. Older Chrome OS devices had a write protect screw |
| that had to be physically removed. More details on this history can be found |
| here: http://go/cros-wp-status. |
| |
| Another way of disabling hardware write protection is to remove the battery; |
| this method is mainly used during bringup. |
| |
| Additional reference: |
| https://www.google.com/chromeos/partner/fe/docs/cpfe/firmwaretestmanual.html#hardware-write-protect |
| |
| ## Changing Hardware Write Protection |
| |
| Modifying the state of hardware write protection (via Cr50 GPIO) can be done |
| if the ["CCD open"] process has been completed. |
| |
| *** note |
| `servod` *must* be running for `dut-control` to work. See the [Servo] page for |
| details. |
| *** |
| |
| ### Enable Hardware Write Protection |
| |
| ```bash |
| (chroot)$ dut-control fw_wp_state:force_on |
| ``` |
| |
| ### Disable Hardware Write Protection |
| |
| ```bash |
| (chroot)$ dut-control fw_wp_state:force_off |
| ``` |
| |
| ### Enable/Disable Hardware Write Protection via Cr50 Console |
| |
| You can use the following commands from the [Cr50 console]: |
| |
| ```bash |
| wp disable |
| ``` |
| |
| ```bash |
| wp enable |
| ``` |
| |
| ```bash |
| wp follow_batt_pres |
| ``` |
| |
| ## Software Write Protect |
| |
| Software-based write protect state stored in non-volatile memory. If hardware |
| write protect is enabled, software write protect can be enabled but can’t be |
| disabled. If hardware write protect is disabled, software write protect can be |
| enabled or disabled (note that some implementations require an EC reset to |
| disable software write protect). |
| |
| The underlying mechanism implementing software write protect may differ between |
| EC chips. However the common requirements are that software write protect can |
| only be disabled when hardware write protect is off and that the RO firmware |
| must be protected before jumping to RW firmware if protection is enabled. |
| |
| *** note |
| *WARNING*: If you disable HW write protect *and* then reboot the FPMCU, it will do |
| a mass erase of the chip, due to [RDP1](#rdp1). |
| *** |
| |
| Additional reference: |
| https://www.google.com/chromeos/partner/fe/docs/cpfe/firmwaretestmanual.html#software-write-protect |
| |
| ## Changing Software Write Protection |
| |
| *** note |
| *NOTE*: You cannot disable software write protect if hardware write protect is |
| enabled. |
| *** |
| |
| Software write protection can be toggled with `ectool --name=cros_fp flashprotect |
| enable/disable`, which sends the `EC_CMD_FLASH_PROTECT` command toggling |
| `EC_FLASH_PROTECT_RO_AT_BOOT` (changing `--name` to target different ECs). |
| |
| ### Changing Software Write Protection with `ectool` |
| |
| #### `ectool flashprotect` |
| |
| Print out current flash protection state. |
| |
| ``` |
| Flash protect flags: 0x0000000f wp_gpio_asserted ro_at_boot ro_now all_now |
| Valid flags: 0x0000003f wp_gpio_asserted ro_at_boot ro_now all_now STUCK INCONSISTENT |
| Writable flags: 0x00000000 |
| ``` |
| |
| `Flash protect flags` - Current flags that are set. |
| |
| `Valid flags` - All the options for flash protection. |
| |
| `Writable flags` - The flags that currently can be changed. (In this case, no |
| flags can be changed). |
| |
| Flags: |
| |
| * `wp_gpio_asserted` - Whether the hardware write protect GPIO is currently |
| asserted (read only). |
| |
| * `ro_at_boot` - Whether the EC will write protect the RO firmware on the next |
| boot of the EC. |
| |
| * `ro_now` - Protect the read-only portion of flash immediately. Requires |
| hardware WP be enabled. |
| |
| * `all_now` - Protect the entire flash (including RW) immediately. Requires |
| hardware WP be enabled. |
| |
| * `STUCK` - Flash protection settings have been fused and can’t be cleared |
| (should not happen during normal operation. Read only.) |
| |
| * `INCONSISTENT` - One or more banks of flash is not protected when it should |
| be (should not happen during normal operation. Read only.). |
| |
| #### `ectool flashprotect enable` |
| |
| Set `ro_at_boot` flag. The next time the EC is reset it will protect the flash. |
| Note that this requires a cold reset. |
| |
| #### `ectool flashprotect enable now` |
| |
| Set `ro_at_boot` `ro_now all_now` flags and immediately protect the flash. Note |
| that this will fail if hardware write protect is disabled. |
| |
| #### `ectool flashprotect disable` |
| |
| Clear `ro_at_boot` flag. This can only be cleared if the EC booted without |
| hardware write protect enabled. |
| |
| Note that you must reset the EC to clear write protect after removing the screw. |
| If the `ro_at_boot` flag set and the EC resets with the HW gpio disabled, the EC |
| will leave the flash unprotected (`ro_now` and `all_now` flags are not set) but |
| leave `ro_at_boot` flag set. |
| |
| ## `system_is_locked()` |
| |
| The [`system_is_locked()`] function in the EC code returns false if the HW |
| write protect GPIO is disabled or the read-only firmware is not protected. |
| |
| One way this is used in the FPMCU source is to compile test or debug |
| functionality into the firmware. Guarding the test functionality with |
| `system_is_locked` allows us to execute the test code in automated testing by |
| disabling the hardware write protection; this means we can run the automated |
| tests against the exact same firmware we ship, rather than a different version |
| that has test functionality compiled in or out. |
| |
| ## RDP1 {#rdp1} |
| |
| Stands for Readout Protection Level 1. |
| |
| Protects user flash memory against a debugger (JTAG/SWD) or potential malicious |
| code stored in RAM by disabling access (a bus error is generated when read |
| access is requested). Otherwise (no debugger connected and no boot in RAM set), |
| all read/program/erase operations from/to flash are allowed. |
| |
| When switching to a lower level of RDP (i.e., setting to 0), the user flash |
| memory is mass erased (set to all `0xFF`). |
| |
| Note that this completely destroys *all* of the firmware, including the RO |
| section. |
| |
| ### Additional References |
| |
| https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/1222094 |
| |
| ## EC Flash Read/Write Command Write Protection Checks |
| |
| The EC code command handlers (`command_flash_erase`, `command_flash_write`, |
| etc.) return an error if `EC_FLASH_PROTECT_ALL_NOW` is set. |
| |
| [write_protect_gpio]: https://chromium.googlesource.com/chromiumos/platform/ec/+/aaba1d5efd51082d143ce2ac64e6caf9cb14d5e5/include/ec_commands.h#1599 |
| ["CCD open"]: ./case_closed_debugging_gsc.md#Open-CCD |
| [Cr50 console]: ./case_closed_debugging_gsc.md#Consoles |
| [`OverrideWP`]: ./case_closed_debugging_gsc.md |
| [wp_screw]: https://www.chromium.org/chromium-os/firmware-porting-guide/firmware-ec-write-protection |
| [`system_is_locked()`]: https://chromium.googlesource.com/chromiumos/platform/ec/+/aaba1d5efd51082d143ce2ac64e6caf9cb14d5e5/common/system.c#195 |
| [Servo]: https://www.chromium.org/chromium-os/servo |