<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom"><title>Codethink</title><link href="https://www.codethink.co.uk/" rel="alternate"></link><link href="https://www.codethink.co.uk/feeds/atom.xml" rel="self"></link><id>https://www.codethink.co.uk/</id><updated>2026-09-08T00:00:00+01:00</updated><entry><title>What is Deadline Scheduling in Linux?</title><link href="https://www.codethink.co.uk/articles/2026/deadline-scheduling.html" rel="alternate"></link><published>2026-09-08T00:00:00+01:00</published><updated>2026-09-08T00:00:00+01:00</updated><author><name>Roan Richmond</name></author><id>tag:www.codethink.co.uk,2026-09-08:/articles/2026/deadline-scheduling.html</id><summary type="html">&lt;p&gt;This article details how real-time scheduling works in Linux, specifically deadline scheduling, including its limitations and how to mitigate those limitations.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://www.ibm.com/think/topics/real-time-operating-system"&gt;Real-time operating systems&lt;/a&gt; are ubiquitous in industries such as automotive and aerospace where systems are required to operate under strict timing constraints with possible catastrophic consequences for failure.&lt;/p&gt;
&lt;p&gt;In real-time systems, tasks must be executed both with the correct behaviour and within precise timing constraints. In other words, a task is only executed correctly if it produces the correct logical result before some deadline. In a multi-tasking operating system such as Linux, a scheduler is responsible for coordinating access to hardware resources, such as the CPU. The Linux scheduler supports different scheduling policies depending on the workload, and for non-real-time workloads, scheduling policies such as &lt;code&gt;SCHED_OTHER&lt;/code&gt; and &lt;code&gt;SCHED_NORMAL&lt;/code&gt; are sufficient. For real-time workloads, the Linux scheduler supports some additional policies and adds support for pre-emption: temporarily pausing a running task to allow a high priority one to run instead.&lt;/p&gt;
&lt;p&gt;Due to the industry demands for real-time systems, this article explores Linux's capabilities to meet real-time schediuling constraints. This article details how real-time scheduling works in Linux, specifically deadline scheduling, including its limitations and how to mitigate those limitations.&lt;/p&gt;
&lt;p&gt;To schedule real-time tasks Linux provides two schedulers: the &lt;a href="https://github.com/torvalds/linux/blob/master/kernel/sched/rt.c"&gt;POSIX real-time scheduler&lt;/a&gt;, and the &lt;a href="https://github.com/torvalds/linux/blob/master/kernel/sched/deadline.c"&gt;deadline scheduler&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;strong&gt;POSIX real-time scheduler&lt;/strong&gt;, which provides the &lt;a href="https://man7.org/linux/man-pages/man7/sched.7.html"&gt;&lt;strong&gt;first-in-first-out&lt;/strong&gt; (FIFO) and &lt;strong&gt;round-robin&lt;/strong&gt; (RR)&lt;/a&gt; scheduling policies, schedules each task according to its priority. The task with the highest priority is scheduled first. These scheduling policies differ only when tasks have the same priority. The FIFO scheduler will schedule the task which arrived first running it till completion or until it yields. The RR scheduler will schedule tasks in a round-robin fashion, executing each task up until a maximum amount of time, at which point the next task execution is started and the current task is moved to the back of the queue.&lt;/p&gt;
&lt;p&gt;Deadline scheduling works very differently; it instead schedules each task according to the task’s deadline. The task with the promptest deadline will be served first. This means that when a task is requesting to be scheduled, it is required to provide parameters about its deadline and execution. These parameters are provided to the scheduler via the &lt;code&gt;sched_setattr&lt;/code&gt; syscall:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Period&lt;/strong&gt;: the time between providing the task it's runtime again, and the last time it was provided, in nanoseconds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Deadline&lt;/strong&gt;: the time from the start of the period by which the thread must have finished its execution for that period, in nanoseconds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Runtime&lt;/strong&gt;: the thread’s expected worst-case execution time in each period, in nanoseconds.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reclaim (optional)&lt;/strong&gt;: whether the kernel should provide the thread with unused CPU time in a period if the thread hadn’t finished its execution&lt;/p&gt;
&lt;p&gt;For example a process scheduled with:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;#define SCHED_DEADLINE 6

struct sched_attr attr = {0}; attr.size = sizeof(attr);
attr.sched_policy = SCHED_DEADLINE;

/* Configure time parameters in nanoseconds */
attr.sched_runtime = 10 * 1000 * 1000;  /* 10 ms CPU runtime */ attr.sched_deadline = 20 * 1000 * 1000; /* 20 ms relative deadline */ attr.sched_period = 30 * 1000 * 1000;   /* 30 ms period */
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Would repeat every 30 milliseconds, and would be finished by the 20th millisecond in each of those 30 millisecond blocks. The process would be allowed to run for 10 milliseconds. This can be visualised as:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Deadline scheduling visualisation" src="https://www.codethink.co.uk/articles/2026/deadline-diagram.webp"&gt;&lt;/p&gt;
&lt;p&gt;Although the deadline schedulers parameters look complex, they attempt to remove the complexity from the calling process. The scheduler hides the complexity of deciding task priority and fitting the task’s execution around other scheduled tasks.&lt;/p&gt;
&lt;p&gt;Because the deadline scheduler knows the required CPU time for each deadline task, it is able to stop the system becoming overloaded by implementing a check for each new schedule. This &lt;a href="https://docs.kernel.org/scheduler/sched-deadline.html#default-behavior"&gt;acceptance test&lt;/a&gt; by default works to keep the utilisation of a core at less than 95%, therefore allowing some CPU time for system tasks to run. This acceptance test is controlled by &lt;code&gt;kernel.sched_rt_runtime_us&lt;/code&gt; and &lt;code&gt;kernel.sched_rt_period_us&lt;/code&gt; &lt;code&gt;sysctl&lt;/code&gt; configs, which default to 950000 and 1000000 respectively.&lt;/p&gt;
&lt;p&gt;This is sufficient for a single-core system, however for a multi-core system this is not. &lt;a href="https://www.jstor.org/stable/169896"&gt;Dhall’s effect&lt;/a&gt; shows how the global deadline scheduler acceptance test is unable to schedule a task even though there is sufficient CPU time.&lt;/p&gt;
&lt;p&gt;This limitation can be avoided by manual partitioning of the system into &lt;a href="https://man7.org/linux/man-pages/man7/cpuset.7.html"&gt;‘cpusets’&lt;/a&gt; each containing one CPU. This is advantageous as it allows for deadline scheduling in a multicore system to have a higher overall utilisation, however it pushes the responsibility of managing this configuration onto the user.&lt;/p&gt;
&lt;p&gt;Common questions at this point are: how do these different schedulers interface with each other? Should the user manage the interface between deadline scheduled tasks and other tasks?&lt;/p&gt;
&lt;p&gt;The deadline scheduled tasks have a higher priority than any other tasks, including other real-time tasks such as FIFO or RR scheduled tasks. Therefore, for deadline scheduled tasks, there is no need to consider the current state of real-time or normal scheduled tasks. However, there is the risk of non-deadline tasks being pushed aside.&lt;/p&gt;
&lt;p&gt;This concern can also be addressed using ‘cpusets’ and solid system architecture, by dividing the CPUs into different sets depending on the systems requirements. For an 8-core machine, that needs to run deadline, round-robin and normal tasks the cores could be divided as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Core 1: Deadline tasks
Core 2: Deadline tasks
Core 3 and 4: RR tasks
Core 5 to 8: All other tasks
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The deadline scheduler is heavily associated with the &lt;a href="https://www.kernel.org/pub/linux/kernel/projects/rt/"&gt;PREEMPT_RT patch set&lt;/a&gt;, which was a separate patch set but has since &lt;a href="https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=baeb9a7d8b60b021d907127509c44507539c15e5"&gt;merged into the mainline Linux kernel&lt;/a&gt;. The aim of this patch set was to reduce the time that a lower priority task could block access to a core, while a higher priority task(s) waited in the wings. It works by reducing the time a task can run with &lt;a href="https://www.bitshift-dynamics.com/en/blog/real-time-linux-preempt-rt/"&gt;IRQs&lt;/a&gt; and preemption disabled.&lt;/p&gt;
&lt;p&gt;Overall this article has outlined Linux scheduling, specifically Linux deadline scheduling, some common pitfalls with it, and how it relates to the PREEMPT_RT patch set.&lt;/p&gt;</content><category term="2026"></category><category term="deadline scheduling"></category><category term="linux"></category></entry><entry><title>Real-Time Performance Optimisations for Multi-Axis Robotic Systems using copper-rs</title><link href="https://www.codethink.co.uk/articles/2026/copper-rs-real-time-robotics.html" rel="alternate"></link><published>2026-08-20T00:00:00+01:00</published><updated>2026-08-20T00:00:00+01:00</updated><author><name>George Lo</name></author><id>tag:www.codethink.co.uk,2026-08-20:/articles/2026/copper-rs-real-time-robotics.html</id><summary type="html">&lt;p&gt;Real-time execution is essential for safety-critical robotics systems in autonomous vehicles, surgical robots, and industrial automation. This article examines copper-rs as an alternative to ROS 2, presenting a case study on an 18 degrees of freedom hexapod robot.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Real-time execution is essential for safety-critical robotics systems in autonomous vehicles, surgical robots, and industrial automation. The real-time performance of traditional middleware frameworks are often &lt;a href="#Where-ROS-2-Struggles"&gt;constrained by design&lt;/a&gt; limitations which are difficult to address with minimal modifications to application code.&lt;/p&gt;
&lt;p&gt;Consequently, this article will explore the practicality of robotic frameworks outside of well-established ecosystems. Copper-rs is positioned as a direct competitor to ROS 2, offering OS-independent, real-time, deterministic scheduling. I discuss a case study on an open-loop, 18 degrees of freedom (DoF) hexapod robot, demonstrating copper-rs on a multi-axis robot that demands low latency in a high frequency message environment. I aim to provide empirical evidence justifying claims about determinism, observability, and runtime latency that make copper-rs uniquely suitable for real-time applications.&lt;/p&gt;
&lt;h2&gt;Real-time Considerations in Robotics&lt;/h2&gt;
&lt;h3&gt;Operating System&lt;/h3&gt;
&lt;p&gt;For real-time applications, the fairness of scheduling principles in default Linux (CFS and Round-Robin) are unsuitable and liable to priority inversions. Furthermore, locking primitives such as spinlock_t disable preemption, driving sentiment towards two soft real-time platforms: Linux with a PREEMPT_RT patch and QNX.&lt;/p&gt;
&lt;p&gt;Studies target QNX for multi-axis robotic systems which demand high bandwidth capability to drive numerous joints, though lack the empirical evidence necessary for widespread adoption. Conversely, the PREEMPT_RT patch in Linux uses forced-threaded interrupts, sleep spinlocks and real-time mutexes to construct a preemptive event-driven system kernel.This uncertainty in the current &lt;em&gt;best&lt;/em&gt; solution leaves a lot to be desired.&lt;/p&gt;
&lt;p&gt;copper-rs does not claim to be a replacement for Linux or RTOS. It exists within its own category, supporting both baremetal and GPOS targets. Accordingly, we consider copper-rs capacity to bypass OS constraints.&lt;/p&gt;
&lt;h3&gt;Middleware Frameworks&lt;/h3&gt;
&lt;p&gt;The supporting middleware may introduce latent behaviour. ROS 2 has maintained its position as the primary framework for robotic applications, offering modularity and distributed execution in a way that doesn't compromise on convenience. &lt;/p&gt;
&lt;p&gt;Complex behaviour in ROS 2 can be described by a network of topics and nodes that emphasise flexibility and loose, composable design. As a result, the default model for inter-process communication using the Data Distribution Service (DDS) protocol is unfit for real-time performance, suffering from copying overheads, data serialisation, and asynchronicity which significantly reduce throughput. &lt;/p&gt;
&lt;p&gt;Demand for time-critical solutions that guarantee performance and reliability have surged and copper-rs has emerged to bridge the real-time gap.&lt;/p&gt;
&lt;h2&gt;copper-rs: A Case Study into Rust-first Robotics&lt;/h2&gt;
&lt;h3&gt;Where ROS 2 Struggles&lt;/h3&gt;
&lt;p&gt;In ROS 2, communication is facilitated by a Data Distribution Service (DDS) layer. Nodes are mapped to processes in the underlying OS and execute behaviour through callbacks on a single/multi-threaded executor. In asynchronous mode, the execution of the callback is freed by delegating the sending process to the DDS. This is the first fundamental flaw in the ROS 2 framework: callbacks are executed without strict time or sequence ordering. OS scheduling, thread preemption and system load all affect callback execution in a way that breaks determinism.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of the ROS 2 system architecture showing nodes, executors, the abstract DDS layer, and OS threads" src="https://www.codethink.co.uk/articles/2026/ROS2_1.png"&gt;
&lt;strong&gt;Figure 1&lt;/strong&gt; Illustration of ROS 2 system. Source: &lt;a href="https://doi.org/10.1109/RTSS59052.2023.00026"&gt;https://doi.org/10.1109/RTSS59052.2023.00026&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;The second fundamental issue in ROS 2 is inter-process data transport. In the DDS layer, DataWriters and DataReaders are registered for callbacks to publish and receive messages from a topic. Messages must be serialised (expensive) and transported via the underlying transportation protocol, by default set to UDPv4. Every step introduces runtime latency and despite community effort for enhancements like EtherCAT (the underlying network protocol) and Agnocast (zero-copy communication), latency and jitter remains an issue in DDS.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of inter-process communication over TCP/UDP, showing a message copied from a user-space buffer, serialised, passed through kernel socket buffers between two processes, then deserialised" src="https://www.codethink.co.uk/articles/2026/ROS2_2.png"&gt;
&lt;strong&gt;Figure 2&lt;/strong&gt; Interprocess communication over TCP/UDP. Source: &lt;a href="https://doi.org/10.23919/ICCAS66577.2025.11301358"&gt;https://doi.org/10.23919/ICCAS66577.2025.11301358&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;copper-rs&lt;/h3&gt;
&lt;p&gt;Beyond ROS 2, competing frameworks have redefined the robotics execution layer seeking proven real-time performance. The 3rd of July 2026 marked the first stable release of copper-rs: a Rust-first robotics runtime that is designed with determinism as a first principle.&lt;/p&gt;
&lt;p&gt;The copper-rs runtime is categorically different to ROS 2, more closely resembling a time-triggered operating system than a middleware framework. In a typical workflow, tasks (nodes), payloads (messages), and bridges to middleware are statically described in a configuration file (copperconfig.ron) and checked at compile-time.&lt;/p&gt;
&lt;p&gt;Based on user-defined configuration, copper-rs generates a task graph and custom deterministic scheduler to execute tasks in a pre-computed order. Two lists are generated which form the order of execution: CopperList and CuTasks. &lt;/p&gt;
&lt;p&gt;&lt;img alt="Flowchart of the copper-rs task lifecycle, running from new and start through preprocess, process and postprocess to stop and drop, with only the process stage inside the time-critical section" src="https://www.codethink.co.uk/articles/2026/copper_rs_lifecycle.png"&gt;
&lt;strong&gt;Figure 3&lt;/strong&gt; copper-rs task lifecycle. Source: &lt;a href="https://github.com/copper-project/copper-rs/wiki/Task-Lifecycle"&gt;https://github.com/copper-project/copper-rs/wiki/Task-Lifecycle&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can read the rust docs about how copper-rs provides deterministic replay by serialising the internal state of tasks (freeze) &lt;a href="https://docs.rs/cu29/latest/cu29/cutask/trait.Freezable.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Figure 3 illustrates the task lifecycle and usage of the scheduler. All tasks in CuTasks are executed synchronously and communicate over shared buffers to reduce runtime latency. In the best effort section, high throughput jobs are executed to minimise the interference with the critical path. This is essential for visual perception, motion planning, and control to run at low-latency in the time-critical section. copper-rs' low latency and time-triggered scheduling principles make it ideal for real-time, multi-axis robotics systems. Consider the table below which provides an overview of optimisations to latency and jitter.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Consideration&lt;/th&gt;
&lt;th&gt;copper-rs&lt;/th&gt;
&lt;th&gt;ROS 2&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Execution order&lt;/td&gt;
&lt;td&gt;Synchronous task execution in a determined order&lt;/td&gt;
&lt;td&gt;Asynchronous, callback-oriented architecture mean that execution order is uncertain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;DDS overhead&lt;/td&gt;
&lt;td&gt;Data is only serialised at keyframes for resimulation. Zero-copy between tasks&lt;/td&gt;
&lt;td&gt;DDS suffers from copying overheads and expensive data serialisation that affect runtime latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data transport&lt;/td&gt;
&lt;td&gt;Tasks write directly into a buffer for the next task. Memory is organised to optimise cache usage and prefetches&lt;/td&gt;
&lt;td&gt;DDS relies on its underlying network protocol for data transport&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Scheduling&lt;/td&gt;
&lt;td&gt;Internal unified scheduler and filesystem: supports baremetal&lt;/td&gt;
&lt;td&gt;Scheduling on GPOS platforms struggle to guarantee deterministic execution&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Logging&lt;/td&gt;
&lt;td&gt;Binary serialisation using bincode, pre-allocated buffers and no string construction overhead&lt;/td&gt;
&lt;td&gt;Suffers from string formatting overhead. By default rosbag indexes an SQlite database affecting runtime latency&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Memory&lt;/td&gt;
&lt;td&gt;Zero allocation along the hotpath&lt;/td&gt;
&lt;td&gt;Publishers and subscribers allocate memory on the heap when handling variably-sized messages&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3&gt;Modularity and Observability&lt;/h3&gt;
&lt;p&gt;Complex behaviour in robotics often relies on sensors, cameras, and safety monitors on the critical path. In high data frequency environments we observe the declining performance of ROS 2. Developers choose to generalise behaviour and reduce log capture points to achieve lower-latency in production at the cost of modularity and observability. The same scenario is significantly less likely in copper-rs because it is significantly less latent by design, and has 12x higher logging throughput than ROS 2.&lt;/p&gt;
&lt;h3&gt;Thread Pools and Real-time Scheduling&lt;/h3&gt;
&lt;p&gt;copper-rs exposes parallel-rt and rt-scheduling for std targets, offering multi-threaded execution and real-time scheduling without sacrificing deterministic behaviour. Each thread pool's processor affinity, scheduling policy and on_error behaviour can be configured.&lt;/p&gt;
&lt;p&gt;Available scheduling policies are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Fair - Fair time-sharing (SCHED_OTHER / CFS)&lt;/li&gt;
&lt;li&gt;Nice - Fair scheduler with a niceness bias&lt;/li&gt;
&lt;li&gt;Fifo - SCHED_FIFO hard real-time&lt;/li&gt;
&lt;li&gt;RoundRobin - SCHED_RR, same as Fifo except threads at the same priority are time-sliced in turn&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can read more about parallel-rt and rt-scheduling in the &lt;a href="https://copper-project.github.io/copper-rs-book/thread-pools.html"&gt;copper-rs documentation&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;copper-rs on a Hexapod Robot&lt;/h2&gt;
&lt;p&gt;This section presents the findings of a 4 week case-study using copper-rs to drive an open-loop, 18DoF hexapod robot. Time constraints meant compromises were made to some of copper-rs core design principles. I provide an overview of copper-rs performance on multi-axis robotic systems based on anecdotal experience, evaluating the runtime quantitatively in a high message frequency environment.&lt;/p&gt;
&lt;h3&gt;Specification&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Servo2040 (RP2040)&lt;/li&gt;
&lt;li&gt;Raspberry Pi 5 8GB&lt;/li&gt;
&lt;li&gt;Zeee 2S Lipo Battery 5200mAh 7.4V 80C&lt;/li&gt;
&lt;li&gt;18 TD-8130MG 30 KG Digital Servos&lt;/li&gt;
&lt;li&gt;&lt;a href="https://makerworld.com/en/models/1973965-hexapod-spider-robot-with-servo-arduino#profileId-2122819"&gt;Leg model&lt;/a&gt; by Emre Kalem&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Hexapod model" src="https://www.codethink.co.uk/articles/2026/hexapod_2.jpg"&gt;
&lt;strong&gt;Figure 4&lt;/strong&gt; Hexapod robot model&lt;/p&gt;
&lt;h3&gt;How It Works&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;On initialisation, a source task (clock.rs) triggers the main loop at the target frequency and serialises (freeze) the current timeframe for resim. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The payload emitted by the source task is read from the buffer by the Gait task (gait.rs), which stores the description of the hexapod robot (Spyder) defined by the custom motion planner (spyder_ik). &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;gait.rs calls the motion planner to produce joint angles in radians based on the current command and emits the ServoFrame payload. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, the ServoMapper task maps the joint angles to pulse widths, encodes to bytes and emits a PWMFrame payload that can be sent over the serial to a Servo2040 (rp2040) board.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt="Hexapod performance metrics" src="https://www.codethink.co.uk/articles/2026/performance_analytics.png"&gt;
&lt;strong&gt;Figure 5&lt;/strong&gt; Hexapod runtime performance metrics&lt;/p&gt;
&lt;p&gt;Figure 5 shows performance metrics pulled live from the consolemon console monitor. This run represents the baseline without optimisation. Observe that the maximum latency for end-to-end is 730.364μs and the end-to-end jitter is 362.720μs. Furthermore, some tasks have achieved sub-microsecond latency. Average payload size is 144 bytes ([f64; 18]) at 30Hz. Note that the results have no direct comparison and are otherwise inconclusive.&lt;/p&gt;
&lt;p&gt;Copper-rs' capacity for real-time performance is worth further investigation. The framework is 'metamorphic' by design: for any given task graph copper-rs will generate a deterministic scheduler that unblocks time-critical tasks along the hot path.&lt;/p&gt;
&lt;p&gt;Combine scheduling with direct improvements to latency and it becomes abundantly clear why copper-rs should be considered alongside competing middleware. To raise areas for investigation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Directly improve performance of RP2040 by rewriting Micropython firmware in Rust&lt;/li&gt;
&lt;li&gt;Reduce data size by using smaller data types than f64&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In terms of real-time performance, the following are applicable given the project does not move to a baremetal approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Use copper-rs parallel-rt to isolate latency-critical work onto dedicated cores with real-time priority&lt;/li&gt;
&lt;li&gt;Define scheduling policies for thread pools with rt-scheduling&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Logging&lt;/h3&gt;
&lt;p&gt;&lt;img alt="copper-rs task graph showing the src clock source feeding the gait task and then servo_map, with zenoh and rp2040 bridges and a monitor sink" src="https://www.codethink.co.uk/articles/2026/dag.png"&gt;
&lt;strong&gt;Figure 6&lt;/strong&gt; copper-rs DAG for hexapod robot &lt;/p&gt;
&lt;p&gt;copper-rs offers various tooling that effectively demystifies the DAG concept. Run (just dag) to visualise data transportation as a series of tasks, bridges, and payloads.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Debug log output showing timestamped joint angle frames, serial writes and RP2040 monitor messages at roughly 25 hertz" src="https://www.codethink.co.uk/articles/2026/debug_logs.png"&gt;
&lt;strong&gt;Figure 7&lt;/strong&gt; Debug logs generated for serial bridge&lt;/p&gt;
&lt;p&gt;&lt;img alt="Foxglove workspace displaying copper-rs MCAP data, with a topic list showing message counts and frequencies alongside gait and servo_map payload panels" src="https://www.codethink.co.uk/articles/2026/foxglove.png"&gt;
&lt;strong&gt;Figure 8&lt;/strong&gt; Raw data sources in Foxglove using copper-rs MCAP export&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://www.codethink.co.uk/articles/2026/console_1.png"&gt;
&lt;strong&gt;Figure 9&lt;/strong&gt; No colcon build or launch files: execute with cargo run.&lt;/p&gt;
&lt;h3&gt;Serial Bridge and Zenoh Controller&lt;/h3&gt;
&lt;p&gt;copper-rs supports a plethora of middleware through the Bridges API. A bridge lets copper-rs talk to an external transport (serial, CAN, DDS, etc.) and facilitates communication with Zenoh, iceoryx, ROS 2 and more. Users should consider the effect on latency and determinism when using bridges, especially for real-time applications. A Zenoh bridge is used for teleoperation, broadcasting the command that dictates movement direction to the Gait task.&lt;/p&gt;
&lt;p&gt;Using the same bridge API, I define a serial bridge in (bridges.rs) responsible for reading and writing from the RP2040 resource.&lt;/p&gt;
&lt;h3&gt;Rust-centric Workflow&lt;/h3&gt;
&lt;p&gt;Copper-rs feels purposefully ergonomic: having compile-time checks makes iterative development easier and eliminates some of the gripes I had debugging ROS 2. Additionally, copper-rs utilises the speed of Rust to offer significant reductions to latency over other robotics frameworks.&lt;/p&gt;
&lt;p&gt;That being said, the adoption of Rust in the robotics ecosystem will be a long and arduous process. Comparatively, robotics has far fewer software engineers than any typical technological discipline. Familiarity with Python and C++ is widespread, so despite its drawbacks, ROS 2 wins in adoptability. The rigidity of copper-rs design principles mean it feels great in the final stages, but might take significant investment throughout the development cycle. &lt;/p&gt;
&lt;h2&gt;Hexapod Motion Planner&lt;/h2&gt;
&lt;p&gt;This project uses a custom, geometry-based motion planner that exposes a gait generator for hexapod robots with the following core features:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Home position, link lengths, angle resolution and fields that mathematically describe the robot are specified in the SpyderBuilder. &lt;/li&gt;
&lt;li&gt;The user calls build to construct a Spyder, exposing standing position, LUT generation and export functionality. &lt;/li&gt;
&lt;li&gt;Inverse kinematics, change of basis and LUT generation are handled for the end-user to simplify usage.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In the future, I plan to port from LUT generation to a live IK model and integrate sensor data. If you want to contribute, I would appreciate it!&lt;/p&gt;
&lt;p&gt;Going forward, I hope to create a hexapod runtime that works straight out of the box, for aspiring roboticists who are uninterested in the complexities of robotics frameworks. copper-rs' Rust oriented approach is surprisingly well suited — simply define the hexapod parameters in copperconfig.ron and run it with cargo run. Logging naturally follows, and with more time I would like to have simulation (Bevy) integrated into the project.&lt;/p&gt;
&lt;p&gt;&lt;img alt="" src="https://www.codethink.co.uk/articles/2026/rviz.png"&gt;
&lt;strong&gt;Figure 10&lt;/strong&gt; Rviz simulation of hexapod robot&lt;/p&gt;
&lt;h2&gt;Plans for Future Development&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Go baremetal with copper-rs&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If the entire copper-rs runtime can be run on a RP2350 why use the Pi 5? This is an open-loop hexapod robot that has no object awareness or visual perception so compute is no issue. This could significantly reduce runtime latency and jitter: no serialisation overhead for USB CDC and full control over resources and scheduling.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Go 100% Rust with Zenoh controller (telemetry) and RP2040 firmware&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The RP2040 firmware and controller could be written in Rust to remove Python dependency in this project. Micropython suffers from high memory usage and poor real-time performance which throttles the performance of the system.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a determinism regression test&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Inclusion that guarantees the binary produced on resimulation is bit-for-bit identical which is a great addition to CI.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In this article, I present a case study on an open-loop, 18 degrees of freedom (DoF) hexapod robot, demonstrating copper-rs on a multi-axis robot that demands low latency in a high frequency message environment. I discuss claims about determinism, observability, and runtime latency, providing an initial framework for quantitative analysis of copper-rs.&lt;/p&gt;
&lt;p&gt;Additionally, this article addresses key architectural limitations in the default ROS 2 framework, in particular:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The cost of the Data Distribution Service (DDS)&lt;/li&gt;
&lt;li&gt;Asynchronous communication and the effect of callbacks on execution order&lt;/li&gt;
&lt;li&gt;The impact of the underlying OS&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I make direct comparisons between ROS 2 and copper-rs, evaluating each in the context of real-time robotic systems. The results of the case study exceeded expectations, achieving a baseline maximum end-to-end latency of 730.364μs on native Linux. Furthermore, subsequent improvements to application code continue to reduce the maximum latency and jitter. I provide a comprehensive overview of scheduling, memory-allocation, and key design principles which contribute to the remarkable performance of copper-rs.&lt;/p&gt;
&lt;p&gt;The source code is available on &lt;a href="https://gitlab.com/CodethinkLabs/"&gt;CodethinkLabs&lt;/a&gt;: feel free to contribute!&lt;/p&gt;
&lt;p&gt;Source code: &lt;a href="https://gitlab.com/CodethinkLabs/spyder_robot"&gt;https://gitlab.com/CodethinkLabs/spyder_robot&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;A. N. Nagiwale, P. K. Uttam and S. E. Talole, "A Real-Time Software Framework for Multi-axis Robotic Systems Using ROS 2 and EtherCAT," 2024 IEEE Pune Section International Conference (PuneCon), Pune, India, 2024, &lt;a href="https://doi.org/10.1109/PuneCon63413.2024.10895565"&gt;https://doi.org/10.1109/PuneCon63413.2024.10895565&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;AutomationDirect.com. “What Is EtherCAT? From AutomationDirect.” YouTube, February 16, 2024. &lt;a href="https://www.youtube.com/watch?v=rt-ykjEL4Y8"&gt;https://www.youtube.com/watch?v=rt-ykjEL4Y8&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;J. -H. Kim, J. -H. Choi, Y. -C. An and T. -Y. Kuc, "Evaluation of RTOS for Robotic Applications with ROS2 on Embedded Systems," 2025 25th International Conference on Control, Automation and Systems (ICCAS), Incheon, Korea, Republic of, 2025. &lt;a href="https://doi.org/10.23919/ICCAS66577.2025.11301358"&gt;https://doi.org/10.23919/ICCAS66577.2025.11301358&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ROS2 Real-time Performance Optimization and Evaluation. &lt;a href="https://link.springer.com/article/10.1186/s10033-023-00976-5"&gt;https://link.springer.com/article/10.1186/s10033-023-00976-5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Ye, Y., Nie, Z., Liu, X. et al. ROS2 Real-time Performance Optimization and Evaluation. Chin. J. Mech. Eng. 36, 144 (2023). &lt;a href="https://doi.org/10.1186/s10033-023-00976-5"&gt;https://doi.org/10.1186/s10033-023-00976-5&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;D. Casini, J. Chen, J. Li, F. Reghenzani, H. Teper. "A Survey of Real-Time Support Analysis and Advancements in ROS 2". arXiv:2601.10722. &lt;a href="https://doi.org/10.48550/arXiv.2601.10722"&gt;https://doi.org/10.48550/arXiv.2601.10722&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;X. Luo, X. Jiang, N. Guan, H. Liang, S. Liu and W. Yi, "Modeling and Analysis of Inter-Process Communication Delay in ROS 2," 2023 IEEE Real-Time Systems Symposium (RTSS), Taipei, Taiwan, 2023, &lt;a href="https://doi.org/10.1109/RTSS59052.2023.00026"&gt;https://doi.org/10.1109/RTSS59052.2023.00026&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Zhang Z, He W, Wu F, Quesada L and Xiang L (2024) Development of a bionic hexapod robot with adaptive gait and clearance for enhanced agricultural field scouting. Front. Robot. &lt;a href="https://doi.org/10.3389/frobt.2024.1426269"&gt;https://doi.org/10.3389/frobt.2024.1426269&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;copper-rs. “Copper Tasks Lifecycle Overview.” Copper Tasks lifecycle overview - Copper Wiki. &lt;a href="https://copper-project.github.io/copper-rs/Task-Lifecycle/"&gt;https://copper-project.github.io/copper-rs/Task-Lifecycle/&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;copper-rs. "Copper Robotics" Copper Robotics website. &lt;a href="https://www.copper-robotics.com/"&gt;https://www.copper-robotics.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ROS 2. "ROS 2 Platform Support". &lt;a href="https://www.ros.org/reps/rep-2000.html"&gt;https://www.ros.org/reps/rep-2000.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;“Bridging Robotics and Systems Programming: Why Copper-Rs Is a Game Changer.” 2026. Guillaume BINET 2026. &lt;a href="https://fosdem.org/2026/schedule/event/SK8EGJ-copper-rust-robotics-runtime/"&gt;https://fosdem.org/2026/schedule/event/SK8EGJ-copper-rust-robotics-runtime/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Copper Robotics. "What are we solving for with copper-rs?" Youtube, March 9, 2026. &lt;a href="https://youtu.be/IuduPa_nyks?si=I7dVYDD7wVOc4FqY"&gt;https://youtu.be/IuduPa_nyks?si=I7dVYDD7wVOc4FqY&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Tokyo Rust. "Copper: The OS for Physical AI" Youtube, July 15, 2026. &lt;a href="https://youtu.be/XxaSLWtGHxM?si=sttKzCPy9MlbVc6J"&gt;https://youtu.be/XxaSLWtGHxM?si=sttKzCPy9MlbVc6J&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;ROS 2 Agnocast: Supporting Unsized Message
Types for True Zero-Copy Publish/Subscribe IPC. &lt;a href="https://arxiv.org/pdf/2506.16882v1"&gt;https://arxiv.org/pdf/2506.16882v1&lt;/a&gt;&lt;/p&gt;</content><category term="2026"></category><category term="rust"></category><category term="robotics"></category><category term="open source"></category></entry><entry><title>Instantaneous CI: Reducing Development Lag Through Cicada with Local-ﬁrst Design and Caching</title><link href="https://www.codethink.co.uk/articles/2026/cicada-instantaneous-ci.html" rel="alternate"></link><published>2026-07-16T00:00:00+01:00</published><updated>2026-07-16T00:00:00+01:00</updated><author><name>Adam Coldrick</name></author><id>tag:www.codethink.co.uk,2026-07-16:/articles/2026/cicada-instantaneous-ci.html</id><summary type="html">&lt;p&gt;Cicada is the potential for mapping CI via REAPI, which would have the benefits of instantaneous CI&lt;/p&gt;</summary><content type="html">&lt;h3&gt;Understanding Continuous Integration&lt;/h3&gt;
&lt;p&gt;Continuous integration and continuous deployment (CI/CD) enable the automation of building, testing, and releasing software. This approach streamlines collaboration between development and operations teams. CI/CD reduces large points of failure by providing immediate feedback on errors or bugs in an update cycle that can be made more efficient and reliable through being smaller and more frequent. Since the 1990s, CI has become a fundamental practice for devops, with CD following suit in the 2010s. At Codethink, we have a storied history of integrating CI/CD into our software development cycle, being early and prominent advocates of its use in the automotive industry. Our latest approach uses a self-hosted GitLab namespace, maintaining our open source ethos and enabling scalability. &lt;/p&gt;
&lt;h3&gt;Available Tooling for CI/CD&lt;/h3&gt;
&lt;p&gt;Tooling including Zuul, Jenkins, and GitLab CI offer framework support in the CI/CD process but each have benefits and drawbacks, which must be carefully considered when used for developing software. There is no perfect CI tool out there that will solve the problem for every use case. Often, modifications are required in the tooling to tailor and adapt the process for your project. This can result in unmanageable maintenance costs in the development cycle, which creates frustration, delays, and unwieldy dependency management.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.gitlab.com/ci/"&gt;GitLab CI&lt;/a&gt; makes local debugging difficult and does not offer a way to test jobs pre-merging. GitLab attempts to be an all-in-one tool and the successive feature creep increasingly makes it harder to manage. &lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.jenkins.io/"&gt;Jenkins CI&lt;/a&gt; runs on a system of plugins, which too often are abandonware, increasing security risk and requiring a lot of overhead and dependency mitigation if an upgrade is required. The maintenance and hosting cost can become unreasonable when scaling a project. &lt;/p&gt;
&lt;p&gt;&lt;a href="https://zuul-ci.org/"&gt;Zuul CI&lt;/a&gt; still has a smaller uptake. While it is powerful and open source, which provides great project management flexibility, there are limited resources and documentations for its integration into a development workflow at this stage. &lt;/p&gt;
&lt;h3&gt;REAPI and CI&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/bazelbuild/remote-apis"&gt;Remote Execution Application Programming Interface (REAPI)&lt;/a&gt; is an open and standardised gRPC-based protocol that enables build tools to offload actions onto remote servers for more efficient execution. REAPI operates on a client-server model where tasks are serialised by the build tool before being executed remotely, returning the output. Caching is added to this process in order to avoid delays from the duplication of task runs.&lt;/p&gt;
&lt;p&gt;It has been used as a component part of CI/CD to make reproducible builds faster at scale with tools like &lt;a href="https://buildstream.apache.org/"&gt;Buildstream&lt;/a&gt; and &lt;a href="https://bazel.build/"&gt;Bazel&lt;/a&gt;. However, these benefits could be applied further to CI by incorporating REAPI in the CI system itself, rather than just within individual jobs.&lt;/p&gt;
&lt;p&gt;By combining REAPI with integration tooling, remote caching could be shared and would produce a near instantaneous result on a job output after it has been run once if the input is exactly the same.&lt;/p&gt;
&lt;p&gt;This would improve the efficiency of the integration process.&lt;/p&gt;
&lt;p&gt;A common pain point with many existing CI systems is the inability to run jobs outside of the CI workflow. If the CI system is an REAPI client, we can develop a way to submit the same jobs to the REAPI server from outside of the CI itself. This local-first design improves the feedback loop when developing new CI jobs, as well as giving full confidence that a CI job will pass after pushing.&lt;/p&gt;
&lt;p&gt;Using REAPI caching within a CI job allows reuse of old results of the same job for a given set of inputs. However, by using REAPI caching as part of the CI system itself we can also cache the job's environment construction, saving time spent fetching the git repository and dependencies.&lt;/p&gt;
&lt;p&gt;This combination provides the opportunity for instantaneous CI.&lt;/p&gt;
&lt;p&gt;The performance of our CI system's cache could be enhanced with speculative execution of CI jobs across multiple MRs, essentially pre-testing the potential merges to provide instantaneous CI when the target branch of an MR is updated.&lt;/p&gt;
&lt;h3&gt;Developing Cicada&lt;/h3&gt;
&lt;p&gt;Advancing from the theoretical application and implications of this variety of CI tooling, &lt;a href="https://sotk.co.uk/posts/2026/cicada.html"&gt;Cicada&lt;/a&gt; was built as a prototype.&lt;/p&gt;
&lt;p&gt;Cicada jobs and pipelines are declared in YAML, then modeled as REAPI Actions to provide caching and offload job scheduling to REAPI server tooling. By using remote execution, the cache can be safely shared between locally invoked runs of Cicada jobs and jobs invoked during a CI run, making the actual CI run provide an almost instantaneous result.&lt;/p&gt;
&lt;p&gt;You can read more about the development of Cicada on &lt;a href="https://sotk.co.uk/posts/2026/cicada.html"&gt;this blog post&lt;/a&gt;. There are real-world applications of this tool, notes on how this could work with Rust, and a list of ideas for future development. &lt;/p&gt;</content><category term="2026"></category><category term="CI"></category><category term="CD"></category><category term="Builds"></category><category term="Software Development"></category><category term="REAPI"></category></entry><entry><title>Assessing the reproducibility workflow in freedesktop-sdk</title><link href="https://www.codethink.co.uk/articles/2026/assessing-the-reproducibility-workflow-in-freedesktop-sdk.html" rel="alternate"></link><published>2026-06-16T00:00:00+01:00</published><updated>2026-06-16T00:00:00+01:00</updated><author><name>Tanvir Roshid</name></author><id>tag:www.codethink.co.uk,2026-06-16:/articles/2026/assessing-the-reproducibility-workflow-in-freedesktop-sdk.html</id><summary type="html">&lt;p&gt;Reproducibility is a fundamental concept for long-term maintainability and the trustability of systems, but the validation process itself across dependency graphs comes at a cost. This article examines freedesktop-sdk's current approach to reproducibility and proposes a lightweight target-level workflow using Bash and discusses the tradeoffs with this approach.&lt;/p&gt;</summary><content type="html">&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://gitlab.com/freedesktop-sdk/freedesktop-sdk"&gt;Freedesktop SDK&lt;/a&gt; is a free, community-developed, and open-source
project that simplifies the process of creating various software artefacts. Common use cases include building
containers, Flatpak runtimes, snaps, and complete operating systems.&lt;/p&gt;
&lt;p&gt;In this article, we explore a more lightweight approach to assessing reproducibility that prioritises speed, whilst
providing meaningful validation to the engineer at the artefact level. Additionally, we provide metrics to help quantify
the performance gap and discuss the tradeoffs between the two approaches. Note that this article focuses on
&lt;a href="https://gitlab.com/freedesktop-sdk/freedesktop-sdk/-/tree/62f1f0698bcc907896173d2ce3381710db70710b"&gt;Freedesktop SDK&lt;/a&gt; as
an example repository to analyse the reproducibility workflow inside it and explores an alternative workflow strategy
that aligns more closely with how &lt;a href="https://archive.fosdem.org/2015/schedule/event/stretching_out_for_trustworthy_reproducible_builds/attachments/paper/668/export/events/attachments/stretching_out_for_trustworthy_reproducible_builds/paper/668/2015_01_31_FOSDEM15.pdf"&gt;upstream Linux distributions&lt;/a&gt; handle reproducibility.&lt;/p&gt;
&lt;h3&gt;Summarising reproducibility&lt;/h3&gt;
&lt;p&gt;Reproducibility in the context of Software Engineering can refer to different definitions depending on the environment
and requirements. In this article, we consider bit-for-bit reproducibility, i.e., the ability to generate an
identical binary given the same source code input and build environment.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://en.wikipedia.org/wiki/Reproducibility"&gt;concept of reproducibility&lt;/a&gt; as a principle applies broadly to
scientific and engineering disciplines, where the results from an experiment should be achievable with a high degree of
reliability when the study is replicated. The &lt;a href="https://en.wikipedia.org/wiki/Reproducible_builds"&gt;Wikipedia article on reproducible builds&lt;/a&gt; provides a broad overview of the concept and its historical
    context within Software Engineering. It defines reproducible builds as:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Reproducible builds, also known as deterministic compilation, is a process of building software which ensures the&lt;/em&gt;
&lt;em&gt;resulting binary code can be reproduced. Source code compiled deterministically will always output the same binary."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In theory, this sounds both useful and doable. In reality, achieving reproducibility is harder than it sounds. Build
processes can be influenced by a range of factors, such as timestamps, environment variables, filesystem ordering, and
differences within the toolchain behaviour. Unless you control these factors, you risk introducing stochastic
variations in the final artefact, which decreases the confidence in the system as a whole.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://reproducible-builds.org/"&gt;reproducible-builds&lt;/a&gt; project is one well-known community effort focused on
improving reproducible builds. They have regular &lt;a href="https://reproducible-builds.org/resources/"&gt;talks&lt;/a&gt; and &lt;a href="https://reproducible-builds.org/events/"&gt;events&lt;/a&gt; that take place each year. In addition, they publish &lt;a href="https://reproducible-builds.org/news/"&gt;monthly reports&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The official definition on their &lt;a href="https://reproducible-builds.org/"&gt;website&lt;/a&gt; is:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;“Reproducible builds are a set of software development practices that create an independently-verifiable path from&lt;/em&gt;
&lt;em&gt;source to binary code.”&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In short, reproducibility is widely regarded as an important topic within Software Engineering, particularly in
environments that prioritise trustworthiness and long-term maintainability.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://buildstream.build/index.html"&gt;BuildStream&lt;/a&gt; plays a crucial role in reducing this problem because it enables you
to declare software stacks in a declarative format and control the build environment, which helps reduce sources of
non-determinism and makes reproducibility achievable in practice.&lt;/p&gt;
&lt;p&gt;At Codethink, reproducibility is one of the key concepts integrated into our &lt;a href="https://pages.eclipse.dev/eclipse/tsf/tsf/index.html"&gt;Eclipse Trustable Software Framework&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;The current solution inside of freedesktop-sdk&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://gitlab.com/freedesktop-sdk/freedesktop-sdk/-/tree/62f1f0698bcc907896173d2ce3381710db70710b"&gt;freedesktop-sdk&lt;/a&gt; repository (at
    this moment in time) uses the &lt;a href="https://gitlab.com/freedesktop-sdk/buildstream-reprotest/-/tree/f5b053059e410ce3ae9f7a767299075324ca20d1"&gt;buildstream-reprotest&lt;/a&gt;
        library to establish the reproducibility of the BuildStream elements that reside inside it.&lt;/p&gt;
&lt;p&gt;The buildstream-reprotest is a Python script that essentially uses the &lt;a href="https://docs.python.org/3/library/subprocess.html"&gt;subprocess&lt;/a&gt; module in Python to invoke BuildStream and orchestrate a method
    for assessing the reproducibility status of a given BuildStream element. The script evaluates the entire dependency
    graph and essentially asks the question: &lt;em&gt;Is the entire dependency graph reproducible at every level?&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The script runs the following phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Phase 1&lt;/strong&gt;: Build the target element and pull the remote cache where possible, and fall back to manually compiling
if necessary.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 2&lt;/strong&gt;: Iterate through the dependency graph in reverse order, disable the remote cache and build each element
manually from scratch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Phase 3&lt;/strong&gt;: Use the output from Phases 1 and 2 and execute &lt;a href="https://diffoscope.org/"&gt;diffoscope&lt;/a&gt; to identify any
differences in the output.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Applying this methodology, by definition, assesses the reproducibility of the build process. Our argument is that,
although there is nothing incorrect about this approach, it is time-consuming due to its very nature.&lt;/p&gt;
&lt;p&gt;This approach introduces the following key challenges:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;High computational cost&lt;/strong&gt;: rebuilding all dependencies from scratch.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Time inefficiency&lt;/strong&gt;: running comparisons at every layer of the graph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Limited iteration speed&lt;/strong&gt;: not ideal for rapid development feedback loops.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This raises an important question for us all:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Do we always need full dependency graph validation to get useful reproducibility signals?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;A target level approach&lt;/h3&gt;
&lt;p&gt;To address this, we developed a Bash script that focused on target-level reproducibility rather than full graph
validation. The reason for choosing Bash was primarily that it requires minimal boilerplate, is highly interactive, and
has low resource usage.&lt;/p&gt;
&lt;p&gt;Instead of asking:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Is the entire dependency graph reproducible?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We are now asking:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;If we rebuild this element in isolation, do we get the same artefact?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The distinction between the two questions enables a faster feedback cycle, while also addressing many real-world
reproducibility issues.&lt;/p&gt;
&lt;h3&gt;Workflow overview&lt;/h3&gt;
&lt;p&gt;The &lt;a href="https://gitlab.com/CodethinkLabs/buildstream-reprotest-script"&gt;buildstream-reprotest-script&lt;/a&gt; executes the following
phases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Phase 1&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Build the target element with all dependencies, using remote cache where possible.&lt;/li&gt;
&lt;li&gt;Fetch sources to ensure we can build without the cache/internet.&lt;/li&gt;
&lt;li&gt;Check out the resulting artefact into a temporary directory.&lt;/li&gt;
&lt;li&gt;Remove the target element from the local cache whilst keeping the dependencies.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Phase 2&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Disable cache via a temporary configuration file.&lt;/li&gt;
&lt;li&gt;Rebuild the target element without rebuilding its dependencies.&lt;/li&gt;
&lt;li&gt;Check out the new artefact.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Phase 3&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Run &lt;a href="https://diffoscope.org/"&gt;diffoscope&lt;/a&gt; between the two outputs.&lt;/li&gt;
&lt;li&gt;Generate a detailed HTML report.&lt;/li&gt;
&lt;li&gt;Return the comparison exit status.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The key design decision here is to reuse the previously built dependencies from Phase One into Phase Two. The
benefit of reusing previously built dependencies means that we significantly reduce the time taken to assess the
reproducibility of a project because we’re not devoting time to rebuilding each dependency from scratch. The time
reduction is particularly useful when processing large dependency graphs, e.g., those within the &lt;a href="https://gitlab.com/freedesktop-sdk/freedesktop-sdk/-/tree/62f1f0698bcc907896173d2ce3381710db70710b"&gt;freedesktop-sdk&lt;/a&gt; environment.&lt;/p&gt;
&lt;p&gt;We should note that this workflow aligns closely with how upstream Linux distributions approach
&lt;a href="https://archive.fosdem.org/2015/schedule/event/stretching_out_for_trustworthy_reproducible_builds/attachments/paper/668/export/events/attachments/stretching_out_for_trustworthy_reproducible_builds/paper/668/2015_01_31_FOSDEM15.pdf"&gt;reproducibility workflows&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;The pros and cons of buildstream-reprotest-script&lt;/h3&gt;
&lt;p&gt;Pros:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ability to assess the reproducibility of a BuildStream element.&lt;/li&gt;
&lt;li&gt;Faster than the &lt;a href="https://gitlab.com/freedesktop-sdk/buildstream-reprotest/-/tree/f5b053059e410ce3ae9f7a767299075324ca20d1"&gt;buildstream-reprotest&lt;/a&gt; equivalent.&lt;/li&gt;
&lt;li&gt;Helps expose situations where the target element is reproducible, but it’s a dependency that’s actually at fault.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Cons:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Does not assess the reproducibility of the dependencies and thus cannot assess the reproducibility of the entire
Directed Acyclic Graph (DAG), which represents the build dependencies. &lt;/li&gt;
&lt;li&gt;Does not detect reproducibility issues introduced by dependencies that are reused from cache rather than rebuilt.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Metrics&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Element&lt;/th&gt;
&lt;th&gt;No. of dependencies&lt;/th&gt;
&lt;th&gt;Time for execution (s)&lt;/th&gt;
&lt;th&gt;Time for diffoscope (s)&lt;/th&gt;
&lt;th&gt;Run type&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;components/podman.bst&lt;/td&gt;
&lt;td&gt;291&lt;/td&gt;
&lt;td&gt;4716&lt;/td&gt;
&lt;td&gt;372&lt;/td&gt;
&lt;td&gt;Cold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;components/podman.bst&lt;/td&gt;
&lt;td&gt;291&lt;/td&gt;
&lt;td&gt;472&lt;/td&gt;
&lt;td&gt;365&lt;/td&gt;
&lt;td&gt;Hot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vm/desktop/filesystem.bst&lt;/td&gt;
&lt;td&gt;726&lt;/td&gt;
&lt;td&gt;4669&lt;/td&gt;
&lt;td&gt;118&lt;/td&gt;
&lt;td&gt;Cold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;vm/desktop/filesystem.bst&lt;/td&gt;
&lt;td&gt;726&lt;/td&gt;
&lt;td&gt;203&lt;/td&gt;
&lt;td&gt;128&lt;/td&gt;
&lt;td&gt;Hot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;abi/sdk-lib-headers.bst&lt;/td&gt;
&lt;td&gt;615&lt;/td&gt;
&lt;td&gt;3205&lt;/td&gt;
&lt;td&gt;10&lt;/td&gt;
&lt;td&gt;Cold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;abi/sdk-lib-headers.bst&lt;/td&gt;
&lt;td&gt;615&lt;/td&gt;
&lt;td&gt;42&lt;/td&gt;
&lt;td&gt;11&lt;/td&gt;
&lt;td&gt;Hot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;extensions/mesa/mesa.bst&lt;/td&gt;
&lt;td&gt;220&lt;/td&gt;
&lt;td&gt;3801&lt;/td&gt;
&lt;td&gt;44&lt;/td&gt;
&lt;td&gt;Cold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;extensions/mesa/mesa.bst&lt;/td&gt;
&lt;td&gt;220&lt;/td&gt;
&lt;td&gt;369&lt;/td&gt;
&lt;td&gt;46&lt;/td&gt;
&lt;td&gt;Hot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;components/nasm.bst&lt;/td&gt;
&lt;td&gt;154&lt;/td&gt;
&lt;td&gt;2369&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;Cold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;components/nasm.bst&lt;/td&gt;
&lt;td&gt;154&lt;/td&gt;
&lt;td&gt;62&lt;/td&gt;
&lt;td&gt;22&lt;/td&gt;
&lt;td&gt;Hot&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oci/layers/flatpak-stack.bst&lt;/td&gt;
&lt;td&gt;618&lt;/td&gt;
&lt;td&gt;5630&lt;/td&gt;
&lt;td&gt;288&lt;/td&gt;
&lt;td&gt;Cold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;oci/layers/flatpak-stack.bst&lt;/td&gt;
&lt;td&gt;618&lt;/td&gt;
&lt;td&gt;408&lt;/td&gt;
&lt;td&gt;314&lt;/td&gt;
&lt;td&gt;Hot&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;sub&gt;The run type is split into &lt;em&gt;Cold&lt;/em&gt; and &lt;em&gt;Hot&lt;/em&gt;, which simply refer to running the script without and with local cache,
respectively. A Cold run has no local cache, and a Hot run has local cache.&lt;/sub&gt;&lt;/p&gt;
&lt;p&gt;The above table shows us a series of elements that we tested the &lt;a href="https://gitlab.com/CodethinkLabs/buildstream-reprotest-script"&gt;buildstream-reprotest-script&lt;/a&gt; against. Whilst we did not execute the original
    &lt;a href="https://gitlab.com/freedesktop-sdk/buildstream-reprotest/-/tree/f5b053059e410ce3ae9f7a767299075324ca20d1"&gt;buildstream-reprotest&lt;/a&gt;
        workflow for direct comparison, prior experience indicates that full dependency graph reproducibility testing
        can take between 5 and 15 hours, depending on the graph size.&lt;/p&gt;
&lt;p&gt;The original &lt;a href="https://gitlab.com/Tanvir786123/buildstream-reprotest-script/-/merge_requests/3"&gt;Merge Request&lt;/a&gt; contains
a detailed series of tests against a specific instance of &lt;a href="https://gitlab.com/freedesktop-sdk/freedesktop-sdk/-/tree/22177ec91f4e33abd0c17310e037f7317c697c5d"&gt;freedesktop-sdk&lt;/a&gt;, which includes
    two interesting cases:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;components/nasm.bst&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;oci/toolbox-oci.bst&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The &lt;a href="https://gitlab.com/CodethinkLabs/buildstream-reprotest-script"&gt;buildstream-reprotest-script&lt;/a&gt; helped us deduce that
the non-reproducibility of [1] is due to a dependency within the pipeline. In addition, the script helped expose an
issue with the cache keys associated with [2].&lt;/p&gt;
&lt;p&gt;In summary, the metrics show us that our script is significantly faster and raises an interesting follow-up
question:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Should the diffoscope step be optional in fast validation workflows?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Practical Impact&lt;/h3&gt;
&lt;p&gt;In practice, this approach:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Reduces testing time significantly.&lt;/li&gt;
&lt;li&gt;Enables faster debugging cycles.&lt;/li&gt;
&lt;li&gt;Provides actionable feedback early.&lt;/li&gt;
&lt;li&gt;Helps isolate whether issues originate in the target or its dependencies.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Reproducibility testing doesn’t always need to be exhaustive to be useful, and in practice, we can alter the mechanism
to enable engineers to detect non-determinism earlier in the development cycle, rather than discovering it during
expensive full-graph validation.&lt;/p&gt;
&lt;p&gt;By focusing on target-level validation, we can improve:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Speed.&lt;/li&gt;
&lt;li&gt;Practicality.&lt;/li&gt;
&lt;li&gt;Engineering insight.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The critical question for us all (at least within the Freedesktop SDK environment and ones like it):&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Can target-level validation complement the existing full dependency graph validation within Freedesktop SDK (and&lt;/em&gt;
&lt;em&gt;similar environments)?&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Future work&lt;/h3&gt;
&lt;p&gt;Potential improvements for our &lt;a href="https://gitlab.com/CodethinkLabs/buildstream-reprotest-script"&gt;buildstream-reprotest-script&lt;/a&gt; include:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Making logging depth dynamic, i.e., removing our brittle assumption and reliance on &lt;a href="https://ss64.com/bash/caller.html"&gt;&lt;code&gt;caller 1&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Providing an option to perform full dependency graph reproducibility testing.&lt;/li&gt;
&lt;li&gt;Making the &lt;a href="https://diffoscope.org/"&gt;diffoscope&lt;/a&gt; operation optional.&lt;/li&gt;
&lt;li&gt;Capturing metadata for deeper diagnostics.&lt;/li&gt;
&lt;/ol&gt;</content><category term="2026"></category><category term="Reproducibility"></category><category term="BuildStream"></category><category term="freedesktop-sdk"></category><category term="Upstream"></category><category term="Long Term Maintainability"></category><category term="Safety"></category><category term="Build"></category></entry><entry><title>Deep Dive into Upstream RISC-V Boot Chain</title><link href="https://www.codethink.co.uk/articles/2026/deep-dive-into-upstream-risc-v-boot-chain.html" rel="alternate"></link><published>2026-06-09T00:00:00+01:00</published><updated>2026-06-09T00:00:00+01:00</updated><author><name>Marcel Ziswiler</name></author><id>tag:www.codethink.co.uk,2026-06-09:/articles/2026/deep-dive-into-upstream-risc-v-boot-chain.html</id><summary type="html">&lt;p&gt;This article looks in-depth at the RISC-V boot chain and was presented as a poster at the RISC-V Summit Europe 2026 in Bologna, Italy.&lt;/p&gt;</summary><content type="html">&lt;p&gt;This deep dive into the upstream RISC-V boot chain was presented as a &lt;a href="https://riscv-europe.org/summit/2026/posters"&gt;poster at the RISC-V Summit Europe 2026&lt;/a&gt; in Bologna, Italy.&lt;/p&gt;
&lt;p&gt;While porting Freedesktop SDK to the EBC7700 for my &lt;a href="https://fosdem.org/2026/schedule/event/LX3NNU-upstream-embedded-linux-on-risc-v-sbcs"&gt;FOSDEM'26 talk&lt;/a&gt;, I encountered some UEFI boot issues, which motivated me to dig deeper and uncover all the mysteries about the RISC-V boot chain. We start by introducing a few important concepts/tools.&lt;/p&gt;
&lt;h2&gt;RISC-V Toolchain&lt;/h2&gt;
&lt;p&gt;First, you may need a toolchain to actually be able to compile any of the binaries involved in the boot chain. And if you are, just like me, still waiting for your long pre-ordered RISC-V laptop/workstation, and therefore still using an x86_64 machine as your daily driver, the easiest is to &lt;a href="https://github.com/riscv-collab/riscv-gnu-toolchain"&gt;download a pre-built cross-toolchain&lt;/a&gt;. Throughout this tutorial, I used the nightly 2026.05.19 releases:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;riscv64-glibc-ubuntu-24.04-gcc.tar.xz&lt;/li&gt;
&lt;li&gt;riscv64-glibc-ubuntu-24.04-llvm.tar.xz&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The setup for gcc:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;envsetup-gcc-riscv64-16.1.sh
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/Toolchains/riscv64-glibc-ubuntu-24.04-gcc-2026.05.19-nightly/bin/riscv64-unknown-linux-gnu-
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DTC_FLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-@&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en_US.UTF-8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Or alternatively for llvm, which is my preferred option nowadays:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;
&lt;span class="normal"&gt;8&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;envsetup-llvm-riscv64-21.1.1.sh
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;ARCH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;clang
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CROSS_COMPILE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;riscv64-unknown-linux-gnu-
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;DTC_FLAGS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;-@&amp;quot;&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en_US.UTF-8
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;LLVM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
&lt;span class="nb"&gt;export&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/Toolchains/riscv64-glibc-ubuntu-24.04-llvm-2026.05.19-nightly/bin/:&lt;span class="nv"&gt;$PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note: The &lt;code&gt;-@&lt;/code&gt; flag instructs the device tree compiler to generate symbol tables, which are mainly used should you ever play with device tree overlays.&lt;/p&gt;
&lt;h2&gt;Privilege Levels&lt;/h2&gt;
&lt;p&gt;Second, I want to briefly mention an important low-level RISC-V hardware concept called privilege levels, which define the processor's access rights to memory, I/O devices, and instructions. Understanding the basic concept of privilege levels is important for the overall understanding of this article. RISC-V knows 3 privilege levels:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;M (machine) mode:&lt;ul&gt;
&lt;li&gt;highest privilege&lt;/li&gt;
&lt;li&gt;low-level firmware (e.g. OpenSBI), system boot, and security (e.g. secure execution environments)&lt;/li&gt;
&lt;li&gt;handles critical system traps&lt;/li&gt;
&lt;li&gt;return to lower-privileged levels using mret instruction&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;S (supervisor) mode:&lt;ul&gt;
&lt;li&gt;intermediate privilege&lt;/li&gt;
&lt;li&gt;used by operating system kernels to manage memory and hardware resources&lt;/li&gt;
&lt;li&gt;restrict direct access to machine-level configuration registers and physical memory protection (PMP)&lt;/li&gt;
&lt;li&gt;uses Memory Management Unit (MMU) and virtual memory&lt;/li&gt;
&lt;li&gt;ecall instruction to request kernel services&lt;/li&gt;
&lt;li&gt;return to U-mode using sret instruction&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;U (user) mode:&lt;ul&gt;
&lt;li&gt;lowest privilege&lt;/li&gt;
&lt;li&gt;cannot execute privileged instructions, access physical memory directly without virtual memory translations, or directly alter core control registers&lt;/li&gt;
&lt;li&gt;user space executing standard applications&lt;/li&gt;
&lt;li&gt;prevents incorrect or malicious application code from interfering with other running programs or the operating system itself&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;QEMU&lt;/h2&gt;
&lt;p&gt;Third, I want to introduce the Quick Emulator (QEMU), which is a free and open source machine emulator and virtualiser. The QEMU RISC-V emulation with its (riscv64) &lt;code&gt;virt&lt;/code&gt; machine is the perfect playground for us to gain a deeper understanding of the RISC-V boot flow. It uses a hard-coded reset vector, and by default, loads the built-in OpenSBI binary into system memory space, unless a custom firmware binary is specified using the &lt;code&gt;-bios&lt;/code&gt; argument. OpenSBI executes hardware-specific initialisation (in M-mode) and passes control (in S-mode) to the &lt;code&gt;-kernel&lt;/code&gt; or bootloader payload at &lt;code&gt;0x80000000&lt;/code&gt;. The virt machine automatically generates a device tree blob (DTB) which it passes to the guest, if there is no &lt;code&gt;-dtb&lt;/code&gt; option specified.&lt;/p&gt;
&lt;p&gt;For QEMU I relied on the latest version shipped with my distro, rather than also compiling it from scratch:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;rpm&lt;span class="w"&gt; &lt;/span&gt;-q&lt;span class="w"&gt; &lt;/span&gt;qemu-system-riscv
qemu-system-riscv-10.2.2-1.fc44.x86_64
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;Boot Flow Overview&lt;/h2&gt;
&lt;p&gt;With those three things introduced, let us now look at the RISC-V boot flow:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Boot flow overview" src="https://www.codethink.co.uk/articles/2026/risc-v_boot_flow.drawio.png"&gt;&lt;/p&gt;
&lt;h2&gt;Boot ROM aka ZSBL (Zero Stage Bootloader)&lt;/h2&gt;
&lt;p&gt;The ZSBL is what a RISC-V chip, or its emulated derivative, runs first at power-up out of reset. It initialises a few registers and jumps directly to some hard-coded address (in case of QEMU's riscv64 virt machine, that is &lt;code&gt;0x80000000&lt;/code&gt;), where the first user-provided code runs (by default, QEMU will load OpenSBI there).&lt;/p&gt;
&lt;h3&gt;Debugging&lt;/h3&gt;
&lt;p&gt;Let us use QEMU's inbuilt gdb server (debugger) to have a look at what exactly ZSBL does in the QEMU case.&lt;/p&gt;
&lt;p&gt;In a first terminal, we launch QEMU with the &lt;code&gt;-S&lt;/code&gt; argument, which instructs it to just wait for a gdb debugger session to connect to its gdb server and further specify what to do next:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;qemu-system-riscv64&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;-gdb&lt;span class="w"&gt; &lt;/span&gt;tcp::1234&lt;span class="w"&gt; &lt;/span&gt;-machine&lt;span class="w"&gt; &lt;/span&gt;virt&lt;span class="w"&gt; &lt;/span&gt;-nographic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And in a second terminal, we launch gdb-multiarch (again, just using the distro-provided one), instruct it to disassemble the next line and connect to the above-launched QEMU. Note that it will automatically detect the &lt;code&gt;riscv:rv64&lt;/code&gt; architecture. We can use &lt;code&gt;si&lt;/code&gt; a few times to step through ZSBL's instructions one by one, until we observe the final jump to OpenSBI at &lt;code&gt;0x80000000&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;
&lt;span class="normal"&gt;39&lt;/span&gt;
&lt;span class="normal"&gt;40&lt;/span&gt;
&lt;span class="normal"&gt;41&lt;/span&gt;
&lt;span class="normal"&gt;42&lt;/span&gt;
&lt;span class="normal"&gt;43&lt;/span&gt;
&lt;span class="normal"&gt;44&lt;/span&gt;
&lt;span class="normal"&gt;45&lt;/span&gt;
&lt;span class="normal"&gt;46&lt;/span&gt;
&lt;span class="normal"&gt;47&lt;/span&gt;
&lt;span class="normal"&gt;48&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;gdb&lt;span class="w"&gt; &lt;/span&gt;-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;set disassemble-next-line on&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;target remote localhost:1234&amp;#39;&lt;/span&gt;
GNU&lt;span class="w"&gt; &lt;/span&gt;gdb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Fedora&lt;span class="w"&gt; &lt;/span&gt;Linux&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;17&lt;/span&gt;.1-6.fc44
Copyright&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;C&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2025&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Free&lt;span class="w"&gt; &lt;/span&gt;Software&lt;span class="w"&gt; &lt;/span&gt;Foundation,&lt;span class="w"&gt; &lt;/span&gt;Inc.
License&lt;span class="w"&gt; &lt;/span&gt;GPLv3+:&lt;span class="w"&gt; &lt;/span&gt;GNU&lt;span class="w"&gt; &lt;/span&gt;GPL&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;later&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;http://gnu.org/licenses/gpl.html&amp;gt;
This&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;software:&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;are&lt;span class="w"&gt; &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;change&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;redistribute&lt;span class="w"&gt; &lt;/span&gt;it.
There&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;NO&lt;span class="w"&gt; &lt;/span&gt;WARRANTY,&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;extent&lt;span class="w"&gt; &lt;/span&gt;permitted&lt;span class="w"&gt; &lt;/span&gt;by&lt;span class="w"&gt; &lt;/span&gt;law.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show copying&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show warranty&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;details.
This&lt;span class="w"&gt; &lt;/span&gt;GDB&lt;span class="w"&gt; &lt;/span&gt;was&lt;span class="w"&gt; &lt;/span&gt;configured&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x86_64-redhat-linux-gnu&amp;quot;&lt;/span&gt;.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show configuration&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;configuration&lt;span class="w"&gt; &lt;/span&gt;details.
For&lt;span class="w"&gt; &lt;/span&gt;bug&lt;span class="w"&gt; &lt;/span&gt;reporting&lt;span class="w"&gt; &lt;/span&gt;instructions,&lt;span class="w"&gt; &lt;/span&gt;please&lt;span class="w"&gt; &lt;/span&gt;see:
&amp;lt;https://www.gnu.org/software/gdb/bugs/&amp;gt;.
Find&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;GDB&lt;span class="w"&gt; &lt;/span&gt;manual&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;other&lt;span class="w"&gt; &lt;/span&gt;documentation&lt;span class="w"&gt; &lt;/span&gt;resources&lt;span class="w"&gt; &lt;/span&gt;online&lt;span class="w"&gt; &lt;/span&gt;at:
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;http://www.gnu.org/software/gdb/documentation/&amp;gt;.

For&lt;span class="w"&gt; &lt;/span&gt;help,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;help&amp;quot;&lt;/span&gt;.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;apropos word&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;commands&lt;span class="w"&gt; &lt;/span&gt;related&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;word&amp;quot;&lt;/span&gt;.
Remote&lt;span class="w"&gt; &lt;/span&gt;debugging&lt;span class="w"&gt; &lt;/span&gt;using&lt;span class="w"&gt; &lt;/span&gt;localhost:1234
⚠️&lt;span class="w"&gt; &lt;/span&gt;warning:&lt;span class="w"&gt; &lt;/span&gt;No&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;has&lt;span class="w"&gt; &lt;/span&gt;been&lt;span class="w"&gt; &lt;/span&gt;specified&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;target&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;support
determining&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;automatically.&lt;span class="w"&gt;  &lt;/span&gt;Try&lt;span class="w"&gt; &lt;/span&gt;using&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;file&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;command.
0x0000000000001000&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000001000:&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00000297&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;auipc&lt;span class="w"&gt;   &lt;/span&gt;t0,0x0
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;si
0x0000000000001004&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000001004:&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;02828613&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;addi&lt;span class="w"&gt;    &lt;/span&gt;a2,t0,40
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;si
0x0000000000001008&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000001008:&lt;span class="w"&gt;  &lt;/span&gt;f1402573&lt;span class="w"&gt;            &lt;/span&gt;csrr&lt;span class="w"&gt;    &lt;/span&gt;a0,mhartid
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;si
0x000000000000100c&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;0x000000000000100c:&lt;span class="w"&gt;  &lt;/span&gt;0202b583&lt;span class="w"&gt;            &lt;/span&gt;ld&lt;span class="w"&gt;  &lt;/span&gt;a1,32&lt;span class="o"&gt;(&lt;/span&gt;t0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;si
0x0000000000001010&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000001010:&lt;span class="w"&gt;  &lt;/span&gt;0182b283&lt;span class="w"&gt;            &lt;/span&gt;ld&lt;span class="w"&gt;  &lt;/span&gt;t0,24&lt;span class="o"&gt;(&lt;/span&gt;t0&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;si
0x0000000000001014&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000001014:&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00028067&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;jr&lt;span class="w"&gt;  &lt;/span&gt;t0
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;si
0x0000000080000000&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000080000000:&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;00050433&lt;/span&gt;&lt;span class="w"&gt;            &lt;/span&gt;add&lt;span class="w"&gt; &lt;/span&gt;s0,a0,zero
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
A&lt;span class="w"&gt; &lt;/span&gt;debugging&lt;span class="w"&gt; &lt;/span&gt;session&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;active.

&lt;span class="w"&gt;    &lt;/span&gt;Inferior&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;process&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;will&lt;span class="w"&gt; &lt;/span&gt;be&lt;span class="w"&gt; &lt;/span&gt;detached.

Quit&lt;span class="w"&gt; &lt;/span&gt;anyway?&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
Detaching&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;pid&lt;span class="w"&gt; &lt;/span&gt;process&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
Ending&lt;span class="w"&gt; &lt;/span&gt;remote&lt;span class="w"&gt; &lt;/span&gt;debugging.
&lt;span class="o"&gt;[&lt;/span&gt;Inferior&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;process&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;detached&lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So QEMU's ZSBL execution starts at address &lt;code&gt;0x1000&lt;/code&gt;, and there are only 6 instructions executed, but what exactly are they doing? Turns out in the case of QEMU's &lt;code&gt;virt&lt;/code&gt; machine, this is the &lt;code&gt;riscv_setup_rom_reset_vec()&lt;/code&gt; function in &lt;code&gt;hw/riscv/boot.c&lt;/code&gt; which is populated as part of its own bring up before booting the guest:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;riscv_setup_rom_reset_vec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MachineState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;machine&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;RISCVHartArrayState&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;harts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="n"&gt;hwaddr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;start_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="n"&gt;hwaddr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rom_base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;hwaddr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;rom_size&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;kernel_entry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                               &lt;/span&gt;&lt;span class="kt"&gt;uint64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fdt_load_addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;start_addr_hi32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x00000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fdt_load_addr_hi32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x00000000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;riscv_is_32bit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;harts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;start_addr_hi32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;start_addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;fdt_load_addr_hi32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;fdt_load_addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/* reset vector */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;uint32_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;reset_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mh"&gt;0x00000297&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="cm"&gt;/* 1:  auipc  t0, %pcrel_hi(fw_dyn) */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mh"&gt;0x02828613&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="cm"&gt;/*     addi   a2, t0, %pcrel_lo(1b) */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mh"&gt;0xf1402573&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="cm"&gt;/*     csrr   a0, mhartid  */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="mh"&gt;0x00028067&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="cm"&gt;/*     jr     t0 */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;start_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;                  &lt;/span&gt;&lt;span class="cm"&gt;/* start: .dword */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;start_addr_hi32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;fdt_load_addr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;               &lt;/span&gt;&lt;span class="cm"&gt;/* fdt_laddr: .dword */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;fdt_load_addr_hi32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="w"&gt;                                     &lt;/span&gt;&lt;span class="cm"&gt;/* fw_dyn: */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;riscv_is_32bit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;harts&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;reset_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x0202a583&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="cm"&gt;/*     lw     a1, 32(t0) */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;reset_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x0182a283&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="cm"&gt;/*     lw     t0, 24(t0) */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;reset_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x0202b583&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="cm"&gt;/*     ld     a1, 32(t0) */&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="n"&gt;reset_vec&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;4&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mh"&gt;0x0182b283&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="cm"&gt;/*     ld     t0, 24(t0) */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So the 6 instructions are literally hard-coded right there in QEMU's source code!&lt;/p&gt;
&lt;p&gt;QEMU populates a &lt;code&gt;struct fw_dynamic_info&lt;/code&gt; with the information required to boot the next stage and passes its address in the &lt;code&gt;a2&lt;/code&gt; register of the RISC-V CPU. The address must be aligned to 8 bytes on RV64 and 4 bytes on RV32. The &lt;code&gt;struct fw_dynamic_info&lt;/code&gt; is defined in &lt;code&gt;include/hw/riscv/boot_opensbi.h&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="cm"&gt;/** Representation dynamic info passed by previous booting stage */&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nc"&gt;fw_dynamic_info64&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/** Info magic */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;magic&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/** Info version */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;version&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/** Next booting stage address */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_addr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/** Next booting stage mode */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;next_mode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/** Options for OpenSBI library */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="kt"&gt;int64_t&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;next_mode&lt;/code&gt; is set to &lt;code&gt;S&lt;/code&gt; to select a CPU that supports S-mode.&lt;/p&gt;
&lt;p&gt;QEMU also passes a unique per-hart ID in the &lt;code&gt;a0&lt;/code&gt; register. We skipped the discussion of harts here as it is not relevant for the discussion of the boot chain.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;fdt_load_addr&lt;/code&gt;, pointing to the flattened device tree, is passed via the &lt;code&gt;a1&lt;/code&gt; register.&lt;/p&gt;
&lt;p&gt;Then it jumps to address &lt;code&gt;0x80000000&lt;/code&gt; to execute the next element within the boot chain.&lt;/p&gt;
&lt;h2&gt;OpenSBI&lt;/h2&gt;
&lt;p&gt;SBI stands for Supervisor Binary Interface, which is the system call style calling convention between a Supervisor (e.g. S-mode OS) and Supervisor Execution Environment (SEE, e.g. the M-mode runtime firmware). SBI calls help to reduce duplicate platform code across OSes, provide common drivers for an OS which can be shared by multiple platforms and an interface for direct access to hardware resources (M-mode only resources). The specification is maintained by the Platform Runtime Services (PRS) Task Group (TG), which ratified the &lt;a href="https://github.com/riscv-non-isa/riscv-sbi-doc"&gt;SBI specification v1.0&lt;/a&gt; in 2022.&lt;/p&gt;
&lt;p&gt;OpenSBI (&lt;a href="https://github.com/riscv-software-src/opensbi"&gt;Open Source Supervisor Binary Interface&lt;/a&gt;) is an open source implementation of that RISC-V SBI specification.&lt;/p&gt;
&lt;p&gt;In OpenSBI, the &lt;code&gt;FW_DYNAMIC&lt;/code&gt; approach dynamically obtains the runtime configuration and information about the next boot stage (e.g. ⁠U-Boot proper) from any previous boot stage via passing a pointer to a &lt;code&gt;struct fw_dynamic_info&lt;/code&gt; structure in a CPU register, rather than using hard-coded compile-time values.&lt;/p&gt;
&lt;p&gt;The assembly bootstrapping phase does early reset and sanity checks by validating its environment and setting up basic &lt;code&gt;mtvec&lt;/code&gt; trap vectors. Subsequently, the dynamic information from &lt;code&gt;fw_dynamic_info&lt;/code&gt; gets parsed, stack allocated, scratch space initialised (&lt;code&gt;mscratch&lt;/code&gt;), and the C-runtime jumped into.&lt;/p&gt;
&lt;p&gt;The core initialisation and platform setup phase (&lt;code&gt;sbi_init&lt;/code&gt;) does early console setup, configuring the primitive serial ⁠UART block for debug log output. The platform hardware init (&lt;code&gt;platform_init&lt;/code&gt;) invokes platform-specific functions, initialising critical hardware blocks. The physical memory protection (PMP) registers are provisioned, shielding M-mode memory from unauthorised S-mode accesses. The interrupt controllers, like core timers, inter-processor interrupts (IPIs), and peripheral controllers like the PLIC/APLIC get initialised.&lt;/p&gt;
&lt;p&gt;The payload transition phase (&lt;code&gt;sbi_expected_trap&lt;/code&gt; to S-mode) extracts the jump target from &lt;code&gt;struct fw_dynamic_info&lt;/code&gt;, modifies the &lt;code&gt;mstatus&lt;/code&gt;/&lt;code&gt;misa&lt;/code&gt; registers, configuring the previous privilege level to S-mode, programs the target &lt;code&gt;next_addr&lt;/code&gt; value directly into the machine exception program counter (&lt;code&gt;mepc&lt;/code&gt;), restores the rest of the original dynamic information (e.g. FDT address) passed via CPU registers, and executes the &lt;code&gt;mret&lt;/code&gt; instruction, which drops the processor privilege down to S-mode and jumps directly to the address staged inside &lt;code&gt;mepc&lt;/code&gt;, passing system execution to U-Boot proper.&lt;/p&gt;
&lt;p&gt;Rather than using QEMU's built-in OpenSBI, we can build our own for later debugging:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/riscv/opensbi.git
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;opensbi
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;opensbi&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;v1.8.1-test&lt;span class="w"&gt; &lt;/span&gt;v1.8.1
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;opensbi&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;BUILD_INFO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;PLATFORM&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;generic
...
&lt;span class="w"&gt; &lt;/span&gt;AR&lt;span class="w"&gt;        &lt;/span&gt;platform/generic/lib/libplatsbi.a
&lt;span class="w"&gt; &lt;/span&gt;AS&lt;span class="w"&gt;        &lt;/span&gt;platform/generic/firmware/payloads/test_head.o
&lt;span class="w"&gt; &lt;/span&gt;CC&lt;span class="w"&gt;        &lt;/span&gt;platform/generic/firmware/payloads/test_main.o
&lt;span class="w"&gt; &lt;/span&gt;MERGE&lt;span class="w"&gt;     &lt;/span&gt;platform/generic/firmware/payloads/test.o
&lt;span class="w"&gt; &lt;/span&gt;ELF&lt;span class="w"&gt;       &lt;/span&gt;platform/generic/firmware/payloads/test.elf
&lt;span class="w"&gt; &lt;/span&gt;OBJCOPY&lt;span class="w"&gt;   &lt;/span&gt;platform/generic/firmware/payloads/test.bin
&lt;span class="w"&gt; &lt;/span&gt;AS&lt;span class="w"&gt;        &lt;/span&gt;platform/generic/firmware/fw_dynamic.o
&lt;span class="w"&gt; &lt;/span&gt;ELF&lt;span class="w"&gt;       &lt;/span&gt;platform/generic/firmware/fw_dynamic.elf
&lt;span class="w"&gt; &lt;/span&gt;OBJCOPY&lt;span class="w"&gt;   &lt;/span&gt;platform/generic/firmware/fw_dynamic.bin
&lt;span class="w"&gt; &lt;/span&gt;AS&lt;span class="w"&gt;        &lt;/span&gt;platform/generic/firmware/fw_jump.o
&lt;span class="w"&gt; &lt;/span&gt;ELF&lt;span class="w"&gt;       &lt;/span&gt;platform/generic/firmware/fw_jump.elf
&lt;span class="w"&gt; &lt;/span&gt;OBJCOPY&lt;span class="w"&gt;   &lt;/span&gt;platform/generic/firmware/fw_jump.bin
&lt;span class="w"&gt; &lt;/span&gt;AS&lt;span class="w"&gt;        &lt;/span&gt;platform/generic/firmware/fw_payload.o
&lt;span class="w"&gt; &lt;/span&gt;ELF&lt;span class="w"&gt;       &lt;/span&gt;platform/generic/firmware/fw_payload.elf
&lt;span class="w"&gt; &lt;/span&gt;OBJCOPY&lt;span class="w"&gt;   &lt;/span&gt;platform/generic/firmware/fw_payload.bin
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note: The latest master branch did not build for me with llvm, which is why I picked released v1.8.1 instead, which seems to work fine.&lt;/p&gt;
&lt;p&gt;The resulting binary may be launched by QEMU (remember, without the &lt;code&gt;-bios&lt;/code&gt; argument, it would just launch the built-in/default OpenSBI):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;
&lt;span class="normal"&gt;39&lt;/span&gt;
&lt;span class="normal"&gt;40&lt;/span&gt;
&lt;span class="normal"&gt;41&lt;/span&gt;
&lt;span class="normal"&gt;42&lt;/span&gt;
&lt;span class="normal"&gt;43&lt;/span&gt;
&lt;span class="normal"&gt;44&lt;/span&gt;
&lt;span class="normal"&gt;45&lt;/span&gt;
&lt;span class="normal"&gt;46&lt;/span&gt;
&lt;span class="normal"&gt;47&lt;/span&gt;
&lt;span class="normal"&gt;48&lt;/span&gt;
&lt;span class="normal"&gt;49&lt;/span&gt;
&lt;span class="normal"&gt;50&lt;/span&gt;
&lt;span class="normal"&gt;51&lt;/span&gt;
&lt;span class="normal"&gt;52&lt;/span&gt;
&lt;span class="normal"&gt;53&lt;/span&gt;
&lt;span class="normal"&gt;54&lt;/span&gt;
&lt;span class="normal"&gt;55&lt;/span&gt;
&lt;span class="normal"&gt;56&lt;/span&gt;
&lt;span class="normal"&gt;57&lt;/span&gt;
&lt;span class="normal"&gt;58&lt;/span&gt;
&lt;span class="normal"&gt;59&lt;/span&gt;
&lt;span class="normal"&gt;60&lt;/span&gt;
&lt;span class="normal"&gt;61&lt;/span&gt;
&lt;span class="normal"&gt;62&lt;/span&gt;
&lt;span class="normal"&gt;63&lt;/span&gt;
&lt;span class="normal"&gt;64&lt;/span&gt;
&lt;span class="normal"&gt;65&lt;/span&gt;
&lt;span class="normal"&gt;66&lt;/span&gt;
&lt;span class="normal"&gt;67&lt;/span&gt;
&lt;span class="normal"&gt;68&lt;/span&gt;
&lt;span class="normal"&gt;69&lt;/span&gt;
&lt;span class="normal"&gt;70&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;opensbi&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;qemu-system-riscv64&lt;span class="w"&gt; &lt;/span&gt;-machine&lt;span class="w"&gt; &lt;/span&gt;virt&lt;span class="w"&gt; &lt;/span&gt;-nographic&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-bios&lt;span class="w"&gt; &lt;/span&gt;build/platform/generic/firmware/fw_payload.bin

OpenSBI&lt;span class="w"&gt; &lt;/span&gt;v1.8.1-87-g547a5bbda7c3
Build&lt;span class="w"&gt; &lt;/span&gt;time:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2026&lt;/span&gt;-05-29&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;13&lt;/span&gt;:51:26&lt;span class="w"&gt; &lt;/span&gt;+0200
Build&lt;span class="w"&gt; &lt;/span&gt;compiler:&lt;span class="w"&gt; &lt;/span&gt;gcc&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;.1.0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;g6afcc4f6d&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;____&lt;span class="w"&gt;                    &lt;/span&gt;_____&lt;span class="w"&gt; &lt;/span&gt;____&lt;span class="w"&gt; &lt;/span&gt;_____
&lt;span class="w"&gt;  &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;__&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;____&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\_&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;_&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;__&lt;span class="w"&gt;   &lt;/span&gt;___&lt;span class="w"&gt; &lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;__&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;___&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_ \ / _ \ &amp;#39;&lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\ \_&lt;/span&gt;__&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;__&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;__/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;____&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\_&lt;/span&gt;___/&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.__/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\_&lt;/span&gt;__&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="p"&gt;|&lt;/span&gt;_____/&lt;span class="p"&gt;|&lt;/span&gt;____/_____&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="p"&gt;|&lt;/span&gt;

Platform&lt;span class="w"&gt; &lt;/span&gt;Name&lt;span class="w"&gt;               &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;riscv-virtio,qemu
Platform&lt;span class="w"&gt; &lt;/span&gt;Features&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;medeleg
Platform&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Count&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
Platform&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Protection&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;pmp
Platform&lt;span class="w"&gt; &lt;/span&gt;IPI&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;aclint-mswi
Platform&lt;span class="w"&gt; &lt;/span&gt;Timer&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;       &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;aclint-mtimer&lt;span class="w"&gt; &lt;/span&gt;@&lt;span class="w"&gt; &lt;/span&gt;10000000Hz
Platform&lt;span class="w"&gt; &lt;/span&gt;Console&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;     &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;uart8250
Platform&lt;span class="w"&gt; &lt;/span&gt;HSM&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;---
Platform&lt;span class="w"&gt; &lt;/span&gt;PMU&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;---
Platform&lt;span class="w"&gt; &lt;/span&gt;Reboot&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;      &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;syscon-reboot
Platform&lt;span class="w"&gt; &lt;/span&gt;Shutdown&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;syscon-poweroff
Platform&lt;span class="w"&gt; &lt;/span&gt;Suspend&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;     &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;---
Platform&lt;span class="w"&gt; &lt;/span&gt;CPPC&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;---
Firmware&lt;span class="w"&gt; &lt;/span&gt;Base&lt;span class="w"&gt;               &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x80000000
Firmware&lt;span class="w"&gt; &lt;/span&gt;Size&lt;span class="w"&gt;               &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;321&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB
Firmware&lt;span class="w"&gt; &lt;/span&gt;RW&lt;span class="w"&gt; &lt;/span&gt;Offset&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x40000
Firmware&lt;span class="w"&gt; &lt;/span&gt;RW&lt;span class="w"&gt; &lt;/span&gt;Size&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB
Firmware&lt;span class="w"&gt; &lt;/span&gt;Heap&lt;span class="w"&gt; &lt;/span&gt;Offset&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x47000
Firmware&lt;span class="w"&gt; &lt;/span&gt;Heap&lt;span class="w"&gt; &lt;/span&gt;Size&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;37&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;total&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;reserved&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;used&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;22&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;free&lt;span class="o"&gt;)&lt;/span&gt;
Firmware&lt;span class="w"&gt; &lt;/span&gt;Scratch&lt;span class="w"&gt; &lt;/span&gt;Size&lt;span class="w"&gt;       &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4096&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;B&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;total&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1600&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;B&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;used&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2496&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;B&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;free&lt;span class="o"&gt;)&lt;/span&gt;
Runtime&lt;span class="w"&gt; &lt;/span&gt;SBI&lt;span class="w"&gt; &lt;/span&gt;Version&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.0
Standard&lt;span class="w"&gt; &lt;/span&gt;SBI&lt;span class="w"&gt; &lt;/span&gt;Extensions&lt;span class="w"&gt;     &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;time,rfnc,ipi,base,hsm,srst,pmu,dbcn,fwft,legacy,dbtr,sse
Experimental&lt;span class="w"&gt; &lt;/span&gt;SBI&lt;span class="w"&gt; &lt;/span&gt;Extensions&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;none

Domain0&lt;span class="w"&gt; &lt;/span&gt;Name&lt;span class="w"&gt;                &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;root
Domain0&lt;span class="w"&gt; &lt;/span&gt;Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;HARTs&lt;span class="w"&gt;               &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0*
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region00&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000080040000-0x000000008005ffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;F,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region01&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000080000000-0x000000008003ffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;F,R,X&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region02&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000000100000-0x0000000000100fff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;I,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;R,W&lt;span class="o"&gt;)&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region03&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000010000000-0x0000000010000fff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;I,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;R,W&lt;span class="o"&gt;)&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region04&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000002000000-0x000000000200ffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;I,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region05&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x000000000c400000-0x000000000c5fffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;I,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;R,W&lt;span class="o"&gt;)&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region06&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x000000000c000000-0x000000000c3fffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;I,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;R,W&lt;span class="o"&gt;)&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region07&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000000000000-0xffffffffffffffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;R,W,X&lt;span class="o"&gt;)&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Next&lt;span class="w"&gt; &lt;/span&gt;Address&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000080200000
Domain0&lt;span class="w"&gt; &lt;/span&gt;Next&lt;span class="w"&gt; &lt;/span&gt;Arg1&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000082200000
Domain0&lt;span class="w"&gt; &lt;/span&gt;Next&lt;span class="w"&gt; &lt;/span&gt;Mode&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;S-mode
Domain0&lt;span class="w"&gt; &lt;/span&gt;SysReset&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;yes
Domain0&lt;span class="w"&gt; &lt;/span&gt;SysSuspend&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;yes

Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;ID&lt;span class="w"&gt;                &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Domain&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;root
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Priv&lt;span class="w"&gt; &lt;/span&gt;Version&lt;span class="w"&gt;      &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;v1.12
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Base&lt;span class="w"&gt; &lt;/span&gt;ISA&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;rv64imafdch
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;ISA&lt;span class="w"&gt; &lt;/span&gt;Extensions&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu,f,d
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;PMP&lt;span class="w"&gt; &lt;/span&gt;Count&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;PMP&lt;span class="w"&gt; &lt;/span&gt;Granularity&lt;span class="w"&gt;   &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bits
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;PMP&lt;span class="w"&gt; &lt;/span&gt;Address&lt;span class="w"&gt; &lt;/span&gt;Bits&lt;span class="w"&gt;  &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;54&lt;/span&gt;
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;MHPM&lt;span class="w"&gt; &lt;/span&gt;Info&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;0x0007fff8&lt;span class="o"&gt;)&lt;/span&gt;
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Debug&lt;span class="w"&gt; &lt;/span&gt;Triggers&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;triggers
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;MIDELEG&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000000001666
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;MEDELEG&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000000f4b509

Test&lt;span class="w"&gt; &lt;/span&gt;payload&lt;span class="w"&gt; &lt;/span&gt;running
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;Debugging&lt;/h3&gt;
&lt;p&gt;We can launch QEMU in the first terminal, but this time, in addition to instructing it with the &lt;code&gt;-bios&lt;/code&gt; argument to load our self-built OpenSBI, again with the &lt;code&gt;-S&lt;/code&gt; argument, which instructs it to just wait for the gdb debugger to connect to it and further specify what to do next:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;opensbi&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;qemu-system-riscv64&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;-bios&lt;span class="w"&gt; &lt;/span&gt;build/platform/generic/firmware/fw_payload.bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-gdb&lt;span class="w"&gt; &lt;/span&gt;tcp::1234&lt;span class="w"&gt; &lt;/span&gt;-machine&lt;span class="w"&gt; &lt;/span&gt;virt&lt;span class="w"&gt; &lt;/span&gt;-nographic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the second terminal, we start gdb, giving it the OpenSBI symbol file and instructing it to break at &lt;code&gt;sbi_boot_print_banner&lt;/code&gt;. Then we can let it print the banner line by line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;
&lt;span class="normal"&gt;39&lt;/span&gt;
&lt;span class="normal"&gt;40&lt;/span&gt;
&lt;span class="normal"&gt;41&lt;/span&gt;
&lt;span class="normal"&gt;42&lt;/span&gt;
&lt;span class="normal"&gt;43&lt;/span&gt;
&lt;span class="normal"&gt;44&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;opensbi&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;gdb&lt;span class="w"&gt; &lt;/span&gt;-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;add-symbol-file build/platform/generic/firmware/fw_payload.elf \&lt;/span&gt;
&lt;span class="s1"&gt;0x80000000&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;target remote localhost:1234&amp;#39;&lt;/span&gt;
GNU&lt;span class="w"&gt; &lt;/span&gt;gdb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Fedora&lt;span class="w"&gt; &lt;/span&gt;Linux&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;17&lt;/span&gt;.1-6.fc44
Copyright&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;C&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2025&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Free&lt;span class="w"&gt; &lt;/span&gt;Software&lt;span class="w"&gt; &lt;/span&gt;Foundation,&lt;span class="w"&gt; &lt;/span&gt;Inc.
License&lt;span class="w"&gt; &lt;/span&gt;GPLv3+:&lt;span class="w"&gt; &lt;/span&gt;GNU&lt;span class="w"&gt; &lt;/span&gt;GPL&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;later&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;http://gnu.org/licenses/gpl.html&amp;gt;
This&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;software:&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;are&lt;span class="w"&gt; &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;change&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;redistribute&lt;span class="w"&gt; &lt;/span&gt;it.
There&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;NO&lt;span class="w"&gt; &lt;/span&gt;WARRANTY,&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;extent&lt;span class="w"&gt; &lt;/span&gt;permitted&lt;span class="w"&gt; &lt;/span&gt;by&lt;span class="w"&gt; &lt;/span&gt;law.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show copying&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show warranty&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;details.
This&lt;span class="w"&gt; &lt;/span&gt;GDB&lt;span class="w"&gt; &lt;/span&gt;was&lt;span class="w"&gt; &lt;/span&gt;configured&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x86_64-redhat-linux-gnu&amp;quot;&lt;/span&gt;.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show configuration&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;configuration&lt;span class="w"&gt; &lt;/span&gt;details.
For&lt;span class="w"&gt; &lt;/span&gt;bug&lt;span class="w"&gt; &lt;/span&gt;reporting&lt;span class="w"&gt; &lt;/span&gt;instructions,&lt;span class="w"&gt; &lt;/span&gt;please&lt;span class="w"&gt; &lt;/span&gt;see:
&amp;lt;https://www.gnu.org/software/gdb/bugs/&amp;gt;.
Find&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;GDB&lt;span class="w"&gt; &lt;/span&gt;manual&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;other&lt;span class="w"&gt; &lt;/span&gt;documentation&lt;span class="w"&gt; &lt;/span&gt;resources&lt;span class="w"&gt; &lt;/span&gt;online&lt;span class="w"&gt; &lt;/span&gt;at:
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;http://www.gnu.org/software/gdb/documentation/&amp;gt;.

For&lt;span class="w"&gt; &lt;/span&gt;help,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;help&amp;quot;&lt;/span&gt;.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;apropos word&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;commands&lt;span class="w"&gt; &lt;/span&gt;related&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;word&amp;quot;&lt;/span&gt;.
add&lt;span class="w"&gt; &lt;/span&gt;symbol&lt;span class="w"&gt; &lt;/span&gt;table&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;build/platform/generic/firmware/fw_payload.elf&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at
&lt;span class="w"&gt;    &lt;/span&gt;.text_addr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x80000000
&lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
Reading&lt;span class="w"&gt; &lt;/span&gt;symbols&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;build/platform/generic/firmware/fw_payload.elf...
Remote&lt;span class="w"&gt; &lt;/span&gt;debugging&lt;span class="w"&gt; &lt;/span&gt;using&lt;span class="w"&gt; &lt;/span&gt;localhost:1234
⚠️&lt;span class="w"&gt; &lt;/span&gt;warning:&lt;span class="w"&gt; &lt;/span&gt;No&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;has&lt;span class="w"&gt; &lt;/span&gt;been&lt;span class="w"&gt; &lt;/span&gt;specified&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;target&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;support
determining&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;automatically.&lt;span class="w"&gt;  &lt;/span&gt;Try&lt;span class="w"&gt; &lt;/span&gt;using&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;file&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;command.
0x0000000000001000&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;b&lt;span class="w"&gt; &lt;/span&gt;sbi_boot_print_banner
Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;0x8000b14c:&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;/var/home/zim/Sources/opensbi/lib/sbi/sbi_init.c,&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;51&lt;/span&gt;.
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.

Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;sbi_boot_print_banner&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;scratch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x80046000&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;/var/home/zim/Sources/opensbi/lib/sbi/sbi_init.c:51
&lt;span class="m"&gt;51&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;scratch-&amp;gt;options&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;SBI_SCRATCH_NO_BOOT_PRINTS&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;55&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;sbi_printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;\nOpenSBI %s\n&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;OPENSBI_VERSION_GIT&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;62&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;sbi_printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Build time: %s\n&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;OPENSBI_BUILD_TIME_STAMP&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;66&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;sbi_printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Build compiler: %s\n&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;OPENSBI_BUILD_COMPILER_VERSION&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;69&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;sbi_printf&lt;span class="o"&gt;(&lt;/span&gt;BANNER&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.
&lt;span class="o"&gt;[&lt;/span&gt;Inferior&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;process&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;exited&lt;span class="w"&gt; &lt;/span&gt;normally&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;U-Boot&lt;/h2&gt;
&lt;p&gt;Das U-Boot (Universal Boot Loader) is the de facto open source bootloader used in embedded devices. Its main job is to initialise hardware, load the operating system (usually Linux) into memory, and boot. It supports multi-stage booting, often split into an SPL (Secondary Program Loader) that sets up basic RAM, and full U-Boot proper that takes over to load the next stage. It features an interactive command line interface (CLI) usually accessible via serial port UART used to interrupt the boot sequence, poke/test hardware, and configure various aspects of the system boot. As part of its hardware management, it allows loading files from file systems on various (boot) devices like NAND/NOR flashes, (e)MMC/SD cards/chips, NVMe drives, SD cards, UFS, or USB mass storage. But it also features a built-in network stack for downloading images over Ethernet using e.g. TFTP, which is very convenient for debugging/development. Last but not least, it does the OS hand-off, including preparing a device tree and loading the Linux kernel image into RAM before transferring execution. Alternatively, as we will see later on, it may also hand off to a next-stage UEFI boot manager.&lt;/p&gt;
&lt;p&gt;The first phase of U-Boot's SPL consists of M-mode assembly entry. The hardware reset vector gets directly set to the entry point &lt;code&gt;_start&lt;/code&gt; in &lt;code&gt;arch/riscv/cpu/start.S&lt;/code&gt;. The hardware setup includes configuring fundamental control and status registers (CSRs), like disabling interrupts (&lt;code&gt;mstatus&lt;/code&gt;), clearing delegation registers (&lt;code&gt;medeleg&lt;/code&gt;/&lt;code&gt;mideleg&lt;/code&gt;), and setting the trap vector base address (&lt;code&gt;mtvec&lt;/code&gt;) to catch early faults. The hart ID is extracted by querying via the &lt;code&gt;csrr a0, mhartid&lt;/code&gt; instruction. The primary executing core (boot "hart") continues initialisation, while auxiliary harts are placed in a secondary wait loop if symmetric multiprocessing (&lt;code&gt;CONFIG_SPL_SMP&lt;/code&gt;) is enabled, as is the case on QEMU. An early runtime C stack is prepared in internal SRAM or cache-as-RAM (L2 cache). The &lt;code&gt;gp&lt;/code&gt; (Global Pointer) register is set up to reserve space for the early global data structure pointer (&lt;code&gt;gd&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;The second phase consists of the pre-RAM initialisation (&lt;code&gt;board_init_f&lt;/code&gt;), which now runs as a C runtime within &lt;code&gt;arch/riscv/lib/spl.c&lt;/code&gt;. The &lt;code&gt;spl_early_init()&lt;/code&gt; sets up a basic driver model (DM) tree structure to find essential SoC components configured via device tree, either passed by QEMU or baked into U-Boot SPL. The &lt;code&gt;riscv_cpu_setup()&lt;/code&gt; issues early CPU core tweaks and configures the physical memory protection (PMP) boundaries. The &lt;code&gt;preloader_console_init()&lt;/code&gt; prepares the system console (typically a standard 16550 UART or similar device), allowing debug log prints. And &lt;code&gt;spl_board_init_f()&lt;/code&gt; is the board/SoC-specific implementation called to power up, configure clock phase-locked loops (PLLs), and explicitly initialise the external RAM.&lt;/p&gt;
&lt;p&gt;The third phase consists of the post-RAM initialisation (&lt;code&gt;board_init_r&lt;/code&gt;), which now runs within generic U-Boot SPL &lt;code&gt;common/spl/spl.c&lt;/code&gt;. The operational stack and active global data structures get transferred out of early internal SRAM into RAM if configured to do so (&lt;code&gt;CONFIG_SPL_STACK_R&lt;/code&gt;), however, on QEMU this is not the case. Storage media drivers and devices, such as MMC/SD cards or NAND/NOR flashes, get initialised as preparation for loading the next stage payloads, the exact source of which may be dynamically discovered.&lt;/p&gt;
&lt;p&gt;The fourth and final phase of the SPL consists of image loading and executing the jump into OpenSBI, which does the transition from M-mode, where the SPL runs, to S-mode for U-Boot proper to accommodate an operating system like Linux. The FIT image gets parsed and may consist of U-Boot proper, OpenSBI and optionally a device tree blob (DTB). However, QEMU usually passes in its own DTB via &lt;code&gt;FW_DYNAMIC&lt;/code&gt; mechanism. U-Boot proper gets loaded into its target execution RAM address, while OpenSBI is loaded into its respective baseline location. The instruction pipeline is flushed in &lt;code&gt;invalidate_icache_all()&lt;/code&gt;, clearing caches. Last but not least, the jump gets initiated by loading the target execution entry address to point directly to OpenSBI's entry point. The information gets passed via regular RISC-V registers: &lt;code&gt;a0&lt;/code&gt; receives the boot hart ID (&lt;code&gt;gd-&amp;gt;arch.boot_hart&lt;/code&gt;), &lt;code&gt;a1&lt;/code&gt; the memory pointer referencing the DTB, and &lt;code&gt;a2&lt;/code&gt; the memory pointer referencing the &lt;code&gt;struct fw_dynamic_info&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once OpenSBI completes its M-mode initialisation, it transitions to S-mode, and ultimately branches to the pre-loaded U-Boot proper entry address in system RAM to continue the full boot sequence.&lt;/p&gt;
&lt;p&gt;The first phase of U-Boot proper consists of S-mode assembly entry (&lt;code&gt;start.S&lt;/code&gt;), where the incoming information from OpenSBI is parsed, the S-mode trap vector base address register (&lt;code&gt;stvec&lt;/code&gt;) is set up to catch early faults, the S-mode interrupt enable flags (&lt;code&gt;sie&lt;/code&gt;) are cleared to ensure sequential, deterministic initialisation, and the global pointer registers, thread pointer (&lt;code&gt;tp&lt;/code&gt;) and global pointer (&lt;code&gt;gp&lt;/code&gt;), get loaded.&lt;/p&gt;
&lt;p&gt;The second phase consists of pre-relocation C initialisation (&lt;code&gt;board_init_f&lt;/code&gt;), where a sequential array of early setup functions get executed, the driver model (DM) gets activated in &lt;code&gt;dm_init_f&lt;/code&gt; from device tree information for early core devices (clocks, reset controllers, and pin multiplexers), the memory layout is calculated reserving space at the top of memory to store the relocated U-Boot binary, the master page tables, the permanent stack, and the new heap.&lt;/p&gt;
&lt;p&gt;The third phase consists of the binary relocation &lt;code&gt;relocate.S&lt;/code&gt;, where U-Boot migrates itself out of the way towards the top of memory leaving low memory addresses available for further boot stages, the &lt;code&gt;.rela.dyn&lt;/code&gt; relocation tables are iterated updating all address offsets to point to the newly relocated ones, the RISC-V fence instruction &lt;code&gt;fence.i&lt;/code&gt; gets called to flush any stale instructions out of the CPU pipeline execution cache.&lt;/p&gt;
&lt;p&gt;The fourth phase consists of the post-relocation C initialisation (&lt;code&gt;board_init_r&lt;/code&gt;), where the full suite of drivers as declared in the device tree gets bound to, the environment gets loaded from non-volatile storage with configuration scripts and parameters (e.g. bootargs, bootcmd), and further subsystems like Ethernet, PCIe/NVMe, and USB get dynamically probed.&lt;/p&gt;
&lt;p&gt;The fifth and final phase consists of the main loop and OS handover, where user interaction may be established or the default boot initiated after a certain countdown time. The interactive shell features command parsing in a &lt;code&gt;cli_loop&lt;/code&gt;, where user debugging commands are evaluated. The OS handover gets initiated by a booting call like booti, bootm or bootefi, where the system gets prepared to transfer control to the next S-mode stage, first disabling all active hardware peripheral timers, flushing caches, and triggering the next entry point. Just like in previous stages, information gets passed using the &lt;code&gt;FW_DYNAMIC&lt;/code&gt; mechanism via CPU registers.&lt;/p&gt;
&lt;p&gt;We get the U-Boot source code:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/u-boot/u-boot.git
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;u-boot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;However, if we do like the OpenSBI banner to be printed, we need to change its configuration as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;u-boot&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;diff
diff&lt;span class="w"&gt; &lt;/span&gt;--git&lt;span class="w"&gt; &lt;/span&gt;a/configs/qemu-riscv64_spl_defconfig&lt;span class="w"&gt; &lt;/span&gt;b/configs/qemu-riscv64_spl_defconfig
index&lt;span class="w"&gt; &lt;/span&gt;04dec72ceac7..4212699c3dd3&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;100644&lt;/span&gt;
---&lt;span class="w"&gt; &lt;/span&gt;a/configs/qemu-riscv64_spl_defconfig
+++&lt;span class="w"&gt; &lt;/span&gt;b/configs/qemu-riscv64_spl_defconfig
@@&lt;span class="w"&gt; &lt;/span&gt;-18,6&lt;span class="w"&gt; &lt;/span&gt;+18,7&lt;span class="w"&gt; &lt;/span&gt;@@&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CONFIG_DISPLAY_BOARDINFO&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# CONFIG_BOARD_INIT is not set&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CONFIG_SPL_MAX_SIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x100000
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CONFIG_SPL_SYS_MALLOC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
+CONFIG_SPL_OPENSBI_SCRATCH_OPTIONS&lt;span class="o"&gt;=&lt;/span&gt;0x0
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;# CONFIG_CMD_MII is not set&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CONFIG_ENV_RELOC_GD_ENV_ADDR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;CONFIG_DM_MTD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;y
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Guess how/where I found out about this OpenSBI scratch-options handling? Yes, the attentive reader noticed it during our previous debugging session (the terminal box just above the U-Boot section) at line 32, source code line 51!&lt;/p&gt;
&lt;p&gt;Only now we configure/build U-Boot for SPL:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;u-boot&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;qemu-riscv64_spl_defconfig
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;u-boot&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;make&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;OPENSBI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;../opensbi/build/platform/generic/firmware/fw_dynamic.bin
...
&lt;span class="w"&gt;  &lt;/span&gt;LDS&lt;span class="w"&gt;     &lt;/span&gt;spl/u-boot-spl.lds
&lt;span class="w"&gt;  &lt;/span&gt;LD&lt;span class="w"&gt;      &lt;/span&gt;spl/u-boot-spl
&lt;span class="w"&gt;  &lt;/span&gt;OBJCOPY&lt;span class="w"&gt; &lt;/span&gt;spl/u-boot-spl-nodtb.bin
mkdir&lt;span class="w"&gt; &lt;/span&gt;-p&lt;span class="w"&gt; &lt;/span&gt;spl/dts/
&lt;span class="w"&gt;  &lt;/span&gt;FDTGREP&lt;span class="w"&gt; &lt;/span&gt;spl/dts/dt-spl.dtb
&lt;span class="w"&gt;  &lt;/span&gt;COPY&lt;span class="w"&gt;    &lt;/span&gt;spl/u-boot-spl.dtb
&lt;span class="w"&gt;  &lt;/span&gt;CAT&lt;span class="w"&gt;     &lt;/span&gt;spl/u-boot-spl-dtb.bin
&lt;span class="w"&gt;  &lt;/span&gt;COPY&lt;span class="w"&gt;    &lt;/span&gt;spl/u-boot-spl.bin
&lt;span class="w"&gt;  &lt;/span&gt;SYM&lt;span class="w"&gt;     &lt;/span&gt;spl/u-boot-spl.sym
&lt;span class="w"&gt;  &lt;/span&gt;MKIMAGE&lt;span class="w"&gt; &lt;/span&gt;u-boot.img
&lt;span class="w"&gt;  &lt;/span&gt;COPY&lt;span class="w"&gt;    &lt;/span&gt;u-boot.dtb
&lt;span class="w"&gt;  &lt;/span&gt;MKIMAGE&lt;span class="w"&gt; &lt;/span&gt;u-boot-dtb.img
&lt;span class="w"&gt;  &lt;/span&gt;BINMAN&lt;span class="w"&gt;  &lt;/span&gt;.binman_stamp
&lt;span class="w"&gt;  &lt;/span&gt;OFCHK&lt;span class="w"&gt;   &lt;/span&gt;.config
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This actually builds the SPL binary &lt;code&gt;spl/u-boot-spl.bin&lt;/code&gt;, as well as the U-Boot proper binary &lt;code&gt;u-boot.bin&lt;/code&gt; packaged together with the OpenSBI binary &lt;code&gt;fw_dynamic.bin&lt;/code&gt;, into a single flattened image tree (FIT) &lt;code&gt;itb.fit.fit&lt;/code&gt; blob. Its former name was &lt;code&gt;u-boot.itb&lt;/code&gt;, where ITB meant image tree blob. As I am more familiar with that former name, I will stick to &lt;code&gt;u-boot.itb&lt;/code&gt; throughout this article. Such &lt;code&gt;.fit&lt;/code&gt; or &lt;code&gt;.itb&lt;/code&gt; files may be inspected using U-Boot's &lt;code&gt;mkimage&lt;/code&gt; tool:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;u-boot&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;mkimage&lt;span class="w"&gt; &lt;/span&gt;-l&lt;span class="w"&gt; &lt;/span&gt;u-boot.itb
FIT&lt;span class="w"&gt; &lt;/span&gt;description:&lt;span class="w"&gt; &lt;/span&gt;Configuration&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;load&lt;span class="w"&gt; &lt;/span&gt;OpenSBI&lt;span class="w"&gt; &lt;/span&gt;before&lt;span class="w"&gt; &lt;/span&gt;U-Boot
Created:&lt;span class="w"&gt;         &lt;/span&gt;Sat&lt;span class="w"&gt; &lt;/span&gt;May&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;:23:39&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2026&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;Image&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;uboot&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;Description:&lt;span class="w"&gt;  &lt;/span&gt;U-Boot
&lt;span class="w"&gt;  &lt;/span&gt;Created:&lt;span class="w"&gt;      &lt;/span&gt;Sat&lt;span class="w"&gt; &lt;/span&gt;May&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;:23:39&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2026&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;Type:&lt;span class="w"&gt;         &lt;/span&gt;Standalone&lt;span class="w"&gt; &lt;/span&gt;Program
&lt;span class="w"&gt;  &lt;/span&gt;Compression:&lt;span class="w"&gt;  &lt;/span&gt;uncompressed
&lt;span class="w"&gt;  &lt;/span&gt;Data&lt;span class="w"&gt; &lt;/span&gt;Size:&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;895160&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Bytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;874&lt;/span&gt;.18&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;KiB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.85&lt;span class="w"&gt; &lt;/span&gt;MiB
&lt;span class="w"&gt;  &lt;/span&gt;Architecture:&lt;span class="w"&gt; &lt;/span&gt;RISC-V
&lt;span class="w"&gt;  &lt;/span&gt;Load&lt;span class="w"&gt; &lt;/span&gt;Address:&lt;span class="w"&gt; &lt;/span&gt;0x81200000
&lt;span class="w"&gt;  &lt;/span&gt;Entry&lt;span class="w"&gt; &lt;/span&gt;Point:&lt;span class="w"&gt;  &lt;/span&gt;unavailable
&lt;span class="w"&gt; &lt;/span&gt;Image&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;opensbi&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;Description:&lt;span class="w"&gt;  &lt;/span&gt;OpenSBI&lt;span class="w"&gt; &lt;/span&gt;fw_dynamic&lt;span class="w"&gt; &lt;/span&gt;Firmware
&lt;span class="w"&gt;  &lt;/span&gt;Created:&lt;span class="w"&gt;      &lt;/span&gt;Sat&lt;span class="w"&gt; &lt;/span&gt;May&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;18&lt;/span&gt;:23:39&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2026&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;Type:&lt;span class="w"&gt;         &lt;/span&gt;Firmware
&lt;span class="w"&gt;  &lt;/span&gt;Compression:&lt;span class="w"&gt;  &lt;/span&gt;uncompressed
&lt;span class="w"&gt;  &lt;/span&gt;Data&lt;span class="w"&gt; &lt;/span&gt;Size:&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;275768&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;Bytes&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;269&lt;/span&gt;.30&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;KiB&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;.26&lt;span class="w"&gt; &lt;/span&gt;MiB
&lt;span class="w"&gt;  &lt;/span&gt;Architecture:&lt;span class="w"&gt; &lt;/span&gt;RISC-V
&lt;span class="w"&gt;  &lt;/span&gt;OS:&lt;span class="w"&gt;           &lt;/span&gt;RISC-V&lt;span class="w"&gt; &lt;/span&gt;OpenSBI
&lt;span class="w"&gt;  &lt;/span&gt;Load&lt;span class="w"&gt; &lt;/span&gt;Address:&lt;span class="w"&gt; &lt;/span&gt;0x80100000
&lt;span class="w"&gt; &lt;/span&gt;Default&lt;span class="w"&gt; &lt;/span&gt;Configuration:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;conf-1&amp;#39;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;Configuration&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;conf-1&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;  &lt;/span&gt;Description:&lt;span class="w"&gt;  &lt;/span&gt;NAME
&lt;span class="w"&gt;  &lt;/span&gt;Kernel:&lt;span class="w"&gt;       &lt;/span&gt;unavailable
&lt;span class="w"&gt;  &lt;/span&gt;Firmware:&lt;span class="w"&gt;     &lt;/span&gt;opensbi
&lt;span class="w"&gt;  &lt;/span&gt;Loadables:&lt;span class="w"&gt;    &lt;/span&gt;uboot
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The interested reader may find much more information in the &lt;a href="https://fitspec.osfw.foundation"&gt;Flattened Image Tree Specification&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But, now, let us actually launch this by QEMU, which is slightly more complex, as not only a &lt;code&gt;-bios&lt;/code&gt; argument, but also a loader device with the additional parts needs to be specified:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;  1&lt;/span&gt;
&lt;span class="normal"&gt;  2&lt;/span&gt;
&lt;span class="normal"&gt;  3&lt;/span&gt;
&lt;span class="normal"&gt;  4&lt;/span&gt;
&lt;span class="normal"&gt;  5&lt;/span&gt;
&lt;span class="normal"&gt;  6&lt;/span&gt;
&lt;span class="normal"&gt;  7&lt;/span&gt;
&lt;span class="normal"&gt;  8&lt;/span&gt;
&lt;span class="normal"&gt;  9&lt;/span&gt;
&lt;span class="normal"&gt; 10&lt;/span&gt;
&lt;span class="normal"&gt; 11&lt;/span&gt;
&lt;span class="normal"&gt; 12&lt;/span&gt;
&lt;span class="normal"&gt; 13&lt;/span&gt;
&lt;span class="normal"&gt; 14&lt;/span&gt;
&lt;span class="normal"&gt; 15&lt;/span&gt;
&lt;span class="normal"&gt; 16&lt;/span&gt;
&lt;span class="normal"&gt; 17&lt;/span&gt;
&lt;span class="normal"&gt; 18&lt;/span&gt;
&lt;span class="normal"&gt; 19&lt;/span&gt;
&lt;span class="normal"&gt; 20&lt;/span&gt;
&lt;span class="normal"&gt; 21&lt;/span&gt;
&lt;span class="normal"&gt; 22&lt;/span&gt;
&lt;span class="normal"&gt; 23&lt;/span&gt;
&lt;span class="normal"&gt; 24&lt;/span&gt;
&lt;span class="normal"&gt; 25&lt;/span&gt;
&lt;span class="normal"&gt; 26&lt;/span&gt;
&lt;span class="normal"&gt; 27&lt;/span&gt;
&lt;span class="normal"&gt; 28&lt;/span&gt;
&lt;span class="normal"&gt; 29&lt;/span&gt;
&lt;span class="normal"&gt; 30&lt;/span&gt;
&lt;span class="normal"&gt; 31&lt;/span&gt;
&lt;span class="normal"&gt; 32&lt;/span&gt;
&lt;span class="normal"&gt; 33&lt;/span&gt;
&lt;span class="normal"&gt; 34&lt;/span&gt;
&lt;span class="normal"&gt; 35&lt;/span&gt;
&lt;span class="normal"&gt; 36&lt;/span&gt;
&lt;span class="normal"&gt; 37&lt;/span&gt;
&lt;span class="normal"&gt; 38&lt;/span&gt;
&lt;span class="normal"&gt; 39&lt;/span&gt;
&lt;span class="normal"&gt; 40&lt;/span&gt;
&lt;span class="normal"&gt; 41&lt;/span&gt;
&lt;span class="normal"&gt; 42&lt;/span&gt;
&lt;span class="normal"&gt; 43&lt;/span&gt;
&lt;span class="normal"&gt; 44&lt;/span&gt;
&lt;span class="normal"&gt; 45&lt;/span&gt;
&lt;span class="normal"&gt; 46&lt;/span&gt;
&lt;span class="normal"&gt; 47&lt;/span&gt;
&lt;span class="normal"&gt; 48&lt;/span&gt;
&lt;span class="normal"&gt; 49&lt;/span&gt;
&lt;span class="normal"&gt; 50&lt;/span&gt;
&lt;span class="normal"&gt; 51&lt;/span&gt;
&lt;span class="normal"&gt; 52&lt;/span&gt;
&lt;span class="normal"&gt; 53&lt;/span&gt;
&lt;span class="normal"&gt; 54&lt;/span&gt;
&lt;span class="normal"&gt; 55&lt;/span&gt;
&lt;span class="normal"&gt; 56&lt;/span&gt;
&lt;span class="normal"&gt; 57&lt;/span&gt;
&lt;span class="normal"&gt; 58&lt;/span&gt;
&lt;span class="normal"&gt; 59&lt;/span&gt;
&lt;span class="normal"&gt; 60&lt;/span&gt;
&lt;span class="normal"&gt; 61&lt;/span&gt;
&lt;span class="normal"&gt; 62&lt;/span&gt;
&lt;span class="normal"&gt; 63&lt;/span&gt;
&lt;span class="normal"&gt; 64&lt;/span&gt;
&lt;span class="normal"&gt; 65&lt;/span&gt;
&lt;span class="normal"&gt; 66&lt;/span&gt;
&lt;span class="normal"&gt; 67&lt;/span&gt;
&lt;span class="normal"&gt; 68&lt;/span&gt;
&lt;span class="normal"&gt; 69&lt;/span&gt;
&lt;span class="normal"&gt; 70&lt;/span&gt;
&lt;span class="normal"&gt; 71&lt;/span&gt;
&lt;span class="normal"&gt; 72&lt;/span&gt;
&lt;span class="normal"&gt; 73&lt;/span&gt;
&lt;span class="normal"&gt; 74&lt;/span&gt;
&lt;span class="normal"&gt; 75&lt;/span&gt;
&lt;span class="normal"&gt; 76&lt;/span&gt;
&lt;span class="normal"&gt; 77&lt;/span&gt;
&lt;span class="normal"&gt; 78&lt;/span&gt;
&lt;span class="normal"&gt; 79&lt;/span&gt;
&lt;span class="normal"&gt; 80&lt;/span&gt;
&lt;span class="normal"&gt; 81&lt;/span&gt;
&lt;span class="normal"&gt; 82&lt;/span&gt;
&lt;span class="normal"&gt; 83&lt;/span&gt;
&lt;span class="normal"&gt; 84&lt;/span&gt;
&lt;span class="normal"&gt; 85&lt;/span&gt;
&lt;span class="normal"&gt; 86&lt;/span&gt;
&lt;span class="normal"&gt; 87&lt;/span&gt;
&lt;span class="normal"&gt; 88&lt;/span&gt;
&lt;span class="normal"&gt; 89&lt;/span&gt;
&lt;span class="normal"&gt; 90&lt;/span&gt;
&lt;span class="normal"&gt; 91&lt;/span&gt;
&lt;span class="normal"&gt; 92&lt;/span&gt;
&lt;span class="normal"&gt; 93&lt;/span&gt;
&lt;span class="normal"&gt; 94&lt;/span&gt;
&lt;span class="normal"&gt; 95&lt;/span&gt;
&lt;span class="normal"&gt; 96&lt;/span&gt;
&lt;span class="normal"&gt; 97&lt;/span&gt;
&lt;span class="normal"&gt; 98&lt;/span&gt;
&lt;span class="normal"&gt; 99&lt;/span&gt;
&lt;span class="normal"&gt;100&lt;/span&gt;
&lt;span class="normal"&gt;101&lt;/span&gt;
&lt;span class="normal"&gt;102&lt;/span&gt;
&lt;span class="normal"&gt;103&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;u-boot&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;qemu-system-riscv64&lt;span class="w"&gt; &lt;/span&gt;-bios&lt;span class="w"&gt; &lt;/span&gt;spl/u-boot-spl.bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-device&lt;span class="w"&gt; &lt;/span&gt;loader,file&lt;span class="o"&gt;=&lt;/span&gt;u-boot.itb,addr&lt;span class="o"&gt;=&lt;/span&gt;0x80200000&lt;span class="w"&gt; &lt;/span&gt;-machine&lt;span class="w"&gt; &lt;/span&gt;virt&lt;span class="w"&gt; &lt;/span&gt;-nographic

U-Boot&lt;span class="w"&gt; &lt;/span&gt;SPL&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2026&lt;/span&gt;.07-rc3-00008-g987907ae4bcc-dirty&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Jun&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2026&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;04&lt;/span&gt;:26:09&lt;span class="w"&gt; &lt;/span&gt;+0200&lt;span class="o"&gt;)&lt;/span&gt;
Trying&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;boot&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;RAM

OpenSBI&lt;span class="w"&gt; &lt;/span&gt;v1.8.1
Build&lt;span class="w"&gt; &lt;/span&gt;time:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2026&lt;/span&gt;-05-29&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;17&lt;/span&gt;:28:04&lt;span class="w"&gt; &lt;/span&gt;+0200
Build&lt;span class="w"&gt; &lt;/span&gt;compiler:&lt;span class="w"&gt; &lt;/span&gt;clang&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;21&lt;/span&gt;.1.1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;https://github.com/llvm/llvm-project.git
&lt;span class="w"&gt; &lt;/span&gt;5a86dc996c26299de63effc927075dcbfb924167&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;   &lt;/span&gt;____&lt;span class="w"&gt;                    &lt;/span&gt;_____&lt;span class="w"&gt; &lt;/span&gt;____&lt;span class="w"&gt; &lt;/span&gt;_____
&lt;span class="w"&gt;  &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;__&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\ &lt;/span&gt;&lt;span class="w"&gt;                 &lt;/span&gt;/&lt;span class="w"&gt; &lt;/span&gt;____&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\_&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;_&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;__&lt;span class="w"&gt;   &lt;/span&gt;___&lt;span class="w"&gt; &lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;__&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;___&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;_ \ / _ \ &amp;#39;&lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\ \_&lt;/span&gt;__&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;_&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;__&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;__/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;____&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_
&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="se"&gt;\_&lt;/span&gt;___/&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;.__/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\_&lt;/span&gt;__&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="p"&gt;|&lt;/span&gt;_____/&lt;span class="p"&gt;|&lt;/span&gt;____/_____&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;
&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt;_&lt;span class="p"&gt;|&lt;/span&gt;

Platform&lt;span class="w"&gt; &lt;/span&gt;Name&lt;span class="w"&gt;               &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;riscv-virtio,qemu
Platform&lt;span class="w"&gt; &lt;/span&gt;Features&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;medeleg
Platform&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Count&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;
Platform&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Protection&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;pmp
Platform&lt;span class="w"&gt; &lt;/span&gt;IPI&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;aclint-mswi
Platform&lt;span class="w"&gt; &lt;/span&gt;Timer&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;       &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;aclint-mtimer&lt;span class="w"&gt; &lt;/span&gt;@&lt;span class="w"&gt; &lt;/span&gt;10000000Hz
Platform&lt;span class="w"&gt; &lt;/span&gt;Console&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;     &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;uart8250
Platform&lt;span class="w"&gt; &lt;/span&gt;HSM&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;---
Platform&lt;span class="w"&gt; &lt;/span&gt;PMU&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;---
Platform&lt;span class="w"&gt; &lt;/span&gt;Reboot&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;      &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;syscon-reboot
Platform&lt;span class="w"&gt; &lt;/span&gt;Shutdown&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;syscon-poweroff
Platform&lt;span class="w"&gt; &lt;/span&gt;Suspend&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;     &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;---
Platform&lt;span class="w"&gt; &lt;/span&gt;CPPC&lt;span class="w"&gt; &lt;/span&gt;Device&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;---
Firmware&lt;span class="w"&gt; &lt;/span&gt;Base&lt;span class="w"&gt;               &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x80100000
Firmware&lt;span class="w"&gt; &lt;/span&gt;Size&lt;span class="w"&gt;               &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;321&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB
Firmware&lt;span class="w"&gt; &lt;/span&gt;RW&lt;span class="w"&gt; &lt;/span&gt;Offset&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x40000
Firmware&lt;span class="w"&gt; &lt;/span&gt;RW&lt;span class="w"&gt; &lt;/span&gt;Size&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;65&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB
Firmware&lt;span class="w"&gt; &lt;/span&gt;Heap&lt;span class="w"&gt; &lt;/span&gt;Offset&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x47000
Firmware&lt;span class="w"&gt; &lt;/span&gt;Heap&lt;span class="w"&gt; &lt;/span&gt;Size&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;37&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;total&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;reserved&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;12&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;used&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;23&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;KB&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;free&lt;span class="o"&gt;)&lt;/span&gt;
Firmware&lt;span class="w"&gt; &lt;/span&gt;Scratch&lt;span class="w"&gt; &lt;/span&gt;Size&lt;span class="w"&gt;       &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4096&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;B&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;total&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1464&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;B&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;used&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2632&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;B&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;free&lt;span class="o"&gt;)&lt;/span&gt;
Runtime&lt;span class="w"&gt; &lt;/span&gt;SBI&lt;span class="w"&gt; &lt;/span&gt;Version&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;.0
Standard&lt;span class="w"&gt; &lt;/span&gt;SBI&lt;span class="w"&gt; &lt;/span&gt;Extensions&lt;span class="w"&gt;     &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;time,rfnc,ipi,base,hsm,srst,pmu,dbcn,fwft,legacy,dbtr,sse
Experimental&lt;span class="w"&gt; &lt;/span&gt;SBI&lt;span class="w"&gt; &lt;/span&gt;Extensions&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;none

Domain0&lt;span class="w"&gt; &lt;/span&gt;Name&lt;span class="w"&gt;                &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;root
Domain0&lt;span class="w"&gt; &lt;/span&gt;Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;HARTs&lt;span class="w"&gt;               &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;*
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region00&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000080140000-0x000000008015ffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;F,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region01&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000080100000-0x000000008013ffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;F,R,X&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region02&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000000100000-0x0000000000100fff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;I,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;R,W&lt;span class="o"&gt;)&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region03&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000010000000-0x0000000010000fff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;I,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;R,W&lt;span class="o"&gt;)&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region04&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000002000000-0x000000000200ffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;I,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region05&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x000000000c400000-0x000000000c5fffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;I,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;R,W&lt;span class="o"&gt;)&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region06&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x000000000c000000-0x000000000c3fffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;I,R,W&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;R,W&lt;span class="o"&gt;)&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Region07&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000000000000-0xffffffffffffffff&lt;span class="w"&gt; &lt;/span&gt;M:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;S/U:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;R,W,X&lt;span class="o"&gt;)&lt;/span&gt;
Domain0&lt;span class="w"&gt; &lt;/span&gt;Next&lt;span class="w"&gt; &lt;/span&gt;Address&lt;span class="w"&gt;        &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000081200000
Domain0&lt;span class="w"&gt; &lt;/span&gt;Next&lt;span class="w"&gt; &lt;/span&gt;Arg1&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x00000000812da8c8
Domain0&lt;span class="w"&gt; &lt;/span&gt;Next&lt;span class="w"&gt; &lt;/span&gt;Mode&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;S-mode
Domain0&lt;span class="w"&gt; &lt;/span&gt;SysReset&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;yes
Domain0&lt;span class="w"&gt; &lt;/span&gt;SysSuspend&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;yes

Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;ID&lt;span class="w"&gt;                &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Domain&lt;span class="w"&gt;            &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;root
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Priv&lt;span class="w"&gt; &lt;/span&gt;Version&lt;span class="w"&gt;      &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;v1.12
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Base&lt;span class="w"&gt; &lt;/span&gt;ISA&lt;span class="w"&gt;          &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;rv64imafdch
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;ISA&lt;span class="w"&gt; &lt;/span&gt;Extensions&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;sstc,zicntr,zihpm,zicboz,zicbom,sdtrig,svadu
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;PMP&lt;span class="w"&gt; &lt;/span&gt;Count&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;PMP&lt;span class="w"&gt; &lt;/span&gt;Granularity&lt;span class="w"&gt;   &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bits
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;PMP&lt;span class="w"&gt; &lt;/span&gt;Address&lt;span class="w"&gt; &lt;/span&gt;Bits&lt;span class="w"&gt;  &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;54&lt;/span&gt;
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;MHPM&lt;span class="w"&gt; &lt;/span&gt;Info&lt;span class="w"&gt;         &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;16&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;0x0007fff8&lt;span class="o"&gt;)&lt;/span&gt;
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;Debug&lt;span class="w"&gt; &lt;/span&gt;Triggers&lt;span class="w"&gt;    &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;triggers
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;MIDELEG&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000000001666
Boot&lt;span class="w"&gt; &lt;/span&gt;HART&lt;span class="w"&gt; &lt;/span&gt;MEDELEG&lt;span class="w"&gt;           &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;0x0000000000f4b509


U-Boot&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2026&lt;/span&gt;.07-rc3-00008-g987907ae4bcc-dirty&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Jun&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;01&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2026&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;04&lt;/span&gt;:26:09&lt;span class="w"&gt; &lt;/span&gt;+0200&lt;span class="o"&gt;)&lt;/span&gt;

CPU:&lt;span class="w"&gt;   &lt;/span&gt;riscv
Model:&lt;span class="w"&gt; &lt;/span&gt;riscv-virtio,qemu
DRAM:&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;128&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;MiB
using&lt;span class="w"&gt; &lt;/span&gt;memory&lt;span class="w"&gt; &lt;/span&gt;0x86ef5000-0x87715000&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;malloc&lt;span class="o"&gt;()&lt;/span&gt;
Core:&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;27&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;devices,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;14&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;uclasses,&lt;span class="w"&gt; &lt;/span&gt;devicetree:&lt;span class="w"&gt; &lt;/span&gt;board
Flash:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;MiB
Loading&lt;span class="w"&gt; &lt;/span&gt;Environment&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;nowhere...&lt;span class="w"&gt; &lt;/span&gt;OK
In:&lt;span class="w"&gt;    &lt;/span&gt;serial,usbkbd
Out:&lt;span class="w"&gt;   &lt;/span&gt;serial,vidconsole
Err:&lt;span class="w"&gt;   &lt;/span&gt;serial,vidconsole
No&lt;span class="w"&gt; &lt;/span&gt;USB&lt;span class="w"&gt; &lt;/span&gt;controllers&lt;span class="w"&gt; &lt;/span&gt;found
Net:&lt;span class="w"&gt;   &lt;/span&gt;No&lt;span class="w"&gt; &lt;/span&gt;ethernet&lt;span class="w"&gt; &lt;/span&gt;found.

Hit&lt;span class="w"&gt; &lt;/span&gt;any&lt;span class="w"&gt; &lt;/span&gt;key&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;stop&lt;span class="w"&gt; &lt;/span&gt;autoboot:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;

Device&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;unknown&lt;span class="w"&gt; &lt;/span&gt;device

Device&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;unknown&lt;span class="w"&gt; &lt;/span&gt;device

Device&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;unknown&lt;span class="w"&gt; &lt;/span&gt;device
scanning&lt;span class="w"&gt; &lt;/span&gt;bus&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;devices...

Device&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;unknown&lt;span class="w"&gt; &lt;/span&gt;device
No&lt;span class="w"&gt; &lt;/span&gt;ethernet&lt;span class="w"&gt; &lt;/span&gt;found.
No&lt;span class="w"&gt; &lt;/span&gt;ethernet&lt;span class="w"&gt; &lt;/span&gt;found.
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;poweroff
poweroff&lt;span class="w"&gt; &lt;/span&gt;...
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;Debugging&lt;/h3&gt;
&lt;p&gt;Now, it starts to get interesting. We can launch QEMU again in the first terminal like before, but again with the &lt;code&gt;-S&lt;/code&gt; argument, which instructs it to just wait for the gdb debugger to connect to it and further specify what to do next:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;u-boot&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;qemu-system-riscv64&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;-bios&lt;span class="w"&gt; &lt;/span&gt;spl/u-boot-spl.bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-device&lt;span class="w"&gt; &lt;/span&gt;loader,file&lt;span class="o"&gt;=&lt;/span&gt;u-boot.itb,addr&lt;span class="o"&gt;=&lt;/span&gt;0x80200000&lt;span class="w"&gt; &lt;/span&gt;-gdb&lt;span class="w"&gt; &lt;/span&gt;tcp::1234&lt;span class="w"&gt; &lt;/span&gt;-machine&lt;span class="w"&gt; &lt;/span&gt;virt&lt;span class="w"&gt; &lt;/span&gt;-nographic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the second terminal, we start gdb, giving it not only the OpenSBI symbol file as before, but also U-Boot's SPL and U-Boot proper symbol files as well. We instruct it to break at &lt;code&gt;board_init_f&lt;/code&gt; and &lt;code&gt;spl_invoke_opensbi&lt;/code&gt; in the U-Boot SPL, &lt;code&gt;sbi_boot_print_banner&lt;/code&gt; and &lt;code&gt;sbi_hart_switch_mode&lt;/code&gt; in OpenSBI and &lt;code&gt;display_options&lt;/code&gt; in U-Boot proper. Then we can let it print the various banners line by line:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;  1&lt;/span&gt;
&lt;span class="normal"&gt;  2&lt;/span&gt;
&lt;span class="normal"&gt;  3&lt;/span&gt;
&lt;span class="normal"&gt;  4&lt;/span&gt;
&lt;span class="normal"&gt;  5&lt;/span&gt;
&lt;span class="normal"&gt;  6&lt;/span&gt;
&lt;span class="normal"&gt;  7&lt;/span&gt;
&lt;span class="normal"&gt;  8&lt;/span&gt;
&lt;span class="normal"&gt;  9&lt;/span&gt;
&lt;span class="normal"&gt; 10&lt;/span&gt;
&lt;span class="normal"&gt; 11&lt;/span&gt;
&lt;span class="normal"&gt; 12&lt;/span&gt;
&lt;span class="normal"&gt; 13&lt;/span&gt;
&lt;span class="normal"&gt; 14&lt;/span&gt;
&lt;span class="normal"&gt; 15&lt;/span&gt;
&lt;span class="normal"&gt; 16&lt;/span&gt;
&lt;span class="normal"&gt; 17&lt;/span&gt;
&lt;span class="normal"&gt; 18&lt;/span&gt;
&lt;span class="normal"&gt; 19&lt;/span&gt;
&lt;span class="normal"&gt; 20&lt;/span&gt;
&lt;span class="normal"&gt; 21&lt;/span&gt;
&lt;span class="normal"&gt; 22&lt;/span&gt;
&lt;span class="normal"&gt; 23&lt;/span&gt;
&lt;span class="normal"&gt; 24&lt;/span&gt;
&lt;span class="normal"&gt; 25&lt;/span&gt;
&lt;span class="normal"&gt; 26&lt;/span&gt;
&lt;span class="normal"&gt; 27&lt;/span&gt;
&lt;span class="normal"&gt; 28&lt;/span&gt;
&lt;span class="normal"&gt; 29&lt;/span&gt;
&lt;span class="normal"&gt; 30&lt;/span&gt;
&lt;span class="normal"&gt; 31&lt;/span&gt;
&lt;span class="normal"&gt; 32&lt;/span&gt;
&lt;span class="normal"&gt; 33&lt;/span&gt;
&lt;span class="normal"&gt; 34&lt;/span&gt;
&lt;span class="normal"&gt; 35&lt;/span&gt;
&lt;span class="normal"&gt; 36&lt;/span&gt;
&lt;span class="normal"&gt; 37&lt;/span&gt;
&lt;span class="normal"&gt; 38&lt;/span&gt;
&lt;span class="normal"&gt; 39&lt;/span&gt;
&lt;span class="normal"&gt; 40&lt;/span&gt;
&lt;span class="normal"&gt; 41&lt;/span&gt;
&lt;span class="normal"&gt; 42&lt;/span&gt;
&lt;span class="normal"&gt; 43&lt;/span&gt;
&lt;span class="normal"&gt; 44&lt;/span&gt;
&lt;span class="normal"&gt; 45&lt;/span&gt;
&lt;span class="normal"&gt; 46&lt;/span&gt;
&lt;span class="normal"&gt; 47&lt;/span&gt;
&lt;span class="normal"&gt; 48&lt;/span&gt;
&lt;span class="normal"&gt; 49&lt;/span&gt;
&lt;span class="normal"&gt; 50&lt;/span&gt;
&lt;span class="normal"&gt; 51&lt;/span&gt;
&lt;span class="normal"&gt; 52&lt;/span&gt;
&lt;span class="normal"&gt; 53&lt;/span&gt;
&lt;span class="normal"&gt; 54&lt;/span&gt;
&lt;span class="normal"&gt; 55&lt;/span&gt;
&lt;span class="normal"&gt; 56&lt;/span&gt;
&lt;span class="normal"&gt; 57&lt;/span&gt;
&lt;span class="normal"&gt; 58&lt;/span&gt;
&lt;span class="normal"&gt; 59&lt;/span&gt;
&lt;span class="normal"&gt; 60&lt;/span&gt;
&lt;span class="normal"&gt; 61&lt;/span&gt;
&lt;span class="normal"&gt; 62&lt;/span&gt;
&lt;span class="normal"&gt; 63&lt;/span&gt;
&lt;span class="normal"&gt; 64&lt;/span&gt;
&lt;span class="normal"&gt; 65&lt;/span&gt;
&lt;span class="normal"&gt; 66&lt;/span&gt;
&lt;span class="normal"&gt; 67&lt;/span&gt;
&lt;span class="normal"&gt; 68&lt;/span&gt;
&lt;span class="normal"&gt; 69&lt;/span&gt;
&lt;span class="normal"&gt; 70&lt;/span&gt;
&lt;span class="normal"&gt; 71&lt;/span&gt;
&lt;span class="normal"&gt; 72&lt;/span&gt;
&lt;span class="normal"&gt; 73&lt;/span&gt;
&lt;span class="normal"&gt; 74&lt;/span&gt;
&lt;span class="normal"&gt; 75&lt;/span&gt;
&lt;span class="normal"&gt; 76&lt;/span&gt;
&lt;span class="normal"&gt; 77&lt;/span&gt;
&lt;span class="normal"&gt; 78&lt;/span&gt;
&lt;span class="normal"&gt; 79&lt;/span&gt;
&lt;span class="normal"&gt; 80&lt;/span&gt;
&lt;span class="normal"&gt; 81&lt;/span&gt;
&lt;span class="normal"&gt; 82&lt;/span&gt;
&lt;span class="normal"&gt; 83&lt;/span&gt;
&lt;span class="normal"&gt; 84&lt;/span&gt;
&lt;span class="normal"&gt; 85&lt;/span&gt;
&lt;span class="normal"&gt; 86&lt;/span&gt;
&lt;span class="normal"&gt; 87&lt;/span&gt;
&lt;span class="normal"&gt; 88&lt;/span&gt;
&lt;span class="normal"&gt; 89&lt;/span&gt;
&lt;span class="normal"&gt; 90&lt;/span&gt;
&lt;span class="normal"&gt; 91&lt;/span&gt;
&lt;span class="normal"&gt; 92&lt;/span&gt;
&lt;span class="normal"&gt; 93&lt;/span&gt;
&lt;span class="normal"&gt; 94&lt;/span&gt;
&lt;span class="normal"&gt; 95&lt;/span&gt;
&lt;span class="normal"&gt; 96&lt;/span&gt;
&lt;span class="normal"&gt; 97&lt;/span&gt;
&lt;span class="normal"&gt; 98&lt;/span&gt;
&lt;span class="normal"&gt; 99&lt;/span&gt;
&lt;span class="normal"&gt;100&lt;/span&gt;
&lt;span class="normal"&gt;101&lt;/span&gt;
&lt;span class="normal"&gt;102&lt;/span&gt;
&lt;span class="normal"&gt;103&lt;/span&gt;
&lt;span class="normal"&gt;104&lt;/span&gt;
&lt;span class="normal"&gt;105&lt;/span&gt;
&lt;span class="normal"&gt;106&lt;/span&gt;
&lt;span class="normal"&gt;107&lt;/span&gt;
&lt;span class="normal"&gt;108&lt;/span&gt;
&lt;span class="normal"&gt;109&lt;/span&gt;
&lt;span class="normal"&gt;110&lt;/span&gt;
&lt;span class="normal"&gt;111&lt;/span&gt;
&lt;span class="normal"&gt;112&lt;/span&gt;
&lt;span class="normal"&gt;113&lt;/span&gt;
&lt;span class="normal"&gt;114&lt;/span&gt;
&lt;span class="normal"&gt;115&lt;/span&gt;
&lt;span class="normal"&gt;116&lt;/span&gt;
&lt;span class="normal"&gt;117&lt;/span&gt;
&lt;span class="normal"&gt;118&lt;/span&gt;
&lt;span class="normal"&gt;119&lt;/span&gt;
&lt;span class="normal"&gt;120&lt;/span&gt;
&lt;span class="normal"&gt;121&lt;/span&gt;
&lt;span class="normal"&gt;122&lt;/span&gt;
&lt;span class="normal"&gt;123&lt;/span&gt;
&lt;span class="normal"&gt;124&lt;/span&gt;
&lt;span class="normal"&gt;125&lt;/span&gt;
&lt;span class="normal"&gt;126&lt;/span&gt;
&lt;span class="normal"&gt;127&lt;/span&gt;
&lt;span class="normal"&gt;128&lt;/span&gt;
&lt;span class="normal"&gt;129&lt;/span&gt;
&lt;span class="normal"&gt;130&lt;/span&gt;
&lt;span class="normal"&gt;131&lt;/span&gt;
&lt;span class="normal"&gt;132&lt;/span&gt;
&lt;span class="normal"&gt;133&lt;/span&gt;
&lt;span class="normal"&gt;134&lt;/span&gt;
&lt;span class="normal"&gt;135&lt;/span&gt;
&lt;span class="normal"&gt;136&lt;/span&gt;
&lt;span class="normal"&gt;137&lt;/span&gt;
&lt;span class="normal"&gt;138&lt;/span&gt;
&lt;span class="normal"&gt;139&lt;/span&gt;
&lt;span class="normal"&gt;140&lt;/span&gt;
&lt;span class="normal"&gt;141&lt;/span&gt;
&lt;span class="normal"&gt;142&lt;/span&gt;
&lt;span class="normal"&gt;143&lt;/span&gt;
&lt;span class="normal"&gt;144&lt;/span&gt;
&lt;span class="normal"&gt;145&lt;/span&gt;
&lt;span class="normal"&gt;146&lt;/span&gt;
&lt;span class="normal"&gt;147&lt;/span&gt;
&lt;span class="normal"&gt;148&lt;/span&gt;
&lt;span class="normal"&gt;149&lt;/span&gt;
&lt;span class="normal"&gt;150&lt;/span&gt;
&lt;span class="normal"&gt;151&lt;/span&gt;
&lt;span class="normal"&gt;152&lt;/span&gt;
&lt;span class="normal"&gt;153&lt;/span&gt;
&lt;span class="normal"&gt;154&lt;/span&gt;
&lt;span class="normal"&gt;155&lt;/span&gt;
&lt;span class="normal"&gt;156&lt;/span&gt;
&lt;span class="normal"&gt;157&lt;/span&gt;
&lt;span class="normal"&gt;158&lt;/span&gt;
&lt;span class="normal"&gt;159&lt;/span&gt;
&lt;span class="normal"&gt;160&lt;/span&gt;
&lt;span class="normal"&gt;161&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;u-boot&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;gdb&lt;span class="w"&gt; &lt;/span&gt;-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;add-symbol-file spl/u-boot-spl 0x80000000&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;add-symbol-file ../opensbi/build/platform/generic/firmware/fw_payload.elf 0x80100000&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;add-symbol-file u-boot 0x81200000&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;add-symbol-file u-boot 0x87715000&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;target remote localhost:1234&amp;#39;&lt;/span&gt;
GNU&lt;span class="w"&gt; &lt;/span&gt;gdb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Fedora&lt;span class="w"&gt; &lt;/span&gt;Linux&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;17&lt;/span&gt;.1-6.fc44
Copyright&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;C&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2025&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Free&lt;span class="w"&gt; &lt;/span&gt;Software&lt;span class="w"&gt; &lt;/span&gt;Foundation,&lt;span class="w"&gt; &lt;/span&gt;Inc.
License&lt;span class="w"&gt; &lt;/span&gt;GPLv3+:&lt;span class="w"&gt; &lt;/span&gt;GNU&lt;span class="w"&gt; &lt;/span&gt;GPL&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;later&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;http://gnu.org/licenses/gpl.html&amp;gt;
This&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;software:&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;are&lt;span class="w"&gt; &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;change&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;redistribute&lt;span class="w"&gt; &lt;/span&gt;it.
There&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;NO&lt;span class="w"&gt; &lt;/span&gt;WARRANTY,&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;extent&lt;span class="w"&gt; &lt;/span&gt;permitted&lt;span class="w"&gt; &lt;/span&gt;by&lt;span class="w"&gt; &lt;/span&gt;law.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show copying&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show warranty&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;details.
This&lt;span class="w"&gt; &lt;/span&gt;GDB&lt;span class="w"&gt; &lt;/span&gt;was&lt;span class="w"&gt; &lt;/span&gt;configured&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x86_64-redhat-linux-gnu&amp;quot;&lt;/span&gt;.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show configuration&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;configuration&lt;span class="w"&gt; &lt;/span&gt;details.
For&lt;span class="w"&gt; &lt;/span&gt;bug&lt;span class="w"&gt; &lt;/span&gt;reporting&lt;span class="w"&gt; &lt;/span&gt;instructions,&lt;span class="w"&gt; &lt;/span&gt;please&lt;span class="w"&gt; &lt;/span&gt;see:
&amp;lt;https://www.gnu.org/software/gdb/bugs/&amp;gt;.
Find&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;GDB&lt;span class="w"&gt; &lt;/span&gt;manual&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;other&lt;span class="w"&gt; &lt;/span&gt;documentation&lt;span class="w"&gt; &lt;/span&gt;resources&lt;span class="w"&gt; &lt;/span&gt;online&lt;span class="w"&gt; &lt;/span&gt;at:
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;http://www.gnu.org/software/gdb/documentation/&amp;gt;.

For&lt;span class="w"&gt; &lt;/span&gt;help,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;help&amp;quot;&lt;/span&gt;.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;apropos word&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;commands&lt;span class="w"&gt; &lt;/span&gt;related&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;word&amp;quot;&lt;/span&gt;.
add&lt;span class="w"&gt; &lt;/span&gt;symbol&lt;span class="w"&gt; &lt;/span&gt;table&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;spl/u-boot-spl&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at
&lt;span class="w"&gt;    &lt;/span&gt;.text_addr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x80000000
&lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
Reading&lt;span class="w"&gt; &lt;/span&gt;symbols&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;spl/u-boot-spl...
add&lt;span class="w"&gt; &lt;/span&gt;symbol&lt;span class="w"&gt; &lt;/span&gt;table&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../opensbi/build/platform/generic/firmware/fw_payload.elf&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at
&lt;span class="w"&gt;    &lt;/span&gt;.text_addr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x80100000
&lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
Reading&lt;span class="w"&gt; &lt;/span&gt;symbols&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;../opensbi/build/platform/generic/firmware/fw_payload.elf...
add&lt;span class="w"&gt; &lt;/span&gt;symbol&lt;span class="w"&gt; &lt;/span&gt;table&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;u-boot&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at
&lt;span class="w"&gt;    &lt;/span&gt;.text_addr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x81200000
&lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
Reading&lt;span class="w"&gt; &lt;/span&gt;symbols&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;u-boot...
add&lt;span class="w"&gt; &lt;/span&gt;symbol&lt;span class="w"&gt; &lt;/span&gt;table&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;u-boot&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at
&lt;span class="w"&gt;    &lt;/span&gt;.text_addr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x87715000
&lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
Reading&lt;span class="w"&gt; &lt;/span&gt;symbols&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;u-boot...
Remote&lt;span class="w"&gt; &lt;/span&gt;debugging&lt;span class="w"&gt; &lt;/span&gt;using&lt;span class="w"&gt; &lt;/span&gt;localhost:1234
⚠️&lt;span class="w"&gt; &lt;/span&gt;warning:&lt;span class="w"&gt; &lt;/span&gt;No&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;has&lt;span class="w"&gt; &lt;/span&gt;been&lt;span class="w"&gt; &lt;/span&gt;specified&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;target&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;support
determining&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;automatically.&lt;span class="w"&gt;  &lt;/span&gt;Try&lt;span class="w"&gt; &lt;/span&gt;using&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;file&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;command.
0x0000000000001000&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;b&lt;span class="w"&gt; &lt;/span&gt;board_init_f
Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;0x80000a3a:&lt;span class="w"&gt; &lt;/span&gt;board_init_f.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;locations&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;b&lt;span class="w"&gt; &lt;/span&gt;spl_invoke_opensbi
Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;0x8000167a:&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;common/spl/spl_opensbi.c,&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;55&lt;/span&gt;.
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;b&lt;span class="w"&gt; &lt;/span&gt;sbi_boot_print_banner
Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;0x8010b14c:&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;/var/home/zim/Sources/opensbi/lib/sbi/sbi_init.c,&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;51&lt;/span&gt;.
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;b&lt;span class="w"&gt; &lt;/span&gt;sbi_hart_switch_mode
Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;0x80104b68:&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;/var/home/zim/Sources/opensbi/lib/sbi/sbi_hart.c,&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;779&lt;/span&gt;.
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;b&lt;span class="w"&gt; &lt;/span&gt;display_options
Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;0x8125bec0:&lt;span class="w"&gt; &lt;/span&gt;display_options.&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;locations&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.

Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.1,&lt;span class="w"&gt; &lt;/span&gt;board_init_f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;dummy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;arch/riscv/lib/spl.c:26
&lt;span class="m"&gt;26&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nv"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;spl_early_init&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;27&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ret&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;30&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;riscv_cpu_setup&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;32&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;preloader_console_init&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;34&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nv"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;spl_board_init_f&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.

Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;spl_invoke_opensbi&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;spl_image&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x801fff60,&lt;span class="w"&gt; &lt;/span&gt;spl_image@entry&lt;span class="o"&gt;=&lt;/span&gt;0x801ffe90&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;common/spl/spl_opensbi.c:55
&lt;span class="m"&gt;55&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;!spl_image-&amp;gt;fdt_addr&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;60&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;!IS_ALIGNED&lt;span class="o"&gt;((&lt;/span&gt;uintptr_t&lt;span class="o"&gt;)&lt;/span&gt;spl_image-&amp;gt;fdt_addr,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;8&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;89&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nv"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;spl_opensbi_find_os_node&lt;span class="o"&gt;(&lt;/span&gt;spl_image-&amp;gt;fdt_addr,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;os_node,&lt;span class="w"&gt; &lt;/span&gt;os_type&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;97&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="nv"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fit_image_get_entry&lt;span class="o"&gt;(&lt;/span&gt;spl_image-&amp;gt;fdt_addr,&lt;span class="w"&gt; &lt;/span&gt;os_node,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;os_entry&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;98&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ret&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;99&lt;/span&gt;&lt;span class="w"&gt;          &lt;/span&gt;&lt;span class="nv"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fit_image_get_load&lt;span class="o"&gt;(&lt;/span&gt;spl_image-&amp;gt;fdt_addr,&lt;span class="w"&gt; &lt;/span&gt;os_node,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;os_entry&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;102&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;opensbi_info.magic&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;FW_DYNAMIC_INFO_MAGIC_VALUE&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;103&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;opensbi_info.version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;FW_DYNAMIC_INFO_VERSION&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;105&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;opensbi_info.next_mode&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;FW_DYNAMIC_INFO_NEXT_MODE_S&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;106&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;opensbi_info.options&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;CONFIG_SPL_OPENSBI_SCRATCH_OPTIONS&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;107&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;opensbi_info.boot_hart&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;gd-&amp;gt;arch.boot_hart&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;109&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;opensbi_entry&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;opensbi_entry_t&lt;span class="o"&gt;)&lt;/span&gt;spl_image-&amp;gt;entry_point&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;110&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;invalidate_icache_all&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;123&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;ret&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;smp_call_function&lt;span class="o"&gt;((&lt;/span&gt;ulong&lt;span class="o"&gt;)&lt;/span&gt;spl_image-&amp;gt;entry_point,
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;126&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ret&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;129&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;opensbi_entry&lt;span class="o"&gt;(&lt;/span&gt;gd-&amp;gt;arch.boot_hart,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;ulong&lt;span class="o"&gt;)&lt;/span&gt;spl_image-&amp;gt;fdt_addr,
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.

Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;sbi_boot_print_banner&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;scratch&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x80146000&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;/var/home/zim/Sources/opensbi/lib/sbi/sbi_init.c:51
&lt;span class="m"&gt;51&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;scratch-&amp;gt;options&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;SBI_SCRATCH_NO_BOOT_PRINTS&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;55&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;sbi_printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;\nOpenSBI %s\n&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;OPENSBI_VERSION_GIT&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;62&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;sbi_printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Build time: %s\n&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;OPENSBI_BUILD_TIME_STAMP&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;66&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;sbi_printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Build compiler: %s\n&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;OPENSBI_BUILD_COMPILER_VERSION&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;69&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;sbi_printf&lt;span class="o"&gt;(&lt;/span&gt;BANNER&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;70&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.

Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;sbi_hart_switch_mode&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;arg0&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;arg1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2167253192&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;next_addr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;2166358016&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;next_mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;next_virt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;/var/home/zim/Sources/opensbi/lib/sbi/sbi_hart.c:779
&lt;span class="m"&gt;779&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;switch&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;next_mode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;783&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;!misa_extension&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;S&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;794&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;csr_read&lt;span class="o"&gt;(&lt;/span&gt;CSR_MSTATUS&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;795&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="nv"&gt;val&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;INSERT_FIELD&lt;span class="o"&gt;(&lt;/span&gt;val,&lt;span class="w"&gt; &lt;/span&gt;MSTATUS_MPP,&lt;span class="w"&gt; &lt;/span&gt;next_mode&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;804&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;misa_extension&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;H&amp;#39;&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;807&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;csr_write&lt;span class="o"&gt;(&lt;/span&gt;CSR_MSTATUS,&lt;span class="w"&gt; &lt;/span&gt;val&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;808&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;csr_write&lt;span class="o"&gt;(&lt;/span&gt;CSR_MEPC,&lt;span class="w"&gt; &lt;/span&gt;next_addr&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;810&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;next_mode&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;==&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;PRV_S&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;811&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;next_virt&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;{&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;817&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;csr_write&lt;span class="o"&gt;(&lt;/span&gt;CSR_STVEC,&lt;span class="w"&gt; &lt;/span&gt;next_addr&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;818&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;csr_write&lt;span class="o"&gt;(&lt;/span&gt;CSR_SSCRATCH,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;819&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;csr_write&lt;span class="o"&gt;(&lt;/span&gt;CSR_SIE,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;820&lt;/span&gt;&lt;span class="w"&gt;             &lt;/span&gt;csr_write&lt;span class="o"&gt;(&lt;/span&gt;CSR_SATP,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;832&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;__asm__&lt;span class="w"&gt; &lt;/span&gt;__volatile__&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;mret&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;:&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;a0&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;r&amp;quot;&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;a1&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.

Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;.2,&lt;span class="w"&gt; &lt;/span&gt;board_init_f&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;boot_flags&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;common/board_f.c:1036
&lt;span class="m"&gt;1036&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;gd-&amp;gt;flags&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;~GD_FLG_HAVE_CONSOLE&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.

Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;5&lt;/span&gt;.1,&lt;span class="w"&gt; &lt;/span&gt;display_options&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;lib/display_options.c:50
&lt;span class="m"&gt;50&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;display_options_get_banner&lt;span class="o"&gt;(&lt;/span&gt;true,&lt;span class="w"&gt; &lt;/span&gt;buf,&lt;span class="w"&gt; &lt;/span&gt;sizeof&lt;span class="o"&gt;(&lt;/span&gt;buf&lt;span class="o"&gt;))&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;51&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;%s&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;buf&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;53&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.
&lt;span class="o"&gt;[&lt;/span&gt;Inferior&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;process&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;exited&lt;span class="w"&gt; &lt;/span&gt;normally&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;preloader_console_init&lt;/code&gt; in U-Boot SPL is what prints its banner and &lt;code&gt;spl_invoke_opensbi&lt;/code&gt; is where it hands over execution to OpenSBI. The &lt;code&gt;sbi_boot_print_banner&lt;/code&gt; as we learned already, prints OpenSBI's banner and &lt;code&gt;sbi_hart_switch_mode&lt;/code&gt; does the &lt;code&gt;mret&lt;/code&gt; instruction handing over execution to U-Boot proper. The &lt;code&gt;display_options&lt;/code&gt; in U-Boot proper finally prints its banner.&lt;/p&gt;
&lt;p&gt;The observant reader may have noticed that we instructed gdb to load U-Boot proper's symbols twice. This has to do with the fact that U-Boot proper actually re-locates itself during execution towards the end of RAM leaving as much RAM as possible free to load further artifacts to be booted. To find the relocation address one can use U-Boot's &lt;code&gt;bdinfo&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;bdinfo
&lt;span class="nv"&gt;boot_params&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000000000
DRAM&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;bank&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000000000
-&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;start&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000080000000
-&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000008000000
&lt;span class="nv"&gt;flashstart&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000020000000
&lt;span class="nv"&gt;flashsize&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000002000000
&lt;span class="nv"&gt;flashoffset&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000000000
&lt;span class="nv"&gt;baudrate&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;115200&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bps
&lt;span class="nv"&gt;relocaddr&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000087715000
reloc&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;off&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000006515000
&lt;span class="nv"&gt;Build&lt;/span&gt;&lt;span class="w"&gt;       &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;64&lt;/span&gt;-bit
current&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;eth&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;unknown
eth-1addr&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
IP&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;addr&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;NULL&amp;gt;
&lt;span class="nv"&gt;fdt_blob&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000086ef28e0
lmb_dump_all:
&lt;span class="w"&gt; &lt;/span&gt;memory.count&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x1
&lt;span class="w"&gt; &lt;/span&gt;memory&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;0x80000000-0x87ffffff&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;0x8000000&lt;span class="w"&gt; &lt;/span&gt;bytes,&lt;span class="w"&gt; &lt;/span&gt;flags:&lt;span class="w"&gt; &lt;/span&gt;none
&lt;span class="w"&gt; &lt;/span&gt;reserved.count&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x3
&lt;span class="w"&gt; &lt;/span&gt;reserved&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;0x80100000-0x8015ffff&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;0x60000&lt;span class="w"&gt; &lt;/span&gt;bytes,&lt;span class="w"&gt; &lt;/span&gt;flags:&lt;span class="w"&gt; &lt;/span&gt;no-map
&lt;span class="w"&gt; &lt;/span&gt;reserved&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;0x85eef000-0x85ef1fff&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;0x3000&lt;span class="w"&gt; &lt;/span&gt;bytes,&lt;span class="w"&gt; &lt;/span&gt;flags:&lt;span class="w"&gt; &lt;/span&gt;no-notify,&lt;span class="w"&gt; &lt;/span&gt;no-overwrite
&lt;span class="w"&gt; &lt;/span&gt;reserved&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="m"&gt;2&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;0x85ef28d0-0x87ffffff&lt;span class="o"&gt;]&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;0x210d730&lt;span class="w"&gt; &lt;/span&gt;bytes,&lt;span class="w"&gt; &lt;/span&gt;flags:&lt;span class="w"&gt; &lt;/span&gt;no-overwrite
&lt;span class="nv"&gt;devicetree&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;board
serial&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;addr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000010000000
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;width&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000000001
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;shift&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000000000
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;offset&lt;/span&gt;&lt;span class="w"&gt;     &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000000000
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;clock&lt;/span&gt;&lt;span class="w"&gt;      &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000384000
boot&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;hart&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x0000000000000000
firmware&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;fdt&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x00000000812da8c8
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;UEFI Boot Manager&lt;/h2&gt;
&lt;p&gt;Systemd-boot (sd-boot for short, previously known as gummiboot) is a simple, easy-to-configure UEFI (Unified Extensible Firmware Interface) boot manager. It provides a textual menu to select the entry to boot and an editor for the kernel command line. Its source code is located primarily within &lt;code&gt;src/boot/&lt;/code&gt; (previously &lt;code&gt;src/boot/efi/&lt;/code&gt;) within systemd.&lt;/p&gt;
&lt;p&gt;The first phase consists of the entry point handover, where control gets transferred to the primary UEFI payload with &lt;code&gt;efi_main()&lt;/code&gt; in src/boot/efi/boot.c being the initial code hook. The UEFI firmware, in our case U-Boot proper, passes standard arguments providing access to core console interfaces, system tables, and basic file system drivers.&lt;/p&gt;
&lt;p&gt;The second phase consists of execution environment sanity checks, where the configuration is validated, a watchdog is initialised to safely reset the platform if a hard freeze occurs during image parsing, and optional secure boot checks are invoked, including random seed provisioning. Note that secure boot is outside the scope of this article.&lt;/p&gt;
&lt;p&gt;The third phase consists of driver and file system probing, where file-access routines get initialised to locate available OS targets. The EFI system partition (ESP) where the binary was run from gets probed, and supplementary storage or file system drivers are loaded by iterating over &lt;code&gt;/EFI/systemd/drivers/&lt;/code&gt;. It dynamically handles and loads any such compiled specifically with the architecture suffix &lt;code&gt;riscv64.efi&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The fourth phase consists of config processing and menu presentation, where &lt;code&gt;/loader/loader.conf&lt;/code&gt; gets parsed to handle general preferences, &lt;code&gt;/loader/entries/*.conf&lt;/code&gt; gets scanned for target detection, searching inside &lt;code&gt;/EFI/Linux/&lt;/code&gt; looking for Unified Kernel Images (UKIs), and drawing the interactive boot menu, usually automatically booting the default target after a timeout occurred.&lt;/p&gt;
&lt;p&gt;The fifth and final phase consists of executing the Kernel via EFI stub, where systemd-boot hands execution directly off to a kernel image after selection. It calls the &lt;code&gt;EFI_BOOT_SERVICES.StartImage()&lt;/code&gt; execution standard, as the Linux kernel acts like a second UEFI application. Hardware information gets passed through via flattened device tree (FDT) or ACPI tables. Note that ACPI is outside the scope of this article.&lt;/p&gt;
&lt;p&gt;To build systemd to include systemd-boot, make sure to have &lt;code&gt;python3-pyelftools&lt;/code&gt; available and proceed as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://github.com/systemd/systemd.git
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;systemd
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;systemd&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;-b&lt;span class="w"&gt; &lt;/span&gt;v260.2-test&lt;span class="w"&gt; &lt;/span&gt;v260.2
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;systemd&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;cat&lt;span class="w"&gt; &lt;/span&gt;riscv64.txt
&lt;span class="o"&gt;[&lt;/span&gt;binaries&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;c&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/var/home/zim/Toolchains/riscv64-glibc-ubuntu-24.04-gcc-2026.05.19-nightly/bin/riscv64-unknown-linux-gnu-gcc&amp;#39;&lt;/span&gt;
&lt;span class="nv"&gt;cpp&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/var/home/zim/Toolchains/riscv64-glibc-ubuntu-24.04-gcc-2026.05.19-nightly/bin/riscv64-unknown-linux-gnu-g++&amp;#39;&lt;/span&gt;
&lt;span class="nv"&gt;ar&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/var/home/zim/Toolchains/riscv64-glibc-ubuntu-24.04-gcc-2026.05.19-nightly/bin/riscv64-unknown-linux-gnu-ar&amp;#39;&lt;/span&gt;
&lt;span class="nv"&gt;strip&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/var/home/zim/Toolchains/riscv64-glibc-ubuntu-24.04-gcc-2026.05.19-nightly/bin/riscv64-unknown-linux-gnu-strip&amp;#39;&lt;/span&gt;
&lt;span class="nv"&gt;exe_wrapper&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;qemu-system-riscv64 -nographic&amp;#39;&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;host_machine&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="nv"&gt;system&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;linux&amp;#39;&lt;/span&gt;
&lt;span class="nv"&gt;cpu_family&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;riscv64&amp;#39;&lt;/span&gt;
&lt;span class="nv"&gt;cpu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;riscv64&amp;#39;&lt;/span&gt;
&lt;span class="nv"&gt;endian&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;little&amp;#39;&lt;/span&gt;
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;systemd&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;meson&lt;span class="w"&gt; &lt;/span&gt;setup&lt;span class="w"&gt; &lt;/span&gt;--cross-file&lt;span class="w"&gt; &lt;/span&gt;riscv64.txt&lt;span class="w"&gt; &lt;/span&gt;build/&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ninja&lt;span class="w"&gt; &lt;/span&gt;-C&lt;span class="w"&gt; &lt;/span&gt;build/
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;systemd&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;build/src/boot/systemd-bootriscv64.efi
build/src/boot/systemd-bootriscv64.efi:&lt;span class="w"&gt; &lt;/span&gt;PE32+&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;EFI&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;application&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;span class="w"&gt; &lt;/span&gt;RISC-V&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;64&lt;/span&gt;-bit&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;stripped&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;external&lt;span class="w"&gt; &lt;/span&gt;PDB&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;6&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sections
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note: The latest main branch did not work for me, which is why I picked released v260.2 instead, which seems to work fine.&lt;/p&gt;
&lt;p&gt;Testing/debugging sd-boot is slightly more involved, as one can't just easily deploy it on its own, plus what would be the point, as we want it to subsequently boot a full Linux system. So we need to build one for RISC-V first. We can do this using &lt;a href="https://gitlab.com/freedesktop-sdk/freedesktop-sdk/-/wikis/RISC-V-qemu-bootable-image"&gt;Freedesktop SDK&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;git&lt;span class="w"&gt; &lt;/span&gt;clone&lt;span class="w"&gt; &lt;/span&gt;https://gitlab.com/freedesktop-sdk/freedesktop-sdk.git
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;freedesktop-sdk
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;freedesktop-sdk&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;bst&lt;span class="w"&gt; &lt;/span&gt;--colors&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;bootstrap_build_arch&lt;span class="w"&gt; &lt;/span&gt;x86_64&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-o&lt;span class="w"&gt; &lt;/span&gt;target_arch&lt;span class="w"&gt; &lt;/span&gt;riscv64&lt;span class="w"&gt; &lt;/span&gt;build&lt;span class="w"&gt; &lt;/span&gt;vm/minimal/efi.bst
⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;freedesktop-sdk&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;bst&lt;span class="w"&gt; &lt;/span&gt;--colors&lt;span class="w"&gt; &lt;/span&gt;-o&lt;span class="w"&gt; &lt;/span&gt;bootstrap_build_arch&lt;span class="w"&gt; &lt;/span&gt;x86_64&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-o&lt;span class="w"&gt; &lt;/span&gt;target_arch&lt;span class="w"&gt; &lt;/span&gt;riscv64&lt;span class="w"&gt; &lt;/span&gt;checkout&lt;span class="w"&gt; &lt;/span&gt;vm/minimal/efi.bst&lt;span class="w"&gt; &lt;/span&gt;vm-minimal-efi/
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We can now replace sd-boot inside the EFI partition of that &lt;code&gt;disk.img&lt;/code&gt; with our previously built one:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;zim@fedora:~$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;losetup&lt;span class="w"&gt; &lt;/span&gt;-P&lt;span class="w"&gt; &lt;/span&gt;/dev/loop0&lt;span class="w"&gt; &lt;/span&gt;freedesktop-sdk/vm-minimal-efi/disk.img
zim@fedora:~$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;mount&lt;span class="w"&gt; &lt;/span&gt;/dev/loop0p1&lt;span class="w"&gt; &lt;/span&gt;/mnt
zim@fedora:~$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;cp&lt;span class="w"&gt; &lt;/span&gt;systemd/build/src/boot/systemd-bootriscv64.efi&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
/mnt/EFI/BOOT/BOOTRISCV64.EFI
zim@fedora:~$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;umount&lt;span class="w"&gt; &lt;/span&gt;/mnt
zim@fedora:~$&lt;span class="w"&gt; &lt;/span&gt;sudo&lt;span class="w"&gt; &lt;/span&gt;losetup&lt;span class="w"&gt; &lt;/span&gt;-D
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;With that in place, we can now launch it in QEMU:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;u-boot&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;qemu-system-riscv64&lt;span class="w"&gt; &lt;/span&gt;-bios&lt;span class="w"&gt; &lt;/span&gt;spl/u-boot-spl.bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-device&lt;span class="w"&gt; &lt;/span&gt;loader,file&lt;span class="o"&gt;=&lt;/span&gt;u-boot.itb,addr&lt;span class="o"&gt;=&lt;/span&gt;0x80200000&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-device&lt;span class="w"&gt; &lt;/span&gt;virtio-blk-device,drive&lt;span class="o"&gt;=&lt;/span&gt;hd0&lt;span class="w"&gt; &lt;/span&gt;-device&lt;span class="w"&gt; &lt;/span&gt;virtio-net-pci,netdev&lt;span class="o"&gt;=&lt;/span&gt;n1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-drive&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;../freedesktop-sdk/vm-minimal-efi/disk.img,format&lt;span class="o"&gt;=&lt;/span&gt;raw,id&lt;span class="o"&gt;=&lt;/span&gt;hd0,if&lt;span class="o"&gt;=&lt;/span&gt;none&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-m&lt;span class="w"&gt; &lt;/span&gt;256M&lt;span class="w"&gt; &lt;/span&gt;-machine&lt;span class="w"&gt; &lt;/span&gt;virt&lt;span class="w"&gt; &lt;/span&gt;-netdev&lt;span class="w"&gt; &lt;/span&gt;user,id&lt;span class="o"&gt;=&lt;/span&gt;n1&lt;span class="w"&gt; &lt;/span&gt;-nographic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note: Without setting the &lt;code&gt;-m 256M&lt;/code&gt; argument to QEMU, it will fail to load with an &lt;code&gt;Unhandled exception: Load access fault&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Once we are on the U-Boot command line shell, we can poke it some more to find out about what it loads next:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;
&lt;span class="normal"&gt;39&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$bootcmd&lt;/span&gt;
run&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;distro_bootcmd&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$distro_bootcmd&lt;/span&gt;
&lt;span class="nv"&gt;scsi_need_init&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setenv&lt;span class="w"&gt; &lt;/span&gt;nvme_need_init&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;virtio_need_init&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;target&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;boot_targets&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;bootcmd_&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;target&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$boot_targets&lt;/span&gt;
nvme0&lt;span class="w"&gt; &lt;/span&gt;virtio0&lt;span class="w"&gt; &lt;/span&gt;virtio1&lt;span class="w"&gt; &lt;/span&gt;scsi0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;dhcp&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$bootcmd_virtio0&lt;/span&gt;
&lt;span class="nv"&gt;devnum&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;virtio_boot&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$virtio_boot&lt;/span&gt;
run&lt;span class="w"&gt; &lt;/span&gt;boot_pci_enum&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;virtio_init&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;virtio&lt;span class="w"&gt; &lt;/span&gt;dev&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devnum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;devtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;virtio&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;scan_dev_for_boot_part&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$scan_dev_for_boot_part&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;env&lt;span class="w"&gt; &lt;/span&gt;exists&lt;span class="w"&gt; &lt;/span&gt;distro_bootpart&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setenv&lt;span class="w"&gt; &lt;/span&gt;devplist&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;distro_bootpart&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;part&lt;span class="w"&gt; &lt;/span&gt;list&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devtype&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devnum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-bootable&lt;span class="w"&gt; &lt;/span&gt;devplist&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;env&lt;span class="w"&gt; &lt;/span&gt;exists&lt;span class="w"&gt; &lt;/span&gt;devplist&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;||&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setenv&lt;span class="w"&gt; &lt;/span&gt;devplist&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;distro_bootpart&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devplist&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fstype&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devtype&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devnum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;distro_bootpart&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bootfstype&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;part&lt;span class="w"&gt; &lt;/span&gt;uuid&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devtype&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devnum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;distro_bootpart&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;distro_bootpart_uuid&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;scan_dev_for_boot&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setenv&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;devplist&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$scan_dev_for_boot&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Scanning&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devtype&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devnum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;distro_bootpart&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;...&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;prefix&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;boot_prefixes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;scan_dev_for_extlinux&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;scan_dev_for_scripts&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;scan_dev_for_efi&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$scan_dev_for_efi&lt;/span&gt;
setenv&lt;span class="w"&gt; &lt;/span&gt;efi_fdtfile&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fdtfile&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;prefix&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;efi_dtb_prefixes&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devtype&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devnum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;distro_bootpart&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;prefix&lt;/span&gt;&lt;span class="si"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;efi_fdtfile&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;load_efi_dtb&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;boot_efi_bootmgr&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;test&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;-e&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devtype&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devnum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;distro_bootpart&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;efi/boot/bootriscv64.efi&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Found&lt;span class="w"&gt; &lt;/span&gt;EFI&lt;span class="w"&gt; &lt;/span&gt;removable&lt;span class="w"&gt; &lt;/span&gt;media&lt;span class="w"&gt; &lt;/span&gt;binary&lt;span class="w"&gt; &lt;/span&gt;efi/boot/bootriscv64.efi&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;run&lt;span class="w"&gt; &lt;/span&gt;boot_efi_binary&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;EFI&lt;span class="w"&gt; &lt;/span&gt;LOAD&lt;span class="w"&gt; &lt;/span&gt;FAILED:&lt;span class="w"&gt; &lt;/span&gt;continuing...&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;setenv&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;efi_fdtfile&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$boot_efi_binary&lt;/span&gt;
load&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devtype&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;devnum&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;:&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;distro_bootpart&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;kernel_addr_r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;efi/boot/bootriscv64.efi&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;fdt&lt;span class="w"&gt; &lt;/span&gt;addr&lt;span class="w"&gt; &lt;/span&gt;-q&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fdt_addr_r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;then&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bootefi&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;kernel_addr_r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fdt_addr_r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bootefi&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;kernel_addr_r&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;fdtcontroladdr&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;load&lt;span class="w"&gt; &lt;/span&gt;virtio&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;:1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$loadaddr&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;efi/boot/bootriscv64.efi
&lt;span class="m"&gt;176128&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;bytes&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;read&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;ms&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="m"&gt;168&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;MiB/s&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&lt;/span&gt;&amp;gt;&lt;span class="w"&gt; &lt;/span&gt;bootefi&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;$loadaddr&lt;/span&gt;
Missing&lt;span class="w"&gt; &lt;/span&gt;RNG&lt;span class="w"&gt; &lt;/span&gt;device&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;EFI_RNG_PROTOCOL
No&lt;span class="w"&gt; &lt;/span&gt;RNG&lt;span class="w"&gt; &lt;/span&gt;device
Booting&lt;span class="w"&gt; &lt;/span&gt;/efi&lt;span class="se"&gt;\b&lt;/span&gt;oot&lt;span class="se"&gt;\b&lt;/span&gt;ootriscv64.efi
systemd-boot@0x8de61000&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;260&lt;/span&gt;.2
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;And once we are fully booted, we can look at the partition layout and where sd-boot's binary and configuration got deployed to:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Freedesktop&lt;span class="w"&gt; &lt;/span&gt;Platform&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;26&lt;/span&gt;.08beta&lt;span class="w"&gt; &lt;/span&gt;localhost&lt;span class="w"&gt; &lt;/span&gt;ttyS0
localhost&lt;span class="w"&gt; &lt;/span&gt;login:&lt;span class="w"&gt; &lt;/span&gt;root
Password:
&lt;span class="o"&gt;[&lt;/span&gt;root@localhost&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;# lsblk&lt;/span&gt;
NAME&lt;span class="w"&gt;   &lt;/span&gt;MAJ:MIN&lt;span class="w"&gt; &lt;/span&gt;RM&lt;span class="w"&gt;  &lt;/span&gt;SIZE&lt;span class="w"&gt; &lt;/span&gt;RO&lt;span class="w"&gt; &lt;/span&gt;TYPE&lt;span class="w"&gt; &lt;/span&gt;MOUNTPOINTS
vda&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;254&lt;/span&gt;:0&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;489M&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;disk
├─vda1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;254&lt;/span&gt;:1&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;   &lt;/span&gt;36M&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;part&lt;span class="w"&gt; &lt;/span&gt;/efi
└─vda2&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;254&lt;/span&gt;:2&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;452M&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;part&lt;span class="w"&gt; &lt;/span&gt;/
&lt;span class="o"&gt;[&lt;/span&gt;root@localhost&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;# ls -l /efi/EFI/BOOT/BOOTRISCV64.EFI&lt;/span&gt;
-rwx------&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;root&lt;span class="w"&gt; &lt;/span&gt;root&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;101888&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Nov&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="m"&gt;2011&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;/efi/EFI/BOOT/BOOTRISCV64.EFI
&lt;span class="o"&gt;[&lt;/span&gt;root@localhost&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;# file /efi/EFI/BOOT/BOOTRISCV64.EFI&lt;/span&gt;
/efi/EFI/BOOT/BOOTRISCV64.EFI:&lt;span class="w"&gt; &lt;/span&gt;PE32+&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;EFI&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;application&lt;span class="o"&gt;)&lt;/span&gt;,
&lt;span class="w"&gt; &lt;/span&gt;RISC-V&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;64&lt;/span&gt;-bit&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;stripped&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;external&lt;span class="w"&gt; &lt;/span&gt;PDB&lt;span class="o"&gt;)&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;sections
&lt;span class="o"&gt;[&lt;/span&gt;root@localhost&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;# cat /efi/loader/loader.conf&lt;/span&gt;
timeout&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;
editor&lt;span class="w"&gt; &lt;/span&gt;yes
console-mode&lt;span class="w"&gt; &lt;/span&gt;keep
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;Debugging&lt;/h3&gt;
&lt;p&gt;Finally, the culmination. We can launch QEMU again in the first terminal like before, but again with the &lt;code&gt;-S&lt;/code&gt; argument, which instructs it to just wait for the gdb debugger to connect to it and further specify what to do next:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;u-boot&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;qemu-system-riscv64&lt;span class="w"&gt; &lt;/span&gt;-S&lt;span class="w"&gt; &lt;/span&gt;-bios&lt;span class="w"&gt; &lt;/span&gt;spl/u-boot-spl.bin&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-device&lt;span class="w"&gt; &lt;/span&gt;loader,file&lt;span class="o"&gt;=&lt;/span&gt;u-boot.itb,addr&lt;span class="o"&gt;=&lt;/span&gt;0x80200000&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-device&lt;span class="w"&gt; &lt;/span&gt;virtio-blk-device,drive&lt;span class="o"&gt;=&lt;/span&gt;hd0&lt;span class="w"&gt; &lt;/span&gt;-device&lt;span class="w"&gt; &lt;/span&gt;virtio-net-pci,netdev&lt;span class="o"&gt;=&lt;/span&gt;n1&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-drive&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;file&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;../freedesktop-sdk/vm-minimal-efi/disk.img,format&lt;span class="o"&gt;=&lt;/span&gt;raw,id&lt;span class="o"&gt;=&lt;/span&gt;hd0,if&lt;span class="o"&gt;=&lt;/span&gt;none&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-gdb&lt;span class="w"&gt; &lt;/span&gt;tcp::1234&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;256M&lt;span class="w"&gt; &lt;/span&gt;-machine&lt;span class="w"&gt; &lt;/span&gt;virt&lt;span class="w"&gt; &lt;/span&gt;-netdev&lt;span class="w"&gt; &lt;/span&gt;user,id&lt;span class="o"&gt;=&lt;/span&gt;n1&lt;span class="w"&gt; &lt;/span&gt;-nographic
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In the second terminal, we start gdb, giving it sd-boot's symbol file, and we instruct it to break at &lt;code&gt;notify_debugger&lt;/code&gt; in sd-boot. Then, we can let it print sd-boot's banner:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;
&lt;span class="normal"&gt;28&lt;/span&gt;
&lt;span class="normal"&gt;29&lt;/span&gt;
&lt;span class="normal"&gt;30&lt;/span&gt;
&lt;span class="normal"&gt;31&lt;/span&gt;
&lt;span class="normal"&gt;32&lt;/span&gt;
&lt;span class="normal"&gt;33&lt;/span&gt;
&lt;span class="normal"&gt;34&lt;/span&gt;
&lt;span class="normal"&gt;35&lt;/span&gt;
&lt;span class="normal"&gt;36&lt;/span&gt;
&lt;span class="normal"&gt;37&lt;/span&gt;
&lt;span class="normal"&gt;38&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;⬢&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;zim@toolbx&lt;span class="w"&gt; &lt;/span&gt;u-boot&lt;span class="o"&gt;]&lt;/span&gt;$&lt;span class="w"&gt; &lt;/span&gt;gdb&lt;span class="w"&gt; &lt;/span&gt;-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;add-symbol-file ../systemd/build/src/boot/systemd-bootriscv64.elf 0x8de62000&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
-ex&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;target remote localhost:1234&amp;#39;&lt;/span&gt;
GNU&lt;span class="w"&gt; &lt;/span&gt;gdb&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;Fedora&lt;span class="w"&gt; &lt;/span&gt;Linux&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;17&lt;/span&gt;.1-6.fc44
Copyright&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;C&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;2025&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;Free&lt;span class="w"&gt; &lt;/span&gt;Software&lt;span class="w"&gt; &lt;/span&gt;Foundation,&lt;span class="w"&gt; &lt;/span&gt;Inc.
License&lt;span class="w"&gt; &lt;/span&gt;GPLv3+:&lt;span class="w"&gt; &lt;/span&gt;GNU&lt;span class="w"&gt; &lt;/span&gt;GPL&lt;span class="w"&gt; &lt;/span&gt;version&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;later&lt;span class="w"&gt; &lt;/span&gt;&amp;lt;http://gnu.org/licenses/gpl.html&amp;gt;
This&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;software:&lt;span class="w"&gt; &lt;/span&gt;you&lt;span class="w"&gt; &lt;/span&gt;are&lt;span class="w"&gt; &lt;/span&gt;free&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;change&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;redistribute&lt;span class="w"&gt; &lt;/span&gt;it.
There&lt;span class="w"&gt; &lt;/span&gt;is&lt;span class="w"&gt; &lt;/span&gt;NO&lt;span class="w"&gt; &lt;/span&gt;WARRANTY,&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;extent&lt;span class="w"&gt; &lt;/span&gt;permitted&lt;span class="w"&gt; &lt;/span&gt;by&lt;span class="w"&gt; &lt;/span&gt;law.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show copying&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show warranty&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;details.
This&lt;span class="w"&gt; &lt;/span&gt;GDB&lt;span class="w"&gt; &lt;/span&gt;was&lt;span class="w"&gt; &lt;/span&gt;configured&lt;span class="w"&gt; &lt;/span&gt;as&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;x86_64-redhat-linux-gnu&amp;quot;&lt;/span&gt;.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;show configuration&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;configuration&lt;span class="w"&gt; &lt;/span&gt;details.
For&lt;span class="w"&gt; &lt;/span&gt;bug&lt;span class="w"&gt; &lt;/span&gt;reporting&lt;span class="w"&gt; &lt;/span&gt;instructions,&lt;span class="w"&gt; &lt;/span&gt;please&lt;span class="w"&gt; &lt;/span&gt;see:
&amp;lt;https://www.gnu.org/software/gdb/bugs/&amp;gt;.
Find&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;GDB&lt;span class="w"&gt; &lt;/span&gt;manual&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;other&lt;span class="w"&gt; &lt;/span&gt;documentation&lt;span class="w"&gt; &lt;/span&gt;resources&lt;span class="w"&gt; &lt;/span&gt;online&lt;span class="w"&gt; &lt;/span&gt;at:
&lt;span class="w"&gt;    &lt;/span&gt;&amp;lt;http://www.gnu.org/software/gdb/documentation/&amp;gt;.

For&lt;span class="w"&gt; &lt;/span&gt;help,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;help&amp;quot;&lt;/span&gt;.
Type&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;apropos word&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;search&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;commands&lt;span class="w"&gt; &lt;/span&gt;related&lt;span class="w"&gt; &lt;/span&gt;to&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;word&amp;quot;&lt;/span&gt;.
add&lt;span class="w"&gt; &lt;/span&gt;symbol&lt;span class="w"&gt; &lt;/span&gt;table&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;../systemd/build/src/boot/systemd-bootriscv64.elf&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at
&lt;span class="w"&gt;    &lt;/span&gt;.text_addr&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;0x8de62000
&lt;span class="o"&gt;(&lt;/span&gt;y&lt;span class="w"&gt; &lt;/span&gt;or&lt;span class="w"&gt; &lt;/span&gt;n&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;y
Reading&lt;span class="w"&gt; &lt;/span&gt;symbols&lt;span class="w"&gt; &lt;/span&gt;from&lt;span class="w"&gt; &lt;/span&gt;../systemd/build/src/boot/systemd-bootriscv64.elf...
Remote&lt;span class="w"&gt; &lt;/span&gt;debugging&lt;span class="w"&gt; &lt;/span&gt;using&lt;span class="w"&gt; &lt;/span&gt;localhost:1234
⚠️&lt;span class="w"&gt; &lt;/span&gt;warning:&lt;span class="w"&gt; &lt;/span&gt;No&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;has&lt;span class="w"&gt; &lt;/span&gt;been&lt;span class="w"&gt; &lt;/span&gt;specified&lt;span class="w"&gt; &lt;/span&gt;and&lt;span class="w"&gt; &lt;/span&gt;target&lt;span class="w"&gt; &lt;/span&gt;does&lt;span class="w"&gt; &lt;/span&gt;not&lt;span class="w"&gt; &lt;/span&gt;support
determining&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;automatically.&lt;span class="w"&gt;  &lt;/span&gt;Try&lt;span class="w"&gt; &lt;/span&gt;using&lt;span class="w"&gt; &lt;/span&gt;the&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;file&amp;quot;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;command.
0x0000000000001000&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;??&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;b&lt;span class="w"&gt; &lt;/span&gt;notify_debugger
Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;0x8de7e004:&lt;span class="w"&gt; &lt;/span&gt;file&lt;span class="w"&gt; &lt;/span&gt;../src/boot/util.c,&lt;span class="w"&gt; &lt;/span&gt;line&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;366&lt;/span&gt;.
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.

Breakpoint&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;notify_debugger&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;identity&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0x8de87ae0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;systemd-boot&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;at&lt;span class="w"&gt; &lt;/span&gt;../src/boot/util.c:366
&lt;span class="m"&gt;366&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;printf&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;%s@%p %s\n&amp;quot;&lt;/span&gt;,&lt;span class="w"&gt; &lt;/span&gt;identity,&lt;span class="w"&gt; &lt;/span&gt;__executable_start,&lt;span class="w"&gt; &lt;/span&gt;GIT_VERSION&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;n
&lt;span class="m"&gt;367&lt;/span&gt;&lt;span class="w"&gt;         &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;wait&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;c
Continuing.
&lt;span class="o"&gt;[&lt;/span&gt;Inferior&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;process&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;exited&lt;span class="w"&gt; &lt;/span&gt;normally&lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="o"&gt;(&lt;/span&gt;gdb&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The watchful reader might have noticed that the address &lt;code&gt;0x8de62000&lt;/code&gt; we loaded sd-boot's symbols for differs from the address previously reported &lt;code&gt;systemd-boot@0x8de61000 260.2&lt;/code&gt;. Yes, there is an additional text segment offset of &lt;code&gt;0x1000&lt;/code&gt;, which we have to keep in mind.&lt;/p&gt;
&lt;p&gt;Initially, I had some trouble getting gdb to ever show me the correct symbols. I then stumbled over the &lt;a href="https://github.com/systemd/systemd/blob/main/docs/HACKING.md#debugging-systemd-boot"&gt;Debugging systemd-boot&lt;/a&gt; section of systemd's hacking documentation, but I was unable to get gdb to ever stop at a reasonable location to figure out what is going on.&lt;/p&gt;
&lt;p&gt;Digging deeper, I found &lt;code&gt;notify_debugger&lt;/code&gt;, which was just missing the RISC-V case in order to ease stopping gdb at a sensible location. I patched the missing RISC-V assembly instruction in (also sent an &lt;a href="https://github.com/systemd/systemd/pull/42549"&gt;Upstream pull request&lt;/a&gt;), and noticed the address just being off by &lt;code&gt;0x1000&lt;/code&gt;. There you go!&lt;/p&gt;
&lt;h2&gt;Linux Kernel&lt;/h2&gt;
&lt;p&gt;The Linux kernel needs to be built as an EFI executable (&lt;code&gt;CONFIG_EFI_STUB&lt;/code&gt;) for the EFI boot manager to be able to execute it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;Freedesktop&lt;span class="w"&gt; &lt;/span&gt;Platform&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;26&lt;/span&gt;.08beta&lt;span class="w"&gt; &lt;/span&gt;localhost&lt;span class="w"&gt; &lt;/span&gt;ttyS0
localhost&lt;span class="w"&gt; &lt;/span&gt;login:&lt;span class="w"&gt; &lt;/span&gt;root
Password:
&lt;span class="o"&gt;[&lt;/span&gt;root@localhost&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;# ls /efi&lt;/span&gt;
config&lt;span class="w"&gt;  &lt;/span&gt;dtbs&lt;span class="w"&gt;  &lt;/span&gt;EFI&lt;span class="w"&gt;  &lt;/span&gt;initramfs-6.19.6.img&lt;span class="w"&gt;  &lt;/span&gt;loader&lt;span class="w"&gt;  &lt;/span&gt;System.map&lt;span class="w"&gt;  &lt;/span&gt;ubootefi.var&lt;span class="w"&gt;  &lt;/span&gt;vmlinuz
&lt;span class="o"&gt;[&lt;/span&gt;root@localhost&lt;span class="w"&gt; &lt;/span&gt;~&lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="c1"&gt;# file /efi/vmlinuz&lt;/span&gt;
/efi/vmlinuz:&lt;span class="w"&gt; &lt;/span&gt;Linux&lt;span class="w"&gt; &lt;/span&gt;kernel&lt;span class="w"&gt; &lt;/span&gt;RISC-V64&lt;span class="w"&gt; &lt;/span&gt;EFI&lt;span class="w"&gt; &lt;/span&gt;executable&lt;span class="w"&gt; &lt;/span&gt;gzip&lt;span class="w"&gt; &lt;/span&gt;compressed&lt;span class="w"&gt; &lt;/span&gt;zboot&lt;span class="w"&gt; &lt;/span&gt;Image
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Note that the Linux kernel EFI stub exits the boot services by executing &lt;code&gt;ExitBootServices()&lt;/code&gt;, terminating systemd-boot's operations and shifting control into the Linux S-mode kernel environment.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;The RISC-V boot process is well understood, and we are ready to tackle applying our knowledge to any RISC-V system out there. Looking forward to getting our hands on the first RVA23 boards, making sure they have their upstream boot chains ready.&lt;/p&gt;</content><category term="2026"></category><category term="Debugging"></category><category term="Embedded"></category><category term="Firmware"></category><category term="RISC-V"></category><category term="Upstream"></category></entry><entry><title>Porting an Automotive Operating System to RISC-V</title><link href="https://www.codethink.co.uk/articles/2026/CTRL-RISC-V.html" rel="alternate"></link><published>2026-06-02T00:00:00+01:00</published><updated>2026-06-02T00:00:00+01:00</updated><author><name>Roan Richmond</name></author><id>tag:www.codethink.co.uk,2026-06-02:/articles/2026/CTRL-RISC-V.html</id><summary type="html">&lt;p&gt;RISC-V is an open instruction set architecture (ISA) in comparison with the proprietary x86 or Arm architectures. Codethink Trustable and Reproducible Linux (CTRL) is an automotive and industrial operating system, which is being actively used today. Therefore, porting CTRL onto RISC-V displays how ready the architecture is to become a major player in the automotive industry. The move to RISC-V has never been easier and supporting this open architecture is the valuable next stage.&lt;/p&gt;</summary><content type="html">&lt;h3&gt;RISC-V in Automotive&lt;/h3&gt;
&lt;p&gt;RISC-V is an open instruction set architecture (ISA) based on reduced instruction set computer (RISC) principles. RISC-V has a growing ecosystem, supported by companies including NVIDIA and Qualcomm, and open-source groups, positioning it as a viable alternative to traditional proprietary architectures.&lt;/p&gt;
&lt;p&gt;Equipped to be a major player in the automotive industry, RISC-V’s extensible architecture enables automotive-specific specialisations and optimisations to be integrated, ensuring the hardware meets the use case.&lt;/p&gt;
&lt;p&gt;The unified architecture of RISC-V enables it to scale from simple sensors to high-performance autonomous driving systems on chips (SoCs). For example, a car might use RISC-V microcontrollers and powerful RISC-V-based SoCs in advanced driver assistance systems (ADAS), leveraging the same instruction set and tooling across very different compute needs.&lt;/p&gt;
&lt;p&gt;RISC-V allows for multiple processing units all to use the same base ISA with different extensions, depending on the unit's purpose. Resulting in a chiplet architecture made up of graphics processing units (GPUs), neural processing units (NPUs) and central processing units (CPUs), all of which are based on RISC-V.&lt;/p&gt;
&lt;p&gt;In addition, the open nature allows for multi-sourcing. For instance, an automotive supplier can source RISC-V chips from multiple vendors such as Infineon and SiFive, mitigating risks associated with supply chain shortages and reducing dependency on a single vendor.
As a result, RISC-V is on the brink of transforming the automotive industry.&lt;/p&gt;
&lt;p&gt;It may have already started. Infineon announced the release of an automotive microcontroller family based on RISC-V. This will be part of Infineon’s well-established AURIX microcontroller brand, with a virtual prototype already available. NVIDIA, one of the leading companies in the automotive industry, already shipped one billion RISC-V cores in 2024.&lt;/p&gt;
&lt;h3&gt;Porting Software&lt;/h3&gt;
&lt;p&gt;RISC-V, as a new ISA, requires software to be ported to support it. This work is being undertaken by an extensive community, including companies, working groups, and open-source community members. Due to RISC-V’s clear alignment with free and open-source software (FOSS) ideals, there has been a highly enthusiastic community response, including support for RISC-V added to the Linux kernel in 2017.&lt;/p&gt;
&lt;p&gt;However, many components, both FOSS and proprietary, still do not support RISC-V.&lt;/p&gt;
&lt;p&gt;Codethink Trustable and Reproducible Linux (CTRL) is Codethink's own safety-oriented platform development kit (PDK) used in automotive and industrial projects.  Due to the clear benefits of RISC-V’s use in automotive and its alignment with FOSS principles, Codethink added RISC-V support to CTRL to demonstrate porting a large mature software project to RISC-V.&lt;/p&gt;
&lt;p&gt;CTRL is built upon the foundation of Freedesktop SDK, which already supports RISC-V. Therefore, it is well placed to answer the question: how hard is it to port a Linux-based OS to a RISC-V target?&lt;/p&gt;
&lt;h3&gt;CTRL to RISC-V&lt;/h3&gt;
&lt;p&gt;Adding support for a new ISA can be more involved than just adding an additional target. Therefore, the porting of CTRL is a phased adoption of RISC-V, broken into three parts: &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Supporting RISC-V target compilation in the build environment&lt;/li&gt;
&lt;li&gt;Building CTRL for RISC-V and running on a virtual target&lt;/li&gt;
&lt;li&gt;Adding support for actual RISC-V hardware target&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Updating the build container to add support for RISC-V targets means ensuring all tools in the container support the RISC-V ISA, mirroring any additional dependencies for the RISC-V versions of the tooling, and adding the RISC-V container build as an optional, manual step as part of the containers' continuous development (CD) pipeline. This was fairly straightforward; the only challenges centred on components that didn’t produce releases for RISC-V, including OpenTofu and Jsonnet. For this initial implementation, these components could be excluded from the RISC-V container as they were not required for the later stages of porting CTRL.&lt;/p&gt;
&lt;p&gt;The second stage of RISC-V adoption requires the support of virtual targets, which are crucial in automotive as they align with “shift-left” strategies from software-defined vehicles (SDV). Using a virtual target enables shorter cycle times and easier debugging during RISC-V porting. Since CTRL already supports both Aarch64 and x86_64 QEMU virtual targets, it is a logical extension to support the qemu-system-riscv64 target.&lt;/p&gt;
&lt;p&gt;When porting to the QEMU RISC-V target, the main challenges faced were related to intricacies in the UEFI boot stage. UEFI expects the next-stage binary, in this case, grub2, to be named bootriscv64, which differs significantly from the x86 and Aarch64 naming patterns (bootaa64 and bootx64). Further on this front, the default edk2 vars meant that the UEFI stage was not attempting to run the grub2 binary. The final QEMU command used to run CTRL for reference was:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;qemu-system-riscv64&lt;span class="w"&gt; &lt;/span&gt;-nographic&lt;span class="w"&gt; &lt;/span&gt;-M&lt;span class="w"&gt; &lt;/span&gt;virt&lt;span class="w"&gt; &lt;/span&gt;-m&lt;span class="w"&gt; &lt;/span&gt;2G&lt;span class="w"&gt; &lt;/span&gt;-smp&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;4&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;object&lt;span class="w"&gt; &lt;/span&gt;rng-random,id&lt;span class="o"&gt;=&lt;/span&gt;rng0,filename&lt;span class="o"&gt;=&lt;/span&gt;/dev/urandom&lt;span class="w"&gt; &lt;/span&gt;-device&lt;span class="w"&gt; &lt;/span&gt;virtio-rng-pci,rng&lt;span class="o"&gt;=&lt;/span&gt;rng0&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;nic&lt;span class="w"&gt; &lt;/span&gt;user,model&lt;span class="o"&gt;=&lt;/span&gt;virtio,hostfwd&lt;span class="o"&gt;=&lt;/span&gt;tcp::2222-:22&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;drive&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;pflash,format&lt;span class="o"&gt;=&lt;/span&gt;raw,file&lt;span class="o"&gt;=&lt;/span&gt;edk2-riscv-code.fd,readonly&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;drive&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nv"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;raw,file&lt;span class="o"&gt;=&lt;/span&gt;riscv_image/disk.img
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After porting to the virtual target, and confirming all the expected functionality was present, CTRL was ported to the RISC-V hardware target, the &lt;a href="https://banana-pi.org/en/banana-pi-sbcs/175.html"&gt;Banana Pi BPI-F3&lt;/a&gt;. This board was chosen as it is readily available and widely supported throughout the RISC-V ecosystem, containing a SpacemiT K1 octa-core RISC-V processor. The process was made up of three main steps:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Building the bootloader components (OpenSBI and U-boot)&lt;/li&gt;
&lt;li&gt;Building the BPI-F3 Linux kernel &lt;/li&gt;
&lt;li&gt;Constructing a flashable image for an SD card&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Although the sources for the BPI-F3 versions of OpenSBI, U-boot and the Linux kernel are easy to find and construct, they are maintained in forked repositories, rather than being upstreamed to official open-source repositories. This is a common pattern among RISC-V hardware, and other architectures as well, that is detrimental to the industry. By maintaining these sources separately, these sources will eventually drift from the upstream, meaning they lag behind in functionality, and the patches applied will be tested less, increasing the chances of bugs.&lt;/p&gt;
&lt;h3&gt;Challenges Encountered&lt;/h3&gt;
&lt;p&gt;Throughout this work, there were two issues common across all stages. &lt;/p&gt;
&lt;p&gt;The first issue is the comparatively poor performance of the currently available RISC-V hardware when compared to Arm and x86. Since CTRL is built with Apache BuildStream, which is primarily aimed at native compilation, CTRL had to be compiled on RISC-V hardware. For a full build of CTRL, with every component built from source, this took over 24 hours on the Milk-V Pioneer hardware. BuildStream has in-built caching of components, which supported a significantly quicker rebuilding process and reduced the time to around 2 hours for full kernel rebuilds.&lt;/p&gt;
&lt;p&gt;The second recurring issue is that, since RISC-V is still relatively new compared to other architectures, many projects still do not produce RISC-V releases as standard, and some do not even support RISC-V. Luckily, all of the core components required for CTRL did have RISC-V support. It is important to note that the Linux Foundation have also produced a &lt;a href="https://training.linuxfoundation.org/training/porting-software-to-risc-v-lfd114/"&gt;course on porting software projects&lt;/a&gt;.&lt;/p&gt;
&lt;h3&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;So, how hard is it to port a Linux-based OS to a RISC-V target? &lt;/p&gt;
&lt;p&gt;The answer for us was: trivial.  A single Codethink engineer completed all three stages described above in under a week. The move to RISC-V has never been easier. Although hardware may still be under development and some software dependencies may not yet support RISC-V, there is considerable value in adding support for a new ISA sooner rather than playing catch-up later.&lt;/p&gt;</content><category term="2026"></category><category term="CTRL"></category><category term="RISC-V"></category></entry><entry><title>Understanding Codethink's IEC 61508 Mapping for the Eclipse Trustable Software Framework</title><link href="https://www.codethink.co.uk/articles/2026/understanding-iec61508-mapping.html" rel="alternate"></link><published>2026-05-29T00:00:00+01:00</published><updated>2026-05-29T00:00:00+01:00</updated><author><name>Paul Albertella and Kaspar Matas</name></author><id>tag:www.codethink.co.uk,2026-05-29:/articles/2026/understanding-iec61508-mapping.html</id><summary type="html">&lt;p&gt;IEC61508 is an international safety standard that provides methods on how to design and maintain safety-related systems. Codethink released an early preview for the mapping of the Trustable Software Framework to this safety standard covering a standard-to-framework layer.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Codethink's mapping between the Eclipse Trustable Software Framework (TSF) and IEC 61508 shows how the methodology designed specifically for complex software system integration and modern continuous delivery workflows can be used to demonstrate compliance with the standard. We have achieved a process functional safety assessment of the Trustable Software Framework against IEC 61508, conducted by &lt;a href="https://www.exida.com/"&gt;&lt;strong&gt;exida&lt;/strong&gt;&lt;/a&gt; specialists in the assessment of software development tools for safety-critical real-time systems. Our goal is that software engineers will adopt the TSF methodology to record and organise compliance evidence across the full software lifecycle, whether they are building new software or integrating pre-existing components into safety-critical systems.&lt;/p&gt;
&lt;p&gt;While resources like CASS provide assessment templates to guide auditors through mapping evidence to the standard’s clauses, Codethink’s mapping is designed for software engineering teams. We believe it’s the first independently assessed, openly available methodology designed for software integration at scale and speed. The mapping is available as an early access release while Codethink completes the functional safety assessment of CTRL OS, after which it will be contributed to the Eclipse Trustable Software Framework project.&lt;/p&gt;
&lt;h3&gt;What IEC 61508 Requires for Pre-Existing Software&lt;/h3&gt;
&lt;p&gt;IEC 61508 is the sector-agnostic base standard for functional safety of electrical and electronic systems. Sector-specific standards derived from it include ISO 26262 for automotive, IEC 62061 for machinery, IEC 62304 for medical devices, and EN 50128 for rail. Each adapts IEC 61508's framework to the particular demands of its domain, but the foundational requirements trace back to it.&lt;/p&gt;
&lt;p&gt;The software part of IEC 61508 is written from the perspective that you are developing software for a defined purpose: you specify what that software must do, understand how it contributes to a system with particular safety goals, and build your assurance argument from that foundation. However, this creates a problem for open source software as it is typically intended for anyone to use in any way they like, licensed as ‘not fit for any particular purpose’, with no warranty, and no claim of quality. That determination is left to whoever integrates it. IEC 61508 recognises this reality and provides a specific Route 3S for pre-existing software that was not developed with a safety application in mind.&lt;/p&gt;
&lt;p&gt;Route 3S requires organisations to provide evidence across a defined set of categories for software that was not developed for their specific safety application, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Software safety requirements specification&lt;/li&gt;
&lt;li&gt;Justification for use&lt;/li&gt;
&lt;li&gt;Design&lt;/li&gt;
&lt;li&gt;Integration with the hardware&lt;/li&gt;
&lt;li&gt;Verification and validation&lt;/li&gt;
&lt;li&gt;Interference from functions not used by the safety function&lt;/li&gt;
&lt;li&gt;Credible failure mechanisms and mitigations&lt;/li&gt;
&lt;li&gt;Configuration and runtime environment&lt;/li&gt;
&lt;li&gt;Assumptions of use&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In doing so, it provides a structured basis for the evaluation of pre-existing software based on the same criteria used for purpose-built safety-critical software. The principles underpinning Route 3S are shared by other standards addressing pre-existing software, including ISO/PAS 8926.&lt;/p&gt;
&lt;p&gt;The mapping Codethink has published addresses those requirements directly, covering Routes 1 and 3 of IEC 61508 Edition 2.&lt;/p&gt;
&lt;h3&gt;What the Eclipse Trustable Software Framework Is&lt;/h3&gt;
&lt;p&gt;The Eclipse Trustable Software Framework (TSF) is an open source methodology, hosted at the Eclipse Foundation. Originated by Codethink through its initial project contribution, and actively supported by Codethink as a continuing maintainer, TSF provides a structured, measurable foundation for building software that can be trusted by OEMs and regulators alike. The framework is domain-agnostic: it is suited to any software-intensive system, not only Linux-based or open source systems or IEC 61508 applications.&lt;/p&gt;
&lt;p&gt;TSF begins with a set of Tenets and Assertions. These are concise, verifiable statements about what must be true for software to be considered trustable. There are six Tenets, each defining a distinct area of evidence:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Provenance&lt;/strong&gt; captures where the software came from, what its dependencies are, and what is claimed about them, supported by mirrored dependencies, supply chain attestations, and regular reassessment of upstream risk.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Construction&lt;/strong&gt; defines how the software is built, installed, and run reproducibly, demonstrated through hash-verified, hermetic builds in controlled environments that can be reconstructed and independently verified.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Changes&lt;/strong&gt; tracks how the software is updated without breakage or regression and how upstream fixes are absorbed, maintained through CI-gated merge policies, vulnerability triage, and systematic tracking of upstream changes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Expectations&lt;/strong&gt; document what the software must and must not do, independently of any particular implementation, grounded in defined behaviours, identified misbehaviours, and runtime monitors.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Results&lt;/strong&gt; establish whether the software does what it must and does not do what it must not, with test results linked directly to the specific Expectations they address in the Trustable Graph.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Confidence&lt;/strong&gt; brings together the reasoning about how confident we are in the software based on all of the above, including where uncertainty remains and why, maintained as an explicit, living account alongside the code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The methodology is git-native. Statements and Artifacts live alongside source code in version control. The intent behind engineering decisions can often be lost in document-driven processes, which is why the TSF keeps it current alongside the code itself. When a developer makes a change that affects an existing Statement, the continuous integration pipeline flags it for review before it can be merged, keeping the assurance argument aligned with the software at every step.&lt;/p&gt;
&lt;p&gt;Because the review happens inside the tools and workflow that engineers are already using, compliance is integrated into the workflow, rather than it being a separate exercise. TSF is designed to make compliance something engineers see as valuable to what they do, rather than a process imposed on top of their work.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.youtube.com/watch?v=jzYzRnKKkQw"&gt;John Ellis, "Rebuilding Trust: From Open Source to Open Accountability," OCX 2026&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;What the IEC 61508 Mapping to the TSF Covers&lt;/h3&gt;
&lt;p&gt;The mapping is Codethink's interpretation of how the TSF's Tenets and Assertions address the objectives of IEC 61508 Routes 1 and 3, translating each area of evidence into the standard's requirements. For example, the TSF category of Expectations documents how the behaviours, misbehaviours, indicators and constraints are derived and specified for software, and how these are traced to supporting evidence, which satisfies the IEC 61508 objectives for software safety requirements.&lt;/p&gt;
&lt;p&gt;The mapping covers a standard-to-framework layer. An organisation that uses it will need to build on it to construct a compliance argument for a specific product and context, using the mapping as a practical, independently assessed starting point.&lt;/p&gt;
&lt;p&gt;To understand what this looks like in a working system, consider how it operates day-to-day in our CTRL OS project. When a developer makes a change to the build environment, checking the Trustable Graph immediately surfaces any linked Statements that need review. The developer confirms the change does not invalidate them, marks them reviewed, and commits. Committed changes are reviewed through daily change control processes via tools such as GitLab, with safety-related changes signed off by a safety expert. The safety argument stays current with the code, providing a practical method for achieving continuous compliance. &lt;/p&gt;
&lt;h3&gt;Create Your Own Mapping Between the TSF and a Compliance Standard&lt;/h3&gt;
&lt;p&gt;Codethink's goal is for the Eclipse Trustable Software Framework to become the common engineering basis on which organisations across industries demonstrate alignment with the safety, security, and regulatory standards that are relevant to them. &lt;/p&gt;
&lt;p&gt;For a long time, organisations have tried to navigate safety compliance in siloes, solving similar problems confidentially and separately. By making this mapping openly available, Codethink is taking a stand against that opacity. The vehicles, medical equipment, and industrial systems that depend on safety-critical software are used by everyone, and the reasoning behind why that software can be trusted should be open to scrutiny.
That is why Codethink is inviting the industry to build on this work. The same process of mapping the TSF to IEC 61508 can be applied to other compliance standards: ISO 13849 for machinery safety, DO-178C for airborne systems, IEC 62443 for industrial security, or the medical device regulations applicable in your market. An open, community-maintained portfolio of mappings between the TSF and the world's most important safety and regulatory standards makes a shared foundation for reasoning about software trust at scale possible for open source projects, OEMs, and regulators. 
If you want to contribute to making software trust an open, shared endeavour, the first step is understanding what an independently assessed TSF mapping looks like in practice. &lt;/p&gt;
&lt;p&gt;Request the early access IEC 61508 mapping &lt;a href="https://www.codethink.co.uk//news/tsf-61508-mapping-announcement.html"&gt;here&lt;/a&gt;, review it, and reach out to discuss building one for the standards that matter in your domain.&lt;/p&gt;</content><category term="2026"></category><category term="IEC61508"></category><category term="TSF"></category></entry><entry><title>Resisting Hyrum's Law with Private Constructors in Python</title><link href="https://www.codethink.co.uk/articles/2026/python-private-constructors.html" rel="alternate"></link><published>2026-05-07T00:00:00+01:00</published><updated>2026-05-07T00:00:00+01:00</updated><author><name>Richard Maw</name></author><id>tag:www.codethink.co.uk,2026-05-07:/articles/2026/python-private-constructors.html</id><summary type="html">&lt;p&gt;If you have objects that should only be created by named constructors
or returned by functions or methods in other classes
you may want to make the constructor uncallable for API stability.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Hyrum's Law States:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;With a sufficient number of users of an API,
it does not matter what you promise in the contract:
all observable behaviors of your system will be depended on by somebody.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;However, by hiding implementation details and providing stable interfaces
for operations your users want to do, the "sufficient" number can be increased.&lt;/p&gt;
&lt;p&gt;Python doesn't support completely hiding implementation details
but has a convention that names beginning with a single leading underscore
(e.g. a function called &lt;code&gt;_create&lt;/code&gt;) are classed as implementation details
and that the library developer makes no stability guarantees.&lt;/p&gt;
&lt;p&gt;Unfortunately, there are features that require implementing &lt;a href="https://docs.python.org/3/reference/datamodel.html#specialnames"&gt;special methods&lt;/a&gt;
with fixed names that can't be used to determine whether they are public.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/reference/datamodel.html#object.__init__"&gt;&lt;code&gt;__init__&lt;/code&gt;&lt;/a&gt;, the constructor special method,
is a noteworthy challenge
since the Python data model requires this method to always exist.&lt;/p&gt;
&lt;h2&gt;Why Might You Want to Make the Constructor Private?&lt;/h2&gt;
&lt;p&gt;You might want private constructors if:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Your object should only exist when returned from another object's method.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;version&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;get_element_by_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Ref&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;....&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your object can be constructed from different types
    and want to manage your API surface with named constructors instead,
    and there's either no "natural" constructor
    or you want to have all constructors named for consistency.&lt;/p&gt;
&lt;p&gt;Supposing we have a class representing a config file that can be constructed
in a number of different ways, you could have an overloaded constructor
where the parameter determines how to build it like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TextIOBase&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TextIOWrapper&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BinaryIO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TextIO&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyConfigFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TextIO&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;BinaryIO&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;TextIO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="nb"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TextIOBase&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
            &lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;TextIOWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This form of overloading is useful when you've already committed to having
an API based on the passed in parameter,
but with foresight you might find named constructors more manageable.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;
&lt;span class="normal"&gt;18&lt;/span&gt;
&lt;span class="normal"&gt;19&lt;/span&gt;
&lt;span class="normal"&gt;20&lt;/span&gt;
&lt;span class="normal"&gt;21&lt;/span&gt;
&lt;span class="normal"&gt;22&lt;/span&gt;
&lt;span class="normal"&gt;23&lt;/span&gt;
&lt;span class="normal"&gt;24&lt;/span&gt;
&lt;span class="normal"&gt;25&lt;/span&gt;
&lt;span class="normal"&gt;26&lt;/span&gt;
&lt;span class="normal"&gt;27&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;io&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;TextIOWrapper&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;pathlib&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;BinaryIO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TextIO&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyConfigFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TextIO&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TextIO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;from_binary_file_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fobj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;BinaryIO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;TextIOWrapper&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fobj&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;from_file_descriptor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;from_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;open&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;from_name&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;In this example there &lt;em&gt;is&lt;/em&gt; a natural default constructor
that wraps a pre-existing text file object,
but exposing that constructor as API is a leaky abstraction
that commits the implementation to wrapping the text file object.&lt;/p&gt;
&lt;p&gt;You might find that the config file is rarely opened so lazily opening it
on-demand would be a potential optimisation that is made more difficult
by the constructor requiring an already open file.&lt;/p&gt;
&lt;p&gt;So for consistency and API management it would be better to have another
&lt;code&gt;from_text_file_object&lt;/code&gt; named constructor.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Your object may be constructed from the same types
    but the values may be interpreted differently so require named constructors
    and there may not be a natural default constructor.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/datetime.html#datetime.datetime"&gt;&lt;code&gt;datetime&lt;/code&gt;&lt;/a&gt; has two named constructors for creation
from a timestamp.
&lt;a href="https://docs.python.org/3/library/datetime.html#datetime.datetime.fromtimestamp"&gt;&lt;code&gt;datetime.fromtimestamp(timestamp)&lt;/code&gt;&lt;/a&gt; will create a datetime
in the local timezone
while &lt;a href="https://docs.python.org/3/library/datetime.html#datetime.datetime.utcfromtimestamp"&gt;&lt;code&gt;datetime.utcfromtimestamp(timestamp)&lt;/code&gt;&lt;/a&gt; will create
one in &lt;a href="https://en.wikipedia.org/wiki/Coordinated_Universal_Time"&gt;Coordinated Universal Time&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;As with the constructor overloading example above
the &lt;a href="https://docs.python.org/3/library/datetime.html#datetime.datetime"&gt;datetime constructor&lt;/a&gt; exposes that the internal representation
is separate attributes for the year, month, day, hour, minute, second,
microseconds and time zone.&lt;/p&gt;
&lt;p&gt;This has the same problem of making it hard to change the representation
if it would be more convenient to operate on time as a single number.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The only way to create an object may be expensive
    and a constructor may imply it's cheaper than it is.&lt;/p&gt;
&lt;p&gt;The natural constructors of the last two examples are pretty cheap,
they assign passed in values to the object
(though &lt;a href="https://docs.python.org/3/library/datetime.html#datetime.datetime"&gt;datetime&lt;/a&gt; does some validation).&lt;/p&gt;
&lt;p&gt;An object representing the contents of some remote file may be represented
as:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SplitResult&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoteFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt;
    &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Since the only way to construct it is to fetch the contents of the URL
it might feel natural to fetch in the constructor.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SplitResult&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib.request&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoteFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt;
    &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geturl&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;However if &lt;code&gt;RemoteFile(url)&lt;/code&gt; implies the object is cheap to construct
then it may be accidentally called in a tight loop and cause performance
problems in a way that is not obvious.&lt;/p&gt;
&lt;p&gt;This would be more obvious with a named constructor.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib.parse&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SplitResult&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;urllib.request&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;RemoteFile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt;
    &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bytes&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;contents&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;contents&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;geturl&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;However this then means that the constructor is public
and objects could be created directly,
potentially causing confusion from the contents not matching what could be
fetched from the URL.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;How Do I Make My Constructor Uncallable Then?&lt;/h2&gt;
&lt;p&gt;Uncallable is a relative term in Python,
but there are two important things we can achieve:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Misuse of an object you shouldn't have created.&lt;/li&gt;
&lt;li&gt;A warning from tooling that you shouldn't use the constructor.&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;So How Do We Prevent the Object Being Created?&lt;/h3&gt;
&lt;p&gt;Conventionally &lt;a href="https://docs.python.org/3/library/exceptions.html#NotImplementedError"&gt;&lt;code&gt;NotImplementedError&lt;/code&gt;&lt;/a&gt; is raised
from methods in &lt;a href="https://docs.python.org/3/library/abc.html"&gt;Abstract Base Classes&lt;/a&gt;
to indicate that the subclass is incomplete if the method is not overridden,
which is similar enough to "this method should not be called".&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;
&lt;span class="normal"&gt;3&lt;/span&gt;
&lt;span class="normal"&gt;4&lt;/span&gt;
&lt;span class="normal"&gt;5&lt;/span&gt;
&lt;span class="normal"&gt;6&lt;/span&gt;
&lt;span class="normal"&gt;7&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Don&amp;#39;t use the MyClass constructor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_private_constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Of course this then makes the constructor unusable for &lt;em&gt;all&lt;/em&gt; users,
so how do we implement our private constructor?&lt;/p&gt;
&lt;h3&gt;Private Constructors With Private Tokens&lt;/h3&gt;
&lt;p&gt;One approach is to require that the constructor requires a special value be
provided and that the value is explicitly marked as private.&lt;/p&gt;
&lt;p&gt;This value is sometimes called a &lt;a href="https://en.wikipedia.org/wiki/Sentinel_value"&gt;sentinel value&lt;/a&gt; due to their historic use
as a special value at the end of a sequence to identify that it's over
e.g. the &lt;code&gt;NUL&lt;/code&gt; byte at the end of a C string.&lt;/p&gt;
&lt;p&gt;A historically popular way of creating these in python is to use the &lt;code&gt;object()&lt;/code&gt;
constructor since it creates a unique value that is different from any other
value that can be created.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;_PRIVATE_TOKEN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_private_token&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;_private_token&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;_PRIVATE_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Don&amp;#39;t use the MyClass constructor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_private_constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_private_token&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;_PRIVATE_TOKEN&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Since &lt;code&gt;_PRIVATE_TOKEN&lt;/code&gt; is explicitly private, and so is &lt;code&gt;_private_constructor&lt;/code&gt;
the user can't unintentionally create the object incorrectly,
and will discover they've created it wrong when they test it.&lt;/p&gt;
&lt;p&gt;However, this additional parameter check is extra overhead
that could cause worse performance in a tight loop.&lt;/p&gt;
&lt;h3&gt;How Can I Create Objects Without Using &lt;code&gt;__init__&lt;/code&gt;?&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/reference/datamodel.html#object.__init__"&gt;&lt;code&gt;__init__&lt;/code&gt;&lt;/a&gt; is not the only special method that plays a part during
object construction.&lt;/p&gt;
&lt;p&gt;When you use an object's constructor such as our &lt;code&gt;MyClass&lt;/code&gt;,
the general process is:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;
&lt;span class="normal"&gt;2&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__new__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So there is a separate step for creating an empty object of the appropriate type
and initializing it, and &lt;a href="https://docs.python.org/3/reference/datamodel.html#object.__init__"&gt;&lt;code&gt;__init__&lt;/code&gt;&lt;/a&gt; is expected to call the
constructors of its parent classes.&lt;/p&gt;
&lt;p&gt;Which means if we don't want to use the constructor we can do this ourselves.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Don&amp;#39;t use the MyClass constructor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_private_constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__new__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So now the constructor is unusable but we have an explicitly private constructor
that we can use to implement our public named constructors
or functions or methods on other objects that might want to return our object.&lt;/p&gt;
&lt;h3&gt;Getting Tooling to Warn&lt;/h3&gt;
&lt;p&gt;You may have read this and thought
"why don't we just document that the constructor shouldn't be used"
and it's a good idea to do that,
but time is finite and developers are typically working under pressure
so will reach for what looks like the most obvious thing to do
and the documentation may only be read &lt;em&gt;after&lt;/em&gt; it doesn't work as expected.&lt;/p&gt;
&lt;p&gt;Developers know this about themselves though, so invest in tooling like
IDEs, test suites, pre-commit check hooks and CI pipelines to catch issues.&lt;/p&gt;
&lt;p&gt;Type &lt;a href="https://docs.python.org/3/glossary.html#term-annotation"&gt;annotations&lt;/a&gt; allow IDEs and static type checkers like &lt;a href="https://www.mypy-lang.org/"&gt;mypy&lt;/a&gt;
allow library developers to annotate their types and functions to support
additional checking before the code is evaluated.&lt;/p&gt;
&lt;p&gt;Here is our previous solution with type annotations:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Don&amp;#39;t use the MyClass constructor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_private_constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__new__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;As you can see there's nothing indicating that &lt;a href="https://docs.python.org/3/reference/datamodel.html#object.__init__"&gt;&lt;code&gt;__init__&lt;/code&gt;&lt;/a&gt;
should not be called in the type system.&lt;/p&gt;
&lt;p&gt;Now that we are discussing annotations, it may have caught your attention
that we have an annotation for functions that can only raise an exception
or infinitely loop called &lt;a href="https://docs.python.org/3/library/typing.html#typing.NoReturn"&gt;&lt;code&gt;NoReturn&lt;/code&gt;&lt;/a&gt; and it may be tempting to
annotate the constructor with it.&lt;/p&gt;
&lt;p&gt;However this will confuse &lt;a href="https://www.mypy-lang.org/"&gt;mypy&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;NoReturn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;reveal_type&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;NoReturn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Don&amp;#39;t call the MyClass constructor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_private_constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__new__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;

&lt;span class="n"&gt;reveal_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="c1"&gt;# Revealed type is &amp;quot;def () -&amp;gt; Never&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Other type checkers like &lt;a href="https://docs.astral.sh/ty/"&gt;ty&lt;/a&gt; may have different results,
but constructors are expected to return &lt;a href="https://docs.python.org/3/library/constants.html#None"&gt;&lt;code&gt;None&lt;/code&gt;&lt;/a&gt;
and there's other ways to accommodate them.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/typing.html#typing.NoReturn"&gt;&lt;code&gt;NoReturn&lt;/code&gt;&lt;/a&gt; is a &lt;em&gt;bottom&lt;/em&gt; type indicating a value that can't exist.&lt;/p&gt;
&lt;p&gt;There's also the &lt;a href="https://docs.python.org/3/library/typing.html#typing.Never"&gt;&lt;code&gt;Never&lt;/code&gt;&lt;/a&gt;&lt;sup id="fnref:never"&gt;&lt;a class="footnote-ref" href="#fn:never"&gt;1&lt;/a&gt;&lt;/sup&gt; type that represents unreachable code,
a placeholder in generic types for states that don't exist
or parameters to functions that cannot be called,
which is precisely what we want to do with our &lt;a href="https://docs.python.org/3/reference/datamodel.html#object.__init__"&gt;&lt;code&gt;__init__&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Never&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_never&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Never&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="ne"&gt;NotImplementedError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;&amp;quot;Don&amp;#39;t call the MyClass constructor&amp;quot;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nd"&gt;@classmethod&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;_private_constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__new__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;cls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nb"&gt;super&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;

&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# error: Missing named argument &amp;quot;_never&amp;quot; for &amp;quot;MyClass&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;So we must provide the &lt;code&gt;_never&lt;/code&gt; parameter,
it's indicated to be explicitly a private parameter
and there's no value you can pass that satisfies the type checker&lt;sup id="fnref:casting"&gt;&lt;a class="footnote-ref" href="#fn:casting"&gt;2&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt;1&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_never&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;object&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;  &lt;span class="c1"&gt;# error: Argument &amp;quot;_never&amp;quot; to &amp;quot;MyClass&amp;quot; has incompatible type &amp;quot;object&amp;quot;; expected &amp;quot;Never&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;This doesn't prevent the constructor from being called at runtime,
but the raised exception will handle that.&lt;/p&gt;
&lt;h2&gt;What Are My Alternatives if This Doesn't Work for Me?&lt;/h2&gt;
&lt;p&gt;The simplest alternative is to rename your whole class
with a leading underscore (e.g. &lt;code&gt;_MyClass&lt;/code&gt;) to indicate that it is private
but that will result in confusing documentation about whether it should be used.&lt;/p&gt;
&lt;p&gt;Another, which follows a common pattern in Java, is a private implementation
of a public interface.&lt;/p&gt;
&lt;p&gt;Python doesn't have Java-like interfaces,
but it does have &lt;a href="https://docs.python.org/3/library/abc.html"&gt;Abstract Base Classes (ABCs)&lt;/a&gt;
(and supports Java interface-like behaviour by allowing multiple inheritance)
which raises an exception when attempting to create an object
when the class doesn't implement all the required methods
and support run-time checking of whether an object is the required class.&lt;/p&gt;
&lt;p&gt;Private construction can then be implemented by only making the ABC part of your
public API and give the subclass that implements the ABC a private name
like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;
&lt;span class="normal"&gt;14&lt;/span&gt;
&lt;span class="normal"&gt;15&lt;/span&gt;
&lt;span class="normal"&gt;16&lt;/span&gt;
&lt;span class="normal"&gt;17&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;abc&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;abstractproperty&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ABC&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="nd"&gt;@abstractproperty&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_MyClassImpl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;

    &lt;span class="nd"&gt;@property&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_foo&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_myclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_MyClassImpl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The run-time check that the class has been implemented correctly
may be a useful safeguard,
but it introduces additional run-time overhead
that may not be acceptable.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://docs.python.org/3/library/typing.html#typing.Protocol"&gt;&lt;code&gt;typing.Protocol&lt;/code&gt;&lt;/a&gt; is a newer alternative
that supports static (or optionally run-time) &lt;a href="https://docs.python.org/3/glossary.html#term-duck-typing"&gt;duck-type&lt;/a&gt;
checking of an object's behaviour without inheritance.&lt;/p&gt;
&lt;p&gt;As a &lt;a href="https://docs.python.org/3/library/typing.html#typing.Protocol"&gt;Protocol&lt;/a&gt; it would look like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;table class="highlighttable"&gt;&lt;tr&gt;&lt;td class="linenos"&gt;&lt;div class="linenodiv"&gt;&lt;pre&gt;&lt;span class="normal"&gt; 1&lt;/span&gt;
&lt;span class="normal"&gt; 2&lt;/span&gt;
&lt;span class="normal"&gt; 3&lt;/span&gt;
&lt;span class="normal"&gt; 4&lt;/span&gt;
&lt;span class="normal"&gt; 5&lt;/span&gt;
&lt;span class="normal"&gt; 6&lt;/span&gt;
&lt;span class="normal"&gt; 7&lt;/span&gt;
&lt;span class="normal"&gt; 8&lt;/span&gt;
&lt;span class="normal"&gt; 9&lt;/span&gt;
&lt;span class="normal"&gt;10&lt;/span&gt;
&lt;span class="normal"&gt;11&lt;/span&gt;
&lt;span class="normal"&gt;12&lt;/span&gt;
&lt;span class="normal"&gt;13&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;div&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;typing&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Protocol&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Protocol&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;_MyClassImpl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;None&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;foo&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;make_myclass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;MyClass&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_MyClassImpl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I hope this advice has been interesting and helps you learn a new technique
that you may find applicable in other circumstances.&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:never"&gt;
&lt;p&gt;If you are unfortunate enough
to only be able to use an old version of Python
and unable to use third-party modules
then &lt;a href="https://docs.python.org/3/library/typing.html#typing.NoReturn"&gt;&lt;code&gt;NoReturn&lt;/code&gt;&lt;/a&gt; is equivalent
to &lt;a href="https://docs.python.org/3/library/typing.html#typing.Never"&gt;&lt;code&gt;Never&lt;/code&gt;&lt;/a&gt; and can be used instead.&amp;#160;&lt;a class="footnote-backref" href="#fnref:never" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:casting"&gt;
&lt;p&gt;Strictly you &lt;em&gt;could&lt;/em&gt; use &lt;code&gt;MyClass(_never=cast(Never, object()))&lt;/code&gt; to force it
to pass type checking since it's not foolproof, but you would have to be a
mighty fool indeed to do it and it'll get caught at runtime.&amp;#160;&lt;a class="footnote-backref" href="#fnref:casting" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="2026"></category><category term="Python"></category><category term="API"></category></entry><entry><title>FOSDEM 2026</title><link href="https://www.codethink.co.uk/articles/fosdem-2026/" rel="alternate"></link><published>2026-01-27T00:00:00+00:00</published><updated>2026-01-27T00:00:00+00:00</updated><author><name>Sara Sherwood</name></author><id>tag:www.codethink.co.uk,2026-01-27:/articles/fosdem-2026/</id><summary type="html">&lt;p&gt;Codethink will be attending FOSDEM 2026 with multiple presentations, talks and panels from our team!&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://fosdem.org/2026/"&gt;FOSDEM&lt;/a&gt; week is back. &lt;/p&gt;
&lt;p&gt;At the end of January, Codethink will once again be in Brussels, this time with more than 40+ Codethings speaking, volunteering, and presenting across FOSDEM itself &lt;em&gt;and&lt;/em&gt; a set of pre-FOSDEM events and conversations that lead into the weekend.&lt;/p&gt;
&lt;p&gt;From 31 January to 1 February 2026, FOSDEM will bring together the people who build, maintain, foster the open source community. We're looking forward to a few full days of deep technical talks, devrooms, hallway conversations, and long-overdue catch-ups. Just as importantly, the days leading into FOSDEM are where many of the most interesting discussions begin, and Codethink will be there too. &lt;/p&gt;
&lt;p&gt;We're excited to listen, share, reconnect with familiar faces, and to meet new ones along the way.&lt;/p&gt;
&lt;p&gt;Codethink is proud to sponsor FOSDEM, and members of our team will be speaking across a range of topics throughout the week. You'll find details of all Codethink talks and events, both during FOSDEM and in the run-up to it, below.&lt;/p&gt;
&lt;h2&gt;Pre-FOSDEM Events&lt;/h2&gt;
&lt;h3&gt;Talk: Your CRA Plan Is Already Broken: Only Trustable Software Can Save It&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Speaker:&lt;/strong&gt; John Ellis&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Eclipse Foundation Code &amp;amp; Compliance Workshop&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Location:&lt;/strong&gt; Maison de la Poste, Rue Picard 7, 1000 Bruxelles&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; Thursday, 29th January&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Start:&lt;/strong&gt; 10:30&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;End:&lt;/strong&gt; 10:50&lt;/p&gt;
&lt;p&gt;&lt;a href="https://www.eclipse-foundation.events/event/code-compliance-2026/program"&gt;Link here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Description:&lt;/h3&gt;
&lt;p&gt;Most organisations are about to learn the hard way that the CRA is not a compliance exercise. It is an engineering audit disguised as regulation. CRA doesn’t care how impressive your policies are, how many documents you’ve written, or how many consultants have “mapped controls”. It cares about something brutally simple: Can you prove how your software is built, secured, and maintained?&lt;/p&gt;
&lt;p&gt;The Eclipse TSF provides the escape hatch. By generating reproducible builds, provenance trails, SBOM integrity checks, tamper-evident pipeline metadata, and continuous trust signals, TSF creates the exact evidence CRA expects but does not explicitly spell out. TSF doesn’t add bureaucracy, it replaces guesswork with verifiable fact.&lt;/p&gt;
&lt;p&gt;The provocation is explicit: If your CRA strategy doesn’t start in the pipeline, you don’t have a CRA strategy at all.&lt;/p&gt;
&lt;h3&gt;Panel: Deep Dive: Competitiveness &amp;amp; Open Source in the Automotive Industry&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Speaker:&lt;/strong&gt; John Ellis&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;EU Open Source Policy Summit&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Location:&lt;/strong&gt; Sparks Meeting, Rue Ravenstein 60, 1000 Bruxelles&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; Friday, 30th January&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Start:&lt;/strong&gt; 16:10&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;End:&lt;/strong&gt; 16:50&lt;/p&gt;
&lt;p&gt;&lt;a href="https://summit.openforumeurope.org/"&gt;Link here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Description:&lt;/h3&gt;
&lt;p&gt;Europe’s automotive sector is restructuring around software-defined mobility, with manufacturers, suppliers, open source organisations, and technology providers adopting shared development models to manage complexity and maintain strategic control. This session will examine how open source can strengthen competitiveness across the automotive value chain, drawing on perspectives from industry leaders, software suppliers, and the European Commission.&lt;/p&gt;
&lt;p&gt;Panellists will consider how collaborative platforms support interoperability, reduce duplication, and enable long-term capability, and how these industry efforts align with the Commission’s Automotive Action Plan and broader transport innovation agenda. The discussion will address the regulatory and organisational conditions needed to sustain shared development, ensure safety and compliance, and support deployment at scale. It will also explore how insights from software-defined vehicle initiatives can inform open approaches across other strategic sectors.&lt;/p&gt;
&lt;h2&gt;FOSDEM Events&lt;/h2&gt;
&lt;h3&gt;Talk: KDE at 30: Still looking ahead&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Speaker:&lt;/strong&gt; Aleix Pol&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Track:&lt;/strong&gt; Main Track (K-Building)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Room:&lt;/strong&gt; K.1.105 (La Fountaine)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; Saturday 31st January &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Start:&lt;/strong&gt; 11:00&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;End:&lt;/strong&gt; 11:50&lt;/p&gt;
&lt;p&gt;&lt;a href="https://fosdem.org/2026/schedule/event/GWT99L-kde_at_30_still_looking_ahead/"&gt;Link here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Description:&lt;/h3&gt;
&lt;p&gt;In 2026 the KDE project will turn 30, an extraordinary milestone in such a fast-paced ecosystem.&lt;/p&gt;
&lt;p&gt;This talk explores the challenges faced over the years and how the KDE community has adapted to stay relevant, continuing to deliver a good experience for people to use on their computers ranging across laptops, mobiles and even gaming consoles.&lt;/p&gt;
&lt;p&gt;After glancing through our historical context, it will discuss what's in store for KDE today and how they're preparing for a bright and sustainable future in the evolving Free Software landscape.&lt;/p&gt;
&lt;h3&gt;Talk: Upstream Embedded Linux on RISC-V SBCs: The Past, Present and Future&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Speaker:&lt;/strong&gt; Marcel Ziswiler&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Track:&lt;/strong&gt; RISC-V&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Room:&lt;/strong&gt; H.2214&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; Saturday 31st January &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Start:&lt;/strong&gt; 14:00&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;End:&lt;/strong&gt; 14:35&lt;/p&gt;
&lt;p&gt;&lt;a href="https://fosdem.org/2026/schedule/event/LX3NNU-upstream-embedded-linux-on-risc-v-sbcs/"&gt;Link here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Description:&lt;/h3&gt;
&lt;p&gt;Last year’s talk on running upstream embedded Linux on RISC-V, using the SpacemiT K1-based Banana Pi BPI-F3, is revisited with an update on upstreaming progress and remaining issues. &lt;/p&gt;
&lt;p&gt;The talk then introduces several new contenders added to the embedded Linux testing lab, including the Siflower SF21H8898-based Banana Pi BPI-RV2, the ESWIN EIC7700-based EBC77 with SiFive HiFive Premier P550 cores, and the Ky X1-based Orange Pi RV2 and R2S, the latter two of which will be addressed by a subsequent speaker. By comparing downstream vendor options with upstream efforts, the session highlights the overall progress of embedded Linux on RISC-V, before looking ahead to upcoming RVA23-compatible boards based on chips such as SpacemiT K3, Tenstorrent TT-Ascalon, UltraRISC UR-DP1000 and Zhihe A600. &lt;/p&gt;
&lt;p&gt;The time is ripe for embedded Linux to shine on RISC-V.&lt;/p&gt;
&lt;h3&gt;Talk: Running GNOME OS on mobile phones&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Speaker:&lt;/strong&gt; Abderrahim Kitouni &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Track:&lt;/strong&gt; FOSS on Mobile &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Room:&lt;/strong&gt; UB4.132&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; Saturday 31st January &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Start:&lt;/strong&gt; 17:30&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;End:&lt;/strong&gt; 18:00&lt;/p&gt;
&lt;p&gt;&lt;a href="https://fosdem.org/2026/schedule/event/ESE3GP-gnome-os-on-mobile/"&gt;Link here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Description:&lt;/h3&gt;
&lt;p&gt;GNOME OS is GNOME's development, testing, and QA operating system. It builds the in-development versions of the GNOME desktop and core applications. It is also a modern image-based Linux system.&lt;/p&gt;
&lt;p&gt;This talk will present recent efforts to run GNOME OS on phones. Right now, the FairPhone 5 and the OnePlus 6 are supported, but ideally any phone that is supported by the mainline Linux kernel could have GNOME OS support.&lt;/p&gt;
&lt;p&gt;It will present different tools and projects that make this possible, and what this initative hopes to achieve: better testing for the GNOME applications, and more ways to do FOSS on Mobile.&lt;/p&gt;
&lt;h3&gt;Talk: Externally verifying Linux’s real-time deadline scheduling capabilities&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Speaker:&lt;/strong&gt; Theodore Tucker&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Track:&lt;/strong&gt; Testing and Continuous Delivery&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Room:&lt;/strong&gt; H.2213&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; Sunday 1st February&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Start:&lt;/strong&gt; 09:00&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;End:&lt;/strong&gt; 09:25&lt;/p&gt;
&lt;p&gt;&lt;a href="https://fosdem.org/2026/schedule/event/A9QEXB-externally-verifying-linux-scheduling/"&gt;Link here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Description:&lt;/h3&gt;
&lt;p&gt;Many industrial systems require hard real-time scheduling from Linux, but scheduling measurements from the system itself cannot be completely trusted as they are referenced to the same clock as the kernel-under-test.&lt;/p&gt;
&lt;p&gt;This talk presents Codethink’s hardware-in-the-loop approach to verifying Linux as an RTOS. A Linux “Rusty Worker” requests real-time scheduling via &lt;code&gt;sched_setattr&lt;/code&gt; and emits timing data over UART, while embedded Rust firmware on a Raspberry Pi Pico captures independent, microsecond-precision measurements outside the kernel’s scheduling domain.&lt;/p&gt;
&lt;p&gt;We show how embedded Rust enables deterministic, memory-safe verification software and reproducible builds, and how continuous testing is integrated into CI. Using the Eclipse Trustable Software Framework (TSF), we aggregate and analyse results across commits, providing clear, evidence-based confidence in Linux’s real-time capabilities.&lt;/p&gt;
&lt;h3&gt;Talk: Rust meets cheap bare-metal RISC-V&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Speaker:&lt;/strong&gt; Marcel Ziswiler&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Track:&lt;/strong&gt; Rust&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Room:&lt;/strong&gt; UB2.252A (Lameere)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; Sunday 1st February&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Start:&lt;/strong&gt; 09:30&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;End:&lt;/strong&gt; 09:55&lt;/p&gt;
&lt;p&gt;&lt;a href="https://fosdem.org/2026/schedule/event/JCZA8P-rust-meets-cheap-bare-metal-risc-v/"&gt;Link here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Description:&lt;/h3&gt;
&lt;p&gt;With the release of embedded-hal v1.0 almost two years ago, Rust is now well suited for bare metal embedded development. &lt;/p&gt;
&lt;p&gt;This talk focuses on low cost RISC-V microcontrollers such as the CH32V003, in the same space as boards like the Raspberry Pi Pico or Teensy but costing under €1.30, and shows how to build an embedded system using bare metal Rust. It covers the full workflow from getting to main, to flashing and debugging with probe-rs, IDE integration, and using &lt;code&gt;no-std&lt;/code&gt; to achieve the smallest possible footprint.&lt;/p&gt;
&lt;h3&gt;Talk: Generating SBoMs for BuildStream projects&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Speaker:&lt;/strong&gt; Abderrahim Kitouni &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Track:&lt;/strong&gt; SBOMS and supply chains&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Room:&lt;/strong&gt; UD2.208 (Decroly)&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Date:&lt;/strong&gt; Sunday 1st February&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Start:&lt;/strong&gt; 16:30&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;End:&lt;/strong&gt; 17:00&lt;/p&gt;
&lt;p&gt;&lt;a href="https://fosdem.org/2026/schedule/event/P9FNH3-buildstream-sbom/"&gt;Link here&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Description:&lt;/h3&gt;
&lt;p&gt;BuildStream is a software integration tool that allows building software aggregated from multiple sources in a single pipeline to produce a final output. This final output could be a container image, an operating system image or anything that you can write a plugin for.&lt;/p&gt;
&lt;p&gt;This talk presents &lt;code&gt;buildstream-sbom&lt;/code&gt; - a tool that extracts information from a BuildStream project and uses it to generate an SPDX-formatted SBoM. It will also discuss some of the issues in translating from BuildStream concepts to SPDX.&lt;/p&gt;
&lt;h2&gt;Can't make it to Brussels this year?&lt;/h2&gt;
&lt;p&gt;If you won't be in Brussels, FOSDEM offers livestreaming for talks throughout the weekend - full details are available on the &lt;a href="https://fosdem.org/2026/"&gt;FOSDEM website&lt;/a&gt;. Recordings from related pre-FOSDEM events hosted by the &lt;a href="https://www.ocxconf.org/event/403bffe2-15d8-49ac-af52-e2781b7296b3/summary"&gt;Eclipse Foundation&lt;/a&gt; and &lt;a href="https://openforumeurope.org"&gt;OpenForum Europe&lt;/a&gt; will be published in the weeks following their respective events. &lt;/p&gt;
&lt;p&gt;After the dust settles, we'll share a post-FOSDEM write-up with highlights, themes, and takeaways from the week. Until then, you can find more information about the events Codethink is sponsoring and participating in on our &lt;a href="https://www.codethink.co.uk/events.html"&gt;events page&lt;/a&gt;. &lt;/p&gt;</content><category term="2026"></category><category term="Conferences"></category><category term="Open Source"></category><category term="Community"></category></entry><entry><title>Building on STPA: How TSF and RAFIA can uncover misbehaviours in complex software integration</title><link href="https://www.codethink.co.uk/articles/building-on-stpa/" rel="alternate"></link><published>2026-01-23T00:00:00+00:00</published><updated>2026-01-23T00:00:00+00:00</updated><author><name>Theodore Tucker</name></author><id>tag:www.codethink.co.uk,2026-01-23:/articles/building-on-stpa/</id><summary type="html">&lt;p&gt;A case study of an iterative approach to safety engineering of a Linux-based OS, allowing lessons from hardware-in-the-loop tests to immediately feed back to a formal STPA analysis.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Codethink Trustable Reproducible Linux (CTRL OS) integrates open-source software components to provide a stable but leading-edge platform for safety-critical designs.&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt; We use the Eclipse Trustable Software Framework (TSF) to continuously quality-assure CTRL OS as it and its components evolve, and present this case study to show how TSF’s RAFIA methodology allowed us to close a feedback loop, by detecting and remedying defects in an upstream open-source component and, crucially, our initial analysis of that component.&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;h3&gt;Setting the scene&lt;/h3&gt;
&lt;p&gt;TSF sets out a number of generic and high-level &lt;em&gt;Trustable Assertions&lt;/em&gt;: testable statements which a robust software integration project should aim to fulfill through analysis and evidence. For this case study, we consider the &lt;strong&gt;TA-MISBEHAVIOURS&lt;/strong&gt; assertion, which reads: &lt;em&gt;“Prohibited misbehaviours for [CTRL OS] are identified, and mitigations are specified, verified and validated based on analysis”.&lt;/em&gt;&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt; TSF is not prescriptive, and instead encourages projects to think critically about their own unique circumstances and therefore choose appropriate methods to achieve and validate this.&lt;/p&gt;
&lt;p&gt;TSF proposes, but does not require, RAFIA (Risk Analysis, Fault Induction and Automation) as an alternative process model for the development of complex safety-critical software systems.&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt; RAFIA is deliberately a cyclical and continuous process, making it well suited to TSF’s goal of continuous compliance, and setting it clearly apart from the V-model traditionally applied in these situations, where validation of test results against the original design may be a one-off activity.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of the RAFIA cycle, linking the Analysis, Specification, Implementation, and Testing stages in a directed loop, returning to Analysis" src="https://www.codethink.co.uk/articles/building-on-stpa/RAFIA_overview_diagram.svg"&gt; 
&lt;em&gt;Source: &lt;a href="https://codethinklabs.gitlab.io/trustable/trustable/extensions/rafia/index.html"&gt;https://codethinklabs.gitlab.io/trustable/trustable/extensions/rafia/index.html&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The RAFIA process has four ordered but cyclical stages: Analysis, Specification, Implementation, and Testing. Critically and uniquely, the Analysis stage involves analysing the results of testing from the previous iteration and using those results to re-analyse the system’s design to inform test specifications for the next iteration. For the first complete iteration of a RAFIA loop, however, an initial risk analysis of the system must be undertaken, typically without reference to test results. RAFIA strongly recommends that projects use the STPA methodology to enumerate the behaviours that the system should exhibit, and misbehaviours that should be either absent, or detected and mitigated. The results of this analysis are then used to drive testing of the system.&lt;/p&gt;
&lt;p&gt;STPA (System Theoretic Process Analysis) is a technique for analysing complex systems and identifying modes of failure arising from ‘unsafe’ interactions between the components, which may involve otherwise correct behaviour from components as well as errors or faults. STPA is highly systematic in nature, and requires practitioners to sequentially identify:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The scope of their system&lt;/li&gt;
&lt;li&gt;Losses (or harms) that the system should prevent&lt;/li&gt;
&lt;li&gt;Hazards that might lead to a loss occurring&lt;/li&gt;
&lt;li&gt;Interactions between the system’s components&lt;/li&gt;
&lt;li&gt;How each interaction may be “unsafe” and lead to a hazard&lt;/li&gt;
&lt;li&gt;Constraints that must apply to the system and its components to prevent or mitigate hazards&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;TSF provides guidance for using STPA in the Risk Analysis stage of RAFIA, and the STPA Handbook describes the procedure and its rationale in greater detail.&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt;, &lt;sup id="fnref:6"&gt;&lt;a class="footnote-ref" href="#fn:6"&gt;6&lt;/a&gt;&lt;/sup&gt; Yet, for the RAFIA procedure, its most important output is a set of constraints. We can close the RAFIA loop by writing a test specification to check these constraints are true for a real system, implementing that test specification, and then analysing the results of the tests to determine whether the STPA model is flawed or if our software misbehaves. In a TSF context, as STPA constraints are testable statements, they can be represented and continuously scored in a Trustable directed acyclic graph (DAG) for every iteration of the software, just like the overarching goal of &lt;strong&gt;TA-MISBEHAVIOURS&lt;/strong&gt; itself. &lt;/p&gt;
&lt;h3&gt;Case study: integration of the Safety Monitor&lt;/h3&gt;
&lt;p&gt;For the integration of Codethink’s open-source Safety Monitor – a safety application that controls an external ‘mitigation of last resort’ – into CTRL OS, we chose to follow the RAFIA process to be certain that the mitigation is always triggered when the OS enters an unsafe state, but never triggered while CTRL OS is in a safe state.&lt;sup id="fnref:7"&gt;&lt;a class="footnote-ref" href="#fn:7"&gt;7&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;At the start of the RAFIA process, our safety engineer undertook an initial STPA analysis which started by considering the scope of the Safety Monitor system and its components. In summary we have:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Watchdog: a device external to CTRL OS which transitions the system to a safe state (for example, by transferring responsibility to another system, or simply rebooting) if it does not receive input (a &lt;em&gt;“pet”&lt;/em&gt; signal) every 100 ms&lt;/li&gt;
&lt;li&gt;Critical Processes: programs running on CTRL OS to provide the customer’s safety-related function(s)&lt;/li&gt;
&lt;li&gt;Safety Monitor: a program running on CTRL OS which receives updates from &lt;em&gt;Critical Processes&lt;/em&gt; (called &lt;em&gt;Extrinsic Indicators&lt;/em&gt;) and sends &lt;em&gt;pet&lt;/em&gt; signals to the &lt;em&gt;Watchdog&lt;/em&gt; every 100 ms only if:&lt;ul&gt;
&lt;li&gt;All received Extrinsic Indicators are within predefined numerical bounds&lt;/li&gt;
&lt;li&gt;All expected Extrinsic Indicators have been received&lt;/li&gt;
&lt;li&gt;No internal corruption or malfunction as been detected&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Control structure diagram showing the interactions between the Watchdog, Critical Processes, and Safety Monitor in CTRL OS" src="https://www.codethink.co.uk/articles/building-on-stpa/CTRL_monitor_control_structure.svg"&gt; &lt;/p&gt;
&lt;h4&gt;Detecting a defect in the software&lt;/h4&gt;
&lt;p&gt;Having taken our system requirements and system components, we followed the STPA procedure and eventually generated a list of testable constraints, amongst them Constraint 1: &lt;em&gt;If any of the monitored Extrinsic Indicators are not updated at the specified cadence and frequency, the Watchdog-petting thread exits&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Continuing with the RAFIA process, we wrote a specification to test Constraint 1 on real hardware running CTRL OS both in pre-merge testing (to assert the constraint is still met for each proposed change to CTRL OS) and post-merge soak testing (to assert the constraint is still met for CTRL OS’s releases and mainline branch over thousands of repetitions). As part of this, we applied the next part of RAFIA: Fault Injection. This involves deliberately causing the misbehaviours that are identified by STPA, to determine whether the system either prevents them, or detects them and responds appropriately.&lt;/p&gt;
&lt;p&gt;Implementing the specification produced a test in which a mock Critical Process was configured to not send the expected Extrinsic Indicators to the Safety Monitor, and the Safety Monitor’s logs were checked to see if the Watchdog petting ceased, and if so, the time that took. In a real system, the Watchdog would trigger an appropriate response at the system level, such as restarting a misbehaving component, but our tests in this case measure how long the system takes to respond at all when an unsafe condition is reported. However, when we came to analyse the results of the soak tests (around 500 samples each week), we were surprised to see aberrant response times from the Safety Monitor.&lt;/p&gt;
&lt;p&gt;According to Safety Monitor’s published User Documentation, system integrators can configure the &lt;em&gt;Timeout&lt;/em&gt; for an Extrinsic Indicator, after which the Watchdog petting action will stop if no such Extrinsic Indicators are received.&lt;sup id="fnref:8"&gt;&lt;a class="footnote-ref" href="#fn:8"&gt;8&lt;/a&gt;&lt;/sup&gt; Having consulted the upstream documentation and CTRL OS’s Safety Monitor configuration files, we expected this timeout to be 135 seconds for the Constraint 1 test case. When soak test results reported a mean timeout of 117 seconds for the Constraint 1 test case we started to investigate.&lt;/p&gt;
&lt;p&gt;In the implementation of the Constraint 1 test case, the mock Critical Process’s configuration was temporarily overridden so that Extrinsic Indicator reporting would cease; this involved restarting the Critical Process. Following conversations with upstream Safety Monitor engineers and a joint interrogation of the test logs, we realised that Safety Monitor maintained an internal buffer of Extrinsic Indicators, did not empty this buffer when a Critical Process restarted, and actually calculated the Timeout starting at the last Extrinsic Indicator in the buffer rather than when it detected a restart of the Critical Process. As the system had been running in a “happy path” state before the test case induced the fault, the state of this buffer was not certain at the start of the test, so neither was the point at which Safety Monitor would start counting the Timeout or the measured response time itself.&lt;/p&gt;
&lt;p&gt;The CTRL OS team raised an issue in the open, which was triaged, implemented, and tested by the upstream team.&lt;sup id="fnref:9"&gt;&lt;a class="footnote-ref" href="#fn:9"&gt;9&lt;/a&gt;&lt;/sup&gt; As a result, Safety Monitor now clears Extrinsic Indicator buffers when a process restarts, the measured response times in these scenarios are now consistent and predictable, and the expected behaviour is clearly documented upstream. This was verified through soak tests against a CTRL OS image containing a patched Safety Monitor.&lt;/p&gt;
&lt;p&gt;In this way, the RAFIA process detected and resolved an obscure misbehaviour in an upstream software component of a complex system, but, more importantly, it also provided documentation so that future STPA analyses in the RAFIA loop will consider this buffering behaviour.&lt;/p&gt;
&lt;h4&gt;Detecting a defect in the analysis&lt;/h4&gt;
&lt;p&gt;While methodically inspecting soak test logs to determine the reason for the unexpectedly short response times, we noticed messages such as &lt;code&gt;safety-monitor: Adding pause indicator&lt;/code&gt;, which prompted the CTRL OS team to check Safety Monitor’s upstream documentation and source code for an explanation. This revealed that an Extrinsic Indicator can be “paused” by any process sending the requisite message to the safety monitor.&lt;sup id="fnref:10"&gt;&lt;a class="footnote-ref" href="#fn:10"&gt;10&lt;/a&gt;&lt;/sup&gt; Moreover, Safety Monitor enabled this pausing behaviour for all monitored Extrinsic Indicators by default, meaning that if a rogue process A somehow sent Safety Monitor the “pause” signal for a critical process B, then B could have stopped providing Extrinsic Indicators altogether without petting ceasing.&lt;/p&gt;
&lt;p&gt;We identified this behaviour as a pathway to a potential dangerous failure of CTRL OS, where a fault reported by a Critical Process was not mitigated externally. However, this failure mode was completely absent from the initial STPA. We handled this in two ways: firstly, according to the RAFIA process, we revisited our STPA analysis with the new information about pausing from the tests, and considered this as a Control Action to be systematically analysed according to the methodology. Secondly, we raised a further issue upstream so that pausing had to be explicitly enabled in CTRL OS’s configuration of Safety Monitor, allowing us to later argue through our TSF graph that new constraints around unsafe “pause” control actions were satisfied.&lt;sup id="fnref:11"&gt;&lt;a class="footnote-ref" href="#fn:11"&gt;11&lt;/a&gt;&lt;/sup&gt; Here, we see that the thorough test data analysis that RAFIA mandated for one STPA Constraint revealed a different gap in the STPA itself, allowing immediate refinements to the model and neatly closing the development loop with Risk Analysis.&lt;/p&gt;
&lt;h3&gt;Conclusions&lt;/h3&gt;
&lt;p&gt;Historically, integrators have emphasised the "Theoretic” element of STPA, so when gaps in the analysis open up in the face of engineering reality, the analysis is re-done from first principles. As we have shown, TSF’s RAFIA process brings together safety, testing, and upstream expertise so that data (even anecdotal) from tests – with real hardware and software in the loop – can immediately be used to readjust the expectations of analysis and design, eliminating uncertainty early without necessarily starting again, just as software itself may evolve by incremental steps.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To find out more about how TSF, RAFIA, and CTRL OS can help you nail risks in complex software projects, please get in touch at &lt;a href="mailto:connect@codethink.co.uk"&gt;connect@codethink.co.uk&lt;/a&gt;.&lt;/strong&gt;&lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://www.codethink.co.uk/ctrl-os.html"&gt;https://www.codethink.co.uk/ctrl-os.html&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://codethinklabs.gitlab.io/trustable/trustable/index.html"&gt;https://codethinklabs.gitlab.io/trustable/trustable/index.html&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;a href="https://codethinklabs.gitlab.io/trustable/trustable/trustable/TA.html#ta-misbehaviours"&gt;https://codethinklabs.gitlab.io/trustable/trustable/trustable/TA.html#ta-misbehaviours&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;&lt;a href="https://codethinklabs.gitlab.io/trustable/trustable/extensions/rafia/index.html"&gt;https://codethinklabs.gitlab.io/trustable/trustable/extensions/rafia/index.html&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 4 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;&lt;a href="https://codethinklabs.gitlab.io/trustable/trustable/extensions/stpa/index.html"&gt;https://codethinklabs.gitlab.io/trustable/trustable/extensions/stpa/index.html&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:5" title="Jump back to footnote 5 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;&lt;a href="https://psas.scripts.mit.edu/home/get_file.php?name=STPA_handbook.pdf"&gt;https://psas.scripts.mit.edu/home/get_file.php?name=STPA_handbook.pdf&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:6" title="Jump back to footnote 6 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;&lt;a href="https://gitlab.com/CodethinkLabs/safety-monitor/safety-monitor"&gt;https://gitlab.com/CodethinkLabs/safety-monitor/safety-monitor&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:7" title="Jump back to footnote 7 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:8"&gt;
&lt;p&gt;&lt;a href="https://codethinklabs.gitlab.io/safety-monitor/safety-monitor/user/usage.html#monitoring-services"&gt;https://codethinklabs.gitlab.io/safety-monitor/safety-monitor/user/usage.html#monitoring-services&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:8" title="Jump back to footnote 8 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:9"&gt;
&lt;p&gt;&lt;a href="https://gitlab.com/CodethinkLabs/safety-monitor/safety-monitor/-/issues/41"&gt;https://gitlab.com/CodethinkLabs/safety-monitor/safety-monitor/-/issues/41&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:9" title="Jump back to footnote 9 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:10"&gt;
&lt;p&gt;&lt;a href="https://gitlab.com/CodethinkLabs/safety-monitor/safety-monitor/-/blob/b621f7015d69a91074a9d83276ce201aecc1fe8f/docs/usage.md#pausing-a-metric"&gt;https://gitlab.com/CodethinkLabs/safety-monitor/safety-monitor/-/blob/b621f7015d69a91074a9d83276ce201aecc1fe8f/docs/usage.md#pausing-a-metric&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:10" title="Jump back to footnote 10 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:11"&gt;
&lt;p&gt;&lt;a href="https://gitlab.com/CodethinkLabs/safety-monitor/safety-monitor/-/issues/48"&gt;https://gitlab.com/CodethinkLabs/safety-monitor/safety-monitor/-/issues/48&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:11" title="Jump back to footnote 11 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="2026"></category><category term="safety"></category><category term="STPA"></category><category term="Linux"></category><category term="open source"></category></entry><entry><title>Adding big‑endian support to CVA6 RISC‑V FPGA processor</title><link href="https://www.codethink.co.uk/articles/cva6-big-endian/" rel="alternate"></link><published>2025-10-23T00:00:00+01:00</published><updated>2025-10-23T00:00:00+01:00</updated><author><name>Ben Dooks</name></author><id>tag:www.codethink.co.uk,2025-10-23:/articles/cva6-big-endian/</id><summary type="html">&lt;p&gt;We added big‑endian support for an open-source RISC‑V processor and booted Linux on it, using an FPGA board.&lt;/p&gt;</summary><content type="html">&lt;h3&gt;Introduction&lt;/h3&gt;
&lt;p&gt;Big‑endian RISC‑V is an interesting area of experiments, and Codethink has previously &lt;a href="https://www.codethink.co.uk/articles/risc-v-big-endian-support-runtime-testing/"&gt;demonstrated&lt;/a&gt; booted big‑endian RISC‑V Linux in QEMU. Building on that, we now achieved booting big‑endian Linux on an FPGA board. To do this we used the &lt;a href="https://docs.openhwgroup.org/projects/cva6-user-manual/"&gt;CVA6&lt;/a&gt; open-source processor, which we investigated in one of our &lt;a href="https://www.codethink.co.uk/articles/running-freedesktop-sdk-on-cva6/"&gt;previous projects&lt;/a&gt;. CVA6 has its &lt;a href="https://github.com/openhwgroup/cva6"&gt;source code&lt;/a&gt; available on GitHub, written in SystemVerilog. It implements the RISC‑V instruction set and can target a few FPGA boards. We modified its code to add support for runtime configurable endianness. Read on to find out how we did it!&lt;/p&gt;
&lt;h3&gt;RISC‑V big‑endian specification&lt;/h3&gt;
&lt;p&gt;Let's start with RISC‑V specification. The endianness of the RISC‑V processor is controlled by the {M/S/U}BE bits in the Control-and-Status-Register &lt;code&gt;mstatus&lt;/code&gt;. For example, if the processor is operating in M privilege mode and bit 37 of &lt;code&gt;mstatus&lt;/code&gt; is set then the processor operates in big‑endian mode. Each privilege mode (M/S/U) endianness can be set individually. It’s also valid for SBE and UBE to just be mirrors of MBE, which is what we chose to implement. One interesting point of the specification is that processor instructions are always little‑endian, regardless of the current endianness mode.&lt;/p&gt;
&lt;p&gt;How does different endianness look in practice? When CVA6 is operating in little‑endian mode, then everything happens as normal. But once the MBE bit is set in &lt;code&gt;mstatus&lt;/code&gt;, then the processor switches to big‑endian mode, and all loads and stores to the memory are treated as being in big‑endian format. But because the CVA6 core still carries out operations in little‑endian format, we need to byte swap the data to convert between the two.&lt;/p&gt;
&lt;p&gt;&lt;img alt="CVA6 diagram" src="https://www.codethink.co.uk/articles/cva6-big-endian/CVA6_diagram.svg"&gt;&lt;/p&gt;
&lt;h3&gt;Assembly example&lt;/h3&gt;
&lt;p&gt;This is a simple assembly program that demonstrates changing endianness at runtime:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;&lt;span class="na"&gt;.section&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;.data&lt;/span&gt;
&lt;span class="na"&gt;.balign&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;32&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/* Variable stored in LE format */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;var1:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="na"&gt;.word&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0x11223344&lt;/span&gt;

&lt;span class="na"&gt;.section&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;.text&lt;/span&gt;
&lt;span class="na"&gt;.global&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;_start&lt;/span&gt;

&lt;span class="nl"&gt;_start:&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/* Some initial vars */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;li&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="no"&gt;a1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;37&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;la&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="no"&gt;a2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;var1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;li&lt;/span&gt;&lt;span class="w"&gt;        &lt;/span&gt;&lt;span class="no"&gt;a3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;0x1&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/* Add 1 to var1, keep the result in a4 (LE) */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;lwu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;a4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;a2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;a4&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;a3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;a4&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/* Switch to BE */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;csrs&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;mstatus&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;a1&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;fence&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/* Add 1 to var1, keep the result in a5 (BE) */&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;lwu&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;a5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="no"&gt;a2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;a5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;a3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="no"&gt;a5&lt;/span&gt;

&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="cm"&gt;/* Expected outcome:&lt;/span&gt;
&lt;span class="cm"&gt;       a4: 0x11223345&lt;/span&gt;
&lt;span class="cm"&gt;       a5: 0x44332212  */&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;Now let's see which CVA6 units we had to change.&lt;/p&gt;
&lt;h3&gt;Load/store unit and MMU&lt;/h3&gt;
&lt;p&gt;We started with load/store unit and added endianness flag and byte swapping there. An important point is that load/store operations can have different sizes – 8, 16, 32, and 64 bits – and they have to be byte-swapped differently. For example, 8‑bit sized operations don't need to be modified at all. Another place where memory accesses happen is memory management unit's (MMU) page table walker, so we added byte swapping there too. This was easier because we just replicated what we'd done for the load/store unit.&lt;/p&gt;
&lt;h3&gt;Atomic memory operations&lt;/h3&gt;
&lt;p&gt;Big‑endian support for atomic memory operations (AMOs) is more involved than for load/store unit, because in CVA6 AMOs are encoded as memory bus transactions. These transactions are then processed in another module called axi_riscv_atomics. &lt;a href="https://en.wikipedia.org/wiki/Advanced_eXtensible_Interface"&gt;AXI&lt;/a&gt; is the specification of the memory bus that CVA6 uses. Conveniently for us, AXI specifies an endianness flag as part of the transaction, it's just CVA6 doesn't utilise it. Having learnt that, we modified code that sends these transactions to set the endianness field depending on the current endianness mode, and we added code to the axi_riscv_atomics module to handle that flag correctly. axi_riscv_atomics module does AMOs calculations in its own arithmetic logic unit (ALU), so our changes there were mostly byte-swapping input and output of the ALU.&lt;/p&gt;
&lt;p&gt;&lt;img alt="CVA6 AMOs diagram" src="https://www.codethink.co.uk/articles/cva6-big-endian/CVA6_atomics_diagram.svg"&gt;&lt;/p&gt;
&lt;h3&gt;Testing&lt;/h3&gt;
&lt;p&gt;CVA6 supports GDB debug, the process of doing that is &lt;a href="https://github.com/openhwgroup/cva6/blob/master/tutorials/fpga.md#debugging"&gt;described&lt;/a&gt; in the documentation. So, for testing our changes we used GDB, with which we ran our small assembly programs on the processor and validated their results. We discovered that GDB doesn't connect to the processor when it's in big‑endian mode, so at the end of our test programs we switched the mode back to little‑endian to work around that.&lt;/p&gt;
&lt;p&gt;In parallel we modified the CVA6 SDK to build Linux for big‑endian system. For that we mostly reused our work from a &lt;a href="https://www.codethink.co.uk/articles/risc-v-big-endian-support-runtime-testing/"&gt;previous project&lt;/a&gt;, where we ran big‑endian Linux in QEMU. We published our CVA6 SDK modifications &lt;a href="https://github.com/CodethinkLabs/cva6-sdk/tree/bjdooks/bigendian1"&gt;here&lt;/a&gt;. You can build it with &lt;code&gt;make BE=1 FS=1 images&lt;/code&gt;, and then flash it with &lt;code&gt;sudo -E make BE=1 FS=1 flash-sdcard SDDEVICE=/dev/sdX&lt;/code&gt;, where &lt;code&gt;sdX&lt;/code&gt; is your SD card.&lt;/p&gt;
&lt;p&gt;After successful big‑endian Linux boot, we celebrated by playing Tetris there :)&lt;/p&gt;
&lt;p&gt;&lt;img alt="CVA6 big‑endian boot log" src="https://www.codethink.co.uk/articles/cva6-big-endian/CVA6_big_endian_console.webp"&gt;&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;We &lt;a href="https://github.com/openhwgroup/cva6/pull/3057"&gt;modified&lt;/a&gt; CVA6 to allow runtime endianness switch and &lt;a href="https://github.com/CodethinkLabs/cva6-sdk/tree/bjdooks/bigendian1"&gt;demonstrated&lt;/a&gt; big‑endian Linux running there. This demonstrates possibility of big‑endian RISC‑V systems and makes experiments on them easier.&lt;/p&gt;
&lt;p&gt;You can read more RISC‑V articles from our blog &lt;a href="https://www.codethink.co.uk/tag/risc-v.html"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Are you looking for help with your RISC‑V projects? Get in touch with Codethink's team of experts by writing to us at connect@codethink.co.uk.&lt;/p&gt;</content><category term="2025"></category><category term="RISC-V"></category><category term="FPGA"></category><category term="Big-endian"></category></entry><entry><title>Bringing up a new distro for the CVA6 RISC‑V FPGA processor</title><link href="https://www.codethink.co.uk/articles/running-freedesktop-sdk-on-cva6/" rel="alternate"></link><published>2025-10-09T00:00:00+01:00</published><updated>2025-10-09T00:00:00+01:00</updated><author><name>Nazar Kazakov</name></author><id>tag:www.codethink.co.uk,2025-10-09:/articles/running-freedesktop-sdk-on-cva6/</id><summary type="html">&lt;p&gt;In this blogpost we will cover how to bring CVA6 processor up with Linux on it and how we replaced the OS there with a different one, based on Freedesktop SDK, updating the kernel version on the way.&lt;/p&gt;</summary><content type="html">&lt;p&gt;In this blogpost we will cover how to bring the CVA6 processor up with Linux on it and how we replaced the OS there with a different one, based on Freedesktop SDK, updating the kernel version on the way.&lt;/p&gt;
&lt;p&gt;CVA6 is being developed by the OpenHW Group, part of the Eclipse Foundation, of which Codethink is a member. We thought it would be useful to see how we can combine our approach to building software with the processor developed by a fellow Eclipse Foundation member.&lt;/p&gt;
&lt;p&gt;CVA6 has its &lt;a href="https://github.com/openhwgroup/cva6"&gt;source code&lt;/a&gt; available on GitHub, written in SystemVerilog. It implements the RISC‑V instruction set and can target a few FPGA boards. We found the best FPGA board with CVA6 support was the Digilent Genesys2, so we selected that to get going. &lt;/p&gt;
&lt;p&gt;We also had a lot of experience with using &lt;a href="https://freedesktop-sdk.io/"&gt;Freedesktop SDK&lt;/a&gt; as a platform for building a Linux OS, so we wanted to try to run it on CVA6. Freedesktop SDK allows you to easily get reproducible builds, and be as close to upstream as possible.&lt;/p&gt;
&lt;p&gt;During this project we managed to build and deploy a &lt;a href="https://github.com/CodethinkLabs/cva6-sdk-bst"&gt;new OS&lt;/a&gt;, and solved a few open issues along the way. We've &lt;a href="https://github.com/openhwgroup/cva6-sdk/pull/109"&gt;proposed&lt;/a&gt; updates and fixes to the CVA6 SDK. For QEMU, we proposed an initial &lt;a href="https://lists.gnu.org/archive/html/qemu-devel/2025-05/msg06183.html"&gt;CVA6 implementation&lt;/a&gt; with &lt;a href="https://lists.gnu.org/archive/html/qemu-devel/2025-05/msg05099.html"&gt;ethernet support&lt;/a&gt;. And we've published the build instructions for our OS on GitHub at &lt;a href="https://github.com/CodethinkLabs/cva6-sdk-bst"&gt;CodethinkLabs/cva6-sdk-bst&lt;/a&gt;, which you can build yourself using BuildStream – more on that below. Read on to find out how we did it, and what we'd like to happen next!&lt;/p&gt;
&lt;p&gt;&lt;img alt="Genesys2 board" src="https://www.codethink.co.uk/articles/running-freedesktop-sdk-on-cva6/genesys2_full.webp"&gt;&lt;/p&gt;
&lt;h2&gt;Generating and flashing the bitstream&lt;/h2&gt;
&lt;p&gt;The configuration data that's loaded into an FPGA is called bitstream, and all FPGA vendors have their own software suites to generate bitstreams. Despite being an open-source processor, CVA6 requires one of these proprietary tools to generate its bitstream. There's an open-source alternative called Yosys, but it's lacking some functionality of commercial tools.&lt;/p&gt;
&lt;p&gt;We are targeting a Xilinx FPGA, so we have to use the Xilinx's software suite called Vivado. CVA6 requires Vivado 2018.2 version to compile, which in turn requires old Ubuntu to run. To work around that we used a container. You can find instructions on how to install Vivado 2018.2 in a container &lt;a href="https://gist.github.com/nazar-codethink/d4fc4b8c377caebaba49cfc8167cc271"&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We also need to compile RISC‑V GCC toolchain following instructions in the CVA6 repository.&lt;/p&gt;
&lt;p&gt;Now that we have working Vivado and RISC‑V GCC, we can generate the bitstream from the CVA6 repository as follows:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre&gt;&lt;span&gt;&lt;/span&gt;&lt;code&gt;source /path/to/Xilinx/Vivado/2018.2/settings64.sh
LD_PRELOAD=/lib/x86_64-linux-gnu/libudev.so.1 CC=&amp;quot;$RISCV/bin/riscv-none-elf-gcc&amp;quot; make fpga
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;p&gt;The LD_PRELOAD is to work around &lt;a href="https://adaptivesupport.amd.com/s/article/000034450"&gt;this issue&lt;/a&gt;. The &lt;code&gt;$RISCV&lt;/code&gt; variable comes from toolchain installation.&lt;/p&gt;
&lt;p&gt;The next step is flashing the bitstream to the board. Add the &lt;a href="https://github.com/openhwgroup/cva6/blob/master/tutorials/fpga.md#debugging"&gt;udev rule&lt;/a&gt; to your system  and then follow the &lt;a href="https://github.com/openhwgroup/cva6/blob/master/tutorials/fpga.md#programming-the-memory-configuration-file-or-bitstream"&gt;flashing instructions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;After flashing you should see zero-stage bootloader output on the board's UART port. It tries to load something from the SD card, but since we haven't inserted the card yet, the bootloader just waits and does nothing.&lt;/p&gt;
&lt;h2&gt;Building cva6-sdk&lt;/h2&gt;
&lt;p&gt;Now let's generate an SD card image for our processor. The official way of doing it is to use &lt;a href="https://github.com/openhwgroup/cva6-sdk"&gt;cva6-sdk&lt;/a&gt;, which can generate SD card image with OpenSBI, U‑Boot, and Linux with busybox initrd. To do that, run &lt;code&gt;make images&lt;/code&gt; in the cva6-sdk repository. After that insert an SD card into your computer, and run &lt;code&gt;sudo -E make flash-sdcard SDDEVICE=/dev/sdX&lt;/code&gt;, where sdX is your SD card.&lt;/p&gt;
&lt;p&gt;Now insert the SD card into the Genesys2 board, and power it on. On the UART port you should see booting logs and eventually get to the busybox shell. You can play tetris there if you run &lt;code&gt;/tetris&lt;/code&gt; :-)&lt;/p&gt;
&lt;p&gt;We now have Linux running on our processor, even though the kernel and userspace versions are somewhat outdated.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Tetris on CVA6" src="https://www.codethink.co.uk/articles/running-freedesktop-sdk-on-cva6/tetris.webp"&gt;&lt;/p&gt;
&lt;h2&gt;Using Freedesktop SDK userspace with old kernel&lt;/h2&gt;
&lt;p&gt;On our way to full Freedesktop SDK OS, we first aimed at an intermediate step where we wanted to use cva6-sdk's bootloader and kernel but replace busybox initrd with our rootfs.&lt;/p&gt;
&lt;p&gt;We modified cva6-sdk to use a separate partition for rootfs and not include initrd in the kernel – this allowed us to easily use Freedesktop SDK based rootfs. To generate it we've written a few BuildStream “elements”, which are .bst files with YAML syntax that describe how to produce various software, in our case – rootfs image. As part of doing that we replaced default Freedesktop SDK kernel headers with the older ones from cva6-sdk, and disabled some profiling functionality in binutils, to fix a compilation error.&lt;/p&gt;
&lt;p&gt;Our first attempt to boot the new rootfs failed because systemd was timing out waiting for the serial device to appear. It was a problem with the slow boot process – by the time &lt;code&gt;systemd-udev-trigger.service&lt;/code&gt; started, it was too late, the serial device systemd unit had already stopped waiting. We solved this by increasing the default device timeout. We also disabled a few non-critical systemd services as they were taking a very long time to start. This is something to revisit later.&lt;/p&gt;
&lt;p&gt;After doing that we were able to log into Freedesktop SDK userspace! &lt;/p&gt;
&lt;h2&gt;Using new kernel&lt;/h2&gt;
&lt;p&gt;With Freedesktop SDK userspace working, it was time to update the kernel. cva6-sdk used Linux v5.10, which is an LTS kernel with support until 2026, but we wanted the newest stable release – v6.14 in this case. Using newer version brings us better performance and some new features.&lt;/p&gt;
&lt;p&gt;The first problem we encountered was the larger kernel size – U‑Boot's bootcmd wasn't reading enough data from the SD card, so we increased that amount.&lt;/p&gt;
&lt;p&gt;The kernel started booting now but was triggering kernel panic at a later stage. We found out that it was happening because of the memory protections set up by OpenSBI. Turned out in newer kernel versions Linux doesn't reserve the first 2MB of memory for OpenSBI as it used to. Adding a reserved-memory node to the device tree fixed this issue.&lt;/p&gt;
&lt;p&gt;Then we saw systemd failures. On startup it printed an error message about being unable to set its environment, and frozen execution. Also, the error messages were truncated. Unclear how to debug that, we turned to git bisect, with which we found that the errors were happening when Linux was doing unaligned memory accesses. When we disabled them with CONFIG_RISCV_EMULATED_UNALIGNED_ACCESS, the problem went away. We didn't get to the bottom of it, so it's something for future TODO.&lt;/p&gt;
&lt;p&gt;Now we have the new kernel working. \o/&lt;/p&gt;
&lt;p&gt;We were lacking some functionality though – cva6-sdk had a patch for v5.10 kernel with the board's ethernet driver, so we wanted to port it to v6.14. The porting itself was straightforward – fixing compilation errors, and one runtime error. We noted that the driver itself was poorly written and ideally needed to be rewritten from scratch, but we decided not to do that at the moment, so this is another thing for future TODO. Despite that, the ported driver worked and allowed us to use ssh to connect to the board.&lt;/p&gt;
&lt;p&gt;You can find the BuildStream project with all our .bst files and instructions on how to use it to generate an SD card image there – &lt;a href="https://github.com/CodethinkLabs/cva6-sdk-bst"&gt;https://github.com/CodethinkLabs/cva6-sdk-bst&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Freedesktop SDK on CVA6" src="https://www.codethink.co.uk/articles/running-freedesktop-sdk-on-cva6/fd-sdk.webp"&gt;&lt;/p&gt;
&lt;h2&gt;QEMU model&lt;/h2&gt;
&lt;p&gt;In parallel, we developed a QEMU model that simulates the CVA6. It was useful during initial kernel bring up, because with QEMU we could iterate quicker. Also having a working QEMU model should help in future developments for CVA6.
The patches for QEMU were submitted upstream – &lt;a href="https://lists.gnu.org/archive/html/qemu-devel/2025-05/msg06183.html"&gt;1&lt;/a&gt;, &lt;a href="https://lists.gnu.org/archive/html/qemu-devel/2025-05/msg05099.html"&gt;2&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Contributing back to cva6-sdk&lt;/h2&gt;
&lt;p&gt;We wanted to contribute something back to the cva6-sdk, so we replicated our kernel work there and also updated the software versions in their initrd. This is the merge request with the updates – &lt;a href="https://github.com/openhwgroup/cva6-sdk/pull/109"&gt;https://github.com/openhwgroup/cva6-sdk/pull/109&lt;/a&gt;. Creating it also required replicating everything we've done with 64 bit CVA6 to 32 bit CVA6, as cva6-sdk is common for both widths – this replication was straightforward though.&lt;/p&gt;
&lt;p&gt;An interesting fact is that during testing the updated cva6-sdk version with tetris we noticed that it always generated the same shapes. After some investigation, it was obvious that we had a bug in the floating point unit. We confirmed that it was present before our updates and was not caused by us. And then we discovered that it was &lt;a href="https://github.com/openhwgroup/cva6/commit/2700d14471963909923bdcfa1b013b4d1b30e567"&gt;just fixed&lt;/a&gt; in the master branch of CVA6. Regenerating the bitstream and using the latest CVA6 version fixed this bug with tetris.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;We &lt;a href="https://github.com/CodethinkLabs/cva6-sdk-bst"&gt;demonstrated&lt;/a&gt; how to boot Freedesktop SDK on CVA6, created &lt;a href="https://lists.gnu.org/archive/html/qemu-devel/2025-05/msg06183.html"&gt;QEMU model&lt;/a&gt; of it, and &lt;a href="https://github.com/openhwgroup/cva6-sdk/pull/109"&gt;updated&lt;/a&gt; the kernel and initrd in cva6-sdk. This brings the software on CVA6 closer to upstream and makes experiments on it easier.&lt;/p&gt;
&lt;p&gt;Potential future work includes updating U‑Boot version, debugging why some systemd services time out, rewriting the board's ethernet driver, and debugging unaligned memory access issue.&lt;/p&gt;
&lt;p&gt;Are you looking for help with your RISC‑V projects? Get in touch with Codethink's team of experts by writing to us at &lt;a href="mailto:connect@codethink.co.uk"&gt;connect@codethink.co.uk&lt;/a&gt;.&lt;/p&gt;</content><category term="2025"></category><category term="RISC-V"></category><category term="FPGA"></category><category term="Freedesktop SDK"></category><category term="Linux"></category><category term="QEMU"></category></entry><entry><title>Externally verifying Linux deadline scheduling with reproducible embedded Rust</title><link href="https://www.codethink.co.uk/articles/externally-verifying-linux/" rel="alternate"></link><published>2025-10-01T00:00:00+01:00</published><updated>2025-10-01T00:00:00+01:00</updated><author><name>Theodore Tucker</name></author><id>tag:www.codethink.co.uk,2025-10-01:/articles/externally-verifying-linux/</id><summary type="html">&lt;p&gt;Exploring how Codethink used embedded Rust to measure Linux’s scheduling performance with an external clock, while maintaining bit-for-bit reproducibility of the testing firmware.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;em&gt;Exploring how Codethink used embedded Rust to measure Linux’s scheduling performance with an external clock, while maintaining bit-for-bit reproducibility of the testing firmware.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Before choosing a Linux-based operating system on a multi-core processor for a safety-critical task, we must first be certain of its real-time capabilities. These can be demonstrated through repeated long-duration “soak” testing of a running system, but such results are less valuable if all timing measurements are referenced to the system’s own clock. To help demonstrate the real-time scheduling capabilities of Codethink’s Trustable Reproducible Linux-based Operating System (&lt;a href="https://www.codethink.co.uk/ctrl-os.html"&gt;CTRL OS&lt;/a&gt;) we employed custom embedded Rust firmware on an external microcontroller, with its own clock, to monitor the timings of Linux processes. Moreover, as our Trustable Software Framework (TSF) stipulates, the Rust toolchain lets us compile that firmware reproducibly and in a controlled environment, just like any other testing tool.&lt;/p&gt;
&lt;p&gt;The Linux kernel provides the &lt;code&gt;SCHED_DEADLINE&lt;/code&gt; mode to allocate CPU time to threads with hard real-time constraints running on the same processor core&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;. &lt;code&gt;SCHED_DEADLINE&lt;/code&gt; threads declare their mode and constraints to the kernel with the &lt;code&gt;sched_setattr&lt;/code&gt; syscall, specifying 4 important parameters&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Period:&lt;/strong&gt; how often the kernel should provide the thread with its runtime, in ns  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deadline:&lt;/strong&gt; the time in each period by which the thread must have finished its execution for that period, in ns  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runtime:&lt;/strong&gt; the thread’s expected worst-case execution time in each period, in ns  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;RECLAIM&lt;/code&gt; flag &lt;em&gt;(optional)&lt;/em&gt;:&lt;/strong&gt; whether the kernel should provide the thread with unused CPU time in a period if the thread hadn’t finished its execution&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Using the earliest deadline first algorithm, the kernel then balances CPU time between &lt;code&gt;SCHED_DEADLINE&lt;/code&gt; threads such that each meets its own scheduling parameters. If it would be impossible to allocate CPU time to a new thread without violating its own or existing threads’ requirements, the kernel rejects the sched_setattr call. &lt;code&gt;SCHED_DEADLINE&lt;/code&gt; thus provides a theoretical basis for Linux as a powerful, open-source, and real-time operating system family.&lt;/p&gt;
&lt;p&gt;However, to put this into practice, and work towards certifying Linux’s real-time scheduling as a safety function of CTRL OS, we must evidence that a set of &lt;code&gt;SCHED_DEADLINE&lt;/code&gt; threads can be successfully scheduled on real hardware repeatedly, over many thousands of hours of soak testing. Codethink provides the userspace program &lt;em&gt;rusty-worker&lt;/em&gt; for this purpose. rusty-worker has two threads: a &lt;code&gt;SCHED_DEADLINE&lt;/code&gt; “worker” thread, and a &lt;code&gt;SCHED_OTHER&lt;/code&gt; “monitor” thread. (&lt;code&gt;SCHED_OTHER&lt;/code&gt; is the default scheduling mode for Linux threads, subjecting them to round-robin, not deadline, scheduling.) rusty-worker’s monitor thread initially configures the sched_setattr parameters of the worker according to user configuration. It then periodically reports the worker’s measured scheduling parameters, specifically runtime and period, to the systemd journal for post-test analysis and Codethink’s open-source safety-monitor running on the same system to trigger runtime mitigations&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Diagram of the SCHED_DEADLINE worker thread" src="https://www.codethink.co.uk/articles/externally-verifying-linux/Sched-Deadline-Diagram.webp"&gt; 
&lt;em&gt;rusty-worker’s &lt;code&gt;SCHED_DEADLINE&lt;/code&gt; worker thread shares CPU time with other threads. It declares its period, deadline, and worst-case runtime to the kernel with the &lt;code&gt;sched_setattr&lt;/code&gt; syscall, and the kernel ensures that it gets all its runtime before each period’s real-time deadline by balancing with other threads. Note that the rusty-worker’s real runtime and the position of that runtime in the period may differ between periods, however execution should always finish before the deadline expires.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The safety-monitor relies on rusty-worker’s extrinsic reports of scheduling period and runtime to make safety decisions (e.g. should the system be restarted by allowing a watchdog timer to expire?), and CTRL OS’s engineers rely on the post-test reports to investigate regressions and remedy them. If there were a problem with the device-under-test’s clock, a rusty-worker’s true runtime and period might differ from what it reported, without safety-monitor or engineers being any the wiser. To guard against this, we modified rusty-worker to send signals on a UART serial port when it started and completed execution for a period. As the UART protocol is, by definition, asynchronous, a device on the other end of the serial port can time these signals and derive its own external measurement of scheduling parameters without relying on the device-under-test’s clock: the time between a &lt;em&gt;started&lt;/em&gt; and &lt;em&gt;completed&lt;/em&gt; signal is the runtime, and the time between two &lt;em&gt;started&lt;/em&gt; signals is the period.&lt;/p&gt;
&lt;p&gt;We chose a Raspberry Pi Pico as our external measurement device. It is mounted on a custom printed circuit board with a second Pico, which acts as a probe to flash the first Pico with firmware before each test, and a logic-level shifter so signals from both RS232 and 3.3V level UART devices can be analysed. The Pico is based on the RP2040 ARM Cortex-M0+ microcontroller, and has first-class support for embedded Rust, without the overhead of an operating system. We thus set about writing Rust verification firmware (vfrs) for the Pico, utilising the rp-hal (Hardware Abstraction Layer) crate to build up an interrupt-driven architecture to provide accurate timings of signals on the UART line, and hence report its measurements of rusty-worker’s runtime and period on the device-under-test over USB to a CI runner&lt;sup id="fnref:5"&gt;&lt;a class="footnote-ref" href="#fn:5"&gt;5&lt;/a&gt;&lt;/sup&gt;. The CI runner aggregates the external measurements with rusty-worker’s reports to the systemd journal, performing pairwise matching of measurement sessions, before uploading to an OpenSearch data lake for statistical analysis.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image of an EM PCB" src="https://www.codethink.co.uk/articles/externally-verifying-linux/EM_PCB.webp"&gt;&lt;br&gt;
&lt;em&gt;The custom PCB used for external scheduling measurement in CTRL OS’s test rigs. The left-hand Pico U1 serves as a Debug Probe, flashing the right-hand Pico U2 with a controlled build of vfrs in each test&lt;sup id="fnref:6"&gt;&lt;a class="footnote-ref" href="#fn:6"&gt;6&lt;/a&gt;&lt;/sup&gt;. The right-hand Pico connects to the CTRL OS device-under-test via an RS232 UART (J3) or a 3.3V UART (J4), and the test runner via USB.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We chose Rust for vfrs despite the well-documented and extensive C/C++ SDK provided by the Raspberry Pi Pico’s manufacturers&lt;sup id="fnref:7"&gt;&lt;a class="footnote-ref" href="#fn:7"&gt;7&lt;/a&gt;&lt;/sup&gt;. Our reasoning for this was threefold: firstly, HALs take advantage of Rust’s type and reference systems to ensure illegal hardware configurations and memory-unsafe operations are rejected at compile-time, not runtime, giving us more certainty about the Pico’s stability and accuracy over hours of soak testing. The only code exempted from the Rust compiler’s memory safety checks must be presented in explicitly marked &lt;code&gt;unsafe&lt;/code&gt; code blocks, alongside human-written safety argumentation and justification.&lt;/p&gt;
&lt;p&gt;Secondly, as the Rust code needed to bootstrap interrupts, USB and UARTs on a Pico is more verbose than the equivalent C, less information is hidden from engineers and safety assessors about how we use the RP2040’s registers and peripherals, giving more certainty around its exact behaviour and timing accuracy. For example, as we needed to calculate the Pico’s clock frequency (125 MHz) in code, we realised that our methodology would be inappropriate for the fastest rusty-worker processes with runtimes of hundreds of nanoseconds; this equates to just hundreds of Pico reference clock ticks.&lt;/p&gt;
&lt;p&gt;Lastly, we chose Rust as its embedded toolchain can be used in a controlled build system just as easily as its desktop toolchain. CTRL OS is developed in accordance with the Trustable Software Framework (TSF), which asserts that &lt;em&gt;“All tests for CTRL OS, and its build and test environments, are constructed from controlled/mirrored sources and are reproducible”&lt;/em&gt;&lt;sup id="fnref:8"&gt;&lt;a class="footnote-ref" href="#fn:8"&gt;8&lt;/a&gt;&lt;/sup&gt;. We achieved this by defining vfrs as an BuildStream element integrated into the CI image for CTRL OS’s tests&lt;sup id="fnref:9"&gt;&lt;a class="footnote-ref" href="#fn:9"&gt;9&lt;/a&gt;&lt;/sup&gt;. This element specifies vfrs’s source code, dependency crates, build commands and freedesktop-sdk’s Rust toolchain, with minimal patching to add the &lt;code&gt;thumbv6m-none-eabi&lt;/code&gt; target and specify an LLVM rather than GCC backend. Since we build vfrs like any other testing component, we can make the same guarantees about its provenance (asserting the origins of source code, dependencies, build environment, and hence the final binary) and bit-for-bit reproducibility. We achieve this without the ARM toolchain wrangling associated with the C equivalent, such as handling vendor-specific compiler versions, nested Git submodules, and build scripts which can download arbitrary binaries from the Internet at compile-time.&lt;/p&gt;
&lt;p&gt;We perform weekly analysis of the external and rusty-worker scheduling measurements stored in OpenSearch, both in version-controlled Jupyter notebooks and automatic validators to provide scores for CTRL OS’s Trustable compliance report&lt;sup id="fnref:10"&gt;&lt;a class="footnote-ref" href="#fn:10"&gt;10&lt;/a&gt;&lt;/sup&gt;. With CTRL OS and rusty-workers running on an ARM64 Rockchip RK3588-based test system, we found exceptional correlation between rusty-worker and external measurements of runtime and period, with matching distributions and summary statistics to within 10 µs. These external measurement soak tests gave us great confidence in the many millions of samples of scheduling metrics collected in other soak tests on the same hardware. Furthermore, as external measurement requires that a rusty-worker’s worker thread interacts with a TTY serial device, we predicted and observed improved scheduling performance for the entire system when CTRL OS ran with the &lt;code&gt;PREEMPT_RT&lt;/code&gt; kernel configuration option enabled&lt;sup id="fnref:11"&gt;&lt;a class="footnote-ref" href="#fn:11"&gt;11&lt;/a&gt;&lt;/sup&gt;. This was because, with &lt;code&gt;PREEMPT_RT&lt;/code&gt;, the &lt;code&gt;SCHED_DEADLINE&lt;/code&gt; worker thread may be interrupted (or pre-empted) while it works through the notoriously non-deterministic spin locks of the kernel’s TTY layer when sending signals to the Pico.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image of a histogram " src="https://www.codethink.co.uk/articles/externally-verifying-linux/EM_histogram.webp"&gt;  &lt;br&gt;
&lt;em&gt;A histogram showing the mean elapsed runtime of a rusty-worker as measured by the rusty-worker itself and externally by vfrs on a Pico. Elapsed runtime plotted here is the time between the “started working” and “work completed” signals from rusty-worker, rather than the runtime specified in &lt;code&gt;sched_setattr&lt;/code&gt;. These signals may have been sent in different blocks of runtime in the same period if the thread was pre-empted, so we see two peaks in the distribution: one at 710 µs without pre-emption, and one at 780 µs with pre-emption. Importantly though, the orange rusty-worker distribution maps well to the blue vfrs distribution.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;In summary, external measurement, powered by embedded Rust firmware, allowed us to robustly evidence the real-time scheduling capabilities of a Linux environment, while maintaining tight control of test environment provenance and stability. &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;If you are interested in Codethink’s CTRL OS, Trustable Software Framework, or build engineering and embedded software expertise, then please get in touch at &lt;a href="mailto:connect@codethink.co.uk"&gt;connect@codethink.co.uk&lt;/a&gt;.&lt;/strong&gt; &lt;/p&gt;
&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;https://www.kernel.org/doc/html/latest/scheduler/sched-deadline.html&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;https://www.man7.org/linux/man-pages/man2/sched_setattr.2.html&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;https://www.man7.org/linux/man-pages/man8/systemd-journald.service.8.html&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;https://gitlab.com/CodethinkLabs/safety-monitor/safety-monitor&amp;#160;&lt;a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 4 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:5"&gt;
&lt;p&gt;https://github.com/rp-rs/rp-hal&amp;#160;&lt;a class="footnote-backref" href="#fnref:5" title="Jump back to footnote 5 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:6"&gt;
&lt;p&gt;https://github.com/raspberrypi/debugprobe&amp;#160;&lt;a class="footnote-backref" href="#fnref:6" title="Jump back to footnote 6 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:7"&gt;
&lt;p&gt;https://www.raspberrypi.com/documentation/microcontrollers/c_sdk.html&amp;#160;&lt;a class="footnote-backref" href="#fnref:7" title="Jump back to footnote 7 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:8"&gt;
&lt;p&gt;https://codethinklabs.gitlab.io/trustable/trustable/doorstop/TA.html#ta-tests&amp;#160;&lt;a class="footnote-backref" href="#fnref:8" title="Jump back to footnote 8 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:9"&gt;
&lt;p&gt;https://buildstream.build/&amp;#160;&lt;a class="footnote-backref" href="#fnref:9" title="Jump back to footnote 9 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:10"&gt;
&lt;p&gt;https://codethinklabs.gitlab.io/trustable/trustable/trudag/validators.html&amp;#160;&lt;a class="footnote-backref" href="#fnref:10" title="Jump back to footnote 10 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:11"&gt;
&lt;p&gt;https://dl.acm.org/doi/10.1145/3297714&amp;#160;&lt;a class="footnote-backref" href="#fnref:11" title="Jump back to footnote 11 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="2025"></category><category term="Linux"></category><category term="Rust"></category><category term="open source"></category><category term="safety"></category><category term="scheduling"></category></entry><entry><title>Engineering Trust: Formulating Continuous Compliance for Open Source</title><link href="https://www.codethink.co.uk/articles/engineering-trust/" rel="alternate"></link><published>2025-09-22T00:00:00+01:00</published><updated>2025-09-22T00:00:00+01:00</updated><author><name>Paul Albertella</name></author><id>tag:www.codethink.co.uk,2025-09-22:/articles/engineering-trust/</id><summary type="html">&lt;p&gt;Our latest blog post provides an overview of the Trustable Software Framework and how it's applied.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Software has an increasingly important role to play in business, security and safety-critical applications. The complexity of the systems and hardware involved in these applications is also growing rapidly, and open source software is increasingly used in these contexts.&lt;/p&gt;
&lt;p&gt;Established formal engineering approaches are not keeping up. They are often slow, difficult and costly to scale when applied to complex software and systems, and are not widely or consistently applied to open source software. They are often not intended, or not suitable, for managing rapid changes or continuous updates.&lt;/p&gt;
&lt;p&gt;Requirements are a fundamental element in all of these established approaches, but requirements are broken. High-level requirements are a wish list; stakeholders just make a list of features. Low-level requirements are a narrative; developers just describe what is implemented. &lt;/p&gt;
&lt;p&gt;Formal process models see the dynamic nature of FOSS projects as a problem, but this can be a key part of their value. However, because requirements are broken, the intent and expectations that inform the software are often lost in the noise.&lt;/p&gt;
&lt;p&gt;So, how do we address these issues?&lt;/p&gt;
&lt;p&gt;Codethink’s Paul Albertella and Kaspar Matas outline how the Eclipse Trustable Software Framework (TSF) — a lightweight continuous compliance framework, designed as and for FOSS — can help you to do just this.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Watch the talk to discover:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An overview of TSF and examples of how it is applied  &lt;/li&gt;
&lt;li&gt;How to organise and evidence your own objectives, including the intent and expectations that inform the software you are working on  &lt;/li&gt;
&lt;li&gt;How the framework is used to manage automated, transparent and traceable bodies of evidence, which can be quantified by a confidence score  &lt;/li&gt;
&lt;li&gt;How the results can be mapped to functional safety standards, to support certification and ongoing assessment.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Got your attention? Watch the talk in full below:&lt;/p&gt;
&lt;div style="width: 100%; min-width: 300px; max-width: 560px;"&gt; &lt;div style="position: relative; width: 100%; overflow: hidden; padding-top: 56.25%;"&gt; &lt;p&gt;&lt;iframe style="position: absolute; top: 0; left: 0; right: 0; width: 100%; height: 100%; border: none;" src="https://www.youtube.com/embed/zsmhZrZnRL8?si=-44if8ByKVSu9-Gt" width="560" height="315" allowfullscreen="allowfullscreen" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"&gt;&lt;/iframe&gt;&lt;/p&gt; &lt;/div&gt; &lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Interested in learning more about how to apply TSF to your project? Complete the form below and a member of the team will be in touch with you.&lt;/strong&gt;&lt;/p&gt;
&lt;style type="text/css" scoped&gt;
    .mauticform_wrapper { max-width: 600px; margin: 10px auto; }
    .mauticform-innerform {}
    .mauticform-post-success {}
    .mauticform-name { font-weight: bold; font-size: 1.5em; margin-bottom: 3px; }
    .mauticform-description { margin-top: 2px; margin-bottom: 10px; }
    .mauticform-error { margin-bottom: 10px; color: red; }
    .mauticform-message { margin-bottom: 10px;color: green; }
    .mauticform-row { display: block; margin-bottom: 20px; }
    .mauticform-label { font-size: 1.1em; display: block; font-weight: bold; margin-bottom: 5px; }
    .mauticform-row.mauticform-required .mauticform-label:after { color: #e32; content: " *"; display: inline; }
    .mauticform-helpmessage { display: block; font-size: 0.9em; margin-bottom: 3px; }
    .mauticform-errormsg { display: block; color: red; margin-top: 2px; }
    .mauticform-selectbox, .mauticform-input, .mauticform-textarea { width: 100%; padding: 0.5em 0.5em; border: 1px solid #CCC; background: #fff; box-shadow: 0px 0px 0px #fff inset; border-radius: 4px; box-sizing: border-box; }
    .mauticform-checkboxgrp-row {}
    .mauticform-checkboxgrp-label { font-weight: normal; }
    .mauticform-checkboxgrp-checkbox {}
    .mauticform-radiogrp-row {}
    .mauticform-radiogrp-label { font-weight: normal; }
    .mauticform-radiogrp-radio {}
    .mauticform-button-wrapper .mauticform-button.btn-default, .mauticform-pagebreak-wrapper .mauticform-pagebreak.btn-default { color: #5d6c7c;background-color: #ffffff;border-color: #dddddd;}
    .mauticform-button-wrapper .mauticform-button, .mauticform-pagebreak-wrapper .mauticform-pagebreak { display: inline-block;margin-bottom: 0;font-weight: 600;text-align: center;vertical-align: middle;cursor: pointer;background-image: none;border: 1px solid transparent;white-space: nowrap;padding: 6px 12px;font-size: 13px;line-height: 1.3856;border-radius: 3px;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;}
    .mauticform-button-wrapper .mauticform-button.btn-default[disabled], .mauticform-pagebreak-wrapper .mauticform-pagebreak.btn-default[disabled] { background-color: #ffffff; border-color: #dddddd; opacity: 0.75; cursor: not-allowed; }
    .mauticform-pagebreak-wrapper .mauticform-button-wrapper {  display: inline; }
&lt;/style&gt;
&lt;div id="mauticform_wrapper_engineeringtrust" class="mauticform_wrapper"&gt;
    &lt;form autocomplete="false" role="form" method="post" action="https://marke2ing.codethink.co.uk/form/submit?formId=4" id="mauticform_engineeringtrust" data-mautic-form="engineeringtrust" enctype="multipart/form-data"&gt;
        &lt;div class="mauticform-error" id="mauticform_engineeringtrust_error"&gt;&lt;/div&gt;
        &lt;div class="mauticform-message" id="mauticform_engineeringtrust_message"&gt;&lt;/div&gt;
        &lt;div class="mauticform-innerform"&gt;


          &lt;div class="mauticform-page-wrapper mauticform-page-1" data-mautic-form-page="1"&gt;

            &lt;div id="mauticform_engineeringtrust_email" data-validate="email" data-validation-type="email" class="mauticform-row mauticform-email mauticform-field-1 mauticform-required"&gt;
                &lt;label id="mauticform_label_engineeringtrust_email" for="mauticform_input_engineeringtrust_email" class="mauticform-label"&gt;Email&lt;/label&gt;
                &lt;span class="mauticform-helpmessage"&gt;Please input your company email address.&lt;/span&gt;
                &lt;input id="mauticform_input_engineeringtrust_email" name="mauticform[email]" value="" class="mauticform-input" type="email"&gt;
                &lt;span class="mauticform-errormsg" style="display: none;"&gt;This field is required.&lt;/span&gt;
            &lt;/div&gt;

            &lt;div id="mauticform_engineeringtrust_accept_our_privacy_policy1" data-validate="accept_our_privacy_policy1" data-validation-type="checkboxgrp" class="mauticform-row mauticform-checkboxgrp mauticform-field-2 mauticform-required"&gt;
                &lt;label class="mauticform-label" for="mauticform_checkboxgrp_checkbox_accept_our_privacy_policy1_Yes1"&gt;Accept our Privacy Policy&lt;/label&gt;
                &lt;div class="mauticform-checkboxgrp-row"&gt;                    &lt;input class="mauticform-checkboxgrp-checkbox" name="mauticform[accept_our_privacy_policy1][]" id="mauticform_checkboxgrp_checkbox_accept_our_privacy_policy1_yesprivacypolicy0" type="checkbox" value="yes_privacy_policy"&gt;
                    &lt;label id="mauticform_checkboxgrp_label_accept_our_privacy_policy1_yesprivacypolicy0" for="mauticform_checkboxgrp_checkbox_accept_our_privacy_policy1_yesprivacypolicy0" class="mauticform-checkboxgrp-label"&gt;Yes&lt;/label&gt;
                &lt;/div&gt;
                &lt;span class="mauticform-errormsg" style="display: none;"&gt;This field is required.&lt;/span&gt;
            &lt;/div&gt;

            &lt;div id="mauticform_engineeringtrust_accept_our_privacy_policy" class="mauticform-row mauticform-freetext mauticform-field-3"&gt;    
                &lt;h3 id="mauticform_label_engineeringtrust_accept_our_privacy_policy" for="mauticform_input_engineeringtrust_accept_our_privacy_policy" class="mauticform-label"&gt;
                    Accept our Privacy Policy
                &lt;/h3&gt;
                &lt;div id="mauticform_input_engineeringtrust_accept_our_privacy_policy" name="mauticform[accept_our_privacy_policy]" value="" class="mauticform-freetext"&gt;
                    &lt;p&gt;Learn more about our &lt;a href="https://www.codethink.co.uk/privacy.html"&gt;Privacy Policy here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;By clicking submit, I agree to receive email updates from Codethink Ltd about our latest products and services.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div id="mauticform_engineeringtrust_submit" class="mauticform-row mauticform-button-wrapper mauticform-field-4"&gt;
                &lt;button type="submit" name="mauticform[submit]" id="mauticform_input_engineeringtrust_submit" value="" class="mauticform-button btn btn-default"&gt;Submit&lt;/button&gt;
            &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;input type="hidden" name="mauticform[formId]" id="mauticform_engineeringtrust_id" value="4"&gt;
        &lt;input type="hidden" name="mauticform[return]" id="mauticform_engineeringtrust_return" value=""&gt;
        &lt;input type="hidden" name="mauticform[formName]" id="mauticform_engineeringtrust_name" value="engineeringtrust"&gt;

        &lt;/form&gt;
&lt;/div&gt;</content><category term="2025"></category><category term="Trustable"></category><category term="FOSS"></category></entry><entry><title>Why Renting Software Is a Dangerous Game</title><link href="https://www.codethink.co.uk/articles/software-ownership/" rel="alternate"></link><published>2025-07-29T00:00:00+01:00</published><updated>2025-07-29T00:00:00+01:00</updated><author><name>Rob Kendrick</name></author><id>tag:www.codethink.co.uk,2025-07-29:/articles/software-ownership/</id><summary type="html">&lt;p&gt;Software is never a temporary feature, especially in the fast-moving world of embedded systems. In industries like automotive, software is at the core of the product’s function, but time and time again, companies treat it like a short-term asset.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Software is &lt;em&gt;never&lt;/em&gt; a temporary feature, especially in the fast-moving world of embedded systems. In industries like automotive, software is at the core of the product’s function, but time and time again, companies treat it like a short-term asset. It’s a common practice to license third-party components without proper planning for long-term ownership of the software.  &lt;/p&gt;
&lt;p&gt;But fast-forward 15 years and it is a situation ripe for disaster. The vendor might not exist anymore, or could be acquired and fully disassembled for parts. Then what? How can a car with unmaintained software stay on the road safely? It’s a dangerous game to rent your software. The best way to guarantee long-term maintainability? You have to own it.    &lt;/p&gt;
&lt;h2&gt;Ownership = Control, Not Just IP&lt;/h2&gt;
&lt;p&gt;In software, ownership isn’t just about having intellectual property and rights to the product. When licensing software without access to the source code or perpetual rights, you have essentially put a timer on your product. Think about it: You are placing your bets that the vendor will (1) still be around, (2) be willing to support you, and (3) still be selling the same or a compatible version of the software 15 years down the line.   &lt;/p&gt;
&lt;p&gt;In other instances where projects are taking a longer time in the development phase, you also face the possibility of the vendor no longer existing when your product is ready to go to market. The vendor could get acquired by a competitor who refuses to license or support the product you’re hoping to back. Without access to the source code, you’re forced to go back to the drawing board to re-engineer.  &lt;/p&gt;
&lt;h2&gt;15 Years Later&lt;/h2&gt;
&lt;p&gt;Focusing on the automotive world, many of the large markets legally oblige car manufacturers to support their &lt;a href="https://unece.org/sites/default/files/2023-02/R155e%20%282%29.pdf"&gt;software for 10 years after the last unit rolls off the production line&lt;/a&gt;. Considering development, certification, and testing time, these 10 years might turn into a 20-year timeline for full cybersecurity support.  &lt;/p&gt;
&lt;p&gt;Imagine when year 12 comes around, and your product faces a critical security vulnerability. The problem: you don’t own the software, and the vendor refuses to cooperate. What comes next? You can’t patch or rebuild and could even be forced to re-engineer the entire ECU. This includes changing the different hardware components to accommodate for the new OS. You’re looking at a &lt;a href="https://www.wired.com/2015/07/hackers-remotely-kill-jeep-highway/"&gt;product recall-level issue&lt;/a&gt; or, worse, having no option to buy back the remaining fleet.  &lt;/p&gt;
&lt;p&gt;With &lt;a href="https://www.oecd.org/en/data/indicators/inflation-cpi.html"&gt;inflation&lt;/a&gt;, the cost of fixing or rebuilding these systems years later would also be considerably higher than if you had control from the start. Ownership doesn’t just mitigate risk but addresses cost predictability in the long term.  &lt;/p&gt;
&lt;h2&gt;The Toolchain&lt;/h2&gt;
&lt;p&gt;Another often-overlooked risk is development tooling. You might license a proprietary application with no issues, but what about the compilers, SDKs, or development environments used to build and maintain it?  &lt;/p&gt;
&lt;p&gt;For example, could Creative Cloud Photoshop open a file made in Photoshop 1.0? The answer is ‘sort of’. While it may be able to &lt;a href="https://community.adobe.com/t5/photoshop-ecosystem-discussions/transfer-old-ps-files-to-new-version/m-p/15240912#:~:text=First%2C%20D.,open%20it%20and%20resave%20it."&gt;open the older file format&lt;/a&gt;, this file likely contains features and tools not supported in the new version, meaning it wouldn’t be fully functional. Now imagine trying to rebuild 15-year-old software with tools that are no longer supported or that require new licences just to install.  &lt;/p&gt;
&lt;h2&gt;When Does Proprietary Make Sense?&lt;/h2&gt;
&lt;p&gt;Like in the case of bleeding-edge technology, there are occasions where proprietary software is the only viable option as the products might only be available through a commercial provider. Still, there are options to negotiate access to the product’s source code under defined conditions. For example, some companies place code in &lt;a href="https://www.escode.com/resources/what-is-software-escrow/"&gt;escrow&lt;/a&gt;, with events like bankruptcy allowing the customer to take control if needed.  &lt;/p&gt;
&lt;p&gt;Unfortunately, a lot of companies assess this problem when it is too late. Getting ahead would be increasingly helpful in sectors like automotive where the scale of software continues to increase.  &lt;/p&gt;
&lt;h2&gt;Where Does FOSS Stand?&lt;/h2&gt;
&lt;p&gt;FOSS thrives in this environment. The use of open technologies helps avoid vendor lock-in, while gaining traceability, and long-term maintainability. When problems arise, you have the source code &lt;em&gt;and&lt;/em&gt; the right to modify it. In a &lt;a href="https://www.linuxfoundation.org/blog/blog/how-open-source-is-transforming-the-automotive-industry"&gt;FOSS setting&lt;/a&gt;, you’re also part of a community where there is a sustainable ecosystem that looks to maintain, patch, and evolve the software shared.  &lt;/p&gt;
&lt;h2&gt;Questions Every Company Should Be Asking&lt;/h2&gt;
&lt;p&gt;To protect your future, ask these questions at the start of every project:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Will I be able to run this software exactly as-is in 15 years’ time?  &lt;/li&gt;
&lt;li&gt;Will I be able to make changes quickly if needed?  &lt;/li&gt;
&lt;li&gt;How hard will those two things be when time and pressure are working against me?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you can’t confidently answer all three then you don’t own your software, you’re renting it.  &lt;/p&gt;
&lt;h2&gt;Own Your Software, Own Your Future&lt;/h2&gt;
&lt;p&gt;Owning your software is about more than access. Think of control, sustainability, and resilience over time. If you lease &lt;em&gt;every&lt;/em&gt; component of your stack, you’re betting that your vendors will still be around in 15 years, still offering the same support, and still aligned with your needs. That’s rare.  &lt;/p&gt;
&lt;p&gt;Futureproofing does not start at the top. It starts with the engineers, architects, and procurement teams that need awareness, tools, and authority to make long-term decisions today that protect the business tomorrow. Choosing open technologies, negotiating source access, and asking the right questions early on can mean the difference between a quick patch and a product recall 12 years after launch.  &lt;/p&gt;
&lt;p&gt;If your product needs to last 15 years, your software must last just as long. Start owning your software today. Talk to &lt;a href="https://www.codethink.co.uk/contact.html"&gt;Codethink&lt;/a&gt; about building long-term, trustable systems that stand the test of time.&lt;/p&gt;</content><category term="2025"></category><category term="FOSS"></category><category term="Linux"></category><category term="QNX"></category><category term="Automotive"></category></entry><entry><title>Linux vs. QNX in Safety-Critical Systems: A Pragmatic View</title><link href="https://www.codethink.co.uk/articles/qnx-vs-linux/" rel="alternate"></link><published>2025-06-17T00:00:00+01:00</published><updated>2025-06-17T00:00:00+01:00</updated><author><name>John Ellis</name></author><id>tag:www.codethink.co.uk,2025-06-17:/articles/qnx-vs-linux/</id><summary type="html">&lt;p&gt;Codethink dives into the Linux vs. QNX debate—focusing on architecture, performance, scalability, and long-term production viability.&lt;/p&gt;</summary><content type="html">&lt;p&gt;In the ongoing debate between Linux&lt;sup id="fnref:1"&gt;&lt;a class="footnote-ref" href="#fn:1"&gt;1&lt;/a&gt;&lt;/sup&gt;,&lt;sup id="fnref:2"&gt;&lt;a class="footnote-ref" href="#fn:2"&gt;2&lt;/a&gt;&lt;/sup&gt; and QNX,&lt;sup id="fnref:3"&gt;&lt;a class="footnote-ref" href="#fn:3"&gt;3&lt;/a&gt;&lt;/sup&gt;,&lt;sup id="fnref:4"&gt;&lt;a class="footnote-ref" href="#fn:4"&gt;4&lt;/a&gt;&lt;/sup&gt; much of the noise comes from theoretical positioning and outdated assumptions—not from real-world engineering experience. It’s time to set aside the marketing spin and focus on what matters: architecture, performance, scalability, and long-term viability in production environments.&lt;/p&gt;
&lt;p&gt;At Codethink, we are deeply invested in open source software, and we’ve seen Linux outperform alternatives in most safety-critical use cases. That said, QNX can offer specific advantages under certain conditions. This post outlines the key architectural, performance, and ecosystem trade-offs—grounded in production experience, not ideology.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;1. Are Linux and QNX Even Comparable?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Not exactly. While both are used in embedded and safety-related systems, they’re typically not deployed in the same architectural roles. QNX aims for predictability and simplicity, while Linux is designed for performance, flexibility, and scale. Each comes with trade-offs.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;2. Architecture: Microkernel vs. Macrokernel&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;QNX&lt;/strong&gt; uses a microkernel architecture, where OS services (e.g., file system, networking) run as separate userland processes. This can offer fault isolation but introduces latency from inter-process communication.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt;, as a macrokernel, integrates these services within the kernel itself. The result: significantly faster internal communication and higher throughput—critical in systems that demand responsiveness.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;3. IPC and Real-Time Behaviour&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;QNX’s&lt;/strong&gt; blocking IPC model supports tight real-time behavior by reducing priority inversion. However, this model introduces overhead when porting POSIX software, which is emulated using userland helpers.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt; favours non-blocking, POSIX-native IPC designed for throughput and scalability. While this can introduce jitter in extreme cases, real-world latency is typically lower—and its scheduler is deeply optimized for modern CPU topologies.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;4. Performance: Consistency vs. Throughput&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;QNX&lt;/strong&gt; offers more consistent latency, but poor hardware utilisation and limited scalability.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt; delivers vastly higher throughput, supported by advanced scheduling, isolation, and containerisation techniques, allowing critical systems to be prioritised deterministically.&lt;/p&gt;
&lt;p&gt;With proper configuration, Linux can provide real-time performance under load—making the “Linux isn’t real-time” narrative outdated.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;5. Scheduling and Resource Isolation&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Linux&lt;/strong&gt; goes far beyond simple priority queues:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Real-time scheduling modes (FIFO, round-robin, deadline) are widely used.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Cgroups and namespaces enable fine-grained CPU, memory, and IO partitioning.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Resource isolation lets critical functions maintain access without resorting to hypervisors.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This richness is crucial for mixed-criticality systems where multiple workloads share hardware.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Linux delivers verifiable confidence in real-time scheduling performance.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;While many real-time operating systems—including proprietary ones—claim support for widely used scheduling algorithms (e.g., FIFO, round-robin, deadline), Linux stands apart in one critical respect: transparency. Because Linux is open source, its implementation of these algorithms is actively studied, verified, and continuously improved by a global technical community. This visibility fosters greater confidence that the real-world behaviour of the system aligns with theoretical guarantees. This assurance is difficult, if not impossible, to obtain with closed-source alternatives. This ability to independently verify system behaviour is a material strategic advantage in safety- or performance-critical environments.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;6. Legal and Operational Realities&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;The conversation isn’t just technical—it’s strategic. Consider:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;QNX tooling and SDKs are rarely perpetually licensed&lt;/strong&gt;. In 15 years, can you still rebuild your product?&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Linux is open, auditable, and vendor-independent&lt;/strong&gt;. Most BSPs are public, and support is available from hundreds of organizations.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;QNX binaries are opaque&lt;/strong&gt;. No source, limited debugging capability, and long lead times for fixes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Certification is contextual&lt;/strong&gt;. An OS with a certificate means nothing if your integrated system isn't re-validated.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;And finally—QNX has changed hands multiple times. There is no assurance it won’t end up owned by a competitor.&lt;/p&gt;
&lt;h2&gt;&lt;strong&gt;7. Common Misunderstandings&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Let’s clear up some persistent myths:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Myth&lt;/th&gt;
&lt;th&gt;Reality&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Microkernel = safer&lt;/td&gt;
&lt;td&gt;Faults in user apps—not kernel design—cause most issues&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Closed = secure&lt;/td&gt;
&lt;td&gt;Open source fixes vulnerabilities faster and more transparently&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Linux isn’t real-time&lt;/td&gt;
&lt;td&gt;It is—with higher throughput and flexible isolation&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Vendor BSPs = better&lt;/td&gt;
&lt;td&gt;Often less maintained and lower quality than open-source equivalents&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Certification = ready&lt;/td&gt;
&lt;td&gt;Pre-certified components still require full system validation&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;&lt;strong&gt;Conclusion: Choose Based on Outcomes, Not Labels&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Both Linux and QNX have their place—but they are built on different philosophies and deliver different outcomes. Linux offers unmatched scalability, flexibility, and control—critical in environments where performance and long-term autonomy matter.&lt;/p&gt;
&lt;p&gt;QNX may suit niche applications where extreme determinism is required and system complexity is tightly bound. But for modern, software-defined, connected systems—&lt;strong&gt;Linux is the platform of record.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;If you're building for the future, &lt;strong&gt;trustability, flexibility, and computational throughput will define your competitive edge. And that’s where Linux leads.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Want to learn more about this topic?&lt;/h2&gt;
&lt;p&gt;Reach out to the Codethink team, using the form below:&lt;/p&gt;
&lt;style type="text/css" scoped&gt;
    .mauticform_wrapper { max-width: 600px; margin: 10px auto; }
    .mauticform-innerform {}
    .mauticform-post-success {}
    .mauticform-name { font-weight: bold; font-size: 1.5em; margin-bottom: 3px; }
    .mauticform-description { margin-top: 2px; margin-bottom: 10px; }
    .mauticform-error { margin-bottom: 10px; color: red; }
    .mauticform-message { margin-bottom: 10px;color: green; }
    .mauticform-row { display: block; margin-bottom: 20px; }
    .mauticform-label { font-size: 1.1em; display: block; font-weight: bold; margin-bottom: 5px; }
    .mauticform-row.mauticform-required .mauticform-label:after { color: #e32; content: " *"; display: inline; }
    .mauticform-helpmessage { display: block; font-size: 0.9em; margin-bottom: 3px; }
    .mauticform-errormsg { display: block; color: red; margin-top: 2px; }
    .mauticform-selectbox, .mauticform-input, .mauticform-textarea { width: 100%; padding: 0.5em 0.5em; border: 1px solid #CCC; background: #fff; box-shadow: 0px 0px 0px #fff inset; border-radius: 4px; box-sizing: border-box; }
    .mauticform-checkboxgrp-row {}
    .mauticform-checkboxgrp-label { font-weight: normal; }
    .mauticform-checkboxgrp-checkbox {}
    .mauticform-radiogrp-row {}
    .mauticform-radiogrp-label { font-weight: normal; }
    .mauticform-radiogrp-radio {}
    .mauticform-button-wrapper .mauticform-button.btn-default, .mauticform-pagebreak-wrapper .mauticform-pagebreak.btn-default { color: #5d6c7c;background-color: #ffffff;border-color: #dddddd;}
    .mauticform-button-wrapper .mauticform-button, .mauticform-pagebreak-wrapper .mauticform-pagebreak { display: inline-block;margin-bottom: 0;font-weight: 600;text-align: center;vertical-align: middle;cursor: pointer;background-image: none;border: 1px solid transparent;white-space: nowrap;padding: 6px 12px;font-size: 13px;line-height: 1.3856;border-radius: 3px;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;}
    .mauticform-button-wrapper .mauticform-button.btn-default[disabled], .mauticform-pagebreak-wrapper .mauticform-pagebreak.btn-default[disabled] { background-color: #ffffff; border-color: #dddddd; opacity: 0.75; cursor: not-allowed; }
    .mauticform-pagebreak-wrapper .mauticform-button-wrapper {  display: inline; }
&lt;/style&gt;
&lt;div id="mauticform_wrapper_qnxvslinuxenquiry" class="mauticform_wrapper"&gt;
    &lt;form autocomplete="false" role="form" method="post" action="https://marke2ing.codethink.co.uk/form/submit?formId=5" id="mauticform_qnxvslinuxenquiry" data-mautic-form="qnxvslinuxenquiry" enctype="multipart/form-data"&gt;
        &lt;div class="mauticform-error" id="mauticform_qnxvslinuxenquiry_error"&gt;&lt;/div&gt;
        &lt;div class="mauticform-message" id="mauticform_qnxvslinuxenquiry_message"&gt;&lt;/div&gt;
        &lt;div class="mauticform-innerform"&gt;


          &lt;div class="mauticform-page-wrapper mauticform-page-1" data-mautic-form-page="1"&gt;

            &lt;div id="mauticform_qnxvslinuxenquiry_email" data-validate="email" data-validation-type="email" class="mauticform-row mauticform-email mauticform-field-1 mauticform-required"&gt;
                &lt;label id="mauticform_label_qnxvslinuxenquiry_email" for="mauticform_input_qnxvslinuxenquiry_email" class="mauticform-label"&gt;Email&lt;/label&gt;
                &lt;span class="mauticform-helpmessage"&gt;Please input your company email address.&lt;/span&gt;
                &lt;input id="mauticform_input_qnxvslinuxenquiry_email" name="mauticform[email]" value="" class="mauticform-input" type="email"&gt;
                &lt;span class="mauticform-errormsg" style="display: none;"&gt;This is required.&lt;/span&gt;
            &lt;/div&gt;

            &lt;div id="mauticform_qnxvslinuxenquiry_accept_our_privacy_policy" data-validate="accept_our_privacy_policy" data-validation-type="select" class="mauticform-row mauticform-select mauticform-field-2 mauticform-required"&gt;
                &lt;label id="mauticform_label_qnxvslinuxenquiry_accept_our_privacy_policy" for="mauticform_input_qnxvslinuxenquiry_accept_our_privacy_policy" class="mauticform-label"&gt;Accept our Privacy Policy&lt;/label&gt;
                &lt;select id="mauticform_input_qnxvslinuxenquiry_accept_our_privacy_policy" name="mauticform[accept_our_privacy_policy]" value="" class="mauticform-selectbox"&gt;
                    &lt;option value=""&gt;&lt;/option&gt;                    &lt;option value="Accepts Privacy Policy"&gt;Yes&lt;/option&gt;
                &lt;/select&gt;
                &lt;span class="mauticform-errormsg" style="display: none;"&gt;This field is required.&lt;/span&gt;
            &lt;/div&gt;

            &lt;div id="mauticform_qnxvslinuxenquiry_privacy_policy__consent" class="mauticform-row mauticform-freetext mauticform-field-3"&gt;    
                &lt;h3 id="mauticform_label_qnxvslinuxenquiry_privacy_policy__consent" for="mauticform_input_qnxvslinuxenquiry_privacy_policy__consent" class="mauticform-label"&gt;
                    Privacy Policy &amp;amp; Consent
                &lt;/h3&gt;
                &lt;div id="mauticform_input_qnxvslinuxenquiry_privacy_policy__consent" name="mauticform[privacy_policy__consent]" value="" class="mauticform-freetext"&gt;
                    &lt;p&gt;Learn more about our &lt;a href="https://www.codethink.co.uk/privacy.html"&gt;Privacy Policy here&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;By clicking submit, I agree to receive email updates from Codethink Ltd about our latest products and services.&lt;/p&gt;
                &lt;/div&gt;
            &lt;/div&gt;

            &lt;div id="mauticform_qnxvslinuxenquiry_submit" class="mauticform-row mauticform-button-wrapper mauticform-field-4"&gt;
                &lt;button type="submit" name="mauticform[submit]" id="mauticform_input_qnxvslinuxenquiry_submit" value="" class="mauticform-button btn btn-default"&gt;Submit&lt;/button&gt;
            &lt;/div&gt;
            &lt;/div&gt;
        &lt;/div&gt;

        &lt;input type="hidden" name="mauticform[formId]" id="mauticform_qnxvslinuxenquiry_id" value="5"&gt;
        &lt;input type="hidden" name="mauticform[return]" id="mauticform_qnxvslinuxenquiry_return" value=""&gt;
        &lt;input type="hidden" name="mauticform[formName]" id="mauticform_qnxvslinuxenquiry_name" value="qnxvslinuxenquiry"&gt;

        &lt;/form&gt;
&lt;/div&gt;

&lt;div class="footnote"&gt;
&lt;hr&gt;
&lt;ol&gt;
&lt;li id="fn:1"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/Linux"&gt;https://en.wikipedia.org/wiki/Linux&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:1" title="Jump back to footnote 1 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:2"&gt;
&lt;p&gt;&lt;a href="https://kernel.org/"&gt;https://kernel.org/&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:2" title="Jump back to footnote 2 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:3"&gt;
&lt;p&gt;&lt;a href="https://en.wikipedia.org/wiki/QNX"&gt;https://en.wikipedia.org/wiki/QNX&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:3" title="Jump back to footnote 3 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id="fn:4"&gt;
&lt;p&gt;&lt;a href="https://blackberry.qnx.com/en"&gt;https://blackberry.qnx.com/en&lt;/a&gt;&amp;#160;&lt;a class="footnote-backref" href="#fnref:4" title="Jump back to footnote 4 in the text"&gt;&amp;#8617;&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/div&gt;</content><category term="2025"></category><category term="Linux"></category><category term="QNX"></category><category term="open source"></category><category term="safety"></category></entry><entry><title>Is Rust ready for safety related applications?</title><link href="https://www.codethink.co.uk/articles/rust-ready/" rel="alternate"></link><published>2025-05-30T00:00:00+01:00</published><updated>2025-05-30T00:00:00+01:00</updated><author><name>Daniel Silverstone</name></author><id>tag:www.codethink.co.uk,2025-05-30:/articles/rust-ready/</id><summary type="html">&lt;p&gt;Three years ago, we co-presented about Rust's readiness for use in Automotive Safety contexts, let's look back at that and then consider the future.&lt;/p&gt;</summary><content type="html">&lt;p&gt;In late 2022, Codethink and &lt;a href="https://www.exidascom/"&gt;exida&lt;/a&gt; showed that &lt;a href="https://www.exida-eu.com/images/home/exida_Automotive_Symposium_2022_V3.pdf"&gt;almost 90% of C (and C++) safety-related bugs were mitigated in some fashion by switching to Rust&lt;/a&gt;.  We argued that the safety community must justify its continued use of C or C++ if not switch to Rust.  Three years on, let's revisit what was said, and look at what has changed.&lt;/p&gt;
&lt;h2&gt;Is Rust ready for safety related applications?&lt;/h2&gt;
&lt;p&gt;You can &lt;a href="https://marke2ing.codethink.co.uk/asset/a91c0b7c-3692-4402-a4e7-7c592a329737"&gt;download the slides&lt;/a&gt; and then the below will make a little more sense.  The below is a reconstruction of content, which was delivered in person, from my slide notes, since unfortunately the video of the presentation no longer appears to be available.  The reconstruction is written looking back at the deck, so if you happen to have been present at the symposium and remember the talk, just bear that in mind.&lt;/p&gt;
&lt;h3&gt;Abstract&lt;/h3&gt;
&lt;p&gt;As of October 2022, it was getting increasingly difficult to ignore the impact and capabilities of Rust.  Rust brings a number of advantages, as a language, to the world of safety related application software.  At the time, a commercially supported compiler was already available with support for several devices used in automotive projects.&lt;/p&gt;
&lt;p&gt;Our goal on the day was to talk a little about Rust, and to hopefully convince the delegates to consider using it in their safety applications.  We proposed to discuss a little about options around linking to existing C libraries, and porting code from C to Rust; as well as exploring the stated advantages of Rust as compared with C, and to not shy away from some of the new difficulties that might have arisen for programmers starting new projects in Rust or converting existing codebases.&lt;/p&gt;
&lt;p&gt;Finally we explained that we hoped to give the delegates a basis on which they could start to construct an argument around using Rust, either alone or in conjunction with C, in their safety applications.&lt;/p&gt;
&lt;h3&gt;What do we do with C today&lt;/h3&gt;
&lt;p&gt;In order to make C suitable for use in safety applications we had to surround ourselves with large amounts of rules, cross-checks, guidelines, and tooling.  There's the MISRA rules, AUTOSAR, if you get to C++, even more MISRA, AUTOSAR, and even JSF rules. Then there were tool qualification suites, including &lt;a href="https://www.exidascom/"&gt;exida&lt;/a&gt;'s ctools suite; coding guidelines, static analysis, etc.  All of these tools were wrappered up with architecture guidelines, application specific tooling etc.&lt;/p&gt;
&lt;p&gt;All of these things were, or are, valuable, and all of them address, in some part, things which the language is unable to help you with.&lt;/p&gt;
&lt;p&gt;But this is, I'm sure you'll agree, a lot to deal with as we consider writing our arguments for using C in a safety context.&lt;/p&gt;
&lt;h3&gt;Background to Rust&lt;/h3&gt;
&lt;p&gt;Rust 1.0 was in late 2015, and it has been considered 'stable' in a backward-compatibility sense since then.  The language's capabilities grow year on year, through a process called RFCs and MCPs, and though the rate of change is relatively high, the Rust project's commitment to compatibility tempers that somewhat.&lt;/p&gt;
&lt;p&gt;Target support improves rapidly, once the compiler can build for something, library ecosystems catch up very quickly.  Often wrappering C libraries until native Rust ones are written, and at the time it was evident that the Linux kernel was moving toward allowing Rust drivers to exist in-kernel.&lt;/p&gt;
&lt;p&gt;The ecosystem is well documented with a number of 'books' around the core ecosystem, and every published library having documentation available in one place (docs.rs). The "official" language specification was the Rust compiler codebase itself; however there were efforts to write properly standardisable specifications, and indeed recently &lt;a href="https://rustfoundation.org/media/ferrous-systems-donates-ferrocene-language-specification-to-rust-project/"&gt;Ferrous systems donated their specification to the Rust foundation&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Contribution to the ecosystem, and getting support, was (and remains) similar to typical open source projects - there are community forums, Github or other code forges, and some consultancies offering Rust specific help. Governance was mostly handled by small teams within the community, though there is a Rust foundation tasked with stewarding the ecosystem and the language.&lt;/p&gt;
&lt;h3&gt;Core Rust&lt;/h3&gt;
&lt;p&gt;At the time, change to Rust was typically managed via the RFC or MCP processes where discussion is had to design things. Changes were then made in a way that is feature-flagged so that normal users are not affected by them. After changes were considered good enough, there would be a stabilisation proposal and if that goes through, the change becomes live for all users.  This process remains largely unchanged to today, though there are more nuances than we wanted to go into in the presentation slot.  There has been a lot of effort put into helping with this process, including documents such as &lt;a href="https://lang-team.rust-lang.org/how_to/propose.html"&gt;Proposing a change to the language&lt;/a&gt; written up by the Rust language design team.&lt;/p&gt;
&lt;p&gt;Rust libraries (and some programs), which are called crates, are published to a central package repository called &lt;a href="https://crates.io/"&gt;&lt;code&gt;crates.io&lt;/code&gt;&lt;/a&gt; used by both people and tooling to acquire them. All crates uploaded to &lt;code&gt;crates.io&lt;/code&gt; are automatically documented on &lt;a href="https://docs.rs/"&gt;&lt;code&gt;docs.rs&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The Rust packaging and build tool &lt;code&gt;cargo&lt;/code&gt; uses &lt;code&gt;crates.io&lt;/code&gt; by default to acquire dependencies for projects (though this can be overridden for software supply-chain management reasons). Cargo then builds your dependencies and your whole project managing the process for you. Testing is also generally handled, at least initially, via Cargo and Rust's built in testing.&lt;/p&gt;
&lt;p&gt;But as an additional benefit to &lt;code&gt;crates.io&lt;/code&gt; - there is a Rust service called '&lt;a href="https://github.com/rust-lang/crater"&gt;Crater&lt;/a&gt;' which can run the entire crates ecosystem through a proposed compiler change allowing changes to be verified against a near-complete corpus of open-source Rust code.  This leads to even more confidence in the stability of the tooling and ecosystem.&lt;/p&gt;
&lt;h3&gt;What makes Rust special&lt;/h3&gt;
&lt;p&gt;The primary thing which draws people to Rust is the fact that the language forces developers to consider the lifetime of data and data access.  This, and a few other aspects of Rust's design allows it to make claims about safety in the sense of certain aspects of memory /safety such as data races, use-after-free, etc. which can plague other languages. Rust calls this the 'borrow checker' and it's effectively part of the early stages of the compiler.  Lifetimes are a part of the Rust type system, so Rust's expressive type system is there to help you to write correct code.&lt;/p&gt;
&lt;p&gt;Because the authors of Rust's standard library are aware of all the places that there could be problems, due in no small part to the Rust language helping them, there are many ways in which the standard library helps developers to do the right thing.  One aspect of that is that all functions which could do something dangerous are more likely to panic rather than do the dangerous thing (such as indexing out of the bounds of an array).  Panics are a controlled mechanism for dealing with such behaviour at runtime.  Better, of course, is the APIs in Rust which make explicit the fallibility of such operations, requiring that the author of the code deal with such problems.&lt;/p&gt;
&lt;p&gt;Outside of the language and the standard library, there are a number of pieces of Rust's tooling which really help - for example Rust compiler error messages try and act like a pair programmer, doing their best to assist the developer in fixing their problems - as mentioned above, there are runtime protections such as controlled panics which developers can take advantage of, or control the existence of, at their pleasure; and there is an ecosystem-provided extra-linting tool called &lt;code&gt;clippy&lt;/code&gt; which helps with spotting bad idioms and offering fixes to the developer.&lt;/p&gt;
&lt;h3&gt;What do beginners struggle with&lt;/h3&gt;
&lt;p&gt;Newcomers to Rust often face a number of struggles depending on their past experience (or lack thereof) in Software.  Rust is essentially a procedural language, with some fundamental rules around data lifetime and access lifetimes, with a behavioural modelling abstraction which it calls Traits - this forces a change in thinking around software design since most traditional design processes are object-oriented.&lt;/p&gt;
&lt;p&gt;Sometimes Rust is forced on newcomers because of existing projects, but in this space, typically people come to Rust because of the promises it makes about helping programmers to not create certain classes of bugs in their code.  Rust is low-level enough that we should consider using it in almost any safety application where we'd be using C "by default" and high-level enough that it offers many benefits once the learning curve is surmounted.&lt;/p&gt;
&lt;p&gt;Because Rust is a low-level systems language at its heart, its interoperability with C code is very good; however there are also tools out there which help with converting C code into Rust which can be used as a starting point to understand how to write software in Rust.  But do not let this lead you to false confidence, even very low-level Rust code feels distinct from C code and will require your engineers to rethink their approaches.&lt;/p&gt;
&lt;p&gt;Rust is one of the recent neo-languages (at the time of the presentation it was only 7 years old and has only recently celebrated its &lt;a href="https://rustweek.org/celebration/"&gt;10th birthday&lt;/a&gt;) and as such it has a significant amount of modern tooling around it.  This can cause a lot of confusion in engineers used to a more traditional C, C++, CMake/GNU Make environment. The neo-languages were, at the time, not at the same maturity of ecosystem support as the old guard, and as such newcomers also struggle with understanding which library is best to use; or what devices are supported well, which can complicate any decision to use Rust in a project.&lt;/p&gt;
&lt;h3&gt;What else is hard in Rust&lt;/h3&gt;
&lt;p&gt;While the compiler refusing to build bad code is more helpful than simply dealing with a runtime canary, the shift in cognitive load is not in a direction developers are used to, which can cause difficulty as new engineers come to Rust.  Rust trades a lot of the pain of debugging something when a bug becomes apparent, for the work required to express to the compiler that your design is sound.  This is very much something which many engineers are not used to. The corollary to this point is that not all syntactically valid Rust is semantically correct and no matter how helpful a compiler is, you cannot be certain you have written the right thing.  There is a middle part to the Rust learning curve where developers will tend to assume that because the compiler said something was OK, that it was in fact correct.  Overcoming this overconfidence in the tooling and returning to a disciplined approach to requirements-driven testing can be hard for newcomers who are experiencing the power of the Rust language for the first time.&lt;/p&gt;
&lt;p&gt;Perfectly valid Rust code may yet not be considered idiomatic - there is a linting tool called &lt;code&gt;Clippy&lt;/code&gt; which can catch a number of errors in your code, but which also guides developers toward a style of coding which aims at reducing the cognitive load of comprehending code written by others.  Learning to obey these tools can be difficult when, again, newcomers are faced with the power of Rust and old-guard programmers who are more set in their ways with design and even layout of code.&lt;/p&gt;
&lt;p&gt;Understanding the runtime canaries can be a sticking point as people try to integrate Rust into their products - Rust's "panic" approach is a controlled unwinding of the program state which can be hooked into.  It is possible to force your program to fail to build if panics are happening in certain areas of your code, and learning to code in a way which cannot induce panics is important, but it is a step along the way to Rust mastery which is quite far along the learning curve and can take quite a while to get used to. Ultimately though, no amount of compiler, linter, and ecosystem tooling, will help you to avoid systematic semantic errors in your code.  Even with Rust around, your developers will still need the support of your requirements team and a comprehensive testing approach in order to make good progress.&lt;/p&gt;
&lt;h3&gt;Coexistence with other code (C)&lt;/h3&gt;
&lt;p&gt;At the time the talk was given, a significant effort had already been put into permitting Linux device drivers to be written in Rust and used in the Linux kernel - this was a huge step forward for Automotive developers who need to create device drivers for custom Automotive hardware and who want to take advantage of Rust's language benefits.  This coexistence is made possible primarily because Rust is designed to be a systems programming language and as such has interoperability with C built in.  The Kernel has significant numbers of very well designed interfaces which are explicit about lifetimes of objects etc. permitting reasonably simple, or at least comprehensible, bindings into the Rust world. - This interoperability is achieved by means of Rust's safe/unsafe dichotomy, and its FFI.&lt;/p&gt;
&lt;p&gt;The Rust project talks about "safety" but it's not safety in the same sense as the delegates at the symposium would have thought of it.  Rust makes a few very constrained claims about what it considers 'safe'.  But because computers, and the real world, are not built around Rust's safety model, it still permits developers to express to the compiler that they have manually verified that the Rust safety guarantees are still met by the codebase.  These two worlds, safe and unsafe, live together by means of very constrained boundaries at which the assumptions Rust makes can be asserted back to the compiler.&lt;/p&gt;
&lt;p&gt;This is done by means of a keyword &lt;code&gt;unsafe&lt;/code&gt; which does double-duty, marking both things which the compiler cannot be certain about on its own and will need human assistance to confirm rules are not broken, and also by marking the places where a human has done the work to constrain the "unsafety" and reestablish the constraints to permit the compiler to continue to make the assumptions it needs in order to support the developer properly.&lt;/p&gt;
&lt;p&gt;With all of the above, developers can then use Rust's ability to reference symbols in C code (or export symbols with C linkage) by means of Rust's FFI.  FFI is inherently unsafe, and so your developers will need to understand and appreciate all of the safety rules before engaging in this kind of development.  The Rust ecosystem provides documentation around safe/unsafe and the FFI to help you to develop this expertise.&lt;/p&gt;
&lt;p&gt;As you'd expect, the Rust ecosystem provides tooling to assist with managing all this, there's &lt;a href="https://crates.io/crates/cargo-geiger"&gt;&lt;code&gt;cargo geiger&lt;/code&gt;&lt;/a&gt; which is a tool to determine how much of, and what kind of, unsafe code exists in your software and its dependency tree.  And there's &lt;a href="https://github.com/rust-lang/miri"&gt;&lt;code&gt;cargo miri&lt;/code&gt;&lt;/a&gt; which can actually interpret the intermediate form of Rust within the compiler and can be used to check for undefined behaviour at runtime (typically via your test suite), allowing you to assert the correctness of the second class of &lt;code&gt;unsafe&lt;/code&gt; keyword usage mentioned above.&lt;/p&gt;
&lt;h3&gt;Testing in Rust&lt;/h3&gt;
&lt;p&gt;Rust's main build tooling (Cargo) comes with mechanisms for testing built into it.  Unit tests are written as annotated functions in your codebase directly which tightly couples unit tests and the code they cover.  Integration tests are written in separate source files alongside your codebase, in a &lt;code&gt;tests&lt;/code&gt; directory; and any examples in your code documentation are automatically extracted, compiled, and run as tests, ensuring that documentation examples remain functional as you develop your software.  The &lt;a href="https://doc.rust-lang.org/cargo/"&gt;Cargo manual&lt;/a&gt; covers most of how these work; and the rustdoc manual covers code documentation testing.&lt;/p&gt;
&lt;p&gt;In addition to the built-in testing framework, there is a rich ecosystem of additional testing libraries such as frameworks for mocking out parts of your design, benchmarking time-critical code sections, or fuzzing your software to help to ensure there are no crashes associated with unexpected or corrupt inputs. Each of these has good documentation to help you to use them in your project.&lt;/p&gt;
&lt;p&gt;There is also a lot of other tooling out there to help.  I mentioned &lt;a href="https://github.com/rust-lang/miri"&gt;&lt;code&gt;miri&lt;/code&gt;&lt;/a&gt; briefly before, and it is there to help you run your tests in such a way that any undefined behaviour can be found and addressed; and there's &lt;a href="https://crates.io/crates/cargo-tarpaulin"&gt;Tarpaulin&lt;/a&gt;, a Cargo addon which assists with producing code-coverage reports to show if your tests are covering as much code as they need to in order to be confident of the codebase.  Once again, documentation is a big thing in the Rust ecosystem and so there's plenty there too.&lt;/p&gt;
&lt;p&gt;Finally you need to be able to test the tooling itself.  As you'd expect, there is an extensive test suite for the compiler and core tooling built into their repositories and you could run all those tests yourself if you build your own Rust toolchain.  Despite that, initiatives such as &lt;a href="https://ferrocene.dev/en/"&gt;Ferrocene&lt;/a&gt; offer even more confidence and qualification should you require it.  Whatever means you use to acquire your toolchain we recommend that you establish a baseline for confidence in the tooling yourselves.&lt;/p&gt;
&lt;h3&gt;C2Rust conversion&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://tools.galois.com/c2rust"&gt;&lt;code&gt;C2Rust&lt;/code&gt;&lt;/a&gt; is a project supported by DARPA. The &lt;code&gt;C2Rust&lt;/code&gt; tool helps by transpiling C99 compliant code into Rust.  It does not produce idiomatic Rust, nor necessarily nice Rust, but it does the syntactic conversion so that you can focus on the semantics of the software.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/immunant/c2rust"&gt;Github repository&lt;/a&gt; contains instructions for downloading, building, and installing it on Ubuntu Linux, but there is a demo on &lt;a href="https://c2rust.com/"&gt;https://c2rust.com/&lt;/a&gt; which you can use to try things out on smaller pieces of code.&lt;/p&gt;
&lt;p&gt;In terms of trust, you have the same problem you would have with any other transpiler or code generator - you need to develop trust in the tool for yourself.  C2Rust explicitly states that they expect a human being to be involved in dealing with its output - it's the first step along a path to switching to Rust, not a silver-bullet.&lt;/p&gt;
&lt;p&gt;What the tool does is take some C code, and effectively consumes unsafe C code, and produces the equivalent unsafe Rust code.  This code should then be refactored and reengineered by your Rust engineers until the unsafety is isolated into places where it is necessary and well audited, and the rest of the code is in the Safe Rust language.&lt;/p&gt;
&lt;h3&gt;Tool support&lt;/h3&gt;
&lt;p&gt;The Rust community (often referred to as rust-lang) provides, free of encumbrance, all the tooling required to get going with Rust.  The licences in use in the community are usually Apache 2.0 or MIT/BSD terms which permit use in proprietary situations.  In contrast, organisations such as &lt;a href="https://ferrocene.dev/en/"&gt;Ferrocene&lt;/a&gt; offer Long Term Support, qualified compilers, and other ISO 26262 and ASIL related assistance and tooling.  Though there are costs associated with those.&lt;/p&gt;
&lt;p&gt;Ultimately there is a question of how can you develop some trust in the tooling, and the community and ecosystem are working toward providing those assurances, though those of you familiar with Ken Thompson's seminal essay &lt;a href="https://www.cs.cmu.edu/~rdriley/487/papers/Thompson_1984_ReflectionsonTrustingTrust.pdf"&gt;"Reflections on trusting trust"&lt;/a&gt; will be aware that this is a long and painstaking process. As with any switch in your approach to software in your safety applications; choosing Rust brings with it some amount of risk.  Mitigations for those risks will vary depending on your particular applications, however the basic design and goals of the Rust language and core ecosystem are already mitigations for any number of C related risks you already carry.&lt;/p&gt;
&lt;h3&gt;Comparing ctools checks to stock Rust&lt;/h3&gt;
&lt;p&gt;In preparation for this talk, we went through the &lt;a href="https://github.com/exida/ctools"&gt;exida ctools&lt;/a&gt; repository, and considered around a hundred of the C test cases.
ctools was presented by Michał Szczepankiewicz / Krystian Radlak / Piotr Serwa / Lukas Bulwahn at the exida 2020 Symposium Session 14: Tool-based code verification for C and C++ in dependable projects – systematic approach. In red you can see a snapshot of the uncaught language insecurities using 17 different C language static analysis tools.&lt;/p&gt;
&lt;p&gt;Making the assumption that we do not want to use &lt;code&gt;unsafe&lt;/code&gt;, unless it's literally impossible to demonstrate how Rust helps without it, we set about seeing how a default Rust toolchain does with these cases.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Rust, by construction, prevents many of the things checked for in the ctools suite such as use-after-free or leaked pointers. A full two thirds of the test cases are simply not compilable.&lt;/li&gt;
&lt;li&gt;At runtime, Rust's controlled panics would catch a further 25% of the tests.  With the use of the &lt;code&gt;no-panic&lt;/code&gt; crate, the majority of these would be convertible to compile-time failures. Which could bring the "not possible by construction" percentage up as high as 90%&lt;/li&gt;
&lt;li&gt;Three of the tests in the suite explicitly work with copying memory around, to do this we would need unsafe, but failure to use the correct copying semantics would likely be flagged by using MIRI to do testing around undefined behaviour.&lt;/li&gt;
&lt;li&gt;There are a small number of things which Rust simply won't detect by default because they're defined clearly in things like the IEEE specification for floating point numbers. Further tooling work will likely be necessary to improve on this.&lt;/li&gt;
&lt;li&gt;In most of the compile-fail cases, Rust gives a useful error message explaining why a certain pattern is not acceptable in the language.  Rust's error messages tend to lead the way in the compiler world these days.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Given the spread of outcomes, we would not expect this distribution to have changed over the past couple of years.&lt;/p&gt;
&lt;p&gt;&lt;img alt="ctools image" src="https://www.codethink.co.uk/articles/rust-ready/rust-ready-slide-15.webp"&gt;&lt;/p&gt;
&lt;h3&gt;ISO 26262 part six and Rust&lt;/h3&gt;
&lt;p&gt;This slide assumes reasonably intimate knowledge of ISO 26262.  Since that is a proprietary document I cannot include it here for reference, so I'll summarise briefly.&lt;/p&gt;
&lt;p&gt;If we take tables one, three, six, and seven from ISO 26262 part six as examples, we can look at each item in those tables and characterise them in terms of the Rust ecosystem and language. Many of the items in these tables are realistically things that your design process, review process and other human-related activities cover.  These are not coloured.&lt;/p&gt;
&lt;p&gt;A significant number of the items are either covered by built-in features of the Rust language or core tooling, or are at least human activities which can be supported by the tooling. As we would expect, there were still some aspects of ISO 26262 which needed tooling to be developed, however these are few and some tooling is already underway.&lt;/p&gt;
&lt;p&gt;Briefly, table one considers modelling and coding guidelines, and everywhere Rust touches directly is covered nicely.  Table three considers software design principles and while most of it is for humans, Rust at worst merely gets out of the way of the principles in question, and at best actively assists in following the principles.  Table six is about software unit design and implementation.  Rust gets much more involved here and other than one unit design principle (the one about each subprogram/function only having a single entry and single exit) Rust assists wholeheartedly.  (Interestingly, because Rust encourages the use of the &lt;code&gt;?&lt;/code&gt; operator to neatly propagate runtime errors through the type system, it actively works against 1a.)  Table 7 talks about unit testing.  Tooling around data flow and control flow analysis has been developed since the talk, one example of which is &lt;a href="https://github.com/willcrichton/flowistry"&gt;Flowistry&lt;/a&gt; which is available as a VSCode plugin.&lt;/p&gt;
&lt;p&gt;&lt;img alt="ISO 26262 part six and Rust" src="https://www.codethink.co.uk/articles/rust-ready/rust-ready-slide-16.webp"&gt;&lt;/p&gt;
&lt;h3&gt;Is there an argument to use Rust&lt;/h3&gt;
&lt;p&gt;Up until this point in the presentation we were posing the question as "&lt;strong&gt;What argument can we make for using Rust in safety applications?&lt;/strong&gt;", particularly given the experience and long-history of using C, and the effort invested into standards such as MISRA.&lt;/p&gt;
&lt;p&gt;However, we'd much rather turn the question on its head, and that that point we did.&lt;/p&gt;
&lt;h3&gt;Is there an argument for not using Rust&lt;/h3&gt;
&lt;p&gt;Let's instead ask "&lt;strong&gt;What is your argument for not using Rust?&lt;/strong&gt;"&lt;/p&gt;
&lt;p&gt;There are some fairly simple reasons, such as AutoSAR MCAL availability, target availability or support tier; a particular C library you need for which wrappering would be inappropriate; or simply what batteries you get or do not get in a Rust distribution.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;But&lt;/em&gt; - think also about what you actually need, in a lot of cases there is a good argument for originating the support you require, and contributing it to the now growing community of Rust users in the safety world.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;And&lt;/em&gt; consider this - is the cost of wrapping a C library, or even rewriting it into Rust, greater than the cost of the risks associated with still using C.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Finally&lt;/em&gt; ask yourselves:- "&lt;strong&gt;What is my argument for using C? How can I justify the risks and dangers of continuing to work in C when there are alternatives like Rust to consider?&lt;/strong&gt;"&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The idea that the delegates ought to be justifying their use of C in their safety argumentation was one which very few at the conference had even considered before this point.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;References&lt;/h3&gt;
&lt;p&gt;Several of the links are simply integrated above, however the rest from the slide deck are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.oreilly.com/library/view/programming-rust-2nd/9781492052586/"&gt;Programming Rust, Fast Safe Systems Development Blandy, Orendorff &amp;amp; Tindall O'Reilly 2nd Ed June 2021&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://ferrous-systems.com/"&gt;Ferrous Systems&lt;/a&gt; and &lt;a href="https://ferrocene.dev/en/"&gt;Ferrocene&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rust-lang.org/"&gt;The Rust programming language&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.stroustrup.com/JSF-AV-rules.pdf"&gt;JSF / MISRA C rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jubnzv/MISRA-Example-Suite"&gt;MISRA Exemplar&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.autosar.org/fileadmin/user_upload/standards/adaptive/17-03/AUTOSAR_RS_CPP14Guidelines.pdf"&gt;AUTOSAR rules&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.misra.org.uk/"&gt;MISRA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.exida.com/Blog/software-metrics-iso-26262-iec-61508"&gt;Software metrics&lt;/a&gt; post from &lt;a href="https://www.exidascom/"&gt;exida&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In addition, at the time there had been a &lt;a href="https://static.sched.com/hosted_files/elisasummit2022/2a/AUTOSARAdaptiveApplicationsInRust.pdf"&gt;Rust in adaptive AutoSAR&lt;/a&gt; presentation at the Elisa summit.&lt;/p&gt;
&lt;h2&gt;How has the landscape changed in the past years?&lt;/h2&gt;
&lt;p&gt;In the past few years, the fundamentals of Rust have not changed dramatically.  Ecosystem support for various aspects of the language has improved and there are many new tools (some of which were mentioned above) which can be used to address aspects of concern the safety community may have had before.  However the main shift has been in the understanding of just how poor of an idea it is to continue with using C (and to a lesser extent C++) without careful consideration of the risks.&lt;/p&gt;
&lt;p&gt;There have been advances in possible C replacements such as &lt;a href="https://ziglang.org/"&gt;Zig&lt;/a&gt; and possible updates/changes to C++ such as &lt;a href="https://www.circle-lang.org/site/index.html"&gt;Circle&lt;/a&gt; and &lt;a href="https://hsutter.github.io/cppfront/"&gt;CPP2&lt;/a&gt; though the latter two are very much bandaids/wrappers around the issues in C++.  The C++ committees around safety and the language have worked on things such as a &lt;a href="https://safecpp.org/"&gt;Safe C++&lt;/a&gt; paper and &lt;a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p3081r1.pdf"&gt;C++ safety profiles&lt;/a&gt; though these also feel a little /like an addon rather than the paradigm shift which Rust provides.&lt;/p&gt;
&lt;p&gt;The risks of C/C++ memory unsafety in particular continue to be talked about in increasing amounts by a more and more diverse set of people.  For example, Sternum wrote in 2024 about &lt;a href="https://sternumiot.com/iot-blog/memory-safety-5-common-memory-bugs-and-how-to-secure-your-system/"&gt;memory safety from the perspective of IoT&lt;/a&gt;, and Runsafe wrote about &lt;a href="https://runsafesecurity.com/blog/memory-safety-vulnerabilities/"&gt;memory safety vulnerabilities&lt;/a&gt;.  While both of those articles are particularly focussed on cybersecurity, the close relationship between safety and cybersecurity means that they are relevant to us in the safety context as well.  Furthermore, the continued evolution of sites such as the ISRG's &lt;a href="https://www.memorysafety.org/"&gt;Memory safety&lt;/a&gt; site point to the software community embracing the idea of shifting to languages such as Rust more and more as time passes.&lt;/p&gt;
&lt;p&gt;When we then look at the safety community itself, we can also see a shift toward Rust.  The &lt;a href="https://github.com/eclipse-kuksa"&gt;Eclipse Kuksa&lt;/a&gt; project has embraced Rust as part of Eclipse SDV.  The [ELISA][elisa] project recently &lt;a href="https://elisa.tech/event/elisa-workshop-lund-2025/"&gt;held a workshop&lt;/a&gt; where our own Paul Albertella presented on the topic of the &lt;a href="https://directory.elisa.tech/workshops/2025-05-Lund/3-2_Rust_in_Safety_Critical_Applications_and_Implications_for_ELISA.pdf"&gt;role of Rust in safety critical applications&lt;/a&gt; along with Daniel Krippner of ETAS.&lt;/p&gt;
&lt;p&gt;Finally we would be remiss not to consider the direct approaches being taken to utilise Rust in safety critical areas which are emerging.  The ongoing work on &lt;a href="https://ferrocene.dev/en/"&gt;Ferrocene&lt;/a&gt; along with the Rust Foundation's &lt;a href="https://rustfoundation.org/media/announcing-the-safety-critical-rust-consortium/"&gt;Safety Critical Rust Consortium&lt;/a&gt; are good examples; the latter of which publishes the &lt;a href="https://arewesafetycriticalyet.org/"&gt;https://arewesafetycriticalyet.org/&lt;/a&gt; website, the most useful page of which at the moment is probably the lists of members of the &lt;a href="https://arewesafetycriticalyet.org/coding_guidelines/members"&gt;coding guidelines&lt;/a&gt; and &lt;a href="https://arewesafetycriticalyet.org/tooling/members"&gt;tooling&lt;/a&gt; subcommittees.  In addition, last year a paper about &lt;a href="https://arxiv.org/pdf/2405.18135"&gt;Bringing Rust to Safety-Critical Systems in Space&lt;/a&gt; was published.&lt;/p&gt;
&lt;h2&gt;Would Codethink recommend using Rust in safety applications?&lt;/h2&gt;
&lt;p&gt;Given the mention above about our talking about Rust at an ELISA workshop, it might be reasonable to assume that Codethink believes that Rust is ready for safety applications.  In no small sense, we do believe that to be the case, but with reservations such as those we expressed a few years ago -- Rust won't be right for everyone or for every situation yet.  There remains a lot of work to be done to ensure that the language specification is complete, then further work on the standard library and other pieces of the ecosystem before every traditional safety scenario could be comfortable with Rust.&lt;/p&gt;
&lt;p&gt;Some of that work we have been involved with, and the rest we keep an eye on with enthusiasm.&lt;/p&gt;
&lt;p&gt;However, we are already actively using Rust in our own safety work.  In February, at FOSDEM, we open-sourced and spoke about our &lt;a href="https://codethinklabs.gitlab.io/trustable/trustable/"&gt;Trustable&lt;/a&gt; software framework (which is now an Eclipse project) as well as open-sourcing our &lt;a href="https://gitlab.com/codethinklabs/safety-monitor/safety-monitor/"&gt;Safety Monitor&lt;/a&gt; project which is written in Rust.  This was a precursor to &lt;a href="https://www.codethink.co.uk/news/trustable-software.html"&gt;our announcement&lt;/a&gt; of a baseline safety assessment of Codethink's Trustable Reproducible Linux OS.  We firmly believe that using Rust in the sort of situation where, rather than fully traditional ISO 26262-style safety, you are on board with the Trustable approach to safety, is completely viable and indeed desirable.&lt;/p&gt;
&lt;p&gt;There is a long way to go, but where in 2022 we asked the question "&lt;strong&gt;What is my argument for using C? How can I justify the risks and dangers of continuing to work in C when there are alternatives like Rust to consider?&lt;/strong&gt;" we now feel that the better question is "&lt;strong&gt;What is my argument for &lt;em&gt;not&lt;/em&gt; choosing Rust.&lt;/strong&gt;" and since, for us, the answer to that question is "&lt;em&gt;I have no good argument not to&lt;/em&gt;".&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;We choose Rust&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://marke2ing.codethink.co.uk/asset/a91c0b7c-3692-4402-a4e7-7c592a329737" target="_blank" class="center-button"&gt;Download slides&lt;/a&gt;&lt;/p&gt;</content><category term="2025"></category><category term="Rust"></category><category term="Events"></category><category term="Safety"></category></entry><entry><title>The open projects rethinking safety culture</title><link href="https://www.codethink.co.uk/articles/rethinking-safety-culture/" rel="alternate"></link><published>2025-05-16T00:00:00+01:00</published><updated>2025-05-16T00:00:00+01:00</updated><author><name>Paul Albertella</name></author><id>tag:www.codethink.co.uk,2025-05-16:/articles/rethinking-safety-culture/</id><summary type="html">&lt;p&gt;After Codethink's milestone on safe open source in safety-critical systems, we joined two workshops in Lund to explore the journey further.&lt;/p&gt;</summary><content type="html">&lt;p&gt;In 2016, Codethink started out on a journey to discover how open source software can be safely used to build safety-critical systems — that is, in products where people might be harmed if the software fails to do its job correctly. &lt;/p&gt;
&lt;p&gt;Free / libre open source software (FLOSS) projects like Linux have clearly demonstrated the value of collaboration in public when creating software that is — amongst many other things — trusted as the backbone of the web and millions of smart phones. FLOSS projects have also established the essential role of transparency and rapid software updates in dealing with cybersecurity threats. When it comes to safety, however, the difficulties of making a case for using FLOSS in a solution have long been a frustrating obstacle for product developers.&lt;/p&gt;
&lt;p&gt;Immediately following Codethink’s &lt;a href="https://www.codethink.co.uk/news/trustable-software.html"&gt;announcement&lt;/a&gt; about our latest milestone in this journey, I took part in two workshops focussing on safety and open source. This gave me the opportunity to talk about the &lt;a href="https://www.codethink.co.uk/trustable-software-framework.html"&gt;Trustable Software Framework (TSF)&lt;/a&gt; and how we are using it in our development of CTRL OS. I also learnt more from other open source projects about their approaches to creating software where trustability is as important.      &lt;/p&gt;
&lt;p&gt;The workshops were hosted by Volvo Cars in the Swedish city of Lund, and our hosts also provided several enthusiastic participants. The events were organised by two open source projects that have common goals and challenges, but approach these from different perspectives and with different focuses. The &lt;a href="https://sdv.eclipse.org/"&gt;Eclipse SDV&lt;/a&gt; project aims to build an automotive software stack to provide “an open technology platform for the software-defined vehicle of the future”. In contrast, the &lt;a href="https://elisa.tech/"&gt;ELISA&lt;/a&gt; project is concerned with the use of Linux-based operating systems for safety applications in a range of different domains. &lt;/p&gt;
&lt;p&gt;&lt;img alt="Image of Lund University Library" src="https://www.codethink.co.uk/articles/rethinking-safety-culture/lund-building.webp"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;→ Read: &lt;a href="https://www.codethink.co.uk/news/trustable-software.html"&gt;'Codethink Limited Announces World’s First Baseline Safety Assessment for a Linux-Based OS to SIL 3 / ASIL D'&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Day 1&lt;/h2&gt;
&lt;p&gt;Markus Bechter from BMW started the Eclipse SDV workshop by describing the approach to safety being developed for the Eclipse S-CORE or &lt;a href="https://eclipse-score.github.io"&gt;Safe Open Vehicle Core&lt;/a&gt; project. The intent is to establish a common set of development processes for components of this project, making the software amenable to safety certification using the ISO 26262 Automotive Safety Standard. &lt;/p&gt;
&lt;p&gt;The &lt;a href="https://www.codethink.co.uk/trustable-software-framework.html"&gt;Trustable Software Framework&lt;/a&gt; project was recently accepted into the &lt;a href="https://projects.eclipse.org/projects/technology.tsf"&gt;Eclipse Foundation&lt;/a&gt;, so I gave the next presentation. TSF approaches the challenge of using FLOSS in safety more broadly: how can we make a case for using software that has &lt;em&gt;not&lt;/em&gt; been developed following a process that conforms to an applicable safety standard? Since this describes the vast majority of existing FLOSS, including many of the tools and dependencies that S-CORE plans to use, an answer to this question is sorely needed, and TSF provides a methodology for making such a case.&lt;/p&gt;
&lt;p&gt;After lunch, it was time to welcome a new set of participants and start the ELISA workshop. This began with an introduction to the project for newcomers (see my &lt;a href="https://www.codethink.co.uk/articles/2024/elisa-safety-frontier/"&gt;retrospective&lt;/a&gt; from last year’s workshop if you are also new to the project), followed by an Ask Me Anything discussion. Then we had a fascinating talk from David Cuartielles, a founder of the &lt;a href="https://www.arduino.cc/"&gt;Arduino&lt;/a&gt; project who was recently honoured in the &lt;a href="https://blog.arduino.cc/2025/02/03/david-cuartielles-receives-the-open-source-award-on-skills-and-education/"&gt;European Open Source Awards&lt;/a&gt;. After telling us about the latest Arduino (the Portenta x8) and the features of the boards that are relevant for trust, he went on to talk about a topic that he is passionate about: the &lt;a href="https://desire4eu-eic.eu/"&gt;DESIRE4EU&lt;/a&gt; project, which is exploring how to make printed circuit boards that are recyclable, in support of the European sustainable electronics goal.&lt;/p&gt;
&lt;p&gt;The rest of the day focussed on the efforts of the ELISA Systems working group to describe and build systems involving Linux in combination with two other FLOSS components: the Zephyr RTOS and the Xen Hypervisor. This led naturally into a discussion of ELISA’s interactions with other adjacent open source communities.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image of a presentation" src="https://www.codethink.co.uk/articles/rethinking-safety-culture/presentation-image.webp"&gt;&lt;/p&gt;
&lt;h2&gt;Day 2&lt;/h2&gt;
&lt;p&gt;Philipp Ahmann and I started the second day with a discussion exploring some common misapprehensions about Linux and safety. We talked about some of the ‘routes’ to certification in the safety standards for pre-existing software, and why these are difficult to apply to open source software. We also explained why the notion of creating a ‘safe’ Linux is misleading, because safety can only really be understood in terms of a system, as opposed to an intrinsic property of a component. This led into discussions of various system models involving Linux, the use of complete redundant systems as part of a larger system design, and the role of hardware components in this, which was a perfect segue to the next session. &lt;/p&gt;
&lt;p&gt;Olivier Charrier talked about the role of hardware integration in safety, describing how the responsibilities for achieving specific safety objectives as part of a system design are typically assigned to hardware and software components, and then refined or re-defined in a series of iterations to address the identified gaps. Alessandro Carminati then shared the results of a Linux Features working group investigation to build and analyse a minimal Linux configuration and identify a core set of features that must be considered for any Linux-based system.&lt;/p&gt;
&lt;p&gt;After lunch we had a series of ‘special topic’ talks, beginning with interesting talks on &lt;a href="https://github.com/DISCOWER/PX4-Space-Systems"&gt;PX4SPace&lt;/a&gt;  — a flight control solution for drones that is being used to build robotic space vehicle solutions — and the SPDX Safety Profile, which extends the SPDX 3.0 ‘knowledge graph’ to include metadata relating to development processes for safety. &lt;/p&gt;
&lt;p&gt;Håkan Sivencrona from Volvo then talked about Safe Continuous Deployment, emphasising the importance of building development processes that deliver an ongoing stream of ‘safe’ software deliveries using DevOps principles, not just one ‘blessed’ release that is never expected to change. Igor Stoppa’s talk on “Resilient Safety Analysis and Qualification” sparked a lively discussion, as he argued that any safety analysis of Linux must be based on a detailed understanding of the code, and that this might be a reason not to rely on more complex features or extensions of the kernel.&lt;/p&gt;
&lt;p&gt;We then had a talk by Gustavo Padovan of the &lt;a href="https://kernelci.org/"&gt;Kernel CI&lt;/a&gt; project, which recently became an associate member of ELISA. He explained that a key goal of the project is to enable projects and organisations testing the kernel to share their results with the wider kernel community by providing a common framework for reporting results. Recent developments include &lt;a href="https://kci.dev/"&gt;kci.dev&lt;/a&gt;, a command line tool enabling developers and maintainers to interact with Kernel CI, and a YAML config file format to enable Linux subsystems to share tailored test case executions for maintainers and the wider community.&lt;/p&gt;
&lt;p&gt;The rest of the day focussed on requirements management and traceability, looking first at ELISA’s &lt;a href="https://github.com/elisa-tech/BASIL"&gt;BASIL&lt;/a&gt; tool, and then at an initiative with the Linux Tracing subsystem to develop a low-level requirements specification approach. The latter involved documenting detailed requirements for each function in the kernel, which would be intended to support complete reimplementation of the functionality without reference to the code. One participant noted that this approach might enable the kernel to be re-written in Rust!&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image of a street lamp in Lund" src="https://www.codethink.co.uk/articles/rethinking-safety-culture/street-lamp.webp"&gt;&lt;/p&gt;
&lt;h2&gt;Day 3&lt;/h2&gt;
&lt;p&gt;I kicked off the last day by reprising my presentation about the TSF from the Eclipse workshop for the ELISA attendees. Once again, the enthusiastic engagement and insightful questions from the participants were very gratifying, and Daniel Krippner helped to illustrate how the framework may be applied in practice by talking through his use of it as part of the Eclipse uProtocol project. Daniel and I followed this with a quick discussion of how Rust is becoming increasingly relevant in the safety sphere, and how this may be relevant for ELISA.&lt;/p&gt;
&lt;p&gt;The workshop wrapped up with a discussion on the Open Source Best Practices Standard, an initiative that was &lt;a href="https://linuxfoundation.eu/newsroom/new-initiative-seeks-to-establish-open-source-software-best-practices-standard"&gt;launched&lt;/a&gt; earlier this year. It included a live survey collecting input from the audience about their awareness of existing standards and suggestions for projects to be considered for examples of best practices.&lt;/p&gt;
&lt;h2&gt;Key Takeaways&lt;/h2&gt;
&lt;p&gt;I’ve attended numerous ELISA workshops since the first one in 2019, and it was wonderful to note how many passionate and enthusiastic newcomers we had attending this time. We also had participants from a variety of different backgrounds, including academics from the local university and engineers from the rail, medical and aeronautics industries, as well as the always-prevalent automotive specialists. &lt;/p&gt;
&lt;p&gt;ELISA’s increasing engagement with other open source communities, including those from the Eclipse Foundation and Linux Foundation projects, is also good to see. The growing interest in safety-related topics in these communities, building on the already well-established awareness of cybersecurity topics, is also encouraging. After the enthusiastic reception that my talks had last week, I am hopeful that the Trustable Software Framework can help to continue this trend, giving all open source projects a way to start engaging with these topics and to share their thinking and strategies for building trust with other projects and communities.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;→ Read: &lt;a href="https://www.codethink.co.uk/news/trustable-software.html"&gt;'Codethink Limited Announces World’s First Baseline Safety Assessment for a Linux-Based OS to SIL 3 / ASIL D'&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;</content><category term="2025"></category><category term="Trustable"></category><category term="Trustable Software Framework"></category><category term="Events"></category><category term="Safety"></category></entry><entry><title>RISC-V Summit Europe 2025: What to Expect from Codethink</title><link href="https://www.codethink.co.uk/articles/risc-v-summit-europe-2025-preview/" rel="alternate"></link><published>2025-05-08T00:00:00+01:00</published><updated>2025-05-08T00:00:00+01:00</updated><author><name>Will Stonier</name></author><id>tag:www.codethink.co.uk,2025-05-08:/articles/risc-v-summit-europe-2025-preview/</id><summary type="html">&lt;p&gt;Join Codethink at RISC-V Summit Europe 2025 for a talk on runtime-configurable endianness in RISC-V and a poster session.&lt;/p&gt;</summary><content type="html">&lt;p&gt;&lt;a href="https://riscv-europe.org/summit/2025/"&gt;RISC-V Summit Europe 2025&lt;/a&gt; is just around the corner (12-15 May), and we’re excited to present a technical presentation and a poster exhibition. Learn out more about where and when you can find us below.&lt;/p&gt;
&lt;h2&gt;Talk: ‘Implementing Runtime-Configurable Endianness in RISC-V: Challenges and Solutions’&lt;/h2&gt;
&lt;p&gt;Join Lawrence Hunter, Roan Richmond, and Ben Dooks as they dive into the intricacies of implementing dynamic endianness support in RISC-V.&lt;/p&gt;
&lt;h3&gt;Talk information:&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Session T2.3.06&lt;/strong&gt; (Submission #105)&lt;br&gt;
&lt;strong&gt;Date: Wednesday 14 May at 12:45&lt;/strong&gt;&lt;br&gt;
&lt;strong&gt;Location: Gaston Berger (S2)&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Abstract:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The RISC-V Privileged Specification introduced dynamic endianness switching in version 1.12, though no commercial hardware yet supports it. This work extends QEMU to enable big-endian execution, allowing the booting of a big-endian Linux system with OpenSBI. Modifications were required across QEMU, OpenSBI, the Linux kernel, and supporting libraries to ensure correct memory operations, instruction encoding, and runtime patching. The project demonstrates the feasibility of big-endian support for RISC-V, providing a foundation for future hardware and software development.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;→ &lt;a href="https://www.codethink.co.uk/articles/risc-v-big-endian-support-runtime-testing/"&gt;Sound interesting? You can read about the project in our blog post: ‘To boldly big-endian where no one has big-endianded before’&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Poster Exhibition:&lt;/h2&gt;
&lt;p&gt;Come and chat with our engineers during the &lt;strong&gt;poster session on Tuesday, 13 May&lt;/strong&gt;, when we’ll showcase this work in further detail.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Location:&lt;/strong&gt; Island 2.1 (Level -2)&lt;/p&gt;
&lt;h2&gt;Learn more about our work on RISC-V: meet Codethink in Paris&lt;/h2&gt;
&lt;p&gt;If you’re attending RISC-V Summit and would like to learn more about Codethink’s work on RISC-V, &lt;strong&gt;&lt;a href="https://www.codethink.co.uk/contact.html"&gt;let’s set up a meeting&lt;/a&gt;&lt;/strong&gt;. See you in Paris!&lt;/p&gt;</content><category term="2025"></category><category term="RISC-V"></category><category term="QEMU"></category><category term="Big-endian"></category><category term="Events"></category></entry><entry><title>Cyber Resilience Act (CRA): What You Need to Know</title><link href="https://www.codethink.co.uk/articles/what-is-cyber-resilience-act-cra/" rel="alternate"></link><published>2025-04-22T00:00:00+01:00</published><updated>2025-04-22T00:00:00+01:00</updated><author><name>Will Stonier</name></author><id>tag:www.codethink.co.uk,2025-04-22:/articles/what-is-cyber-resilience-act-cra/</id><summary type="html">&lt;p&gt;Explore the EU Cyber Resilience Act (CRA), its impact on businesses and affected sectors, its implementation, and penalties for non-compliance.&lt;/p&gt;</summary><content type="html">&lt;p&gt;Robust cybersecurity measures are becoming increasingly important in software design and development. Cyber threats continue to evolve and become more sophisticated, requiring governments to take action to safeguard vulnerable digital infrastructure.&lt;/p&gt;
&lt;p&gt;Over the past decade, several significant cyberattacks have occurred across the public and private sectors. These include the &lt;a href="https://www.cloudflare.com/en-gb/learning/security/ransomware/wannacry-ransomware/"&gt;WannaCry Ransomware Attack in 2017&lt;/a&gt;, which impacted more than 200,000 people worldwide, and the &lt;a href="https://www.upguard.com/blog/how-did-kaseya-get-hacked"&gt;Kaseya VSA attack&lt;/a&gt;, in which hackers used insecure network administration software to disrupt over 100,000 businesses. These incidents (and others) underscored the need for a systemic solution, so the Cyber Resilience Act was developed.&lt;/p&gt;
&lt;p&gt;This blog post provides a top-level overview of the EU Cyber Resilience Act, who it will impact, what happens if businesses don’t comply, and when it will be implemented.&lt;/p&gt;
&lt;h2&gt;What is the EU Cyber Resilience Act?&lt;/h2&gt;
&lt;p&gt;The Cyber Resilience Act (CRA) is an EU regulation that requires manufacturers of digital products to satisfy its standards before they can offer those products in EU member states.&lt;/p&gt;
&lt;p&gt;Furthermore, the CRA is designed to protect consumers and businesses purchasing software products that include a digital component. This includes hardware products such as smartphones, tablets, smart home devices, computers, laptops, and software products such as mobile apps or cloud services.&lt;/p&gt;
&lt;p&gt;The Cyber Resilience Act addresses the insufficient cybersecurity measures in products and the delays in security updates for software and hardware. In addition, the CRA establishes mandatory cybersecurity standards for manufacturers and retailers, regulating these products' planning, design, development, and maintenance. The act also mandates manufacturers to ensure proper support throughout their products' lifecycle.&lt;/p&gt;
&lt;p&gt;→ &lt;a href="https://digital-strategy.ec.europa.eu/en/library/cyber-resilience-act"&gt;Access the Cyber Resilience Act in all official EU languages&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;Who is impacted by the Cyber Resilience Act?&lt;/h2&gt;
&lt;p&gt;While the CRA covers a broad range of areas, its primary focus is on manufacturers of software-enabled devices. However, it will impact organisations throughout the supply chain.&lt;/p&gt;
&lt;p&gt;The Cyber Resilience Act outlines three categories of businesses/organisations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Manufacturers&lt;/strong&gt; must ensure their products comply with the regulations.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Distributors&lt;/strong&gt; need to verify that the products display the CE mark.  &lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Importers&lt;/strong&gt; are responsible for making sure only products that meet CRA requirements (and carry the CE mark) are made available.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;When will the Cyber Resilience Act be implemented?&lt;/h2&gt;
&lt;p&gt;The Cyber Resilience Act came into effect on &lt;strong&gt;December 10, 2024&lt;/strong&gt;, with the primary obligations set to take effect on &lt;strong&gt;December 11, 2027&lt;/strong&gt;. &lt;/p&gt;
&lt;h2&gt;Cyber Resilience Act Enforcement and Penalties: What Businesses Need to Know&lt;/h2&gt;
&lt;p&gt;EU member states will appoint market surveillance authorities to enforce the Cyber Resilience Act. The respective national authorities will work alongside an EU-level supervisory structure to ensure the CRA is being observed.&lt;/p&gt;
&lt;p&gt;Penalties include product withdrawals from the market. There is also the potential for fines up to €15 million or 2.5% of total worldwide annual turnover (whichever is higher).&lt;/p&gt;
&lt;h2&gt;How Codethink can help&lt;/h2&gt;
&lt;p&gt;Ultimately, the Cyber Resilience Act emphasises that manufacturers must integrate stringent cybersecurity measures into designing, developing, and implementing software-enabled digital products. Additionally, the CRA requires these products to maintain cybersecurity standards from production to end-of-life, with support for regular updates.&lt;/p&gt;
&lt;p&gt;Codethink specialises in delivering secure and maintainable software systems, aligning with the Cyber Resilience Act's requirements. Codethink's experience in software development and design in safety-critical industries enables manufacturers to bring digital products to market and satisfy regulatory requirements.&lt;/p&gt;
&lt;h2&gt;Get in touch!&lt;/h2&gt;
&lt;p&gt;Interested in how Codethink can help your business with the Cyber Resilience Act?&lt;/p&gt;
&lt;p&gt;→ &lt;strong&gt;&lt;a href="https://www.codethink.co.uk/contact.html"&gt;Get in touch&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Related Content&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.codethink.co.uk/trustable.html"&gt;Delivering Trustable Software&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.codethink.co.uk/long-term-maintainability.html"&gt;Codethink &amp;amp; Long-Term Maintainability&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Article: &lt;a href="https://www.codethink.co.uk/articles/continuous-testing-unece-r155-r156/"&gt;'How Continuous Testing Helps OEMs Navigate UNECE R155/156'&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Podcast: &lt;a href="https://www.codethink.co.uk/articles/embedded-insiders-podcast/"&gt;'The Road Ahead: Automotive Tech, Standards, and Safety for 2025'&lt;/a&gt; with John Ellis&lt;/li&gt;
&lt;/ul&gt;</content><category term="2025"></category><category term="Cyber Resilience Act"></category><category term="Security"></category><category term="Safety"></category><category term="Trustable Software Framework"></category></entry></feed>