Tow-Boot icon indicating copy to clipboard operation
Tow-Boot copied to clipboard

Dual eMMC boot support

Open oliv3r opened this issue 3 years ago • 2 comments

In issue #76 the desire to add eMMC boot support was brought up; however the current implementation is a little lacking.

the advantage of the eMMC boot partition, is that it comes in two, for redundancy purposes. Allowing for an update of it, without affecting the running one.

The behavior of which partition is being used, can be controlled in two ways. One that requires special support in the host (usually the BootROM) to either access the proper partitions, or to stream data from an eMMC. The later being interesting as it can significantly improve performance. For U-Boot however, this is not that interesting, the SPL is small enough that we'd be shaving off a few ms at best from the command overhead. The former, well requires special BootROM support which is not actually even needed.

The other way this can be controlled, is through the boot partition register (EXT_CSD 179). In this register, you tell the eMMC chip, to return data from the boot0 (or boot1, or user partition) to return data when the host issues a read command. This means no modification is needed to make use of the boot partition. It does require the eMMC chip to be set up appropriately using U-Boot's partconf.

The bootbus configuration is needed for performance reasons only.

As all this highly depends on the BootROM, eMMC chip and board layout, this can only be done via configuration variables.

Further more, all of this is useless without having U-Boot in two partitions, so this patch also ensures that U-Boot is actually installed in both partitions. The 'flipping' switching can be useful in various ways. the upgrade script first writes + verifies the second partition, then the first, and leverages partconf (or the mmc utils from Linux) to do so. Resulting in validated writes, never bricks.

One optimization that could be done as part of this commit also, is to actually make partconf use mandatory, and then remove the whole 'head/tail' mechanism, as it becomes useless. E.g. always write the in-active partition.

Due to lack of time, I haven't been able to quite test this well. But this methodology I have reliably used for more then 2 years in the field. For some supporting linux scriptage; see my U-Boot update script: https://gitlab.com/esbs/updater/-/blob/master/scripts/update_u-boot.sh#L411

oliv3r avatar Aug 19 '22 14:08 oliv3r

Hi! :wave: Thanks for the contribution.

Good looking commits here. The descriptive commit messages are great.

I will preface this comment saying that my experience with mmc boot partitions is quite superficial.

I will defer to your knowledge/expertise in this situation, if you can help.

Your thoughts, PR description and commit messages were crystal clear.

Beware, my following thoughts are a bit messy.


First, I have had weird observations on Amlogic platforms. I don't know if you have had the chance to observe the same.

As far as I tested, their BootROM will not respect the boot partition register (what is configured via partconf), and always try in order [mmc×boot0, mmc×boot1, mmc×]. Though I'm not 100% positive what happens when a valid platform firmware (e.g. U-Boot) is present on both and the boot partition is set to the second one.

I probably should have documented it better, but this kind of situation makes it awkward to harden in an A/B scheme, if I have correctly understood the underlying problem. And since I only have had experience with two platforms (allwinner, amlogic), I preferred leaving the "dual partition" question as an open question.

Note that for platforms that require usage of the register, like allwinner, device-specific instructions are used:

  • https://github.com/Tow-Boot/Tow-Boot/blob/51c7799fc767352ce2bf46b12e4630ae055c10f3/modules/hardware/allwinner/default.nix#L79-L82

Another area I lack knowledge about: what are the known "limits" of mmcboot... I don't remember being able to find something like a specification that states how big or how small they can be, or even should be.

Since it is desirable to save the environment for Tow-Boot, I was considering reserving one of the two mmcboot partitions for environment storage and "misc use", if storage size can get a bit too constrained. Though that would only be done once I know more about those limitations.


As far as this PR goes, can you share how you have tested it? I really need to remember to add a PR template asking the contributor to tell "what they have done, how the changes were tested" :).

I'm assuming, without malice, that it was not built, as this is now referring to new undeclared options. That is understandable given how hush's (U-Boot's shell) variable expansion can clash with Nix strings variable interpolation. No worries, we can work out the details, the intention is clearly explained.

samueldr avatar Aug 19 '22 17:08 samueldr

Hi! wave Thanks for the contribution. No problem :)

Good looking commits here. The descriptive commit messages are great. Thanks :)

I will preface this comment saying that my experience with mmc boot partitions is quite superficial.

I will defer to your knowledge/expertise in this situation, if you can help. Yeah sure thing

Your thoughts, PR description and commit messages were crystal clear.

Beware, my following thoughts are a bit messy.

First, I have had weird observations on Amlogic platforms. I don't know if you have had the chance to observe the same. I have no Amlogic devices; lots of Allwinner ones in a box somewhere though :)

As far as I tested, their BootROM will not respect the boot partition register (what is configured via partconf), and always try in order [mmc×boot0, mmc×boot1, mmc×]. Though I'm not 100% positive what happens when a valid platform firmware (e.g. U-Boot) is present on both and the boot partition is set to the second one. Afaik, it doesn't have to, as long as it doesn't actually try to mess with it. AFAIK, the iMX6 also doesn't know nor care about the partition_configuration register.

As I mentioned however, either the bootrom does nothing and doesn't care and just tries to read from an eMMC device (using CMD25 if I'm not mistaken). If that's the case, partconf and the 'partition access' (last field) is important.

