feat: integra renaser (kernel SASOS bare-metal) al monorepo

renaser —kernel asíncrono de espacio de direcciones único, no-POSIX,
`no_std` x86_64— entra al monorepo como su PROPIO workspace de Cargo,
no fusionado: usa toolchain nightly, target `x86_64-unknown-none` y
`panic = "abort"`, incompatibles con los perfiles globales de brahman.

- `renaser/` — copia del proyecto (sin su `.git`; el repo original
  conserva su historia standalone). Workspace propio con su
  `rust-toolchain.toml` y `.cargo/`.
- `exclude = ["renaser"]` en el workspace de brahman: Cargo lo trata
  como ajeno.
- El kernel de renaser path-depende `mirada-layout` cruzando la
  frontera de workspace — primer núcleo compartido. Semilla de la
  Fase 8 (compositor): geometría de teselado compartida, framebuffer
  nativo de renaser; smithay se queda en el lado Linux.

Verificado: `cargo build -p boot` compila kernel + imagen UEFI con
mirada-layout enlazado para bare-metal.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-22 14:37:14 +00:00
parent 1c6aafbc24
commit e2272c0ed3
55 changed files with 6668 additions and 0 deletions
+173
View File
@@ -0,0 +1,173 @@
# renaser — Arquitectura
Este documento describe la arquitectura del kernel `renaser` subsistema a
subsistema. Para el estado por fases, ver `ROADMAP.md`.
## 1. Filosofía
renaser es un **SASOS***Single Address Space Operating System*. No hay un
espacio de direcciones por proceso ni cambios de contexto de hardware: todo el
sistema —kernel y aplicaciones— comparte una única RAM plana.
El aislamiento NO lo da la MMU ni los anillos de privilegio de la CPU. Lo da el
**software**: las aplicaciones se distribuyen como bytecode WebAssembly y se
ejecutan dentro de un intérprete que acota matemáticamente cada acceso a
memoria. Una aplicación solo puede hacer aquello para lo que el kernel le haya
inyectado una *capacidad* (una función de host). Lo que no está importado no
tiene camino físico que recorrer.
La interfaz es **visual desde el primer microsegundo**. No hay TTY; el texto se
rasteriza como gráfico vectorial. "El texto es un caso particular del dibujo."
## 2. La cadena de arranque
```
Firmware UEFI → crate `bootloader` 0.11 → kernel::_start → kernel_main
```
El miembro `boot/` (que corre en el **anfitrión**) toma el ELF del kernel,
lo fusiona con el cargador `bootloader` 0.11 en una imagen de disco UEFI GPT y
la lanza en QEMU con el firmware OVMF. El cargador deja la CPU en modo largo de
64 bits, mapea el kernel y le entrega una `BootInfo` con el framebuffer GOP.
## 3. Estructura del espacio de trabajo
El workspace tiene **un solo miembro**, `boot`. El `kernel` y las `apps` están
**excluidos** a propósito:
- El kernel es `#![no_std] #![no_main]` y define su propio `_start`. Si fuese
miembro del workspace, `cargo build` intentaría compilarlo para el anfitrión
y fallaría por un símbolo `_start` duplicado con el `crt0` del sistema.
- Por eso el kernel se construye **solo** como *dependencia de artefacto*
(RFC 3028) de `boot`, que le fija el target `x86_64-unknown-none`.
- Las apps WASM tienen su propio target (`wasm32-unknown-unknown`) y se
compilan aparte.
El kernel usa el target **nativo precompilado** `x86_64-unknown-none`: sin
target JSON propio, sin `build-std`. Soft-float y sin SSE, para que las
interrupciones no corrompan registros de punto flotante.
## 4. Subsistemas del kernel
### `grafico` — el sustrato visual
`Color` (RGB de 24 bits, independiente del hardware), `Pantalla` (el
framebuffer GOP físico, envuelto en seguridad) y `Lienzo` (el búfer intermedio
en RAM). Toda composición ocurre en el `Lienzo`; `Pantalla::presentar` lo
vuelca de un solo gesto con escrituras volátiles — **doble búfer**, sin
desgarros. `codificar` traduce un `Color` al formato nativo del framebuffer.
### `consola` — la superficie de texto e imagen
Une `Lienzo` + `Pantalla` + una pluma de escritura. Rasteriza glifos con
`fontdue` y los funde sobre el lienzo mezclando por cobertura (anti-aliasing).
También vuelca fotogramas crudos del userspace WASM. Es global, tras un `Mutex`.
### `baliza` — la red de seguridad visual
Publica de forma atómica y sin cerrojos los datos mínimos del framebuffer. Los
manejadores `#[panic_handler]` y `#[alloc_error_handler]` los usan para pintar
una franja de advertencia —**roja** si el sistema colapsa, **naranja** si el
heap se agota— escribiendo directo sobre el silicio, sin confiar en el heap ni
en estructura dinámica alguna.
### `gdt` — cimientos del manejo de fallos
GDT propia con segmentos de código y datos del kernel, y un TSS cuyo único
cometido es alojar un **stack de emergencia** (IST) para el doble fallo: ni un
desbordamiento de la pila del kernel impide su diagnóstico.
### `interrupts` — la tabla de reflejos
IDT con manejadores de excepción de CPU (breakpoint recuperable; el resto,
fatales → `panic!`) e interrupciones de hardware. El doble fallo se atiende
sobre el stack de emergencia del TSS.
### `pic` — el latido del hardware
Remapea el par 8259 (PIC) fuera del rango de las excepciones de CPU y programa
el temporizador de intervalos (PIT). El teclado (IRQ1) deposita scancodes en
una cola lock-free. `desenmascarar` abre una línea de IRQ concreta —la del
disco, descubierta en tiempo de ejecución (Fase 6.2)—.
### `drivers` — el hardware que el kernel conquista (Fases 6.1, 6.2)
A diferencia del framebuffer o el temporizador —que el firmware sirve en
bandeja—, el disco hay que DESCUBRIRLO y reclamarlo.
- `pci``CamPuertos`: acceso al espacio de configuración del bus PCI por el
mecanismo #1 (puertos `0xCF8`/`0xCFC`); `linea_irq` lee la IRQ del dispositivo.
- `disco` — el disco virtio-blk. Un asignador de marcos por **mapa de bits**
—con liberación real— reparte páginas físicas para el DMA; `KernelHal`
implementa el `trait Hal` de `virtio-drivers` (traducción de direcciones,
memoria rebote para el DMA); el `VirtIOBlk` se monta una vez y persiste tras
un `Mutex`. La E/S de bloques es **asíncrona** (Fase 6.2): `EsperaDisco` es un
`Future` que envía la petición por la API no bloqueante de `virtio-drivers`,
cede la CPU y se reanuda con la IRQ del disco (`atender_irq`). `bloquear_en`
conduce ese `Future` desde los contextos síncronos durmiendo la CPU con
`hlt`; sobre él, `leer_sectores`/`escribir_sectores` sirven al grafo de
objetos. La IRQ del disco se enruta por el PIC 8259, no por el IOAPIC.
### `almacen` — el grafo de objetos direccionado por contenido (Fase 6.1c)
renaser no tiene un sistema de archivos plano POSIX: tiene un DAG de objetos.
Un `Objeto` es una carga útil de bytes y una lista de aristas (hashes de
hijos). La identidad de un objeto es el hash BLAKE3 de su forma serializada
(`postcard`) — de ahí integridad (un objeto se verifica al leerlo) y
deduplicación (contenido idéntico, un solo registro). El disco es un **log**:
el sector 0 es el superbloque (magia, versión, cursor, raíz); tras él se anexan
los registros de objetos. Un índice hash→sector se reconstruye al arrancar
recorriendo el log. Las capacidades `sys_object_*` exponen el grafo al
userspace.
### `memory` — el heap dinámico
`linked_list_allocator` como `#[global_allocator]` sobre una región estática de
64 MiB en `.bss`. Desbloquea `alloc::*``Box`, `Vec`, `BTreeMap`, `Arc`.
### `async_system` — el reactor cooperativo
- `task``Task`: un `Future` anclado (`Pin<Box<dyn Future>>`) con `TaskId`.
- `executor` — el `Executor`: censo de tareas, cola de listas, y un `hlt`
controlado cuando no hay trabajo.
- `waker` — un `Waker` que reinyecta el `TaskId` en la cola al despertar.
- `teclado` — los canales de scancodes. Cada app abre el suyo; la IRQ1 **difunde**
cada scancode a todos, de modo que varias apps reciben la entrada en paralelo.
- `reloj` — convierte la IRQ0 (PIT, 100 Hz) en el `Future` `EsperaFrame`, que se
resuelve en el siguiente pulso. Es el **compás de los fotogramas**: una tarea
WASM hace su trabajo de un fotograma y `.await`-ea el siguiente.
Las interrupciones de hardware no conmutan el contexto de la CPU: **despiertan
tareas**. El kernel avanza cooperativamente.
### `texto` — tipografía vectorial
Empotra un `.ttf` en el binario (`include_bytes!`) y lo rasteriza con `fontdue`
glifo a glifo, bajo demanda.
### `wasm` — el escudo de aislamiento
- `mod` — el runtime y la `AplicacionWasm`: una instancia **persistente** entre
fotogramas (`Store` + `TypedFunc<(), ()>` + región). El ABI del userspace es
`init()` (una vez) y `tick()` (un fotograma, y retorna). Dos guardarraíles
acotan a cada app: el **temporal** —un presupuesto de **combustible** (`fuel`)
por `tick`; agotarlo la desaloja (baliza púrpura)— y el **espacial** —un techo
de memoria lineal vía `Store::limiter`; rebasarlo la desaloja (baliza
amarilla)—. En ambos casos `wasmi` lanza una trampa, el kernel recupera el
mando y el sistema no sufre. El `Drop` de `AplicacionWasm` reconcilia el ciclo
de vida: da de baja su canal de teclado.
- `env` — la **matriz de capacidades**: las funciones de host que el módulo
WASM puede invocar. Hoy son siete:
- `sys_render_frame(ptr, len)` — compone un fotograma dentro de la **región**
de pantalla asignada a la app. El kernel valida **matemáticamente** que
`[ptr, ptr+len)` cae dentro de la memoria lineal del módulo, y que el
tamaño es el de la región, antes de leer un byte; si no, aborta la app.
- `sys_get_scancode()` — entrega, sin bloquear, el siguiente scancode del
canal de teclado **propio** de la app.
- `sys_object_put` / `sys_object_datos` / `sys_object_hijo` /
`sys_object_raiz` / `sys_object_fijar_raiz` — el acceso al grafo de objetos
persistente (ver `almacen`): grabar un objeto, leer su carga útil, recorrer
sus aristas, y leer o fijar la raíz del DAG. Cada puntero que la app
entrega se valida contra su memoria lineal, igual que en `sys_render_frame`.
Todo puntero inválido aborta la app —es su culpa: el `Error` se traduce en una
trampa de WASM, la app se desaloja y el resto del sistema sigue—. Un fallo del
almacenamiento, en cambio, no es culpa de la app: se le devuelve un código de
error que ella decide cómo afrontar.
## 5. Restricciones de ingeniería
- **Escrituras volátiles** obligatorias para framebuffer y MMIO.
- **`unsafe`** confinado en células mínimas, cada una con un contrato
`SEGURIDAD:` y envuelta en una abstracción segura. `unsafe_op_in_unsafe_fn`
está en `deny`.
- **Alineación**: 16 bytes para estructuras genéricas; 4096 (página) para
buffers de asignador y el lienzo de respaldo.
- **`no_std` estricto**: `core` + `alloc`; nada de `std`.