Modernising Allocation: U-Boot Upgrades to dlmalloc 2.8.6

For over two decades—since 2002—U-Boot has relied on version 2.6.6 of Doug Lea’s malloc (dlmalloc, old docs) to handle dynamic memory allocation. While reliable, the codebase was showing its age.

In a massive 37-patch series, we have finally updated the core allocator to dlmalloc 2.8.6. This update brings modern memory efficiency algorithms, better security checks, and a cleaner codebase, all while fighting to keep the binary size footprint minimal for constrained devices.

Why the Update?

The move to version 2.8.6 isn’t just about bumping version numbers. The new implementation offers specific technical advantages:

  • Improved Binning: The algorithm for sorting free chunks is more sophisticated, leading to better memory efficiency and less fragmentation.
  • Overflow Protection: Robust checks via MAX_REQUEST prevent integer overflows during allocation requests, improving security.
  • Reduced Data Usage: The old pre-initialised av_[] array (which lived in the data section) has been replaced with a _sm_ struct in the BSS (Block Started by Symbol) section. This change reduces usage in the data section by approximately 1.5KB, a significant win for boards with constraints on initialised data.

The Battle for Code Size

One of the biggest challenges in embedded development is code size. Out of the box, the newer dlmalloc was significantly larger than the old version—a non-starter for SPL (Secondary Program Loader) where every byte counts.

To combat this, the patch series a Kconfig option to strip down the allocator for space-constrained SPL builds (specifically via CONFIG_SYS_MALLOC_SMALL).

Key optimisations include:

  1. NO_TREE_BINS: Disables complex binary-tree bins for large allocations (>256 bytes), falling back to a simple doubly-linked list. This trades O(log n) performance for O(n) but saves ~1.25KB of code.
  2. SIMPLE_MEMALIGN: Simplifies the logic for aligned allocations, removing complex retry fallback mechanisms that are rarely needed in SPL. This saves ~100-150 bytes.
  3. NO_REALLOC_IN_PLACE: Disables the logic that attempts to resize a memory block in place. Instead, it always allocates a new block and copies data. This saves ~500 bytes.

With these adjustments enabled, the new implementation is actually smaller than the old 2.6.6 version on architectures like Thumb2.

Keeping U-Boot Features Alive

This wasn’t a clean upstream import. Over the last 20 years, U-Boot accrued many custom modifications to dlmalloc. This series carefully ports these features to the new engine:

  • Pre-relocation Malloc: Support for malloc_simple, allowing memory allocation before the main heap is initialised.
  • Valgrind Support: Annotations that allow developers to run U-Boot sandbox under Valgrind to detect memory leaks and errors.
  • Heap Protection: Integration with mcheck to detect buffer overruns and heap corruption.

New Documentation and Tests

To ensure stability, a new test suite (test/common/malloc.c) was added to verify edge cases, realloc behaviours, and large allocations. Additionally, comprehensive documentation has been added to doc/develop/malloc.rst, explaining the differences between pre- and post-relocation allocation and how to tune the new Kconfig options.

Next Steps

The legacy allocator is still available via CONFIG_SYS_MALLOC_LEGACY for compatibility testing, but new boards are encouraged to use the default 2.8.6 implementation.




Where did I come from? Introducing the backtrace command

Debugging embedded bootloaders can often feel like working in the dark. When execution crashes or behaves unexpectedly, the first question an engineer usually asks is, “How did I get here?”

Of course, one should always have a JTAG debugger ready to press into service, but so few boards provide a JTAG header.

To help, a recent series introduces a standardised Backtrace Library and a corresponding command to U-Boot. While currently implemented for the sandbox architecture, the design provides a generic API that paves the way for runtime stack traces across other architectures in the future.

The Power of Context

Previously, getting a call stack in U-Boot often meant attaching an external debugger (like GDB) or manually instrumenting code with print statements. This series adds runtime support to look at the backtrace directly from the console.

With the new backtrace command, developers can instantly see the call stack, complete with function names, source filenames, and line numbers.

How it Works

The implementation is split into a generic library and architecture-specific drivers:

  1. Generic API (include/backtrace.h): Provides functions like backtrace_init() to collect the stack and backtrace_show() to print it.
  2. Sandbox Implementation: For sandbox, we use the host’s libbacktrace (bundled with GCC). This allows U-Boot to read the DWARF debug information generated during the build to resolve addresses into human-readable symbols.

Cleaner Output

