The RISC OS mouse pointer

My first ARM machine was an Archimedes way back in about 1987. I received the first unit sold in New Zealand. At some point my machine started running Acorn’s RISC OS. For me, some of the amazing things about RISC OS were anti-aliased outline fonts, a string-sound generator which worked without a data table and a really nice mouse pointer. There is a fairly recent article on The Register which talks about how it came to be.

Anyway, for U-Boot I’ve chosen to use this same pointer for the mouse. It has good contrast and has a cheerful colour schema. Of course you can change it if you like, but see what you think!




Measuring expo performance

Expo is U-Boot’s menu- and GUI-layout system. It provides a set of objects like text, images and menu items. Expo allows these objects to be positioned on the display. Most importantly it can render the objects onto the display.

Input delays

The typical loop polls the expo for input (keyboard or mouse), does any required updates and then renders the expo on the display. This works fine, but is it efficient?

Recent work to add mouse support to expo exposed some shortcomings in the polling process. For example, keyboard input was waiting for up to 100ms for a key to be pressed in the EFI app. The original goal of this delay was to allow for escape sequences (such as are used by arrow keys, for example) to be fully captured in a single poll. But it slows the process down considerably.

This problem was fixed by commit a5c5b3b2fb6 (“expo: Speed up polling the keyboard”).

When to sync?

Another problem that came up was that the video system has its own idea of when to ‘sync’ the display. On most hardware this is a manual process. For sandbox, it involves drawing the contents on the U-Boot framebuffer on the SDL surface / display. On x86 devices it involves updating the framebuffer copy, used to improve performance with write-combining memory. On ARM devices it involves flushing the cache. Once these operations are completed, the changes become visible for the user.

In most cases syncing happens based on a a timer, e.g. a few times a second. In normal use this is fine since we don’t want to waste time updating the display when nothing has changed. A sync is also performed when U-Boot is idle, but in a tight polling loop it may never actually become idle.

In reality, expo should know whether anything has changed and some recent work has started the process of implementing that, making use of the video-damage work. In any case, when a mouse is present, expo wants to be as responsive as possible, so the mouse moves smoothly, rather than jerking from place to place 5 times a second.

Expo mode and manual sync

To resolve this a recent series in Concept adds support for an ‘expo mode’, where expo is in charge of syncing. It allows expo to initiate a video sync when it decides it wants to. The video sync is then done completely under expo control and the timer is not used.

Checking the frame rate

As it turns out, these changes exposed various problems with expo. To help with this a new ‘expo test’ mode was added in another series. This shows the frame rate and other useful information on the top right of the display, like this:

To enable it set the expotest environment variable to 1. From then on, your expo will show this information on the top right:

  • Frame number
  • Frames per second (rolling average over 5 seconds)
  • Render time in milliseconds
  • Sync time (i.e. display update)
  • Poll time (i.e. capturing keyboard and mouse input)

The information should help you track down any slow-downs in your drivers and expo itself!




Dealing with test images

Testing boot scenarios in very important but there are quite a lot of them! Concept currently has about 13 images containing various partitions and files used by the bootstd tests. These are listed in test.dts, for example:

	aliases {
...
		mmc7 = "/mmc7";
		mmc8 = "/mmc8";
...
	};

	/* This is used for Android boot image v4 tests */
	mmc7 {
		status = "disabled";
		compatible = "sandbox,mmc";
		filename = "mmc7.img";
	};

	/* This is used for Android boot image v2 tests. */
	mmc8 {
		status = "disabled";
		compatible = "sandbox,mmc";
		filename = "mmc8.img";
	};

All images are built by test_ut_dm_init_bootstd(), but most of these are disabled by default, to avoid making sandbox unwieldy and confusing. It also means that a test only has to worry about its particular disk image (plus a few common ones) and doesn’t have to be be updated every time a new test case is added in the code base.

But sometimes it is useful to enable a particular image. Tests do this individually, by enabling a devicetree node and then binding a device to it. But there is no way to do this from the command line. The only way is to edit test.dts to remove the status = "disabled" line for a node.

Until now. A very simple addition to sandbox allows an image to be enabled from the command line: the sb devon command. Simply specify the device name (e.g. mmc11) and it will set up a device. From then on the device appears as normal, so it can be used with the bootflow command, ls, etc.

