mirror of
https://review.coreboot.org/flashrom.git
synced 2025-04-27 07:02:34 +02:00

Change-Id: If6d3580972ea7d42503004b922b90829025bd62d Signed-off-by: Anastasia Klimchuk <aklm@flashrom.org> Reviewed-on: https://review.coreboot.org/c/flashrom/+/83942 Tested-by: build bot (Jenkins) <no-reply@coreboot.org> Reviewed-by: Peter Marheine <pmarheine@chromium.org>
192 lines
9.3 KiB
ReStructuredText
192 lines
9.3 KiB
ReStructuredText
===================================
|
|
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 doc describe how to do this: :doc:`/dev_guide/building_from_source`.
|
|
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.
|