<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="es">
    <title>storagebits · #hubris</title>
    <subtitle>Firmware, apps de TV, un homelab, construidos de noche con una organización de agentes de IA. Y blok, el cuaderno de taller.</subtitle>
    <link rel="self" type="application/atom+xml" href="https://blok.storagebits.workers.dev/es/tags/hubris/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/es/tags/hubris/atom.xml</id>
    <entry xml:lang="es">
        <title>Probando la NUCLEO-F412ZG con Hubris y 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/es/blog/hubris-f412/"/>
        <id>https://blok.storagebits.workers.dev/es/blog/hubris-f412/</id>
        <summary type="html">Portar Hubris, el sistema operativo embebido de Oxide Computer, a una NUCLEO-F412ZG a 100 MHz: lo que hubo que tocar, del kernel intacto a los dos parches de Humility, explicado.</summary>
        <content type="html" xml:base="https://blok.storagebits.workers.dev/es/blog/hubris-f412/">&lt;p&gt;El tema: una NUCLEO-F412ZG, una placa de desarrollo de ST con un Cortex-M4, y &lt;a class=&quot;external&quot; rel=&quot;external&quot; href=&quot;https://github.com/oxidecomputer/hubris&quot;&gt;Hubris&lt;/a&gt;, el sistema operativo embebido de Oxide Computer, que no la soporta de serie. El kernel no cambió ni una línea. Hubo que añadir la placa, adaptar tres drivers, actualizar el firmware de la sonda y corregir dos veces Humility, el depurador.&lt;/p&gt;
