|
| 1 | +This document provides demonstration of how one can protect part of a flash |
| 2 | +chip from writing using `flashrom` and its support for manipulating SPI write |
| 3 | +protection (WP). This kind of protection requires changing connection of WP |
| 4 | +pin of a chip to prevent any attempt of disabling the protection by software |
| 5 | +alone. |
| 6 | + |
| 7 | +**Not to be confused** with protection by flash controller of your |
| 8 | +motherboard (PCH protection). |
| 9 | + |
| 10 | +## `flashrom` version |
| 11 | + |
| 12 | +At the time of writing (5 August 2022) there hasn't been a `flashrom` release |
| 13 | +that includes WP manipulation facilities. You might have to build one from |
| 14 | +scratch (assuming you've already installed build dependencies): |
| 15 | + |
| 16 | +``` |
| 17 | +git clone --depth 1 https://github.com/flashrom/flashrom |
| 18 | +cd flashrom |
| 19 | +make |
| 20 | +# flashrom executable will appear in current directory |
| 21 | +``` |
| 22 | + |
| 23 | +## Programmer support of WP |
| 24 | + |
| 25 | +Not all programmers support manipulating WP configuration. A suitable |
| 26 | +programmer must either provide a dedicated API for working with WP or give |
| 27 | +sufficiently comprehensive access to the interface of the flash chip. |
| 28 | + |
| 29 | +In particular, on Intel platforms internal programmer can allow only limited |
| 30 | +access to WP feature of chips or effectively deny it. Read "Intel chipsets" |
| 31 | +section of `flashrom`'s manpage for details on how you can try choosing |
| 32 | +sequencing type to possibly make WP work for you. |
| 33 | + |
| 34 | +In some cases external flashing might be the only option and you need to |
| 35 | +unscrew your device, find the chip, connect it to another device through a |
| 36 | +suitable adapter and finally be able to configure it as you wish. |
| 37 | + |
| 38 | +## Chip support by `flashrom` |
| 39 | + |
| 40 | +There is a great variety of chips with some not supporting write protection at |
| 41 | +all and others doing it in their own peculiar way of which `flashrom` has no |
| 42 | +idea. So the first thing to do is to make sure that `flashrom` knows how WP |
| 43 | +works for your combination of chip and chipset doesn't get in the way. Run a |
| 44 | +command like (adjust this and similar commands below if you're not using |
| 45 | +internal programmer or need other options): |
| 46 | + |
| 47 | +``` |
| 48 | +flashrom --programmer internal --wp-status |
| 49 | +``` |
| 50 | + |
| 51 | +Seeing this output line would mean that `flashrom` doesn't know how to use WP |
| 52 | +feature of the chip you have: |
| 53 | + |
| 54 | +``` |
| 55 | +Failed to get WP status: WP operations are not implemented for this chip |
| 56 | +``` |
| 57 | + |
| 58 | +Otherwise the output might contain something similar to this: |
| 59 | + |
| 60 | +``` |
| 61 | +Protection range: start=0x00000000 length=0x00000000 (none) |
| 62 | +Protection mode: disabled |
| 63 | +``` |
| 64 | + |
| 65 | +If so, you can continue with the rest of the instructions. |
| 66 | + |
| 67 | +## Collecting information about the range |
| 68 | + |
| 69 | +You need to know where the area you want to protect starts and ends. The example |
| 70 | +below will assume you're trying to protect bootblock stored in CBFS at the end |
| 71 | +of some coreboot firmware. In other cases it might be a separate file which is |
| 72 | +put at the beginning of a chip. You need to have an idea of what you're doing |
| 73 | +here or have some reliable instructions to follow. |
| 74 | + |
| 75 | +In this case `cbfstool` can be used to list information about bootblock like |
| 76 | +this: |
| 77 | + |
| 78 | +``` |
| 79 | +$ cbfstool rom print | sed -n '2p; /bootblock/p' |
| 80 | +Name Offset Type Size Comp |
| 81 | +bootblock 0x3ef100 bootblock 36544 none |
| 82 | +``` |
| 83 | + |
| 84 | +However, the offset is relative to the start of CBFS region, so we also need to |
| 85 | +find out offset of CBFS: |
| 86 | + |
| 87 | +``` |
| 88 | +$ cbfstool rom layout | grep CBFS |
| 89 | +'COREBOOT' (CBFS, size 4161536, offset 12615680) |
| 90 | +``` |
| 91 | + |
| 92 | +Now we can calculate: |
| 93 | + |
| 94 | + * start offset (CBFS offset + 64 + bootblock offset): \ |
| 95 | + `12615680 + 64 + 0x3ef100 = 0xff7140` \ |
| 96 | + (`printf "%#x\n" $(( 12615680 + 64 + 0x3ef100 ))`) |
| 97 | + * end offset (start offset + bootblock size - 1): \ |
| 98 | + `0xff7140 + 36544 - 1 = 0xffffff` \ |
| 99 | + (`printf "%#x\n" $(( 0xff7140 + 36544 - 1 ))`) |
| 100 | + |
| 101 | +Thus we need to write-protect the smallest area that covers the range from |
| 102 | +`0xff7140` to `0xffffff` (both bounds are inclusive). |
| 103 | + |
| 104 | +"64" in the computation of start offset is offset of booblock data. |
| 105 | +Unfortunately, current tooling doesn't provide a reliable way of determining |
| 106 | +actual offset, but 64 is the typical "extra offset" one needs to add to account |
| 107 | +for file metadata of CBFS (otherwise it can be its multiple 128 or bigger). |
| 108 | +Bootblock should normally end at the last byte of ROM on x86 systems, giving you |
| 109 | +a way to test the result. |
| 110 | + |
| 111 | +## Finding a matching range |
| 112 | + |
| 113 | +In most chips, the list of supported ranges is fixed and you can't specify an |
| 114 | +arbitrary one. Some others allow more fine-grained control, but that feature is |
| 115 | +not supported by `flashrom` as of now. |
| 116 | + |
| 117 | +Obtain list of supported ranges from which we'll pick the best match: |
| 118 | + |
| 119 | +``` |
| 120 | +$ flashrom --programmer internal --wp-list |
| 121 | +... |
| 122 | +Available protection ranges: |
| 123 | + start=0x00000000 length=0x00000000 (none) |
| 124 | + start=0x00000000 length=0x00001000 (lower 1/4096) |
| 125 | + start=0x00fff000 length=0x00001000 (upper 1/4096) |
| 126 | + start=0x00000000 length=0x00002000 (lower 1/2048) |
| 127 | + start=0x00ffe000 length=0x00002000 (upper 1/2048) |
| 128 | + start=0x00000000 length=0x00004000 (lower 1/1024) |
| 129 | + start=0x00ffc000 length=0x00004000 (upper 1/1024) |
| 130 | + start=0x00000000 length=0x00008000 (lower 1/512) |
| 131 | + start=0x00ff8000 length=0x00008000 (upper 1/512) |
| 132 | + start=0x00000000 length=0x00040000 (lower 1/64) |
| 133 | + start=0x00fc0000 length=0x00040000 (upper 1/64) |
| 134 | + start=0x00000000 length=0x00080000 (lower 1/32) |
| 135 | + start=0x00f80000 length=0x00080000 (upper 1/32) |
| 136 | + start=0x00000000 length=0x00100000 (lower 1/16) |
| 137 | + start=0x00f00000 length=0x00100000 (upper 1/16) |
| 138 | + start=0x00000000 length=0x00200000 (lower 1/8) |
| 139 | + start=0x00e00000 length=0x00200000 (upper 1/8) |
| 140 | + start=0x00000000 length=0x00400000 (lower 1/4) |
| 141 | + start=0x00c00000 length=0x00400000 (upper 1/4) |
| 142 | + start=0x00000000 length=0x00800000 (lower 1/2) |
| 143 | + start=0x00800000 length=0x00800000 (upper 1/2) |
| 144 | + start=0x00000000 length=0x00c00000 (lower 3/4) |
| 145 | + start=0x00400000 length=0x00c00000 (upper 3/4) |
| 146 | + start=0x00000000 length=0x00e00000 (lower 7/8) |
| 147 | + start=0x00200000 length=0x00e00000 (upper 7/8) |
| 148 | + start=0x00000000 length=0x00f00000 (lower 15/16) |
| 149 | + start=0x00100000 length=0x00f00000 (upper 15/16) |
| 150 | + start=0x00000000 length=0x00f80000 (lower 31/32) |
| 151 | + start=0x00080000 length=0x00f80000 (upper 31/32) |
| 152 | + start=0x00000000 length=0x00fc0000 (lower 63/64) |
| 153 | + start=0x00040000 length=0x00fc0000 (upper 63/64) |
| 154 | + start=0x00000000 length=0x00ff8000 (lower 511/512) |
| 155 | + start=0x00008000 length=0x00ff8000 (upper 511/512) |
| 156 | + start=0x00000000 length=0x00ffc000 (lower 1023/1024) |
| 157 | + start=0x00004000 length=0x00ffc000 (upper 1023/1024) |
| 158 | + start=0x00000000 length=0x00ffe000 (lower 2047/2048) |
| 159 | + start=0x00002000 length=0x00ffe000 (upper 2047/2048) |
| 160 | + start=0x00000000 length=0x00fff000 (lower 4095/4096) |
| 161 | + start=0x00001000 length=0x00fff000 (upper 4095/4096) |
| 162 | + start=0x00000000 length=0x01000000 (all) |
| 163 | +``` |
| 164 | + |
| 165 | +Pick a range by scanning the list in the top down order (because the smaller |
| 166 | +ranges come first): |
| 167 | + |
| 168 | + - if bootblock is at the start of a chip, look for the first lower range whose |
| 169 | + length is greater than the end offset |
| 170 | + - if bootblock is at the end of a chip, look for the first upper range which |
| 171 | + starts before the start offset |
| 172 | + - mind that you're unlikely to find an ideal match and will probably protect |
| 173 | + more than you need; this is fine if that's just an empty space, but can |
| 174 | + cause troubles with future updates if that's some data or metadata which |
| 175 | + changes with every release |
| 176 | + |
| 177 | +This is the first upper range starting before `0xff7140`: |
| 178 | + |
| 179 | +``` |
| 180 | + start=0x00fc0000 length=0x00040000 (upper 1/64) |
| 181 | +``` |
| 182 | + |
| 183 | +It covers `0x00fc0000 -- 0x00ffffff` which includes our bootblock. This area |
| 184 | +takes up 256 KiB, about 7 times bigger than our bootblock, but there is no better |
| 185 | +choice in this case and output of `cbfstool rom layout` shows that we |
| 186 | +additionally include a part of 876 KiB empty space which will hopefully remain |
| 187 | +there in future firmware versions (it's a good idea to check before a firmware |
| 188 | +update). |
| 189 | + |
| 190 | +## Protection setup |
| 191 | + |
| 192 | +The following command sets the range and enables WP at the same time, the values |
| 193 | +are taken from the chosen range above: |
| 194 | + |
| 195 | +``` |
| 196 | +flashrom --programmer internal --wp-range=0x00fc0000,0x00040000 --wp-enable |
| 197 | +``` |
| 198 | + |
| 199 | +You can set the range and change WP status independently as well if needed |
| 200 | +(just specify one `--wp-*` option at a time). No need to disable protection for |
| 201 | +changing the range (via `--wp-disable`), just make sure that hardware |
| 202 | +protection is off (state of `W#`/`W/` pin of the chip). |
| 203 | + |
| 204 | +On success, the output of the above command will include such lines: |
| 205 | + |
| 206 | +``` |
| 207 | +Enabled hardware protection |
| 208 | +Activated protection range: start=0x00fc0000 length=0x00040000 (upper 1/64) |
| 209 | +``` |
| 210 | + |
| 211 | +**Caveat**: `flashrom` automatically tries to disable WP before any operation |
| 212 | +on a chip (read, write, erase, verify), so double-check status of WP before |
| 213 | +changing state of WP pin on your chip! |
| 214 | + |
| 215 | +## Verifying hardware protection |
| 216 | + |
| 217 | +Once you've happy with the configuration and changed state of WP pin, you can |
| 218 | +try disabling WP using `flashrom` to make sure that it the operation now fails. |
0 commit comments