Hacker News new | ask | show | jobs
by pbrowne011 482 days ago
Very interesting. I wonder if this a result of some "swiss cheese" effect due to constraints around UEFI and NVRAM themselves, when updating EFI variables.

NVRAM must maintain atomicity of memory transactions for power failures. Its whole purpose is to store data when you turn your computer off. As a result, when deleteing an EFI variable, you can't manage the individual bytes - you have to delete a whole entry (which can be rather large - based on the EFI specification and the code used for edk2, e.g. https://github.com/tianocore/edk2/blob/83a86f465ccb1a792f5c5...). Deleting these entries might become a problem when you start running against memory constraints and what slots in memory are actually available; hence a possible fragmentation issue.

Additionally, I appreciated how short and specific this blog post was. I enjoy this style of post of someone encountering a problem and solving it.

2 comments

There's also the fact that an NVRAM variable is never overwritten inplace; the new value is written elsewhere, and the pointer is updated to use the address of the new value. This is probably mainly for wear-leveling, but I guess it could also introduce fragmentation?

Just an observation from when I was debugging a board that selfdestructed when booting a particular efi-file so I had to dig into the flash contents to figure out why, but I think this particular code was straight from tianocore.

Probably for atomicity. It’s likely only a pointer sized block can be updated atomically so in order to safely update the value that may be larger you write it somewhere else and atomically update the pointer. That way you can only observe the old or new value, and not some intermediate result if power was lost part way through writing the new value. The same techniques are used in journaling file systems.
I'm curious how the NVRAM is actually stored. In embedded systems on microcontrollers the constraints of the HW make you do what we called "Emulated EEPROM". You are able to erase data in increments of, for example 4kb and write it in increments of 16 bytes (but it varies with implementation). So on write you just say... this is block foo and it stores value bar and you append it to the latest not written data in the block. When you recover data, you just look for the latest valid value of foo and say "the value of foo is bar". You might have multiple instances of foo written, but only the latest is the valid one. Once the active block is full, you swap out all the current values of all the NvM blocks to the next erasable HW block.

Yes, this achieves atomicity, yes this gets you wear leveling (with the caveat that the more data you store, the worse the lifetime gets because you need to do more swaps) but it also is a consequence of HW constraints and the approach flows directly from it. It might be the consequence of HW/SW co-design at some point in the past as well, but I have no idea whether this is true.

This information is based on my experience in automotive.

Automotive did 'roll your own' flash handling since almost forever...

I have a toyota where the odometer resets back to 299,995 miles on every power cycle because whoever designed the wear levelling didn't plan that far ahead...

True, I was trying to find the variable storage requirements in the UEFI specification but couldn't (is it Section 3? 8?), so I resorted to linking to the struct definition in the EFI shell package that the author used.
I imagine it's not fragmentation in the strictest sense. It's more than likely it's just the result of a bug. Perhaps garbage collection wasn't being triggered and space wasn't getting freed up. It could be that the author caused the problem themselves by how they were managing their nvram.