When you have finished with the device sb devoff disables it again, putting things back to normal. Here is an example:

=> mmc list
mmc2: 2 (SD)
mmc1: 1 (SD)
mmc0: 0 (SD)
=> ls mmc b:1
** Bad device specification mmc b **
Couldn't find partition mmc b:1
=>
=> sb devon mmc11
Device 'mmc11' enabled
=> mmc list
mmc2: 2 (SD)
mmc1: 1 (SD)
mmc0: 0 (SD)
mmc11: 11 (SD)
=> ls mmc b:1    
            extlinux/
        7   initrd.img-6.8.0-53-generic
     1620   vmlinuz-6.8.0-53-generic

2 file(s), 1 dir(s)

=> sb devoff mmc11
Device 'mmc11' disabled
=> mmc list       
mmc2: 2 (SD)
mmc1: 1 (SD)
mmc0: 0 (SD)
=> 

Give it a try!




Unlocking Disks Earlier: Basic LUKS1 Support Arrives in U-Boot

A new 24-patch series in Concept that introduces basic support for unlocking LUKS1 encrypted partitions directly within U-Boot. This is a foundational step toward a more integrated and user-friendly full-disk encryption (FDE) experience.

🤔 The Problem with “Late” Unlocking

Traditionally, FDE on Linux systems is handled late in the boot process. U-Boot loads a kernel and an initial ramdisk (initramfs), and it’s the initramfs’s job to prompt the user for a passphrase and unlock the main root filesystem.

This common approach works, but it has several drawbacks:

  • Firmware is blind: U-Boot has no way of knowing if the boot will succeed until long after it has handed off control.
  • Confusing user experience: The passphrase prompt appears late in the boot sequence, sometimes after the vendor logo has disappeared.
  • No integrated UI: It’s not possible to create a single, polished firmware UI that handles both boot selection and disk decryption.
  • Inflexible for automation: In VM environments where a key might be known in advance, there’s no way for the firmware to use it, so the ramdisk must handle this through attestation, etc.
  • Ramdisk is required: You can’t boot from an encrypted disk unless you also use a ramdisk to perform the unlock.

đź’ˇ What’s New: The luks Command

This patch series takes a small step to improve this by bringing decryption capabilities into U-Boot itself. The new feature set is centered around the luks command, which allows U-Boot to interact with LUKS-encrypted partitions.

The command introduces three main subcommands:

  1. luks detect: Checks if a given partition is a valid LUKS device.
  2. luks info: Parses and displays the LUKS header metadata. This works for both LUKS1 and LUKS2 partitions, thanks to a new simple JSON parser included in the series for handling the LUKS2 header.
  3. luks unlock: unlocks a LUKS1 partition using a provided passphrase.

Here’s a look at it in action:

=> luks detect mmc 1:2
LUKS1 encrypted partition detected

=> luks info mmc 1:2
Version:         1
Cipher name:     aes
Cipher mode:     cbc-essiv:sha256
Hash spec:       sha256
Payload offset:  4096 sectors
Key bytes:       32

=> luks unlock mmc 1:2 mysecretpassphrase
Unlocked LUKS partition as blkmap device 'luks-mmc-1:2'
=>

🔓 Accessing Decrypted Data via blkmap

Once a partition is unlocked, how do you access the data? The luks unlock command integrates with U-Boot’s blkmap subsystem.

When a partition is successfully unlocked, a new virtual blkmap device is created. This device provides read-only access to the decrypted data on-the-fly.

This means you can now use standard U-Boot commands to read files directly from the encrypted partition, just as if it were a normal, unencrypted disk:

=> ls blkmap 0 /
          ./
          ../
          lost+found/
2481008   vmlinuz-6.8.0-53-generic
1616      initrd.img-6.8.0-53-generic

=> ext4load blkmap 0 ${kernel_addr_r} /vmlinuz-6.8.0-53-generic
2481008 bytes read in 64 ms (37.0 MiB/s)
=>

This simple feature allows U-Boot to load a kernel, read a configuration file, or access any other data from an encrypted volume before booting.

🛠️ Under the Hood: What Made This Possible

