Hi,
This is an RFC.
I'm currently working towards providing a more secure fallback mechanism to Coreboot. I had pushed some preliminary changes to Gerrit some weeks ago¹, and I've tried to take the reviews in account. As the changes touch some pretty critical parts of Coreboot, I'm sending this to the mailing list for comments. The patches are not ready yet, and the changes that have been made to CBFStool and TINY_BOOTBLOCK et al. recently will prolly make the rebasing work non-trivial.
On some chips, it is possible to block writing on some part of the ROM when the system is running. We (at my company) plan to use that to prevent a careless user updating or modifying their BIOS to brick their system, by putting a 'fallback' Coreboot in the high, write-blocked part of the boot ROM, and using the fallback mechanism already implemented in Coreboot in order to fallback in case the user-flashed firmware does not work.
This requires an ability to specify that the components of a Coreboot build have to be written in the high part of the ROM. As cbfstool already supports setting the precise address at which a file must be mapped in memory, this could be implemented completely in the build system. However, this is non-trivial, so I really think it is better for cbfstool to handle the calculations.
However, if the user writes in the low part of the ROM a Coreboot build with the "fallback" CBFS prefix (which is the default), the fallback mechanism won't work, as it will always boot on the first component found with the right name. That's why the fallback mechanism has to search for the fallback image only in the high part of the RAM. That requires modification of walkcbfs_asm and of cbfslib to be able to find a file after an offset.
In order to do this, we add a build option named OFFSET_IN_ROM which defaults to 0x0 and enables putting Coreboot in the high part of the ROM. OFFSET_IN_ROM is used in the build system to call cbfstool (including in order to call link the romstage properly), and is used in Coreboot itself as an argument to the CBFS search functions.
Note that in order to write a component after an offset in cbfstool, the whole beginning of the ROM has to be walked through in order not to overwrite part of another file. On the contrary, when looking for a fallback component, the file headers before the fallback offset should not be trusted (that's the whole point), so the beginning of the ROM should be entirely skipped.
It is to be noted that this mechanism is still imperfect, as the CBFS header is at the bottom of the ROM and holds info about file alignment and offset. In order to prevent problems, the implementation should use conservative values instead of those written in the CBFS header.
I'm preparing a changeset implementing this mechanism. I should post it to gerrit soon if the reactions to this RFC are positive.
Later, we could implement a more robust solution would be storing another CBFS header in the middle of the ROM; either completely splitting the ROM in two or storing the second CBFS header as a CBFS file. In both cases, that would require much more effort to implement than the current proposal.
Any idea/objection?
Thanks in advance.
¹: http://review.coreboot.org/284 http://review.coreboot.org/285 http://review.coreboot.org/286
Hello Noé,
first, welcome to the coreboot community!
Am 15.11.2011 12:07, schrieb Noé Rubinstein:
system, by putting a 'fallback' Coreboot in the high, write-blocked part of the boot ROM, and using the fallback mechanism already implemented in Coreboot in order to fallback in case the user-flashed firmware does not work.
Why put the fallback in the high parts? The only reason I could find is that you intend to use a boot block protection scheme (as these provide protection only for some high region), but they usually cover only a rather small area - too small for coreboot.
found with the right name. That's why the fallback mechanism has to search for the fallback image only in the high part of the RAM. That requires modification of walkcbfs_asm and of cbfslib to be able to find a file after an offset. [...] overwrite part of another file. On the contrary, when looking for a fallback component, the file headers before the fallback offset should not be trusted (that's the whole point), so the beginning of the ROM should be entirely skipped.
These two special cases can be dropped if fallback is aligned to the low end of the image: It's always encountered first, and the CBFS alignment data is protected, too.
I'm snipping away your other proposals for now. The reason is that there are various projects out there that use coreboot and have safe updates on their agenda, and I'd rather have a complete set of constraints (eg. limitations due to flash chips that only provide boot block protection) before planning what to do about them.
Patrick
Hi,
Following up about Noé RFC, who I work with:
On Tue, 15 Nov 2011 21:43:47 +0100 Patrick Georgi patrick@georgi-clan.de wrote:
system, by putting a 'fallback' Coreboot in the high, write-blocked part of the boot ROM, and using the fallback mechanism already implemented in Coreboot in order to fallback in case the user-flashed firmware does not work.
Why put the fallback in the high parts? The only reason I could find is that you intend to use a boot block protection scheme (as these provide protection only for some high region), but they usually cover only a rather small area - too small for coreboot.
The upper area that we plan to store the safe code in would actually be the upper half of the flash, which can be soft-locked RO. That is big enough to contain a coreboot and even more.
found with the right name. That's why the fallback mechanism has to search for the fallback image only in the high part of the RAM. That requires modification of walkcbfs_asm and of cbfslib to be able to find a file after an offset. [...] overwrite part of another file. On the contrary, when looking for a fallback component, the file headers before the fallback offset should not be trusted (that's the whole point), so the beginning of the ROM should be entirely skipped.
These two special cases can be dropped if fallback is aligned to the low end of the image: It's always encountered first, and the CBFS alignment data is protected, too.
We can't put the fallback at the beginning of the image because that area can't be locked on the chip we are using. (SST25VF016B)
I'm snipping away your other proposals for now. The reason is that there are various projects out there that use coreboot and have safe updates on their agenda, and I'd rather have a complete set of constraints (eg. limitations due to flash chips that only provide boot block protection) before planning what to do about them.
On our side, the essential requirement is that the fallback can be put in an upper area of the chip, because the lower area can't be locked (The SST25VF016B can lock its upper 1/32, 1/16, 1/8, 1/4 or 1/2 -- we were thinking about locking the whole upper half, maybe to switch at a late stage of FW development to something just large enough to fit what we need.)
Our idea was that the owner should be allowed to write whatever CBFS image he wants in the unlocked area of the chip, without the need to first extract parameters from a protected header in his target board before using the same to build his own image, and without any impact on the behavior of the fallback image.
So I think it would be better to have two clearly separated areas, maybe each one with its own header. If using only one header is mandatory, then it clearly must be in the protected area, which might not be possible to put at the bottom of the chip on some refs (like the SST25VF016B we are using).
It definitely would be great to come with a general solution that works for everybody and is maintained for a long time in a compatible way in coreboot, because obviously we won't be able to change the structure easily in the field once they have been bumped in protected areas of deployed products, and we also would prefer not to maintain a patch set specific to our board on this subject forever... (we could put a jumper to disable the write protection, but because that could defeat the whole purpose of the protected fallback if misused, we are not yet sure, and we might end up with just an ISP header for those who really want to change the protected area).
Cheers! Guillaume Knispel
Am 15.11.2011 23:42, schrieb Guillaume Knispel:
We can't put the fallback at the beginning of the image because that area can't be locked on the chip we are using. (SST25VF016B)
Thanks! That was the missing part in your scenario. Most current chips support sector granularity (usually 4kb, sometimes rather weird combinations of different sizes).
Current idea under consideration: Modify CBFS to work from top to bottom. That way, you could place all immutable data at the beginning of the chain (ie. top-most). That way all kind of headers, as well as fallback-versions of code, can be in the protected area. If updates fail, some mechanism (counter in nvram, jumper, ...) can be used to tell the bootblock to use the fallback version.
Of course, this is a rather severe change, but actually not that hard in implementation: a couple of additions replaced by subtraction, and different exit conditions. As it also affects payload compatibility (libpayload and seabios at least), it requires some thought.
The only option I see to lock down your system (given that chip) that won't change the format will introduce a much more complex CBFS evaluator: In the easiest case, it would walk the entire flash and return the _last_ occurrence of the requested file name. As it can't trust any file headers, it would have to look for headers in 16byte increments. That's not really a useful solution. ;-)
Regards, Patrick
On Wed, 16 Nov 2011 21:50:59 +0100 Patrick Georgi patrick@georgi-clan.de wrote:
Am 15.11.2011 23:42, schrieb Guillaume Knispel:
We can't put the fallback at the beginning of the image because that area can't be locked on the chip we are using. (SST25VF016B)
Thanks! That was the missing part in your scenario. Most current chips support sector granularity (usually 4kb, sometimes rather weird combinations of different sizes).
Current idea under consideration: Modify CBFS to work from top to bottom. That way, you could place all immutable data at the beginning of the chain (ie. top-most). That way all kind of headers, as well as fallback-versions of code, can be in the protected area. If updates fail, some mechanism (counter in nvram, jumper, ...) can be used to tell the bootblock to use the fallback version.
I think the most useful thing for the long term would be to come up with data structures that allow use on all currently know chips, so in the future every tool will be standard and there won't be a sort of maintenance hell like "card X requires option Y during build of soft Z then don't forget to use the following offsets and order when building the cbfs image, and oh BTW here is a patch you should apply to your toolchain first -- and remember to configure T exactly the same way or they will be incompatible". It would also be more approachable for users who want to modify their firmware if we avoid that mess.
Of course, this is a rather severe change, but actually not that hard in implementation: a couple of additions replaced by subtraction, and different exit conditions. As it also affects payload compatibility (libpayload and seabios at least), it requires some thought.
I think if we are going to modify payload compatibility, the right way is to aim at a generic solution that would work well on all kind of chips.
The advantage of having two clearly separated CBFS, one for the protected area and one for the rest is that it would be clean (no hack, no need to scan the whole chip, etc.), and would work with both chips protected in the lower and chips protected in the upper area. Also I think the CBFS structures would not need to be modified if there are only contiguous areas. We maybe even could manage to keep backward compatibility with older bootblocks (but they would not provide the same level of protection) and tools?
Cheers, Guillaume Knispel
On Tue, Nov 15, 2011 at 7:07 PM, Noé Rubinstein nrubinstein@proformatique.com wrote:
Hi,
This is an RFC.
I'm currently working towards providing a more secure fallback mechanism to Coreboot. I had pushed some preliminary changes to Gerrit some weeks ago¹, and I've tried to take the reviews in account. As the changes touch some pretty critical parts of Coreboot, I'm sending this to the mailing list for comments. The patches are not ready yet, and the changes that have been made to CBFStool and TINY_BOOTBLOCK et al. recently will prolly make the rebasing work non-trivial.
On some chips, it is possible to block writing on some part of the ROM when the system is running. We (at my company) plan to use that to prevent a careless user updating or modifying their BIOS to brick their system, by putting a 'fallback' Coreboot in the high, write-blocked part of the boot ROM, and using the fallback mechanism already implemented in Coreboot in order to fallback in case the user-flashed firmware does not work.
This requires an ability to specify that the components of a Coreboot build have to be written in the high part of the ROM. As cbfstool already supports setting the precise address at which a file must be mapped in memory, this could be implemented completely in the build system. However, this is non-trivial, so I really think it is better for cbfstool to handle the calculations.
However, if the user writes in the low part of the ROM a Coreboot build with the "fallback" CBFS prefix (which is the default), the fallback mechanism won't work, as it will always boot on the first component found with the right name. That's why the fallback mechanism has to search for the fallback image only in the high part of the RAM. That requires modification of walkcbfs_asm and of cbfslib to be able to find a file after an offset.
In order to do this, we add a build option named OFFSET_IN_ROM which defaults to 0x0 and enables putting Coreboot in the high part of the ROM. OFFSET_IN_ROM is used in the build system to call cbfstool (including in order to call link the romstage properly), and is used in Coreboot itself as an argument to the CBFS search functions.
This method looks like the same as what I have done when dealing with different ARM boot rom address mapping. The different is that I also put this address into the master header so that other component or walk_cbfs can get it there.
Note that in order to write a component after an offset in cbfstool, the whole beginning of the ROM has to be walked through in order not to overwrite part of another file. On the contrary, when looking for a fallback component, the file headers before the fallback offset should not be trusted (that's the whole point), so the beginning of the ROM should be entirely skipped.
It is to be noted that this mechanism is still imperfect, as the CBFS header is at the bottom of the ROM and holds info about file alignment and offset. In order to prevent problems, the implementation should use conservative values instead of those written in the CBFS header.
I'm preparing a changeset implementing this mechanism. I should post it to gerrit soon if the reactions to this RFC are positive.
Later, we could implement a more robust solution would be storing another CBFS header in the middle of the ROM; either completely splitting the ROM in two or storing the second CBFS header as a CBFS file. In both cases, that would require much more effort to implement than the current proposal.
Any idea/objection?
Thanks in advance.
¹: http://review.coreboot.org/284 http://review.coreboot.org/285 http://review.coreboot.org/286 -- Noé Rubinstein Avencall - XiVO IPBX Open Hardware 10 bis, rue Lucien VOILIN - 92800 Puteaux
-- coreboot mailing list: coreboot@coreboot.org http://www.coreboot.org/mailman/listinfo/coreboot