- Combinar 'Manifiesto del Ser Desnudo' (texto experiencial) con
'Propósito y alcance' (axiomas, ecuaciones, dinámica de sistemas)
- Estructura en 7 secciones que integran poesía + matemática:
I. Origen, II. Cuerpo Formal, III. Dinámica, IV. Sombras,
V. Medicina, VI. Práctica, VII. Centro de la Nada
- Reindexado en Qdrant (45 fragmentos)
- Respaldo del original (tierra.md.bak)
- Changed install_deck_delegation → install_controls_delegation:
listener on document, not just #deck (controls are outside deck now)
- Graph node click: use dyn_into::<HtmlElement>() then click() instead
of dyn_ref which may fail on <a> elements
- .page-controls visibility: changed from (wrong) to
- Graph node callback: use HTMLElement.click() instead of
dispatch_event(MouseEvent), which wasn't working (untrusted event)
- Moved the minimize/close buttons from dynamic JS creation to
static HTML in index.html (always present, hidden by default)
- sync_page_controls() now just toggles opacity/pointer-events
instead of creating DOM elements
- Graph callback now creates a web_sys::MouseEvent('click') and
dispatches it on the corresponding .tip element.
- The existing install_tip_clicks listener captures it and calls
open_or_switch with proper coordinates.
- Changed all relative paths (./styles.css → /styles.css,
./pkg/gioser_web.js → /pkg/gioser_web.js,
./md/*.md → /md/*.md) to absolute so they don't break when
pushState changes URL to /estudio/aire etc.
- Caddy already has try_files fallback to /index.html for SPA routing.
- Graph: nodes are draggable via pointer events. Snap back with
spring transition on release (cubic-bezier 0.34,1.56,0.64,1)
- Removed hover bounce animation (was distracting)
- Page controls (minimize/close): now fixed position in viewport
(top-right, z-index 100), not inside the deck-page scroll area.
Created once in sync_page_controls() on show/hide deck.
Controls detect active page when data-minimize/close-page is empty.
- Hash routing → history.pushState: URLs are /estudio/aire etc.
popstate listener handles back/forward. Initial path read on boot.
- Added PointerEvent feature to gioser-graph-web Cargo.toml
- Added History feature to gioser-web Cargo.toml
- Edges and nodes now in separate SVG <g> groups (edges first = behind)
- Stroke width: 0.6 + w*4.0 instead of normalized range (more visible variation)
- Brightness: uses raw weight directly, not normalized against max
- Edges group has no breathing animation (only nodes breathe)
- Each edge color is a 50/50 blend of its two nodes' camino colors
- Extra brightness proportional to weight (higher weight = closer to white)
- Edge opacity/width also varies by weight
- Edges drawn before nodes in SVG (already behind them)
- Fix: map positions by node.id (UUID) not doc_id — edges now draw
- Index the 4 docs/ files into Qdrant (15 fragments via index-gioser-docs.py)
- Page background: single smooth radial-gradient per element (no color
divisions), animated 'page-breathe' — opacity pulses 0.35↔0.80
- Graph CSS: 'graph-breathe' 5s opacity animation (feels alive)
- Uses setInterval(50ms) waiting for offsetWidth > 0
- Falls back to minWidth/minHeight after 10s timeout
- Solves 'cannot access property w' when deck is hidden (scale 0)
- Wraps the entire fetch+render in requestAnimationFrame
so the container has real size before Cytoscape runs cose layout
- Uses 'preset' layout first, then cose with animate:'end'
to play nice with 0-size containers during deck animation
- cytoscape-graph.js now uses MutationObserver, not DOMContentLoaded
(the <gioser-graph> element is created dynamically by WASM)
- Remove unused dispatchEvent from lib.rs
- Rebuild WASM
- Switch from circles to horizontal rounded rectangles with text inside
- Text size 12px body + 8px sublabel (camino), no overlaps
- Edge stroke-width proportional to semantic weight
- Fix 'Layout was forced' warning
- Reduce CSS page-ambience animations: only opacity (no transform)
to fix 'breathing background' visual glitch
- Layout: more separation (k*1.6), 80 iterations
- Add gioser-graph-web dependency to gioser-web
- After markdown loads, mount SVG semantic graph below content
- Graph fetches from api.gioser.net/graph endpoint
- Uses Qdrant k-NN edges, colored by camino
- Callback navigation placeholder (will be wired in next commit)
Plan trazado para mañana. Tres capas:
1. Cuatro capacidades de host read-only (sys_grafo_manifiesto,
sys_grafo_raiz, sys_grafo_recuperar, sys_grafo_hijo) que abren
el grafo de objetos al userspace. Mismo patron de validacion
de memoria que sys_net_*.
2. La app 'atlas': lienzo ~520x400, lee el grafo perezosamente con
cache LRU de 16 entradas, navega con flechas / Enter / Backspace
desde el manifiesto hacia los hijos.
3. Representacion radial: foco central con su firma cromatica
(3 primeros bytes del hash), hijos en circulo, padre en cima
si hay historial, cartela inferior con hash completo + tamaño
+ previsualizacion (texto si pasa el test ASCII, hex si no).
Subfases 21a (caps), 21b (app navegable), 21c (paseo guiado).
Sinergias y mejoras del sistema documentadas: dedup visual del
grafo direccionado por contenido, integridad por uso (cada
navegacion rehashea), validacion del almacen, camino natural a la
escritura (Fase 22), encaje con el faro Akasha de la Fase 20
(quien recibe AnunciarRaiz puede explorar la raiz del par).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tres mensajes y un EtherType propio bastan para extender el grafo de
objetos —direccionado por contenido, ya BLAKE3— a otras maquinas
renaser que escuchen en la misma red de capa-2. Sin TCP, sin IP,
sin DNS.
Crate nueva 'akasha/' (no_std compartido, gemela de 'formato',
excluida del workspace):
- MensajeAkasha enum con SolicitarObjeto(id), ProveedorObjeto(id,
payload), AnunciarRaiz(id).
- Codec: postcard (mismo que ya usa el grafo en disco).
- EtherType: 0x88B5. MAX_PAYLOAD_AKASHA = 1486 (MTU sin fragmentar).
- Helpers componer_frame(src, dst, msg) y analizar_frame(bytes) que
distinguen EtherType ajeno, frame truncado y payload basura.
- 6 pruebas unitarias en verde.
Modulo nuevo 'kernel/src/akasha.rs' con tres oficios:
1. Demuxer (drenar_y_demultiplexar): drena la cola RX del dispositivo
virtio-net y demultiplexa: frames AoE con payload valido los
procesa el respondedor; el resto va a una cola del userspace que
'sys_net_recibir' ahora lee. Frames 0x88B5 con payload
no-postcard (saludo de pregon) se cuentan y tambien viajan al
userspace.
2. Atencion de mensajes (procesar):
- SolicitarObjeto(id): consulta almacen::recuperar; si tenemos el
objeto, respondemos ProveedorObjeto unicast con objeto.serializar()
y re-hashing de defensa en profundidad.
- ProveedorObjeto(id, payload): verifica blake3(payload)==id antes
de absorber con almacen::almacenar.
- AnunciarRaiz(id): si ignoramos el nodo, le solicitamos al emisor.
3. Faro periodico (difundir_raiz cada 5 s): broadcast del hash del
manifiesto actual. Cadencia medida contra reloj::milisegundos(),
no contra los awaits — el interprete wasmi de los apps degrada
la cadencia de EsperaFrame::await a varios cientos de ms, asi
que se mide contra el reloj monotono y los oficios per-fotograma
se enganchan al tic del compositor (cuyo latido es fiable).
Contadores ResumenAkasha (rx/tx por variante, descartados, cola del
usuario) listos para un futuro indicador AoE en la barra de tareas.
Cambios complementarios:
- sys_net_recibir lee de akasha::pop_usuario, no de
drivers::red::recibir_en (que queda #[allow(dead_code)] como
primitiva del driver para diagnostico).
- tarea_red queda corta: envia un ARP al gateway y termina. El
demuxer y el faro viven en el tic del compositor.
Verificacion:
- 'cargo test -p akasha' → 6 pruebas en verde.
- QEMU headless 60 s con -object filter-dump → 14 frames: 11
AnunciarRaiz (Δ promedio 5.86 s sobre 5.00 s de target), 2 ARP
y el pregon hello. Cada AnunciarRaiz lleva el hash del manifiesto
'2f3deadfcc7dae25..' en 33 bytes postcard sobre 47 bytes de frame.
- COM1 vuelca 'akasha :: ANUNCIO emitido :: raiz=2f3deadfcc7dae25..'
en cada disparo.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Tres capacidades nuevas en wasm/env (12-14):
- sys_net_mac(salida) -> i32: escribe los seis bytes del MAC del
dispositivo. 0 OK, -1 si no hay red.
- sys_net_enviar(ptr, len) -> i32: envia un frame Ethernet crudo.
Valida rango contra la memoria lineal del modulo.
- sys_net_recibir(salida, capacidad) -> i32: drena UN paquete por
llamada hacia el buffer del modulo. Devuelve los bytes copiados, 0
si nada pendiente, codigos negativos diagnosticos.
Añadida red::recibir_en(buf) -> usize como su contraparte del driver:
gemelo cooperativo de drenar_rx que aterriza en un buffer del usuario.
App nueva pregon (apps/pregon/, 4.2 KiB WASM): lienzo 480x160, tipografia
8x8 (font8x8) escalada x2. Al init pide su MAC y anuncia su presencia
con un broadcast Ethernet — destino FF:FF:FF:FF:FF:FF, EtherType
experimental 0x88B5, payload ASCII 'renaser :: hola desde mi red'. En
cada tick drena un paquete con sys_net_recibir y muestra el titulo, el
MAC propio, las cuentas TX/RX, y los datos del ultimo frame entrante.
GENESIS 8 -> 9 apps (pregon en posicion 2 detras de bitacora);
CELDA_TASKBAR_ANCHO 130 -> 116 px para que las nueve pestañas + lanzador
+ reloj caben holgadas en 1280 px.
tarea_red del kernel ya no drena RX (la cola pertenece al userspace),
conserva solo el envio del ARP de prueba al arrancar.
Verificada en QEMU con -object filter-dump. El pcap captura tres frames
en orden: (1) broadcast 88B5 de pregon con su payload, (2) ARP request
del kernel, (3) ARP reply del gateway 52:55:0a:00:02:02. La consola
anuncia 'manifiesto :: 9 apps nacidas del grafo'.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GPUI panic-ea si no encuentra ninguna fuente del fallback
(.SystemUIFont → Helvetica → Cantarell → DejaVu Sans → ...).
Fedora minimal no trae fuentes — sin esto el greeter crash al
inicializar text_system.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
GPUI (mirada-greeter) requiere libvulkan.so.1 al iniciar; sin un ICD
no levanta. mesa-vulkan-drivers trae lavapipe (Vulkan por software vía
llvmpipe), apto para VPS sin GPU real.
También se agrega seatd al hint (preferido sobre noop para producción
porque maneja VT-switching correctamente).
El check de runtime ahora detecta libEGL, libvulkan, libgbm y libseat.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
renaser hablaba consigo mismo. Esta fase abre una boca y una oreja al
exterior con una tarjeta de red, reutilizando el `KernelHal` del
disco y el mapeador MMIO (la pieza estructural que hizo esto posible).
- `drivers/red`: monta `VirtIONet<KernelHal, PciTransport, 16>`,
expone `enviar(frame)` y `drenar_rx(callback)`. Sin pila TCP/IP —
solo Ethernet crudo; la composición de paquetes la hace el llamante.
- `componer_arp_request(mac, ip, objetivo)` construye el saludo
inicial: «¿quien tiene 10.0.2.2?» dirigido al gateway de QEMU.
- `interrupts::registrar_irq_red` + handler `irq_red`, gemelo del de
disco. La IRQ del dispositivo activa `red::atender_irq`, que hace
`ack_interrupt` y suelta la línea.
- `tarea_red` en el reactor: al arrancar envía el ARP, después cada
fotograma drena la cola RX y vuelca cada paquete a COM1.
- QEMU args ganan `-netdev user,id=net0 -device virtio-net-pci`.
Verificado con `-object filter-dump,...,file=/tmp/red.pcap`:
red :: virtio-net :: MAC 52:54:00:12:34:56 :: IRQ Some(11)
red :: ARP REQUEST enviado :: ¿quien tiene 10.0.2.2?
red :: RX 64 bytes :: src=52:55:0a:00:02:02 type=0x0806
El src del paquete entrante (`52:55:0a:00:02:02`) codifica `10.0.2.2`
dentro del MAC — es el gateway de QEMU respondiendo. Renaser ya habla
con el exterior.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
memoriosa (Fase 7c) demostró que un app podía persistir su huella.
Esta fase la lleva al gesto natural: un editor de texto. Tecleas,
reinicias renaser, el texto sigue ahí. La huella vive en el grafo
de objetos como todo lo demás.
- Nuevo crate `apps/bitacora/`: lienzo 480×280, tipografía 8×8
embebida (`font8x8 = "0.3"`) escalada x2 a 16×16, render pixel
a pixel desde la memoria del propio app. Buffer 512 bytes con
wrap automático a 28 columnas; `Enter` salta línea, Backspace
borra; al desbordar el buffer se descartan los 64 primeros para
amortizar la mudanza. Cada cambio invoca `sys_estado_guardar`;
al arrancar, `init` llama a `sys_estado_cargar` y reconstruye.
- Mapeo de scancodes US a ASCII (letras, dígitos, puntuación
básica, espacio). Sin shift ni mayúsculas — minimalismo.
- `GENESIS` crece de 7 a 8 apps; `bitacora` es la PRIMERA — gana
la celda maestra al arrancar y te invita a teclear.
- `CELDA_TASKBAR_ANCHO` baja de 150 a 130 px para que las ocho
pestañas + lanzador + reloj quepan holgadas en 1280 px.
Verificado en QEMU: tras escribir "hola renaser" y reiniciar el
kernel con el mismo disk.img, bitácora muestra el texto donde lo
dejó. El `almacen` reporta 24 objetos en el grafo (frente a 9
antes de escribir) y `raiz presente` — cada `guardar` anexó una
versión al log direccionado por contenido.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
La barra de tareas era pasiva: nombraba pero no hacía. Ahora lleva
en sus extremos los dos adornos de toda barra digna.
- Botón «+» indigo a la izquierda (36 px). Un clic incrementa
`PARTOS` — el mismo contador que `Alt+N` — y la tarea del
compositor lo recoge para lanzar la siguiente app de la rotación.
Teclado y ratón comparten ya la misma vía para crear ventanas.
- Reloj `mm:ss` a la derecha (80 px), leído de
`reloj::milisegundos()`. Tinta blanca sobre slate.
- `compositor::tick_reloj()` lo invoca la tarea del compositor cada
fotograma; recompone solo cuando el segundo del reloj monótono
cambia respecto al último mostrado (`ULTIMO_SEGUNDO: AtomicU64`).
Cero coste mientras no toca refrescar.
- `Taskbar` crece con `launcher`, `reloj` y `reloj_region`; el
layout de las pestañas se ajusta entre ambas cuñas. La cruz del
lanzador se dibuja en píxeles directos —dos rectángulos cruzados,
independiente de la tipografía—.
Verificado en QEMU con dos capturas separadas: la barra muestra el
«+» indigo, las siete pestañas (con `glotona` ya legible) y el
reloj. En la primera marca `0:17`; diez segundos después, `0:29` —
la barra se refrescó doce veces sin intervención.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sin clang-devel, los crates *-sys que corren bindgen (libinput-sys,
wayland-sys) fallan en runtime con 'couldn't find libclang.so'. El
mensaje de WARN ahora lo lista explícitamente.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- seed arje-prod gana dos Cards antes de getty-tty1:
* kmod-gpu (OneShot): modprobe virtio_gpu — pone /dev/dri/card0
listo antes de que el compositor intente abrir DRM/KMS.
* carmen-dm (Restart): /usr/bin/mirada-compositor --greeter --drm,
con MIRADA_GREETER_BIN apuntando a /usr/bin/mirada-greeter.
- install-arje-as-init.sh gana paso 1b: build nativo (no-musl) de
mirada-compositor y mirada-greeter. GPUI/Mesa/EGL son dinámicos
contra glibc — la cadena DM no puede vivir en el binario musl
estático de arje-zero. Si el build falla por dev-libs ausentes,
se imprime la lista de paquetes Fedora que faltan y se sigue
(la instalación de arje-zero no se rompe).
- En el paso de instalación, los binarios del compositor se copian
a /usr/bin/ sólo si el build de paso 1b tuvo éxito; ldconfig
comprueba libEGL en runtime y avisa si falta.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
La bocina pertenecía al app enfocado (Fase 12), pero el kernel
necesita hablar también. Ahora tiene voz propia, prioritaria.
- `altavoz`: cola `SECUENCIA: Mutex<VecDeque<(u32,u32)>>` (freq, ms)
+ reloj `FIN_NOTA: AtomicU64`. `agendar(&[...])` encola;
`atender()` (tarea del compositor cada fotograma) avanza la
secuencia y silencia al acabar; `kernel_sonando()` gatea a los
apps — mientras el kernel suena, `sys_tono` no-op.
- Catálogo: VOZ_BIENVENIDA (Do5-Mi5-Sol5, 500 ms), VOZ_LANZAR
(700→1050 Hz), VOZ_CERRAR (900→520 Hz), VOZ_DESALOJO (180 Hz).
- Hitos: `kernel_main` agenda el acorde antes de `ejecutor.run`;
`nacer_ventana` (Alt+N), `cerrar` (Alt+Q), `desalojar` (falla)
agendan al hacer su trabajo.
- De paso: las pestañas de la barra de tareas calculan su tinta por
brillo del fondo (ITU-R BT.601); la pestaña crema del desalojo por
memoria, que llevaba texto blanco invisible, ahora luce su nombre
en tinta oscura.
Verificado en QEMU con `-audiodev wav -machine pcspk-audiodev=spk`:
el PCM crudo trae, en orden, el acorde de bienvenida (~520, 630, 760
Hz), un brevísimo 180 Hz (las balizas de discola/glotona desalojadas)
y después la escala de Do mayor de tonada.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Las ventanas eran anónimas: el escritorio no sabía nombrar lo que
mostraba. Esta fase le pone un nombre a cada cuarto y una barra al
pie con la lista de quienes lo habitan.
- Cada `Ventana` lleva un `nombre: String` —del manifiesto, o del
orquestador al engendrarla en vivo—. `Plantilla` lo guarda para las
copias que `Alt+N` instancia.
- Franja `FRANJA_TASKBAR=40px` reservada al pie. `area_apps` la
descuenta — las ventanas teselan y flotan sin tapar la barra.
- `consola`: tipos `Taskbar` / `CeldaTaskbar` + métodos `pintar_taskbar`
y `pintar_etiqueta` (rasteriza una cadena en (x, base_y) sobre un
fondo conocido, sin tocar la pluma). La pestaña enfocada se pinta con
el índigo del foco, las desalojadas con su color de baliza, el resto
con el slate del panel.
- `compositor::recomponer` arma las celdas y las pasa junto a las capas
a la consola; un único repintado, una única presentación.
- `atender_raton`: si el clic cae en la franja de la barra,
`celda_taskbar_en` localiza la pestaña pulsada y la enfoca (sin
iniciar arrastre).
Verificado en QEMU: al arrancar, la barra al pie muestra las 7
pestañas con sus nombres; `tonada` enfocada en índigo, `discola` y
`glotona` en sus colores de baliza. Un clic sobre `pulso` traslada el
foco al instante — el borde del compositor envuelve `pulso` y su
pestaña se ilumina.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Sin systemd ni NetworkManager, arje-zero quedaba sin red y sin sshd:
útil como bare init, inútil para sacar logs de una VPS sin pegado en
la consola web. Dos Cards nuevas en el seed prod:
- `net-up`: corre `/usr/sbin/arje-net-up` (script nuevo en scripts/),
que pone up todas las interfaces y arranca `dhclient -d` en
foreground sobre la primera no-loopback. Fallback a dhcpcd o
busybox-udhcpc si dhclient no está. Crea de paso /run/sshd y
/var/empty/sshd para que sshd no tenga que pelearlos. Restart
supervisión.
- `sshd`: corre `/usr/sbin/sshd -D -e` (foreground + log a stderr).
Usa las host keys que Fedora ya tenía. Restart supervisión.
El install script copia arje-net-up.sh a /usr/sbin/arje-net-up.
Prerequisito en el host (no automatizable desde acá): si la VPS no
tiene un cliente DHCP (Fedora Cloud trae sólo NetworkManager por
defecto), el script duerme con el link up y no obtiene IPv4. En ese
caso instalar antes del próximo boot: `dnf install dhcp-client`.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
El `-global pci-hole64-size=0` del commit anterior NO movía los BAR:
verifiqué con `info pci` que OVMF seguía alojando el BAR4 prefetchable
64-bit del virtio-blk en `0xc000000000` (mi Proxmox) o `0x800000000`
(la laptop del usuario). El cargador `bootloader_api` 0.11 mapea la
memoria física pero no extiende su mapeo hasta la ventana PCI de 64
bits, y `KernelHal::mmio_phys_to_virt` devolvía `phys + offset` a
ciegas — un puntero a memoria sin tabla de páginas, al primer registro
MMIO leído → #PF.
La solución: un mapeador MMIO propio del kernel.
- `memory::mmio`: envuelve la tabla L4 activa (vía CR3 + el mapeo de
memoria física del cargador) en un `OffsetPageTable`. Su función
`mapear(fisica, tam)` abre, para cada página de la región, una
entrada en la L4 con `PRESENT | WRITABLE | NO_CACHE | WRITE_THROUGH`
— las banderas habituales del MMIO.
- Los marcos para tablas intermedias salen del banco DMA del disco
(`drivers::disco::asignar_marco_para_tabla`, sin pánico). Se ponen
a cero antes de cederlos: las tablas empiezan vacías.
- Tratamos `PageAlreadyMapped` y `ParentEntryHugePage` como éxito: la
región ya estaba cubierta por el cargador (con páginas 4 KiB o
hugepages 2 MiB / 1 GiB) y el acceso ya funciona. Solo abortamos el
mapeo si se nos agota la arena DMA.
- `KernelHal::mmio_phys_to_virt` llama a `memory::mmio::mapear` antes
de devolver el puntero virtual. virtio-drivers lo invoca con la
base y el tamaño exactos de cada BAR; el kernel asegura que cada
uno sea accesible antes de devolverlo.
- `kernel_main` funda el mapeador justo después del heap (paso 4.5),
antes del disco. Necesita `physical_memory_offset` para alcanzar
la L4 activa.
Quito el `-global q35-pcihost.pci-hole64-size=0` que añadí antes: no
movía los BAR (verificado con `info pci`) y solo confundía la
descripción del fix. Esta solución es la robusta: el kernel sabe
mapear sus propios MMIOs y deja de depender del firmware.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Síntoma: el screenshot del usuario en la VPS Hetzner mostraba systemd
booteando y se quedaba congelado en el último printk del kernel justo
antes del switch-root. arje-zero arrancaba bien pero su salida iba al
serial invisible.
Causa: el cmdline traía `console=tty1 console=ttyS0,115200` — y el
kernel hace que `/dev/console` apunte al ÚLTIMO `console=`, así toda la
salida de stdout/stderr de arje-zero (tracing + banner de la rescue
shell) caía en ttyS0 (serial), no en la VGA que muestra noVNC.
Dos arreglos:
- Orden de consolas invertido en el menuentry → `/dev/console` = tty1
(lo que efectivamente se ve en la consola web del proveedor).
- arje-zero también escribe a `/dev/kmsg` (ring buffer del kernel), que
el kernel hace eco a TODAS las consolas registradas — el mecanismo
que usa systemd para que sus mensajes salgan tanto en VGA como en
serial. Defense in depth: el banner de rescue y un eco temprano
«despierta como PID 1» aparecen sí o sí en cualquier consola.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Esta era la verdadera causa del «pantalla negra + franja roja» en máquinas
distintas a la del autor. El trace de boot del usuario lo cantó:
boot :: disco :: init <- llegamos aquí
disco :: init offset=0x20000000000 region=[...] base=...
boot :: almacen :: init <- y aquí
*** renaser :: panico ***
EXCEPCION FATAL :: fallo de pagina (#PF) en 0x20800000014
0x20800000014 - phys_offset(0x20000000000) = 0x800000014 (= 32 GiB + 0x14)
Un acceso vía el mapeo de memoria física a phys=32 GiB. Eso es el BAR
MMIO del virtio-blk: OVMF en QEMU q35 moderno con KVM aloja los BARs
prefetchables 64-bit en el «pci hole» de 64 bits (típicamente a partir
de 32 GiB). El `bootloader` 0.11 con `Mapping::Dynamic` mapea la RAM
del sistema, pero no extiende el mapeo hasta los BARs ahí arriba.
KernelHal::mmio_phys_to_virt devolvía `phys + offset` sin verificar
nada — el host esperaba que el BAR estuviese en los primeros 4 GiB,
como mi Proxmox con TCG y mi nightly. En la laptop con KVM y el OVMF
del usuario, OVMF lo subía y todo reventaba al leer el primer registro.
El parche: `-global q35-pcihost.pci-hole64-size=0` en los args por
defecto de QEMU. Apaga la ventana PCI de 64 bits, OVMF se ve forzada
a alojar todos los BARs en los primeros 4 GiB y el mapeo del cargador
los cubre. Verificado: arranca limpio en Proxmox y debería arrancar
también en la laptop del usuario.
(Las verificaciones unchecked_mul del commit anterior eran una pista
falsa — eran solo donde caía la IP del último build; el fallo de
escritura siempre fue el mismo BAR sin mapear.)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Era la causa raíz del «pantalla negra + franja roja» en máquinas con
nightlies recientes (laptops, KVM, etc.) que mi Proxmox no exhibía:
- En modo debug, la stdlib inyecta `<usize>::unchecked_mul::precondition_check`
en cada `unchecked_mul` (RawVec::current_memory, in_place_collect…).
- El camino de pánico de ese check, en bare-metal, escribe en regiones
que el cargador no mapeó → #PF → al panic handler del kernel, que pinta
la franja roja: un colapso DENTRO de la red de seguridad del colapso.
- 1202 callsites en el binario debug — uno fallaba en la laptop del usuario.
El fix: `rustflags = ["-Cdebug-assertions=off", "-Coverflow-checks=off"]`
en `[target.x86_64-unknown-none]` de `.cargo/config.toml`. Los `[profile]`
del manifiesto no propagaban a las deps precompiladas (wasmi, virtio-
drivers, etc., que se quedan como artifact-deps fuera del workspace);
`rustflags` por target sí. Tras un `cargo clean` + rebuild, cero llamadas
a precondition_check, y el boot trace por COM1 corre completo.
También: `[profile.dev]`/`[profile.release]` del kernel y workspace
declaran los flags explícitamente, por si alguna ruta de cargo cambia.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Desde Fedora 34 / RHEL 9, /boot/efi/EFI/<distro>/grub.cfg es un wrapper
que sourcea /boot/grub2/grub.cfg — y grub2-mkconfig se niega a
sobreescribirlo ("will overwrite the GRUB wrapper. Please run [...] on
/boot/grub2/grub.cfg instead"). Mi loop de detección lo encontraba
primero y fallaba. Apuntamos directo al canónico.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Para localizar dónde colapsa el kernel en máquinas que no son la del
autor, cada hito de `kernel_main` deja una traza por COM1 (con el
panic-handler-a-serie de antes, ya tenemos boot trace + autopsia).
- `baliza::Serie` se hace `pub(crate)` para que cualquier módulo deje
trazas con `writeln!(baliza::Serie, ...)`.
- `kernel_main`: traza tras adoptar el framebuffer, encender la baliza,
fundar GDT/IDT/PIC, fundar el heap, fundar teclado/reloj/texto,
publicar la consola, iniciar disco y almacén, arrancar el ratón,
crear el ejecutor, cargar el userspace y arrancar el reactor. Y un
volcado de `physical_memory_offset` + `region_dma` al inicio.
- `drivers::disco::init`: registra offset, región, base de la arena y
número de marcos disponibles.
- Endurecimiento: `disco::init` ahora salta SIEMPRE la primera página
física al elegir la base de la arena DMA. Algunos cargadores la dejan
sin mapear como protección NULL; un marco DMA ahí se traduce a una
dirección que peta al desreferenciar.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>