There is quite a bit of foundational work included in this series:

  • Crypto: The mbedtls library in U-Boot was enhanced to enable PKCS#5 (PBKDF2) functions, which are essential for deriving keys from passphrases in LUKS. A fix for AES-192 and AES-256 key-size handling was also included.
  • blkmap Enhancement: The blkmap driver was extended with a new blkmap_map_crypt() function to handle the on-the-fly decryption.
  • Testing: A large portion of the series is dedicated to building solid testing infrastructure. This includes updates to the Docker image, CI configuration and new Python test helpers (fs_helper.py) to create, encrypt, and test against real LUKS1 and LUKS2 disk images.

This series lays the groundwork for a more secure and streamlined boot process. While full LUKS2 unlock support and read-write access are topics for another day, this is a step forward.





Pass a boot command to QEMU!

The build-qemu script provides lots of useful features and is an easy way to run U-Boot under QEMU with an OS, with or without video, etc. Now in Concept it is possible to pass a boot command!

The -b/–bootcmd option creates a special ‘file’ within QEMU that contains the requested command. Then U-Boot uses an event in main.c to check for that file and read it. The command is then executed instead of the autoboot command. In effect, it provides a way to control what U-Boot does when it starts up.

Possible uses may include setting the bootmeth order to boot using extlinux instead of EFI.

Note that this does not affect the preboot command.

The main patch is the addition of a new EVT_BOOTCMD event which is emitted in main_loop() before starting the main CLI loop. Under the hood, QEMU provides the information in its internal filesystem, the the opt/u-boot/bootcmd file. You can see this using the ‘qfw’ command:

$ ./scripts/build-qemu -a x86 -rs -b 'echo hi'

U-Boot Concept SPL 2025.10
Trying to boot from SPI


U-Boot Concept 2025.10

CPU:   QEMU Virtual CPU version 2.5+
DRAM:  512 MiB
Core:  19 devices, 12 uclasses, devicetree: separate
Loading Environment from FAT... ** Bad device specification virtio 0 **
Model: QEMU x86 (Q35)
Net:   eth0: virtio-net#0

Hit any key to stop autoboot:  0 
hi
=> qfw list
    Addr     Size Sel Name
-------- -------- --- ------------
       0        0  20 bios-geometry                                           
       0        0  21 bootorder                                               
1ec21000       14  22 etc/acpi/rsdp                                           
1ec21040    20000  23 etc/acpi/tables                                         
       0        4  24 etc/boot-fail-wait                                      
       0       28  25 etc/e820                                                
       0       18  26 etc/smbios/smbios-anchor                                
       0      13b  27 etc/smbios/smbios-tables                                
       0        1  28 etc/smi/features-ok                                     
       0        8  29 etc/smi/requested-features                              
       0        8  2a etc/smi/supported-features                              
       0        6  2b etc/system-states                                       
       0     1000  2c etc/table-loader                                        
       0        0  2d etc/tpm/log                                             
       0     2400  2e genroms/kvmvapic.bin                                    
       0        7  2f opt/u-boot/bootcmd                                      
=> 

For now this option is only supported on x86, but ARM support should land in the next few weeks.

The build-efi script also supports this option for the app, but it is implemented in a completely different way. It creates an environment file on the FAT filesystem which U-Boot can read when it starts up. A later series provides this feature which is has also landed in Concept.

The ability to pass information from the host is a useful feature in QEMU. Keep it in mind if you are working in an emulation environment!




ACPI Firmware Performance Data Table

U-Boot Concept now has preliminary support for the ACPI Firmware Performance Data Table (FPDT). It is included in the ‘bootstage and script enhancement’ series. The OS can look at this table to figure out how long the firmware took to boot.

Only the ‘basic boot’ record is supported. It contains the following fields:

  • reset_end – Timestamp for the end of reset. This is set to the timestamp when U-Boot starts, or strictly speaking, when it sets up bootstage.
  • loader_start – Timestamp for when U-Boot starts loading the OS (not supported)
  • loader_exec – Timestamp for when U-Boot starts OS execution. This presupposes the EFI loader, i.e. starting the OS does not actually mean that U-Boot is finished. For now this is not supported.
  • ebs_entry – Timestamp for when the OS (or EFI app) calls exit-boot-services (not supported)
  • ebs_exit – Timestamp for when U-Boot completes the exit-boot-services call (not supported)

All timestamps are 64-bit values in microseconds with 0 assumes to be the end of the reset signal.

