In my intended application, my BBB absolutely must default to booting from 
the uSD when power is applied. I know I could short the boot switch, or 
move pull-up/down resistors, or create a hardware switch gated from reset, 
but I don't want to lose hardware compatibility with other BBB boards, or 
give up the option of manually booting from eMMC occasionally. My goal is a 
uSD configuration that can simply be inserted and booted by any BBB, 
without making any hardware or software changes to the board or its eMMC. 

Because this is so dependent on the boot environment of the BBB you are 
using and the distro you want to boot, nobody can hand you an all-purpose 
uEnv.txt file that will always work. This post is more of a play-by-play 
description of how I made my two installations work together. It will 
hopefully help other users analyze their own environments. 

For now I'm accepting a limitation that the target BBB will be rev A6 or 
newer, and the eMMC will contain the default Angstrom version 
BBB-eMMC-flasher-2013.06.20 or BBB-eMMC-flasher-2013.09.04 - with a 3.8 
kernel and compatible U-Boot version. Both of these have the default 
bootcmd set to:
-----
bootcmd=gpio set 53; i2c mw 0x24 1 0x3e; run findfdt; mmc dev 0; if mmc 
rescan ; then echo micro SD card found;setenv mmcdev 0;else echo No micro 
SD card found, setting mmcdev to 1;setenv mmcdev 1;fi;setenv bootpart 
${mmcdev}:2;mmc dev ${mmcdev}; if mmc rescan; then gpio set 54; echo SD/MMC 
found on device ${mmcdev};if run loadbootenv; then echo Loaded environment 
from ${bootenv};run importbootenv;fi;if test -n $uenvcmd; then echo Running 
uenvcmd ...;run uenvcmd;fi;gpio set 55; if run loaduimage; then gpio set 
56; run loadfdt;run mmcboot;fi;fi;
-----

While it is true that the BBB "checks for an inserted uSD at boot time", it 
does not read the initial boot command from the uSD without the hardware 
boot switch activated. MLO is read from the first (FAT) partition of the 
eMMC, and it loads U-Boot.img from the eMMC. That bootcmd is compiled into 
MLO, so given my boot process goal I'm stuck with it. Here's what it does: 

bootcmd=
gpio set 53;                                                  [first LED]
i2c mw 0x24 1 0x3e;                             [raise input power limit]
run findfdt;                                [configure fdt file location]
mmc dev 0;                 [start search for valid boot partition at uSD]
if mmc rescan ; then echo micro SD card found; setenv mmcdev 0;
  else echo No micro SD card found, setting mmcdev to 1; setenv mmcdev 1;
fi;                      [leave mmcdev set to first valid boot partition]
setenv bootpart ${mmcdev}:2; mmc dev ${mmcdev}; 
if mmc rescan; then gpio set 54; echo SD/MMC found on device ${mmcdev};
  if run loadbootenv; then echo Loaded environment from ${bootenv};run 
importbootenv;
  fi;         [load and "import" uEnv.txt --> now uSD can change things!]
  if test -n $uenvcmd; then echo Running uenvcmd ...; run uenvcmd;
  fi;                            [default uenvcmd is <null> so no action]
  gpio set 55;                                                [third LED]
  if run loaduimage; then gpio set 56; run loadfdt; run mmcboot;
  fi;          [load uImage from /boot, am335x-boneblack.dtb, boot those]
fi;


Using uEnv.txt we can change the boot environment, but we can't change the 
already running bootcmd. My choice of Ubuntu 12.04 for the uSD image uses 
the more recent zImage format rather than Angstrom's uImage, so it would be 
difficult to hack that final loaduimage process. 

We can't just delete unwanted commands. The "test" command can cope with a 
missing file, but the "run" command can't:
---
U-Boot# run loaduimage
## Error: "loaduimage" not defined
U-Boot# <INTERRUPT>
---

