diff --git a/doc/index.rst b/doc/index.rst index 194393434..3a303e184 100644 --- a/doc/index.rst +++ b/doc/index.rst @@ -10,6 +10,7 @@ :hidden: dev_guide/index + user_docs/index classic_cli_manpage contact about_flashrom/index diff --git a/doc/user_docs/example_partial_wp.rst b/doc/user_docs/example_partial_wp.rst new file mode 100644 index 000000000..67a382a71 --- /dev/null +++ b/doc/user_docs/example_partial_wp.rst @@ -0,0 +1,191 @@ +=================================== +Example of partial write-protection +=================================== + +This document provides demonstration of how one can protect part of a flash chip +from writing using :code:`flashrom` and its support for manipulating SPI write protection (WP). +This kind of protection requires changing connection of WP pin of the chip to prevent +any attempt of disabling the protection by software alone. + +Not to be confused with protection by flash controller of your motherboard (PCH protection). + +Version of flashrom +=================== + +Write-protect manipulation functionality is included in flashrom since release v1.3.0. +If for any reasons you need the latest code from head, you might need to build :code:`flashrom` +from scratch. The following docs describe how to do this :doc:`/dev_guide/building_from_source` and +:doc:`/dev_guide/building_with_make`. See also :doc:`/dev_guide/development_guide`. + +Alternatively, your operating system might provide development version of :code:`flashrom` as a package. + +Programmer support of WP +======================== + +Not all programmers support manipulating WP configuration. A suitable programmer must either +provide a dedicated API for working with WP or give sufficiently comprehensive access to the +interface of the flash chip. + +In particular, *internal* programmer on Intel platforms might allow only limited access to WP +feature of chips or effectively deny it. Read "Intel chipsets" section of flashrom's manpage +for details on how you can try choosing sequencing type to possibly make WP work for you. + +In some cases external flashing might be the only option and you need to unscrew your device, +find the chip, connect it to another device through a suitable adapter and finally be able +to configure it as you wish. + +Chip support in flashrom +======================== + +There is a great variety of chips with some not supporting write protection at all and others +doing it in their own peculiar way of which :code:`flashrom` has no idea. So the first thing to do is +to make sure that :code:`flashrom` knows how WP works for your chip and chipset doesn't get in the way. +Run a command like (adjust this and similar commands below if you're not using *internal* programmer +or need to specify other options):: + + flashrom --programmer internal --wp-status: + +Seeing this output line would mean that :code:`flashrom` doesn't know how to use WP feature of the chip you have:: + + Failed to get WP status: WP operations are not implemented for this chip + +Otherwise the output might contain something similar to this:: + + Protection range: start=0x00000000 length=0x00000000 (none) + Protection mode: disabled + +If so, you can continue with the rest of the instructions. + +Collecting information about the range +====================================== + +You need to know where the area you want to protect starts and ends. The example below assumes +you're trying to protect bootblock stored in CBFS at the end of some :code:`coreboot` firmware. In other cases +it might be a separate file which is put at the beginning of a chip. You need to have an idea of what +you're doing here or have some reliable instructions to follow. + +In this case :code:`cbfstool` can be used to list information about bootblock like this:: + + $ cbfstool rom print | sed -n '2p; /bootblock/p' + Name Offset Type Size Comp + bootblock 0x3ef100 bootblock 36544 none + +However, the offset is relative to the start of CBFS region, so we also need to find out offset of CBFS:: + + $ cbfstool rom layout | grep CBFS + 'COREBOOT' (CBFS, size 4161536, offset 12615680) + +Now we can calculate: + +* start offset (CBFS offset + 64 + bootblock offset):: + + 12615680 + 64 + 0x3ef100 = 0xff7140 + (printf "%#x\n" $(( 12615680 + 64 + 0x3ef100 ))) + +* end offset (start offset + bootblock size - 1):: + + 0xff7140 + 36544 - 1 = 0xffffff + (printf "%#x\n" $(( 0xff7140 + 36544 - 1 ))) + +Thus we need to write-protect the smallest area that covers the range from :code:`0xff7140` to :code:`0xffffff` +(both bounds are inclusive). + +“64” in the computation of start offset is offset of booblock data. Unfortunately, current tooling +doesn't provide a reliable way of determining actual offset, but 64 is the typical “extra offset” one needs +to add to account for file metadata of CBFS (otherwise it can be its multiple 128 or bigger). Bootblock +should normally end at the last byte of ROM on x86 systems, giving you a way to test the result of computations. + +Finding a matching range +======================== + +In most chips the list of supported ranges is fixed and you can't specify an arbitrary one. Some others +allow more fine-grained control (sector/block-based), but that feature is not supported even by development +version of flashrom at the time of writing (September 2023). + +Obtain list of supported ranges from which we'll pick the best match:: + + $ flashrom --programmer internal --wp-list + ... + Available protection ranges: + start=0x00000000 length=0x00000000 (none) + start=0x00000000 length=0x00001000 (lower 1/4096) + start=0x00fff000 length=0x00001000 (upper 1/4096) + start=0x00000000 length=0x00002000 (lower 1/2048) + start=0x00ffe000 length=0x00002000 (upper 1/2048) + start=0x00000000 length=0x00004000 (lower 1/1024) + start=0x00ffc000 length=0x00004000 (upper 1/1024) + start=0x00000000 length=0x00008000 (lower 1/512) + start=0x00ff8000 length=0x00008000 (upper 1/512) + start=0x00000000 length=0x00040000 (lower 1/64) + start=0x00fc0000 length=0x00040000 (upper 1/64) + start=0x00000000 length=0x00080000 (lower 1/32) + start=0x00f80000 length=0x00080000 (upper 1/32) + start=0x00000000 length=0x00100000 (lower 1/16) + start=0x00f00000 length=0x00100000 (upper 1/16) + start=0x00000000 length=0x00200000 (lower 1/8) + start=0x00e00000 length=0x00200000 (upper 1/8) + start=0x00000000 length=0x00400000 (lower 1/4) + start=0x00c00000 length=0x00400000 (upper 1/4) + start=0x00000000 length=0x00800000 (lower 1/2) + start=0x00800000 length=0x00800000 (upper 1/2) + start=0x00000000 length=0x00c00000 (lower 3/4) + start=0x00400000 length=0x00c00000 (upper 3/4) + start=0x00000000 length=0x00e00000 (lower 7/8) + start=0x00200000 length=0x00e00000 (upper 7/8) + start=0x00000000 length=0x00f00000 (lower 15/16) + start=0x00100000 length=0x00f00000 (upper 15/16) + start=0x00000000 length=0x00f80000 (lower 31/32) + start=0x00080000 length=0x00f80000 (upper 31/32) + start=0x00000000 length=0x00fc0000 (lower 63/64) + start=0x00040000 length=0x00fc0000 (upper 63/64) + start=0x00000000 length=0x00ff8000 (lower 511/512) + start=0x00008000 length=0x00ff8000 (upper 511/512) + start=0x00000000 length=0x00ffc000 (lower 1023/1024) + start=0x00004000 length=0x00ffc000 (upper 1023/1024) + start=0x00000000 length=0x00ffe000 (lower 2047/2048) + start=0x00002000 length=0x00ffe000 (upper 2047/2048) + start=0x00000000 length=0x00fff000 (lower 4095/4096) + start=0x00001000 length=0x00fff000 (upper 4095/4096) + start=0x00000000 length=0x01000000 (all) + +Pick a range by scanning the list in the top down order (because the smaller ranges come first): + + * if bootblock is at the start of a chip, look for the first lower range whose length is greater than the end offset + * if bootblock is at the end of a chip, look for the first upper range which starts before or at the start offset + * mind that you're unlikely to find an ideal match and will probably protect more than you need; this is fine + if that's just an empty space, but can cause trouble with future updates if that's some data or metadata which + changes with every release (see :doc:`fw_updates_vs_spi_wp` for more on this) + +This is the first upper range starting before 0xff7140:: + + start=0x00fc0000 length=0x00040000 (upper 1/64) + +It covers :code:`0x00fc0000 - 0x00ffffff` which includes our bootblock. This area takes up 256 KiB, about 7 times bigger +than our bootblock, but there is no better choice in this case and output of :code:`cbfstool rom layout` shows +that we additionally include a part of 876 KiB empty space which will hopefully remain there in future firmware versions +(it's a good idea to check before a firmware update). + +Protection setup +================ + +The following command sets the range and enables WP at the same time, the values are taken from the chosen range above:: + + flashrom --programmer internal --wp-range=0x00fc0000,0x00040000 --wp-enable + +You can set the range and change WP status independently as well if needed (just specify one :code:`--wp-*` option at a time). +Make sure that hardware protection is off (state of :code:`W#`/:code:`W/` pin of the chip) or you won't be able +to change WP configuration. + +On success, the output of the above command will include such lines:: + + Enabled hardware protection + Activated protection range: start=0x00fc0000 length=0x00040000 (upper 1/64) + +**Caveat:** :code:`flashrom` automatically tries to disable WP before any operation on a chip (read, write, erase, verify), +so double-check status of WP before changing state of WP pin on your chip! + +Verifying hardware protection +============================= + +Once you're happy with the configuration and changed state of WP pin, you can try disabling WP +using :code:`flashrom --wp-disable` to make sure that it fails now. diff --git a/doc/user_docs/fw_updates_vs_spi_wp.rst b/doc/user_docs/fw_updates_vs_spi_wp.rst new file mode 100644 index 000000000..43bcbe04e --- /dev/null +++ b/doc/user_docs/fw_updates_vs_spi_wp.rst @@ -0,0 +1,60 @@ +======================================== +Firmware updates vs SPI write-protection +======================================== + +Enabling write-protection of any kind is meant to obstruct changing data, but it also limits +what you can do to the part of firmware that's still writable. This document is meant to cover +some of the origins of such limitations and situations which might arise after +part of a flash chip has been protected. + +Firmware updates after locking bootblock +======================================== + +This section is primarily concerned with :code:`coreboot` with bootblock being protected from writing, +but similar problems can happen for any kind of firmware. + +Risks of partial updates +------------------------ + +Partial updates can produce an unbootable image if an old bootblock doesn't work with a more recent +version of :code:`coreboot`. This can be manifested in various ways ranging from an old bootblock not being able +to find new romstage to system booting successfully but data in :code:`coreboot` tables being mangled or incomplete. + +The incompatibilities might happen when switching version of firmware or even when using the same version +with a slightly different configuration. + +Another thing that can potentially cause trouble is CBFS layout. When bootblock is part of CBFS, +it doesn't necessarily have a fixed address, moreover it can change location as well if it depends on file size +(when bootblock's last byte must be the last byte of the image, which is the case on x86). If newer bootblock +is smaller such that an old WP range now covers bootblock and some other file, this file won't be fully updated +due to write-protection, potentially resulting in a corrupt image. Luckily, when bootblock is the last file +it's normally preceded by a significant amount of empty space, which won't let this situation to occur. + +On top of that, last 4 bytes of the image contain offset to the master header of CBFS. Depending on +the :code:`coreboot` version this offset might be crucial for the loading of romstage, in which case moving CBFS +within the image without updating the offset (when it's locked by WP) can also prevent the system from booting. + +Recovering from a broken state +------------------------------ + +Since broken flash won't let the system to boot, the way to fix it is to flash the chip externally by connecting +it to a different device. A possible alternative could be to have a backup flash created beforehand and swapping +it for the broken one (not very applicable if swapping doesn't require soldering). There are also some mainboards +with dual flash chips one of which acts as a backup that can be restored by holding power on button long enough. + +Flashing whole firmware image +============================= + +The function of the hardware protection mechanism (:code:`W#` or :code:`W/` pin of flash chips) is to lock state of +software protection thus preventing it from being disabled. After the chip is physically unlocked by changing +the state of the pin, the state of the write protection doesn't change. However, in this state the protection +can be easily turned off programmatically, which is what :code:`flashrom` tries to do before performing an operation on a chip. + +In other words, changing state of the WP pin might be enough to be able to flash the chip in full. +If :code:`flashrom` errors or you don't want to rely on the automatic behaviour, you can try to +explicitly disable the protection by running :code:`flashrom` like this:: + + flashrom --wp-disable + +If you need to pass extra parameters to flash your chip (e.g., programmer or chip name), add them to the above command +(order of such parameters shouldn't matter). diff --git a/doc/user_docs/index.rst b/doc/user_docs/index.rst new file mode 100644 index 000000000..b4cca3439 --- /dev/null +++ b/doc/user_docs/index.rst @@ -0,0 +1,8 @@ +Users documentation +=================== + +.. toctree:: + :maxdepth: 1 + + fw_updates_vs_spi_wp + example_partial_wp