A UART Boot adventure

The board

December 2025, I need a small board for my N2K testbed, something like a BeagleBoneBlack. Since these boards are pretty much indestructible, I decide to buy one second-hand for next to nothing. I find one at a giveaway price because the SD slot is dysfunctional. No problem, I have the gear for soldering/desoldering and the slot costs about 5 euros. It’s an Industrial BB Black. No real use for it in my workshop, but it doesn’t hurt. So I buy the industrial BBB for about ten euros and a new SD slot.

Objective: replace the SD slot and boot a fresh system.

Reception and first boot

The board looks in excellent condition, and there’s no sign that the SD slot is dead, at least to the naked eye. Because at boot, it’s impossible to boot from the SD card. The ROM code boots over UART.

Even by pressing on the slot, no SD card is detected. I then replace the SD slot.

SD card build

SD card type: MLC 8GB Gigastone, quite robust on my various builds. They’ve never let me down.

sudo mkfs.vfat -F 32 -n "BOOT_SD" /dev/sdc1
mkfs.fat 4.2 (2021-01-31)
sudo mkfs.ext4 -L "ROOT_SD" /dev/sdc2
mke2fs 1.47.3 (8-Jul-2025)
Creating filesystem with 1924608 4k blocks and 481440 inodes
Filesystem UUID: 0a157080-1f28-410c-9fdc-21849815d136
Superblock backups stored on blocks:
        32768, 98304, 163840, 229376, 294912, 819200, 884736, 1605632

Allocating group tables: done
Writing inode tables: done
Creating journal (16384 blocks):
done
Writing superblocks and filesystem accounting information: done

sudo mount /dev/sdc1 /mnt/alpha

file MLO
MLO: data
du -sh MLO
96K     MLO

file u-boot.img
u-boot.img: Device Tree Blob version 17, size=3708, boot CPU=0, string block size=150, DT structure block size=3500
du -sh u-boot.img
1.4M    u-boot.img

