UEFI booting survey [and compared ubldr.bin booting behavior]

Mark Millard markmi at dsl-only.net
Sat Dec 23 22:49:15 UTC 2017

[Just adding notes about what ubldr.bin does
with and without loaderdev being specified
(correctly). Overall it does not appear to
me that the ubldr.bin based contexts can
fully avoid the "systems with multiple
partitions that might be right" issue.]

On 2017-Dec-20, at 2:47 AM, Mark Millard <markmi at dsl-only.net> wrote:

> On 2017-Dec-19, at 9:10 PM, Warner Losh <imp at bsdimp.com> wrote:
>> On Tue, Dec 19, 2017 at 9:06 PM, Mark Millard <markmi at dsl-only.net> wrote:
>> [The usdcard in my rpi2 example does not contain any kernel files,
>> nor any dtb files. Only the USB SSD stick does. The kernel and dtb
>> do not come from the usdcard. This is what I'm not sure if it is
>> generally supported or not.]
>> On 2017-Dec-19, at 7:26 PM, Warner Losh <imp at bsdimp.com> wrote:
>>> On Dec 19, 2017 4:26 PM, "Mark Millard" <markmi at dsl-only.net> wrote:
>>>>> . . .
>>>>> It sounds different then the results I get with ubldr.bin
>>>>> on a rpi2 V1.1 . With the usdcard having a UFS / with
>>>>> basically only:
>>>>> /etc/fstab (redirecting to a USB SSD stick)
>>>>> /boot/* (with /boot/kernel/ empty and /boot/dtb/ empty)
>>>>> the result is that all 3 of the following come from the
>>>>> USB SSD stick based on the "/" line from the /etc/fstab
>>>>> from the usdcard:
>>>>> /boot/kernel
>>>>> /boot/dtb/bcm2836-rpi-2-b.dtb
>>>>> / (mounted root file system)
>>>>> In other words: it appears that for ubldr.bin on
>>>>> a rpi2 V1.1 /etc/fstab is read and used before
>>>>> finding the kernel that is to be loaded. (It or
>>>>> another /etc/fstab may be read again later.)
>>>>> It is read. It is literally only used to set the root for userland. That is all. Nothing else.
>>>>> /usr/src/stand/common/boot.c does show an explicit
>>>>> attempt to find a /etc/fstab:
>>>>> # grep -r /etc/fstab /usr/src/stand/
>>>>> . . .
>>>>> /usr/src/stand/common/boot.c: * Try to find the /etc/fstab file on the filesystem (rootdev),
>>>>> /usr/src/stand/common/boot.c:    sprintf(lbuf, "%s/etc/fstab", rootdev);
>>>>> . . .
>>>>> That is from getrootmount(char *rootdev):
>>>>> int
>>>>> getrootmount(char *rootdev)
>>>>> {
>>>>>    char        lbuf[128], *cp, *ep, *dev, *fstyp, *options;
>>>>>    int         fd, error;
>>>>>    if (getenv("vfs.root.mountfrom") != NULL)
>>>>>        return(0);
>>>>> So if you set vfs.root.mountfrom in /boot/loader.conf, we don't read rootdev:/etc/fstab for the value to set vfs.root.mountfrom to.
>>>>>    error = 1;
>>>>>    sprintf(lbuf, "%s/etc/fstab", rootdev);
>>>>>    if ((fd = open(lbuf, O_RDONLY)) < 0)
>>>>>        goto notfound;
>>>>> . . .
>>>>> Supporting detail for the example rpi2
>>>>> boot context:
>>>>> With /mnt being the / from the usdcard:
>>>>> # find /mnt -print | more
>>>>> /mnt
>>>>> /mnt/.snap
>>>>> /mnt/boot
>>>>> /mnt/boot/defaults
>>>>> /mnt/boot/defaults/loader.conf
>>>>> /mnt/boot/dtb
>>>>> /mnt/boot/firmware
>>>>> /mnt/boot/kernel
>>>>> /mnt/boot/modules
>>>>> /mnt/boot/zfs
>>>>> /mnt/boot/msdos
>>>>> /mnt/boot/entropy
>>>>> /mnt/boot/menu.rc.sample
>>>>> /mnt/boot/ubldr
>>>>> /mnt/boot/ubldr.bin
>>>>> /mnt/boot/brand-fbsd.4th
>>>>> /mnt/boot/logo-beastie.4th
>>>>> /mnt/boot/logo-beastiebw.4th
>>>>> /mnt/boot/logo-fbsdbw.4th
>>>>> /mnt/boot/logo-orb.4th
>>>>> /mnt/boot/logo-orbbw.4th
>>>>> /mnt/boot/loader.conf
>>>>> /mnt/boot/loader.efi
>>>>> /mnt/boot/boot1.efi
>>>>> /mnt/boot/boot1.efifat
>>>>> /mnt/boot/beastie.4th
>>>>> /mnt/boot/brand.4th
>>>>> /mnt/boot/color.4th
>>>>> /mnt/boot/check-password.4th
>>>>> /mnt/boot/delay.4th
>>>>> /mnt/boot/frames.4th
>>>>> /mnt/boot/loader.4th
>>>>> /mnt/boot/loader.help
>>>>> /mnt/boot/menu.4th
>>>>> /mnt/boot/menu-commands.4th
>>>>> /mnt/boot/menusets.4th
>>>>> /mnt/boot/screen.4th
>>>>> /mnt/boot/shortcuts.4th
>>>>> /mnt/boot/support.4th
>>>>> /mnt/boot/version.4th
>>>>> /mnt/boot/loader.rc
>>>>> /mnt/boot/efi.4th
>>>>> /mnt/boot/pcibios.4th
>>>>> /mnt/boot/menu.rc
>>>>> /mnt/etc
>>>>> /mnt/etc/fstab
>>>>> /mnt/COPYRIGHT
>>>>> /mnt/lost+found
>>>>> # more /mnt/etc/fstab
>>>>> /dev/da0p1      /               ufs     rw,noatime      1 1
>>>>> /dev/da0p2      none            swap    sw              0 0
>>>>> May be this is somehow special to the rpi2 or to
>>>>> ubldr.bin operation. (I've never managed to identify
>>>>> accidents from deliberately working status in this
>>>>> area.)
>>>> So you load /boot/loader and the kernel from the sdcard.
>>> No: There are no kernel files on the usdcard. See the complete
>>> file list of its only UFS partition above. No dtb files either.
>>> [On a rpi2 ubldr.bin is copied to the msdosfs, where it
>>> is actually put to use.]
>> OK. Looks like the uboot code has the same bogus 'let's search everything' code that I'm removing from the UEFI case.
> Could be. My case was so limited that it does not show if
> the search would continue if multiple USB drives were
> present vs. stopping at the first even if the files would
> not be found there (for example).
>> Still doesn't get anything from /etc/fstab. It can't. There's no translation code in the boot loader to try to guess. It just does a bruit force plow through all the devices and hopes for the best.
> Just FYI, the /usr/src/stand/common/boot.c comments say about
> the /etc/fstab use:
> * Try to find the /etc/fstab file on the filesystem (rootdev),
> * which should be be the root filesystem, and parse it to find
> * out what the kernel ought to think the root filesystem is.
> *
> * If we're successful, set vfs.root.mountfrom to <vfstype>:<path>
> * so that the kernel can tell both which VFS and which node to use
> * to mount the device.  If this variable's already set, don't
> * overwrite it.
> . . .
>        /* Build the <fstype>:<device> and save it in vfs.root.mountfrom */
> This would fit with your description: finding any
> kernel, as you have described, and then feeding it
> the <fstype>:<device> text to find other things.
> [I have not yet identified the code that goes looking
> around for the first /boot/kernel/kernel that it
> can find that appears to be some form of kernel.]
>>>> /etc/fstab on the card points to the usb drive for /.
>>> True. And that is were the kernel and dtb come from,
>>> not the usdcard.
>> Right, but that's not how ubldr finds them.
> If the kernel loads the dtb file, it might use the
> <fstype>:<device> that it was given to find
> /boot/dtb/ to look up the dtb file?
> (That would still leave an arbitrary kernel being
> loaded first if more than one is around.)
>>> For reference loader.config has:
>>> # more /mnt/boot/loader.conf
>>> geom_label_load="YES"           # File system labels (see glabel(8))
>>> #
>>> kern.cam.boot_delay="10000"
>>> vfs.mountroot.timeout="10"
>>> dumpdev="/dev/da0p2"
>>> (So no vfs.root.mountfrom .)
>> What does config.txt have?
> The rpi2 CONFIG.TXT on the msdosfs has:
> # more /media/CONFIG.TXT
> init_uart_clock=3000000
> enable_uart=1
> kernel=u-boot.bin
> kernel7=u-boot.bin
> (Unchanged by me: simply copied over from
> the sysutils materials for the rpi2.)
>>>> This is all standard loader behavior.
>>> I'm not sure avoiding the kernel and dtb being on the usdcard
>>> is standard-supported behavior. It might be a lucky,
>>> limited-context accident rather than a general property as a
>>> technique.
> Looks like not-desired in its current form.
>>>> It won't change. In your case, you aren't even using UEFI, so it doubly won't change.
>>> I also have access to an rpi3 and a pine64+ 2GB, which are
>>> UEFI based. But I'd not yet tried a similar configuration
>>> to what I'd recently done on the rpi2 V1.1 .
>>> The list notices made me unsure if I should even try. It
>>> is this part that I'm trying to figure out, both for now
>>> and for after the changes.
>> You should try, but you may need one additional line to explicitly declare things.
>>>> UEFI has more direct ways of doing this, but this setup would work there because it is explicit. It doesn't depend on the current boot1.efi behavior...
>>> The lack of depending on the "random order" status may well
>>> be true.
>>> But I'm still not sure if the the lack of a kernel and dtb
>>> file on the usdcard puts the technique out of bounds for
>>> the rpi2 and pine64+ 2GB.
> [That last rpi2 should have been a rpi3.]
>> I think this is more of the same bogus random order behavior that's making your stuff word accidentally. Since ubldr is being phased out, I have no plans to change it.
>> But as someone that deploys systems with multiple partitions that might be right, I hate random :(
> Understandable.
>> Warner
> Thanks much. I understand a bit more now.
>>> Warner
>>>>>> (For my particular interest the context uses UFS, not
>>>>>> ZFS.)
>>>>>>> What is being deleted is one final step: "otherwise use the first UFS partition on any drive in a random order that's usable." which used to be at the end of the boot1.efi psuedo code. It's my belief that no such installations actually use this due to the random factor today (plug in a new USB drive and it might take over). If my belief is wrong, it's my belief that efibootmgr will solve it, and failing that, the fallback mechanism (for platforms that use u-boot + EFI where UEFI variables don't work) will allow the two or three people that are doing this today.

Looking at the ubldr.bin code it appears
that the only way to avoid the ubldr.bin
"find first in some order" is to have
set loaderdev to indicate where to
get the loader scripts and kernel
from. Otherwise it will do the general
probing, for example:

. . .
FreeBSD/armv7 U-Boot loader, Revision 1.2

Number of U-Boot devices: 3
U-Boot env: loaderdev not set, will probe all devices.
Found U-Boot device: disk
  Probing all disk devices...
  Checking unit=0 slice=<auto> partition=<auto>... good.
Booting from disk0p1:
. . .

Matches up with:

. . .
device_types[] = {
        { "disk", DEV_TYP_STOR },
        { "ide",  DEV_TYP_STOR | DT_STOR_IDE },
        { "mmc",  DEV_TYP_STOR | DT_STOR_MMC },
        { "sata", DEV_TYP_STOR | DT_STOR_SATA },
        { "scsi", DEV_TYP_STOR | DT_STOR_SCSI },
        { "usb",  DEV_TYP_STOR | DT_STOR_USB },
        { "net",  DEV_TYP_NET }
. . .
 * Parse a device string into type, unit, slice and partition numbers. A
 * returned value of -1 for type indicates a search should be done for the
 * first loadable device, otherwise a returned value of -1 for unit
 * indicates a search should be done for the first loadable device of the
 * given type.
 * The returned values for slice and partition are interpreted by
 * disk_open().         
 * Valid device strings:                     For device types:
 * <type_name>                               DEV_TYP_STOR, DEV_TYP_NET
 * <type_name><unit>                         DEV_TYP_STOR, DEV_TYP_NET
 * <type_name><unit>:                        DEV_TYP_STOR, DEV_TYP_NET
 * <type_name><unit>:<slice>                 DEV_TYP_STOR
 * <type_name><unit>:<slice>.                DEV_TYP_STOR
 * <type_name><unit>:<slice>.<partition>     DEV_TYP_STOR
 * For valid type names, see the device_types array, above.
 * Slice numbers are 1-based.  0 is a wildcard.
static void
get_load_device(int *type, int *unit, int *slice, int *partition)
        char *devstr;
        const char *p;
        char *endp;
        *type = -1;
        *unit = -1;
        *slice = 0;
        *partition = -1;
        devstr = ub_env_get("loaderdev");
        if (devstr == NULL) {
                printf("U-Boot env: loaderdev not set, will probe all devices.\n");
        printf("U-Boot env: loaderdev='%s'\n", devstr);
. . .

It appears that loaderdev's notation depends
on the <type_name><unit>: having a stable
interpretation if it is to avoid picking
different things based on what combination
of other devices happen to be plugged in at
the time. Stablility require binding, say, a
unit 0 to a specific place even when the
place was not populated. (Unlikely?)

This suggests that <type_name><unit> notation
is specific to each device type. But I'm not
aware of a place to go look up a device type
and get a definition for how <type_name><unit>
will be bound to plugged in devices.

It is not clear to me what happens with <slice>
in the notation when the <type_name><unit>:
identifies something with just, say, gpt
partitioning. (No slice layer.) Listing 0 (the
slice wildcard) could match a device with some
non-gpt structure. An explicit, say, 1 could
as well but would also have no slice to refer
to for gpt partitioning.

Overall it does not appear to me that the
ubldr.bin based contexts can fully avoid the
"systems with multiple partitions that might
be right" issue when what is connected can
change from boot to boot: There just is not
the right kind of context for avoiding the

Still an explicit loaderdev use in some styles
of usage could make mismatches less likely.

Mark Millard
markmi at dsl-only.net

More information about the freebsd-current mailing list