Boot fails with self-build u-boot

Seems that the 10 bytes after sdmmc_boot pointing to brlyt 0x200 and brlyt to preloader at 0x800+2k (also 0x800). Preloader loads uboot at 0x50000+2k

Spent quite a lot of time today debugging why my BPiR2 refuses to boot from SD card if this card has some partitions defined in MBR. Turned out that board seems to ignore SD card in case any entry in partition table is marked as “active” (also known as “boot” flag). In my case I was generating SD card image template using OpenWRT’s ptgen utility which by default sets first primary partition as “active”. Quick fix using fdisk and viola - board is able to boot from this SD card.

You have installed openwrt to an sd with partitiontable? Is openwrt also on a partition or outside?

It is a bit more flexible. Right now I’m running openwrt on eMMC with partitions but it works the same way for SD card. Partitions look like this:

root@lx2bpir2:/etc# block info
/dev/mmcblk0p1: UUID="047A-02BF" LABEL="NO NAME" VERSION="FAT16" MOUNT="/boot" TYPE="vfat"
/dev/mmcblk0p2: UUID="57f8f4bc-abf4-655f-bf67-946fc0f9f25b" VERSION="1.0" MOUNT="/" TYPE="ext4"
/dev/sda1: UUID="7ebdbb13-3c7b-e844-1f52-049f80e63836" LABEL="OpenWrt:r1" TYPE="linux_raid_member"
/dev/sdb1: UUID="7ebdbb13-3c7b-e844-1f52-049f80e63836" LABEL="OpenWrt:r1" TYPE="linux_raid_member"
/dev/md127: UUID="1bb2dfe2-8158-4833-9dff-9260b998716d" LABEL="owrt-raid1" VERSION="1.0" MOUNT="/mnt/storage" TYPE="ext4"

Kernel and uEnv.txt reside on /dev/mmcblk0p1 vfat partition (mounted as /boot in openwrt), rootfs is ext4 on /dev/mmcblk0p2. uUnv.txt had been modified a bit compared from your variant so it detects if the system had been booted from eMMD or SD and then it boots the kernel and rootfs from the same device. Original logic in your uEnv.txt was to check if the first partition on SD is a valid FAT filesystem and boot from it even if preloader/u-boot were booted from eMMC. It resulted in nasty behavior for the case when openwrt is installed on eMMC and hardware boot switch is set to eMMC. For such case if an SD card is inserted into the slot and have got valid FAT filesystem on the first partition then u-boot will try to boot from this SD card - which is not what I want. Instead I had implemented two bootmenu options allowing user to select what bootdevice to use.

Ah, and another change is to move u-boot saveenv target offset to be less than 1MB - default partition scheme for modern linux is the first partition to start at 1MB so if u-boot is compiled to saveenv to the first megabyte the first time saveenv is used would result in data corruption on the first partition. Good offset to store saveenv is 950k from the start of the disk: it is far enough to provide plenty room for u-boot itself (950k-320k=630k available for u-boot binary) and leaves 50k for environment storage - which is also more than enough.