sudo cp MLO /mnt/alpha
sudo cp u-boot.img /mnt/alpha
sync /mnt/alpha
sudo umount /mnt/*

sudo fdisk -l /dev/sdc
Disk /dev/sdc: 7.44 GiB, 7989100544 bytes, 15603712 sectors
Disk model: Mass-Storage
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0x7d7ae755

Device     Boot  Start      End  Sectors  Size Id Type
/dev/sdc1  *      2048   206847   204800  100M  c W95 FAT32 (LBA)
/dev/sdc2       206848 15603711 15396864  7.3G 83 Linux

False victory

Even after soldering the new slot, impossible to boot from the SD card. Attempt with a classic Debian image, with an MLO and u-boot.img extracted from an image that works on my other BeagleBone Black. Nothing works, the SD card won’t be recognized.

I change the SD card, same result: the ROM didn’t find a valid image on SD/eMMC, it switches to UART boot and emits ‘C’ (XMODEM).

SD slots have a pin dedicated to SD card detection. Maybe this one is dysfunctional. I find the relevant pin in the BBB doc and I see that it correctly goes to ground when the card is inserted. So it’s working fine.

Hypotheses: is the SD slot badly soldered or defective? Or is the problem elsewhere?

Despite the new solder and an SD card prepared with love, the result is final: the serial console remains silent at startup (or doesn’t display the bootloader). The SD slot is still not operational. Instead of obsessing over the hardware, I decide to bypass the problem: if the board won’t read the SD, I’m going to inject the code directly into its veins via UART.

Preparing the ground for UART

As for my probes, I now use tio, which is very handy. I like to know what’s happening on the serial port when I transfer data. For that, socat is perfect. It creates a middleman quite simply. The only problem is that it releases the targeted port with difficulty. It’s sometimes necessary to make it spit it out with fuser -k -9 <serial port>.

So we have socat holding the /dev/ttyUSB0 in a sandwich and creating an interface on /tmp/vtty that tio will use.

[BBB UART] <---> [/dev/ttyUSB0] <---> [SOCAT] <---> [/tmp/vtty] <---> [TIO]

socat

 socat -v /dev/ttyUSB0,raw,echo=0 PTY,link=/tmp/vtty,raw,echo=0,unlink-early=1,unlink-close=1
C> 2026/02/11 15:15:56.000376128  length=1 from=0 to=0
C> 2026/02/11 15:15:57.000176133  length=1 from=1 to=1
C> 2026/02/11 15:15:57.000459264  length=1 from=2 to=2
C> 2026/02/11 15:15:57.000859289  length=1 from=3 to=3

tio

tio -b 115200 /tmp/vtty
[15:16:05.858] tio c43d2f6
[15:16:05.858] Press ctrl-a q to quit
[15:16:05.858] Connected to /tmp/vtty
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

The wall

OK, so everything is ready to launch the XMODEM/YMODEM protocol. I created my u-boot image and its SPL with official u-boot tools, using am335x_evm_defconfig and the arm-linux-gnueabihf toolchain. tio perfectly integrates XMODEM and YMODEM protocols. I then move to my u-boot folder and launch the hostilities.

I use XMODEM-CRC send and select the MLO file, which serves as my SPL when I boot with my SDs or when I flash to eMMC.

The transfer is successful.

tio -b 115200 /tmp/vtty
[10:00:06.585] tio c43d2f6
[10:00:06.585] Press ctrl-a q to quit
[10:00:06.585] Connected to /tmp/vtty
CCCCC
[10:00:08.525] Please enter which X modem protocol to use:
[10:00:08.525]  (0) XMODEM-1K send
[10:00:08.525]  (1) XMODEM-CRC send
[10:00:08.525]  (2) XMODEM-CRC receive
[10:00:09.237] Send file with XMODEM-CRC
[10:00:10.949] Sending file 'MLO'
[10:00:10.949] Press any key to abort transfer
................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................|
[10:00:23.952] Done

But on the socat side, we see messages from the CPU signaling serial failures.

.j.].|..........n6~UNt^...>....Bypassing DPLL failed 0x%x
.DPLL locking failed for 0x%x
.Clock enable failed for 0x%p idlest 0x%x
._.> 2026/02/12 10:00:21.000648910  length=1 from=870 to=870
.< 2026/02/12 10:00:21.000648993  length=133 from=82220 to=82352
.k.DDR3 H/W leveling incomplete with errors
.ti-musb-peripheral.ti-musb-host.gpio_omap.i2c_omap.ns16550_serial.resetting ...
.A335B..> 2026/02/12 10:00:21.000665928  length=1 from=871 to=871
.< 2026/02/12 10:00:21.000666032  length=133 from=82353 to=82485
.l.ONE.A33515BB.SKU#02.A335X_SK.A335BNLT.A335PBGL.A335_ICE.Bad EEPROM or unknown board, cannot configure pinmux..No AC power, switc.}> 2026/02/12 10:00:21.000682981  length=1 from=872 to=872
.< 2026/02/12 10:00:21.000683062  length=133 from=82486 to=82618
.m.hing to default OPP
.ti_i2c_eeprom_init failed
.boot_os.1.5.00A1.tps65217_reg_write failure
.tps65217_voltage_update failure
.BBU.> 2026/02/12 10:00:21.000699980  length=1 from=873 to=873
.< 2026/02/12 10:00:21.000700082  length=133 from=82619 to=82751
.n.G1.ddr_vtt_en.am335x-evm.am335x-bone.am335x-boneblack.am335x-pocketbeagle.am335x-evmsk.am335x-bonegreen.am335x-icev2.am335x-sanc.A> 2026/02/12 10:00:21.000716995  length=1 from=874 to=874
.< 2026/02/12 10:00:21.000717086  length=133 from=82752 to=82884
.o.loud-bbe-lite.am335x-sancloud-bbe-extended-wifi.am335x-sancloud-bbe.omap_hsmmc.eth_cpsw.ti,am3352-cpsw-phy-sel.cpsw,am33xx.fdt_r..> 2026/02/12 10:00:21.000733999  length=1 from=875 to=875
.< 2026/02/12 10:00:21.000734084  length=133 from=82885 to=83017
.p.oot: %s
.SPL: fdt_shrink_to_minimum err - %d
.SPL: arch_fixup_fdt err - %d
.U-Boot.Trying to boot from %s
.SPL: Unsupported BootM.> 2026/02/12 10:00:21.000750990  length=1 from=876 to=876
.< 2026/02/12 10:00:21.000751074  length=133 from=83018 to=83150
.q. Device!
.SPL: failed to boot from all boot devices
.
U-Boot SPL 2024.01 (Feb 11 2026 - 11:56:32 +0000)
.Can't load %s: No load %y> 2026/02/12 10:00:21.000768120  length=1 from=877 to=877
.< 2026/02/12 10:00:21.000768219  length=133 from=83151 to=83283
.r.address and no buffer
.%s: Skip load '%s': image size is 0!
.Cannot get image data/size
.fdt./images.kernel.loadables.%s: can't .l> 2026/02/12 10:00:21.000785619  length=1 from=878 to=878
.< 2026/02/12 10:00:21.000785757  length=133 from=83284 to=83416
.s.load image loadables index %d (ret = %d)
.load_simple_fit.spl_load_simple_fit.spl: ymodem err - %s
.Loaded %lu bytes
.UART.The E.F> 2026/02/12 10:00:21.000802844  length=1 from=879 to=879
.< 2026/02/12 10:00:21.000802942  length=133 from=83417 to=83549
.t.xpected Linux image was not found. Please check your NAND configuration.
.Trying to start u-boot now...
.NAND.yes.autoload.No Et.9> 2026/02/12 10:00:21.000820149  length=1 from=880 to=880
.< 2026/02/12 10:00:21.000820239  length=133 from=83550 to=83682
.u.hernet devices found
.ethact.Problem booting with BOOTP
.usb_ether.USB eth.eth device.architecture.compression.operating system.!4> 2026/02/12 10:00:21.000837199  length=1 from=881 to=881

I manage to initialize the sending procedure of the u-boot image by YMODEM on the serial port via tio, but no follow-up and especially, no sign of life on socat, except for the console inputs. As can be read in the boot attempt, the SPL dies and fails to initialize the memory needed for u-boot.img.

The click

My tio config seems correct: 115200 baud, 8 data bits, 1 stop bit, no parity.

After checking my power supply, switching to my lab supply, changing cables, doing a meticulous check of the solder joints, I decide to dig deeper into the injected file. Since a hexdump is always instructive, I decide to dissect the head of the MLO.

hexdump -C MLO | head -n 20
00000000  40 00 00 00 0c 00 00 00  00 00 00 00 00 00 00 00  |@...............|
00000010  00 00 00 00 43 48 53 45  54 54 49 4e 47 53 00 00  |....CHSETTINGS..|
00000020  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00000040  c1 c0 c0 c0 00 01 00 00  00 00 00 00 00 00 00 00  |................|
00000050  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
00000200  d8 75 01 00 00 04 2f 40  0f 00 00 ea 14 f0 9f e5  |.u..../@........|
00000210  14 f0 9f e5 14 f0 9f e5  14 f0 9f e5 14 f0 9f e5  |................|
00000220  14 f0 9f e5 14 f0 9f e5  40 04 2f 40 40 04 2f 40  |........@./@@./@|
00000230  40 04 2f 40 40 04 2f 40  40 04 2f 40 40 04 2f 40  |@./@@./@@./@@./@|
00000240  40 04 2f 40 ef be ad de  fe ff ff ea 2d 03 00 ea  |@./@........-...|
00000250  00 00 0f e1 1f 10 00 e2  1a 00 31 e3 1f 00 c0 13  |..........1.....|
00000260  13 00 80 13 c0 00 80 e3  00 f0 29 e1 10 0f 11 ee  |..........).....|
00000270  02 0a c0 e3 10 0f 01 ee  98 00 9f e5 10 0f 0c ee  |................|
00000280  06 00 00 eb 22 00 00 eb  1e 04 00 eb 15 0f 07 ee  |...."...........|

Interesting. We clearly see some kind of header, relatively empty lines, many identical lines [0x20 to 0x40], [0x50 to 0x200]. But above all, a visual break: at line 0x200, life begins. An entry point, then 14 f0 9f e5 patterns that remind me of assembly.

A simple search for CHSETTINGS on Kagi was enough to land on a Texas Instruments forum mentioning the 0x200-byte header. So it’s confirmed, MLO is not UART friendly. https://e2e.ti.com/support/processors-group/processors/f/processors-forum/207888/description-of-the-chsettings-header

The processor didn’t reject the MLO file, it tried to execute the graphic/binary header as code!

Cutting the header

If it’s not the MLO that we must send to the ROM code, I’ll have to find the messenger. After some well-targeted research, I come across the Texas Instruments doc about u-boot and UART. Marvelous! And we can read: “In some cases we support loading SPL and U-Boot over the console UART. You will need to use the spl/u-boot-spl.bin and u-boot.img files to boot.” The Uart ROM expects u-boot-spl.bin and not MLO, which is a MultiMediaCard Load Object. MLO is nothing more than u-boot-spl.bin to which a header has been added. A simple fd -HI -e bin u-boot-spl command allows me to locate the u-boot-spl.bin. The rest is absolutely smooth.

[10:02:28.843] Connected to /tmp/vtty
CCCCCCCCCCCCCCCC
[10:15:04.937] Please enter which X modem protocol to use:
[10:15:04.937]  (0) XMODEM-1K send
[10:15:04.937]  (1) XMODEM-CRC send
[10:15:04.937]  (2) XMODEM-CRC receive
[10:15:05.956] Send file with XMODEM-CRC
[10:15:12.937] Sending file 'spl/u-boot-spl.bin'
[10:15:12.937] Press any key to abort transfer
............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................|
[10:15:25.855] Done

U-Boot SPL 2024.01 (Feb 11 2026 - 11:56:32 +0000)
Trying to boot from UART
CC
[10:15:42.173] Send file with YMODEM
[10:15:46.120] Sending file 'u-boot.img'
[10:15:46.120] Press any key to abort transfer
............................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................|
[10:18:04.924] Done
Loaded 1452836 bytes


U-Boot 2024.01 (Feb 11 2026 - 11:56:32 +0000)

CPU  : AM335X-GP rev 2.1
Model: TI AM335x BeagleBone Black
DRAM:  512 MiB
Core:  160 devices, 18 uclasses, devicetree: separate
WDT:   Started wdt@44e35000 with servicing every 1000ms (60s timeout)
NAND:  0 MiB
MMC:   OMAP SD/MMC: 0, OMAP SD/MMC: 1
Loading Environment from FAT... ** Bad device specification mmc 0 **
<ethaddr> not set. Validating first E-fuse MAC
Net:   eth2: ethernet@4a100000using musb-hdrc, OUT ep1out IN ep1in STATUS ep2in
MAC de:ad:be:ef:00:01
HOST MAC de:ad:be:ef:00:00
RNDIS ready
, eth3: usb_ether
Hit any key to stop autoboot:  0
...
=>

OK, u-boot is alive!

=> mmc info
Device: OMAP SD/MMC
Manufacturer ID: fe
OEM: 4e
Name: P1XXXX
Bus Speed: 48000000
Mode: MMC High Speed (52MHz)
Rd Block Len: 512
MMC version 4.5
High Capacity: Yes
Capacity: 3.6 GiB
Bus Width: 8-bit
Erase Group Size: 512 KiB
User Capacity: 3.6 GiB
Boot Capacity: 16 MiB ENH
RPMB Capacity: 128 KiB ENH
Boot area 0 is not write protected
Boot area 1 is not write protected
=> ls mmc 1:1

0 file(s), 0 dir(s)

=> ls mmc 1:2
<DIR>       4096 .
<DIR>       4096 ..
<DIR>      16384 lost+found
=> mmc dev 0
switch to partitions #0, OK
mmc0 is current device
=> mmc info
Device: OMAP SD/MMC
Manufacturer ID: 0
OEM: 3432
Name: 00000
Bus Speed: 48000000
Mode: SD High Speed (50MHz)
Rd Block Len: 512
SD version 3.0
High Capacity: Yes
Capacity: 7.4 GiB
Bus Width: 4-bit
Erase Group Size: 512 Bytes
=> ls mmc 0:0
=> ls mmc 0:1
    70112   am335x-boneblack.dtb
    66650   am335x-bone.dtb
    66878   am335x-bonegreen.dtb
            extlinux/
   110016   MLO
  1450036   u-boot.img
  8632336   zImage

6 file(s), 1 dir(s)

=>

My SD card is well recognized and the eMMC is empty. Perfect!

Conclusion

I looked for a complex solution when the acronym was whispering that I wasn’t on the right device: indeed MLO stands for MultiMediaCard Load Object. MMCs are for SD cards and eMMCs. My mistake was mixing up UART boot and SD boot, and therefore using the MLO as a raw SPL. And this was confirmed by several LLMs (Claude Code, Gemini and Grok). As for the SD card, the mystery remains: the hardware was working, but the ROM code refused to load this MLO from the SD, even though it was valid.

This first UART boot brought me a lot. Here are the lessons I’ve learned:

  • Beware of AI for low-level: Models often confuse use cases (SD vs UART).

  • Know what you’re sending: Always ask “Who’s going to read this file?” (A file system or the RAM directly?).

  • The importance of raw logs: Without the serial port, the board would have ended up in the trash.

  • Docs & Logs are the only truths.

Next step: get a “ROM-native” SD boot. The SD is readable (U-Boot sees mmc 0), so I’m going to test a forced SD boot with eMMC neutralized and a reconstructed FAT where MLO is copied first, to check for a ROM constraint rather than a hardware problem.