But broken ROM's are broken :) So what AMLogic does, in their mindfullness, is 'hwpart 1; read; hwpart 2; read; hwart 7; read (I've not looked at the BROM though. But this is typical of ignorant rushed engineering. "I don't know how it works, I don't know what else is out there, but trying all 3 partitions seems sensible.

To be fair, some parts of the partconf register are optional, so this approach (while costly in development/testing/ROM size) is at least guaranteed to mostly always work, while loosing configuration. If I where to engineer this, I'd first check the partconf register :p

Having said that, the feature is fully optional; If these variables are not setup in the configuration, this behavior should be ignored (for partconf/busconf). Writing U-Boot two both partitions, is a choice however. We could put that into configuration too; but that'll be a bit messier? (store the string "1 2" or "2 1" as a variable? store it as 'first/second/start/finish?

I probably should have documented it better, but this kind of situation makes it awkward to harden in an A/B scheme, if I have correctly understood the underlying problem. And since I only have had experience with two platforms (allwinner, amlogic), I preferred leaving the "dual partition" question as an open question. For high-reliability systems, this is crucial; for simple 'desktop-usage', meh, who cares, just reflash it. We have millions of units in the field, in that case, it's not a matter 'should we', but 'we must'. Because it's not a matter of 'if' but 'when'.

So you can use this feature (as user/distro) in one of two ways. Ensure both are always the same, so that if one fails, you don't immediatly have a brick; or 'hey I just upgraded U-Boot, but it's acting iffy, i'll switch back to the old one (using partconf if possible, which for AMLogic doesn't seem to be the case, so you only have the first option anyway).

Note that for platforms that require usage of the register, like allwinner, device-specific instructions are used:

* https://github.com/Tow-Boot/Tow-Boot/blob/51c7799fc767352ce2bf46b12e4630ae055c10f3/modules/hardware/allwinner/default.nix#L79-L82

So that could be removed by removing that into the configuration file :)

'require' being strong here, it only 'requires' it if you insist on using eMMC boot (which I would argue is a important). I don't know what the A64 (or even the earlier SoC's actually) exactly do. I think A64 has support for the eMMC 'stream' mode (partconf last argument). The earlier allwinner SoC's probably don't support this (it's been 8 years since I [looked at that stufff]](https://github.com/hno/Allwinner-Info). In that case, you should be able to make it work on A10+ SoC's.

But, with this new config, you can drop that specific installer section, and just rely on the default from the installer :)

I can dig up some boards and try it out, but that won't be in days time, but weeks :p

Another area I lack knowledge about: what are the known "limits" of mmcboot... I don't remember being able to find something like a specification that states how big or how small they can be, or even should be. mmcboot partitions? Vendor specific. The smallest being obviously a single erase page. I've seen 128k I think; but generally vendors want 'as small as possible (to not waste space)' while still being usefull. 2MiB is very common though. I have seen 16MiB too (the spec allows for 32MiB). There is also a register that allows you to reconfigure the boot partition, but can't remember if it is optional or mandatory to support this. An older eMMC spec might reveal the answer though :)

There are two partition regions. The minimum size of each boot partition is 128KB. Boot partition size is
calculated as follows:
Maximum boot partition size = 128K byte x BOOT_SIZE_MULT
BOOT_SIZE_MULT: the value in Extended CSD register byte [226]

found it quicker then I thought :p The spec says it's read-only, but I know I've seen it as re-writeable in the form of a fuse at the least. Might be vendor specific and use a different command, but dig through the pdf, it should be in there. The PDF is of eMMC spec 4.3; I think the most important one (it's quite old, I think even A10 supports 4.3) and introduced all the fun stuff for the most part. All chips in use should really support everything that's in there.

Since it is desirable to save the environment for Tow-Boot, I was considering reserving one of the two mmcboot partitions for environment storage and "misc use", if storage size can get a bit too constrained. Though that would only be done once I know more about those limitations. What I did, reserve the trailing 2k? or so of the eMMC boot partition for the environment; and the head for U-Boot. E.g. /dev/mmcblk0boot0 -0x20000 0x20000

One thing I still have on my really long todo list; is support 'redundancy' of the environment over the two partitions. U-Boot does have redundancy support; but only on the same device. I still write the env to both partitions though, and U-Boot does read it out: https://gitlab.com/evbox/open-source/u-boot/-/blob/evbox/v2020.10/include/configs/evbox-idc.h#L147 (also see the altbootcmd for fun)

I still intend to upstream all of those patches in that repo (time ... ugh)

As far as this PR goes, can you share how you have tested it? I really need to remember to add a PR template asking the contributor to tell "what they have done, how the changes were tested" :). Not well enough :p as I basically yanked it from an installer that I did ages ago.

I'm assuming, without malice, that it was not built, as this is now referring to new undeclared options. That is understandable given how hush's (U-Boot's shell) variable expansion can clash with Nix strings variable interpolation. No worries, we can work out the details, the intention is clearly explained. Yeah, I'm new to nix, so not sure how that all mixes and matches. Personally, I'm not a fan of 'lang-in-lang' solutions. GitLab pipelines are horrible in that regard too. Shell in Yaml. No way to easily lint it, no way to easily write it, often get expansion issues left/right. For my own stuff, I prefer keeping templates ".in" files, and do some sed magic in replacing magic markers. That keeps the source easily readable (the template), lintable etc etc.

Having said that, I have no idea how Nix deals with 'i want this var, but it's optional'. In shell/hush even, this be easiy ("${bla:-false_default}" but you'd probably need to somehow escape the nix parser ... ugh, lang-in-lang :S

oliv3r avatar Aug 20 '22 06:08 oliv3r