We could change the command itself, but it is tricky because substitutions 
for the ${variables} are performed during the setenv:
---
U-Boot# setenv loaduimage load mmc ${bootpart} ${loadaddr} 
${bootdir}/${bootfile}
U-Boot# echo $loaduimage
load mmc 0:2 0x80200000 /boot/uImage
---

In case you are testing these functions in U-Boot, avoid the temptation to 
type "boot" or "bootd". 
U-Boot# boot  --> boot default, i.e., run 'bootcmd'
U-Boot# bootd  --> boot default, i.e., run 'bootcmd'
In our scenario the default bootcmd is already running before we get to 
insert our modifications, and we probably don't want to start over by 
running it again.

If you aren't automatically dumped to the U-Boot prompt by a boot failure, 
you need to be ready to hit a terminal key when the "Hit any key to stop 
autoboot:" prompt appears. Obviously you need a debug terminal adapter 
connected to the J1 serial port header. That prompt is the only place you 
can stop the boot process; to "single-step" through the rest of your boot 
you'll need to manually type or paste the commands to the U-Boot prompt. 


It seems much simpler and closer to the design intention to add an 
appropriate uenvcmd to our uEnv.txt. With that we can intercept the boot 
process before it gets to loaduimage. 


There are several critical clues we need in order to modify uEnv.txt 
successfully:

1. The file must have UNIX line endings (<CR> only, not <CRLF>), and must 
have a blank line as the last line. 

2. A sequence of commands intended as a single variable value must be 
delimited by ';' within a single line of the uEnv.txt file. If manually 
entered at the U-Boot prompt for testing, the sequence must be enclosed by 
double quotes. 

3. Using "if" in a command without a matching "fi" produces a syntax error 
from U-Boot!

4. The active uEnv.txt file is the one in the first (FAT) partition of the 
boot medium. Once you are booted to Linux, it might be mounted as 
/boot/uboot, and this warning is true: "The boot device will always be seen 
by the OS as /dev/mmcblk0. U-Boot checks for an uSD card and swaps the 
ordering if it's found." But while you are still in U-Boot, the uSD is 
always 0 and the eMMC is always 1. The way to be sure you are changing the 
right file is to check from within U-Boot. For the uSD (my 8GB card with 
Ubuntu 12.04):
---
U-Boot# echo $mmcdev
0
U-Boot# mmc part
Partition Map for MMC device 0  --   Partition Type: DOS
Part Start Sector Num Sectors UUID Type
  1 2048       2048       0005a754-01 01 Boot
  2 4096       15640576   0005a754-02 83
U-Boot# ls mmc 0:1
   104320   mlo 
   370652   u-boot.img 
      340   uenv.txt 
---
For the eMMC (with default Angstrom):
---
U-Boot# ls mmc 1:1
    99976   mlo 
   379428   u-boot.img 
       26   uenv.txt 
---


The uenvcmd we want to add will depend upon the Linux distribution we want 
to boot from our uSD. I'm working with 
ubuntu-precise-12.04.3-armhf-3.8.13-bone30, which normally boots like this:
---
bootcmd=run findfdt; run mmcboot;setenv mmcdev 1; setenv bootpart 1:2; run 
mmcboot;run nandboot;
---

If the uSD boot is successful, only this part gets executed:
---
run findfdt; run mmcboot;
---

Here's findfdt:
---
findfdt=if test $board_name = A335BONE; then setenv fdtfile 
am335x-bone.dtb; fi; if test $board_name = A335BNLT; then setenv fdtfile 
am335x-boneblack.dtb; fi; if test $board_name = A33515BB; then setenv 
fdtfile am335x-evm.dtb; fi; if test $board_name = A335X_SK; then setenv 
fdtfile am335x-evmsk.dtb; fi; if test $fdtfile = undefined; then echo 
WARNING: Could not determine device tree to use; fi; 
---
The second option succeeds - fdtfile=am335x-boneblack.dtb. No other 
effects. 

