refactor: ByteOffset newtype for window/register/ring offsets; named virtio-gpu cmd fields
This commit is contained in:
@@ -23,7 +23,7 @@ fn main() {
|
|||||||
libkos::println!("smptest: cannot map the counter page");
|
libkos::println!("smptest: cannot map the counter page");
|
||||||
syscall::exit();
|
syscall::exit();
|
||||||
};
|
};
|
||||||
let words = window.atomics(0, 1 + THREADS);
|
let words = window.atomics(types::address::ByteOffset::ZERO, 1 + THREADS);
|
||||||
for _ in 1..THREADS {
|
for _ in 1..THREADS {
|
||||||
if !libkos::thread::spawn(words, spin) {
|
if !libkos::thread::spawn(words, spin) {
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use hw_virtio::block::{self, DmaAddrs};
|
|||||||
use hw_virtio::mmio::{MmioAccess, Registers};
|
use hw_virtio::mmio::{MmioAccess, Registers};
|
||||||
use hw_virtio::queue::VirtioQueue;
|
use hw_virtio::queue::VirtioQueue;
|
||||||
use port_block_device::{BlockDevice, SectorId};
|
use port_block_device::{BlockDevice, SectorId};
|
||||||
use types::address::{PhysicalAddress, VirtualAddress};
|
use types::address::{ByteOffset, PhysicalAddress, VirtualAddress};
|
||||||
use types::bounded::PageCount;
|
use types::bounded::PageCount;
|
||||||
|
|
||||||
const MMIO_VA: VirtualAddress = VirtualAddress::from_raw(0x20000);
|
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 at = |offset: usize| PhysicalAddress::from_raw(req.physical.raw() + offset);
|
||||||
let dma = DmaAddrs {
|
let dma = DmaAddrs {
|
||||||
header_pa: at(0),
|
header_pa: at(0),
|
||||||
header: req.window.sub(0, 16),
|
header: req.window.sub(ByteOffset::new(0), 16),
|
||||||
status_pa: at(16),
|
status_pa: at(16),
|
||||||
status: req.window.sub(16, 1),
|
status: req.window.sub(ByteOffset::new(16), 1),
|
||||||
data_pa: at(32),
|
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
|
let capacity = regs.read(block::CONFIG_CAPACITY) as u64
|
||||||
| (regs.read(block::CONFIG_CAPACITY + 4) as u64) << 32;
|
| (regs.read(block::CONFIG_CAPACITY + 4) as u64) << 32;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use hw_virtio::mmio::{MmioAccess, Registers};
|
|||||||
use hw_virtio::queue::VirtioQueue;
|
use hw_virtio::queue::VirtioQueue;
|
||||||
use libkos::Window;
|
use libkos::Window;
|
||||||
use port_framebuffer::Framebuffer;
|
use port_framebuffer::Framebuffer;
|
||||||
use types::address::{PhysicalAddress, VirtualAddress};
|
use types::address::{ByteOffset, PhysicalAddress, VirtualAddress};
|
||||||
use types::bounded::PageCount;
|
use types::bounded::PageCount;
|
||||||
use types::display::{Color, Dimension, ScreenCoord};
|
use types::display::{Color, Dimension, ScreenCoord};
|
||||||
|
|
||||||
@@ -50,7 +50,7 @@ impl VirtioFramebuffer {
|
|||||||
let dma = GpuDmaAddrs {
|
let dma = GpuDmaAddrs {
|
||||||
cmd_pa: cmd.physical,
|
cmd_pa: cmd.physical,
|
||||||
cmd: cmd.window,
|
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)
|
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()?)))
|
.and_then(|(w, h)| Some((u16::try_from(w).ok()?, u16::try_from(h).ok()?)))
|
||||||
@@ -93,7 +93,7 @@ impl Framebuffer for VirtioFramebuffer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let offset = (y.raw() as usize) * self.stride + (x.raw() as usize) * FB_BPP;
|
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) {
|
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) {
|
fn clear(&mut self, color: Color) {
|
||||||
let total = self.width.raw() as usize * self.height.raw() as usize;
|
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) {
|
fn flush(&mut self) {
|
||||||
|
|||||||
@@ -3,11 +3,12 @@
|
|||||||
|
|
||||||
use libkos::Window;
|
use libkos::Window;
|
||||||
use port_input_device::{InputDevice, InputEvent, Key};
|
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_VA: VirtualAddress = VirtualAddress::from_raw(0x20000);
|
||||||
const UART_IER_OFFSET: usize = 1;
|
const UART_RBR: ByteOffset = ByteOffset::new(0);
|
||||||
const UART_LSR_OFFSET: usize = 5;
|
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
|
/// IER bit: interrupt when a received byte is waiting (cleared by reading
|
||||||
/// it, so `acknowledge` has nothing to do).
|
/// it, so `acknowledge` has nothing to do).
|
||||||
const IER_RX_READY: u8 = 1;
|
const IER_RX_READY: u8 = 1;
|
||||||
@@ -27,7 +28,7 @@ impl UartInputDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_byte(&self) -> Option<u8> {
|
fn read_byte(&self) -> Option<u8> {
|
||||||
self.has_event().then(|| self.regs.read(0))
|
self.has_event().then(|| self.regs.read(UART_RBR))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn byte_to_key(byte: u8) -> Key {
|
fn byte_to_key(byte: u8) -> Key {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use core::sync::atomic::{Ordering, fence};
|
|||||||
use hw_virtio::mmio::{MmioAccess, Registers};
|
use hw_virtio::mmio::{MmioAccess, Registers};
|
||||||
use libkos::Window;
|
use libkos::Window;
|
||||||
use port_input_device::{InputDevice, InputEvent};
|
use port_input_device::{InputDevice, InputEvent};
|
||||||
use types::address::{PhysicalAddress, VirtualAddress};
|
use types::address::{ByteOffset, PhysicalAddress, VirtualAddress};
|
||||||
use types::bounded::PageCount;
|
use types::bounded::PageCount;
|
||||||
|
|
||||||
const INPUT_MMIO_VA: VirtualAddress = VirtualAddress::from_raw(0x25000);
|
const INPUT_MMIO_VA: VirtualAddress = VirtualAddress::from_raw(0x25000);
|
||||||
@@ -18,8 +18,8 @@ const QUEUE_SIZE: usize = 32;
|
|||||||
const EVENT_SIZE: usize = 8;
|
const EVENT_SIZE: usize = 8;
|
||||||
const DESC_SIZE: usize = 16;
|
const DESC_SIZE: usize = 16;
|
||||||
/// Where the legacy layout puts the available and used rings.
|
/// Where the legacy layout puts the available and used rings.
|
||||||
const AVAIL: usize = QUEUE_SIZE * DESC_SIZE;
|
const AVAIL: ByteOffset = ByteOffset::new(QUEUE_SIZE * DESC_SIZE);
|
||||||
const USED: usize = 4096;
|
const USED: ByteOffset = ByteOffset::new(4096);
|
||||||
|
|
||||||
const EV_KEY: u16 = 1;
|
const EV_KEY: u16 = 1;
|
||||||
const KEY_PRESS: u32 = 1;
|
const KEY_PRESS: u32 = 1;
|
||||||
@@ -67,7 +67,7 @@ impl VirtioInputDevice {
|
|||||||
/// Every buffer goes to the device, one event each.
|
/// Every buffer goes to the device, one event each.
|
||||||
fn fill_queue(&mut self) {
|
fn fill_queue(&mut self) {
|
||||||
for i in 0..QUEUE_SIZE {
|
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, (self.events_pa.raw() + i * EVENT_SIZE) as u64);
|
||||||
self.rings.write(desc + 8, EVENT_SIZE as u32);
|
self.rings.write(desc + 8, EVENT_SIZE as u32);
|
||||||
self.rings.write(desc + 12, 2u16); // WRITE flag
|
self.rings.write(desc + 12, 2u16); // WRITE flag
|
||||||
@@ -92,7 +92,7 @@ impl VirtioInputDevice {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn read_event(&self, desc_idx: u16) -> (u16, u16, u32) {
|
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))
|
(self.events.read(at), self.events.read(at + 2), self.events.read(at + 4))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::mmio::MmioAccess;
|
use crate::mmio::MmioAccess;
|
||||||
use crate::queue::{Descriptor, VirtioQueue};
|
use crate::queue::{Descriptor, VirtioQueue};
|
||||||
use libkos::Window;
|
use libkos::Window;
|
||||||
use types::address::PhysicalAddress;
|
use types::address::{ByteOffset, PhysicalAddress};
|
||||||
|
|
||||||
const SECTOR_SIZE: usize = 512;
|
const SECTOR_SIZE: usize = 512;
|
||||||
|
|
||||||
@@ -16,11 +16,15 @@ pub struct DmaAddrs {
|
|||||||
pub data: Window,
|
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) {
|
fn put_header(dma: &DmaAddrs, req_type: u32, sector: u32) {
|
||||||
dma.header.write(0, req_type);
|
dma.header.write(HEADER_TYPE, req_type);
|
||||||
dma.header.write(4, 0u32);
|
dma.header.write(HEADER_RESERVED, 0u32);
|
||||||
dma.header.write(8, sector as u64);
|
dma.header.write(HEADER_SECTOR, sector as u64);
|
||||||
dma.status.write(0, 0xFFu8);
|
dma.status.write(ByteOffset::ZERO, 0xFFu8);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_sector(
|
pub fn read_sector(
|
||||||
@@ -42,15 +46,15 @@ pub fn read_sector(
|
|||||||
|
|
||||||
if !queue.submit(mmio, &descs) { return false; }
|
if !queue.submit(mmio, &descs) { return false; }
|
||||||
|
|
||||||
if dma.status.read::<u8>(0) != 0 { return false; }
|
if dma.status.read::<u8>(ByteOffset::ZERO) != 0 { return false; }
|
||||||
dma.data.copy_to(0, &mut buf[..SECTOR_SIZE]);
|
dma.data.copy_to(ByteOffset::ZERO, &mut buf[..SECTOR_SIZE]);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Request types (virtio spec 5.2.6).
|
/// Request types (virtio spec 5.2.6).
|
||||||
const T_OUT: u32 = 1;
|
const T_OUT: u32 = 1;
|
||||||
/// Device configuration: the disk's size in 512-byte sectors (le64).
|
/// 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.
|
/// Writes one sector from `buf`; true once the device reports success.
|
||||||
pub fn write_sector(
|
pub fn write_sector(
|
||||||
@@ -62,7 +66,7 @@ pub fn write_sector(
|
|||||||
) -> bool {
|
) -> bool {
|
||||||
if buf.len() < SECTOR_SIZE { return false; }
|
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);
|
put_header(dma, T_OUT, sector);
|
||||||
|
|
||||||
// The device reads the data this time; only the status is written back.
|
// 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 },
|
Descriptor { addr: dma.status_pa.raw() as u64, len: 1, writable: true },
|
||||||
];
|
];
|
||||||
if !queue.submit(mmio, &descs) { return false; }
|
if !queue.submit(mmio, &descs) { return false; }
|
||||||
dma.status.read::<u8>(0) == 0
|
dma.status.read::<u8>(ByteOffset::ZERO) == 0
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
use crate::mmio::MmioAccess;
|
use crate::mmio::MmioAccess;
|
||||||
use crate::queue::{Descriptor, VirtioQueue};
|
use crate::queue::{Descriptor, VirtioQueue};
|
||||||
use libkos::Window;
|
use libkos::Window;
|
||||||
use types::address::PhysicalAddress;
|
use types::address::{ByteOffset, PhysicalAddress};
|
||||||
use types::display::Dimension;
|
use types::display::Dimension;
|
||||||
|
|
||||||
/// The command page: commands go out from its start, responses come back
|
/// The command page: commands go out from its start, responses come back
|
||||||
@@ -12,59 +12,76 @@ pub struct GpuDmaAddrs {
|
|||||||
pub resp_pa: PhysicalAddress,
|
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 {
|
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);
|
write_cmd_header(&dma.cmd, 0x0101);
|
||||||
dma.cmd.write(24, resource_id);
|
// Resource id, format (1: B8G8R8A8), width, height.
|
||||||
dma.cmd.write(28, 1u32);
|
dma.cmd.write(BODY, resource_id);
|
||||||
dma.cmd.write(32, width.raw() as u32);
|
dma.cmd.write(BODY + 4, 1u32);
|
||||||
dma.cmd.write(36, height.raw() as u32);
|
dma.cmd.write(BODY + 8, width.raw() as u32);
|
||||||
|
dma.cmd.write(BODY + 12, height.raw() as u32);
|
||||||
submit_cmd(mmio, queue, dma, 40)
|
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 {
|
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);
|
write_cmd_header(&dma.cmd, 0x0106);
|
||||||
dma.cmd.write(24, resource_id);
|
// Resource id, one memory entry: its address and length.
|
||||||
dma.cmd.write(28, 1u32);
|
dma.cmd.write(BODY, resource_id);
|
||||||
dma.cmd.write(32, fb_pa.raw() as u64 as u64);
|
dma.cmd.write(BODY + 4, 1u32);
|
||||||
dma.cmd.write(40, fb_size);
|
dma.cmd.write(BODY + 8, fb_pa.raw() as u64);
|
||||||
|
dma.cmd.write(BODY + 16, fb_size);
|
||||||
submit_cmd(mmio, queue, dma, 48)
|
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 {
|
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);
|
write_cmd_header(&dma.cmd, 0x0103);
|
||||||
dma.cmd.write(32, width.raw() as u32);
|
// The rectangle (from 0, 0), then scanout and resource ids.
|
||||||
dma.cmd.write(36, height.raw() as u32);
|
dma.cmd.write(RECT_W, width.raw() as u32);
|
||||||
dma.cmd.write(40, scanout_id);
|
dma.cmd.write(RECT_H, height.raw() as u32);
|
||||||
dma.cmd.write(44, resource_id);
|
dma.cmd.write(BODY + 16, scanout_id);
|
||||||
|
dma.cmd.write(BODY + 20, resource_id);
|
||||||
submit_cmd(mmio, queue, dma, 48)
|
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 {
|
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);
|
write_cmd_header(&dma.cmd, 0x0105);
|
||||||
dma.cmd.write(24, x);
|
put_rect(&dma.cmd, x, y, w, h);
|
||||||
dma.cmd.write(28, y);
|
// Offset into the resource's memory, then the resource id.
|
||||||
dma.cmd.write(32, w);
|
dma.cmd.write(BODY + 16, 0u64);
|
||||||
dma.cmd.write(36, h);
|
dma.cmd.write(BODY + 24, resource_id);
|
||||||
dma.cmd.write(40, 0u64);
|
|
||||||
dma.cmd.write(48, resource_id);
|
|
||||||
submit_cmd(mmio, queue, dma, 56)
|
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 {
|
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);
|
write_cmd_header(&dma.cmd, 0x0104);
|
||||||
dma.cmd.write(24, x);
|
put_rect(&dma.cmd, x, y, w, h);
|
||||||
dma.cmd.write(28, y);
|
dma.cmd.write(BODY + 16, resource_id);
|
||||||
dma.cmd.write(32, w);
|
|
||||||
dma.cmd.write(36, h);
|
|
||||||
dma.cmd.write(40, resource_id);
|
|
||||||
submit_cmd(mmio, queue, dma, 48)
|
submit_cmd(mmio, queue, dma, 48)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_cmd_header(cmd: &Window, cmd_type: u32) {
|
fn write_cmd_header(cmd: &Window, cmd_type: u32) {
|
||||||
cmd.fill_u32(0, 128, 0);
|
cmd.fill_u32(ByteOffset::ZERO, 128, 0);
|
||||||
cmd.write(0, cmd_type);
|
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)> {
|
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 !queue.submit(mmio, &descs) { return None; }
|
||||||
if dma.cmd.read::<u32>(RESPONSE) != 0x1101 { return None; }
|
if dma.cmd.read::<u32>(RESPONSE) != 0x1101 { return None; }
|
||||||
let w: u32 = dma.cmd.read(RESPONSE + 24 + 8);
|
let w: u32 = dma.cmd.read(RESPONSE + DISPLAY_W.raw());
|
||||||
let h: u32 = dma.cmd.read(RESPONSE + 24 + 12);
|
let h: u32 = dma.cmd.read(RESPONSE + DISPLAY_H.raw());
|
||||||
if w > 0 && h > 0 { Some((w, h)) } else { None }
|
if w > 0 && h > 0 { Some((w, h)) } else { None }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,26 +1,28 @@
|
|||||||
|
use types::address::ByteOffset;
|
||||||
|
|
||||||
pub trait MmioAccess {
|
pub trait MmioAccess {
|
||||||
fn read(&self, offset: usize) -> u32;
|
fn read(&self, offset: ByteOffset) -> u32;
|
||||||
fn write(&self, offset: usize, val: u32);
|
fn write(&self, offset: ByteOffset, val: u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub const MAGIC: u32 = 0x74726976;
|
pub const MAGIC: u32 = 0x74726976;
|
||||||
|
|
||||||
pub const REG_MAGIC: usize = 0x000;
|
pub const REG_MAGIC: ByteOffset = ByteOffset::new(0x000);
|
||||||
pub const REG_VERSION: usize = 0x004;
|
pub const REG_VERSION: ByteOffset = ByteOffset::new(0x004);
|
||||||
pub const REG_DEVICE_ID: usize = 0x008;
|
pub const REG_DEVICE_ID: ByteOffset = ByteOffset::new(0x008);
|
||||||
pub const REG_STATUS: usize = 0x070;
|
pub const REG_STATUS: ByteOffset = ByteOffset::new(0x070);
|
||||||
pub const REG_GUEST_PAGE_SIZE: usize = 0x028;
|
pub const REG_GUEST_PAGE_SIZE: ByteOffset = ByteOffset::new(0x028);
|
||||||
pub const REG_FEATURES_SEL: usize = 0x014;
|
pub const REG_FEATURES_SEL: ByteOffset = ByteOffset::new(0x014);
|
||||||
pub const REG_FEATURES: usize = 0x010;
|
pub const REG_FEATURES: ByteOffset = ByteOffset::new(0x010);
|
||||||
pub const REG_DRIVER_FEATURES_SEL: usize = 0x024;
|
pub const REG_DRIVER_FEATURES_SEL: ByteOffset = ByteOffset::new(0x024);
|
||||||
pub const REG_DRIVER_FEATURES: usize = 0x020;
|
pub const REG_DRIVER_FEATURES: ByteOffset = ByteOffset::new(0x020);
|
||||||
pub const REG_QUEUE_SEL: usize = 0x030;
|
pub const REG_QUEUE_SEL: ByteOffset = ByteOffset::new(0x030);
|
||||||
pub const REG_QUEUE_NUM_MAX: usize = 0x034;
|
pub const REG_QUEUE_NUM_MAX: ByteOffset = ByteOffset::new(0x034);
|
||||||
pub const REG_QUEUE_NUM: usize = 0x038;
|
pub const REG_QUEUE_NUM: ByteOffset = ByteOffset::new(0x038);
|
||||||
pub const REG_QUEUE_PFN: usize = 0x040;
|
pub const REG_QUEUE_PFN: ByteOffset = ByteOffset::new(0x040);
|
||||||
pub const REG_QUEUE_NOTIFY: usize = 0x050;
|
pub const REG_QUEUE_NOTIFY: ByteOffset = ByteOffset::new(0x050);
|
||||||
pub const REG_INTERRUPT_STATUS: usize = 0x060;
|
pub const REG_INTERRUPT_STATUS: ByteOffset = ByteOffset::new(0x060);
|
||||||
pub const REG_INTERRUPT_ACK: usize = 0x064;
|
pub const REG_INTERRUPT_ACK: ByteOffset = ByteOffset::new(0x064);
|
||||||
|
|
||||||
pub const STATUS_ACK: u32 = 1;
|
pub const STATUS_ACK: u32 = 1;
|
||||||
pub const STATUS_DRIVER: u32 = 2;
|
pub const STATUS_DRIVER: u32 = 2;
|
||||||
@@ -42,11 +44,11 @@ impl Registers {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MmioAccess for Registers {
|
impl MmioAccess for Registers {
|
||||||
fn read(&self, offset: usize) -> u32 {
|
fn read(&self, offset: ByteOffset) -> u32 {
|
||||||
self.0.read(offset)
|
self.0.read(offset)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(&self, offset: usize, val: u32) {
|
fn write(&self, offset: ByteOffset, val: u32) {
|
||||||
self.0.write(offset, val)
|
self.0.write(offset, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
use crate::mmio::{MmioAccess, REG_QUEUE_NOTIFY};
|
use crate::mmio::{MmioAccess, REG_QUEUE_NOTIFY};
|
||||||
use core::sync::atomic::{Ordering, fence};
|
use core::sync::atomic::{Ordering, fence};
|
||||||
use libkos::Window;
|
use libkos::Window;
|
||||||
|
use types::address::ByteOffset;
|
||||||
|
|
||||||
pub const QUEUE_SIZE: usize = 8;
|
pub const QUEUE_SIZE: usize = 8;
|
||||||
const DESC_SIZE: usize = 16;
|
const DESC_SIZE: usize = 16;
|
||||||
const TIMEOUT: u32 = 10_000_000;
|
const TIMEOUT: u32 = 10_000_000;
|
||||||
/// Where the legacy layout puts the available and used rings.
|
/// Where the legacy layout puts the available and used rings.
|
||||||
const AVAIL: usize = QUEUE_SIZE * DESC_SIZE;
|
const AVAIL: ByteOffset = ByteOffset::new(QUEUE_SIZE * DESC_SIZE);
|
||||||
const USED: usize = 4096;
|
const USED: ByteOffset = ByteOffset::new(4096);
|
||||||
|
|
||||||
pub struct Descriptor {
|
pub struct Descriptor {
|
||||||
pub addr: u64,
|
pub addr: u64,
|
||||||
@@ -32,7 +33,7 @@ impl VirtioQueue {
|
|||||||
}
|
}
|
||||||
let r = &self.rings;
|
let r = &self.rings;
|
||||||
for (i, d) in descs.iter().enumerate() {
|
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 last = i == descs.len() - 1;
|
||||||
let flags: u16 = if d.writable { 2 } else { 0 } | if !last { 1 } else { 0 };
|
let flags: u16 = if d.writable { 2 } else { 0 } | if !last { 1 } else { 0 };
|
||||||
r.write(at, d.addr);
|
r.write(at, d.addr);
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
use libkos::fs::Storage;
|
use libkos::fs::Storage;
|
||||||
use libkos::syscall;
|
use libkos::syscall;
|
||||||
use types::address::{EntryPoint, VirtualAddress};
|
use types::address::{ByteOffset, EntryPoint, VirtualAddress};
|
||||||
use types::bounded::{CapSlot, PageCount};
|
use types::bounded::{CapSlot, PageCount};
|
||||||
use types::budget::Limits;
|
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 page_count = PageCount::new(u16::try_from(seg_pages).ok()?)?;
|
||||||
let local = libkos::window::request(page_count)?;
|
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 copy_len = segment.file_size.min(segment.mem_size);
|
||||||
let src = segment
|
let src = segment
|
||||||
@@ -61,7 +61,7 @@ pub(crate) fn load_binary(
|
|||||||
.checked_add(copy_len)
|
.checked_add(copy_len)
|
||||||
.and_then(|end| elf_data.get(segment.file_offset..end));
|
.and_then(|end| elf_data.get(segment.file_offset..end));
|
||||||
if let Some(src) = src.filter(|_| copy_len > 0) {
|
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 {
|
for p in 0..seg_pages {
|
||||||
|
|||||||
@@ -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> {
|
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 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 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")?;
|
let events = syscall::create_notification().map_err(|_| "no notification")?;
|
||||||
timer_is_on_time(events)?;
|
timer_is_on_time(events)?;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
//! Every access is volatile and bounds-checked; out of range panics.
|
//! Every access is volatile and bounds-checked; out of range panics.
|
||||||
|
|
||||||
use core::sync::atomic::AtomicUsize;
|
use core::sync::atomic::AtomicUsize;
|
||||||
use types::address::VirtualAddress;
|
use types::address::{ByteOffset, VirtualAddress};
|
||||||
use types::bounded::{CapSlot, PageCount};
|
use types::bounded::{CapSlot, PageCount};
|
||||||
use types::mapping::PagePerms;
|
use types::mapping::PagePerms;
|
||||||
|
|
||||||
@@ -76,7 +76,8 @@ impl Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The address of `size` bytes at `offset`, aligned for them.
|
/// 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);
|
let end = offset.checked_add(size);
|
||||||
assert!(end.is_some_and(|e| e <= self.len), "window access out of range");
|
assert!(end.is_some_and(|e| e <= self.len), "window access out of range");
|
||||||
let addr = self.base.raw() + offset;
|
let addr = self.base.raw() + offset;
|
||||||
@@ -84,33 +85,33 @@ impl Window {
|
|||||||
addr
|
addr
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn read<T: Scalar>(&self, offset: usize) -> T {
|
pub fn read<T: Scalar>(&self, offset: ByteOffset) -> T {
|
||||||
let addr = self.at(offset, size_of::<T>(), align_of::<T>());
|
let addr = self.at(offset, size_of::<T>(), align_of::<T>());
|
||||||
// SAFETY: in range of memory mapped for this process, aligned for
|
// SAFETY: in range of memory mapped for this process, aligned for
|
||||||
// T; T is a plain integer, valid for any bits.
|
// T; T is a plain integer, valid for any bits.
|
||||||
unsafe { core::ptr::read_volatile(addr as *const T) }
|
unsafe { core::ptr::read_volatile(addr as *const T) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write<T: Scalar>(&self, offset: usize, value: T) {
|
pub fn write<T: Scalar>(&self, offset: ByteOffset, value: T) {
|
||||||
let addr = self.at(offset, size_of::<T>(), align_of::<T>());
|
let addr = self.at(offset, size_of::<T>(), align_of::<T>());
|
||||||
// SAFETY: as in `read`; the window hands out no references to it.
|
// SAFETY: as in `read`; the window hands out no references to it.
|
||||||
unsafe { core::ptr::write_volatile(addr as *mut T, value) }
|
unsafe { core::ptr::write_volatile(addr as *mut T, value) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The `len` bytes at `offset`, as a window of their own.
|
/// 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));
|
let base = VirtualAddress::from_raw(self.at(offset, len, 1));
|
||||||
Window { base, len }
|
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);
|
self.at(offset, src.len(), 1);
|
||||||
for (i, b) in src.iter().enumerate() {
|
for (i, b) in src.iter().enumerate() {
|
||||||
self.write(offset + i, *b);
|
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);
|
self.at(offset, dst.len(), 1);
|
||||||
for (i, b) in dst.iter_mut().enumerate() {
|
for (i, b) in dst.iter_mut().enumerate() {
|
||||||
*b = self.read(offset + i);
|
*b = self.read(offset + i);
|
||||||
@@ -118,7 +119,7 @@ impl Window {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// `count` u32s from `offset` set to `value`.
|
/// `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");
|
let bytes = count.checked_mul(4).expect("window fill too large");
|
||||||
self.at(offset, bytes, 4);
|
self.at(offset, bytes, 4);
|
||||||
for i in 0..count {
|
for i in 0..count {
|
||||||
@@ -128,7 +129,7 @@ impl Window {
|
|||||||
|
|
||||||
/// `count` words from `offset` as atomics, for memory shared with
|
/// `count` words from `offset` as atomics, for memory shared with
|
||||||
/// threads or processes that only ever touch it atomically.
|
/// 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::<AtomicUsize>()).expect("too many atomics");
|
let bytes = count.checked_mul(size_of::<AtomicUsize>()).expect("too many atomics");
|
||||||
let addr = self.at(offset, bytes, align_of::<AtomicUsize>());
|
let addr = self.at(offset, bytes, align_of::<AtomicUsize>());
|
||||||
// SAFETY: mapped and aligned; AtomicUsize may be shared and is
|
// SAFETY: mapped and aligned; AtomicUsize may be shared and is
|
||||||
|
|||||||
@@ -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<usize> 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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user