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:
@@ -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`.
|
||||
Reference in New Issue
Block a user