As you can see from the above, most of the fields are not currently filled in. Further work will be needed to plumb this into U-Boot’s EFI loader. As with the BGRT (blog link) the FPDT is added to the ACPI tables provided by QEMU, when running under emulation.




Some thoughts on Claude

I’ve been experimenting with AI coding tools, mostly Claude, for the last few months. Here are some thoughts on my experience so far.

Things I have tried

So far I have tried using Claude for many different tasks:

  • Solving a coding problem, such as creating a Python script to scan header files
  • Writing tests for existing code
  • Checking patches before I send them
  • Writing drafts for blog posts
  • Figuring out bugs
  • Adjusting code to make tests pass

First, a caveat. I am a beginner at using this technology and do not spend much time following leaders in this space. I started using Claude based on a family recommendation and am far from being any sort of expert.

The good

When I first tried out creating a new command in Claude, it produced code which compiled and looked like U-Boot code. This seemed quite magical to me and i left the computer with a feeling of awe and bewilderment. I have a basic understanding of how AI operates but it is amazing to see it producing code like this. Also the inference is very fast, sometimes producing hundreds of lines of code in a minute or less.

Claude works within your project directory. It can read and write commits, run tests, etc. For a code base like U-Boot with lots of tests, it is relatively easy to get it to make changes while ensuring that the tests still pass. It can execute most commands that you can. It asks first but you can add rules to a JSON file to give it permission to run things itself.

In most cases, Claude is able to solve the problem. For example, I wanted to build U-Boot as a library. I actually had some old patches for this, but I decided to give the take to Claude. I provided a lot of guidance on exactly how it should work, but Claude figured out the Makefile rules, iterating until things worked and fixing bugs that I found. I was able to make changes myself, tell Claude and it would build on them.

The easiest tasks for Claude are to extend and existing feature and the associated tests. The hardest are to refactor code in non-trivial ways.

One interesting case where Claude does well is dealing with conflicts when rebasing a series or applying patches. I have only tried this a few times but it has not made any mistakes yet. It is fairly slow, though.

The bad

Claude is a long way from perfect. It feels to me like a very junior but knowledgeable software engineer. It happily writes 600 lines of code when 300 would do. For larger tasks, the code feels extremely ‘flabby’ and a bit painful to read. Variables names are too long. Useless comments are added to obvious code. It happily creates highly redundant code instead of setting up a helper function first.

But with all of these problems, you can guide it to perform better. You can review the code and ask it to make changes and it happily does so. It might not be able to conceive of a refactoring that would improve code, but it can execute it.

For one tasks, Claude was able to create a 1200-line Python script in about an hour (with lots of prompting, etc.) that would have likely taken me 1-2 days (it is hard to be sure, since I didn’t do it). I then spent about 6 hours refacoring and rewriting the script after which I felt that the code wasn’t too bad. The end result was likely not as good as I would have done if I had written it myself from scratch, but it was fine.

Sometimes Claude gets stuck. It tends to happen when I am trying to get it to do something I’m not to sure about. It sometimes just gets the wrong end of the stick, or solves a slightly different problem. This is most common when first firing it up. After a bit of back and forth it settles in and makes progress

The ugly

What is Claude bad at? When running programs it can see the console output, but it cannot interact with the program, or at least I’m not sure how to make it do that. So for example it can run U-Boot sandbox with a timeout and it can even pass it commands to run. But it doesn’t know how to ‘type’ commands interactively.

Claude can sometimes change the test to fit the code, when the tasks was to fix the code. It tends to write non-deterministic tests, such as asserting that an integer is greater than zero, rather than checking for an exact value.

Claude can produce commit messages. It tends to be verbose by default but you can ask it to be brief. But I have not had much success in getting Claude to create its own commits after making a whole lot of changes.

Getting things done can sometimes just be very slow. I have not really compared the time to create high-quality, finished patches myself versus with Claude. That is something I must try, now that I am learning more about it.

My workflow

How do I actually use Claude? Well, first I figure out a design for what I want to do, typically a diagram or some notes. Then I ask Claude to perform an initial change to make a start. Then I build on that piece by piece until I have something working. I have not tried giving it a large challenge.

In some cases I create commits as I go, or even ask Claude to do this, but mostly I just create the whole thing and then manually build the commits afterwards. For code that Claude created in whole or part I add a Co-developed-by tag.