Back to openwrt changes:

  • Had hacked in the support for built-in openwrt’s sysupgrade functionality to allow for easy system upgrades by end-user. It is a “classic openwrt sysupgrade.bin-style” system upgrade: put all known conffiles + all files listed by end-user as “save at sysupgrade” into a tarball, flash the preloader, u-boot, kernel (vfat partition containing kernel and uEnv.txt for our case) and filesystem with new ones coming from sysupgrade file, put saved configs tarball to the root of the rootfs and reboot. Pre-init script will find tarball on the first boot and restore all configuration files to original places.
  • Currently I’m considering implementing another way of sysupgrading system that is available in openwrt - through sysupgrade.tar. This upgrade style works a bit differently as it only allows for updating kernel and rootfs - which might be great for people who don’t want to update preloader/u-boot and loose uEnv.txt local changes each time they to a sysupgrade. Shouldn’t be that hard to implement but it is time to spend and I’ve got a lot of other things to fix that seem to be more important than this.
  • Had properly integrated DTS fixes into the openwrt’s kernel patch/build system (fix for second gmac, fix for memory to be 2GB instead of 1GB, add pcie entries). No more ugly “copy dts/dtsi to this directory after executing make target/linux/{clean,prepare}”.
  • Had created new uboot-mediatek package using openwrt’s u-boot building infrastructure coupled with u-boot patches from your u-boot 2019.01 repo.
  • What is left to implement is an auto-download functionality for preloader binary. What @Jackzeng had done in latest Openwrt 2018.06.2 based image is an ugly hack: he had put preloader binary files directly into the git repo at staging_dir/target-arm_cortex-a7+neon-vfpv4_musl_eabi/image/. It is not upstreamable but it’s not the worst thing here. Main fault with this approach is the fact that staging_dir image target folder gets emptied on make clean. I.e. execute make clean once and get the sudden and surprising build failure on following make target/install. There are several ways to re-implement this properly but fist thing I need is an “official vendor source” for preloader which I can rely on to put the download URL into makefile. I believe you’ve seen my question on this matter in u-boot thread but it looks like vendor representatives have next to none incentive to provide me with an answer :frowning: .

Nevertheless my end-goal for this project it to try to upstream at least some of these changes into openwrt main tree so it would be less hassle for me in future to maintain my own fork.

how have you done this? Using my getbootdev-function? afair the uboot-cmd is not in actual uboot because i don’t have pushed this part to upstream,only the saveenv-mapping.

I have adapted the original behaviour (and the offset of 1MB) to be compatible to official images. I don’t know atm how bis my current uboot-binary is,but there is a check in build.sh to not overwrite saveenv-position (prevent saveenv overwrite uboot). Original partition scheme starts with first partition at 100 mb…

https://www.fw-web.de/dokuwiki/doku.php?id=en:bpi-r2:storage

I do not stick with original partition scheme and prefer to keep limitations to the bare minimum possible. As had been figured out in this thread minimal requirements for board to boot from SD are:

  1. SDBOOT 20 byte signature at offset 0.
  2. BRLYT 32 byte signature at offset 512 (1 sector).
  3. Preloader binary at offset 2048 (2k == 4 sectors).
  4. U-boot raw binary at offset 327680 (320k = 640 sectors).
  5. Only MS-DOS MBR-style partition table label is allowed.
  6. No active partitions are allowed.

So my script to create bootable SD is like this:

#!/usr/bin/env bash
#
# Copyright (C) 2013-2019 OpenWrt.org
#
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.
#

set -ex
[ $# -eq 7 ] || {
    echo "SYNTAX: $0 <file> <preloader image> <u-boot image> <bootfs image> <rootfs image> <bootfs size> <rootfs size>"
    exit 1
}

OUTPUT="$1"
PRELOADER="$2"
UBOOT="$3"
BOOTFS="$4"
ROOTFS="$5"
BOOTFSSIZE="$6"
ROOTFSSIZE="$7"

head=4
sect=63

set `ptgen -o $OUTPUT -h $head -s $sect -l 1024 -t c -p ${BOOTFSSIZE}M -t 83 -p ${ROOTFSSIZE}M -a 0`

BOOT_OFFSET="$(($1 / 512))"
BOOT_SIZE="$(($2 / 512))"
ROOTFS_OFFSET="$(($3 / 512))"
ROOTFS_SIZE="$(($4 / 512))"

PRELOADER_OFFSET=2     # 2KB
UBOOT_OFFSET=320       # 320KB

SDMMC_BOOT="SDMMC_BOOT\x00\x00\x01\x00\x00\x00\x00\x02\x00\x00"
BRLYT="\
BRLYT\x00\x00\x00\x01\x00\x00\x00\x00\x08\x00\x00\
\x00\x08\x00\x00\x42\x42\x42\x42\x08\x00\x01\x00\x00\x08\x00\x00\
\x00\x08\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"

echo -en "${SDMMC_BOOT}" | dd bs=1 of="${OUTPUT}" seek=0   conv=notrunc
echo -en "${BRLYT}"      | dd bs=1 of="${OUTPUT}" seek=512 conv=notrunc

