diff --git a/crates/apps/smptest/src/main.rs b/crates/apps/smptest/src/main.rs index 25cdb76..0b8ccf4 100644 --- a/crates/apps/smptest/src/main.rs +++ b/crates/apps/smptest/src/main.rs @@ -23,7 +23,7 @@ fn main() { libkos::println!("smptest: cannot map the counter page"); syscall::exit(); }; - let words = window.atomics(0, 1 + THREADS); + let words = window.atomics(types::address::ByteOffset::ZERO, 1 + THREADS); for _ in 1..THREADS { if !libkos::thread::spawn(words, spin) { break; diff --git a/crates/drivers/block/virtio/src/lib.rs b/crates/drivers/block/virtio/src/lib.rs index ea29bd3..a7578fb 100644 --- a/crates/drivers/block/virtio/src/lib.rs +++ b/crates/drivers/block/virtio/src/lib.rs @@ -5,7 +5,7 @@ use hw_virtio::block::{self, DmaAddrs}; use hw_virtio::mmio::{MmioAccess, Registers}; use hw_virtio::queue::VirtioQueue; use port_block_device::{BlockDevice, SectorId}; -use types::address::{PhysicalAddress, VirtualAddress}; +use types::address::{ByteOffset, PhysicalAddress, VirtualAddress}; use types::bounded::PageCount; const MMIO_VA: VirtualAddress = VirtualAddress::from_raw(0x20000); @@ -38,11 +38,11 @@ impl VirtioBlockDevice { let at = |offset: usize| PhysicalAddress::from_raw(req.physical.raw() + offset); let dma = DmaAddrs { header_pa: at(0), - header: req.window.sub(0, 16), + header: req.window.sub(ByteOffset::new(0), 16), status_pa: at(16), - status: req.window.sub(16, 1), + status: req.window.sub(ByteOffset::new(16), 1), data_pa: at(32), - data: req.window.sub(32, SECTOR_SIZE), + data: req.window.sub(ByteOffset::new(32), SECTOR_SIZE), }; let capacity = regs.read(block::CONFIG_CAPACITY) as u64 | (regs.read(block::CONFIG_CAPACITY + 4) as u64) << 32; diff --git a/crates/drivers/framebuffer/virtio/src/lib.rs b/crates/drivers/framebuffer/virtio/src/lib.rs index e0ca6ce..54063a7 100644 --- a/crates/drivers/framebuffer/virtio/src/lib.rs +++ b/crates/drivers/framebuffer/virtio/src/lib.rs @@ -6,7 +6,7 @@ use hw_virtio::mmio::{MmioAccess, Registers}; use hw_virtio::queue::VirtioQueue; use libkos::Window; use port_framebuffer::Framebuffer; -use types::address::{PhysicalAddress, VirtualAddress}; +use types::address::{ByteOffset, PhysicalAddress, VirtualAddress}; use types::bounded::PageCount; use types::display::{Color, Dimension, ScreenCoord}; @@ -50,7 +50,7 @@ impl VirtioFramebuffer { let dma = GpuDmaAddrs { cmd_pa: cmd.physical, cmd: cmd.window, - resp_pa: PhysicalAddress::from_raw(cmd.physical.raw() + gpu::RESPONSE), + resp_pa: PhysicalAddress::from_raw(cmd.physical.raw() + gpu::RESPONSE.raw()), }; let (w, h) = gpu::get_display_info(®s, &queue, &dma) .and_then(|(w, h)| Some((u16::try_from(w).ok()?, u16::try_from(h).ok()?))) @@ -93,7 +93,7 @@ impl Framebuffer for VirtioFramebuffer { return; } let offset = (y.raw() as usize) * self.stride + (x.raw() as usize) * FB_BPP; - self.pixels.write(offset, color.raw()); + self.pixels.write(ByteOffset::new(offset), color.raw()); } fn fill_rect(&mut self, x: ScreenCoord, y: ScreenCoord, w: Dimension, h: Dimension, color: Color) { @@ -106,7 +106,7 @@ impl Framebuffer for VirtioFramebuffer { fn clear(&mut self, color: Color) { let total = self.width.raw() as usize * self.height.raw() as usize; - self.pixels.fill_u32(0, total, color.raw()); + self.pixels.fill_u32(ByteOffset::ZERO, total, color.raw()); } fn flush(&mut self) { diff --git a/crates/drivers/input/uart/src/lib.rs b/crates/drivers/input/uart/src/lib.rs index bb4ce1f..a2081c8 100644 --- a/crates/drivers/input/uart/src/lib.rs +++ b/crates/drivers/input/uart/src/lib.rs @@ -3,11 +3,12 @@ use libkos::Window; use port_input_device::{InputDevice, InputEvent, Key}; -use types::address::VirtualAddress; +use types::address::{ByteOffset, VirtualAddress}; const UART_VA: VirtualAddress = VirtualAddress::from_raw(0x20000); -const UART_IER_OFFSET: usize = 1; -const UART_LSR_OFFSET: usize = 5; +const UART_RBR: ByteOffset = ByteOffset::new(0); +const UART_IER_OFFSET: ByteOffset = ByteOffset::new(1); +const UART_LSR_OFFSET: ByteOffset = ByteOffset::new(5); /// IER bit: interrupt when a received byte is waiting (cleared by reading /// it, so `acknowledge` has nothing to do). const IER_RX_READY: u8 = 1; @@ -27,7 +28,7 @@ impl UartInputDevice { } fn read_byte(&self) -> Option { - self.has_event().then(|| self.regs.read(0)) + self.has_event().then(|| self.regs.read(UART_RBR)) } fn byte_to_key(byte: u8) -> Key { diff --git a/crates/drivers/input/virtio/src/lib.rs b/crates/drivers/input/virtio/src/lib.rs index fbd7163..89844c8 100644 --- a/crates/drivers/input/virtio/src/lib.rs +++ b/crates/drivers/input/virtio/src/lib.rs @@ -7,7 +7,7 @@ use core::sync::atomic::{Ordering, fence}; use hw_virtio::mmio::{MmioAccess, Registers}; use libkos::Window; use port_input_device::{InputDevice, InputEvent}; -use types::address::{PhysicalAddress, VirtualAddress}; +use types::address::{ByteOffset, PhysicalAddress, VirtualAddress}; use types::bounded::PageCount; const INPUT_MMIO_VA: VirtualAddress = VirtualAddress::from_raw(0x25000); @@ -18,8 +18,8 @@ const QUEUE_SIZE: usize = 32; const EVENT_SIZE: usize = 8; const DESC_SIZE: usize = 16; /// Where the legacy layout puts the available and used rings. -const AVAIL: usize = QUEUE_SIZE * DESC_SIZE; -const USED: usize = 4096; +const AVAIL: ByteOffset = ByteOffset::new(QUEUE_SIZE * DESC_SIZE); +const USED: ByteOffset = ByteOffset::new(4096); const EV_KEY: u16 = 1; const KEY_PRESS: u32 = 1; @@ -67,7 +67,7 @@ impl VirtioInputDevice { /// Every buffer goes to the device, one event each. fn fill_queue(&mut self) { for i in 0..QUEUE_SIZE { - let desc = i * DESC_SIZE; + let desc = ByteOffset::new(i * DESC_SIZE); self.rings.write(desc, (self.events_pa.raw() + i * EVENT_SIZE) as u64); self.rings.write(desc + 8, EVENT_SIZE as u32); self.rings.write(desc + 12, 2u16); // WRITE flag @@ -92,7 +92,7 @@ impl VirtioInputDevice { } fn read_event(&self, desc_idx: u16) -> (u16, u16, u32) { - let at = desc_idx as usize % QUEUE_SIZE * EVENT_SIZE; + let at = ByteOffset::new(desc_idx as usize % QUEUE_SIZE * EVENT_SIZE); (self.events.read(at), self.events.read(at + 2), self.events.read(at + 4)) } diff --git a/crates/hw-virtio/src/block.rs b/crates/hw-virtio/src/block.rs index 5a31f2e..ce78f37 100644 --- a/crates/hw-virtio/src/block.rs +++ b/crates/hw-virtio/src/block.rs @@ -1,7 +1,7 @@ use crate::mmio::MmioAccess; use crate::queue::{Descriptor, VirtioQueue}; use libkos::Window; -use types::address::PhysicalAddress; +use types::address::{ByteOffset, PhysicalAddress}; const SECTOR_SIZE: usize = 512; @@ -16,11 +16,15 @@ pub struct DmaAddrs { pub data: Window, } +const HEADER_TYPE: ByteOffset = ByteOffset::new(0); +const HEADER_RESERVED: ByteOffset = ByteOffset::new(4); +const HEADER_SECTOR: ByteOffset = ByteOffset::new(8); + fn put_header(dma: &DmaAddrs, req_type: u32, sector: u32) { - dma.header.write(0, req_type); - dma.header.write(4, 0u32); - dma.header.write(8, sector as u64); - dma.status.write(0, 0xFFu8); + dma.header.write(HEADER_TYPE, req_type); + dma.header.write(HEADER_RESERVED, 0u32); + dma.header.write(HEADER_SECTOR, sector as u64); + dma.status.write(ByteOffset::ZERO, 0xFFu8); } pub fn read_sector( @@ -42,15 +46,15 @@ pub fn read_sector( if !queue.submit(mmio, &descs) { return false; } - if dma.status.read::(0) != 0 { return false; } - dma.data.copy_to(0, &mut buf[..SECTOR_SIZE]); + if dma.status.read::(ByteOffset::ZERO) != 0 { return false; } + dma.data.copy_to(ByteOffset::ZERO, &mut buf[..SECTOR_SIZE]); true } /// Request types (virtio spec 5.2.6). const T_OUT: u32 = 1; /// Device configuration: the disk's size in 512-byte sectors (le64). -pub const CONFIG_CAPACITY: usize = 0x100; +pub const CONFIG_CAPACITY: ByteOffset = ByteOffset::new(0x100); /// Writes one sector from `buf`; true once the device reports success. pub fn write_sector( @@ -62,7 +66,7 @@ pub fn write_sector( ) -> bool { if buf.len() < SECTOR_SIZE { return false; } - dma.data.copy_from(0, &buf[..SECTOR_SIZE]); + dma.data.copy_from(ByteOffset::ZERO, &buf[..SECTOR_SIZE]); put_header(dma, T_OUT, sector); // The device reads the data this time; only the status is written back. @@ -72,5 +76,5 @@ pub fn write_sector( Descriptor { addr: dma.status_pa.raw() as u64, len: 1, writable: true }, ]; if !queue.submit(mmio, &descs) { return false; } - dma.status.read::(0) == 0 + dma.status.read::(ByteOffset::ZERO) == 0 } diff --git a/crates/hw-virtio/src/gpu.rs b/crates/hw-virtio/src/gpu.rs index 6e4e96d..37e27b8 100644 --- a/crates/hw-virtio/src/gpu.rs +++ b/crates/hw-virtio/src/gpu.rs @@ -1,7 +1,7 @@ use crate::mmio::MmioAccess; use crate::queue::{Descriptor, VirtioQueue}; use libkos::Window; -use types::address::PhysicalAddress; +use types::address::{ByteOffset, PhysicalAddress}; use types::display::Dimension; /// The command page: commands go out from its start, responses come back @@ -12,59 +12,76 @@ pub struct GpuDmaAddrs { pub resp_pa: PhysicalAddress, } -pub const RESPONSE: usize = 512; +pub const RESPONSE: ByteOffset = ByteOffset::new(512); + +/// Command fields (virtio spec 5.7.6.7): a 24-byte header, then the body. +const TYPE: ByteOffset = ByteOffset::new(0); +const BODY: ByteOffset = ByteOffset::new(24); +/// Most bodies start with a rectangle: x, y, width, height. +const RECT_X: ByteOffset = ByteOffset::new(24); +const RECT_Y: ByteOffset = ByteOffset::new(28); +const RECT_W: ByteOffset = ByteOffset::new(32); +const RECT_H: ByteOffset = ByteOffset::new(36); +/// The 24 bytes of header and 16 of rectangle in a display-info response. +const DISPLAY_W: ByteOffset = ByteOffset::new(24 + 8); +const DISPLAY_H: ByteOffset = ByteOffset::new(24 + 12); pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, width: Dimension, height: Dimension) -> bool { write_cmd_header(&dma.cmd, 0x0101); - dma.cmd.write(24, resource_id); - dma.cmd.write(28, 1u32); - dma.cmd.write(32, width.raw() as u32); - dma.cmd.write(36, height.raw() as u32); + // Resource id, format (1: B8G8R8A8), width, height. + dma.cmd.write(BODY, resource_id); + dma.cmd.write(BODY + 4, 1u32); + dma.cmd.write(BODY + 8, width.raw() as u32); + dma.cmd.write(BODY + 12, height.raw() as u32); submit_cmd(mmio, queue, dma, 40) } pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) -> bool { write_cmd_header(&dma.cmd, 0x0106); - dma.cmd.write(24, resource_id); - dma.cmd.write(28, 1u32); - dma.cmd.write(32, fb_pa.raw() as u64 as u64); - dma.cmd.write(40, fb_size); + // Resource id, one memory entry: its address and length. + dma.cmd.write(BODY, resource_id); + dma.cmd.write(BODY + 4, 1u32); + dma.cmd.write(BODY + 8, fb_pa.raw() as u64); + dma.cmd.write(BODY + 16, fb_size); submit_cmd(mmio, queue, dma, 48) } pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, scanout_id: u32, resource_id: u32, width: Dimension, height: Dimension) -> bool { write_cmd_header(&dma.cmd, 0x0103); - dma.cmd.write(32, width.raw() as u32); - dma.cmd.write(36, height.raw() as u32); - dma.cmd.write(40, scanout_id); - dma.cmd.write(44, resource_id); + // The rectangle (from 0, 0), then scanout and resource ids. + dma.cmd.write(RECT_W, width.raw() as u32); + dma.cmd.write(RECT_H, height.raw() as u32); + dma.cmd.write(BODY + 16, scanout_id); + dma.cmd.write(BODY + 20, resource_id); submit_cmd(mmio, queue, dma, 48) } pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool { write_cmd_header(&dma.cmd, 0x0105); - dma.cmd.write(24, x); - dma.cmd.write(28, y); - dma.cmd.write(32, w); - dma.cmd.write(36, h); - dma.cmd.write(40, 0u64); - dma.cmd.write(48, resource_id); + put_rect(&dma.cmd, x, y, w, h); + // Offset into the resource's memory, then the resource id. + dma.cmd.write(BODY + 16, 0u64); + dma.cmd.write(BODY + 24, resource_id); submit_cmd(mmio, queue, dma, 56) } pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) -> bool { write_cmd_header(&dma.cmd, 0x0104); - dma.cmd.write(24, x); - dma.cmd.write(28, y); - dma.cmd.write(32, w); - dma.cmd.write(36, h); - dma.cmd.write(40, resource_id); + put_rect(&dma.cmd, x, y, w, h); + dma.cmd.write(BODY + 16, resource_id); submit_cmd(mmio, queue, dma, 48) } fn write_cmd_header(cmd: &Window, cmd_type: u32) { - cmd.fill_u32(0, 128, 0); - cmd.write(0, cmd_type); + cmd.fill_u32(ByteOffset::ZERO, 128, 0); + cmd.write(TYPE, cmd_type); +} + +fn put_rect(cmd: &Window, x: u32, y: u32, w: u32, h: u32) { + cmd.write(RECT_X, x); + cmd.write(RECT_Y, y); + cmd.write(RECT_W, w); + cmd.write(RECT_H, h); } pub fn get_display_info(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs) -> Option<(u32, u32)> { @@ -75,8 +92,8 @@ pub fn get_display_info(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDm ]; if !queue.submit(mmio, &descs) { return None; } if dma.cmd.read::(RESPONSE) != 0x1101 { return None; } - let w: u32 = dma.cmd.read(RESPONSE + 24 + 8); - let h: u32 = dma.cmd.read(RESPONSE + 24 + 12); + let w: u32 = dma.cmd.read(RESPONSE + DISPLAY_W.raw()); + let h: u32 = dma.cmd.read(RESPONSE + DISPLAY_H.raw()); if w > 0 && h > 0 { Some((w, h)) } else { None } } diff --git a/crates/hw-virtio/src/mmio.rs b/crates/hw-virtio/src/mmio.rs index a4bb0b0..b2036e2 100644 --- a/crates/hw-virtio/src/mmio.rs +++ b/crates/hw-virtio/src/mmio.rs @@ -1,26 +1,28 @@ +use types::address::ByteOffset; + pub trait MmioAccess { - fn read(&self, offset: usize) -> u32; - fn write(&self, offset: usize, val: u32); + fn read(&self, offset: ByteOffset) -> u32; + fn write(&self, offset: ByteOffset, val: u32); } pub const MAGIC: u32 = 0x74726976; -pub const REG_MAGIC: usize = 0x000; -pub const REG_VERSION: usize = 0x004; -pub const REG_DEVICE_ID: usize = 0x008; -pub const REG_STATUS: usize = 0x070; -pub const REG_GUEST_PAGE_SIZE: usize = 0x028; -pub const REG_FEATURES_SEL: usize = 0x014; -pub const REG_FEATURES: usize = 0x010; -pub const REG_DRIVER_FEATURES_SEL: usize = 0x024; -pub const REG_DRIVER_FEATURES: usize = 0x020; -pub const REG_QUEUE_SEL: usize = 0x030; -pub const REG_QUEUE_NUM_MAX: usize = 0x034; -pub const REG_QUEUE_NUM: usize = 0x038; -pub const REG_QUEUE_PFN: usize = 0x040; -pub const REG_QUEUE_NOTIFY: usize = 0x050; -pub const REG_INTERRUPT_STATUS: usize = 0x060; -pub const REG_INTERRUPT_ACK: usize = 0x064; +pub const REG_MAGIC: ByteOffset = ByteOffset::new(0x000); +pub const REG_VERSION: ByteOffset = ByteOffset::new(0x004); +pub const REG_DEVICE_ID: ByteOffset = ByteOffset::new(0x008); +pub const REG_STATUS: ByteOffset = ByteOffset::new(0x070); +pub const REG_GUEST_PAGE_SIZE: ByteOffset = ByteOffset::new(0x028); +pub const REG_FEATURES_SEL: ByteOffset = ByteOffset::new(0x014); +pub const REG_FEATURES: ByteOffset = ByteOffset::new(0x010); +pub const REG_DRIVER_FEATURES_SEL: ByteOffset = ByteOffset::new(0x024); +pub const REG_DRIVER_FEATURES: ByteOffset = ByteOffset::new(0x020); +pub const REG_QUEUE_SEL: ByteOffset = ByteOffset::new(0x030); +pub const REG_QUEUE_NUM_MAX: ByteOffset = ByteOffset::new(0x034); +pub const REG_QUEUE_NUM: ByteOffset = ByteOffset::new(0x038); +pub const REG_QUEUE_PFN: ByteOffset = ByteOffset::new(0x040); +pub const REG_QUEUE_NOTIFY: ByteOffset = ByteOffset::new(0x050); +pub const REG_INTERRUPT_STATUS: ByteOffset = ByteOffset::new(0x060); +pub const REG_INTERRUPT_ACK: ByteOffset = ByteOffset::new(0x064); pub const STATUS_ACK: u32 = 1; pub const STATUS_DRIVER: u32 = 2; @@ -42,11 +44,11 @@ impl Registers { } impl MmioAccess for Registers { - fn read(&self, offset: usize) -> u32 { + fn read(&self, offset: ByteOffset) -> u32 { self.0.read(offset) } - fn write(&self, offset: usize, val: u32) { + fn write(&self, offset: ByteOffset, val: u32) { self.0.write(offset, val) } } diff --git a/crates/hw-virtio/src/queue.rs b/crates/hw-virtio/src/queue.rs index 0da7896..09fd22f 100644 --- a/crates/hw-virtio/src/queue.rs +++ b/crates/hw-virtio/src/queue.rs @@ -1,13 +1,14 @@ use crate::mmio::{MmioAccess, REG_QUEUE_NOTIFY}; use core::sync::atomic::{Ordering, fence}; use libkos::Window; +use types::address::ByteOffset; pub const QUEUE_SIZE: usize = 8; const DESC_SIZE: usize = 16; const TIMEOUT: u32 = 10_000_000; /// Where the legacy layout puts the available and used rings. -const AVAIL: usize = QUEUE_SIZE * DESC_SIZE; -const USED: usize = 4096; +const AVAIL: ByteOffset = ByteOffset::new(QUEUE_SIZE * DESC_SIZE); +const USED: ByteOffset = ByteOffset::new(4096); pub struct Descriptor { pub addr: u64, @@ -32,7 +33,7 @@ impl VirtioQueue { } let r = &self.rings; for (i, d) in descs.iter().enumerate() { - let at = i * DESC_SIZE; + let at = ByteOffset::new(i * DESC_SIZE); let last = i == descs.len() - 1; let flags: u16 = if d.writable { 2 } else { 0 } | if !last { 1 } else { 0 }; r.write(at, d.addr); diff --git a/crates/init/src/load.rs b/crates/init/src/load.rs index d0ab979..e6315d5 100644 --- a/crates/init/src/load.rs +++ b/crates/init/src/load.rs @@ -2,7 +2,7 @@ use libkos::fs::Storage; use libkos::syscall; -use types::address::{EntryPoint, VirtualAddress}; +use types::address::{ByteOffset, EntryPoint, VirtualAddress}; use types::bounded::{CapSlot, PageCount}; use types::budget::Limits; @@ -53,7 +53,7 @@ pub(crate) fn load_binary( let page_count = PageCount::new(u16::try_from(seg_pages).ok()?)?; let local = libkos::window::request(page_count)?; - local.fill_u32(0, seg_pages * PAGE_SIZE / 4, 0); + local.fill_u32(ByteOffset::ZERO, seg_pages * PAGE_SIZE / 4, 0); let copy_len = segment.file_size.min(segment.mem_size); let src = segment @@ -61,7 +61,7 @@ pub(crate) fn load_binary( .checked_add(copy_len) .and_then(|end| elf_data.get(segment.file_offset..end)); if let Some(src) = src.filter(|_| copy_len > 0) { - local.copy_from(offset_in_page, src); + local.copy_from(ByteOffset::new(offset_in_page), src); } for p in 0..seg_pages { diff --git a/crates/init/src/selftest.rs b/crates/init/src/selftest.rs index 2fc369a..e2be7ca 100644 --- a/crates/init/src/selftest.rs +++ b/crates/init/src/selftest.rs @@ -68,7 +68,7 @@ fn check_stopped(words: Words, what: &'static str) -> Result<(), &'static str> { fn run_phases(storage: &mut Storage, scratch: &mut [u8]) -> Result<(), &'static str> { let page = syscall::create_region(PageCount::new(1).ok_or("page count")?).map_err(|_| "no counter page")?; let window = libkos::window::map(page, OBSERVE_VA, PagePerms::ReadWrite).map_err(|_| "cannot map the counter page")?; - let words = window.atomics(0, 1 + THREADS); + let words = window.atomics(types::address::ByteOffset::ZERO, 1 + THREADS); let events = syscall::create_notification().map_err(|_| "no notification")?; timer_is_on_time(events)?; diff --git a/crates/libkos/src/window.rs b/crates/libkos/src/window.rs index 93d6c49..ab6ee23 100644 --- a/crates/libkos/src/window.rs +++ b/crates/libkos/src/window.rs @@ -5,7 +5,7 @@ //! Every access is volatile and bounds-checked; out of range panics. use core::sync::atomic::AtomicUsize; -use types::address::VirtualAddress; +use types::address::{ByteOffset, VirtualAddress}; use types::bounded::{CapSlot, PageCount}; use types::mapping::PagePerms; @@ -76,7 +76,8 @@ impl Window { } /// The address of `size` bytes at `offset`, aligned for them. - fn at(&self, offset: usize, size: usize, align: usize) -> usize { + fn at(&self, offset: ByteOffset, size: usize, align: usize) -> usize { + let offset = offset.raw(); let end = offset.checked_add(size); assert!(end.is_some_and(|e| e <= self.len), "window access out of range"); let addr = self.base.raw() + offset; @@ -84,33 +85,33 @@ impl Window { addr } - pub fn read(&self, offset: usize) -> T { + pub fn read(&self, offset: ByteOffset) -> T { let addr = self.at(offset, size_of::(), align_of::()); // SAFETY: in range of memory mapped for this process, aligned for // T; T is a plain integer, valid for any bits. unsafe { core::ptr::read_volatile(addr as *const T) } } - pub fn write(&self, offset: usize, value: T) { + pub fn write(&self, offset: ByteOffset, value: T) { let addr = self.at(offset, size_of::(), align_of::()); // SAFETY: as in `read`; the window hands out no references to it. unsafe { core::ptr::write_volatile(addr as *mut T, value) } } /// The `len` bytes at `offset`, as a window of their own. - pub fn sub(&self, offset: usize, len: usize) -> Window { + pub fn sub(&self, offset: ByteOffset, len: usize) -> Window { let base = VirtualAddress::from_raw(self.at(offset, len, 1)); Window { base, len } } - pub fn copy_from(&self, offset: usize, src: &[u8]) { + pub fn copy_from(&self, offset: ByteOffset, src: &[u8]) { self.at(offset, src.len(), 1); for (i, b) in src.iter().enumerate() { self.write(offset + i, *b); } } - pub fn copy_to(&self, offset: usize, dst: &mut [u8]) { + pub fn copy_to(&self, offset: ByteOffset, dst: &mut [u8]) { self.at(offset, dst.len(), 1); for (i, b) in dst.iter_mut().enumerate() { *b = self.read(offset + i); @@ -118,7 +119,7 @@ impl Window { } /// `count` u32s from `offset` set to `value`. - pub fn fill_u32(&self, offset: usize, count: usize, value: u32) { + pub fn fill_u32(&self, offset: ByteOffset, count: usize, value: u32) { let bytes = count.checked_mul(4).expect("window fill too large"); self.at(offset, bytes, 4); for i in 0..count { @@ -128,7 +129,7 @@ impl Window { /// `count` words from `offset` as atomics, for memory shared with /// threads or processes that only ever touch it atomically. - pub fn atomics(&self, offset: usize, count: usize) -> &'static [AtomicUsize] { + pub fn atomics(&self, offset: ByteOffset, count: usize) -> &'static [AtomicUsize] { let bytes = count.checked_mul(size_of::()).expect("too many atomics"); let addr = self.at(offset, bytes, align_of::()); // SAFETY: mapped and aligned; AtomicUsize may be shared and is diff --git a/crates/types/src/address.rs b/crates/types/src/address.rs index 49a30df..085bd7b 100644 --- a/crates/types/src/address.rs +++ b/crates/types/src/address.rs @@ -68,3 +68,29 @@ impl DataRef { } } } + +/// A position in bytes inside a mapped window: a device register, a field +/// of a ring or command. Adding a byte count moves it along. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] +pub struct ByteOffset(usize); + +impl ByteOffset { + pub const ZERO: Self = Self(0); + + pub const fn new(bytes: usize) -> Self { + Self(bytes) + } + + pub const fn raw(&self) -> usize { + self.0 + } +} + +impl core::ops::Add for ByteOffset { + type Output = Self; + + /// Saturates: an offset past the end is refused where it is used. + fn add(self, bytes: usize) -> Self { + Self(self.0.saturating_add(bytes)) + } +}