And here's mmcboot, which executes to its end:
---
mmcboot=mmc dev ${mmcdev}; if mmc rescan; then echo SD/MMC found on device 
${mmcdev};if run loadbootenv; then echo Loaded environment from 
${bootenv};run importbootenv;fi;if test -n $uenvcmd; then echo Running 
uenvcmd ...;run uenvcmd;fi;if run loadimage; then run mmcloados;fi;fi;
---
Loadbootenv and importbootenv read and apply uEnv.txt, which we will 
already have done by the time our replacement mmcboot runs. 
Since we will have co-opted uenvcmd, we need to remember not to run uenvcmd 
again here!
Loadimage loads a zImage file. 

Mmcloados loads the fdt file (if available) and the actual zImage:
---
mmcloados=run mmcargs; if test ${boot_fdt} = yes || test ${boot_fdt} = try; 
then if run loadfdt; then bootz ${loadaddr} - ${fdtaddr}; else if test 
${boot_fdt} = try; then bootz; else echo WARN: Cannot load the DT; fi; fi; 
else bootz; fi;
---
We need to keep the loadfdt and bootz parts. 

Mmcargs sets several things that didn't seem important at first, but proved 
necessary! Without these, the boot process will look exactly like a good 
boot on your terminal, but the BBB will just lock up after "Uncompressing 
Linux... done, booting the kernel". All the correct arguments may be 
visible in your printenv, but if they are not set into "bootargs" the boot 
fails. 
---
mmcargs=setenv bootargs console=${console} ${optargs} root=${mmcroot} 
rootfstype=${mmcrootfstype}
---

Other possible causes of failure at this point:
<http://processors.wiki.ti.com/index.php/Kernel_-_Common_Problems_Booting_Linux>
-----
Problem #2 - No more output is seen on the console after "booting the 
kernel"
Cause #1 - The linux console boot parameter is incorrect
Cause #2 - Mis-match between boot-loader and kernel machine numbers
Cause #3 - A software bug
-----


Booting Ubuntu normally via the boot switch produces this console output:
---
mmc0 is current device
SD/MMC found on device 0
reading uEnv.txt
340 bytes read in 3 ms (110.4 KiB/s)
Loaded environment from uEnv.txt
Importing environment from mmc ...
Running uenvcmd ...
3314544 bytes read in 474 ms (6.7 MiB/s)
24884 bytes read in 32 ms (758.8 KiB/s)
Kernel image @ 0x80200000 [ 0x000000 - 0x329370 ]
## Flattened Device Tree blob at 80f80000
   Booting using the fdt blob at 0x80f80000
   Using Device Tree in place at 80f80000, end 80f89133

Starting kernel ...
---


So our new unevcmd is simple:
+++
uenvcmd=run findfdt; run mmcboot;
+++

The default Angstrom findfdt produces the same result so we don't need to 
worry about it:
---
findfdt=if test $board_name = A33515BB; then setenv fdtfile am335x-evm.dtb; 
fi; if test $board_name = A335X_SK; then setenv fdtfile am335x-evmsk.dtb; 
fi;if test $board_name = A335BONE; then setenv fdtfile am335x-bone.dtb; fi; 
if test $board_name = A335BNLT; then setenv fdtfile am335x-boneblack.dtb; fi
---

The default Ubuntu mmcboot would read uEnv.txt again, and run uenvcmd 
again, so we'll need to change this command:
---
mmcboot=mmc dev ${mmcdev}; if mmc rescan; then echo SD/MMC found on device 
${mmcdev};if run loadbootenv; then echo Loaded environment from 
${bootenv};run importbootenv;fi;if test -n $uenvcmd; then echo Running 
uenvcmd ...;run uenvcmd;fi;if run loadimage; then run mmcloados;fi;fi;
---

