<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>storagebits · #rust</title>
    <subtitle>Firmware, TV apps, a homelab, built at night with an organization of AI agents. And blok, the workshop notebook.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blok.storagebits.workers.dev/tags/rust/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://blok.storagebits.workers.dev"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2026-10-18T18:05:17-04:00</updated>
    <id>https://blok.storagebits.workers.dev/tags/rust/atom.xml</id>
    <entry xml:lang="en">
        <title>Testing the NUCLEO-F412ZG with Hubris and Humility</title>
        <published>2026-10-18T18:05:17-04:00</published>
        <updated>2026-10-18T18:05:17-04:00</updated>
        <author><name>François</name></author>
        <link rel="alternate" type="text/html" href="https://blok.storagebits.workers.dev/blog/hubris-f412/"/>
        <id>https://blok.storagebits.workers.dev/blog/hubris-f412/</id>
        <summary type="html">Porting Hubris, Oxide Computer&#39;s embedded OS, to a NUCLEO-F412ZG at 100 MHz: what had to change, from the untouched kernel to the two Humility fixes, explained.</summary>
        <content type="html" xml:base="https://blok.storagebits.workers.dev/blog/hubris-f412/">&lt;p&gt;The setup: a NUCLEO-F412ZG, an ST development board with a Cortex-M4, and &lt;a class=&quot;external&quot; rel=&quot;external&quot; href=&quot;https://github.com/oxidecomputer/hubris&quot;&gt;Hubris&lt;/a&gt;, Oxide Computer’s embedded OS, which doesn’t support it out of the box. The kernel didn’t change by a single line. The board had to be added, three drivers adapted, the probe firmware updated, and Humility, the debugger, fixed twice.&lt;/p&gt;