Breaking things into chunks saves time, I think. Cleaning up hundreds of lines of AI-generated code is very time-consuming and tedious. It is easier to tweak it a bit as I go.

A note on Gemini

I have tried Gemini CLI and sadly so far it has not been useful except in a few small ears. It is quite slow and seems to go into the weeds when any sort of moderate challenge. I expect this to change rapidly, so I keep trying it every now and then. I also use Gemini to create most of the featured images.

I have not tried OpenAI so far.

What do you think?

Leave a message in the comments if you have any thoughts or suggestions!




New video command and unified embedded image handling

U-Boot has long supported embedding a graphical image directly into the binary – like the boot logo and the recently added BGRT (Boot Graphics Resource Table) image for EFI systems. But the way these images were handled was a bit of a mixed bag, with different patterns for different images and custom boilerplate for each one.

A new 6-patch series cleans this up, introducing a unified infrastructure for embedded images along with new commands to work with them.

What’s new

Unified image infrastructure

Previously, images were handled through ad-hoc Makefile rules that looked for specific patterns like _logo.bmp or _image.bmp. Each image required custom accessor macros and boilerplate code.

The new approach moves all embedded images into a single drivers/video/images/ directory and automatically generates linker list entries for them. This makes it trivial to add new images – just add obj-y += myimage.o to the Makefile and reference it using video_image_get(myimage, &size) or video_image_getptr(myimage).

The linker list infrastructure ensures that all images are discoverable at runtime, which enables the new video images command described below.

New video command

A new video command has been added with four subcommands:

  • video setcursor <col> <row> – Set cursor position (equivalent to existing setcurs command)
  • video puts <string> – Write string at current position (equivalent to existing lcdputs command)
  • video write -p [<col>:<row> <string>...] – Write string at a given position, either a character or a pixel position
  • video images – List all images compiled into U-Boot

The existing standalone setcurs and lcdputs commands remain available for backward compatibility.

Example usage

=> video images
Name                       Size
-------------------- ----------
bgrt                      43926
u_boot                     6932

Total images: 2

=> video setcursor 10 5
=> video puts "Hello U-Boot!"
=> video write -p a3:34 "Pixels"

The payoff

This series removes 46 lines of duplicate accessor code while adding about 500 lines total (mostly documentation and tests). But the real win is in maintainability:

  • Simpler to extend: Adding a new embedded image now requires just a single line in a Makefile
  • Discoverable: The video images command shows what’s available at runtime
  • Better organized: All images live in drivers/video/images/ rather than scattered across the tree
  • Consistent API: One pair of macros works for all images

The series also brings comprehensive documentation for the video commands (which previously had none) and adds tests to ensure everything works correctly.

If you’ve ever wanted to add a custom boot logo or wondered what images are built into your U-Boot binary, this series makes both much easier!




Enhancing EFI Boot and Developer Experience

We’ve just rolled out a series of updates aimed at improving the U-Boot EFI application, with a special focus on streamlining the testing and debugging process, particularly for ARM platforms. This batch of 24 patches introduces several quality-of-life improvements, from better debugging tools to more robust boot procedures. Let’s dive into the key changes.


Streamlining the Boot Process with ‘Fake Go’ 🚀

One of the standout features in this release is the introduction of a ‘fake go’ option for the boot process. Previously available only for tracing, this feature is now a standalone debugging tool enabled by CONFIG_BOOTM_FAKE_GO.

When you initiate a boot with the ‘fake go’ flag (e.g., bootflow boot -f or bootm fake), U-Boot performs all the necessary steps to prepare for booting an OS—loading the kernel, setting up the device tree, and preparing memory—but stops just short of jumping to the OS. This allows you to inspect the system’s state at the final moment before handoff, which is invaluable for debugging complex boot issues without needing a full OS boot cycle.


Pager Improvements for Better Interaction đź“„

The console pager is a useful tool, but it can be cumbersome when you’re working without a serial console or need to quickly bypass lengthy output. We’ve introduced two new ways to control the pager on the fly:

  • Quit and Suppress (q): Pressing q at the pager prompt will now immediately stop all further output for the current command. This is perfect for when you’ve seen what you need and want to return to the prompt without sitting through pages of text.
  • Bypass Session (Q): Pressing Q will put the pager into bypass mode for the rest of your U-Boot session, allowing all subsequent commands to print their full output without interruption.

