Hi again,
As previously discussed on this ML, posting documentation inline for review.
================================================================================
# 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 `coreboot`, 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 `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 `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 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 (mainly applicable if swapping doesn't require soldering).
## Flashing whole firmware image
The function of the hardware protection mechanism (`W#` or `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 `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 `flashrom` errors or you don't want to rely on the automatic behaviour, you can try to explicitly disable the protection by running `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).
Mind that in `flashrom` the code for disabling protection automatically is different from the code which handles `--wp-disable`. This means that they can sometimes produce different results, this is why you should be aware of both code paths that affect WP.
================================================================================
# Example of partial write-protection
This document provides demonstration of how one can protect part of a flash chip from writing using `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).
## `flashrom` version
At the time of writing (11 October 2022) there hasn't been a `flashrom` release that includes WP manipulation facilities. You might have to build one from scratch (assuming you've already installed build dependencies):
``` git clone --depth 1 https://github.com/flashrom/flashrom cd flashrom # the simplest case of building using GNU make make # flashrom executable will appear in current directory ```
## 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, on Intel platforms *internal* programmer 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 `flashrom` has no idea. So the first thing to do is to make sure that `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 `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 `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 `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 `0xff7140` to `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, but that feature is not supported by `flashrom` as of now.
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 troubles with future updates if that's some data or metadata which changes with every release
This is the first upper range starting before `0xff7140`:
``` start=0x00fc0000 length=0x00040000 (upper 1/64) ```
It covers `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 `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 `--wp-*` option at a time). Make sure that hardware protection is off (state of `W#`/`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**: `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've happy with the configuration and changed state of WP pin, you can try disabling WP using `flashrom` to make sure that it fails now.
================================================================================
Regards, Sergii
ping
On Tue, Oct 11, 2022 at 07:21:29PM +0300, Sergii Dmytruk wrote:
Hi again,
As previously discussed on this ML, posting documentation inline for review.
================================================================================
# 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 `coreboot`, 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 `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 `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 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 (mainly applicable if swapping doesn't require soldering).
## Flashing whole firmware image
The function of the hardware protection mechanism (`W#` or `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 `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 `flashrom` errors or you don't want to rely on the automatic behaviour, you can try to explicitly disable the protection by running `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).
Mind that in `flashrom` the code for disabling protection automatically is different from the code which handles `--wp-disable`. This means that they can sometimes produce different results, this is why you should be aware of both code paths that affect WP.
================================================================================
# Example of partial write-protection
This document provides demonstration of how one can protect part of a flash chip from writing using `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).
## `flashrom` version
At the time of writing (11 October 2022) there hasn't been a `flashrom` release that includes WP manipulation facilities. You might have to build one from scratch (assuming you've already installed build dependencies):
git clone --depth 1 https://github.com/flashrom/flashrom cd flashrom # the simplest case of building using GNU make make # flashrom executable will appear in current directory
## 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, on Intel platforms *internal* programmer 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 `flashrom` has no idea. So the first thing to do is to make sure that `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 `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 `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 `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 `0xff7140` to `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, but that feature is not supported by `flashrom` as of now.
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 troubles with future updates if that's some data or metadata which changes with every release
This is the first upper range starting before `0xff7140`:
start=0x00fc0000 length=0x00040000 (upper 1/64)
It covers `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 `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 `--wp-*` option at a time). Make sure that hardware protection is off (state of `W#`/`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**: `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've happy with the configuration and changed state of WP pin, you can try disabling WP using `flashrom` to make sure that it fails now.
================================================================================
Regards, Sergii
This looks good to me! (for writeprotect, I'm not as familiar with coreboot specific stuff)
A couple minor changes:
Mind that in `flashrom` the code for disabling protection automatically is different from the code which handles `--wp-disable`. This means that they can sometimes produce different results, this is why you should be aware of both code paths that affect WP.
Hopefully flashrom will change to WP-only code for this soon but it's fine to have for now. Maybe also include that flashrom automatically restores the previous protection state when it's done.
Once you've happy with the configuration and changed state of WP pin, you
can try disabling WP using `flashrom` to make sure that it fails now.
Use the full command here i.e. `flashrom --wp-disable`
On Wed, Oct 12, 2022 at 3:22 AM Sergii Dmytruk sergii.dmytruk@3mdeb.com wrote:
Hi again,
As previously discussed on this ML, posting documentation inline for review.
================================================================================
# 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 `coreboot`, 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 `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 `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 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 (mainly applicable if swapping doesn't require soldering).
## Flashing whole firmware image
The function of the hardware protection mechanism (`W#` or `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 `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 `flashrom` errors or you don't want to rely on the automatic behaviour, you can try to explicitly disable the protection by running `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).
Mind that in `flashrom` the code for disabling protection automatically is different from the code which handles `--wp-disable`. This means that they can sometimes produce different results, this is why you should be aware of both code paths that affect WP.
================================================================================
# Example of partial write-protection
This document provides demonstration of how one can protect part of a flash chip from writing using `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).
## `flashrom` version
At the time of writing (11 October 2022) there hasn't been a `flashrom` release that includes WP manipulation facilities. You might have to build one from scratch (assuming you've already installed build dependencies):
git clone --depth 1 https://github.com/flashrom/flashrom cd flashrom # the simplest case of building using GNU make make # flashrom executable will appear in current directory
## 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, on Intel platforms *internal* programmer 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 `flashrom` has no idea. So the first thing to do is to make sure that `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 `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 `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 `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 `0xff7140` to `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, but that feature is not supported by `flashrom` as of now.
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 troubles with future updates if that's some data or metadata which changes with every release
This is the first upper range starting before `0xff7140`:
start=0x00fc0000 length=0x00040000 (upper 1/64)
It covers `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 `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 `--wp-*` option at a time). Make sure that hardware protection is off (state of `W#`/`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**: `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've happy with the configuration and changed state of WP pin, you can try disabling WP using `flashrom` to make sure that it fails now.
================================================================================
Regards, Sergii _______________________________________________ flashrom mailing list -- flashrom@flashrom.org To unsubscribe send an email to flashrom-leave@flashrom.org
I am wondering, it has been a few weeks and hopefully people who are interested had a chance to read the suggested documentation? I think we can start thinking of updating the wiki page? In any case, any further corrections/amendments can be made later, if anything comes up.
Nikolai, thanks a lot for your feedback, really appreciate it!
So, let's just double-check, hello everyone, are there any other thoughts/suggestions?
Thanks for taking a look. Diff of changes and new version is below.
================================================================================
bootblock-protection.md | 6 +++--- heads-and-wp.md | 9 +++++---- 2 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/bootblock-protection.md b/bootblock-protection.md index fafb32e..66a6bb2 100644 --- a/bootblock-protection.md +++ b/bootblock-protection.md @@ -11,7 +11,7 @@ motherboard (PCH protection).
## `flashrom` version
-At the time of writing (11 October 2022) there hasn't been a `flashrom` release +At the time of writing (26 October 2022) there hasn't been a `flashrom` release that includes WP manipulation facilities. You might have to build one from scratch (assuming you've already installed build dependencies):
@@ -217,5 +217,5 @@ changing state of WP pin on your chip!
## Verifying hardware protection
-Once you've happy with the configuration and changed state of WP pin, you can -try disabling WP using `flashrom` to make sure that it fails now. +Once you're happy with the configuration and changed state of WP pin, you can +try disabling WP using `flashrom --dp-disable` to make sure that it fails now. diff --git a/heads-and-wp.md b/heads-and-wp.md index 70a80fd..0f5485c 100644 --- a/heads-and-wp.md +++ b/heads-and-wp.md @@ -64,7 +64,8 @@ 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).
-Mind that in `flashrom` the code for disabling protection automatically is -different from the code which handles `--wp-disable`. This means that they can -sometimes produce different results, this is why you should be aware of both -code paths that affect WP. +Mind that as of now (26 October 2022) `flashrom` uses different code for +temporarily disabling protection automatically before an operation (the state +is restored afterwards) and for handling `--wp-disable`. This means that effects +on write protection can be different depending on which code changes it, which +is why you should know about existence of both of these code paths.
================================================================================
# 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 `coreboot`, 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 `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 `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 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 (mainly applicable if swapping doesn't require soldering).
## Flashing whole firmware image
The function of the hardware protection mechanism (`W#` or `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 `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 `flashrom` errors or you don't want to rely on the automatic behaviour, you can try to explicitly disable the protection by running `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).
Mind that as of now (26 October 2022) `flashrom` uses different code for temporarily disabling protection automatically before an operation (the state is restored afterwards) and for handling `--wp-disable`. This means that effects on write protection can be different depending on which code changes it, which is why you should know about existence of both of these code paths.
================================================================================
# Example of partial write-protection
This document provides demonstration of how one can protect part of a flash chip from writing using `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).
## `flashrom` version
At the time of writing (26 October 2022) there hasn't been a `flashrom` release that includes WP manipulation facilities. You might have to build one from scratch (assuming you've already installed build dependencies):
``` git clone --depth 1 https://github.com/flashrom/flashrom cd flashrom # the simplest case of building using GNU make make # flashrom executable will appear in current directory ```
## 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, on Intel platforms *internal* programmer 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 `flashrom` has no idea. So the first thing to do is to make sure that `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 `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 `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 `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 `0xff7140` to `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, but that feature is not supported by `flashrom` as of now.
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 troubles with future updates if that's some data or metadata which changes with every release
This is the first upper range starting before `0xff7140`:
``` start=0x00fc0000 length=0x00040000 (upper 1/64) ```
It covers `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 `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 `--wp-*` option at a time). Make sure that hardware protection is off (state of `W#`/`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**: `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 `flashrom --dp-disable` to make sure that it fails now.