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:
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user