The default Angstrom mmcboot is looking for a uImage, so it won't work 
either:
---
mmcboot=echo Booting from mmc ...; run mmcargs; bootm ${kloadaddr} - 
${fdtaddr}
---

We already know we want to boot from a good uSD, and have dealt with 
uEnv.txt, so our new mmcboot can go directly to:
+++
mmcboot=if run loadimage; then run mmcloados; fi;
+++

Since we know we have an fdt file, mmcloados can go directly to:
+++
mmcloados=run mmcargs; if run loadfdt; then bootz ${loadaddr} - ${fdtaddr}; 
fi;
+++


The uEnv.txt file for our uSD needs these additions. In case there are 
immediate substitutions, it is probably smart to apply them in order of 
reference:
+++
mmcloados=run mmcargs; if run loadfdt; then bootz ${loadaddr} - ${fdtaddr}; 
fi;
mmcboot=if run loadimage; then run mmcloados; fi;
uenvcmd=run findfdt; run mmcboot;
+++



There are many more prerequisites we need to control before those three 
commands can run successfully...  

The default Ubuntu uenv.txt contains:
---
mmcpart=2
optargs=fixrtc
uenvcmd=i2c mw 0x24 1 0x3e; kd=0; if test $mmcdev -eq 1; then mmc dev 0; if 
mmc rescan; then kd=1; fi; mmc dev 1; fi; setenv mmcroot 
/dev/mmcblk${kd}p${mmcpart} ro
loadfdt=load mmc ${mmcdev}:${mmcpart} ${fdtaddr} ${bootdir}/dtbs/${fdtfile}
loadimage=load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${bootfile}
-----

Mmcpart seems to be used in the Ubuntu boot only by the two load... 
commands, which are reaching into the Linux filesystem (mmc 0:2) to load 
the fdt file and Kernel. It shouldn't break any of our other functions. 

Optargs=fixrtc seems to be in every uEnv, but I can't find an explanation. 

Uenvcmd we will change - but we might want to preserve the option for 
booting via the BBB boot switch using the environment on the uSD. So the 
essential parts of what it does might need to be moved to other commands... 
 

Loadfdt we call from mmcloados from mmcboot. But the Angstrom version of 
loadfdt is slightly different:
---
loadfdt=load mmc ${bootpart} ${fdtaddr} ${bootdir}/${fdtfile}
---
So we need to be sure the Ubuntu version is loaded:
+++
loadfdt=load mmc ${mmcdev}:${mmcpart} ${fdtaddr} ${bootdir}/dtbs/${fdtfile}
+++
As it normally is from the Ubuntu uEnv.txt.

Incidentally, loadfdt can complain explicitly about File not found! 
Eliminates some possibilities for the source of any problems...  
U-Boot# run loadfdt
** File not found /boot/dtbs/am335x-boneblack.dtb **
If it is successful you see something like:
U-Boot# run loadfdt
24808 bytes read in 51 ms (474.6 KiB/s)

Loadimage we call from mmcboot. It is not set by Angstrom, so we need the 
version from the Ubuntu uEnv.txt. That uses bootfile, which is set to 
uImage in Angstrom; we must reset it to zImage before adding loadimage:
+++
bootfile=zImage
loadimage=load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${bootfile}
+++

The default Ubuntu uenvcmd, which will be completely replaced. These 
functions might need to be performed by some other mechanism:
---
i2c mw 0x24 1 0x3e; 
kd=0; 
if test $mmcdev -eq 1; 
  then mmc dev 0; 
  if mmc rescan; 
    then kd=1; 
  fi; 
  mmc dev 1; 
fi; 
setenv mmcroot /dev/mmcblk${kd}p${mmcpart} ro
---
"i2c mw 0x24 1 0x3e" is a standalone power limit command in many uEnv.txt 
files, and can be in ours. 
The rest of that code just sets mmcdev which is already set by several 
commands, and mmcroot which is part of the mmcargs/bootargs configuration 
and can be left at the default "mmcroot=/dev/mmcblk0p2 ro" set by both boot 
environments. 