&lt;figure class=&quot;portrait&quot;&gt;&lt;img src=&quot;nucleo-f412zg.webp&quot; alt=&quot;La placa NUCLEO-F412ZG sobre una alfombrilla negra, conectada por USB, con sus LED de usuario verde, azul y rojo encendidos.&quot; width=&quot;1125&quot; height=&quot;1500&quot; loading=&quot;lazy&quot;&gt;&lt;figcaption&gt;La NUCLEO-F412ZG, alimentada por el USB del ST-LINK, la sonda integrada en la parte inferior de la placa. Encima, los tres LED de usuario que controla la tarea pong.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;hubris-humility-que-son-quien-esta-detras&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#hubris-humility-que-son-quien-esta-detras&quot; aria-label=&quot;Anchor link for: hubris-humility-que-son-quien-esta-detras&quot;&gt;#&lt;/a&gt;
Hubris, Humility: ¿qué son? ¿Quién está detrás?&lt;/h2&gt;
&lt;p&gt;Hubris es un pequeño sistema operativo en Rust que Oxide usa en sus servidores para el procesador de servicio. Cada tarea vive en sus propias regiones de memoria, protegidas por la MPU. Las tareas, sus prioridades y sus permisos sobre los periféricos se declaran todos en tiempo de compilación, en un &lt;code&gt;app.toml&lt;/code&gt;. Humility lee la memoria de la placa a través de la sonda y la traduce gracias a la información de depuración: estado de las tareas, pilas, registros, volcados.&lt;/p&gt;
&lt;p&gt;El autor principal es Cliff Biffle. Su charla en la Open Source Firmware Conference 2021 explica por qué Oxide escribió su propio sistema operativo. Esta charla es realmente increíble, ¡qué gran obra de ingeniería!&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;También hay que citar a Bryan Cantrill, cofundador y CTO de Oxide, y a Adam Leventhal entre los grandes nombres detrás de Hubris. En su charla sobre la depuración de Docker en producción, en la DockerCon 2015, Cantrill defiende una idea sencilla: un sistema colgado no se reinicia antes de haberlo depurado, en vivo o a partir de una imagen de su memoria, el &lt;em&gt;core dump&lt;/em&gt;. Humility hace las dos cosas: &lt;code&gt;humility tasks&lt;/code&gt; lee la placa en marcha y &lt;code&gt;humility dump&lt;/code&gt; guarda su estado completo en un archivo que se examina después, con la placa desconectada.&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 retiró el vídeo original en 2018; esta es la copia que volvió a publicar Cantrill.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;lo-que-hubo-que-parchear&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#lo-que-hubo-que-parchear&quot; aria-label=&quot;Anchor link for: lo-que-hubo-que-parchear&quot;&gt;#&lt;/a&gt;
Lo que hubo que parchear&lt;/h2&gt;
&lt;p&gt;Le paso a Claude un prompt de misión: hacer funcionar Hubris en esta placa, conectada por USB al Mac. Su objetivo más cercano en el repositorio es una placa Discovery con un STM32F407. Esa misma noche, Hubris corre a 100 MHz en la NUCLEO.&lt;/p&gt;
&lt;p&gt;Lo que se parcheó, capa por capa:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;El kernel&lt;/strong&gt;: nada, ni una línea.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La placa y la app&lt;/strong&gt;: solo añadidos. Un archivo de placa de 2 líneas (el nombre del chip para probe-rs), una descripción del chip (1 MiB de flash, 256 KiB de RAM, direcciones de los periféricos) y la app en sí, con la configuración de reloj a 100 MHz. La rama añade 621 líneas, documentación incluida.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tres drivers compartidos&lt;/strong&gt;, unas 150 líneas: &lt;code&gt;stm32fx-rcc&lt;/code&gt; elige la variante F412 de la biblioteca de registros, &lt;code&gt;user-leds&lt;/code&gt; controla los LED de la Nucleo en PB0, PB7 y PB14, y &lt;code&gt;stm32fx-usart&lt;/code&gt; pasa a USART3 (PD8/PD9), el puerto serie virtual del ST-LINK, a 50 MHz. El punto delicado: dos variantes de chip del crate &lt;code&gt;stm32f4&lt;/code&gt; no pueden convivir en una misma tarea, y dos de estos drivers forzaban la F407 en su &lt;code&gt;Cargo.toml&lt;/code&gt;. Esa elección pasó a una feature. La Discovery compila exactamente igual, comprobado de nuevo en cada paso.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;La sonda&lt;/strong&gt;: el firmware del ST-LINK, demasiado antiguo, tuvo que actualizarse.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Humility&lt;/strong&gt;, el depurador: dos parches, detallados más abajo. El primero arregla Humility frente al Hubris más reciente, sea cual sea la placa. El segundo corrige una medida errónea que aparece en esta placa.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;la-mision-por-tramos&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#la-mision-por-tramos&quot; aria-label=&quot;Anchor link for: la-mision-por-tramos&quot;&gt;#&lt;/a&gt;
La misión, por tramos&lt;/h2&gt;
&lt;p&gt;La north star del prompt cabe en una frase: «&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; muestra todas las tareas &lt;code&gt;Healthy&lt;/code&gt; en la NUCLEO-F412ZG, los LED de usuario parpadean bajo el control de &lt;code&gt;pong&lt;/code&gt;, y &lt;code&gt;ping&lt;/code&gt; escribe en el puerto COM virtual del ST-LINK.] Cinco tramos, cada uno debe terminar en hardware que funciona: preflight, build de referencia, primer arranque con el oscilador interno a 16 MHz, UART y luego 100 MHz.&lt;/p&gt;
&lt;p&gt;Primera parada ya en el tramo 0. El firmware del ST-LINK de la placa está en V2J25; probe-rs, en el que se apoya Humility, exige J26. El prompt decía que había que pararse y preguntar. Claude propone parchearlo automáticamente, le doy el zip descargado de la web de ST y listo.&lt;/p&gt;
&lt;p&gt;Todas las tareas están &lt;code&gt;Healthy&lt;/code&gt;. Claude no ve los LED: lee el registro de salida de GPIOB varias veces seguidas y lo ve alternar entre PB14, PB0 y nada, al ritmo de &lt;code&gt;pong&lt;/code&gt;. La placa corre a 100 MHz. En 10 segundos, el reloj de Hubris cuenta 10 235 ms frente a 10,32 s de reloj de pared; la diferencia es la latencia de conexión de Humility.&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;Árbol de reloj de la NUCLEO-F412ZG a 100 MHz, desplazable horizontalmente&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;Árbol de reloj de la NUCLEO-F412ZG a 100 MHz&lt;/title&gt;&lt;desc id=&quot;horloge-d&quot;&gt;El MCO del ST-LINK entrega 8 MHz a la entrada HSE bypass. PLLM 4 da 2 MHz, PLLN 100 da un VCO a 200 MHz, PLLP 2 da un SYSCLK a 100 MHz. AHB a 100 MHz, APB1 a 50 MHz para la USART3, APB2 a 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;onda cuadrada, no un cristal&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 antes que 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;entrada VCO 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;núcleo, SysTick: 100 000 ciclos/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 baudios&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 a 2,7–3,6 V) · regulador en 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;Releído en la placa: RCC_PLLCFGR = 0x28401904 · RCC_CFGR = 0x100a · FLASH_ACR = 0x703&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;El tramo 4: de 16 MHz con el HSI a 100 MHz. Cada constante está justificada en el código por una sección del RM0402, y releída en los registros una vez flasheada.&lt;/figcaption&gt;&lt;/figure&gt;
&lt;h2 id=&quot;cuando-se-rompio-savedstate&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#cuando-se-rompio-savedstate&quot; aria-label=&quot;Anchor link for: cuando-se-rompio-savedstate&quot;&gt;#&lt;/a&gt;
Cuando se rompió: SavedState&lt;/h2&gt;
&lt;p&gt;Justo antes del primer arranque, &lt;code&gt;humility tasks&lt;/code&gt; falla: «expected structure SavedState not found».&lt;/p&gt;
&lt;p&gt;Para entenderlo, hay que saber cómo lee Humility una tarea. Cuando el kernel interrumpe una tarea, guarda sus registros (r4 a r11, el puntero de pila, etc.) en una estructura, el campo &lt;code&gt;save&lt;/code&gt; de la entrada &lt;code&gt;Task&lt;/code&gt; de esa tarea. Humility lee la tabla de tareas directamente en la RAM de la placa. Para saber en qué byte está cada registro, necesita el plano de esa estructura, que encuentra en la información de depuración (DWARF) del binario del kernel. Y la buscaba por su nombre: &lt;code&gt;SavedState&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;El 9 de octubre, cuatro días antes, la PR #2704 de Hubris introdujo un trait &lt;code&gt;Arch&lt;/code&gt; para que el kernel pueda apuntar a varias arquitecturas. La estructura del ARMv7-M pasó a llamarse &lt;code&gt;ArmMSavedState&lt;/code&gt;. Humility ya no la encuentra, y ni &lt;code&gt;humility tasks&lt;/code&gt; ni la lectura de los registros de una tarea funcionan, sea cual sea la placa.&lt;/p&gt;
&lt;p&gt;Dos maneras de salir del paso. Claude primero fija la rama justo antes de ese commit y sigue adelante, lo que basta para esta placa. Está en DECISIONS.md, y desbloquea. Después, el parche, en la rama &lt;code&gt;savedstate-by-member&lt;/code&gt; de mi fork de Humility: en lugar de buscar el tipo por su nombre, Humility toma el tipo del campo &lt;code&gt;save&lt;/code&gt; de &lt;code&gt;Task&lt;/code&gt;, que DWARF da directamente. El nombre puede cambiar; el campo se queda. Once líneas añadidas, cuatro eliminadas, en los dos sitios que leían esa estructura: &lt;code&gt;humility tasks&lt;/code&gt; y la lectura de los registros. El parche funciona con el kernel antiguo y con el nuevo, probado en la placa en ambos casos.&lt;/p&gt;
&lt;h2 id=&quot;el-segundo-parche-stackmargin-mostraba-ceros&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#el-segundo-parche-stackmargin-mostraba-ceros&quot; aria-label=&quot;Anchor link for: el-segundo-parche-stackmargin-mostraba-ceros&quot;&gt;#&lt;/a&gt;
El segundo parche: stackmargin mostraba ceros&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;humility stackmargin&lt;/code&gt; indica, para cada tarea, cuánta pila no ha usado nunca. El principio: al arrancar una tarea, el kernel llena toda su pila con un patrón, &lt;code&gt;0xbaddcafe&lt;/code&gt;. Humility lee la pila desde abajo y cuenta las palabras que siguen intactas: ese es el margen.&lt;/p&gt;
&lt;p&gt;En la placa, muestra un margen de 0 para &lt;code&gt;jefe&lt;/code&gt;, el supervisor, con una pila de 152 bytes, cuando su &lt;code&gt;app.toml&lt;/code&gt; declara 2200.&lt;/p&gt;
&lt;p&gt;La MPU de un Cortex-M4 solo admite regiones cuyo tamaño es una potencia de 2. Desde abril (PR #2458), Hubris divide la RAM de una tarea en varias regiones contiguas para desperdiciar menos: los 2560 bytes de &lt;code&gt;jefe&lt;/code&gt; son 1 KiB + 1 KiB + 512 bytes en lugar de una única región de 4 KiB. Así que una pila puede extenderse por varias regiones. El kernel se corrigió en mayo (PR #2492) para pintarla entera. Humility, en cambio, solo leía la región que contiene la cima de la pila: aquí la última, donde caben solo 152 bytes de pila, todos usados. De ahí el margen de 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;La pila de jefe vista por humility stackmargin, antes y después del parche, desplazable horizontalmente&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;La pila de jefe vista por humility stackmargin, antes y después del parche&lt;/title&gt;&lt;desc id=&quot;pile-d&quot;&gt;La RAM de jefe ocupa 2560 bytes repartidos en tres regiones MPU contiguas. Su pila de 2200 bytes empieza en 0x20001400 y su cima inicial es 0x20001C98, en la tercera región. Antes del parche, Humility solo leía esa tercera región: 152 bytes, todos usados, así que un margen de 0. Después del parche, lee los 2200 bytes, encuentra 1928 bytes todavía a 0xbaddcafe y un margen de 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 sin tocar&lt;/text&gt;&lt;text x=&quot;665&quot; y=&quot;81&quot; class=&quot;sys-dans&quot; text-anchor=&quot;middle&quot;&gt;datos&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;región 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;región 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;región 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;cima inicial 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 usados&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;antes&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;lee 152 B, se detiene en la 1.ª palabra → MARGIN 0&lt;/text&gt;&lt;text x=&quot;10&quot; y=&quot;219&quot; class=&quot;sys-serie sys-sain-txt&quot;&gt;después&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;lee 2200 B desde la base, cuenta 1928 B de 0xbaddcafe → MARGIN 1928&lt;/text&gt;&lt;/svg&gt;&lt;/div&gt;&lt;figcaption&gt;Leído en la placa (humility map, readmem). Humility solo miraba la región que contiene la cima de la pila. El kernel, en cambio, pinta toda la pila con 0xbaddcafe en las tres regiones.&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;antes   jefe 0x20001c00       152       152       0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;giallo-l&quot;&gt;&lt;span&gt;después jefe 0x20001400      2200       272    1928&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;El parche, en la rama &lt;code&gt;stackmargin-multi-region&lt;/code&gt;, recorre hacia abajo las regiones contiguas que pertenecen a la misma tarea, como hace el kernel, antes de medir. La prueba de no regresión de Humility también lo validó: en un volcado de un Sidecar, un switch real de Oxide, la instantánea esperada contenía tres falsos «MARGIN 0». Tras la corrección, cada tamaño de pila coincide al byte con el declarado en el &lt;code&gt;app.toml&lt;/code&gt; de ese Sidecar.&lt;/p&gt;
&lt;h2 id=&quot;donde-estoy&quot;&gt;&lt;a class=&quot;zola-anchor&quot; href=&quot;#donde-estoy&quot; aria-label=&quot;Anchor link for: donde-estoy&quot;&gt;#&lt;/a&gt;
Dónde estoy&lt;/h2&gt;
&lt;p&gt;La rama &lt;code&gt;nucleo-f412zg&lt;/code&gt; tiene cinco commits, con un DECISIONS.md y un README. Está fijada antes de la PR #2704; con el primer parche, Humility también sigue al kernel reciente. Los dos parches están en mi fork de Humility. La batería de pruebas del kernel en la F412, el tramo 5 opcional, no está hecha. Y «todo &lt;code&gt;Healthy&lt;/code&gt;» solo es verdad a medias: &lt;code&gt;ping&lt;/code&gt; provoca a propósito un fallo cada 100 respuestas de &lt;code&gt;pong&lt;/code&gt;, como en las demos de Oxide.&lt;/p&gt;
&lt;p&gt;Una pista para más adelante, aún sin hacer: un sensor I2C o SPI detrás de una tarea driver. En Hubris, un driver es una tarea como cualquier otra: solo ella tiene acceso a los registros del periférico, declarados en el &lt;code&gt;app.toml&lt;/code&gt;, y las demás tareas le hablan mediante mensajes. El driver I2C del repositorio apunta a los STM32H7 y G0 (más un driver aparte para el LPC55), el SPI solo a los STM32H7 y al LPC55: nada para la familia F4, habría que escribirlo para esta placa.&lt;/p&gt;
&lt;p&gt;Continuará…&lt;/p&gt;
&lt;aside class=&quot;transparence&quot; aria-label=&quot;Cómo se escribió esta entrada&quot;&gt;
  &lt;p class=&quot;transparence-titre&quot;&gt;Cómo se escribió esta entrada&lt;/p&gt;
  &lt;p&gt;Redactada por Claude Opus 5.5 a partir de: un prompt de misión, una decena de mensajes y una entrevista escritos por François, su sesión de Claude Code, el repositorio &lt;code&gt;hubris&lt;/code&gt; (5 commits en la rama &lt;code&gt;nucleo-f412zg&lt;/code&gt;), dos ramas de Humility (3 commits), las salidas de &lt;code&gt;humility&lt;/code&gt; y los registros releídos en la placa.&lt;/p&gt;
  &lt;p&gt;Revisada y corregida por François en 20 min, 6 % del texto reescrito.&lt;/p&gt;
  &lt;p&gt;Traducida del francés por la IA, a partir de la versión revisada. La traducción no se revisó línea por línea.&lt;/p&gt;
&lt;/aside&gt;
</content>
    </entry>
</feed>