These small changes make console interaction much more fluid and give you greater control over command output.


Key Fixes and Enhancements 🛠️

Alongside these major features, this series includes a number of other smaller updates:

  • Safer Image Relocation on ARM: We’ve improved how kernel images are relocated on ARM. Instead of moving the image to a static offset, which could risk overwriting other critical data like the device tree, U-Boot now uses the LMB (Logical Memory Block) library to safely allocate a new, unused region of memory.
  • Improved Debugging Output: We’ve added more detailed debug messages throughout the boot process, especially in FIT image handling and device tree selection, making it easier to trace the boot flow and diagnose issues.
  • Cleaner ATAGs Messaging: The often-confusing “FDT and ATAGS support not compiled in” error has been clarified. U-Boot will now correctly report when a device tree is missing, preventing developers from going down the wrong path when debugging.
  • CI and Build Fixes: A few patches have been included to fix a bug in our automated release script that was causing CI failures, ensuring our development and release processes remain smooth.

These updates continue the development of the EFI app, while benefiting others boards as well.




Automating the U-Boot Concept Release Cycle


U-Boot Concept releases provide regular, time-based snapshots of our development progress, offering a stable point for users and developers. Up until recently, creating these releases involved a series of manual steps that were time-consuming and prone to human error.

To improve this process, we’re happy to announce a new, fully automated release workflow for U-Boot Concept. This system uses GitLab CI/CD and a custom Python script to handle the entire release process, from version calculation to final tagging, ensuring consistency and reliability.


A Predictable Schedule

The automation is built around a strict, predictable schedule that defines when final and release candidate (RC) versions are created. This removes any ambiguity about when to expect a new release.

  • Final Releases: Occur on the first Monday of every even-numbered month (February, April, June, August, October, December). The version number corresponds to the year and month, such as 2025.10.
  • Release Candidates (RCs): A series of three release candidates are created every two weeks leading up to a final release:

    • rc1: 6 weeks before the final release.
    • rc2: 4 weeks before the final release.
    • rc3: 2 weeks before the final release.

  • Dead Period: The time between a final release and the next rc1 is considered a “dead period” where no automated releases occur. This provides a development window before the next stabilization cycle begins.

The Automation in Action

The entire process is managed by a scheduled GitLab CI/CD pipeline that runs periodically to check if a release is due. Here’s a step-by-step look at what happens on a release day:

  1. Date Check & Version Calculation: The pipeline executes a Python script (scripts/release_version.py), which acts as the brains of the operation. The script checks the current date against the release schedule. If it’s not a designated release Monday, the job simply exits.
  2. Makefile Version Bump: If it is a release day, the script calculates the new version (e.g., 2025.12-rc1) and automatically updates the VERSION, PATCHLEVEL, and EXTRAVERSION variables in the project’s Makefile.
  3. Automated Commit: The CI job commits the modified Makefile directly to the master branch with a standardized message, such as chore: Bump version for release candidate 2025.12-rc1.
  4. Documentation Update: The script then updates the release history in doc/develop/concept_releases.rst, adding an entry for the new version along with the corresponding commit SHA. It also intelligently updates the “Next Release” section to show the schedule for the upcoming final release. This change is also committed and pushed automatically.
  5. Tag and Release: Finally, the pipeline uses the official release-cli tool to create a formal Git tag (e.g., c2025.12-rc1) and a corresponding GitLab Release. This provides a clear, permanent marker for the release, complete with a title and description. The ‘c’ prefix makes it clear that this is a Concept release, not the main tree.

Benefits of Automation

This new workflow brings several key advantages to the U-Boot Concept project:

  • Consistency: Every release is generated using the exact same procedure, ensuring uniformity.
  • Reliability: The risk of human error—like typos in version numbers or forgetting a step—is eliminated.
  • Transparency: The entire process is defined in code, and the release history is now automatically documented and kept up-to-date for everyone to see.
  • Efficiency: Developer time is freed from the manual chores of release management, allowing more focus on development and innovation.

This move to an automated system marks a significant step in improving our release management and ensuring that U-Boot Concept remains a dependable and predictable development platform.