use core::sync::atomic::{AtomicU32, Ordering}; use x86_64::PhysAddr; use crate::mem::addr; use crate::mem::phys::BitmapFrameAllocator; const MAX_RING_FRAMES: usize = 16; const PAGE_SIZE: usize = 4096; const HEADER_SIZE: usize = 16; #[repr(C)] struct RingHeader { write_head: AtomicU32, read_tail: AtomicU32, capacity: u32, _reserved: u32, } #[allow(dead_code)] struct ConsoleRingState { header: *mut RingHeader, data: *mut u8, capacity: u32, phys_addrs: [u64; MAX_RING_FRAMES], frame_count: u16, } unsafe impl Send for ConsoleRingState {} unsafe impl Sync for ConsoleRingState {} static mut RING_STATE: Option = None; pub fn init(frame_count: u16) { assert!( (frame_count as usize) <= MAX_RING_FRAMES && frame_count > 0, "console ring: frame_count out of range" ); let allocator = BitmapFrameAllocator; let phys_base = allocator .allocate_contiguous(frame_count as usize) .expect("console ring: failed to allocate contiguous frames"); let mut phys_addrs = [0u64; MAX_RING_FRAMES]; (0..frame_count as usize).fold((), |(), i| { let frame_phys = PhysAddr::new(phys_base.as_u64() + (i as u64) * PAGE_SIZE as u64); addr::zero_frame(frame_phys); crate::mem::refcount::increment(frame_phys).expect("console ring: refcount init failed"); phys_addrs[i] = frame_phys.as_u64(); }); let virt_base = addr::phys_to_virt(phys_base); let header = virt_base.as_mut_ptr::(); let data = unsafe { (header as *mut u8).add(HEADER_SIZE) }; let capacity = (frame_count as u32) * (PAGE_SIZE as u32) - (HEADER_SIZE as u32); unsafe { (*header).write_head = AtomicU32::new(0); (*header).read_tail = AtomicU32::new(0); (*header).capacity = capacity; (*header)._reserved = 0; RING_STATE = Some(ConsoleRingState { header, data, capacity, phys_addrs, frame_count, }); } } #[allow(clippy::deref_addrof)] pub fn write_bytes(bytes: &[u8]) { let state = unsafe { &*(&raw const RING_STATE) }; let state = match state.as_ref() { Some(s) => s, None => return, }; let header = unsafe { &*state.header }; let head = header.write_head.load(Ordering::Relaxed); let cap = state.capacity; bytes .iter() .enumerate() .fold(head, |current_head, (_i, &b)| { let idx = current_head % cap; unsafe { state.data.add(idx as usize).write_volatile(b) }; current_head.wrapping_add(1) }); let new_head = head.wrapping_add(bytes.len() as u32); header.write_head.store(new_head, Ordering::Release); } #[cfg(not(lancer_test))] #[allow(clippy::deref_addrof)] pub fn phys_frame_count() -> u16 { let state = unsafe { &*(&raw const RING_STATE) }; match state.as_ref() { Some(s) => s.frame_count, None => 0, } } #[cfg(not(lancer_test))] #[allow(clippy::deref_addrof)] pub fn phys_addr(index: u16) -> Option { let state = unsafe { &*(&raw const RING_STATE) }; state .as_ref() .and_then(|s| match (index as usize) < (s.frame_count as usize) { true => Some(PhysAddr::new(s.phys_addrs[index as usize])), false => None, }) }