dd bs=1024 if="${PRELOADER}" of="${OUTPUT}" seek="${PRELOADER_OFFSET}" conv=notrunc
dd bs=1024 if="${UBOOT}"     of="${OUTPUT}" seek="${UBOOT_OFFSET}"     conv=notrunc
dd bs=512  if="${BOOTFS}"    of="${OUTPUT}" seek="${BOOT_OFFSET}"      conv=notrunc
dd bs=512  if="${ROOTFS}"    of="${OUTPUT}" seek="${ROOTFS_OFFSET}"    conv=notrunc

Having u-boot sitting at 320k offset and knowing that u-boot binary is about 300k in size (as of 2019.01 version built from your repo) leaves us with a relatively safe assumption that we may place the start of the first partition at 1MB offset - and it so happens that this offset is a default offset for modern linux-world partitioning tools like fdisk or parted. So I have this patch applied:

diff --git a/include/configs/mt7623.h b/include/configs/mt7623.h
index ba763501..fb6ac073 100644
--- a/include/configs/mt7623.h
+++ b/include/configs/mt7623.h
@@ -53,4 +53,7 @@
 #define CONFIG_EXTRA_ENV_SETTINGS      \
        FDT_HIGH

+#define CONFIG_SYS_MMC_ENV_DEV 0
+#define CONFIG_ENV_OFFSET      0xF3800
+
 #endif

Regarding boot detection logic, here is how I’ve done it:

roottmpl=${rootdev} rootfstype=ext4 rootwait
prepsetroot=setenv setroot setenv root ${roottmpl}

buildargs=setenv bootargs "console=${console} root=${root} ${bootopts} ${graphic}"
checkbootedfrom=if itest.l *81dffff0 == 434d4d65 ; then setenv bootedfrom eMMC; else setenv bootedfrom SD; fi;

checksd=fatinfo ${device} 1:1
selectmmc=run useemmc; if test "${bootedfrom}" == "eMMC"; then echo "Booted from eMMC, using eMMC"; else run selectsd; fi;
selectsd=if run checksd; then echo "Booted from SD, pt#1 FAT OK, use SD"; run usesd; else echo "Booted from SD, pr#1 is not FAT, fall back to eMMC"; fi;
usesd=setenv partition 1:1; setenv rootdev /dev/mmcblk1p2;
useemmc=setenv partition 0:1; setenv rootdev /dev/mmcblk0p2;

newboot=run prepsetroot; run setroot;run buildargs;printenv bootargs;fatload ${device} ${partition} ${loadaddr} ${kernel}; bootm

#bootmenu
boot0=run lskernel;run askkernel;if printenv kernelinput ;then setenv kernel ${kernelinput};run lsdtb;run askdtb;if printenv dtbinput ;then setenv fdt ${dtbinput};fi; run newboot2; fi;
boot1=run newboot;

bootmenu_default=2
bootmenu_0=1. Enter kernel-name to boot from SD/EMMC.=run boot0
bootmenu_1=2. Boot kernel from TFTP.=run bootnet
bootmenu_2=3. Boot from SD/EMMC (auto select).=run boot1
bootmenu_3=4. Boot from eMMC.=run useemmc; run boot1
bootmenu_4=5. Boot from SD.=run usesd; run boot1
1 Like

you can use my uboots bootdevice-detection like this:

as you see there i also tried setting an environment-variable :slight_smile:

This code is not in 2019-01-bri-r2 branch I was basing my work on. FWIS it is in bpi-r2_v5 branch. Anyway I prefer to have direct comparison like “if itest.l *81dffff0 == 434d4d65 ; ...” in uEnv rather than relying on custom command in u-boot that is not available in upstream version. It is just more portable this way - one less patch to care about when porting to more recent version.

right, i have not put this part to upstream (and inside newer branches), because i did not need this before…it was only a test for me :slight_smile:

the other code testing for emmc-string in Memory was from me, but currently only used for determining device for saveenv