&lt;figure class=&quot;portrait&quot;&gt;&lt;img src=&quot;nucleo-f412zg.webp&quot; alt=&quot;The NUCLEO-F412ZG board on a black mat, plugged in over USB, with its green, blue and red user LEDs lit.&quot; width=&quot;1125&quot; height=&quot;1500&quot; loading=&quot;lazy&quot;&gt;&lt;figcaption&gt;The NUCLEO-F412ZG, powered through the USB of the ST-LINK, the probe built into the bottom of the board. Above it, the three user LEDs driven by the pong task.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;hubris-humility-what-are-they-who-s-behind-them&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hubris-humility-what-are-they-who-s-behind-them&quot; aria-label=&quot;Anchor link for: hubris-humility-what-are-they-who-s-behind-them&quot;&gt;#&lt;/a&gt;
Hubris, Humility: what are they? Who’s behind them?&lt;/h2&gt;
&lt;p&gt;Hubris is a small OS written in Rust that Oxide uses in its servers for the service processor. Each task lives in its own memory regions, protected by the MPU. Tasks, their priorities and their access to peripherals are all declared at build time, in an &lt;code&gt;app.toml&lt;/code&gt;. Humility reads the board’s memory through the probe and decodes it using the debug info: task states, stacks, registers, dumps.&lt;/p&gt;
&lt;p&gt;The main author is Cliff Biffle. His talk at the Open Source Firmware Conference 2021 explains why Oxide wrote its own OS. This talk is truly amazing, what a beautiful piece of engineering!&lt;/p&gt;
&lt;figure class=&quot;video video-externe video-large&quot;&gt;&lt;iframe src=&quot;https://player.vimeo.com/video/650913348?dnt=1&quot; title=&quot;On Hubris and Humility, Cliff L. Biffle, OSFC 2021&quot; loading=&quot;lazy&quot; allow=&quot;fullscreen; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption&gt;“On Hubris and Humility: developing an OS for robustness in Rust”, Cliff L. Biffle, Open Source Firmware Conference 2021, 43 min.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;p&gt;Bryan Cantrill, co-founder and CTO of Oxide, and Adam Leventhal also deserve a mention among the big names behind Hubris. In his talk on debugging Docker in production, at DockerCon 2015, Cantrill makes a simple case: a crashed system doesn’t get rebooted before it has been debugged, either live or from an image of its memory, the core dump. Humility does both: &lt;code&gt;humility tasks&lt;/code&gt; reads the running board, and &lt;code&gt;humility dump&lt;/code&gt; saves its full state to a file you examine later, with the board unplugged.&lt;/p&gt;
&lt;figure class=&quot;video video-externe video-large&quot;&gt;&lt;iframe src=&quot;https://www.youtube-nocookie.com/embed/AdMqCUhvRz8&quot; title=&quot;Running Aground: Debugging Docker in Production, Bryan Cantrill, DockerCon 2015&quot; loading=&quot;lazy&quot; allow=&quot;fullscreen; picture-in-picture&quot; allowfullscreen&gt;&lt;/iframe&gt;&lt;figcaption&gt;“Running Aground: Debugging Docker in Production”, Bryan Cantrill, DockerCon 2015. Docker took down the original video in 2018; this is the copy Cantrill reposted.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;what-had-to-be-patched&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#what-had-to-be-patched&quot; aria-label=&quot;Anchor link for: what-had-to-be-patched&quot;&gt;#&lt;/a&gt;
What had to be patched&lt;/h2&gt;
&lt;p&gt;I paste a mission prompt into Claude: get Hubris running on this board, plugged into the Mac over USB. The closest target in the repository is a Discovery board with an STM32F407. That same evening, Hubris runs at 100 MHz on the NUCLEO.&lt;/p&gt;
&lt;p&gt;What was patched, layer by layer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;The kernel&lt;/strong&gt;: nothing, not one line.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The board and the app&lt;/strong&gt;: pure additions. A 2-line board file (the chip name for probe-rs), a chip description (1 MiB of flash, 256 KiB of RAM, peripheral addresses), and the app itself, with the 100 MHz clock configuration. The branch adds 621 lines, documentation included.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Three shared drivers&lt;/strong&gt;, about 150 lines: &lt;code&gt;stm32fx-rcc&lt;/code&gt; selects the F412 variant of the register library, &lt;code&gt;user-leds&lt;/code&gt; drives the Nucleo’s LEDs on PB0, PB7 and PB14, and &lt;code&gt;stm32fx-usart&lt;/code&gt; moves to USART3 (PD8/PD9), the ST-LINK’s virtual serial port, clocked at 50 MHz. The tricky part: two chip variants of the &lt;code&gt;stm32f4&lt;/code&gt; crate can’t coexist in the same task, and two of these drivers forced the F407 in their &lt;code&gt;Cargo.toml&lt;/code&gt;. That choice became a feature. The Discovery still builds identically, rechecked at every step.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The probe&lt;/strong&gt;: the ST-LINK firmware was too old and had to be updated.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Humility&lt;/strong&gt;, the debugger: two fixes, detailed below. The first one repairs Humility against the latest Hubris, whatever the board. The second fixes a wrong measurement that shows up on this board.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;the-mission-in-slices&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-mission-in-slices&quot; aria-label=&quot;Anchor link for: the-mission-in-slices&quot;&gt;#&lt;/a&gt;
The mission, in slices&lt;/h2&gt;
&lt;p&gt;The prompt’s north star fits in one sentence: “&lt;code&gt;humility tasks&lt;/code&gt; montre toutes les tâches &lt;code&gt;Healthy&lt;/code&gt; sur la NUCLEO-F412ZG, les LEDs utilisateur clignotent sous le contrôle de &lt;code&gt;pong&lt;/code&gt;, et &lt;code&gt;ping&lt;/code&gt; écrit sur le port COM virtuel du ST-LINK.” [&lt;code&gt;humility tasks&lt;/code&gt; shows every task &lt;code&gt;Healthy&lt;/code&gt; on the NUCLEO-F412ZG, the user LEDs blink under &lt;code&gt;pong&lt;/code&gt;’s control, and &lt;code&gt;ping&lt;/code&gt; writes to the ST-LINK’s virtual COM port.] Five slices, each one has to end on hardware that works: preflight, reference build, first boot on the 16 MHz internal oscillator, UART, then 100 MHz.&lt;/p&gt;
&lt;p&gt;First stop in slice 0. The board’s ST-LINK firmware is V2J25; probe-rs, which Humility builds on, requires J26. The prompt said to stop and ask. Claude offers to patch it automatically, I give it the zip downloaded from ST’s site, and that’s it.&lt;/p&gt;
&lt;p&gt;Every task is &lt;code&gt;Healthy&lt;/code&gt;. Claude can’t see the LEDs: it reads GPIOB’s output register several times in a row and sees it cycle between PB14, PB0 and nothing, in time with &lt;code&gt;pong&lt;/code&gt;. The board runs at 100 MHz. Over 10 seconds, the Hubris clock counts 10,235 ms against 10.32 s of wall time, the gap being Humility’s connection latency.&lt;/p&gt;
&lt;figure class=&quot;sys sys-horloge&quot;&gt;&lt;div class=&quot;defile&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;Clock tree of the NUCLEO-F412ZG at 100 MHz, scrollable horizontally&quot;&gt;&lt;svg viewBox=&quot;0 0 760 300&quot; role=&quot;img&quot; aria-labelledby=&quot;horloge-t horloge-d&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;title id=&quot;horloge-t&quot;&gt;Clock tree of the NUCLEO-F412ZG at 100 MHz&lt;/title&gt;&lt;desc id=&quot;horloge-d&quot;&gt;The ST-LINK MCO supplies 8 MHz to the HSE bypass input. PLLM 4 gives 2 MHz, PLLN 100 gives a 200 MHz VCO, PLLP 2 gives a 100 MHz SYSCLK. AHB at 100 MHz, APB1 at 50 MHz for USART3, APB2 at 100 MHz.&lt;/desc&gt;&lt;defs&gt;&lt;marker id=&quot;hl-fl&quot; viewBox=&quot;0 0 10 10&quot; refX=&quot;9&quot; refY=&quot;5&quot; markerWidth=&quot;7&quot; markerHeight=&quot;7&quot; orient=&quot;auto-start-reverse&quot;&gt;&lt;path class=&quot;sys-pointe&quot; d=&quot;M0,0 L10,5 L0,10 z&quot;/&gt;&lt;/marker&gt;&lt;/defs&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;10&quot; y=&quot;40&quot; width=&quot;118&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;69&quot; y=&quot;60&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;ST-LINK MCO&lt;/text&gt;&lt;text x=&quot;69&quot; y=&quot;84&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;8 MHz&lt;/text&gt;&lt;text x=&quot;69&quot; y=&quot;103&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;square wave, not a crystal&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;158&quot; y=&quot;40&quot; width=&quot;118&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;217&quot; y=&quot;60&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;HSE bypass&lt;/text&gt;&lt;text x=&quot;217&quot; y=&quot;84&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;8 MHz&lt;/text&gt;&lt;text x=&quot;217&quot; y=&quot;103&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;HSEBYP before HSEON&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;306&quot; y=&quot;40&quot; width=&quot;118&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;365&quot; y=&quot;60&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;PLLM = 4&lt;/text&gt;&lt;text x=&quot;365&quot; y=&quot;84&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;2 MHz&lt;/text&gt;&lt;text x=&quot;365&quot; y=&quot;103&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;VCO input 1–2 MHz&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;454&quot; y=&quot;40&quot; width=&quot;118&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;513&quot; y=&quot;60&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;PLLN = 100&lt;/text&gt;&lt;text x=&quot;513&quot; y=&quot;84&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;200 MHz&lt;/text&gt;&lt;text x=&quot;513&quot; y=&quot;103&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;VCO 100–432 MHz&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;602&quot; y=&quot;40&quot; width=&quot;148&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;676&quot; y=&quot;60&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;PLLP = 2&lt;/text&gt;&lt;text x=&quot;676&quot; y=&quot;84&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;100 MHz&lt;/text&gt;&lt;text x=&quot;676&quot; y=&quot;103&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;SYSCLK, max F412&lt;/text&gt;&lt;line class=&quot;sys-fil sys-courant&quot; x1=&quot;128&quot; y1=&quot;77&quot; x2=&quot;156&quot; y2=&quot;77&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;line class=&quot;sys-fil sys-courant&quot; x1=&quot;276&quot; y1=&quot;77&quot; x2=&quot;304&quot; y2=&quot;77&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;line class=&quot;sys-fil sys-courant&quot; x1=&quot;424&quot; y1=&quot;77&quot; x2=&quot;452&quot; y2=&quot;77&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;line class=&quot;sys-fil sys-courant&quot; x1=&quot;572&quot; y1=&quot;77&quot; x2=&quot;600&quot; y2=&quot;77&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;path class=&quot;sys-fil sys-courant&quot; d=&quot;M676,114 V150 H160 V196&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;path class=&quot;sys-fil sys-courant&quot; d=&quot;M390,150 V196&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;path class=&quot;sys-fil sys-courant&quot; d=&quot;M620,150 V196&quot; marker-end=&quot;url(#hl-fl)&quot;/&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;60&quot; y=&quot;200&quot; width=&quot;200&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;160&quot; y=&quot;220&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;AHB ÷1&lt;/text&gt;&lt;text x=&quot;160&quot; y=&quot;244&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;100 MHz&lt;/text&gt;&lt;text x=&quot;160&quot; y=&quot;263&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;core, SysTick: 100,000 cycles/ms&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;290&quot; y=&quot;200&quot; width=&quot;200&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;390&quot; y=&quot;220&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;APB1 ÷2&lt;/text&gt;&lt;text x=&quot;390&quot; y=&quot;244&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;50 MHz&lt;/text&gt;&lt;text x=&quot;390&quot; y=&quot;263&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;USART3: BRR 434 → 115,207 baud&lt;/text&gt;&lt;rect class=&quot;sys-boite&quot; x=&quot;520&quot; y=&quot;200&quot; width=&quot;200&quot; height=&quot;74&quot; rx=&quot;6&quot;/&gt;&lt;text x=&quot;620&quot; y=&quot;220&quot; class=&quot;sys-titre&quot; text-anchor=&quot;middle&quot;&gt;APB2 ÷1&lt;/text&gt;&lt;text x=&quot;620&quot; y=&quot;244&quot; class=&quot;sys-valeur&quot; text-anchor=&quot;middle&quot;&gt;100 MHz&lt;/text&gt;&lt;text x=&quot;620&quot; y=&quot;263&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;max 100 MHz&lt;/text&gt;&lt;text x=&quot;380&quot; y=&quot;24&quot; class=&quot;sys-note&quot; text-anchor=&quot;middle&quot;&gt;Flash: 3 wait states (90 &amp;lt; HCLK ≤ 100 MHz at 2.7–3.6 V) · regulator in scale 1&lt;/text&gt;&lt;text x=&quot;380&quot; y=&quot;296&quot; class=&quot;sys-code&quot; text-anchor=&quot;middle&quot;&gt;Read back on the board: RCC_PLLCFGR = 0x28401904 · RCC_CFGR = 0x100a · FLASH_ACR = 0x703&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;Slice 4: from 16 MHz on the HSI to 100 MHz. Every constant is justified by a section of RM0402 in the code, and read back from the registers once flashed.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;where-it-broke-savedstate&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#where-it-broke-savedstate&quot; aria-label=&quot;Anchor link for: where-it-broke-savedstate&quot;&gt;#&lt;/a&gt;
Where it broke: SavedState&lt;/h2&gt;
&lt;p&gt;Right before the first boot, &lt;code&gt;humility tasks&lt;/code&gt; fails: “expected structure SavedState not found”.&lt;/p&gt;
&lt;p&gt;To understand why, you need to know how Humility reads a task. When the kernel interrupts a task, it stores its registers (r4 to r11, the stack pointer, and so on) in a structure, the &lt;code&gt;save&lt;/code&gt; field of that task’s &lt;code&gt;Task&lt;/code&gt; entry. Humility reads the task table straight from the board’s RAM. To know at which byte each register sits, it needs the layout of that structure, which it finds in the debug info (DWARF) of the kernel binary. And it looked the structure up by name: &lt;code&gt;SavedState&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;On October 9, four days earlier, Hubris PR #2704 introduced an &lt;code&gt;Arch&lt;/code&gt; trait so the kernel can target several architectures. The ARMv7-M structure was renamed &lt;code&gt;ArmMSavedState&lt;/code&gt;. Humility can no longer find it, and neither &lt;code&gt;humility tasks&lt;/code&gt; nor reading a task’s registers works, whatever the board.&lt;/p&gt;
&lt;p&gt;Two ways out. Claude first pins the branch right before that commit and keeps going, which is enough for this board. It’s in DECISIONS.md, and it unblocks things. Then the fix, on the &lt;code&gt;savedstate-by-member&lt;/code&gt; branch of my Humility fork: instead of looking the type up by name, Humility takes the type of the &lt;code&gt;save&lt;/code&gt; field of &lt;code&gt;Task&lt;/code&gt;, which DWARF provides directly. The name can change; the field stays. Eleven lines added, four removed, in the two places that read this structure: &lt;code&gt;humility tasks&lt;/code&gt; and reading the registers. The fix works with both the old kernel and the new one, tested on the board in both cases.&lt;/p&gt;
&lt;h2 id=&quot;the-second-fix-stackmargin-showed-zeros&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#the-second-fix-stackmargin-showed-zeros&quot; aria-label=&quot;Anchor link for: the-second-fix-stackmargin-showed-zeros&quot;&gt;#&lt;/a&gt;
The second fix: stackmargin showed zeros&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;humility stackmargin&lt;/code&gt; tells you, for each task, how much stack it has never used. The idea: when a task starts, the kernel fills its whole stack with a pattern, &lt;code&gt;0xbaddcafe&lt;/code&gt;. Humility reads the stack from the bottom and counts the words still intact: that’s the margin.&lt;/p&gt;
&lt;p&gt;On the board, it shows a margin of 0 for &lt;code&gt;jefe&lt;/code&gt;, the supervisor, with a 152-byte stack, while its &lt;code&gt;app.toml&lt;/code&gt; declares 2200.&lt;/p&gt;
&lt;p&gt;A Cortex-M4’s MPU only knows regions whose size is a power of 2. Since April (PR #2458), Hubris splits a task’s RAM into several contiguous regions to waste less: &lt;code&gt;jefe&lt;/code&gt;’s 2560 bytes become 1 KiB + 1 KiB + 512 bytes instead of a single 4 KiB region. A stack can therefore span several regions. The kernel was fixed in May (PR #2492) to paint all of it. Humility, though, only read the region holding the top of the stack: here the last one, which holds only 152 bytes of stack, all of them used. Hence the margin of 0.&lt;/p&gt;
&lt;figure class=&quot;sys sys-pile&quot;&gt;&lt;div class=&quot;defile&quot; tabindex=&quot;0&quot; role=&quot;region&quot; aria-label=&quot;The jefe stack as seen by humility stackmargin, before and after the fix, scrollable horizontally&quot;&gt;&lt;svg viewBox=&quot;0 0 800 300&quot; role=&quot;img&quot; aria-labelledby=&quot;pile-t pile-d&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;title id=&quot;pile-t&quot;&gt;The jefe stack as seen by humility stackmargin, before and after the fix&lt;/title&gt;&lt;desc id=&quot;pile-d&quot;&gt;jefe&#39;s RAM is 2560 bytes split into three contiguous MPU regions. Its 2200-byte stack starts at 0x20001400 and its initial top is 0x20001C98, in the third region. Before the fix, Humility only read that third region: 152 bytes, all used, hence a margin of 0. After the fix, it reads all 2200 bytes, finds 1928 bytes still at 0xbaddcafe and a margin of 1928.&lt;/desc&gt;&lt;defs&gt;&lt;pattern id=&quot;pl-peint&quot; width=&quot;6&quot; height=&quot;6&quot; patternUnits=&quot;userSpaceOnUse&quot; patternTransform=&quot;rotate(45)&quot;&gt;&lt;line class=&quot;sys-hachure&quot; x1=&quot;0&quot; y1=&quot;0&quot; x2=&quot;0&quot; y2=&quot;6&quot;/&gt;&lt;/pattern&gt;&lt;/defs&gt;&lt;rect class=&quot;sys-peint-fond&quot; x=&quot;70.0&quot; y=&quot;54&quot; width=&quot;482.0&quot; height=&quot;44&quot;/&gt;&lt;rect fill=&quot;url(#pl-peint)&quot; x=&quot;70.0&quot; y=&quot;54&quot; width=&quot;482.0&quot; height=&quot;44&quot;/&gt;&lt;rect class=&quot;sys-utilise&quot; x=&quot;552.0&quot; y=&quot;54&quot; width=&quot;68.0&quot; height=&quot;44&quot;/&gt;&lt;rect class=&quot;sys-donnees&quot; x=&quot;620.0&quot; y=&quot;54&quot; width=&quot;90.0&quot; height=&quot;44&quot;/&gt;&lt;text x=&quot;198&quot; y=&quot;81&quot; class=&quot;sys-dans&quot; text-anchor=&quot;middle&quot;&gt;0xbaddcafe: 1928 B never touched&lt;/text&gt;&lt;text x=&quot;665&quot; y=&quot;81&quot; class=&quot;sys-dans&quot; text-anchor=&quot;middle&quot;&gt;data&lt;/text&gt;&lt;rect class=&quot;sys-region&quot; x=&quot;70.0&quot; y=&quot;54&quot; width=&quot;256.0&quot; height=&quot;44&quot;/&gt;&lt;text x=&quot;198&quot; y=&quot;44&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;region 1 · 1 KiB&lt;/text&gt;&lt;rect class=&quot;sys-region&quot; x=&quot;326.0&quot; y=&quot;54&quot; width=&quot;256.0&quot; height=&quot;44&quot;/&gt;&lt;text x=&quot;454&quot; y=&quot;44&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;region 2 · 1 KiB&lt;/text&gt;&lt;rect class=&quot;sys-region&quot; x=&quot;582.0&quot; y=&quot;54&quot; width=&quot;128.0&quot; height=&quot;44&quot;/&gt;&lt;text x=&quot;646&quot; y=&quot;44&quot; class=&quot;sys-detail&quot; text-anchor=&quot;middle&quot;&gt;region 3 · 512 B&lt;/text&gt;&lt;line class=&quot;sys-repere&quot; x1=&quot;620.0&quot; y1=&quot;50&quot; x2=&quot;620.0&quot; y2=&quot;168&quot;/&gt;&lt;text x=&quot;624&quot; y=&quot;120&quot; class=&quot;sys-code&quot;&gt;initial top 0x20001C98&lt;/text&gt;&lt;line class=&quot;sys-repere&quot; x1=&quot;552.0&quot; y1=&quot;98&quot; x2=&quot;552.0&quot; y2=&quot;108&quot;/&gt;&lt;text x=&quot;548&quot; y=&quot;120&quot; class=&quot;sys-detail&quot; text-anchor=&quot;end&quot;&gt;272 B used&lt;/text&gt;&lt;text x=&quot;70&quot; y=&quot;290&quot; class=&quot;sys-axe&quot; text-anchor=&quot;middle&quot;&gt;0x20001400&lt;/text&gt;&lt;text x=&quot;326&quot; y=&quot;290&quot; class=&quot;sys-axe&quot; text-anchor=&quot;middle&quot;&gt;0x20001800&lt;/text&gt;&lt;text x=&quot;582&quot; y=&quot;290&quot; class=&quot;sys-axe&quot; text-anchor=&quot;middle&quot;&gt;0x20001C00&lt;/text&gt;&lt;text x=&quot;710&quot; y=&quot;290&quot; class=&quot;sys-axe&quot; text-anchor=&quot;middle&quot;&gt;0x20001E00&lt;/text&gt;&lt;text x=&quot;10&quot; y=&quot;174&quot; class=&quot;sys-serie sys-faute-txt&quot;&gt;before&lt;/text&gt;&lt;line class=&quot;sys-mesure sys-faute&quot; x1=&quot;582.0&quot; y1=&quot;170&quot; x2=&quot;620.0&quot; y2=&quot;170&quot;/&gt;&lt;circle class=&quot;sys-scan sys-scan-avant&quot; cx=&quot;582.0&quot; cy=&quot;170&quot; r=&quot;5&quot;/&gt;&lt;text x=&quot;574&quot; y=&quot;174&quot; class=&quot;sys-detail&quot; text-anchor=&quot;end&quot;&gt;reads 152 B, stops at the 1st word → MARGIN 0&lt;/text&gt;&lt;text x=&quot;10&quot; y=&quot;219&quot; class=&quot;sys-serie sys-sain-txt&quot;&gt;after&lt;/text&gt;&lt;line class=&quot;sys-mesure sys-sain&quot; x1=&quot;70.0&quot; y1=&quot;215&quot; x2=&quot;620.0&quot; y2=&quot;215&quot;/&gt;&lt;g class=&quot;sys-scan-apres&quot;&gt;&lt;circle class=&quot;sys-scan&quot; cx=&quot;70.0&quot; cy=&quot;215&quot; r=&quot;5&quot;/&gt;&lt;/g&gt;&lt;text x=&quot;70&quot; y=&quot;237&quot; class=&quot;sys-detail&quot;&gt;reads 2200 B from the base, counts 1928 B of 0xbaddcafe → MARGIN 1928&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;Read on the board (humility map, readmem). Humility only looked at the region holding the top of the stack. The kernel, for its part, paints the whole stack with 0xbaddcafe across all three regions.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;pre class=&quot;giallo z-code&quot; &gt;&lt;code data-lang=&quot;plain&quot;&gt;&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;           STACKBASE  STACKSIZE  MAXDEPTH  MARGIN&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;before jefe 0x20001c00       152       152       0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;after  jefe 0x20001400      2200       272    1928&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The fix, on the &lt;code&gt;stackmargin-multi-region&lt;/code&gt; branch, walks down the contiguous regions that belong to the same task, as the kernel does, before measuring. Humility’s regression test confirmed it too: on a dump from a Sidecar, a real Oxide switch, the expected snapshot contained three bogus “MARGIN 0”. After the fix, every stack size matches, to the byte, the one declared in that Sidecar’s &lt;code&gt;app.toml&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;where-i-m-at&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#where-i-m-at&quot; aria-label=&quot;Anchor link for: where-i-m-at&quot;&gt;#&lt;/a&gt;
Where I’m at&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;nucleo-f412zg&lt;/code&gt; branch has five commits, with a DECISIONS.md and a README. It’s pinned before PR #2704; with the first fix, Humility also keeps up with the recent kernel. Both fixes are on my Humility fork. The kernel test suite on the F412, the optional slice 5, isn’t done. And “all &lt;code&gt;Healthy&lt;/code&gt;” is only half true: &lt;code&gt;ping&lt;/code&gt; deliberately faults every 100 replies from &lt;code&gt;pong&lt;/code&gt;, as in Oxide’s demos.&lt;/p&gt;
&lt;p&gt;A lead for later, not done yet: an I2C or SPI sensor behind a driver task. In Hubris, a driver is a task like any other: it alone has access to the peripheral’s registers, declared in the &lt;code&gt;app.toml&lt;/code&gt;, and the other tasks talk to it through messages. The repository’s I2C driver targets the STM32H7 and G0 (plus a separate driver for the LPC55), SPI only the STM32H7 and the LPC55: nothing for the F4 family, it would have to be written for this board.&lt;/p&gt;
&lt;p&gt;To be continued…&lt;/p&gt;
&lt;aside class=&quot;transparence&quot; aria-label=&quot;How this post was written&quot;&gt;
  &lt;p class=&quot;transparence-titre&quot;&gt;How this post was written&lt;/p&gt;
  &lt;p&gt;Drafted by Claude Opus 5.5 from: a mission prompt, about ten messages and an interview written by François, his Claude Code session, the &lt;code&gt;hubris&lt;/code&gt; repository (5 commits on the &lt;code&gt;nucleo-f412zg&lt;/code&gt; branch), two Humility branches (3 commits), the &lt;code&gt;humility&lt;/code&gt; outputs and the registers read back on the board.&lt;/p&gt;
  &lt;p&gt;Proofread and corrected by François in 20 min, 6 % of the text rewritten.&lt;/p&gt;
  &lt;p&gt;Translated from French by the AI, from the proofread version. The translation was not reviewed line by line.&lt;/p&gt;
&lt;/aside&gt;
</content>
    </entry>
</feed>