With variables organized in the order they will be needed for replacements 
into higher commands, the final uEnv.txt looks like this:
+++
i2c mw 0x24 1 0x3e
optargs=fixrtc earlyprintk=serial,ttyO0,115200n8 print_fatal_signals=1
mmcpart=2
bootfile=zImage
loadfdt=load mmc ${mmcdev}:${mmcpart} ${fdtaddr} ${bootdir}/dtbs/${fdtfile}
loadimage=load mmc ${mmcdev}:${mmcpart} ${loadaddr} ${bootdir}/${bootfile}
mmcloados=run mmcargs; if run loadfdt; then bootz ${loadaddr} - ${fdtaddr}; 
fi;
mmcboot=if run loadimage; then run mmcloados; fi;
uenvcmd=run findfdt; run mmcboot;
+++


My Windows sftp client can't copy the file to /boot/uboot within Ubuntu, 
even though it sees the files there (/dev/mmcblk0p1 1004K  470K  534K  47% 
/boot/uboot). Lack of permissions, I guess. I can copy it to /home and then 
copy it onward from within Ubuntu. Copying it to the first FAT partition of 
the uSD via a card reader in Windows or another Linux machine also works. 

Once the changed file appears to be in place on the uSD, you _must_ use 
something like "sudo shutdown -h now" in order to force it to be written to 
the uSD. Just cycling power will leave you with your previous file! 


And...  After days of rich "learning experiences", it works! When I apply 
power, my uSD boots. When I remove the uSD and apply power, Angstrom boots 
from the eMMC. Seems that is how it should work by default...  



In web posts I found several "optargs" that supposedly give you more boot 
log information sooner in the process. I haven't tried them individually, 
but using: 
optargs=fixrtc earlyprintk=serial,ttyO0,115200n8 debug ignore_loglevel 
log_buf_len=10M print_fatal_signals=1 LOGLEVEL=8
instead of:
optargs=fixrtc earlyprintk=serial,ttyO0,115200n8 print_fatal_signals=1
did add detail. But it kept adding detail after the boot was complete, 
making normal terminal interaction on the same tty impossible. 

Additional info began at the "<--" mark:
-----
Uncompressing Linux... done, booting the kernel.
[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Initializing cgroup subsys cpu
[    0.000000] Linux version 3.8.13-bone30 (root@imx6q-sabrelite-1gb-1) 
(gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5) ) #1 SMP Thu Nov 14 
11:19:20 UTC 2013
[    0.000000] CPU: ARMv7 Processor [413fc082] revision 2 (ARMv7), 
cr=10c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing 
instruction cache
[    0.000000] Machine: Generic AM33XX (Flattened Device Tree), model: TI 
AM335x BeagleBone
[    0.000000] bootconsole [earlycon0] enabled
[    0.000000] debug: ignoring loglevel setting.  <-- full logging begins
[    0.000000] Memory policy: ECC disabled, Data cache writeback
[    0.000000] On node 0 totalpages: 130816
[    0.000000] free_area_init_node: node 0, pgdat c09ba540, node_mem_map 
c0a35000
[    0.000000]   Normal zone: 1024 pages used for memmap
[    0.000000]   Normal zone: 0 pages reserved
[    0.000000]   Normal zone: 129792 pages, LIFO batch:31
[    0.000000] AM335X ES1.0 (neon )
-----

I'm not sure the added info would help much. Apparently the best way to 
look into the early boot process is to re-build your kernel with 
CONFIG_DEBUG_LL enabled. Haven't tried that. I suspect my main failure mode 
didn't get to the actual kernel at all, despite claiming to have loaded 
something to the correct address and uncompressed it. 

=====

-- 
For more options, visit http://beagleboard.org/discuss
--- 
You received this message because you are subscribed to the Google Groups 
"BeagleBoard" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to