Frank, I’m wondering about the magic behind the address 0x81DFFFF0 that you use to determine if it was eMMC or SD card the device had booted from. Looks like this detection is not that reliable:

[Dram_Buffer] MAX_TEE_DRAM_SIZE: 0
Boot from eMMC!!
[PLFM] Init Boot Device: OK(0)

...

 bootloader load uboot ,the address of uboot is 81E00000
[PART]partition name UBOOT
[PART]partition start block 0x200
[PART]partition size 0x80000
[PART]partition blks 0x400
[PART]partition flags 0x0
[PART]partition name 0x8
[bean] part->startblk(0x200) bdev->blksz(0x200) part->part_id(8) hdr(0xFFB50000)
[BlkDev.c 101 ]partition block size 0x200 ,blks:0xE90000
[BlkDev.c 101 ]partition block erase size 0x200

[PART] load "UBOOT" from 0x0000000000050000 (dev) to 0x81E00000 (mem) [SUCCESS]
[PART] load speed: 10462KB/s, 300000 bytes, 28ms
[BT_SD_PG] device info 0x8590 0x8A00 0xCB01 0x102

...

[PLFM] boot reason: 0
[PLFM] boot mode: 0
[PLFM] META COM0: 0
[PLFM] <0xFFB7CC10>: 0x0
[PLFM] boot time: 2013ms
[PLFM] DDR reserve mode: enable = 0, success = 0

[BLDR] jump to 0x81E00000
[BLDR] <0x81E00000>=0xEA0000B8
[BLDR] <0x81E00004>=0xE59FF014


U-Boot 2019.01 (Jan 30 2019 - 12:21:02 +0000)

CPU:   MediaTek MT7623 E3
DRAM:  2 GiB
MMC:   mmc@11230000: 0, mmc@11240000: 1
Loading Environment from MMC... Boot From SD(id:1)

MMC: no card present

As you can see here I boot the board from the eMMC but u-boot thinks that is was booted from SD - and that’s because of this:

BPI-R2> md 0x81DFFFF0
81dffff0: ffffffff ffffffff ffffffff ffffffff    ................

It is not the first time I see it fail. Sometimes I was getting “eMMC” at this offset even when I was booting from SD. That’s why I’m wondering about the “detection magic” - what was the reasoning behind using this offset as an information source?

I took the offset from jackzengs implementation

But simplyfied the code to make it compatible with upstream uboot. In my tests i got the right value,but you need a powercycle to boot from the other device…on reboot it remembers bootdevice…

I don’t know a better way…also i know no way to get boot-switch position and cannot test it. maybe this is for bootswitch, but i have no such board: https://github.com/BPI-SINOVOIP/BPI-R2-bsp/commit/2bf5f720c99db45ee551176a4f2a44f622fcf8a9

maybe this is interferring with your board, have you tested with switch on SD?

Uh oh, it looks like a hackish hack. g_mmc_devid is an extern that is defined in common/env_mmc.c (which is patched in “Save uboot envs to where it boot from” change) so it is initialized to 0 if at 0x81DFFFF0 there’s a “eMMC” or to 1 if not. And the “hackish” I mean that this change hardcodes dev to always be 0 or 1 no matter what was the actual device number passed to the mmc command. I.e. this change breaks mmc command in unobvious way and makes either SD card or eMMC unavailable from u-boot,

As for boot switch “detection” - my experiments with different preloaders seem to show that the contents of the memory at address 0x81DFFFF0 is set to “eMMC” by some versions of preloaders to indicate that u-boot is loaded from the eMMC. It has no direct relation to the position of the boot switch. If a preloader of such kind is written to the SD card you still would be getting “eMMC” at memory 0x81DFFFF0 despite the fact that boot switch is set to SD and preloader was loader from this SD card. We need something else to detect the real position of the boot switch. I may speculate here that probably boot switch is connected to some gpio line on the MT7623n and the state of this line is used by the in-chip hardcoded firmware “pre-pre-loader” to determine if attempt to boot from SD card should be skipped or not. If that’s the case than in theory we may read the state of this gpio line and get our answer. But this is a speculation and more info from vendor (or further investigation) is needed to be able to solve this.