Raw backtraces can be messy, often printing absolute paths that clutter the terminal. A nice touch in this series (specifically Patch 9) is the automatic stripping of the source tree prefix.

Before:

run_command_list() at /home/user/workspace/u-boot/common/cli.c:168

After:

run_command_list() at common/cli.c:168

Example Usage

Once enabled with CONFIG_CMD_BACKTRACE, using the tool is straightforward:

=> backtrace
backtrace: 14 addresses
  backtrace_show() at lib/backtrace.c:18
  do_backtrace() at cmd/backtrace.c:17
  cmd_process() at common/command.c:637
  run_list_real() at common/cli_hush.c:1868
  parse_stream_outer() at common/cli_hush.c:3207
  parse_string_outer() at common/cli_hush.c:3257
  run_command_list() at common/cli.c:168
  sandbox_main_loop_init() at arch/sandbox/cpu/start.c:153
  board_init_r() at common/board_r.c:774
  ...

Looking Ahead

Currently this feature relies on libbacktrace and is available only for sandbox. However, the infrastructure is now in place. We are looking forward to seeing architecture maintainers implement the backing backtrace_init logic for ARM, RISC-V, and x86 to bring this powerful debugging capability to physical hardware.




Cleaning up the Nulls: Introducing ofnode Stubs for Non-DT Builds

In the world of U-Boot, the Device Model (DM) and Device Tree (DT) are the standard for hardware description. However, U-Boot runs on a massive variety of hardware, including constrained systems where full Device Tree support (OF_REAL) might be disabled.

A recent patch cleans up how the core handles these “no-Device-Tree” scenarios, ensuring that code remains clean, compilable, and safe even when the DT is missing.

The Problem: When the Tree Falls

When OF_REAL is disabled, there is logically no point in trying to find nodes, read properties, or traverse a tree—everything is effectively null.

Previously, handling this required scattering #ifdef guards throughout driver code or dealing with linking errors if a driver attempted to call an ofnode function that wasn’t compiled in. This made the codebase harder to read and harder to maintain.

The Solution: Static Inline Stubs

The patch, dm: core: Create ofnode stubs when OF_REAL is disabled introduces a comprehensive set of static inline “stub” functions in include/dm/ofnode.h.

Here is the logic:

  1. Check Configuration: The header now checks #if CONFIG_IS_ENABLED(OF_REAL).
  2. Provide Stubs: If OF_REAL is off, it defines dummy versions of functions like ofnode_read_u32 or ofnode_next_subnode.
  3. Safe Returns: These stubs return safe default values—typically NULL, false, -ENOSYS, or -EINVAL—allowing the compiler to optimise the code paths without breaking the build.

This allows drivers to call ofnode functions blindly. If the DT is missing, the function simply returns an error code, and the driver handles it gracefully rather than the build failing.

The Cost: Pushing the Limits

While this approach significantly cleans up driver code, it comes with a trade-off. include/dm/ofnode.h has grown significantly with this patch.

