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
+562
View File
@@ -0,0 +1,562 @@
// =============================================================================
// renaser :: kernel/src/drivers/disco.rs — Fase 6.2 :: el disco asincrono
// -----------------------------------------------------------------------------
// La Fase 6.1 hizo hablar al disco; pero lo hacia por SONDEO: el procesador se
// quedaba en un bucle de espera activa vigilando el «used ring» de virtio,
// incapaz de atender nada mas. La Fase 6.2 lo libera. La E/S de bloques pasa a
// ser REACTIVA, guiada por la interrupcion fisica del dispositivo:
//
// * `EsperaDisco` — un `Future` nativo: enviada la peticion, cede la CPU; la
// IRQ del disco lo despertara cuando el bloque este listo.
// * `atender_irq` — el punto al que salta el manejador de la IRQ del disco:
// reconoce la interrupcion en el dispositivo y despierta a quien aguardaba.
// * `bloquear_en` — el puente para los contextos SINCRONOS (el arranque, las
// capacidades WASM): lleva un `Future` de disco hasta su final durmiendo la
// CPU con `hlt` —jamas en espera activa una vez el sistema esta en marcha—.
//
// Subsisten de la Fase 6.1 el asignador de marcos por mapa de bits (con
// liberacion real) y `KernelHal`, el puente DMA hacia `virtio-drivers`.
// =============================================================================
use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
use core::future::Future;
use core::pin::Pin;
use core::ptr::NonNull;
use core::sync::atomic::{AtomicU64, AtomicU8, Ordering};
use core::task::{Context, Poll, Waker};
use spin::{Mutex, Once};
use virtio_drivers::device::blk::{BlkReq, BlkResp, VirtIOBlk, SECTOR_SIZE};
use virtio_drivers::transport::pci::bus::{Command, DeviceFunction, PciRoot};
use virtio_drivers::transport::pci::PciTransport;
use virtio_drivers::{BufferDirection, Hal, PhysAddr};
use x86_64::instructions::interrupts;
use super::pci::CamPuertos;
/// Tamaño de una pagina / marco fisico, en bytes.
const PAGINA: u64 = 4096;
/// Techo de marcos que gestiona el asignador de DMA: 4096 marcos => una arena
/// de 16 MiB. El DMA del disco —colas virtio y buferes rebote— necesita una
/// fraccion minima de eso; el techo solo acota el tamaño del mapa de bits.
const MAX_MARCOS: usize = 4096;
/// Vendor ID de VirtIO; Device IDs de un disco de bloques (transicional/moderno).
const VENDOR_VIRTIO: u16 = 0x1AF4;
const VIRTIO_BLK_IDS: [u16; 2] = [0x1001, 0x1042];
/// El tamaño de un sector, reexportado para el resto del kernel.
pub const TAM_SECTOR: usize = SECTOR_SIZE;
// =============================================================================
// EL OFFSET DE MEMORIA FISICA Y EL ASIGNADOR DE MARCOS
// =============================================================================
/// Desplazamiento al que el cargador mapeo toda la memoria fisica: una
/// direccion fisica `p` es accesible por el kernel en la virtual `p + offset`.
static OFFSET_FISICO: AtomicU64 = AtomicU64::new(0);
/// Asignador de marcos por MAPA DE BITS: gestiona una arena de RAM fisica
/// contigua y LIBERA. Cada bit representa un marco de 4 KiB; `1` es ocupado,
/// `0` libre. Un almacen de objetos vivo asigna y devuelve marcos sin descanso.
struct AsignadorMarcos {
/// Direccion fisica del primer marco gestionado, alineada a pagina.
base: u64,
/// Numero de marcos que abarca la arena.
total: usize,
/// Mapa de ocupacion: un bit por marco.
mapa: Vec<u64>,
}
impl AsignadorMarcos {
/// ¿Esta libre el marco `i`?
fn libre(&self, i: usize) -> bool {
(self.mapa[i / 64] >> (i % 64)) & 1 == 0
}
/// Marca el marco `i` como ocupado.
fn ocupar(&mut self, i: usize) {
self.mapa[i / 64] |= 1 << (i % 64);
}
/// Marca el marco `i` como libre.
fn soltar(&mut self, i: usize) {
self.mapa[i / 64] &= !(1u64 << (i % 64));
}
/// Reserva `paginas` marcos CONTIGUOS y devuelve la direccion fisica del
/// primero. `None` si no hay un hueco contiguo bastante grande.
fn asignar(&mut self, paginas: usize) -> Option<u64> {
if paginas == 0 || paginas > self.total {
return None;
}
let mut i = 0;
while i + paginas <= self.total {
// Buscar `paginas` marcos libres consecutivos a partir de `i`.
match (i..i + paginas).find(|&k| !self.libre(k)) {
// Un marco ocupado rompe la racha: reanudar tras el.
Some(ocupado) => i = ocupado + 1,
// Racha completa: ocuparla y entregar su direccion fisica.
None => {
for k in i..i + paginas {
self.ocupar(k);
}
return Some(self.base + (i as u64) * PAGINA);
}
}
}
None
}
/// Devuelve a la arena `paginas` marcos que arrancan en la direccion
/// fisica `fisica`. Direcciones ajenas a la arena se ignoran sin daño.
fn liberar(&mut self, fisica: u64, paginas: usize) {
if fisica < self.base {
return;
}
let inicio = ((fisica - self.base) / PAGINA) as usize;
for k in inicio..(inicio + paginas).min(self.total) {
self.soltar(k);
}
}
}
/// El asignador global de marcos para DMA. Se funde en `init`.
static ASIGNADOR: Once<Mutex<AsignadorMarcos>> = Once::new();
/// Funda el subsistema de disco: registra el offset de memoria fisica y forja
/// 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) {
OFFSET_FISICO.store(offset_fisico, Ordering::Relaxed);
let base = alinear_arriba(region_inicio, PAGINA);
let disponibles = region_fin.saturating_sub(base) / PAGINA;
let total = (disponibles as usize).min(MAX_MARCOS);
ASIGNADOR.call_once(|| {
Mutex::new(AsignadorMarcos {
base,
total,
mapa: vec![0u64; total.div_ceil(64)],
})
});
}
/// Redondea `valor` hacia arriba al multiplo de `alineacion` (potencia de dos).
fn alinear_arriba(valor: u64, alineacion: u64) -> u64 {
(valor + alineacion - 1) & !(alineacion - 1)
}
/// Reserva `paginas` marcos fisicos contiguos de 4 KiB y devuelve su direccion
/// fisica. Agotar la arena es un fallo del kernel, no recuperable aqui: el
/// rasgo `Hal` no admite que `dma_alloc` falle.
fn asignar_marcos(paginas: usize) -> u64 {
ASIGNADOR
.get()
.expect("DMA :: el asignador de marcos no esta fundado")
.lock()
.asignar(paginas)
.expect("DMA :: la arena de marcos fisicos se agoto")
}
/// Devuelve `paginas` marcos fisicos a la arena. El reverso de `asignar_marcos`.
fn liberar_marcos(fisica: u64, paginas: usize) {
if let Some(asignador) = ASIGNADOR.get() {
asignador.lock().liberar(fisica, paginas);
}
}
/// Traduce una direccion fisica a la virtual que el kernel puede desreferenciar.
fn a_virtual(fisica: u64) -> *mut u8 {
(fisica + OFFSET_FISICO.load(Ordering::Relaxed)) as *mut u8
}
// =============================================================================
// KernelHal — EL PUENTE ENTRE `virtio-drivers` Y LA MEMORIA DE renaser
// =============================================================================
/// La implementacion del rasgo `Hal` de `virtio-drivers`. Sin estado propio:
/// se apoya en el asignador de marcos y el offset fisico, ambos globales.
pub struct KernelHal;
// SEGURIDAD: cada metodo respeta su contrato — `dma_alloc` entrega marcos
// fisicos exclusivos, contiguos, alineados a pagina y a cero; `dma_dealloc` y
// `unshare` los devuelven a la arena; las traducciones de direccion son validas
// porque el cargador mapeo toda la memoria fisica.
unsafe impl Hal for KernelHal {
fn dma_alloc(paginas: usize, _direccion: BufferDirection) -> (PhysAddr, NonNull<u8>) {
let fisica = asignar_marcos(paginas);
let virtual_ = a_virtual(fisica);
// SEGURIDAD: `asignar_marcos` entrego `paginas` marcos exclusivos y
// contiguos; `virtual_` es su imagen valida y escribible en el mapeo
// de memoria fisica. El rasgo exige que las paginas vayan a cero.
unsafe {
core::ptr::write_bytes(virtual_, 0, paginas * PAGINA as usize);
}
(fisica, NonNull::new(virtual_).expect("DMA :: marco fisico nulo"))
}
unsafe fn dma_dealloc(fisica: PhysAddr, _virtual_: NonNull<u8>, paginas: usize) -> i32 {
// Con un asignador real, la liberacion ya NO es un gesto vacio: los
// marcos vuelven a la arena y quedan disponibles para el proximo DMA.
liberar_marcos(fisica, paginas);
0
}
unsafe fn mmio_phys_to_virt(fisica: PhysAddr, _tam: usize) -> NonNull<u8> {
// El cargador mapeo, como minimo, los primeros 4 GiB de memoria fisica;
// todo BAR MMIO de PCI cae dentro y es accesible en `fisica + offset`.
NonNull::new(a_virtual(fisica)).expect("MMIO :: direccion fisica nula")
}
unsafe fn share(bufer: NonNull<[u8]>, direccion: BufferDirection) -> PhysAddr {
let longitud = bufer.len();
let paginas = longitud.div_ceil(PAGINA as usize);
let fisica = asignar_marcos(paginas);
// Si el bufer viaja HACIA el dispositivo, copiarlo al area DMA rebote.
if !matches!(direccion, BufferDirection::DeviceToDriver) {
// SEGURIDAD: el rasgo garantiza que `bufer` apunta a `longitud`
// bytes validos; el area DMA recien reservada los abarca de sobra.
unsafe {
core::ptr::copy_nonoverlapping(
bufer.as_ptr() as *const u8,
a_virtual(fisica),
longitud,
);
}
}
fisica
}
unsafe fn unshare(fisica: PhysAddr, bufer: NonNull<[u8]>, direccion: BufferDirection) {
let longitud = bufer.len();
// Si el bufer viene DESDE el dispositivo, copiar el area DMA de vuelta.
if !matches!(direccion, BufferDirection::DriverToDevice) {
// SEGURIDAD: `fisica` lo entrego `share` para este mismo `bufer`;
// ambas regiones abarcan los `longitud` bytes que se copian.
unsafe {
core::ptr::copy_nonoverlapping(
a_virtual(fisica),
bufer.as_ptr() as *mut u8,
longitud,
);
}
}
// Devolver a la arena los marcos del area rebote.
liberar_marcos(fisica, longitud.div_ceil(PAGINA as usize));
}
}
// =============================================================================
// EL DISCO PERSISTENTE
// =============================================================================
/// El disco virtio-blk, ya montado. Envuelve al `VirtIOBlk` para poder ligarlo
/// a un `static`.
struct Disco(VirtIOBlk<KernelHal, PciTransport>);
// SEGURIDAD: `Disco` encierra punteros crudos a las colas virtio y al MMIO del
// dispositivo. renaser es un kernel de un solo nucleo y todo acceso al disco se
// serializa tras el `Mutex` global `DISCO`; jamas se comparte entre hilos
// reales. Todo acceso normal toma el cerrojo con las interrupciones acalladas,
// de modo que la IRQ del disco jamas lo encuentra ocupado.
unsafe impl Send for Disco {}
/// El disco global de renaser. Se monta una sola vez, en `montar`.
static DISCO: Once<Mutex<Disco>> = Once::new();
/// La linea de IRQ del disco, descubierta al montarlo. Vale 0 si el firmware no
/// enruto una linea util: en ese caso la E/S recae en el sondeo, con la red de
/// seguridad del temporizador.
static IRQ_DISCO: AtomicU8 = AtomicU8::new(0);
/// Cuenta de interrupciones del disco atendidas desde el arranque. Es el
/// testigo vivo de que la E/S asincrona late de verdad.
static PULSOS_DISCO: AtomicU64 = AtomicU64::new(0);
/// El waker de la (unica) espera de disco en curso. Las operaciones de disco se
/// serializan, de modo que una sola ranura basta. La IRQ del disco lo invoca.
static WAKER_DISCO: Mutex<Option<Waker>> = Mutex::new(None);
/// Enumera el bus PCI, localiza el disco virtio-blk, lo monta y lo deja tras el
/// `Mutex` global. Descubre ademas su linea de IRQ, registra el manejador y
/// abre la linea en el PIC: desde aqui el disco puede interrumpir. Devuelve la
/// capacidad del disco en sectores. Toda falla se devuelve como `Err`.
pub fn montar() -> Result<u64, &'static str> {
let mut raiz = PciRoot::new(CamPuertos);
// 1. Localizar el primer disco virtio-blk recorriendo el bus.
let mut hallado: Option<DeviceFunction> = None;
'busqueda: for bus in 0..=255u8 {
for (device_function, info) in raiz.enumerate_bus(bus) {
if info.vendor_id == VENDOR_VIRTIO && VIRTIO_BLK_IDS.contains(&info.device_id) {
hallado = Some(device_function);
break 'busqueda;
}
}
}
let device_function = hallado.ok_or("virtio-blk no hallado en el bus PCI")?;
// 2. Habilitar E/S, espacio de memoria y BUS-MASTER. Sin bus-master el
// dispositivo no puede iniciar transferencias DMA por su cuenta.
raiz.set_command(
device_function,
Command::IO_SPACE | Command::MEMORY_SPACE | Command::BUS_MASTER,
);
// 3. Montar el transporte PCI moderno de virtio y el dispositivo de bloques.
let transporte = PciTransport::new::<KernelHal, _>(&mut raiz, device_function)
.map_err(|_| "no se pudo montar el transporte PCI de virtio")?;
let mut disco = VirtIOBlk::<KernelHal, _>::new(transporte)
.map_err(|_| "no se pudo inicializar el dispositivo virtio-blk")?;
let capacidad = disco.capacity();
// 4. Pedir al dispositivo que EMITA una interrupcion al completar cada
// peticion — el corazon de la E/S asincrona de esta fase.
disco.enable_interrupts();
DISCO.call_once(|| Mutex::new(Disco(disco)));
// 5. Descubrir la linea de IRQ que el firmware asigno al dispositivo y
// enrutarla: registrar el manejador en la IDT y abrir la linea en el
// PIC. Las IRQ 0 y 1 son del temporizador y el teclado; un valor fuera
// de 2..15 (p. ej. 0xFF, «sin conexion») significa que no hay linea
// util — la E/S seguira funcionando, pero por sondeo.
let irq = super::pci::linea_irq(device_function);
if (2..=15).contains(&irq) {
crate::interrupts::registrar_irq_disco(irq);
crate::pic::desenmascarar(irq);
IRQ_DISCO.store(irq, Ordering::SeqCst);
}
Ok(capacidad)
}
/// La linea de IRQ del disco, si el firmware enruto una util.
pub fn irq() -> Option<u8> {
match IRQ_DISCO.load(Ordering::SeqCst) {
0 => None,
n => Some(n),
}
}
/// Numero de interrupciones del disco atendidas desde el arranque.
pub fn pulsos_disco() -> u64 {
PULSOS_DISCO.load(Ordering::Relaxed)
}
/// Punto de entrada DESDE el manejador de la IRQ del disco. Reconoce la
/// interrupcion en el dispositivo —leer su registro ISR baja la linea INTx— y
/// despierta a la tarea que aguardaba el bloque. Breve y libre de panicos:
/// corre en contexto de interrupcion.
pub fn atender_irq() {
// Estamos en contexto de interrupcion (IF=0). Todo acceso normal a `DISCO`
// toma su cerrojo con las interrupciones acalladas, de modo que aqui jamas
// esta ocupado: tomarlo no puede interbloquear.
if let Some(disco) = DISCO.get() {
let _ = disco.lock().0.ack_interrupt();
}
PULSOS_DISCO.fetch_add(1, Ordering::Relaxed);
if let Some(waker) = WAKER_DISCO.lock().take() {
waker.wake();
}
}
/// Inscribe el waker de la espera de disco en curso. Una sola ranura: las
/// operaciones de disco estan serializadas. El cerrojo lo disputa el manejador
/// de IRQ — tomarlo con las interrupciones acalladas hace imposible el bloqueo.
fn registrar_waker(waker: Waker) {
interrupts::without_interrupts(|| *WAKER_DISCO.lock() = Some(waker));
}
// =============================================================================
// EsperaDisco — UNA OPERACION DE BLOQUE COMO `Future`
// =============================================================================
/// Una transferencia de bloques en vuelo, expresada como `Future`. Posee sus
/// propios buferes DMA —el encabezado de peticion, la respuesta y los datos—,
/// que `virtio-drivers` mantiene prestados hasta que la operacion concluye.
///
/// Su `poll` envia la peticion la primera vez y, despues, comprueba el «used
/// ring»: si el dispositivo aun no ha terminado, inscribe el waker y cede; la
/// IRQ del disco lo reanudara. Una operacion a la vez — basta para renaser.
pub struct EsperaDisco {
/// Encabezado de la peticion virtio. En el heap: direccion estable mientras
/// el dispositivo lo tiene prestado, sin importar si el `Future` se mueve.
req: Box<BlkReq>,
/// Respuesta de estado del dispositivo. En el heap, por la misma razon.
resp: Box<BlkResp>,
/// Los datos: destino de una lectura, origen de una escritura.
buf: Vec<u8>,
/// Primer sector de la transferencia.
sector: u64,
/// `true` si la operacion escribe; `false` si lee.
es_escritura: bool,
/// Token que `virtio-drivers` devolvio al enviar la peticion. `None` hasta
/// que el primer `poll` la envia.
token: Option<u16>,
}
impl EsperaDisco {
/// Hace avanzar la operacion: la envia si aun no lo estaba y comprueba si el
/// dispositivo la completo. `None` => sigue en vuelo; `Some` => terminada.
fn avanzar(&mut self) -> Option<Result<Vec<u8>, &'static str>> {
// Todo el dialogo con el dispositivo, con las interrupciones acalladas:
// asi la IRQ del disco jamas encuentra ocupado el cerrojo de `DISCO`.
interrupts::without_interrupts(|| {
let disco = match DISCO.get() {
Some(disco) => disco,
None => return Some(Err("disco no montado")),
};
let mut guardia = disco.lock();
let blk = &mut guardia.0;
// 1. Enviar la peticion la primera vez que se sondea esta espera.
if self.token.is_none() {
// SEGURIDAD: `req`, `buf` y `resp` viven en esta `EsperaDisco`,
// que no se libera ni se accede por otra via hasta que el
// `complete_*` de mas abajo cierre la operacion.
let envio = unsafe {
if self.es_escritura {
blk.write_blocks_nb(
self.sector as usize,
&mut self.req,
self.buf.as_slice(),
&mut self.resp,
)
} else {
blk.read_blocks_nb(
self.sector as usize,
&mut self.req,
self.buf.as_mut_slice(),
&mut self.resp,
)
}
};
match envio {
Ok(token) => self.token = Some(token),
Err(_) => return Some(Err("no se pudo enviar la peticion al disco")),
}
}
let token = self.token.unwrap();
// 2. ¿Ha colocado el dispositivo este token en el «used ring»?
if blk.peek_used() != Some(token) {
return None; // aun en vuelo
}
// 3. Completado: recoger el resultado y desligar los buferes DMA.
// SEGURIDAD: se pasan los MISMOS buferes que recibio el `*_nb`.
let fin = unsafe {
if self.es_escritura {
blk.complete_write_blocks(token, &self.req, self.buf.as_slice(), &mut self.resp)
} else {
blk.complete_read_blocks(
token,
&self.req,
self.buf.as_mut_slice(),
&mut self.resp,
)
}
};
Some(match fin {
Ok(()) => Ok(core::mem::take(&mut self.buf)),
Err(_) => Err("la operacion de disco no se completo"),
})
})
}
}
impl Future for EsperaDisco {
/// Al terminar, una lectura entrega sus datos; una escritura, un vector
/// vacio. El error es siempre un mensaje estable.
type Output = Result<Vec<u8>, &'static str>;
fn poll(self: Pin<&mut Self>, contexto: &mut Context<'_>) -> Poll<Self::Output> {
// `EsperaDisco` es `Unpin` —solo `Box`, `Vec` y escalares—: el `Pin`
// no impone nada y el acceso mutable es directo.
let this = self.get_mut();
// Inscribir el waker ANTES de comprobar: si la IRQ se cuela entre la
// comprobacion y la inscripcion, el waker ya esta puesto y el despertar
// no se pierde — el mismo blindaje que usa `EsperaFrame` (ver `reloj`).
registrar_waker(contexto.waker().clone());
match this.avanzar() {
Some(resultado) => Poll::Ready(resultado),
None => Poll::Pending,
}
}
}
/// Prepara la LECTURA asincrona de `n_sectores` sectores desde `sector`. El
/// `Future` que devuelve no toca el disco hasta que se le sondea.
pub fn leer_bloques(sector: u64, n_sectores: usize) -> EsperaDisco {
EsperaDisco {
req: Box::new(BlkReq::default()),
resp: Box::new(BlkResp::default()),
buf: vec![0u8; n_sectores * TAM_SECTOR],
sector,
es_escritura: false,
token: None,
}
}
/// Prepara la ESCRITURA asincrona de `datos` a partir de `sector`. La longitud
/// de `datos` debe ser multiplo de un sector.
pub fn escribir_bloques(sector: u64, datos: Vec<u8>) -> EsperaDisco {
EsperaDisco {
req: Box::new(BlkReq::default()),
resp: Box::new(BlkResp::default()),
buf: datos,
sector,
es_escritura: true,
token: None,
}
}
// =============================================================================
// bloquear_en — EL PUENTE PARA LOS CONTEXTOS SINCRONOS
// =============================================================================
/// Lleva un `Future` de disco hasta su final desde un contexto SINCRONO — el
/// arranque, o una capacidad WASM, que no pueden `.await`-ear—. Mientras el
/// disco trabaja:
///
/// * si las interrupciones estan activas, duerme la CPU con `hlt`; la
/// despertara la IRQ del disco —o el temporizador, como red de seguridad—;
/// * si no —en el arranque, antes de habilitarlas—, sondea en bucle.
///
/// Asi, una vez el sistema esta en marcha, la espera de disco JAMAS malgasta
/// ciclos en espera activa.
fn bloquear_en<F: Future>(futuro: F) -> F::Output {
let mut futuro = core::pin::pin!(futuro);
let waker = Waker::noop();
let mut contexto = Context::from_waker(waker);
loop {
match futuro.as_mut().poll(&mut contexto) {
Poll::Ready(salida) => return salida,
Poll::Pending => {
if interrupts::are_enabled() {
x86_64::instructions::hlt();
} else {
core::hint::spin_loop();
}
}
}
}
}
/// Lee `buf.len() / 512` sectores consecutivos a partir de `sector`. Sincrono:
/// construido sobre la maquinaria asincrona via `bloquear_en`. El bufer debe
/// medir un multiplo entero de un sector.
pub fn leer_sectores(sector: u64, buf: &mut [u8]) -> Result<(), &'static str> {
let datos = bloquear_en(leer_bloques(sector, buf.len() / TAM_SECTOR))?;
buf.copy_from_slice(&datos);
Ok(())
}
/// Escribe `buf.len() / 512` sectores consecutivos a partir de `sector`.
/// Sincrono, sobre la misma maquinaria asincrona.
pub fn escribir_sectores(sector: u64, buf: &[u8]) -> Result<(), &'static str> {
bloquear_en(escribir_bloques(sector, buf.to_vec())).map(|_| ())
}
+15
View File
@@ -0,0 +1,15 @@
// =============================================================================
// renaser :: kernel/src/drivers — Fase 6.1 :: el puente hacia el hardware real
// -----------------------------------------------------------------------------
// Hasta aqui renaser solo tocaba el silicio que el firmware le servia en
// bandeja: el framebuffer GOP, el temporizador, el teclado. Los `drivers`
// abren la primera via hacia hardware que el kernel debe DESCUBRIR y reclamar
// por si mismo:
//
// * `pci` — acceso al espacio de configuracion del bus PCI (0xCF8/0xCFC).
// * `disco` — el disco virtio-blk: asignador de marcos DMA, `Hal` y la
// lectura, por sondeo, de su primer sector.
// =============================================================================
pub mod disco;
pub mod pci;
+76
View File
@@ -0,0 +1,76 @@
// =============================================================================
// renaser :: kernel/src/drivers/pci.rs — Fase 6.1 :: el acceso al bus PCI
// -----------------------------------------------------------------------------
// El cargador `bootloader` entrega el mapa de memoria y el framebuffer, pero
// NO un censo de perifericos: descubrir el disco es tarea del kernel. Aqui
// renaser habla con el espacio de configuracion del bus PCI a traves del
// mecanismo #1 —los puertos 0xCF8 (direccion) y 0xCFC (datos)—.
//
// Este modulo provee `CamPuertos`, una implementacion del rasgo
// `ConfigurationAccess` de `virtio-drivers`: la crate enumera el bus y mapea
// los BARs del dispositivo apoyandose en estas dos funciones de acceso.
// =============================================================================
use virtio_drivers::transport::pci::bus::{ConfigurationAccess, DeviceFunction};
use x86_64::instructions::port::Port;
/// Puerto de DIRECCION del mecanismo de configuracion PCI #1.
const CONFIG_ADDRESS: u16 = 0xCF8;
/// Puerto de DATOS del mecanismo de configuracion PCI #1.
const CONFIG_DATA: u16 = 0xCFC;
/// Compone la palabra de direccion del mecanismo #1: bit 31 de habilitacion,
/// bus, dispositivo, funcion y offset de registro alineado a dword.
fn direccion(device_function: DeviceFunction, offset: u8) -> u32 {
0x8000_0000
| ((device_function.bus as u32) << 16)
| ((device_function.device as u32) << 11)
| ((device_function.function as u32) << 8)
| ((offset as u32) & 0xFC)
}
/// Lee un registro de 32 bits del espacio de configuracion PCI.
fn leer(device_function: DeviceFunction, offset: u8) -> u32 {
// SEGURIDAD: 0xCF8/0xCFC son los puertos del mecanismo de configuracion
// PCI #1, fijos en la arquitectura PC. La direccion lleva su bit de
// habilitacion y el offset alineado a dword, como exige el protocolo.
unsafe {
Port::<u32>::new(CONFIG_ADDRESS).write(direccion(device_function, offset));
Port::<u32>::new(CONFIG_DATA).read()
}
}
/// Escribe un registro de 32 bits en el espacio de configuracion PCI.
fn escribir(device_function: DeviceFunction, offset: u8, dato: u32) {
// SEGURIDAD: vease `leer` — mismos puertos, mismo protocolo del 8259/PCI.
unsafe {
Port::<u32>::new(CONFIG_ADDRESS).write(direccion(device_function, offset));
Port::<u32>::new(CONFIG_DATA).write(dato);
}
}
/// Lee el registro «Interrupt Line» (byte bajo del offset 0x3C): la linea de
/// IRQ del PIC que el firmware UEFI enruto y asigno a este dispositivo. Es el
/// puente entre el descubrimiento PCI y el manejo de interrupciones (Fase 6.2).
pub fn linea_irq(device_function: DeviceFunction) -> u8 {
(leer(device_function, 0x3C) & 0xFF) as u8
}
/// Acceso al espacio de configuracion PCI por puertos de E/S — el mecanismo #1.
/// Es un tipo sin estado: toda la informacion viaja en cada llamada.
pub struct CamPuertos;
impl ConfigurationAccess for CamPuertos {
fn read_word(&self, device_function: DeviceFunction, register_offset: u8) -> u32 {
leer(device_function, register_offset)
}
fn write_word(&mut self, device_function: DeviceFunction, register_offset: u8, data: u32) {
escribir(device_function, register_offset, data);
}
unsafe fn unsafe_clone(&self) -> Self {
// `CamPuertos` no tiene estado: clonarlo es trivial y sin riesgo.
CamPuertos
}
}