P.S. Or there’s an easiest fix (from my PoV): vendor might provide us with yet another preloader binary that always loads u-boot from SD card and always set memory at address 0x81DFFFF0 to some predefined value like "SD ". Such preloader coupled with already available preloader that always loads u-boot from eMMC and always put “eMMC” to that memory address would allow us to reliably determine the source u-boot was loaded from which is sufficient for our needs. The only rule to follow then would be to place “SD card always” preloader to SD cards and “eMMC always” preloader to eMMC and that will seal the deal.

1 Like

thank you for your investigation. I have a board without this switch but maybe here the gpio-line is soldered to sd-position.

In my tests my detection works…have i used differen preloaders?

afair i used debian-image to create my own (erased boot/root-partitions and leave preloader,other bootheaders,uboot). On emmc i used BPI-R2-EMMC-boot0-DDR1600-0k-0905.img.gz which should not set emmc-string based on your findings.

Btw. Read out switch does not represent boot-device…if switch is on SD and no (bootable) sd is inserted r2 will boot from emmc and your gpio-state shows wrong position

That’s worrying. I will try to re-test with this preloader this evening - it might be me messing something up while testing.

Yep, it was me messing up, but rather than messing up while testing I messed up while copying text from LibreOffice Writer to browser window. BPI-R2-EMMC-boot0-DDR1600-0k-0905.img behave the same way as mtk-bpi-r2-preloader-emmc.bin, not mtk-bpi-r2-preloader-sd.bin. I.e. it always looks for u-boot on the eMMC and initializes memory at 0x81DFFFF0 to “eMMC”.

To be quite frank it all depends on the definition of the boot device. For example if we’ve got SD card inserted that is missing preloader but has proper u-boot, on eMMC we’ve got preloader but do not have u-boot and boot switch is set to eMMC - what should we call “a boot device” for this case? SoC will start up into mtk bootrom which will load preloader from eMMC (enforced by the boot switch position) then preloader will look for u-boot on SD card, load it from there and execute it. Was the eMMC a boot device? Or was it SD? Or both?

My view on this is that we should always treat u-boot source as a boot device as one of the main reasons we need to know what was the boot device is to determine where to look for and where to store u-boot env. “Easiest fix” I described earlier fits this approach nicely. Or as a best case scenario would be vendor to provide us with the source code and build instructions for preloader (should be something similar to this repo; useful references 1, 2, 3). This way we will be able to implement what we want ourselves and be sure what is where and why.

We will not get source of preloader because this is NDA…

Why not just use BPI-R2-EMMC-boot0-DDR1600-0k-0905.img for emmc and one of the others (which do not write emmc-string) for sdcard…and all works as expected

Not really. SD only preloaders that are available now do not touch memory at address 0x81DFFFF0. It means that if the board was booted once with eMMC preloader and then this memory location wasn’t changed by linux then if the next boot without power cycle would happen from SD card with “SD only” preloader we will still have “eMMC” at that location despite the fact we booted from SD.

P.S. Although we may workaround the situation by having two different u-boot builds (SD and eMMC) or we may reset this memory location to, say, 0x00000000, in u-boot after checking for eMMC boot flag. Will test the second approach on my boards today’s evening and report back the results.

Well, I’m almost ready to give up :frowning:. Frank, may I ask you to perform the following test:

  1. Write mtk-bpi-r2-preloader-emmc.bin to eMMC and mtk-bpi-r2-preloader-sd.bin to SD card.
  2. Do not insert SD card, boot from eMMC into u-boot. Check that md 0x81dffff0 reports “eMMC”.
  3. Insert SD card and press reset button (or run reset). Check that there’s “Load u-boot from SD Card...” in the preloader boot log.
  4. Check what’s the output of the md 0x81dffff0. Expected result: “eMMC”.
  5. Execute mw.l 0x81dffff0 0x0. Then re-check the output of the md 0x81dffff0. Expected result: 0x00000000.
  6. Press reset button or execute “reset”. Check that there’s “Load u-boot from SD Card...” in the preloader boot log.
  7. Check what’s the output of the md 0x81dffff0.

