refactor: apply newtypes throughout userspace API and display types

Every syscall stub now uses proper types from types/ — CapSlot,
EntryPoint, StackPointer, VirtualAddress, PageCount, ProcessId,
PermissionSet, Ticks. The compiler prevents swapping entry/stack,
passing a ProcessId where a CapSlot is expected, or using an
address as a page count.

New display types in types/display.rs:
  - Color(u32) with rgb() constructor
  - ScreenCoord(u16) for x/y positions
  - Dimension(u16) for width/height

Updated throughout:
  - userspace/syscall.rs: all params typed, returns Option instead
    of sentinel usize::MAX
  - server-framework: endpoint param is CapSlot
  - port-framebuffer: trait uses Color, ScreenCoord, Dimension
  - hw-virtio: DmaAddrs uses PhysicalAddress/VirtualAddress,
    GPU commands use Dimension
  - All servers, drivers, bins, apps updated for new signatures
  - permission-manager: process field uses ProcessId
  - PermissionSet: added bits() accessor

89 tests pass, full QEMU boot verified.
This commit is contained in:
2026-10-18 13:45:40 +02:00
parent 3a0395453b
commit 7c7e3b8748
42 changed files with 416 additions and 307 deletions

11
Cargo.lock generated
View File

@@ -57,6 +57,7 @@ dependencies = [
"display-server",
"driver-framebuffer-virtio",
"server-framework",
"types",
"userspace",
"userspace-rt",
]
@@ -84,6 +85,7 @@ version = "0.1.0"
dependencies = [
"hw-virtio",
"port-block-device",
"types",
]
[[package]]
@@ -91,6 +93,7 @@ name = "driver-framebuffer-mock"
version = "0.1.0"
dependencies = [
"port-framebuffer",
"types",
]
[[package]]
@@ -99,12 +102,14 @@ version = "0.1.0"
dependencies = [
"hw-virtio",
"port-framebuffer",
"types",
]
[[package]]
name = "echo"
version = "0.1.0"
dependencies = [
"types",
"userspace",
"userspace-rt",
]
@@ -131,6 +136,9 @@ dependencies = [
[[package]]
name = "hw-virtio"
version = "0.1.0"
dependencies = [
"types",
]
[[package]]
name = "init"
@@ -217,6 +225,9 @@ version = "0.1.0"
[[package]]
name = "port-framebuffer"
version = "0.1.0"
dependencies = [
"types",
]
[[package]]
name = "port-input-device"

View File

@@ -8,10 +8,10 @@ const VQUEUE_VA: usize = 0x21000;
const CMDBUF_VA: usize = 0x23000;
const PHYSINFO_VA: usize = 0x24000;
const FB_VA: usize = 0x100000;
const FB_WIDTH: u32 = 640;
const FB_HEIGHT: u32 = 480;
const FB_WIDTH: types::display::Dimension = types::display::Dimension::new(640);
const FB_HEIGHT: types::display::Dimension = types::display::Dimension::new(480);
const FB_BPP: usize = 4;
const FB_PAGES: usize = (FB_WIDTH as usize * FB_HEIGHT as usize * FB_BPP + 4095) / 4096;
const FB_PAGES: usize = (FB_WIDTH.raw() as usize * FB_HEIGHT.raw() as usize * FB_BPP + 4095) / 4096;
pub fn configure(uart: &Uart, display_pid: ProcessId) {
let gpu = match VirtioGpu::probe() {
@@ -43,8 +43,8 @@ pub fn configure(uart: &Uart, display_pid: ProcessId) {
core::ptr::write_volatile(info, cmd_phys as u64);
core::ptr::write_volatile(info.add(1), (cmd_phys + 512) as u64);
core::ptr::write_volatile(info.add(2), fb_phys as u64);
core::ptr::write_volatile(info.add(3), FB_WIDTH as u64);
core::ptr::write_volatile(info.add(4), FB_HEIGHT as u64);
core::ptr::write_volatile(info.add(3), FB_WIDTH.raw() as u64);
core::ptr::write_volatile(info.add(4), FB_HEIGHT.raw() as u64);
}
gpu.init_device(vq_phys);

View File

@@ -1,6 +1,7 @@
use hw_virtio::mmio::{self, DirectMmio};
use hw_virtio::queue::{QueueAddrs, VirtioQueue, QUEUE_SIZE};
use hw_virtio::block::{self, DmaAddrs};
use types::address::{PhysicalAddress, VirtualAddress};
const SECTOR_SIZE: usize = 512;
@@ -31,12 +32,12 @@ impl VirtioBlk {
let dma = unsafe {
DmaAddrs {
header_pa: core::ptr::addr_of!(REQ_HEADER) as u64,
header_va: core::ptr::addr_of_mut!(REQ_HEADER) as usize,
status_pa: core::ptr::addr_of!(REQ_STATUS) as u64,
status_va: core::ptr::addr_of_mut!(REQ_STATUS) as usize,
data_pa: core::ptr::addr_of!(SECTOR_BUF) as u64,
data_va: core::ptr::addr_of_mut!(SECTOR_BUF) as usize,
header_pa: PhysicalAddress::from_raw(core::ptr::addr_of!(REQ_HEADER) as usize),
header_va: VirtualAddress::from_raw(core::ptr::addr_of_mut!(REQ_HEADER) as usize),
status_pa: PhysicalAddress::from_raw(core::ptr::addr_of!(REQ_STATUS) as usize),
status_va: VirtualAddress::from_raw(core::ptr::addr_of_mut!(REQ_STATUS) as usize),
data_pa: PhysicalAddress::from_raw(core::ptr::addr_of!(SECTOR_BUF) as usize),
data_va: VirtualAddress::from_raw(core::ptr::addr_of_mut!(SECTOR_BUF) as usize),
}
};

View File

@@ -1,6 +1,8 @@
use hw_virtio::mmio::{self, DirectMmio};
use hw_virtio::queue::{QueueAddrs, VirtioQueue, QUEUE_SIZE};
use hw_virtio::gpu::{self, GpuDmaAddrs};
use types::address::{PhysicalAddress, VirtualAddress};
use types::display::Dimension;
pub struct VirtioGpu {
mmio: DirectMmio,
@@ -20,15 +22,15 @@ impl VirtioGpu {
mmio::init_legacy_device(&self.mmio, vqueue_phys, QUEUE_SIZE as u32);
}
pub fn setup_framebuffer(&self, cmd_phys: usize, vq_phys: usize, fb_phys: usize, width: u32, height: u32) {
pub fn setup_framebuffer(&self, cmd_phys: usize, vq_phys: usize, fb_phys: usize, width: Dimension, height: Dimension) {
let queue = VirtioQueue::new(QueueAddrs::from_base(vq_phys));
let dma = GpuDmaAddrs {
cmd_pa: cmd_phys as u64,
cmd_va: cmd_phys,
resp_pa: (cmd_phys + 512) as u64,
cmd_pa: PhysicalAddress::from_raw(cmd_phys),
cmd_va: VirtualAddress::from_raw(cmd_phys),
resp_pa: PhysicalAddress::from_raw(cmd_phys + 512),
};
gpu::create_2d(&self.mmio, &queue, &dma, 1, width, height);
gpu::attach_backing(&self.mmio, &queue, &dma, 1, fb_phys as u64, width * height * 4);
gpu::attach_backing(&self.mmio, &queue, &dma, 1, PhysicalAddress::from_raw(fb_phys), width.raw() as u32 * height.raw() as u32 * 4);
gpu::set_scanout(&self.mmio, &queue, &dma, 0, 1, width, height);
}
}

View File

@@ -9,5 +9,6 @@ test = false
bench = false
[dependencies]
types = { workspace = true }
userspace = { workspace = true }
userspace-rt = { workspace = true }

View File

@@ -3,14 +3,13 @@
extern crate userspace_rt;
use types::bounded::CapSlot;
use userspace::syscall;
#[unsafe(no_mangle)]
extern "C" fn main() {
// Cap slot 0 = endpoint cap (granted by adapter at boot)
let ep: usize = 0;
let ep = CapSlot::new(0).unwrap();
// Receive a message, echo it back with word0 incremented
let (w0, w1, w2, w3) = syscall::receive(ep);
syscall::send(ep, w0.wrapping_add(1), w1, w2, w3);

View File

@@ -5,11 +5,13 @@ extern crate userspace_rt;
use snake::game::{Direction, GameState, Snake};
use snake::renderer::{render, RenderCommand};
use types::bounded::CapSlot;
use types::time::Ticks;
use userspace_rt::syscall;
const DISPLAY_EP: usize = 0;
const INPUT_EP: usize = 1;
const TICK_DELAY: usize = 2_000_000;
fn display_ep() -> CapSlot { CapSlot::new(0).unwrap() }
fn input_ep() -> CapSlot { CapSlot::new(1).unwrap() }
const TICK_DELAY: u64 = 2_000_000;
const MAX_RENDER_COMMANDS: usize = 192;
#[unsafe(no_mangle)]
@@ -33,8 +35,8 @@ extern "C" fn main() {
}
fn handle_input(snake: &mut Snake) {
let _ = syscall::send(INPUT_EP, 0, 0, 0, 0);
let (tag, kind, _, _) = syscall::receive(INPUT_EP);
let _ = syscall::send(input_ep(), 0, 0, 0, 0);
let (tag, kind, _, _) = syscall::receive(input_ep());
if tag == 1 {
match kind {
@@ -48,25 +50,25 @@ fn handle_input(snake: &mut Snake) {
}
fn send_render_commands(commands: &[RenderCommand], count: usize) {
let _ = syscall::send(DISPLAY_EP, 1, 0, 0, 0);
let _ = syscall::receive(DISPLAY_EP);
let _ = syscall::send(display_ep(), 1, 0, 0, 0);
let _ = syscall::receive(display_ep());
for i in 0..count {
let cmd = &commands[i];
let xy = cmd.x as usize | ((cmd.y as usize) << 16);
let wh = cmd.w as usize | ((cmd.h as usize) << 16);
let _ = syscall::send(DISPLAY_EP, 0, xy, wh, cmd.color as usize);
let _ = syscall::receive(DISPLAY_EP);
let _ = syscall::send(display_ep(), 0, xy, wh, cmd.color as usize);
let _ = syscall::receive(display_ep());
}
let _ = syscall::send(DISPLAY_EP, 2, 0, 0, 0);
let _ = syscall::receive(DISPLAY_EP);
let _ = syscall::send(display_ep(), 2, 0, 0, 0);
let _ = syscall::receive(display_ep());
}
fn wait_tick() {
let start = syscall::get_time();
let deadline = start + TICK_DELAY;
while syscall::get_time() < deadline {
let deadline = Ticks::from_raw(start.raw() + TICK_DELAY);
while syscall::get_time().raw() < deadline.raw() {
syscall::yield_now();
}
}

View File

@@ -9,6 +9,7 @@ test = false
bench = false
[dependencies]
types = { workspace = true }
userspace-rt = { workspace = true }
userspace = { workspace = true }
server-framework = { workspace = true }

View File

@@ -4,11 +4,13 @@
extern crate userspace_rt;
use driver_framebuffer_virtio::VirtioFramebuffer;
use types::bounded::CapSlot;
#[unsafe(no_mangle)]
extern "C" fn main() {
let fb = VirtioFramebuffer::new();
let mut srv = display_server::server::DisplayServer::new(fb);
server_framework::serve(&mut srv, 0);
let ep = CapSlot::new(0).unwrap();
server_framework::serve(&mut srv, ep);
loop { userspace::syscall::yield_now(); }
}

View File

@@ -6,3 +6,4 @@ edition = "2024"
[dependencies]
port-block-device = { workspace = true }
hw-virtio = { workspace = true }
types = { workspace = true }

View File

@@ -4,6 +4,7 @@ use hw_virtio::mmio::MmioAccess;
use hw_virtio::queue::{QueueAddrs, VirtioQueue};
use hw_virtio::block::{self, DmaAddrs};
use port_block_device::{BlockDevice, SectorId};
use types::address::{PhysicalAddress, VirtualAddress};
const MMIO_BASE: usize = 0x20000;
const VQUEUE_VA: usize = 0x21000;
@@ -35,19 +36,19 @@ impl VirtioBlockDevice {
let (hdr_pa, status_pa, data_pa) = unsafe {
let p = PHYSINFO_VA as *const u64;
(
core::ptr::read_volatile(p),
core::ptr::read_volatile(p.add(1)),
core::ptr::read_volatile(p.add(2)),
core::ptr::read_volatile(p) as usize,
core::ptr::read_volatile(p.add(1)) as usize,
core::ptr::read_volatile(p.add(2)) as usize,
)
};
let dma = DmaAddrs {
header_pa: hdr_pa,
header_va: REQ_HDR_VA,
status_pa,
status_va: REQ_HDR_VA + 16,
data_pa,
data_va: REQ_HDR_VA + 32,
header_pa: PhysicalAddress::from_raw(hdr_pa),
header_va: VirtualAddress::from_raw(REQ_HDR_VA),
status_pa: PhysicalAddress::from_raw(status_pa),
status_va: VirtualAddress::from_raw(REQ_HDR_VA + 16),
data_pa: PhysicalAddress::from_raw(data_pa),
data_va: VirtualAddress::from_raw(REQ_HDR_VA + 32),
};
Self { mmio: UserMmio, queue, dma }

View File

@@ -5,3 +5,4 @@ edition = "2024"
[dependencies]
port-framebuffer = { workspace = true }
types = { workspace = true }

View File

@@ -1,21 +1,22 @@
#![no_std]
use port_framebuffer::Framebuffer;
use types::display::{Color, Dimension, ScreenCoord};
pub struct MockFramebuffer {
width: u16,
height: u16,
pixels: [(u16, u16, u32); 4096],
width: Dimension,
height: Dimension,
pixels: [(ScreenCoord, ScreenCoord, Color); 4096],
pixel_count: usize,
flushed: bool,
}
impl MockFramebuffer {
pub fn new(width: u16, height: u16) -> Self {
pub fn new(width: Dimension, height: Dimension) -> Self {
Self {
width,
height,
pixels: [(0, 0, 0); 4096],
pixels: [(ScreenCoord::new(0), ScreenCoord::new(0), Color::from_raw(0)); 4096],
pixel_count: 0,
flushed: false,
}
@@ -29,31 +30,31 @@ impl MockFramebuffer {
self.flushed
}
pub fn pixel_at(&self, index: usize) -> (u16, u16, u32) {
pub fn pixel_at(&self, index: usize) -> (ScreenCoord, ScreenCoord, Color) {
self.pixels[index]
}
}
impl Framebuffer for MockFramebuffer {
fn width(&self) -> u16 { self.width }
fn height(&self) -> u16 { self.height }
fn width(&self) -> Dimension { self.width }
fn height(&self) -> Dimension { self.height }
fn write_pixel(&mut self, x: u16, y: u16, color: u32) {
fn write_pixel(&mut self, x: ScreenCoord, y: ScreenCoord, color: Color) {
if self.pixel_count < self.pixels.len() {
self.pixels[self.pixel_count] = (x, y, color);
self.pixel_count += 1;
}
}
fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: u32) {
for dy in 0..h {
for dx in 0..w {
self.write_pixel(x + dx, y + dy, color);
fn fill_rect(&mut self, x: ScreenCoord, y: ScreenCoord, w: Dimension, h: Dimension, color: Color) {
for dy in 0..h.raw() {
for dx in 0..w.raw() {
self.write_pixel(ScreenCoord::new(x.raw() + dx), ScreenCoord::new(y.raw() + dy), color);
}
}
}
fn clear(&mut self, _color: u32) {
fn clear(&mut self, _color: Color) {
self.pixel_count = 0;
}

View File

@@ -6,3 +6,4 @@ edition = "2024"
[dependencies]
port-framebuffer = { workspace = true }
hw-virtio = { workspace = true }
types = { workspace = true }

View File

@@ -4,6 +4,8 @@ use hw_virtio::mmio::MmioAccess;
use hw_virtio::queue::{QueueAddrs, VirtioQueue};
use hw_virtio::gpu::{self, GpuDmaAddrs};
use port_framebuffer::Framebuffer;
use types::address::{PhysicalAddress, VirtualAddress};
use types::display::{Color, Dimension, ScreenCoord};
const GPU_MMIO_VA: usize = 0x20000;
const VQUEUE_VA: usize = 0x21000;
@@ -24,8 +26,8 @@ impl MmioAccess for GpuMmio {
}
struct PhysInfo {
cmd_pa: u64,
resp_pa: u64,
cmd_pa: PhysicalAddress,
resp_pa: PhysicalAddress,
width: u16,
height: u16,
}
@@ -34,8 +36,8 @@ fn read_phys_info() -> PhysInfo {
unsafe {
let p = PHYSINFO_VA as *const u64;
PhysInfo {
cmd_pa: core::ptr::read_volatile(p),
resp_pa: core::ptr::read_volatile(p.add(1)),
cmd_pa: PhysicalAddress::from_raw(core::ptr::read_volatile(p) as usize),
resp_pa: PhysicalAddress::from_raw(core::ptr::read_volatile(p.add(1)) as usize),
width: core::ptr::read_volatile(p.add(3)) as u16,
height: core::ptr::read_volatile(p.add(4)) as u16,
}
@@ -46,50 +48,56 @@ pub struct VirtioFramebuffer {
mmio: GpuMmio,
queue: VirtioQueue,
dma: GpuDmaAddrs,
width: u16,
height: u16,
width: Dimension,
height: Dimension,
stride: usize,
}
impl VirtioFramebuffer {
pub fn new() -> Self {
let phys = read_phys_info();
let width = Dimension::new(phys.width);
let height = Dimension::new(phys.height);
let stride = phys.width as usize * FB_BPP;
Self {
mmio: GpuMmio,
queue: VirtioQueue::new(QueueAddrs::from_base(VQUEUE_VA)),
dma: GpuDmaAddrs { cmd_pa: phys.cmd_pa, cmd_va: CMDBUF_VA, resp_pa: phys.resp_pa },
width: phys.width,
height: phys.height,
dma: GpuDmaAddrs { cmd_pa: phys.cmd_pa, cmd_va: VirtualAddress::from_raw(CMDBUF_VA), resp_pa: phys.resp_pa },
width,
height,
stride,
}
}
}
impl Framebuffer for VirtioFramebuffer {
fn width(&self) -> u16 { self.width }
fn height(&self) -> u16 { self.height }
fn width(&self) -> Dimension { self.width }
fn height(&self) -> Dimension { self.height }
fn write_pixel(&mut self, x: u16, y: u16, color: u32) {
if x >= self.width || y >= self.height { return; }
let offset = (y as usize) * self.stride + (x as usize) * FB_BPP;
unsafe { core::ptr::write_volatile((FB_VA + offset) as *mut u32, color); }
fn write_pixel(&mut self, x: ScreenCoord, y: ScreenCoord, color: Color) {
if x.raw() >= self.width.raw() || y.raw() >= self.height.raw() { return; }
let offset = (y.raw() as usize) * self.stride + (x.raw() as usize) * FB_BPP;
unsafe { core::ptr::write_volatile((FB_VA + offset) as *mut u32, color.raw()); }
}
fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: u32) {
for dy in 0..h { for dx in 0..w { self.write_pixel(x + dx, y + dy, color); } }
fn fill_rect(&mut self, x: ScreenCoord, y: ScreenCoord, w: Dimension, h: Dimension, color: Color) {
for dy in 0..h.raw() {
for dx in 0..w.raw() {
self.write_pixel(ScreenCoord::new(x.raw() + dx), ScreenCoord::new(y.raw() + dy), color);
}
}
}
fn clear(&mut self, color: u32) {
let total = self.width as usize * self.height as usize;
fn clear(&mut self, color: Color) {
let total = self.width.raw() as usize * self.height.raw() as usize;
for i in 0..total {
unsafe { core::ptr::write_volatile((FB_VA as *mut u32).add(i), color); }
unsafe { core::ptr::write_volatile((FB_VA as *mut u32).add(i), color.raw()); }
}
}
fn flush(&mut self) {
let w = self.width as u32;
let h = self.height as u32;
let w = self.width.raw() as u32;
let h = self.height.raw() as u32;
gpu::transfer_to_host(&self.mmio, &self.queue, &self.dma, 1, 0, 0, w, h);
gpu::resource_flush(&self.mmio, &self.queue, &self.dma, 1, 0, 0, w, h);
}

View File

@@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
types = { workspace = true }

View File

@@ -1,5 +1,6 @@
use crate::mmio::MmioAccess;
use crate::queue::{Descriptor, VirtioQueue};
use types::address::{PhysicalAddress, VirtualAddress};
const SECTOR_SIZE: usize = 512;
@@ -11,12 +12,12 @@ pub struct BlockReqHeader {
}
pub struct DmaAddrs {
pub header_pa: u64,
pub header_va: usize,
pub status_pa: u64,
pub status_va: usize,
pub data_pa: u64,
pub data_va: usize,
pub header_pa: PhysicalAddress,
pub header_va: VirtualAddress,
pub status_pa: PhysicalAddress,
pub status_va: VirtualAddress,
pub data_pa: PhysicalAddress,
pub data_va: VirtualAddress,
}
pub fn read_sector(
@@ -29,28 +30,28 @@ pub fn read_sector(
if buf.len() < SECTOR_SIZE { return false; }
unsafe {
let hdr = dma.header_va as *mut BlockReqHeader;
let hdr = dma.header_va.raw() as *mut BlockReqHeader;
core::ptr::write_volatile(&raw mut (*hdr).req_type, 0);
core::ptr::write_volatile(&raw mut (*hdr).reserved, 0);
core::ptr::write_volatile(&raw mut (*hdr).sector, sector as u64);
core::ptr::write_volatile(dma.status_va as *mut u8, 0xFF);
core::ptr::write_volatile(dma.status_va.raw() as *mut u8, 0xFF);
}
let descs = [
Descriptor { addr: dma.header_pa, len: 16, writable: false },
Descriptor { addr: dma.data_pa, len: SECTOR_SIZE as u32, writable: true },
Descriptor { addr: dma.status_pa, len: 1, writable: true },
Descriptor { addr: dma.header_pa.raw() as u64, len: 16, writable: false },
Descriptor { addr: dma.data_pa.raw() as u64, len: SECTOR_SIZE as u32, writable: true },
Descriptor { addr: dma.status_pa.raw() as u64, len: 1, writable: true },
];
if !queue.submit(mmio, &descs) { return false; }
let status = unsafe { core::ptr::read_volatile(dma.status_va as *const u8) };
let status = unsafe { core::ptr::read_volatile(dma.status_va.raw() as *const u8) };
if status != 0 { return false; }
if dma.data_va != buf.as_ptr() as usize {
if dma.data_va.raw() != buf.as_ptr() as usize {
unsafe {
core::ptr::copy_nonoverlapping(
dma.data_va as *const u8,
dma.data_va.raw() as *const u8,
buf.as_mut_ptr(),
SECTOR_SIZE,
);

View File

@@ -1,42 +1,44 @@
use crate::mmio::MmioAccess;
use crate::queue::{Descriptor, VirtioQueue};
use types::address::{PhysicalAddress, VirtualAddress};
use types::display::Dimension;
pub struct GpuDmaAddrs {
pub cmd_pa: u64,
pub cmd_va: usize,
pub resp_pa: u64,
pub cmd_pa: PhysicalAddress,
pub cmd_va: VirtualAddress,
pub resp_pa: PhysicalAddress,
}
pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, width: u32, height: u32) {
pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, width: Dimension, height: Dimension) {
write_cmd_header(dma.cmd_va, 0x0101);
unsafe {
let p = dma.cmd_va as *mut u8;
let p = dma.cmd_va.raw() as *mut u8;
core::ptr::write_volatile(p.add(24) as *mut u32, resource_id);
core::ptr::write_volatile(p.add(28) as *mut u32, 1); // B8G8R8A8_UNORM
core::ptr::write_volatile(p.add(32) as *mut u32, width);
core::ptr::write_volatile(p.add(36) as *mut u32, height);
core::ptr::write_volatile(p.add(32) as *mut u32, width.raw() as u32);
core::ptr::write_volatile(p.add(36) as *mut u32, 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: u64, fb_size: u32) {
pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, fb_pa: PhysicalAddress, fb_size: u32) {
write_cmd_header(dma.cmd_va, 0x0104);
unsafe {
let p = dma.cmd_va as *mut u8;
let p = dma.cmd_va.raw() as *mut u8;
core::ptr::write_volatile(p.add(24) as *mut u32, resource_id);
core::ptr::write_volatile(p.add(28) as *mut u32, 1);
core::ptr::write_volatile(p.add(32) as *mut u64, fb_pa);
core::ptr::write_volatile(p.add(32) as *mut u64, fb_pa.raw() as u64);
core::ptr::write_volatile(p.add(40) as *mut u32, 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: u32, height: u32) {
pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, scanout_id: u32, resource_id: u32, width: Dimension, height: Dimension) {
write_cmd_header(dma.cmd_va, 0x0103);
unsafe {
let p = dma.cmd_va as *mut u8;
core::ptr::write_volatile(p.add(32) as *mut u32, width);
core::ptr::write_volatile(p.add(36) as *mut u32, height);
let p = dma.cmd_va.raw() as *mut u8;
core::ptr::write_volatile(p.add(32) as *mut u32, width.raw() as u32);
core::ptr::write_volatile(p.add(36) as *mut u32, height.raw() as u32);
core::ptr::write_volatile(p.add(40) as *mut u32, scanout_id);
core::ptr::write_volatile(p.add(44) as *mut u32, resource_id);
}
@@ -46,7 +48,7 @@ pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddr
pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) {
write_cmd_header(dma.cmd_va, 0x0105);
unsafe {
let p = dma.cmd_va as *mut u8;
let p = dma.cmd_va.raw() as *mut u8;
core::ptr::write_volatile(p.add(24) as *mut u32, x);
core::ptr::write_volatile(p.add(28) as *mut u32, y);
core::ptr::write_volatile(p.add(32) as *mut u32, w);
@@ -60,7 +62,7 @@ pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDm
pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, x: u32, y: u32, w: u32, h: u32) {
write_cmd_header(dma.cmd_va, 0x0106);
unsafe {
let p = dma.cmd_va as *mut u8;
let p = dma.cmd_va.raw() as *mut u8;
core::ptr::write_volatile(p.add(24) as *mut u32, x);
core::ptr::write_volatile(p.add(28) as *mut u32, y);
core::ptr::write_volatile(p.add(32) as *mut u32, w);
@@ -70,9 +72,9 @@ pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaA
submit_cmd(mmio, queue, dma, 48);
}
fn write_cmd_header(cmd_va: usize, cmd_type: u32) {
fn write_cmd_header(cmd_va: VirtualAddress, cmd_type: u32) {
unsafe {
let p = cmd_va as *mut u8;
let p = cmd_va.raw() as *mut u8;
core::ptr::write_bytes(p, 0, 512);
core::ptr::write_volatile(p as *mut u32, cmd_type);
}
@@ -80,8 +82,8 @@ fn write_cmd_header(cmd_va: usize, cmd_type: u32) {
fn submit_cmd(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, cmd_len: u32) {
let descs = [
Descriptor { addr: dma.cmd_pa, len: cmd_len, writable: false },
Descriptor { addr: dma.resp_pa, len: 24, writable: true },
Descriptor { addr: dma.cmd_pa.raw() as u64, len: cmd_len, writable: false },
Descriptor { addr: dma.resp_pa.raw() as u64, len: 24, writable: true },
];
queue.submit(mmio, &descs);
}

View File

@@ -1,15 +1,18 @@
#![no_std]
use types::address::{EntryPoint, StackPointer, VirtualAddress};
use types::bounded::{CapSlot, PageCount};
use types::permission::{Permission, PermissionSet};
use userspace::syscall;
const PAGE_SIZE: usize = 4096;
const USER_CODE_BASE: usize = 0x10000;
const USER_STACK_TOP: usize = USER_CODE_BASE;
const USER_STACK_TOP: StackPointer = StackPointer::from_raw(USER_CODE_BASE);
const USER_STACK_PAGES: usize = 4;
const CMD_READ_FILE: usize = 1;
const CREATE_CAP: usize = 0;
const STORAGE_EP_CAP: usize = 2;
fn create_cap() -> CapSlot { CapSlot::new(0).unwrap() }
fn storage_ep() -> CapSlot { CapSlot::new(2).unwrap() }
fn debug_print(s: &[u8]) {
for &b in s {
@@ -43,14 +46,14 @@ fn request_file(shared_buf: *mut u8, filename: &[u8]) -> usize {
core::ptr::write_volatile(shared_buf.add(i), b);
}
}
syscall::send(STORAGE_EP_CAP, CMD_READ_FILE, filename.len(), 0, 0);
let (file_size, _, _, _) = syscall::receive(STORAGE_EP_CAP);
syscall::send(storage_ep(), CMD_READ_FILE, filename.len(), 0, 0);
let (file_size, _, _, _) = syscall::receive(storage_ep());
file_size
}
struct LoadedProcess {
process_cap: usize,
entry_point: usize,
process_cap: CapSlot,
entry_point: EntryPoint,
}
fn load_binary(shared_buf: *mut u8, filename: &[u8]) -> Option<LoadedProcess> {
@@ -65,11 +68,7 @@ fn load_binary(shared_buf: *mut u8, filename: &[u8]) -> Option<LoadedProcess> {
Err(_) => return None,
};
let process_cap = syscall::create_process(CREATE_CAP);
if process_cap == usize::MAX {
debug_print(b"create_process failed\n");
return None;
}
let process_cap = syscall::create_process(create_cap())?;
for segment in elf_info.segments() {
let page_start = segment.vaddr & !0xFFF;
@@ -77,13 +76,10 @@ fn load_binary(shared_buf: *mut u8, filename: &[u8]) -> Option<LoadedProcess> {
let total_size = offset_in_page + segment.mem_size;
let seg_pages = (total_size + PAGE_SIZE - 1) / PAGE_SIZE;
let local_va = syscall::request_pages(seg_pages);
if local_va == usize::MAX {
debug_print(b"request_pages failed\n");
return None;
}
let page_count = PageCount::new(seg_pages as u16)?;
let local_va = syscall::request_pages(page_count)?;
let local_ptr = local_va as *mut u8;
let local_ptr = local_va.raw() as *mut u8;
unsafe {
core::ptr::write_bytes(local_ptr, 0, seg_pages * PAGE_SIZE);
}
@@ -103,22 +99,22 @@ fn load_binary(shared_buf: *mut u8, filename: &[u8]) -> Option<LoadedProcess> {
}
for p in 0..seg_pages {
let src_page = local_va + p * PAGE_SIZE;
let dst_page = page_start + p * PAGE_SIZE;
let src_page = VirtualAddress::from_raw(local_va.raw() + p * PAGE_SIZE);
let dst_page = VirtualAddress::from_raw(page_start + p * PAGE_SIZE);
syscall::map_page(process_cap, src_page, dst_page, segment.flags.execute);
}
}
for i in 0..USER_STACK_PAGES {
let stack_va = syscall::request_pages(1);
if stack_va == usize::MAX { return None; }
let dst = USER_STACK_TOP - (USER_STACK_PAGES - i) * PAGE_SIZE;
let page_count = PageCount::new(1)?;
let stack_va = syscall::request_pages(page_count)?;
let dst = VirtualAddress::from_raw(USER_CODE_BASE - (USER_STACK_PAGES - i) * PAGE_SIZE);
syscall::map_page(process_cap, stack_va, dst, false);
}
Some(LoadedProcess {
process_cap,
entry_point: elf_info.entry_point,
entry_point: EntryPoint::from_raw(elf_info.entry_point),
})
}
@@ -127,7 +123,6 @@ struct ServerInfo {
name: &'static [u8],
}
// display is loaded by the adapter (needs GPU MMIO access)
const SERVERS: &[ServerInfo] = &[
ServerInfo { filename: b"input.elf", name: b"input" },
ServerInfo { filename: b"log.elf", name: b"log" },
@@ -140,9 +135,13 @@ pub fn run(shared_buf_va: usize) -> ! {
let shared_buf = shared_buf_va as *mut u8;
debug_print(b"init: started (pid=");
debug_hex(syscall::get_process_id());
debug_hex(syscall::get_process_id().raw() as usize);
debug_print(b")\n");
let ipc_perms = PermissionSet::empty()
.with(Permission::Read)
.with(Permission::Execute);
for server in SERVERS {
debug_print(b"init: loading ");
debug_print(server.name);
@@ -156,13 +155,13 @@ pub fn run(shared_buf_va: usize) -> ! {
}
};
let ep_cap = syscall::create_endpoint();
let read_execute: usize = (1 << 0) | (1 << 2);
syscall::delegate(ep_cap, loaded.process_cap, read_execute);
if let Some(ep_cap) = syscall::create_endpoint() {
syscall::delegate(ep_cap, loaded.process_cap, ipc_perms);
syscall::spawn_thread_in(loaded.process_cap, loaded.entry_point, USER_STACK_TOP);
}
debug_print(b"ok (entry=");
debug_hex(loaded.entry_point);
debug_hex(loaded.entry_point.raw());
debug_print(b")\n");
}

View File

@@ -4,3 +4,4 @@ version = "0.1.0"
edition = "2024"
[dependencies]
types = { workspace = true }

View File

@@ -1,10 +1,12 @@
#![no_std]
use types::display::{Color, Dimension, ScreenCoord};
pub trait Framebuffer {
fn width(&self) -> u16;
fn height(&self) -> u16;
fn write_pixel(&mut self, x: u16, y: u16, color: u32);
fn fill_rect(&mut self, x: u16, y: u16, w: u16, h: u16, color: u32);
fn clear(&mut self, color: u32);
fn width(&self) -> Dimension;
fn height(&self) -> Dimension;
fn write_pixel(&mut self, x: ScreenCoord, y: ScreenCoord, color: Color);
fn fill_rect(&mut self, x: ScreenCoord, y: ScreenCoord, w: Dimension, h: Dimension, color: Color);
fn clear(&mut self, color: Color);
fn flush(&mut self);
}

View File

@@ -1,15 +1,16 @@
pub async fn send(endpoint: usize, w0: usize, w1: usize, w2: usize, w3: usize) -> usize {
use types::bounded::CapSlot;
pub async fn send(endpoint: CapSlot, w0: usize, w1: usize, w2: usize, w3: usize) -> usize {
userspace::syscall::send(endpoint, w0, w1, w2, w3)
}
pub async fn receive(endpoint: usize) -> (usize, usize, usize, usize) {
pub async fn receive(endpoint: CapSlot) -> (usize, usize, usize, usize) {
userspace::syscall::receive(endpoint)
}
pub async fn wait_for_interrupt(irq: usize) {
pub async fn wait_for_interrupt(_irq: CapSlot) {
userspace::syscall::yield_now();
}
pub async fn acknowledge_interrupt(irq: usize) {
let _ = irq;
pub async fn acknowledge_interrupt(_irq: CapSlot) {
}

View File

@@ -2,6 +2,7 @@
mod tests;
use crate::codec::{Decode, Encode};
use types::bounded::CapSlot;
pub trait Server {
type Request: Decode;
@@ -9,7 +10,7 @@ pub trait Server {
fn handle(&mut self, request: Self::Request) -> Self::Response;
fn run(&mut self, endpoint: usize) {
fn run(&mut self, endpoint: CapSlot) {
loop {
let words = userspace::syscall::receive(endpoint);
if let Some(request) = Self::Request::decode([words.0, words.1, words.2, words.3]) {
@@ -21,6 +22,6 @@ pub trait Server {
}
}
pub fn serve<S: Server>(server: &mut S, endpoint: usize) {
pub fn serve<S: Server>(server: &mut S, endpoint: CapSlot) {
server.run(endpoint);
}

View File

@@ -1,9 +1,10 @@
use server_framework::{Decode, Encode};
use types::display::{Color, Dimension, ScreenCoord};
#[derive(Debug, PartialEq)]
pub enum DisplayRequest {
DrawRect { x: u16, y: u16, w: u16, h: u16, color: u32 },
Clear { color: u32 },
DrawRect { x: ScreenCoord, y: ScreenCoord, w: Dimension, h: Dimension, color: Color },
Clear { color: Color },
Flush,
GetDimensions,
}
@@ -11,7 +12,7 @@ pub enum DisplayRequest {
#[derive(Debug, PartialEq)]
pub enum DisplayResponse {
Ok,
Dimensions { width: u16, height: u16 },
Dimensions { width: Dimension, height: Dimension },
Error,
}
@@ -19,11 +20,11 @@ impl Encode for DisplayRequest {
fn encode(&self) -> [usize; 4] {
match self {
DisplayRequest::DrawRect { x, y, w, h, color } => {
let xy = (*x as usize) | ((*y as usize) << 16);
let wh = (*w as usize) | ((*h as usize) << 16);
[0, xy, wh, *color as usize]
let xy = (x.raw() as usize) | ((y.raw() as usize) << 16);
let wh = (w.raw() as usize) | ((h.raw() as usize) << 16);
[0, xy, wh, color.raw() as usize]
}
DisplayRequest::Clear { color } => [1, *color as usize, 0, 0],
DisplayRequest::Clear { color } => [1, color.raw() as usize, 0, 0],
DisplayRequest::Flush => [2, 0, 0, 0],
DisplayRequest::GetDimensions => [3, 0, 0, 0],
}
@@ -34,14 +35,14 @@ impl Decode for DisplayRequest {
fn decode(words: [usize; 4]) -> Option<Self> {
match words[0] {
0 => {
let x = (words[1] & 0xFFFF) as u16;
let y = ((words[1] >> 16) & 0xFFFF) as u16;
let w = (words[2] & 0xFFFF) as u16;
let h = ((words[2] >> 16) & 0xFFFF) as u16;
let color = words[3] as u32;
let x = ScreenCoord::new((words[1] & 0xFFFF) as u16);
let y = ScreenCoord::new(((words[1] >> 16) & 0xFFFF) as u16);
let w = Dimension::new((words[2] & 0xFFFF) as u16);
let h = Dimension::new(((words[2] >> 16) & 0xFFFF) as u16);
let color = Color::from_raw(words[3] as u32);
Some(DisplayRequest::DrawRect { x, y, w, h, color })
}
1 => Some(DisplayRequest::Clear { color: words[1] as u32 }),
1 => Some(DisplayRequest::Clear { color: Color::from_raw(words[1] as u32) }),
2 => Some(DisplayRequest::Flush),
3 => Some(DisplayRequest::GetDimensions),
_ => None,
@@ -54,7 +55,7 @@ impl Encode for DisplayResponse {
match self {
DisplayResponse::Ok => [0, 0, 0, 0],
DisplayResponse::Dimensions { width, height } => {
[1, *width as usize, *height as usize, 0]
[1, width.raw() as usize, height.raw() as usize, 0]
}
DisplayResponse::Error => [2, 0, 0, 0],
}
@@ -66,8 +67,8 @@ impl Decode for DisplayResponse {
match words[0] {
0 => Some(DisplayResponse::Ok),
1 => Some(DisplayResponse::Dimensions {
width: words[1] as u16,
height: words[2] as u16,
width: Dimension::new(words[1] as u16),
height: Dimension::new(words[2] as u16),
}),
2 => Some(DisplayResponse::Error),
_ => None,

View File

@@ -1,14 +1,15 @@
use crate::protocol::{DisplayRequest, DisplayResponse};
use server_framework::{Decode, Encode};
use types::display::{Color, Dimension, ScreenCoord};
#[test]
fn draw_rect_roundtrip() {
let req = DisplayRequest::DrawRect {
x: 10,
y: 20,
w: 100,
h: 50,
color: 0xFF0000,
x: ScreenCoord::new(10),
y: ScreenCoord::new(20),
w: Dimension::new(100),
h: Dimension::new(50),
color: Color::from_raw(0xFF0000),
};
let words = req.encode();
let decoded = DisplayRequest::decode(words).unwrap();
@@ -17,7 +18,7 @@ fn draw_rect_roundtrip() {
#[test]
fn clear_roundtrip() {
let req = DisplayRequest::Clear { color: 0x00FF00 };
let req = DisplayRequest::Clear { color: Color::from_raw(0x00FF00) };
let words = req.encode();
let decoded = DisplayRequest::decode(words).unwrap();
assert_eq!(decoded, req);
@@ -40,8 +41,8 @@ fn get_dimensions_roundtrip() {
#[test]
fn dimensions_response_roundtrip() {
let resp = DisplayResponse::Dimensions {
width: 320,
height: 240,
width: Dimension::new(320),
height: Dimension::new(240),
};
let words = resp.encode();
let decoded = DisplayResponse::decode(words).unwrap();

View File

@@ -2,20 +2,21 @@ use driver_framebuffer_mock::MockFramebuffer;
use crate::protocol::{DisplayRequest, DisplayResponse};
use crate::server::DisplayServer;
use server_framework::Server;
use types::display::{Color, Dimension, ScreenCoord};
fn test_server() -> DisplayServer<MockFramebuffer> {
DisplayServer::new(MockFramebuffer::new(320, 240))
DisplayServer::new(MockFramebuffer::new(Dimension::new(320), Dimension::new(240)))
}
#[test]
fn draw_rect_writes_pixels() {
let mut server = test_server();
let resp = server.handle(DisplayRequest::DrawRect {
x: 0,
y: 0,
w: 4,
h: 3,
color: 0xFF,
x: ScreenCoord::new(0),
y: ScreenCoord::new(0),
w: Dimension::new(4),
h: Dimension::new(3),
color: Color::from_raw(0xFF),
});
assert_eq!(resp, DisplayResponse::Ok);
assert_eq!(server.framebuffer().pixel_count(), 12);
@@ -25,13 +26,13 @@ fn draw_rect_writes_pixels() {
fn clear_resets_pixels() {
let mut server = test_server();
server.handle(DisplayRequest::DrawRect {
x: 0,
y: 0,
w: 10,
h: 10,
color: 0xFF,
x: ScreenCoord::new(0),
y: ScreenCoord::new(0),
w: Dimension::new(10),
h: Dimension::new(10),
color: Color::from_raw(0xFF),
});
server.handle(DisplayRequest::Clear { color: 0 });
server.handle(DisplayRequest::Clear { color: Color::from_raw(0) });
assert_eq!(server.framebuffer().pixel_count(), 0);
}
@@ -50,8 +51,8 @@ fn get_dimensions_returns_size() {
assert_eq!(
resp,
DisplayResponse::Dimensions {
width: 320,
height: 240
width: Dimension::new(320),
height: Dimension::new(240),
}
);
}
@@ -60,16 +61,16 @@ fn get_dimensions_returns_size() {
fn draw_rect_correct_pixel_positions() {
let mut server = test_server();
server.handle(DisplayRequest::DrawRect {
x: 5,
y: 10,
w: 2,
h: 2,
color: 0xAB,
x: ScreenCoord::new(5),
y: ScreenCoord::new(10),
w: Dimension::new(2),
h: Dimension::new(2),
color: Color::from_raw(0xAB),
});
let fb = server.framebuffer();
assert_eq!(fb.pixel_count(), 4);
assert_eq!(fb.pixel_at(0), (5, 10, 0xAB));
assert_eq!(fb.pixel_at(1), (6, 10, 0xAB));
assert_eq!(fb.pixel_at(2), (5, 11, 0xAB));
assert_eq!(fb.pixel_at(3), (6, 11, 0xAB));
assert_eq!(fb.pixel_at(0), (ScreenCoord::new(5), ScreenCoord::new(10), Color::from_raw(0xAB)));
assert_eq!(fb.pixel_at(1), (ScreenCoord::new(6), ScreenCoord::new(10), Color::from_raw(0xAB)));
assert_eq!(fb.pixel_at(2), (ScreenCoord::new(5), ScreenCoord::new(11), Color::from_raw(0xAB)));
assert_eq!(fb.pixel_at(3), (ScreenCoord::new(6), ScreenCoord::new(11), Color::from_raw(0xAB)));
}

View File

@@ -16,6 +16,7 @@ pub fn run() -> ! {
let device = adapters::mock::MockInputDevice::new();
let mut srv = server::InputServer::new(device);
server_framework::serve(&mut srv, 0);
let ep = types::bounded::CapSlot::new(0).unwrap();
server_framework::serve(&mut srv, ep);
loop { userspace::syscall::yield_now(); }
}

View File

@@ -9,6 +9,7 @@ mod tests;
pub fn run() -> ! {
let mut srv = server::LogServer::new();
server_framework::serve(&mut srv, 0);
let ep = types::bounded::CapSlot::new(0).unwrap();
server_framework::serve(&mut srv, ep);
loop { userspace::syscall::yield_now(); }
}

View File

@@ -9,6 +9,7 @@ mod tests;
pub fn run() -> ! {
let mut srv = server::PermissionManager::new();
server_framework::serve(&mut srv, 0);
let ep = types::bounded::CapSlot::new(0).unwrap();
server_framework::serve(&mut srv, ep);
loop { userspace::syscall::yield_now(); }
}

View File

@@ -46,10 +46,10 @@ impl CapabilityKind {
#[derive(Debug, PartialEq)]
pub enum PermissionRequest {
RequestCapability { process: u16, kind: CapabilityKind },
RevokeCapability { process: u16, kind: CapabilityKind },
CheckCapability { process: u16, kind: CapabilityKind },
ListGranted { process: u16 },
RequestCapability { process: types::id::ProcessId, kind: CapabilityKind },
RevokeCapability { process: types::id::ProcessId, kind: CapabilityKind },
CheckCapability { process: types::id::ProcessId, kind: CapabilityKind },
ListGranted { process: types::id::ProcessId },
}
#[derive(Debug, PartialEq)]
@@ -66,16 +66,16 @@ impl Encode for PermissionRequest {
fn encode(&self) -> [usize; 4] {
match self {
PermissionRequest::RequestCapability { process, kind } => {
[0, *process as usize, kind.index(), 0]
[0, process.raw() as usize, kind.index(), 0]
}
PermissionRequest::RevokeCapability { process, kind } => {
[1, *process as usize, kind.index(), 0]
[1, process.raw() as usize, kind.index(), 0]
}
PermissionRequest::CheckCapability { process, kind } => {
[2, *process as usize, kind.index(), 0]
[2, process.raw() as usize, kind.index(), 0]
}
PermissionRequest::ListGranted { process } => {
[3, *process as usize, 0, 0]
[3, process.raw() as usize, 0, 0]
}
}
}
@@ -85,19 +85,19 @@ impl Decode for PermissionRequest {
fn decode(words: [usize; 4]) -> Option<Self> {
match words[0] {
0 => Some(PermissionRequest::RequestCapability {
process: words[1] as u16,
process: types::id::ProcessId::from_raw(words[1] as u16),
kind: CapabilityKind::from_index(words[2])?,
}),
1 => Some(PermissionRequest::RevokeCapability {
process: words[1] as u16,
process: types::id::ProcessId::from_raw(words[1] as u16),
kind: CapabilityKind::from_index(words[2])?,
}),
2 => Some(PermissionRequest::CheckCapability {
process: words[1] as u16,
process: types::id::ProcessId::from_raw(words[1] as u16),
kind: CapabilityKind::from_index(words[2])?,
}),
3 => Some(PermissionRequest::ListGranted {
process: words[1] as u16,
process: types::id::ProcessId::from_raw(words[1] as u16),
}),
_ => None,
}

View File

@@ -1,4 +1,5 @@
use server_framework::Server;
use types::id::ProcessId;
use crate::protocol::{CapabilityKind, PermissionRequest, PermissionResponse};
@@ -16,8 +17,8 @@ impl PermissionManager {
}
}
fn granted_bits(&self, process: u16) -> u16 {
let idx = process as usize;
fn granted_bits(&self, process: ProcessId) -> u16 {
let idx = process.raw() as usize;
if idx >= MAX_PROCESSES {
return 0;
}
@@ -38,7 +39,7 @@ impl Server for PermissionManager {
fn handle(&mut self, request: PermissionRequest) -> PermissionResponse {
match request {
PermissionRequest::RequestCapability { process, kind } => {
let idx = process as usize;
let idx = process.raw() as usize;
if idx >= MAX_PROCESSES {
return PermissionResponse::Denied;
}
@@ -46,7 +47,7 @@ impl Server for PermissionManager {
PermissionResponse::Granted
}
PermissionRequest::RevokeCapability { process, kind } => {
let idx = process as usize;
let idx = process.raw() as usize;
if idx >= MAX_PROCESSES {
return PermissionResponse::Denied;
}
@@ -54,7 +55,7 @@ impl Server for PermissionManager {
PermissionResponse::Revoked
}
PermissionRequest::CheckCapability { process, kind } => {
let idx = process as usize;
let idx = process.raw() as usize;
if idx >= MAX_PROCESSES {
return PermissionResponse::HasCapability(false);
}

View File

@@ -1,27 +1,30 @@
use crate::protocol::*;
use server_framework::{Decode, Encode};
use types::id::ProcessId;
fn pid(v: u16) -> ProcessId { ProcessId::from_raw(v) }
#[test]
fn request_capability_roundtrip() {
let req = PermissionRequest::RequestCapability { process: 5, kind: CapabilityKind::Camera };
let req = PermissionRequest::RequestCapability { process: pid(5), kind: CapabilityKind::Camera };
assert_eq!(PermissionRequest::decode(req.encode()), Some(req));
}
#[test]
fn revoke_capability_roundtrip() {
let req = PermissionRequest::RevokeCapability { process: 3, kind: CapabilityKind::Network };
let req = PermissionRequest::RevokeCapability { process: pid(3), kind: CapabilityKind::Network };
assert_eq!(PermissionRequest::decode(req.encode()), Some(req));
}
#[test]
fn check_capability_roundtrip() {
let req = PermissionRequest::CheckCapability { process: 1, kind: CapabilityKind::Storage };
let req = PermissionRequest::CheckCapability { process: pid(1), kind: CapabilityKind::Storage };
assert_eq!(PermissionRequest::decode(req.encode()), Some(req));
}
#[test]
fn list_granted_roundtrip() {
let req = PermissionRequest::ListGranted { process: 7 };
let req = PermissionRequest::ListGranted { process: pid(7) };
assert_eq!(PermissionRequest::decode(req.encode()), Some(req));
}
@@ -33,7 +36,7 @@ fn all_kinds_roundtrip() {
CapabilityKind::Network, CapabilityKind::Gps, CapabilityKind::Bluetooth,
];
for kind in kinds {
let req = PermissionRequest::RequestCapability { process: 0, kind };
let req = PermissionRequest::RequestCapability { process: pid(0), kind };
assert_eq!(PermissionRequest::decode(req.encode()), Some(req));
}
}

View File

@@ -1,12 +1,15 @@
use crate::protocol::*;
use crate::server::PermissionManager;
use server_framework::Server;
use types::id::ProcessId;
fn pid(v: u16) -> ProcessId { ProcessId::from_raw(v) }
#[test]
fn new_manager_has_no_grants() {
let mut mgr = PermissionManager::new();
let resp = mgr.handle(PermissionRequest::CheckCapability {
process: 0,
process: pid(0),
kind: CapabilityKind::Camera,
});
assert_eq!(resp, PermissionResponse::HasCapability(false));
@@ -16,11 +19,11 @@ fn new_manager_has_no_grants() {
fn grant_then_check() {
let mut mgr = PermissionManager::new();
mgr.handle(PermissionRequest::RequestCapability {
process: 1,
process: pid(1),
kind: CapabilityKind::Network,
});
let resp = mgr.handle(PermissionRequest::CheckCapability {
process: 1,
process: pid(1),
kind: CapabilityKind::Network,
});
assert_eq!(resp, PermissionResponse::HasCapability(true));
@@ -30,11 +33,11 @@ fn grant_then_check() {
fn grant_does_not_affect_other_process() {
let mut mgr = PermissionManager::new();
mgr.handle(PermissionRequest::RequestCapability {
process: 1,
process: pid(1),
kind: CapabilityKind::Camera,
});
let resp = mgr.handle(PermissionRequest::CheckCapability {
process: 2,
process: pid(2),
kind: CapabilityKind::Camera,
});
assert_eq!(resp, PermissionResponse::HasCapability(false));
@@ -44,15 +47,15 @@ fn grant_does_not_affect_other_process() {
fn revoke_removes_grant() {
let mut mgr = PermissionManager::new();
mgr.handle(PermissionRequest::RequestCapability {
process: 0,
process: pid(0),
kind: CapabilityKind::Storage,
});
mgr.handle(PermissionRequest::RevokeCapability {
process: 0,
process: pid(0),
kind: CapabilityKind::Storage,
});
let resp = mgr.handle(PermissionRequest::CheckCapability {
process: 0,
process: pid(0),
kind: CapabilityKind::Storage,
});
assert_eq!(resp, PermissionResponse::HasCapability(false));
@@ -62,15 +65,14 @@ fn revoke_removes_grant() {
fn list_granted_returns_bitfield() {
let mut mgr = PermissionManager::new();
mgr.handle(PermissionRequest::RequestCapability {
process: 0,
process: pid(0),
kind: CapabilityKind::Display,
});
mgr.handle(PermissionRequest::RequestCapability {
process: 0,
process: pid(0),
kind: CapabilityKind::Network,
});
let resp = mgr.handle(PermissionRequest::ListGranted { process: 0 });
// Display=bit 0, Network=bit 6 → 0b1000001 = 65
let resp = mgr.handle(PermissionRequest::ListGranted { process: pid(0) });
assert_eq!(resp, PermissionResponse::GrantedList(0b1000001));
}
@@ -78,7 +80,7 @@ fn list_granted_returns_bitfield() {
fn out_of_range_process_denied() {
let mut mgr = PermissionManager::new();
let resp = mgr.handle(PermissionRequest::RequestCapability {
process: 999,
process: pid(999),
kind: CapabilityKind::Camera,
});
assert_eq!(resp, PermissionResponse::Denied);

View File

@@ -8,8 +8,10 @@ pub mod server;
mod tests;
pub fn run() -> ! {
let mut srv = server::SessionServer::new(0, 0, 0);
let create_cap = types::bounded::CapSlot::new(0).unwrap();
let mut srv = server::SessionServer::new(create_cap);
srv.register_app("snake", "/apps/snake.elf");
server_framework::serve(&mut srv, 0);
let ep = types::bounded::CapSlot::new(0).unwrap();
server_framework::serve(&mut srv, ep);
loop { userspace::syscall::yield_now(); }
}

View File

@@ -1,4 +1,6 @@
use server_framework::Server;
use types::bounded::CapSlot;
use types::id::ProcessId;
use crate::protocol::{AppId, SessionRequest, SessionResponse};
@@ -14,7 +16,7 @@ pub struct AppEntry {
#[derive(Clone, Copy)]
struct RunningApp {
app: AppId,
pid: u16,
pid: ProcessId,
}
pub struct SessionServer {
@@ -22,21 +24,17 @@ pub struct SessionServer {
app_count: usize,
running: [Option<RunningApp>; MAX_APPS],
active: Option<AppId>,
create_cap: usize,
display_cap: usize,
input_cap: usize,
create_cap: CapSlot,
}
impl SessionServer {
pub fn new(create_cap: usize, display_cap: usize, input_cap: usize) -> Self {
pub fn new(create_cap: CapSlot) -> Self {
Self {
apps: [const { None }; MAX_APPS],
app_count: 0,
running: [const { None }; MAX_APPS],
active: None,
create_cap,
display_cap,
input_cap,
}
}
@@ -74,9 +72,13 @@ impl SessionServer {
None => return SessionResponse::NotFound,
};
let pid = userspace::syscall::create_process(self.create_cap);
let process_cap = match userspace::syscall::create_process(self.create_cap) {
Some(cap) => cap,
None => return SessionResponse::Error,
};
self.running[idx] = Some(RunningApp { app, pid: pid as u16 });
let pid = ProcessId::from_raw(process_cap.value() as u16);
self.running[idx] = Some(RunningApp { app, pid });
self.active = Some(app);
SessionResponse::Launched { app }
@@ -85,8 +87,7 @@ impl SessionServer {
fn kill_app(&mut self, app: AppId) -> SessionResponse {
let idx = app.raw() as usize;
match self.running[idx].take() {
Some(running) => {
let _ = running.pid;
Some(_running) => {
if self.active == Some(app) {
self.active = None;
}

View File

@@ -3,7 +3,8 @@ use crate::server::SessionServer;
use server_framework::Server;
fn test_server() -> SessionServer {
let mut s = SessionServer::new(0, 0, 0);
let cap = types::bounded::CapSlot::new(0).unwrap();
let mut s = SessionServer::new(cap);
s.register_app("snake", "/apps/snake.elf");
s.register_app("clock", "/apps/clock.elf");
s

View File

@@ -38,7 +38,7 @@ pub fn run() -> ! {
let mut fat16 = fat16.unwrap();
debug_print(b"storage: FAT16 mounted\n");
let ep: usize = 0;
let ep = types::bounded::CapSlot::new(0).unwrap();
loop {
let (w0, w1, _w2, _w3) = syscall::receive(ep);

View File

@@ -0,0 +1,36 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Color(u32);
impl Color {
pub const fn from_raw(v: u32) -> Self { Self(v) }
pub const fn raw(&self) -> u32 { self.0 }
pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
Self(((r as u32) << 16) | ((g as u32) << 8) | (b as u32) | 0xFF000000)
}
pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
Self(((a as u32) << 24) | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ScreenCoord(u16);
impl ScreenCoord {
pub const fn new(v: u16) -> Self { Self(v) }
pub const fn raw(&self) -> u16 { self.0 }
}
impl core::ops::Add<Dimension> for ScreenCoord {
type Output = Self;
fn add(self, rhs: Dimension) -> Self { Self(self.0 + rhs.0) }
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct Dimension(u16);
impl Dimension {
pub const fn new(v: u16) -> Self { Self(v) }
pub const fn raw(&self) -> u16 { self.0 }
}

View File

@@ -2,6 +2,7 @@
pub mod address;
pub mod bounded;
pub mod display;
pub mod generation;
pub mod id;
pub mod macros;

View File

@@ -44,4 +44,8 @@ impl PermissionSet {
pub fn from_bits(bits: u8) -> Self {
PermissionSet(bits)
}
pub fn bits(&self) -> u8 {
self.0
}
}

View File

@@ -47,14 +47,18 @@ impl AllocatorInner {
return ptr::null_mut();
}
let page_addr = crate::syscall::request_pages(1);
if page_addr == usize::MAX {
return ptr::null_mut();
}
let count = match types::bounded::PageCount::new(1) {
Some(c) => c,
None => return ptr::null_mut(),
};
let va = match crate::syscall::request_pages(count) {
Some(va) => va,
None => return ptr::null_mut(),
};
self.pages[self.page_count] = page_addr;
self.pages[self.page_count] = va.raw();
self.page_count += 1;
page_addr as *mut u8
va.raw() as *mut u8
}
fn alloc_from_free_list(&mut self, size: usize, align: usize) -> *mut u8 {

View File

@@ -1,5 +1,10 @@
use crate::arch;
use types::syscall_abi::*;
use types::address::{EntryPoint, StackPointer, VirtualAddress};
use types::bounded::{CapSlot, PageCount};
use types::id::ProcessId;
use types::permission::PermissionSet;
use types::time::Ticks;
pub fn exit() -> ! {
arch::syscall0(SYS_EXIT);
@@ -15,63 +20,66 @@ pub fn yield_now() {
arch::syscall0(SYS_YIELD);
}
pub fn get_time() -> usize {
arch::syscall0(SYS_GET_TIME)
}
pub fn get_process_id() -> usize {
arch::syscall0(SYS_GET_PROCESS_ID)
}
pub fn spawn_thread(entry: usize, stack: usize) -> usize {
arch::syscall2(SYS_SPAWN_THREAD, entry, stack)
}
pub fn request_pages(count: usize) -> usize {
arch::syscall1(SYS_REQUEST_PAGES, count)
}
pub fn create_process(create_cap: usize) -> usize {
arch::syscall1(SYS_CREATE_PROCESS, create_cap)
}
pub fn kill(process_cap: usize) -> usize {
arch::syscall1(SYS_KILL, process_cap)
}
pub fn delegate(slot: usize, to: usize, permissions: usize) -> usize {
arch::syscall3(SYS_DELEGATE, slot, to, permissions)
}
pub fn revoke(slot: usize) -> usize {
arch::syscall1(SYS_REVOKE, slot)
}
pub fn halt() -> ! {
arch::syscall0(SYS_HALT);
loop {}
}
pub fn send(endpoint: usize, w0: usize, w1: usize, w2: usize, w3: usize) -> usize {
arch::syscall5(SYS_SEND, endpoint, w0, w1, w2, w3)
pub fn get_time() -> Ticks {
Ticks::from_raw(arch::syscall0(SYS_GET_TIME) as u64)
}
pub fn receive(endpoint: usize) -> (usize, usize, usize, usize) {
arch::syscall_receive(SYS_RECEIVE, endpoint)
pub fn get_process_id() -> ProcessId {
ProcessId::from_raw(arch::syscall0(SYS_GET_PROCESS_ID) as u16)
}
pub fn create_endpoint() -> usize {
arch::syscall0(SYS_CREATE_ENDPOINT)
pub fn create_process(create_cap: CapSlot) -> Option<CapSlot> {
let r = arch::syscall1(SYS_CREATE_PROCESS, create_cap.value() as usize);
if r == usize::MAX { None } else { CapSlot::new(r as u8) }
}
pub fn create_endpoint() -> Option<CapSlot> {
let r = arch::syscall0(SYS_CREATE_ENDPOINT);
if r == usize::MAX { None } else { CapSlot::new(r as u8) }
}
pub fn spawn_thread(entry: EntryPoint, stack: StackPointer) -> usize {
arch::syscall2(SYS_SPAWN_THREAD, entry.raw(), stack.raw())
}
pub fn spawn_thread_in(process_cap: CapSlot, entry: EntryPoint, stack: StackPointer) -> usize {
arch::syscall3(SYS_SPAWN_THREAD_IN, process_cap.value() as usize, entry.raw(), stack.raw())
}
pub fn request_pages(count: PageCount) -> Option<VirtualAddress> {
let r = arch::syscall1(SYS_REQUEST_PAGES, count.value() as usize);
if r == usize::MAX { None } else { Some(VirtualAddress::from_raw(r)) }
}
pub fn map_page(process_cap: CapSlot, src_va: VirtualAddress, dst_va: VirtualAddress, executable: bool) -> bool {
arch::syscall4(SYS_MAP_PAGE, process_cap.value() as usize, src_va.raw(), dst_va.raw(), executable as usize) == 0
}
pub fn send(endpoint: CapSlot, w0: usize, w1: usize, w2: usize, w3: usize) -> usize {
arch::syscall5(SYS_SEND, endpoint.value() as usize, w0, w1, w2, w3)
}
pub fn receive(endpoint: CapSlot) -> (usize, usize, usize, usize) {
arch::syscall_receive(SYS_RECEIVE, endpoint.value() as usize)
}
pub fn delegate(slot: CapSlot, to: CapSlot, permissions: PermissionSet) -> bool {
arch::syscall3(SYS_DELEGATE, slot.value() as usize, to.value() as usize, permissions.bits() as usize) == 0
}
pub fn kill(process_cap: CapSlot) -> bool {
arch::syscall1(SYS_KILL, process_cap.value() as usize) == 0
}
pub fn revoke(slot: CapSlot) -> bool {
arch::syscall1(SYS_REVOKE, slot.value() as usize) == 0
}
pub fn debug_putchar(byte: u8) {
arch::syscall1(SYS_DEBUG_PUTCHAR, byte as usize);
}
pub fn map_page(process_cap: usize, src_va: usize, dst_va: usize, executable: bool) -> usize {
arch::syscall4(SYS_MAP_PAGE, process_cap, src_va, dst_va, executable as usize)
}
pub fn spawn_thread_in(process_cap: usize, entry: usize, stack: usize) -> usize {
arch::syscall3(SYS_SPAWN_THREAD_IN, process_cap, entry, stack)
}