web/lukegbcom: add 2023-08-08-uefi-boot-for-mochabin
This commit is contained in:
parent
7a8614d2f7
commit
d8de67e55a
1 changed files with 269 additions and 0 deletions
269
web/lukegbcom/posts/2023-08-08-uefi-boot-for-mochabin.md
Normal file
269
web/lukegbcom/posts/2023-08-08-uefi-boot-for-mochabin.md
Normal file
|
@ -0,0 +1,269 @@
|
||||||
|
---
|
||||||
|
title: "UEFI Boot for Mochabin"
|
||||||
|
date: 2023-08-08
|
||||||
|
layout: Post
|
||||||
|
hero: https://images.unsplash.com/photo-1506619216599-9d16d0903dfd
|
||||||
|
hero credit: https://unsplash.com/photos/XtUd5SiX464
|
||||||
|
hero credit text: "Jakub Dziubak"
|
||||||
|
classes:
|
||||||
|
header: header-black-gradient
|
||||||
|
---
|
||||||
|
|
||||||
|
MOCHAbin is a pretty capable ARM board - it has a quad core ARMv8 Cortex-A72 @
|
||||||
|
1400MHz, 8GB of RAM, 16GB of onboard eMMC, not to mention a _bunch_ of Ethernet
|
||||||
|
connectivity (1x 10Gb SFP+ cage, 1x 1Gb SFP cage, a WAN RJ45 port with PoE in,
|
||||||
|
and 4x LAN ports connected to an onboard switch chip).
|
||||||
|
|
||||||
|
The main downside for me, however, was the boot firmware. Out of the box, it
|
||||||
|
ships with a [pretty
|
||||||
|
ancient](https://github.com/globalscaletechnologies/u-boot-marvell/tree/u-boot-2020.10-gti)
|
||||||
|
build of U-Boot, which fails to properly support UEFI.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
There are other options: there are references to people using what I can only
|
||||||
|
assume to be some variant of Marvell's fork of EDK II to provide UEFI support.
|
||||||
|
However, I went down the route of trying to get a more modern version of U-Boot
|
||||||
|
working.
|
||||||
|
|
||||||
|
To cut a long story short, I got [Tow-Boot](https://tow-boot.org), a user-friendly
|
||||||
|
distribution of U-Boot with a pretty decent build system, to boot properly including
|
||||||
|
proper UEFI support.
|
||||||
|
|
||||||
|
## What works
|
||||||
|
|
||||||
|
- Environment storage in SPI flash
|
||||||
|
- Reading (and booting) from:
|
||||||
|
- eMMC
|
||||||
|
- USB
|
||||||
|
- EFI boot
|
||||||
|
|
||||||
|
## What's not tested
|
||||||
|
|
||||||
|
- The SFP+/SFP cages
|
||||||
|
- The WAN NIC
|
||||||
|
- SATA
|
||||||
|
- PCIe
|
||||||
|
|
||||||
|
## What doesn't work
|
||||||
|
|
||||||
|
- Any of the 4 "LAN" ports -- I think they require bringup of the switch chip,
|
||||||
|
and I haven't tried configuring that properly yet
|
||||||
|
- Using the actual `hw_info` SPI block that's supposed to hold the Ethernet MAC
|
||||||
|
addresses and PCB serial number.
|
||||||
|
|
||||||
|
## Building an image
|
||||||
|
|
||||||
|
If you want to build an image yourself, you'll need a Linux system with a
|
||||||
|
working install of [Nix](https://nixos.org). This doesn't need to be NixOS, Nix
|
||||||
|
on Debian should do just fine. You can even (probably) use Nix inside a Docker
|
||||||
|
container if you'd like.
|
||||||
|
|
||||||
|
Got that together? Alright:
|
||||||
|
|
||||||
|
1. Grab https://github.com/lukegb/Tow-Boot. The `lukegb/globalscale-mochabin`
|
||||||
|
branch should be the default, and it's what you'll need.
|
||||||
|
1. Run `nix-build -A globalscale-mochabin-8gb` (if you have the 8GB RAM
|
||||||
|
variant), or `nix-build -A globalscale-mochabin-4gb` (if you have the 4GB
|
||||||
|
variant).
|
||||||
|
1. Wait.
|
||||||
|
1. You should now have a `result` symlink that points to a directory with some
|
||||||
|
files in it. The one you probably want is
|
||||||
|
`result/binaries/Tow-Boot.spi.bin`, which is the build of Tow-Boot that uses
|
||||||
|
the SPI flash for storing the U-Boot environment.
|
||||||
|
|
||||||
|
At this point, you can use the instructions in "Recovering" below to use
|
||||||
|
`mvebu64boot` to just boot this once, to see what it's like, or continue below
|
||||||
|
to flash it as your main bootloader.
|
||||||
|
|
||||||
|
## Flashing
|
||||||
|
|
||||||
|
Apologies in advance: many of these steps _should_ be automated, but _aren't_.
|
||||||
|
There are various steps where you should take notes or backups of things so you
|
||||||
|
can restore your device to a working state later. Please do that, and copy them
|
||||||
|
somewhere safe!
|
||||||
|
|
||||||
|
You will need:
|
||||||
|
|
||||||
|
- a USB stick that you can wipe
|
||||||
|
- a micro-USB cable to use to connect to the MOCHAbin via USB-serial
|
||||||
|
|
||||||
|
### Getting set up
|
||||||
|
|
||||||
|
1. Wipe your memory stick. Put a ext2<sup>\*</sup> filesystem on it, and copy
|
||||||
|
`result/binaries/Tow-Boot.spi.bin` to it.
|
||||||
|
1. Turn off the MOCHAbin.
|
||||||
|
1. Unplug any other USB devices from the MOCHAbin, and connect your memory
|
||||||
|
stick.
|
||||||
|
1. Start your terminal emulator. The MOCHAbin runs at 115200 bps.
|
||||||
|
1. Turn on the MOCHAbin. When prompted to interrupt boot, press a key.
|
||||||
|
1. You should now have a `Marvell>>` prompt.
|
||||||
|
|
||||||
|
### Making sure everything is in place
|
||||||
|
|
||||||
|
1. Run `usb start`. You should get some output ending in `scanning usb for storage devices... 1 Storage Device(s) found`.
|
||||||
|
1. Run `usb part`. You should see your single, ext2 filesystem.
|
||||||
|
1. Run `ext2ls usb 0:1`. You should see the content of your filesystem,
|
||||||
|
including `Tow-Boot.spi.bin`.
|
||||||
|
|
||||||
|
### Backing up the SPI flash
|
||||||
|
|
||||||
|
1. Run `sf probe`. This should report `SF: Detected w25q32bv with page size 256 Bytes, erase size 4 KiB, total 4 MiB`. This is the information about
|
||||||
|
your SPI flash. *If your SPI flash is not 4MiB, stop!*
|
||||||
|
1. Run `sf read $kernel_addr_r 0 0x400000`. This should report `device 0 whole chip` and then hang for 10s or so.
|
||||||
|
1. Run `ext4write usb 0:1 $kernel_addr_r /backupspi.img 0x400000`. This
|
||||||
|
should report `File System is consistent`, and then complete after between
|
||||||
|
6 and 15s.
|
||||||
|
|
||||||
|
### Backing up the environment block
|
||||||
|
|
||||||
|
1. Run `env export -t $kernel_addr_r`. This will appear to do nothing.
|
||||||
|
1. Run `ext4write usb 0:1 $kernel_addr_r /backupenv.txt $filesize`. This
|
||||||
|
should again report `File System is consistent`, then complete after a few
|
||||||
|
seconds.
|
||||||
|
|
||||||
|
### Noting down key environment variables
|
||||||
|
|
||||||
|
1. Run `env print ethaddr eth1addr eth2addr pcb_sn`.
|
||||||
|
1. Copy and save the output somewhere. **You'll need this later.**
|
||||||
|
|
||||||
|
### Flashing the new image
|
||||||
|
|
||||||
|
1. Erase your SPI flash: run `sf erase 0 0x400000`. This ensures you don't
|
||||||
|
have any of the existing U-Boot environment lingering around. This will
|
||||||
|
appear to hang for a few tens of seconds, then report `SF: 4194304 bytes @ 0x0 Erased: OK`.
|
||||||
|
1. Flash the new image: run `bubt Tow-Boot.spi.bin spi usb`.
|
||||||
|
|
||||||
|
### Rebooting and setting the environment again
|
||||||
|
|
||||||
|
1. Turn the power off, then on again
|
||||||
|
1. When prompted to interrupt boot, hit Escape or press Ctrl-C.
|
||||||
|
1. You'll now be presented with the Tow-Boot menu. Scroll down to `Firmware Console` using the arrow keys and hit enter.
|
||||||
|
1. Now instead of `Marvell>> `, you should get a `=> ` prompt.
|
||||||
|
1. For each of `ethaddr`, `eth1addr`, `eth2addr` and `pcb_sn`, run `env set $VARIABLE_NAME $THE_VARIABLE_VALUE_YOU_NOTED_DOWN_EARLIER`
|
||||||
|
1. Run `env save`. This should eventually print `OK`.
|
||||||
|
1. Turn the power off, then on again.
|
||||||
|
|
||||||
|
### Congratulations!
|
||||||
|
|
||||||
|
You now have Tow-Boot. If you have a UEFI-compatible boot medium (either on the
|
||||||
|
eMMC or over USB, for instance), it will Just Work(TM).
|
||||||
|
|
||||||
|
1. Take the memory stick and back up `backupspi.img` and `backupenv.txt`
|
||||||
|
somewhere safe.
|
||||||
|
|
||||||
|
<sup>\*</sup> Technically this could be ext4. I suggest ext2 because modern
|
||||||
|
Linux distros will enable some ext4 extensions that won't work by default, and
|
||||||
|
this is very temporary.
|
||||||
|
|
||||||
|
## Using an up-to-date DTB
|
||||||
|
|
||||||
|
U-Boot will automatically discover new DTBs from the boot partition. I have the
|
||||||
|
following setup (my EFI System Partition is mounted at `/boot`; adjust
|
||||||
|
appropriately if yours is `/boot/efi`):
|
||||||
|
|
||||||
|
```
|
||||||
|
/boot
|
||||||
|
/boot/dtb
|
||||||
|
/boot/dtb/marvell
|
||||||
|
/boot/dtb/marvell/armada-7040-mochabin.dtb
|
||||||
|
```
|
||||||
|
|
||||||
|
I copy this file from the DTBs shipped with my Linux kernel build on update; if
|
||||||
|
you don't do this then you will (I think?) inherit the DTB used by U-Boot.
|
||||||
|
|
||||||
|
## Recovering
|
||||||
|
|
||||||
|
Oops! Something went wrong and you need to recover. No sweat. The Marvell
|
||||||
|
BootROM supports sending it an image via X-Modem, and you can do that over the
|
||||||
|
USB-Serial interface.
|
||||||
|
|
||||||
|
The tool I've been using for doing this is
|
||||||
|
[`mvebu64boot`](https://github.com/pali/mvebu64boot), which is super simple. If
|
||||||
|
you have Nix, then this is available in recent `nixpkgs-unstable`, but this is
|
||||||
|
super trivial to build without Nix: just clone that somewhere and use `make`.
|
||||||
|
|
||||||
|
Make sure you don't have any terminal emulators open on the port, and run:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
$ mvebu64boot -t -b path/to/flash-image.img /dev/ttyUSB0
|
||||||
|
|
||||||
|
# for instance:
|
||||||
|
$ mvebu64boot -t -b backupspi.img /dev/ttyUSB0
|
||||||
|
|
||||||
|
# or:
|
||||||
|
$ mvebu64boot -t -b result/binaries/Tow-Boot.spi.bin /dev/ttyUSB0
|
||||||
|
|
||||||
|
# or, using the bootloader images available from Globalscale's FTP (ftp://76.80.10.5/Downloads/Mochabin/bootloader_for_mochabin_hw-rev-1-5-0_20220905/):
|
||||||
|
$ mvebu64boot -t -b mochabin-bootloader-ddr4-8g-mvddr-41927ee-atf-277d4b6b-uboot-b794de0054-20220905-rel.bin /dev/ttyUSB0
|
||||||
|
```
|
||||||
|
|
||||||
|
(where ttyUSB0 is replaced with whatever device name you have for the flash
|
||||||
|
image you're booting)
|
||||||
|
|
||||||
|
Once this prints `Sending boot pattern...`, powercycle the MOCHAbin. You should
|
||||||
|
then get `BootROM is ready for image file transfer`, followed by a
|
||||||
|
rapidly-increasing percentage. This will take a few minutes - first the image
|
||||||
|
prolog needs to be sent, then there's a `Waiting for BootROM...` stage, then
|
||||||
|
the image itself. You'll then be presented with a serial terminal. Use `Ctrl-\ c` to exit.
|
||||||
|
|
||||||
|
This works for booting the Tow-Boot images you built, or the SPI flash backup
|
||||||
|
image I repeatedly reminded you to make.
|
||||||
|
|
||||||
|
To return to the previous image, boot to the U-Boot console (either the
|
||||||
|
built-in Marvell one or a Tow-Boot one), insert a memory stick containing the
|
||||||
|
image to flash from (I suggest ext2 again, for the same reasons as above) and
|
||||||
|
run `bubt backupspi.img spi usb`.
|
||||||
|
|
||||||
|
## Getting things working
|
||||||
|
|
||||||
|
NOTE: if you're just interested in building your own image, you can ignore
|
||||||
|
everything from this point on.
|
||||||
|
|
||||||
|
Getting things working was all relatively simple: upstream U-Boot 2022.07 and
|
||||||
|
2023.07 have pretty much all the hardware support that's required to boot this
|
||||||
|
board. The main thing missing is a device tree.
|
||||||
|
|
||||||
|
### Device trees
|
||||||
|
|
||||||
|
Device trees are effectively a description of what hardware is where: this
|
||||||
|
avoids some of the automatic configuration, and simplifies a bunch of
|
||||||
|
customization required to get things booting. The Linux kernel has a bunch of
|
||||||
|
these - and we're in luck, because Globalscale have been paying Sartura to
|
||||||
|
upstream a bunch of the hardware enablement required to get the MOCHAbin to
|
||||||
|
boot well
|
||||||
|
(https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/arm64/boot/dts/marvell/armada-7040-mochabin.dts).
|
||||||
|
|
||||||
|
There's one problem here, however: this DTS doesn't work inside the U-Boot
|
||||||
|
tree. U-Boot tries to be compatible with whatever Linux is doing so that device
|
||||||
|
trees are as portable as possible from the Linux source to the U-Boot tree, but
|
||||||
|
the specific naming of the `compatible` arguments is important for drivers, and
|
||||||
|
some of the includes are in different places.
|
||||||
|
|
||||||
|
Instead, I opted to take the device tree [from their version of
|
||||||
|
U-Boot](https://github.com/globalscaletechnologies/u-boot-marvell/blob/u-boot-2020.10-gti/arch/arm/dts/armada-7040-mochabin.dts),
|
||||||
|
and hack it together until it worked on a more recent version.
|
||||||
|
|
||||||
|
### Porting their device tree forwards
|
||||||
|
|
||||||
|
To give Globalscale credit, this mostly worked without a hitch. There are a few
|
||||||
|
differences (`phy-mode = "sfi";` needed to become `phy-mode = "10gbase-r";`),
|
||||||
|
but for the most part this... just worked.
|
||||||
|
|
||||||
|
I haven't tested a bunch of the functionality that I didn't need: in particular,
|
||||||
|
I don't plan on PXE booting or using any network functionality at startup. I also
|
||||||
|
don't have any drives connected over the internal M.2 SATA interface, so I couldn't
|
||||||
|
test that either.
|
||||||
|
|
||||||
|
### Future work
|
||||||
|
|
||||||
|
I'm hoping to, eventually:
|
||||||
|
|
||||||
|
- Try to get the Linux DTS working on U-Boot and upstream it.
|
||||||
|
- Get the 4 RJ45 LAN ports working.
|
||||||
|
- Check that the SATA M.2 port actually works.
|
||||||
|
- Get writable EFI variables working?
|
||||||
|
- Upstream my build to Tow-Boot proper.
|
||||||
|
- Getting the flashing steps automated in the Tow-Boot installer.
|
||||||
|
|
||||||
|
So stay tuned. Maybe.
|
Loading…
Reference in a new issue