My expectations were that as we had reset memory to 0x00000000 at pt.5 and then rebooted board using “SD-card only” preloader then memory at address 0x81dffff0 will be left untouched and thus be equal to 0x00000000 we had set it to. But experiments on my board show that this location gets set to “eMMC” by something (preloader? bootrom?) despite the fact that board booted from SD and “SD-only” preloader was used.

P.S. And it gets even weirder: if I reset memory at 0x81dffff0 to 0x00000000 and then boot to linux instead of reseting the board and only then do a reset (or execute “reboot” in OS shell) - memory at 0x81dffff0 stays at value 0x00000000 instead of being set to “eMMC” by something. I honestly don’t get what’s going on here. I think we need to somehow extract more info from vendor representatives to solve this one.

2: “Load u-boot from eMMC…”

BPI-R2> md 0x81dffff0 
81dffff0: 434d4d65 feef7f7f f9beffff fffffffb    eMMC............

3/4: “Load u-boot from SD Card…”

BPI-R2> md 0x81dffff0
81dffff0: 434d4d65 feef7f7f f9beffff fffffffb    eMMC............

so still emmc…mhm, thats wrong, seems that uboot is loaded from sd, but preloader from emmc or preloader from sdcard does not touch this memory-address (state from previous boot)

5:

BPI-R2> mw.l 0x81dffff0 0x0
BPI-R2> md 0x81dffff0      
81dffff0: 00000000 feef7f7f f9beffff fffffffb    ................

like your expectation

6: after reset again “Load u-boot from SD Card…”

7: oh…

BPI-R2> md 0x81dffff0                                                           
81dffff0: 434d4d65 feef7f7f f9beffff fffffffb    eMMC............

emmc is set again

conclusion, it seem that if first booted from emmc the memory is always set to emmc, but uboot (and maybe preloader) are loaded from sdcard…we still not know if bootrom or preloader setting this memory-position, but it contains the first boot-information after power cycle and will be rewritten after each boot to this.

just a thought…preloader/bootrom set the string to a physical memory-address and give that to uboot, now uboot takes this memory-block and copies it to an own range (around the 0x81dffff0). so uboot does not change the original position of the string…only a copy

i.e.

(uboots) RAM starts at 0x81E00000, like this line shows:

bootloader load uboot ,the address of uboot is 81E00000

and this area does always rewritten by bootrom/preloader with contents from bootmedia and our bootinfo-string which will never changed till a power-cycle. your changes with md are only in ram and do not change the source-string

but i do not know about your last test (loading linux and make a reboot there)…or do you make a real power-reset? maybe reset there triggers bootrom again (watchdog,acpi or similar) to erase the string in emmc

is there any way in uboot to search a string in memory (to get and compare “Load u-boot from …”)?

Yep, that was the conclusion from experiments on my board. I was hoping that it might be me doing something wrong or might be my board being somewhat funky. As your tests show that’s not the case unfortunately for us.

Nope, no power cycle in this test. But in case your theory about string being copied from another place to 0x81dffff0 is correct it might be that linux happens to overwrite the location this string gets copied from and that’s why we this this strange behavior. :thinking: Or - as the SoC used works essentially the same way as Android handhelds do - booting from eMMC might be implemented as a “recovery” or “fastboot” or some other boot mode of such kind. So it might set a flag somewhere in SoC (some internal register or some location in memory) and only reset this flag after linux kernel re-initialize some part of the SoC. We need preloader sources to be able to tell for sure what’s going on here. Or at least get more info from the informed person who has the access to the preloader source.

As for your question about a way in u-boot to search for a string in memory - I’m not experienced enough with u-boot but even if there’s no such thing I’d say that it would be pretty easy to implement it as an additional u-boot command. As an alternative I think that it is even possible to code it in u-boot hash (or is it dash?) shell code but the speed of such solution will be really slow.