As noted in the commit, the file is becoming “a little convoluted” due to the sheer volume of inline implementations and dual definitions. We are likely reaching the limit of how many static inlines can reasonably live in this single header! While this solution is better than the alternative (broken builds or messy #ifdefs in drivers), future work may require splitting the header file to keep the core API definitions digestible and organised.

Key Refinements

The patch also includes some “plumbing” adjustments:

  • Macro Relocation: Iterator macros like ofnode_for_each_compatible_node() were moved outside the OF_REAL condition. Since they rely on the newly created stubs, these loops will now simply do nothing on a non-DT system, rather than causing compiler errors.
  • Edge-Case Support: A specific adjustment in drivers/Makefile ensures ofnode.o is compiled when OF_REAL is enabled but the full DM is not, preserving necessary support for boards like the kontron-sl-mx6ul.



Introducing Codman: A Deep Dive into U-Boot Build Analysis

U-Boot is a massive project. With thousands of files, nearly endless configuration possibilities, and complex Kconfig dependencies, a single board configuration often only compiles a small fraction of the total source tree.

For developers and maintainers, this complexity often leads to difficult questions:

  • “I just enabled CONFIG_CMD_NET; how much code did that actually add?”
  • “How much bloat would I remove by disabling CONFIG_CMDLINE?”
  • “Which specific lines of this driver are active for my board?”

Simply searching for CONFIG_ macros or header inclusions is rarely enough. The build logic takes many forms—Makefile rules, #ifdefs, IS_ENABLED(), and static inlines—making static analysis tricky.

Enter Codman (Code Manager), a new tool designed to cut through this complexity by analysing the actual build artefacts generated by the compiler.

What is Codman?

Codman is a Python-based tool located in tools/codman/. It works out exactly which source files and lines of code are compiled and used for a specific board. It works by:

  1. Building the specified board (or using an existing build).
  2. Parsing .cmd files to find which source files were compiled.
  3. Analysing the source code (using a specialized unifdef) or object files (using DWARF tables) to figure out exactly which lines made it into the final binary.

Feature Highlight: Impact Analysis

One of Codman’s most powerful features is Impact Analysis. This allows you to explore “what if” scenarios without manually editing defconfig files or running menuconfig.

Using the -a (adjust) flag, you can modify the Kconfig configuration on the fly before the analysis runs. This is perfect for seeing exactly how much code a specific feature adds.

Example: Checking the impact of USB To enable the USB subsystem on the sandbox board and see how the code stats change:

./tools/codman/codman.py -b sandbox -a CMD_USB stats

Example: Disabling Networking To see what code remains active when networking is explicitly disabled:

./tools/codman/codman.py -b sandbox -a ~NET,NO_NET stats

Visualising the Build

Codman provides several ways to view your build data.

1. High-Level Statistics The stats command gives you a bird’s-eye view of your build size.

$ codman -b qemu-x86 stats
======================================================================
FILE-LEVEL STATISTICS
======================================================================
Total source files:    14114
Used source files:      1046 (7.4%)
Unused source files:   13083 (92.7%)

Total lines of code:  3646331
Used lines of code:    192543 (5.3%)

2. Directory Breakdown Use dirs to see which subsystems are contributing the most weight to your board.

$ codman dirs
BREAKDOWN BY TOP-LEVEL DIRECTORY
Directory        Files    Used  %Used  %Code     kLOC    Used
-------------------------------------------------------------
arch               234     156     67     72     12.3     8.9
board              123      45     37     25      5.6     1.4
cmd                 89      67     75     81      3.4     2.8
...

You can also break down the information by showing subdirectories (-s) or even individual files (-f).

$ codman -n -b qemu-x86 dirs --subdirs  -f
=======================================================================================
BREAKDOWN BY TOP-LEVEL DIRECTORY
=======================================================================================
Directory                                  Files    Used  %Used  %Code     kLOC    Used
---------------------------------------------------------------------------------------
arch/x86/cpu                                  20      15     75     85      3.8     3.2
  start.S                                    318     190   59.7     128
  cpu.c                                      399     353   88.5      46
  mp_init.c                                  902     877   97.2      25
  turbo.c                                    103      92   89.3      11
  lapic.c                                    158     156   98.7       2
  resetvec.S                                  18      18  100.0       0
  pci.c                                      100     100  100.0       0
  mtrr.c                                     455     455  100.0       0
  start16.S                                  123     123  100.0       0
  sipi_vector.S                              215     215  100.0       0
  ioapic.c                                    36      36  100.0       0
  call32.S                                    61      61  100.0       0
  qfw_cpu.c                                   86      86  100.0       0
  irq.c                                      366     366  100.0       0
  cpu_x86.c                                   99      99  100.0       0
arch/x86/cpu/i386                              4       4    100     98      1.4     1.4
  cpu.c                                      649     630   97.1      19
  interrupt.c                                630     622   98.7       8
  call64.S                                    92      92  100.0       0
  setjmp.S                                    65      65  100.0       0
arch/x86/cpu/intel_common                     18       6     33     23      3.3     0.8
  microcode.c                                187     183   97.9       4
  pch.c                                       23      23  100.0       0
  lpc.c                                      100     100  100.0       0

3. Line-by-Line Detail Perhaps the most useful feature for debugging configuration issues is the detail view. It shows you exactly which lines are active or inactive within a file.

$ codman -b qemu-x86 detail common/main.c
...
    24 | static void run_preboot_environment_command(void)
    25 | {
    26 | 	char *p;
    27 | 
    28 | 	p = env_get("preboot");
    29 | 	if (p != NULL) {
    30 | 		int prev = 0;
    31 | 
-   32 | 		if (IS_ENABLED(CONFIG_AUTOBOOT_KEYED))
-   33 | 			prev = disable_ctrlc(1); /* disable Ctrl-C checking */
    34 | 
    35 | 		run_command_list(p, -1, 0);
    36 | 
-   37 | 		if (IS_ENABLED(CONFIG_AUTOBOOT_KEYED))
-   38 | 			disable_ctrlc(prev);	/* restore Ctrl-C checking */
    39 | 	}
    40 | }
    41 | 
...

(Lines marked with - are not included in the build and show in a different colour)

Under the Hood: Unifdef vs. DWARF

Codman supports two methods for analysis:

  • Unifdef (Default): Simulates the C preprocessor to determine which lines are active based on CONFIG_ settings. It is fast (leveraging multiprocessing) and provides a great preprocessor-level view. It uses a patched version of unifdef that supports U-Boot’s IS_ENABLED() macros.
  • DWARF (-w): Rebuilds the project with debug info and analyses the DWARF line number tables. This is highly accurate for executable code but won’t count declarations or comments.

Getting Started

# Basic stats for sandbox
./tools/codman/codman.py -b sandbox stats

# Find unused files
./tools/codman/codman.py -b sandbox unused

# Extract only used sources to a new directory (great for minimal distributions)
./tools/codman/codman.py -b sandbox copy-used /tmp/minimal-tree

Check the documentation for more details!




Tidying up the FIT: Refactoring, Testing, and Shrinking U-Boot

Flattened Image Trees (FIT) are a cornerstone of modern U-Boot booting, offering a flexible way to package kernels, device trees, ramdisks, and firmware. However, the code responsible for printing information about these images—the output you see when running mkimage -l or iminfo—has been around for a long time.

As with any legacy code, it had become difficult to maintain. It lacked unit tests, relied on ad-hoc printing logic, and was cluttered within the massive boot/image-fit.c file.

This week, I submitted a 30-patch series titled “fit: Improve and test the code to print FIT info” to address these issues. Here is a look at what changed and why.

The Problem: Spaghetti Printing

Previously, the logic for printing FIT details was scattered. Functions manually handled indentation strings, and there were inconsistent printf calls for every property. If we wanted to change how a property was displayed or correct alignment, we had to touch multiple places in the code. Furthermore, there was no safety net; modifying the printing logic risked breaking the output parsing for users or scripts.

The Solution: Context and Helpers

The refactoring process followed a structured approach:

  1. Test First: Before touching the core logic, I added comprehensive tests. This includes a Python test (test_fit_print.py) that generates a FIT with various components (kernels, FDTs, signatures, loadables) and asserts the output is exactly as expected. This ensured that subsequent refactoring didn’t break existing functionality.
  2. Separation of Concerns: The printing code was moved out of boot/image-fit.c and into its own dedicated file, boot/fit_print.c.
  3. Context Structure: Instead of passing the FIT pointer and indentation strings recursively through every function, a new struct fit_print_ctx was introduced.
  4. Helper Functions: I introduced helpers like emit_label_val, emit_timestamp, and emit_addr. This replaced manual formatting with standardized calls, ensuring consistent alignment and handling of “unavailable” optional properties automatically.

AI-Assisted Development

An interesting aspect of this series is the use of AI assistance. You might notice the Co-developed-by: Claude tags in the commit log. The AI assisted in generating boilerplate, suggesting refactoring patterns, and ensuring coding standards were met, speeding up the iterative process of cleaning up the codebase.

The Results

The refactoring didn’t just make the code cleaner; it made it more efficient.

  • Binary Size Reduction: By removing duplicate printing logic and streamlining the flow, we see a binary-size reduction of approximately 320 bytes on aarch64 builds.
  • Better Output: The output columns are now strictly aligned, making it easier to read visually.
  • Maintainability: With the printing logic isolated and heavily tested, future changes to FIT reporting can be made with confidence.

The series is currently available on the mailing list for review.




An update on mouse support

Over the last few months (and since the last post) the mouse support in U-Boot Concept has matured quite a bit. The various performance improvements have had a big impact and the UI is now smooth and useable. Here’s a video:

So what’s next? Let’s look at a few topics.

Touchpads

So far touchpads are only supported in the EFI app, assuming that the underlying firmware enables this. On real hardware, such as the Qualcomm x1e laptops, this seems to work OK.

Support on other platforms requires a driver. The USB mouse driver (CONFIG_USB_MOUSE) might work, or it might not. If the touchpad is attached via I2C or SPI, then a different driver would be needed, perhaps with a special protocol for the device.

EFI app

So how do you the mouse or touchpad running on the EFI app? Just make sure that CONFIG_EFI_MOUSE is enabled when you build the U-Boot app, and all should be well.

Unfortunately the widely used ‘ovmf’ Debian package does not enable the mouse, or at least not properly. This is used for running EFI apps under QEMU. U-Boot’s build-efi script passes the required arguments, but this is not enough.

If you would like to build a version of EDK2 which supports the mouse / touchpad, it is quite simple. Just change these two files.

First, in OvmfPkg/Include/Dsc/UsbComponents.dsc.inc add this line, e.g. between UsbKbDxe and UsbMassStorageDxe so that it builds the mouse driver:

  MdeModulePkg/Bus/Usb/UsbMouseDxe/UsbMouseDxe.inf

Second, in OvmfPkg/OvmfPkgX64.fdf add this line, e.g. between UsbKbDxe and UsbMassStorageDxe so that the driver ends up in the firmware volume:

INF  MdeModulePkg/Bus/Usb/UsbMouseDxe/UsbMouseDxe.inf

Clicking in more places

So far, clicking in a lineedit object just places the cursor at the end of that object. Really it should set the cursor to the clicked position. This will become more important when the test editor is finished, since there may be a lot of text on the screen.

This is fairly simple to implement and should appear early in the new year.

Selecting text

A more ambitious feature would be text selection, where the mouse can be used to select part of a lineedit. This would need to be done as part of a copy/paste feature. It would make the text editor a little more functional, but it is not really core feature. If you are interested in implementing that, send an email to the U-Boot Concept mailing list!




Unlocking Modern Storage: U-Boot Adds LUKSv2 Support

We’re excited to announce that U-Boot concept has merged support for unlocking LUKSv2 encrypted partitions! This is a significant enhancement to U-Boot’s security capabilities, allowing it to handle the encryption standard used today by most current Linux distributions.

This 16-patch series (and a small follow-up) bring U-Boot up to speed with modern disk encryption, building on the existing luks unlock command.

Why LUKSv2?

While LUKSv1 was added initially, it was with the intention of taking this next step. LUKSv2 is the modern standard, offering superior security. The two key features U-Boot now supports from LUKSv2 are:

  1. Argon2id Key Derivation: LUKSv2 defaults to using Argon2id, the winner of the Password Hashing Competition. Unlike LUKSv1’s PBKDF2, Argon2id is a memory-hard function designed to be highly resistant to brute-force attacks using GPUs and ASICs. This series introduces the Argon2 library to U-Boot to handle this.
  2. XTS Cipher Mode: Support for the AES-XTS cipher mode (via mbedtls) has been added. XTS is the modern standard for disk encryption, providing stronger security guarantees than the older CBC mode.

The Implementation: A JSON-to-FDT Converter

One of the most interesting challenges in this series was handling the LUKSv2 metadata format. Unlike LUKSv1’s binary header, LUKSv2 stores its complex, hierarchical metadata as a JSON object.

As noted in the cover letter:

“One interesting part of this series is a converter from JSON to FDT, so that U-Boot’s existing ofnode interface can be used to access the hierarchical data in JSON text. This obviously results in quite a bit of new code, but it is more robust than trying to parse the text directly using strstr(), etc.”

This is the core of the new implementation. Instead of writing a new, complex JSON parser from scratch, a new function, json_to_fdt(), was created. This function parses the JSON text and converts it on-the-fly into a Flattened Device Tree (FDT) blob in memory.

From there, the LUKSv2 code can use U-Boot’s familiar and robust ofnode API (ofnode_find_subnode(), ofnode_read_string(), etc.) to navigate the metadata and retrieve keyslots, digests, and segment information. This approach is not only more reliable but also fits better within the existing U-Boot architecture.

How It Works

For the command interface, not much has changed. The existing luks unlock command just grows some new features:

  • It automatically detects whether the partition is LUKSv1 or LUKSv2.
  • If it’s LUKSv2, it will parse the JSON metadata.
  • If the keyslot uses Argon2id, it will use the new Argon2 library to derive the key.
  • If the partition uses XTS, it will use the newly enabled mbedtls functions to decrypt it.

Once unlocked, the encrypted partition is mapped as a blkmap device (e.g., blkmap 0), which you can then read from using standard commands like ext4load, fatload, or ls.

This work, along with the necessary documentation and test updates, makes U-Boot ready to boot from modern, secure, full-disk-encrypted systems.




Dumping expo contents

Expo is U-Boot’s forms and UI subsystem. It supports text, images and menus and a large expo can contain quite a bit of information. How do you debug and understand that? Tracing through large, linked data structures is not fun.

U-Boot Concept now has an expo_dump() function, designed to be called from your code when you are trying to figure out what is happening. It writes a dump of the expo to an abuf, something like the following:

Expo: name 'my menus'
  display lcd
  cons lcd.vidconsole_tt
  mouse (none)
  scene_id 0
  next_id 46
  req_width 0
  req_height 0
  text_mode 0
  popup 0
  show_highlight 0
  mouse_enabled 0
  mouse_ptr 0000000000000000
  mouse_size 0x0
  mouse_pos (0,0)
  damage (0,0)-(0,0)
  done 0
  save 0
  last_key_ms 4526828813
  video: 1366x768 white_on_black 0
  Theme:
    font_size 0
    white_on_black 0
    menu_inset 0
    menuitem_gap_y 0

Scenes:
  Scene 7: name 'main'
    title_id 0 ((none))
    highlight_id 0 ((none))
    Object 9 (logo): type image
      flags dirty
      bbox: (50,20)-(210,180)
      dims: 160x160
      Image: data ff000000
    Object 10 (text): type text
      flags dirty
      bbox: (400,100)-(526,140)
      dims: 126x40
      Text: str_id 20 font_name 'cantoraone_regular' font_size 40
        str 'my string'
    Object 11 (text): type text
      flags dirty
      bbox: (200,600)-(496,660)
      dims: 296x60
      Text: str_id 21 font_name 'nimbus_sans_l_regular' font_size 60
        str 'another string'
    Object 12 (text): type text
      flags size_valid, dirty
      bbox: (500,200)-(1000,350)
      dims: 477x240
      Text: str_id 22 font_name 'nimbus_sans_l_regular' font_size 60
        str 'this is yet
another string, with word-wrap and it goes on for quite a while'
    Object 13 (main): type menu
      flags size_valid, dirty
      bbox: (50,400)-(210,560)
      dims: 160x160
      Menu: pointer_id 45 title_id 14 manual 0
        Item 35: name 'item1' label_id 36 desc_id 37
        Item 40: name 'item2' label_id 41 desc_id 42
    Object 14 (title): type text
      flags dirty
      bbox: (50,400)-(122,418)
      dims: 72x18
      Text: str_id 24 font_name '(default)' font_size 0
        str 'Main Menu'
    Object 45 (cur_item): type text
      flags dirty
      bbox: (147,436)-(156,454)
      dims: 9x18
      Text: str_id 25 font_name '(default)' font_size 0
        str '>'
    Object 36 (label1): type text
      flags dirty
      bbox: (50,436)-(79,454)
      dims: 28x18
      Text: str_id 26 font_name '(default)' font_size 0
        str 'Play'
    Object 37 (item1-txt): type text
      flags dirty
      bbox: (227,436)-(316,454)
      dims: 89x18
      Text: str_id 27 font_name '(default)' font_size 0
        str 'Lord Melchett'
    Object 38 (item1-key): type text
      flags dirty
      bbox: (177,436)-(186,454)
      dims: 9x18
      Text: str_id 28 font_name '(default)' font_size 0
        str '1'
    Object 41 (label2): type text
      flags dirty
      bbox: (50,454)-(79,472)
      dims: 29x18
      Text: str_id 30 font_name '(default)' font_size 0
        str 'Now'
    Object 42 (item2-txt): type text
      flags dirty
      bbox: (227,454)-(316,472)
      dims: 70x18
      Text: str_id 31 font_name '(default)' font_size 0
        str 'Lord Percy'
    Object 43 (item2-key): type text
      flags dirty
      bbox: (177,454)-(186,472)
      dims: 9x18
      Text: str_id 32 font_name '(default)' font_size 0
        str '2'
    Object 15 (box): type box
      flags size_valid, dirty
      bbox: (500,200)-(1000,350)
      dims: 160x0
      Box: fill 0 width 3
    Object 16 (box2): type box
      flags 
      bbox: (0,0)-(0,0)
      dims: 160x0
      Box: fill 0 width 1
    Object 17 (editor): type textedit
      flags size_valid, dirty
      bbox: (100,200)-(400,650)
      dims: 289x54
      Textedit: str_id 23 font_name '(default)' font_size 0
    Object 18 (overlap): type text
      flags dirty
      bbox: (405,105)-(480,123)
      dims: 75x18
      Text: str_id 34 font_name '(default)' font_size 0
        str 'overlap text'

To print it, use puts() which has no line limits and will just dump the entire block to the console.

One nice thing about this dump is that you can compare it with a desired expo state.

What’s next? It would be nice to be able to move the mouse around and get information about the objects underneath the mouse. Another useful feature would be to write the dump to a file.

For now, it’s a start. Give it a try!




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!