tawasuyu

tawasuyu/brahman

Rust 332TOML 127JSON 17Markdown 154710 Definitions

⎇ main renaser/kernel/src/memory/mmio.rs Rust · 134 lines · 6.5 KB History Who touched what raw

fix(renaser): mapeador MMIO en el kernel — la causa real del colapso 8fcc4dc06 · sergio ·

1// =============================================================================
2//  renaser :: kernel/src/memory/mmio.rs — el mapeador de regiones MMIO
3// -----------------------------------------------------------------------------
4//  El cargador `bootloader_api` mapea la memoria fisica de la maquina, pero la
5//  «ventana PCI de 64 bits» —donde OVMF aloja los BAR prefetchables de virtio
6//  en QEMU q35 modernos— suele caer FUERA de ese mapeo: phys 32 GiB, 768 GiB o
7//  mas, segun la fase de la luna. Sin mapeo, leer el primer registro del disco
8//  era un #PF inmediato; con el, el kernel puede hablar con el dispositivo.
9//
10//  Este modulo abre paginas en la tabla L4 que el cargador nos cedio. Reutiliza
11//  como asignador de marcos el de DMA del disco (`drivers::disco`): los marcos
12//  para las tablas intermedias salen del mismo banco que los buferes virtio.
13//  No mapea de mas: solo lo que se le pide, por paginas.
14// =============================================================================
15
16use core::sync::atomic::{AtomicU64, Ordering};
17
18use spin::{Mutex, Once};
19
20use x86_64::registers::control::Cr3;
21use x86_64::structures::paging::mapper::{MapToError, Mapper};
22use x86_64::structures::paging::{
23    FrameAllocator, OffsetPageTable, Page, PageTable, PageTableFlags, PhysFrame, Size4KiB,
24};
25use x86_64::{PhysAddr, VirtAddr};
26
27/// Desplazamiento al que el cargador mapeo la memoria fisica. Lo necesitamos
28/// tanto para alcanzar la tabla L4 actual como para traducir la direccion
29/// fisica del BAR a su virtual de destino.
30static OFFSET_FISICO: AtomicU64 = AtomicU64::new(0);
31
32/// El mapeador del kernel: una vista mutable de la tabla L4 actual envuelta en
33/// el truco de `OffsetPageTable`, que lo asume todo accesible via el mapeo de
34/// memoria fisica que el cargador ya dejo activo. Se funda en el arranque, una
35/// sola vez, antes de cualquier llamada a `mapear`.
36static MAPEADOR: Once<Mutex<OffsetPageTable<'static>>> = Once::new();
37
38/// Funda el mapeador: localiza la tabla L4 activa (via CR3) y la envuelve en un
39/// `OffsetPageTable` que aprovecha el mapeo de memoria fisica del cargador. A
40/// partir de aqui `mapear` puede abrir paginas nuevas en la tabla.
41pub fn init(offset_fisico: u64) {
42    OFFSET_FISICO.store(offset_fisico, Ordering::Relaxed);
43    let (l4_frame, _) = Cr3::read();
44    let l4_virt = l4_frame.start_address().as_u64() + offset_fisico;
45    // SEGURIDAD: el cargador mapeo toda la RAM fisica (incluida la tabla L4
46    // activa) en `l4_phys + offset_fisico`. Esa direccion es valida, esta
47    // alineada a pagina y la tabla vive lo que vive el kernel. La tomamos como
48    // referencia mutable porque renaser es de un solo nucleo y todo acceso al
49    // mapeador queda serializado tras el `Mutex` de `MAPEADOR`.
50    let l4: &'static mut PageTable = unsafe { &mut *(l4_virt as *mut PageTable) };
51    let mapeador = unsafe { OffsetPageTable::new(l4, VirtAddr::new(offset_fisico)) };
52    MAPEADOR.call_once(|| Mutex::new(mapeador));
53}
54
55/// Asigna marcos para las tablas de paginas intermedias. Toma del banco DMA
56/// del disco —no hay heap aqui— y deja cada marco a CEROS, como una tabla
57/// vacia exige. Si el banco esta exhausto, devuelve `None` y el mapeo falla
58/// en lugar de incendiar el kernel.
59struct Marcos;
60
61unsafe impl FrameAllocator<Size4KiB> for Marcos {
62    fn allocate_frame(&mut self) -> Option<PhysFrame<Size4KiB>> {
63        let fisica = crate::drivers::disco::asignar_marco_para_tabla()?;
64        let offset = OFFSET_FISICO.load(Ordering::Relaxed);
65        // SEGURIDAD: `asignar_marco_para_tabla` entrego un marco exclusivo y
66        // mapeado a `fisica + offset` por el cargador. Las tablas de paginas
67        // exigen empezar a cero — si no, la CPU las leeria como llenas de
68        // basura— asi que lo limpiamos antes de cederlo.
69        unsafe {
70            core::ptr::write_bytes((fisica + offset) as *mut u8, 0, 4096);
71        }
72        Some(PhysFrame::containing_address(PhysAddr::new(fisica)))
73    }
74}
75
76/// Abre en la tabla L4 las paginas que cubren la region MMIO [fisica, fisica +
77/// tam), de modo que cada pagina sea accesible en `fisica + offset_fisico` con
78/// las banderas habituales del MMIO (escribible, sin cache). Si la pagina ya
79/// estaba mapeada, se respeta la entrada existente sin gritar — un BAR puede
80/// solaparse con regiones que el cargador ya cubrio—. Si el mapeo falla en
81/// medio, registramos el problema en COM1 y seguimos: que la app que llame al
82/// MMIO se entere por su propio fallo.
83pub fn mapear(fisica: u64, tam: usize) {
84    use core::fmt::Write;
85
86    let Some(mapeador) = MAPEADOR.get() else {
87        return;
88    };
89    if tam == 0 {
90        return;
91    }
92    let offset = OFFSET_FISICO.load(Ordering::Relaxed);
93    let virt_inicio = fisica + offset;
94    let virt_fin = virt_inicio + tam as u64 - 1;
95
96    let pagina_inicio = Page::<Size4KiB>::containing_address(VirtAddr::new(virt_inicio));
97    let pagina_fin = Page::<Size4KiB>::containing_address(VirtAddr::new(virt_fin));
98    let frame_inicio = PhysFrame::<Size4KiB>::containing_address(PhysAddr::new(fisica));
99
100    let banderas = PageTableFlags::PRESENT
101        | PageTableFlags::WRITABLE
102        | PageTableFlags::NO_CACHE
103        | PageTableFlags::WRITE_THROUGH;
104
105    let mut mapeador = mapeador.lock();
106    let mut frame = frame_inicio;
107    let mut pagina = pagina_inicio;
108    while pagina <= pagina_fin {
109        // SEGURIDAD: cada par (pagina, frame) describe un mapeo MMIO honesto:
110        // la pagina virtual `fisica + offset` apunta a la pagina fisica
111        // `fisica` que el dispositivo posee. El `Marcos` cede marcos limpios
112        // para las tablas intermedias.
113        let resultado = unsafe { mapeador.map_to(pagina, frame, banderas, &mut Marcos) };
114        match resultado {
115            Ok(flush) => flush.flush(),
116            // El cargador ya habia mapeado la region como una pagina 4 KiB
117            // (PageAlreadyMapped) o como una pagina huge — 2 MiB / 1 GiB —
118            // (ParentEntryHugePage). En ambos casos el acceso ya funciona; no
119            // hay nada que añadir y SEGUIMOS sin detenernos. Solo abortamos si
120            // se nos agotan los marcos para una tabla intermedia nueva.
121            Err(MapToError::PageAlreadyMapped(_)) | Err(MapToError::ParentEntryHugePage) => {}
122            Err(MapToError::FrameAllocationFailed) => {
123                let _ = writeln!(
124                    crate::baliza::Serie,
125                    "mmio :: sin marcos para la tabla — mapeo incompleto en {:#x}",
126                    pagina.start_address().as_u64(),
127                );
128                return;
129            }
130        }
131        pagina += 1;
132        frame += 1;
133    }
134}