⎇ 89117f47cc75246d5e14152b97974366c85c19e9
renaser/kernel/src/drivers/disco.rs
Ñawpa willay
Pi imata llamkarqan
ch'uya
diag(renaser): trazado por serie del arranque + DMA salta la página 0
1// =============================================================================
2// renaser :: kernel/src/drivers/disco.rs — Fase 6.2 :: el disco asincrono
3// -----------------------------------------------------------------------------
4// La Fase 6.1 hizo hablar al disco; pero lo hacia por SONDEO: el procesador se
5// quedaba en un bucle de espera activa vigilando el «used ring» de virtio,
6// incapaz de atender nada mas. La Fase 6.2 lo libera. La E/S de bloques pasa a
7// ser REACTIVA, guiada por la interrupcion fisica del dispositivo:
8//
9// * `EsperaDisco` — un `Future` nativo: enviada la peticion, cede la CPU; la
10// IRQ del disco lo despertara cuando el bloque este listo.
11// * `atender_irq` — el punto al que salta el manejador de la IRQ del disco:
12// reconoce la interrupcion en el dispositivo y despierta a quien aguardaba.
13// * `bloquear_en` — el puente para los contextos SINCRONOS (el arranque, las
14// capacidades WASM): lleva un `Future` de disco hasta su final durmiendo la
15// CPU con `hlt` —jamas en espera activa una vez el sistema esta en marcha—.
16//
17// Subsisten de la Fase 6.1 el asignador de marcos por mapa de bits (con
18// liberacion real) y `KernelHal`, el puente DMA hacia `virtio-drivers`.
19// =============================================================================
20
21use alloc::boxed::Box;
22use alloc::vec;
23use alloc::vec::Vec;
24use core::future::Future;
25use core::pin::Pin;
26use core::ptr::NonNull;
27use core::sync::atomic::{AtomicU64, AtomicU8, Ordering};
28use core::task::{Context, Poll, Waker};
29
30use spin::{Mutex, Once};
31use virtio_drivers::device::blk::{BlkReq, BlkResp, VirtIOBlk, SECTOR_SIZE};
32use virtio_drivers::transport::pci::bus::{Command, DeviceFunction, PciRoot};
33use virtio_drivers::transport::pci::PciTransport;
34use virtio_drivers::{BufferDirection, Hal, PhysAddr};
35use x86_64::instructions::interrupts;
36
37use super::pci::CamPuertos;
38
39/// Tamaño de una pagina / marco fisico, en bytes.
40const PAGINA: u64 = 4096;
41
42/// Techo de marcos que gestiona el asignador de DMA: 4096 marcos => una arena
43/// de 16 MiB. El DMA del disco —colas virtio y buferes rebote— necesita una
44/// fraccion minima de eso; el techo solo acota el tamaño del mapa de bits.
45const MAX_MARCOS: usize = 4096;
46
47/// Vendor ID de VirtIO; Device IDs de un disco de bloques (transicional/moderno).
48const VENDOR_VIRTIO: u16 = 0x1AF4;
49const VIRTIO_BLK_IDS: [u16; 2] = [0x1001, 0x1042];
50
51/// El tamaño de un sector, reexportado para el resto del kernel.
52pub const TAM_SECTOR: usize = SECTOR_SIZE;
53
54// =============================================================================
55// EL OFFSET DE MEMORIA FISICA Y EL ASIGNADOR DE MARCOS
56// =============================================================================
57
58/// Desplazamiento al que el cargador mapeo toda la memoria fisica: una
59/// direccion fisica `p` es accesible por el kernel en la virtual `p + offset`.
60static OFFSET_FISICO: AtomicU64 = AtomicU64::new(0);
61
62/// Asignador de marcos por MAPA DE BITS: gestiona una arena de RAM fisica
63/// contigua y LIBERA. Cada bit representa un marco de 4 KiB; `1` es ocupado,
64/// `0` libre. Un almacen de objetos vivo asigna y devuelve marcos sin descanso.
65struct AsignadorMarcos {
66 /// Direccion fisica del primer marco gestionado, alineada a pagina.
67 base: u64,
68 /// Numero de marcos que abarca la arena.
69 total: usize,
70 /// Mapa de ocupacion: un bit por marco.
71 mapa: Vec<u64>,
72}
73
74impl AsignadorMarcos {
75 /// ¿Esta libre el marco `i`?
76 fn libre(&self, i: usize) -> bool {
77 (self.mapa[i / 64] >> (i % 64)) & 1 == 0
78 }
79
80 /// Marca el marco `i` como ocupado.
81 fn ocupar(&mut self, i: usize) {
82 self.mapa[i / 64] |= 1 << (i % 64);
83 }
84
85 /// Marca el marco `i` como libre.
86 fn soltar(&mut self, i: usize) {
87 self.mapa[i / 64] &= !(1u64 << (i % 64));
88 }
89
90 /// Reserva `paginas` marcos CONTIGUOS y devuelve la direccion fisica del
91 /// primero. `None` si no hay un hueco contiguo bastante grande.
92 fn asignar(&mut self, paginas: usize) -> Option<u64> {
93 if paginas == 0 || paginas > self.total {
94 return None;
95 }
96 let mut i = 0;
97 while i + paginas <= self.total {
98 // Buscar `paginas` marcos libres consecutivos a partir de `i`.
99 match (i..i + paginas).find(|&k| !self.libre(k)) {
100 // Un marco ocupado rompe la racha: reanudar tras el.
101 Some(ocupado) => i = ocupado + 1,
102 // Racha completa: ocuparla y entregar su direccion fisica.
103 None => {
104 for k in i..i + paginas {
105 self.ocupar(k);
106 }
107 return Some(self.base + (i as u64) * PAGINA);
108 }
109 }
110 }
111 None
112 }
113
114 /// Devuelve a la arena `paginas` marcos que arrancan en la direccion
115 /// fisica `fisica`. Direcciones ajenas a la arena se ignoran sin daño.
116 fn liberar(&mut self, fisica: u64, paginas: usize) {
117 if fisica < self.base {
118 return;
119 }
120 let inicio = ((fisica - self.base) / PAGINA) as usize;
121 for k in inicio..(inicio + paginas).min(self.total) {
122 self.soltar(k);
123 }
124 }
125}
126
127/// El asignador global de marcos para DMA. Se funde en `init`.
128static ASIGNADOR: Once<Mutex<AsignadorMarcos>> = Once::new();
129
130/// Funda el subsistema de disco: registra el offset de memoria fisica y forja
131/// el asignador de marcos sobre la region de RAM libre que el cargador reporto.
132/// Una sola vez, antes de montar el disco.
133pub fn init(offset_fisico: u64, region_inicio: u64, region_fin: u64) {
134 use core::fmt::Write;
135 OFFSET_FISICO.store(offset_fisico, Ordering::Relaxed);
136 // Saltar SIEMPRE la primera pagina fisica: algunos cargadores la dejan sin
137 // mapear como proteccion contra punteros NULL — un marco DMA ahi seria una
138 // bomba en cuanto el driver lo desreferenciase via el mapeo alto.
139 let base = alinear_arriba(region_inicio.max(PAGINA), PAGINA);
140 let disponibles = region_fin.saturating_sub(base) / PAGINA;
141 let total = (disponibles as usize).min(MAX_MARCOS);
142 let _ = writeln!(
143 crate::baliza::Serie,
144 "disco :: init offset={:#x} region=[{:#x}, {:#x}) base={:#x} marcos={}",
145 offset_fisico,
146 region_inicio,
147 region_fin,
148 base,
149 total,
150 );
151 ASIGNADOR.call_once(|| {
152 Mutex::new(AsignadorMarcos {
153 base,
154 total,
155 mapa: vec![0u64; total.div_ceil(64)],
156 })
157 });
158}
159
160/// Redondea `valor` hacia arriba al multiplo de `alineacion` (potencia de dos).
161fn alinear_arriba(valor: u64, alineacion: u64) -> u64 {
162 (valor + alineacion - 1) & !(alineacion - 1)
163}
164
165/// Reserva `paginas` marcos fisicos contiguos de 4 KiB y devuelve su direccion
166/// fisica. Agotar la arena es un fallo del kernel, no recuperable aqui: el
167/// rasgo `Hal` no admite que `dma_alloc` falle.
168fn asignar_marcos(paginas: usize) -> u64 {
169 ASIGNADOR
170 .get()
171 .expect("DMA :: el asignador de marcos no esta fundado")
172 .lock()
173 .asignar(paginas)
174 .expect("DMA :: la arena de marcos fisicos se agoto")
175}
176
177/// Devuelve `paginas` marcos fisicos a la arena. El reverso de `asignar_marcos`.
178fn liberar_marcos(fisica: u64, paginas: usize) {
179 if let Some(asignador) = ASIGNADOR.get() {
180 asignador.lock().liberar(fisica, paginas);
181 }
182}
183
184/// Traduce una direccion fisica a la virtual que el kernel puede desreferenciar.
185fn a_virtual(fisica: u64) -> *mut u8 {
186 (fisica + OFFSET_FISICO.load(Ordering::Relaxed)) as *mut u8
187}
188
189// =============================================================================
190// KernelHal — EL PUENTE ENTRE `virtio-drivers` Y LA MEMORIA DE renaser
191// =============================================================================
192
193/// La implementacion del rasgo `Hal` de `virtio-drivers`. Sin estado propio:
194/// se apoya en el asignador de marcos y el offset fisico, ambos globales.
195pub struct KernelHal;
196
197// SEGURIDAD: cada metodo respeta su contrato — `dma_alloc` entrega marcos
198// fisicos exclusivos, contiguos, alineados a pagina y a cero; `dma_dealloc` y
199// `unshare` los devuelven a la arena; las traducciones de direccion son validas
200// porque el cargador mapeo toda la memoria fisica.
201unsafe impl Hal for KernelHal {
202 fn dma_alloc(paginas: usize, _direccion: BufferDirection) -> (PhysAddr, NonNull<u8>) {
203 let fisica = asignar_marcos(paginas);
204 let virtual_ = a_virtual(fisica);
205 // SEGURIDAD: `asignar_marcos` entrego `paginas` marcos exclusivos y
206 // contiguos; `virtual_` es su imagen valida y escribible en el mapeo
207 // de memoria fisica. El rasgo exige que las paginas vayan a cero.
208 unsafe {
209 core::ptr::write_bytes(virtual_, 0, paginas * PAGINA as usize);
210 }
211 (fisica, NonNull::new(virtual_).expect("DMA :: marco fisico nulo"))
212 }
213
214 unsafe fn dma_dealloc(fisica: PhysAddr, _virtual_: NonNull<u8>, paginas: usize) -> i32 {
215 // Con un asignador real, la liberacion ya NO es un gesto vacio: los
216 // marcos vuelven a la arena y quedan disponibles para el proximo DMA.
217 liberar_marcos(fisica, paginas);
218 0
219 }
220
221 unsafe fn mmio_phys_to_virt(fisica: PhysAddr, _tam: usize) -> NonNull<u8> {
222 // El cargador mapeo, como minimo, los primeros 4 GiB de memoria fisica;
223 // todo BAR MMIO de PCI cae dentro y es accesible en `fisica + offset`.
224 NonNull::new(a_virtual(fisica)).expect("MMIO :: direccion fisica nula")
225 }
226
227 unsafe fn share(bufer: NonNull<[u8]>, direccion: BufferDirection) -> PhysAddr {
228 let longitud = bufer.len();
229 let paginas = longitud.div_ceil(PAGINA as usize);
230 let fisica = asignar_marcos(paginas);
231 // Si el bufer viaja HACIA el dispositivo, copiarlo al area DMA rebote.
232 if !matches!(direccion, BufferDirection::DeviceToDriver) {
233 // SEGURIDAD: el rasgo garantiza que `bufer` apunta a `longitud`
234 // bytes validos; el area DMA recien reservada los abarca de sobra.
235 unsafe {
236 core::ptr::copy_nonoverlapping(
237 bufer.as_ptr() as *const u8,
238 a_virtual(fisica),
239 longitud,
240 );
241 }
242 }
243 fisica
244 }
245
246 unsafe fn unshare(fisica: PhysAddr, bufer: NonNull<[u8]>, direccion: BufferDirection) {
247 let longitud = bufer.len();
248 // Si el bufer viene DESDE el dispositivo, copiar el area DMA de vuelta.
249 if !matches!(direccion, BufferDirection::DriverToDevice) {
250 // SEGURIDAD: `fisica` lo entrego `share` para este mismo `bufer`;
251 // ambas regiones abarcan los `longitud` bytes que se copian.
252 unsafe {
253 core::ptr::copy_nonoverlapping(
254 a_virtual(fisica),
255 bufer.as_ptr() as *mut u8,
256 longitud,
257 );
258 }
259 }
260 // Devolver a la arena los marcos del area rebote.
261 liberar_marcos(fisica, longitud.div_ceil(PAGINA as usize));
262 }
263}
264
265// =============================================================================
266// EL DISCO PERSISTENTE
267// =============================================================================
268
269/// El disco virtio-blk, ya montado. Envuelve al `VirtIOBlk` para poder ligarlo
270/// a un `static`.
271struct Disco(VirtIOBlk<KernelHal, PciTransport>);
272
273// SEGURIDAD: `Disco` encierra punteros crudos a las colas virtio y al MMIO del
274// dispositivo. renaser es un kernel de un solo nucleo y todo acceso al disco se
275// serializa tras el `Mutex` global `DISCO`; jamas se comparte entre hilos
276// reales. Todo acceso normal toma el cerrojo con las interrupciones acalladas,
277// de modo que la IRQ del disco jamas lo encuentra ocupado.
278unsafe impl Send for Disco {}
279
280/// El disco global de renaser. Se monta una sola vez, en `montar`.
281static DISCO: Once<Mutex<Disco>> = Once::new();
282
283/// La linea de IRQ del disco, descubierta al montarlo. Vale 0 si el firmware no
284/// enruto una linea util: en ese caso la E/S recae en el sondeo, con la red de
285/// seguridad del temporizador.
286static IRQ_DISCO: AtomicU8 = AtomicU8::new(0);
287
288/// Cuenta de interrupciones del disco atendidas desde el arranque. Es el
289/// testigo vivo de que la E/S asincrona late de verdad.
290static PULSOS_DISCO: AtomicU64 = AtomicU64::new(0);
291
292/// El waker de la (unica) espera de disco en curso. Las operaciones de disco se
293/// serializan, de modo que una sola ranura basta. La IRQ del disco lo invoca.
294static WAKER_DISCO: Mutex<Option<Waker>> = Mutex::new(None);
295
296/// Enumera el bus PCI, localiza el disco virtio-blk, lo monta y lo deja tras el
297/// `Mutex` global. Descubre ademas su linea de IRQ, registra el manejador y
298/// abre la linea en el PIC: desde aqui el disco puede interrumpir. Devuelve la
299/// capacidad del disco en sectores. Toda falla se devuelve como `Err`.
300pub fn montar() -> Result<u64, &'static str> {
301 let mut raiz = PciRoot::new(CamPuertos);
302
303 // 1. Localizar el primer disco virtio-blk recorriendo el bus.
304 let mut hallado: Option<DeviceFunction> = None;
305 'busqueda: for bus in 0..=255u8 {
306 for (device_function, info) in raiz.enumerate_bus(bus) {
307 if info.vendor_id == VENDOR_VIRTIO && VIRTIO_BLK_IDS.contains(&info.device_id) {
308 hallado = Some(device_function);
309 break 'busqueda;
310 }
311 }
312 }
313 let device_function = hallado.ok_or("virtio-blk no hallado en el bus PCI")?;
314
315 // 2. Habilitar E/S, espacio de memoria y BUS-MASTER. Sin bus-master el
316 // dispositivo no puede iniciar transferencias DMA por su cuenta.
317 raiz.set_command(
318 device_function,
319 Command::IO_SPACE | Command::MEMORY_SPACE | Command::BUS_MASTER,
320 );
321
322 // 3. Montar el transporte PCI moderno de virtio y el dispositivo de bloques.
323 let transporte = PciTransport::new::<KernelHal, _>(&mut raiz, device_function)
324 .map_err(|_| "no se pudo montar el transporte PCI de virtio")?;
325 let mut disco = VirtIOBlk::<KernelHal, _>::new(transporte)
326 .map_err(|_| "no se pudo inicializar el dispositivo virtio-blk")?;
327 let capacidad = disco.capacity();
328
329 // 4. Pedir al dispositivo que EMITA una interrupcion al completar cada
330 // peticion — el corazon de la E/S asincrona de esta fase.
331 disco.enable_interrupts();
332 DISCO.call_once(|| Mutex::new(Disco(disco)));
333
334 // 5. Descubrir la linea de IRQ que el firmware asigno al dispositivo y
335 // enrutarla: registrar el manejador en la IDT y abrir la linea en el
336 // PIC. Las IRQ 0 y 1 son del temporizador y el teclado; un valor fuera
337 // de 2..15 (p. ej. 0xFF, «sin conexion») significa que no hay linea
338 // util — la E/S seguira funcionando, pero por sondeo.
339 let irq = super::pci::linea_irq(device_function);
340 if (2..=15).contains(&irq) {
341 crate::interrupts::registrar_irq_disco(irq);
342 crate::pic::desenmascarar(irq);
343 IRQ_DISCO.store(irq, Ordering::SeqCst);
344 }
345
346 Ok(capacidad)
347}
348
349/// La linea de IRQ del disco, si el firmware enruto una util.
350pub fn irq() -> Option<u8> {
351 match IRQ_DISCO.load(Ordering::SeqCst) {
352 0 => None,
353 n => Some(n),
354 }
355}
356
357/// Numero de interrupciones del disco atendidas desde el arranque.
358pub fn pulsos_disco() -> u64 {
359 PULSOS_DISCO.load(Ordering::Relaxed)
360}
361
362/// Punto de entrada DESDE el manejador de la IRQ del disco. Reconoce la
363/// interrupcion en el dispositivo —leer su registro ISR baja la linea INTx— y
364/// despierta a la tarea que aguardaba el bloque. Breve y libre de panicos:
365/// corre en contexto de interrupcion.
366pub fn atender_irq() {
367 // Estamos en contexto de interrupcion (IF=0). Todo acceso normal a `DISCO`
368 // toma su cerrojo con las interrupciones acalladas, de modo que aqui jamas
369 // esta ocupado: tomarlo no puede interbloquear.
370 if let Some(disco) = DISCO.get() {
371 let _ = disco.lock().0.ack_interrupt();
372 }
373 PULSOS_DISCO.fetch_add(1, Ordering::Relaxed);
374 if let Some(waker) = WAKER_DISCO.lock().take() {
375 waker.wake();
376 }
377}
378
379/// Inscribe el waker de la espera de disco en curso. Una sola ranura: las
380/// operaciones de disco estan serializadas. El cerrojo lo disputa el manejador
381/// de IRQ — tomarlo con las interrupciones acalladas hace imposible el bloqueo.
382fn registrar_waker(waker: Waker) {
383 interrupts::without_interrupts(|| *WAKER_DISCO.lock() = Some(waker));
384}
385
386// =============================================================================
387// EsperaDisco — UNA OPERACION DE BLOQUE COMO `Future`
388// =============================================================================
389
390/// Una transferencia de bloques en vuelo, expresada como `Future`. Posee sus
391/// propios buferes DMA —el encabezado de peticion, la respuesta y los datos—,
392/// que `virtio-drivers` mantiene prestados hasta que la operacion concluye.
393///
394/// Su `poll` envia la peticion la primera vez y, despues, comprueba el «used
395/// ring»: si el dispositivo aun no ha terminado, inscribe el waker y cede; la
396/// IRQ del disco lo reanudara. Una operacion a la vez — basta para renaser.
397pub struct EsperaDisco {
398 /// Encabezado de la peticion virtio. En el heap: direccion estable mientras
399 /// el dispositivo lo tiene prestado, sin importar si el `Future` se mueve.
400 req: Box<BlkReq>,
401 /// Respuesta de estado del dispositivo. En el heap, por la misma razon.
402 resp: Box<BlkResp>,
403 /// Los datos: destino de una lectura, origen de una escritura.
404 buf: Vec<u8>,
405 /// Primer sector de la transferencia.
406 sector: u64,
407 /// `true` si la operacion escribe; `false` si lee.
408 es_escritura: bool,
409 /// Token que `virtio-drivers` devolvio al enviar la peticion. `None` hasta
410 /// que el primer `poll` la envia.
411 token: Option<u16>,
412}
413
414impl EsperaDisco {
415 /// Hace avanzar la operacion: la envia si aun no lo estaba y comprueba si el
416 /// dispositivo la completo. `None` => sigue en vuelo; `Some` => terminada.
417 fn avanzar(&mut self) -> Option<Result<Vec<u8>, &'static str>> {
418 // Todo el dialogo con el dispositivo, con las interrupciones acalladas:
419 // asi la IRQ del disco jamas encuentra ocupado el cerrojo de `DISCO`.
420 interrupts::without_interrupts(|| {
421 let disco = match DISCO.get() {
422 Some(disco) => disco,
423 None => return Some(Err("disco no montado")),
424 };
425 let mut guardia = disco.lock();
426 let blk = &mut guardia.0;
427
428 // 1. Enviar la peticion la primera vez que se sondea esta espera.
429 if self.token.is_none() {
430 // SEGURIDAD: `req`, `buf` y `resp` viven en esta `EsperaDisco`,
431 // que no se libera ni se accede por otra via hasta que el
432 // `complete_*` de mas abajo cierre la operacion.
433 let envio = unsafe {
434 if self.es_escritura {
435 blk.write_blocks_nb(
436 self.sector as usize,
437 &mut self.req,
438 self.buf.as_slice(),
439 &mut self.resp,
440 )
441 } else {
442 blk.read_blocks_nb(
443 self.sector as usize,
444 &mut self.req,
445 self.buf.as_mut_slice(),
446 &mut self.resp,
447 )
448 }
449 };
450 match envio {
451 Ok(token) => self.token = Some(token),
452 Err(_) => return Some(Err("no se pudo enviar la peticion al disco")),
453 }
454 }
455 let token = self.token.unwrap();
456
457 // 2. ¿Ha colocado el dispositivo este token en el «used ring»?
458 if blk.peek_used() != Some(token) {
459 return None; // aun en vuelo
460 }
461
462 // 3. Completado: recoger el resultado y desligar los buferes DMA.
463 // SEGURIDAD: se pasan los MISMOS buferes que recibio el `*_nb`.
464 let fin = unsafe {
465 if self.es_escritura {
466 blk.complete_write_blocks(token, &self.req, self.buf.as_slice(), &mut self.resp)
467 } else {
468 blk.complete_read_blocks(
469 token,
470 &self.req,
471 self.buf.as_mut_slice(),
472 &mut self.resp,
473 )
474 }
475 };
476 Some(match fin {
477 Ok(()) => Ok(core::mem::take(&mut self.buf)),
478 Err(_) => Err("la operacion de disco no se completo"),
479 })
480 })
481 }
482}
483
484impl Future for EsperaDisco {
485 /// Al terminar, una lectura entrega sus datos; una escritura, un vector
486 /// vacio. El error es siempre un mensaje estable.
487 type Output = Result<Vec<u8>, &'static str>;
488
489 fn poll(self: Pin<&mut Self>, contexto: &mut Context<'_>) -> Poll<Self::Output> {
490 // `EsperaDisco` es `Unpin` —solo `Box`, `Vec` y escalares—: el `Pin`
491 // no impone nada y el acceso mutable es directo.
492 let this = self.get_mut();
493 // Inscribir el waker ANTES de comprobar: si la IRQ se cuela entre la
494 // comprobacion y la inscripcion, el waker ya esta puesto y el despertar
495 // no se pierde — el mismo blindaje que usa `EsperaFrame` (ver `reloj`).
496 registrar_waker(contexto.waker().clone());
497 match this.avanzar() {
498 Some(resultado) => Poll::Ready(resultado),
499 None => Poll::Pending,
500 }
501 }
502}
503
504/// Prepara la LECTURA asincrona de `n_sectores` sectores desde `sector`. El
505/// `Future` que devuelve no toca el disco hasta que se le sondea.
506pub fn leer_bloques(sector: u64, n_sectores: usize) -> EsperaDisco {
507 EsperaDisco {
508 req: Box::new(BlkReq::default()),
509 resp: Box::new(BlkResp::default()),
510 buf: vec![0u8; n_sectores * TAM_SECTOR],
511 sector,
512 es_escritura: false,
513 token: None,
514 }
515}
516
517/// Prepara la ESCRITURA asincrona de `datos` a partir de `sector`. La longitud
518/// de `datos` debe ser multiplo de un sector.
519pub fn escribir_bloques(sector: u64, datos: Vec<u8>) -> EsperaDisco {
520 EsperaDisco {
521 req: Box::new(BlkReq::default()),
522 resp: Box::new(BlkResp::default()),
523 buf: datos,
524 sector,
525 es_escritura: true,
526 token: None,
527 }
528}
529
530// =============================================================================
531// bloquear_en — EL PUENTE PARA LOS CONTEXTOS SINCRONOS
532// =============================================================================
533
534/// Lleva un `Future` de disco hasta su final desde un contexto SINCRONO — el
535/// arranque, o una capacidad WASM, que no pueden `.await`-ear—. Mientras el
536/// disco trabaja:
537///
538/// * si las interrupciones estan activas, duerme la CPU con `hlt`; la
539/// despertara la IRQ del disco —o el temporizador, como red de seguridad—;
540/// * si no —en el arranque, antes de habilitarlas—, sondea en bucle.
541///
542/// Asi, una vez el sistema esta en marcha, la espera de disco JAMAS malgasta
543/// ciclos en espera activa.
544fn bloquear_en<F: Future>(futuro: F) -> F::Output {
545 let mut futuro = core::pin::pin!(futuro);
546 let waker = Waker::noop();
547 let mut contexto = Context::from_waker(waker);
548 loop {
549 match futuro.as_mut().poll(&mut contexto) {
550 Poll::Ready(salida) => return salida,
551 Poll::Pending => {
552 if interrupts::are_enabled() {
553 x86_64::instructions::hlt();
554 } else {
555 core::hint::spin_loop();
556 }
557 }
558 }
559 }
560}
561
562/// Lee `buf.len() / 512` sectores consecutivos a partir de `sector`. Sincrono:
563/// construido sobre la maquinaria asincrona via `bloquear_en`. El bufer debe
564/// medir un multiplo entero de un sector.
565pub fn leer_sectores(sector: u64, buf: &mut [u8]) -> Result<(), &'static str> {
566 let datos = bloquear_en(leer_bloques(sector, buf.len() / TAM_SECTOR))?;
567 buf.copy_from_slice(&datos);
568 Ok(())
569}
570
571/// Escribe `buf.len() / 512` sectores consecutivos a partir de `sector`.
572/// Sincrono, sobre la misma maquinaria asincrona.
573pub fn escribir_sectores(sector: u64, buf: &[u8]) -> Result<(), &'static str> {
574 bloquear_en(escribir_bloques(sector, buf.to_vec())).map(|_| ())
575}