diag(renaser): trazado por serie del arranque + DMA salta la página 0

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>
This commit is contained in:
sergio
2026-05-23 00:07:07 +00:00
parent d85bb3819e
commit 89117f47cc
3 changed files with 47 additions and 3 deletions
+4 -2
View File
@@ -43,8 +43,10 @@ fn serie_escribir(byte: u8) {
}
}
/// Sumidero de impresion al puerto serie — formatea sin tocar el heap.
struct Serie;
/// Sumidero de impresion al puerto serie — formatea sin tocar el heap. Publico
/// para que cualquier modulo del kernel pueda dejar trazas en COM1 con un
/// simple `writeln!(crate::baliza::Serie, "...", ...)`.
pub(crate) struct Serie;
impl Write for Serie {
fn write_str(&mut self, s: &str) -> core::fmt::Result {
+14 -1
View File
@@ -131,10 +131,23 @@ static ASIGNADOR: Once<Mutex<AsignadorMarcos>> = Once::new();
/// el asignador de marcos sobre la region de RAM libre que el cargador reporto.
/// Una sola vez, antes de montar el disco.
pub fn init(offset_fisico: u64, region_inicio: u64, region_fin: u64) {
use core::fmt::Write;
OFFSET_FISICO.store(offset_fisico, Ordering::Relaxed);
let base = alinear_arriba(region_inicio, PAGINA);
// Saltar SIEMPRE la primera pagina fisica: algunos cargadores la dejan sin
// mapear como proteccion contra punteros NULL — un marco DMA ahi seria una
// bomba en cuanto el driver lo desreferenciase via el mapeo alto.
let base = alinear_arriba(region_inicio.max(PAGINA), PAGINA);
let disponibles = region_fin.saturating_sub(base) / PAGINA;
let total = (disponibles as usize).min(MAX_MARCOS);
let _ = writeln!(
crate::baliza::Serie,
"disco :: init offset={:#x} region=[{:#x}, {:#x}) base={:#x} marcos={}",
offset_fisico,
region_inicio,
region_fin,
base,
total,
);
ASIGNADOR.call_once(|| {
Mutex::new(AsignadorMarcos {
base,
+29
View File
@@ -65,6 +65,7 @@ mod wasm;
pub(crate) use sync::CeldaSync;
use alloc::vec::Vec;
use core::fmt::Write;
use core::sync::atomic::{AtomicUsize, Ordering};
use async_system::executor::Executor;
@@ -93,6 +94,13 @@ pub(crate) fn detener() -> ! {
}
}
/// Deja una traza por el puerto serie (COM1) — la enruta QEMU a la terminal
/// donde se ejecuto `cargo run`. Diagnostico barato del arranque: cada hito del
/// `kernel_main` deja una linea, asi una caida muestra HASTA DONDE llego.
fn traza(rotulo: &str) {
let _ = writeln!(baliza::Serie, "boot :: {rotulo}");
}
/// FASE 10 :: el molde de una aplicacion para los lanzamientos EN VIVO. Guarda
/// su bytecode —cacheado en RAM al arrancar, para no volver al disco despues—
/// y la geometria y la cuota de memoria con que instanciarla.
@@ -391,6 +399,7 @@ fn informar_almacen() {
// =============================================================================
fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
traza("kernel_main entrado");
// --- 1. Recuperar el framebuffer GOP que el firmware nos confio. ---
let framebuffer = match boot_info.framebuffer.as_mut() {
Some(fb) => fb,
@@ -399,11 +408,18 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
let info: FrameBufferInfo = framebuffer.info();
let formato: PixelFormat = info.pixel_format;
let pantalla = Pantalla::adoptar(framebuffer, info);
traza("framebuffer adoptado");
// Datos para la sonda de disco (Fase 6.1b): el offset al que el cargador
// mapeo la memoria fisica y la mayor region de RAM libre para el DMA.
let offset_fisico = boot_info.physical_memory_offset.into_option();
let region_dma = mayor_region_usable(&boot_info.memory_regions);
let _ = writeln!(
baliza::Serie,
"boot :: physical_memory_offset={:#x?} region_dma={:#x?}",
offset_fisico,
region_dma,
);
// --- 2. Encender la baliza: la red de seguridad visual va primero. ---
BALIZA_PANICO.encender(
@@ -411,20 +427,24 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
codificar(formato, Color::ALERTA),
codificar(formato, Color::OOM),
);
traza("baliza encendida");
// --- 3. Cimientos de fallos e interrupciones (Fases 2.0 y 2.1). ---
gdt::init();
interrupts::init();
pic::init();
traza("gdt + idt + pic");
// --- 4. FASE 3 :: fundar el heap. A partir de aqui, `alloc` esta vivo. ---
memory::init();
traza("heap fundado");
// --- 5. Con el heap activo, fundar lo que depende de el: el canal de
// scancodes, el reloj de fotogramas y la tipografia vectorial. ---
async_system::teclado::init();
async_system::reloj::init();
texto::init();
traza("teclado + reloj + texto");
// --- 6. Construir el lienzo y la consola; pintar el rotulo inicial,
// ya rasterizado por fontdue, y publicar la consola globalmente. ---
@@ -441,6 +461,7 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
consola.escribir("renaser :: fase 6.2 -- E/S de disco asincrona por interrupcion\n");
consola.presentar();
CONSOLA.call_once(|| Mutex::new(consola));
traza("consola publicada");
// --- 6.5. FASE 6.1c :: fundar el subsistema de disco y, sobre el, el grafo
// de objetos: enumerar el bus PCI, montar el transporte virtio-blk,
@@ -448,8 +469,11 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
// contenido. El kernel adquiere, por fin, una memoria que perdura. ---
match (offset_fisico, region_dma) {
(Some(offset), Some((inicio, fin))) => {
traza("disco :: init");
drivers::disco::init(offset, inicio, fin);
traza("almacen :: init");
informar_almacen();
traza("almacen :: listo");
}
_ => {
if let Some(consola) = CONSOLA.get() {
@@ -457,6 +481,7 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
consola.escribir("virtio-blk :: omitido -- memoria fisica sin mapear\n");
consola.presentar();
}
traza("disco :: OMITIDO (sin offset/region)");
}
}
@@ -465,6 +490,7 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
// desenmascara su IRQ12. Desde aqui hay un puntero en pantalla,
// y los clics pueden alcanzar al compositor.
drivers::raton::init(ancho_lienzo, alto_lienzo);
traza("raton :: listo");
// --- 7. FASE 7 :: levantar el reactor y poblar el userspace DESDE EL
// GRAFO. El kernel ya no empotra los modulos WASM: lee el
@@ -478,11 +504,14 @@ fn kernel_main(boot_info: &'static mut BootInfo) -> ! {
// compas de los fotogramas y la IRQ del teclado difundira cada
// scancode a los canales que las apps consultan. ---
let mut ejecutor = Executor::nuevo();
traza("ejecutor :: creado");
cargar_userspace(&mut ejecutor, ancho_lienzo, alto_lienzo);
traza("userspace :: cargado");
// FASE 6.2 :: una tarea mas del reactor — no una app WASM— que sondea el
// disco de forma ASINCRONA: la demostracion de que la IRQ del disco
// conduce la E/S sin detener a las aplicaciones visuales.
ejecutor.spawn(tarea_sonda_disco());
traza("ejecutor :: arrancando reactor");
x86_64::instructions::interrupts::enable();
ejecutor.run();
}