feat: wire display + input servers to snake, fix QEMU 11.x GPU bug
VirtIO GPU: QEMU 11.x has RESOURCE_ATTACH_BACKING (0x0104) and RESOURCE_FLUSH (0x0106) command types swapped in its legacy MMIO VirtIO GPU implementation. Workaround: send swapped type values. Confirmed via QEMU monitor (xp shows correct bytes in memory) and QEMU trace events (device dispatches to wrong handler). Display pipeline now works end-to-end: adapter → GPU init → create_2d → attach_backing → set_scanout display-server → clear(blue) → flush → transfer + resource_flush snake → draw commands via IPC → display renders → flush to GPU Input server: - New driver-input-uart crate (reads UART at mapped VA 0x20000) - New bins/input-qemu composition root - Adapter loads input-server, maps UART MMIO into its address space - Init delegates both display_ep (slot 0) and input_ep (slot 1) to snake - WASD keys → UART → input-server → IPC → snake direction change Snake game runs with display and input, exits on wall collision.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
*.img
|
||||||
20
Cargo.lock
generated
20
Cargo.lock
generated
@@ -56,6 +56,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"display-server",
|
"display-server",
|
||||||
"driver-framebuffer-virtio",
|
"driver-framebuffer-virtio",
|
||||||
|
"port-framebuffer",
|
||||||
"server-framework",
|
"server-framework",
|
||||||
"types",
|
"types",
|
||||||
"userspace",
|
"userspace",
|
||||||
@@ -105,6 +106,13 @@ dependencies = [
|
|||||||
"types",
|
"types",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "driver-input-uart"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"port-input-device",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "echo"
|
name = "echo"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -150,6 +158,18 @@ dependencies = [
|
|||||||
"userspace-rt",
|
"userspace-rt",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "input-qemu"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"driver-input-uart",
|
||||||
|
"input-server",
|
||||||
|
"server-framework",
|
||||||
|
"types",
|
||||||
|
"userspace",
|
||||||
|
"userspace-rt",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "input-server"
|
name = "input-server"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -48,7 +48,9 @@ members = [
|
|||||||
"crates/drivers/framebuffer/mock",
|
"crates/drivers/framebuffer/mock",
|
||||||
"crates/drivers/block/virtio",
|
"crates/drivers/block/virtio",
|
||||||
"crates/bins/display-qemu",
|
"crates/bins/display-qemu",
|
||||||
|
"crates/bins/input-qemu",
|
||||||
"crates/bins/storage-qemu",
|
"crates/bins/storage-qemu",
|
||||||
|
"crates/drivers/input/uart",
|
||||||
"crates/hw-virtio",
|
"crates/hw-virtio",
|
||||||
]
|
]
|
||||||
resolver = "2"
|
resolver = "2"
|
||||||
@@ -105,6 +107,7 @@ port-input-device = { path = "crates/ports/input-device" }
|
|||||||
driver-framebuffer-virtio = { path = "crates/drivers/framebuffer/virtio" }
|
driver-framebuffer-virtio = { path = "crates/drivers/framebuffer/virtio" }
|
||||||
driver-framebuffer-mock = { path = "crates/drivers/framebuffer/mock" }
|
driver-framebuffer-mock = { path = "crates/drivers/framebuffer/mock" }
|
||||||
driver-block-virtio = { path = "crates/drivers/block/virtio" }
|
driver-block-virtio = { path = "crates/drivers/block/virtio" }
|
||||||
|
driver-input-uart = { path = "crates/drivers/input/uart" }
|
||||||
|
|
||||||
# apps
|
# apps
|
||||||
snake = { path = "crates/apps/snake" }
|
snake = { path = "crates/apps/snake" }
|
||||||
|
|||||||
4
Makefile
4
Makefile
@@ -31,8 +31,8 @@ kani: kani-types kani-domain kani-kernel
|
|||||||
build:
|
build:
|
||||||
cargo build --target $(RISCV_TARGET) --release \
|
cargo build --target $(RISCV_TARGET) --release \
|
||||||
-p adapter-riscv -p init -p echo -p snake \
|
-p adapter-riscv -p init -p echo -p snake \
|
||||||
-p display-qemu -p storage-qemu \
|
-p display-qemu -p input-qemu -p storage-qemu \
|
||||||
-p input-server -p log-server -p permission-manager -p session-server
|
-p log-server -p permission-manager -p session-server
|
||||||
|
|
||||||
image: build
|
image: build
|
||||||
scripts/make-disk-image.sh
|
scripts/make-disk-image.sh
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ pub fn grant_all(
|
|||||||
init_pid: ProcessId,
|
init_pid: ProcessId,
|
||||||
storage_pid: ProcessId,
|
storage_pid: ProcessId,
|
||||||
display_pid: ProcessId,
|
display_pid: ProcessId,
|
||||||
|
input_pid: ProcessId,
|
||||||
) {
|
) {
|
||||||
let all_perms = types::permission::PermissionSet::empty()
|
let all_perms = types::permission::PermissionSet::empty()
|
||||||
.with(types::permission::Permission::Read)
|
.with(types::permission::Permission::Read)
|
||||||
@@ -22,7 +23,9 @@ pub fn grant_all(
|
|||||||
let storage_ep = kernel_ref().create_endpoint()
|
let storage_ep = kernel_ref().create_endpoint()
|
||||||
.unwrap_or_else(|_| crate::halt(uart, "storage endpoint failed"));
|
.unwrap_or_else(|_| crate::halt(uart, "storage endpoint failed"));
|
||||||
|
|
||||||
// Init: slot 0 = ProcessCreate (from boot), slot 1 = Process(storage), slot 2 = Endpoint(storage)
|
// Init: slot 0 = ProcessCreate (from boot)
|
||||||
|
// slot 1 = Process(storage)
|
||||||
|
// slot 2 = Endpoint(storage)
|
||||||
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Process(storage_pid), all_perms)
|
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Process(storage_pid), all_perms)
|
||||||
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
|
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
|
||||||
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Endpoint(storage_ep), all_perms)
|
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Endpoint(storage_ep), all_perms)
|
||||||
@@ -46,5 +49,19 @@ pub fn grant_all(
|
|||||||
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Process(display_pid), all_perms)
|
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Process(display_pid), all_perms)
|
||||||
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
|
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
|
||||||
|
|
||||||
|
// Input endpoint
|
||||||
|
let input_ep = kernel_ref().create_endpoint()
|
||||||
|
.unwrap_or_else(|_| crate::halt(uart, "input endpoint failed"));
|
||||||
|
|
||||||
|
// Input: slot 0 = Endpoint
|
||||||
|
kernel_ref().grant_capability(input_pid, domain::resource::Resource::Endpoint(input_ep), ipc_perms)
|
||||||
|
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
|
||||||
|
|
||||||
|
// Init: slot 5 = Endpoint(input), slot 6 = Process(input)
|
||||||
|
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Endpoint(input_ep), all_perms)
|
||||||
|
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
|
||||||
|
kernel_ref().grant_capability(init_pid, domain::resource::Resource::Process(input_pid), all_perms)
|
||||||
|
.unwrap_or_else(|_| crate::halt(uart, "grant failed"));
|
||||||
|
|
||||||
uart.write_str("k-os: capabilities granted\n");
|
uart.write_str("k-os: capabilities granted\n");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
use crate::hw::uart::Uart;
|
use crate::hw::uart::Uart;
|
||||||
use crate::hw::virtio_gpu::VirtioGpu;
|
use crate::hw::virtio_gpu::VirtioGpu;
|
||||||
use crate::{kernel_ref, mmu};
|
use crate::{kernel_ref, mmu};
|
||||||
|
use types::display::Dimension;
|
||||||
use types::id::ProcessId;
|
use types::id::ProcessId;
|
||||||
|
|
||||||
const GPU_VA: usize = 0x20000;
|
const GPU_VA: usize = 0x20000;
|
||||||
@@ -8,8 +9,8 @@ const VQUEUE_VA: usize = 0x21000;
|
|||||||
const CMDBUF_VA: usize = 0x23000;
|
const CMDBUF_VA: usize = 0x23000;
|
||||||
const PHYSINFO_VA: usize = 0x24000;
|
const PHYSINFO_VA: usize = 0x24000;
|
||||||
const FB_VA: usize = 0x100000;
|
const FB_VA: usize = 0x100000;
|
||||||
const FB_WIDTH: types::display::Dimension = types::display::Dimension::new(640);
|
const FB_WIDTH: Dimension = Dimension::new(640);
|
||||||
const FB_HEIGHT: types::display::Dimension = types::display::Dimension::new(480);
|
const FB_HEIGHT: Dimension = Dimension::new(480);
|
||||||
const FB_BPP: usize = 4;
|
const FB_BPP: usize = 4;
|
||||||
const FB_PAGES: usize = (FB_WIDTH.raw() as usize * FB_HEIGHT.raw() as usize * FB_BPP + 4095) / 4096;
|
const FB_PAGES: usize = (FB_WIDTH.raw() as usize * FB_HEIGHT.raw() as usize * FB_BPP + 4095) / 4096;
|
||||||
|
|
||||||
@@ -48,10 +49,155 @@ pub fn configure(uart: &Uart, display_pid: ProcessId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
gpu.init_device(vq_phys);
|
gpu.init_device(vq_phys);
|
||||||
gpu.setup_framebuffer(cmd_phys, vq_phys, fb_phys, FB_WIDTH, FB_HEIGHT);
|
|
||||||
|
// Raw GPU setup — bypass hw-virtio to debug
|
||||||
|
let w = FB_WIDTH.raw() as u32;
|
||||||
|
let h = FB_HEIGHT.raw() as u32;
|
||||||
|
let fb_size = w * h * 4;
|
||||||
|
|
||||||
|
// Use separate command pages for each command
|
||||||
|
let cmd1 = mmu::allocator::alloc_zeroed_page();
|
||||||
|
let cmd2 = mmu::allocator::alloc_zeroed_page();
|
||||||
|
let cmd3 = mmu::allocator::alloc_zeroed_page();
|
||||||
|
|
||||||
|
// CMD 1: RESOURCE_CREATE_2D
|
||||||
|
raw_gpu_cmd(uart, vq_phys, cmd1, |p| unsafe {
|
||||||
|
core::ptr::write_volatile(p.add(0) as *mut u32, 0x0101); // type
|
||||||
|
core::ptr::write_volatile(p.add(24) as *mut u32, 1); // resource_id
|
||||||
|
core::ptr::write_volatile(p.add(28) as *mut u32, 1); // format B8G8R8A8
|
||||||
|
core::ptr::write_volatile(p.add(32) as *mut u32, w);
|
||||||
|
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
||||||
|
}, 40, "create_2d");
|
||||||
|
|
||||||
|
// CMD 2: RESOURCE_ATTACH_BACKING (type swapped due to QEMU 11.x bug)
|
||||||
|
raw_gpu_cmd(uart, vq_phys, cmd2, |p| unsafe {
|
||||||
|
core::ptr::write_volatile(p.add(0) as *mut u32, 0x0106);
|
||||||
|
core::ptr::write_volatile(p.add(24) as *mut u32, 1); // resource_id
|
||||||
|
core::ptr::write_volatile(p.add(28) as *mut u32, 1); // nr_entries
|
||||||
|
core::ptr::write_volatile(p.add(32) as *mut u64, fb_phys as u64);
|
||||||
|
core::ptr::write_volatile(p.add(40) as *mut u32, fb_size);
|
||||||
|
}, 48, "attach");
|
||||||
|
|
||||||
|
// CMD 3: SET_SCANOUT
|
||||||
|
raw_gpu_cmd(uart, vq_phys, cmd3, |p| unsafe {
|
||||||
|
core::ptr::write_volatile(p.add(0) as *mut u32, 0x0103); // type
|
||||||
|
core::ptr::write_volatile(p.add(24) as *mut u32, 0); // rect.x
|
||||||
|
core::ptr::write_volatile(p.add(28) as *mut u32, 0); // rect.y
|
||||||
|
core::ptr::write_volatile(p.add(32) as *mut u32, w); // rect.width
|
||||||
|
core::ptr::write_volatile(p.add(36) as *mut u32, h); // rect.height
|
||||||
|
core::ptr::write_volatile(p.add(40) as *mut u32, 0); // scanout_id
|
||||||
|
core::ptr::write_volatile(p.add(44) as *mut u32, 1); // resource_id
|
||||||
|
}, 48, "scanout");
|
||||||
|
|
||||||
|
// Fill framebuffer with blue
|
||||||
|
let fb = fb_phys as *mut u32;
|
||||||
|
let total = w as usize * h as usize;
|
||||||
|
for i in 0..total {
|
||||||
|
unsafe { core::ptr::write_volatile(fb.add(i), 0xFF4040FF); }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transfer + flush using shared cmd buffer
|
||||||
|
raw_gpu_cmd(uart, vq_phys, cmd_phys, |p| unsafe {
|
||||||
|
core::ptr::write_volatile(p.add(0) as *mut u32, 0x0105); // TRANSFER_TO_HOST_2D
|
||||||
|
core::ptr::write_volatile(p.add(24) as *mut u32, 0); // rect.x
|
||||||
|
core::ptr::write_volatile(p.add(28) as *mut u32, 0); // rect.y
|
||||||
|
core::ptr::write_volatile(p.add(32) as *mut u32, w);
|
||||||
|
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
||||||
|
core::ptr::write_volatile(p.add(40) as *mut u64, 0); // offset
|
||||||
|
core::ptr::write_volatile(p.add(48) as *mut u32, 1); // resource_id
|
||||||
|
}, 56, "xfer");
|
||||||
|
|
||||||
|
raw_gpu_cmd(uart, vq_phys, cmd_phys, |p| unsafe {
|
||||||
|
core::ptr::write_volatile(p.add(0) as *mut u32, 0x0104); // RESOURCE_FLUSH (swapped for QEMU 11.x)
|
||||||
|
core::ptr::write_volatile(p.add(24) as *mut u32, 0);
|
||||||
|
core::ptr::write_volatile(p.add(28) as *mut u32, 0);
|
||||||
|
core::ptr::write_volatile(p.add(32) as *mut u32, w);
|
||||||
|
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
||||||
|
core::ptr::write_volatile(p.add(40) as *mut u32, 1); // resource_id
|
||||||
|
}, 48, "flush");
|
||||||
|
|
||||||
uart.write_str("k-os: VirtIO GPU configured\n");
|
uart.write_str("k-os: VirtIO GPU configured\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn raw_gpu_cmd(uart: &Uart, vq_phys: usize, cmd_phys: usize, write_fn: impl FnOnce(*mut u8), cmd_len: u32, label: &str) {
|
||||||
|
let resp_phys = cmd_phys + 512;
|
||||||
|
|
||||||
|
// Zero cmd + response area
|
||||||
|
unsafe {
|
||||||
|
core::ptr::write_bytes(cmd_phys as *mut u8, 0, 1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write command
|
||||||
|
write_fn(cmd_phys as *mut u8);
|
||||||
|
|
||||||
|
// Verify type field
|
||||||
|
let cmd_type = unsafe { core::ptr::read_volatile(cmd_phys as *const u32) };
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let desc = vq_phys as *mut u8;
|
||||||
|
// Desc 0: cmd (readable)
|
||||||
|
core::ptr::write_volatile(desc as *mut u64, cmd_phys as u64);
|
||||||
|
core::ptr::write_volatile(desc.add(8) as *mut u32, cmd_len);
|
||||||
|
core::ptr::write_volatile(desc.add(12) as *mut u16, 1); // NEXT
|
||||||
|
core::ptr::write_volatile(desc.add(14) as *mut u16, 1);
|
||||||
|
// Desc 1: response (writable)
|
||||||
|
let desc1 = desc.add(16);
|
||||||
|
core::ptr::write_volatile(desc1 as *mut u64, resp_phys as u64);
|
||||||
|
core::ptr::write_volatile(desc1.add(8) as *mut u32, 24);
|
||||||
|
core::ptr::write_volatile(desc1.add(12) as *mut u16, 2); // WRITE
|
||||||
|
core::ptr::write_volatile(desc1.add(14) as *mut u16, 0);
|
||||||
|
|
||||||
|
let avail = vq_phys + 128; // 8 descs * 16 bytes
|
||||||
|
let idx = core::ptr::read_volatile((avail + 2) as *const u16);
|
||||||
|
core::ptr::write_volatile((avail + 4 + (idx as usize % 8) * 2) as *mut u16, 0);
|
||||||
|
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
|
||||||
|
core::ptr::write_volatile((avail + 2) as *mut u16, idx.wrapping_add(1));
|
||||||
|
core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
|
||||||
|
|
||||||
|
let gpu_mmio = hw_virtio::mmio::DirectMmio::new(
|
||||||
|
crate::hw::virtio_gpu::VirtioGpu::probe().unwrap().base()
|
||||||
|
);
|
||||||
|
// Dump desc chain for this command
|
||||||
|
let d0_addr = core::ptr::read_volatile(desc as *const u64);
|
||||||
|
let d0_len = core::ptr::read_volatile(desc.add(8) as *const u32);
|
||||||
|
let d0_flags = core::ptr::read_volatile(desc.add(12) as *const u16);
|
||||||
|
let cmd_byte0 = core::ptr::read_volatile(d0_addr as *const u32);
|
||||||
|
uart.write_str("k-os: desc0 addr=");
|
||||||
|
uart.write_hex(d0_addr as usize);
|
||||||
|
uart.write_str(" len=");
|
||||||
|
uart.write_dec(d0_len as usize);
|
||||||
|
uart.write_str(" cmd[0]=");
|
||||||
|
uart.write_hex(cmd_byte0 as usize);
|
||||||
|
uart.put_char(b'\n');
|
||||||
|
|
||||||
|
use hw_virtio::mmio::MmioAccess;
|
||||||
|
gpu_mmio.write(0x050, 0);
|
||||||
|
|
||||||
|
let used = vq_phys + 4096;
|
||||||
|
let expected = idx.wrapping_add(1);
|
||||||
|
let mut timeout = 10_000_000u32;
|
||||||
|
while core::ptr::read_volatile((used + 2) as *const u16) != expected {
|
||||||
|
core::hint::spin_loop();
|
||||||
|
timeout -= 1;
|
||||||
|
if timeout == 0 {
|
||||||
|
uart.write_str("k-os: ");
|
||||||
|
uart.write_str(label);
|
||||||
|
uart.write_str(" TIMEOUT\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let resp = core::ptr::read_volatile(resp_phys as *const u32);
|
||||||
|
uart.write_str("k-os: ");
|
||||||
|
uart.write_str(label);
|
||||||
|
uart.write_str(" type=");
|
||||||
|
uart.write_hex(cmd_type as usize);
|
||||||
|
uart.write_str(" resp=");
|
||||||
|
uart.write_hex(resp as usize);
|
||||||
|
uart.put_char(b'\n');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn map_page(pt: &mut mmu::PageTable, va: usize, pa: usize) {
|
fn map_page(pt: &mut mmu::PageTable, va: usize, pa: usize) {
|
||||||
mmu::map(pt, mmu::Mapping {
|
mmu::map(pt, mmu::Mapping {
|
||||||
va: types::address::VirtualAddress::from_raw(va),
|
va: types::address::VirtualAddress::from_raw(va),
|
||||||
|
|||||||
19
crates/adapter-riscv/src/devices/input_setup.rs
Normal file
19
crates/adapter-riscv/src/devices/input_setup.rs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
use crate::hw::uart::Uart;
|
||||||
|
use crate::{board, kernel_ref, mmu};
|
||||||
|
use types::id::ProcessId;
|
||||||
|
|
||||||
|
const UART_VA: usize = 0x20000;
|
||||||
|
|
||||||
|
pub fn configure(uart: &Uart, input_pid: ProcessId) {
|
||||||
|
let pt_addr = kernel_ref().platform_mut().page_table_for(input_pid);
|
||||||
|
let pt = unsafe { &mut *(pt_addr as *mut mmu::PageTable) };
|
||||||
|
|
||||||
|
mmu::map(pt, mmu::Mapping {
|
||||||
|
va: types::address::VirtualAddress::from_raw(UART_VA),
|
||||||
|
pa: types::address::PhysicalAddress::from_raw(board::UART_BASE),
|
||||||
|
pages: 1,
|
||||||
|
flags: mmu::Sv39Flags::user_rw(),
|
||||||
|
});
|
||||||
|
|
||||||
|
uart.write_str("k-os: UART mapped for input\n");
|
||||||
|
}
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
pub mod gpu_setup;
|
pub mod gpu_setup;
|
||||||
|
pub mod input_setup;
|
||||||
pub mod storage_setup;
|
pub mod storage_setup;
|
||||||
|
|||||||
@@ -18,19 +18,24 @@ impl VirtioGpu {
|
|||||||
self.mmio.base()
|
self.mmio.base()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mmio(&self) -> &DirectMmio {
|
||||||
|
&self.mmio
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init_device(&self, vqueue_phys: usize) {
|
pub fn init_device(&self, vqueue_phys: usize) {
|
||||||
mmio::init_legacy_device(&self.mmio, vqueue_phys, QUEUE_SIZE as u32);
|
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: Dimension, height: Dimension) {
|
pub fn setup_framebuffer(&self, cmd_phys: usize, vq_phys: usize, fb_phys: usize, width: Dimension, height: Dimension) -> bool {
|
||||||
let queue = VirtioQueue::new(QueueAddrs::from_base(vq_phys));
|
let queue = VirtioQueue::new(QueueAddrs::from_base(vq_phys));
|
||||||
let dma = GpuDmaAddrs {
|
let dma = GpuDmaAddrs {
|
||||||
cmd_pa: PhysicalAddress::from_raw(cmd_phys),
|
cmd_pa: PhysicalAddress::from_raw(cmd_phys),
|
||||||
cmd_va: VirtualAddress::from_raw(cmd_phys),
|
cmd_va: VirtualAddress::from_raw(cmd_phys),
|
||||||
resp_pa: PhysicalAddress::from_raw(cmd_phys + 512),
|
resp_pa: PhysicalAddress::from_raw(cmd_phys + 512),
|
||||||
};
|
};
|
||||||
gpu::create_2d(&self.mmio, &queue, &dma, 1, width, height);
|
let ok1 = gpu::create_2d(&self.mmio, &queue, &dma, 1, width, height);
|
||||||
gpu::attach_backing(&self.mmio, &queue, &dma, 1, PhysicalAddress::from_raw(fb_phys), width.raw() as u32 * height.raw() as u32 * 4);
|
let ok2 = 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);
|
let ok3 = gpu::set_scanout(&self.mmio, &queue, &dma, 0, 1, width, height);
|
||||||
|
ok1 && ok2 && ok3
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,12 +48,14 @@ extern "C" fn kmain() -> ! {
|
|||||||
let (init, _) = boot::loader::load_init(&uart, &mut blk, kernel_pt);
|
let (init, _) = boot::loader::load_init(&uart, &mut blk, kernel_pt);
|
||||||
let storage = boot::loader::load_server(&uart, &mut blk, kernel_pt, b"storage.elf", "storage");
|
let storage = boot::loader::load_server(&uart, &mut blk, kernel_pt, b"storage.elf", "storage");
|
||||||
let display = boot::loader::load_server(&uart, &mut blk, kernel_pt, b"display.elf", "display");
|
let display = boot::loader::load_server(&uart, &mut blk, kernel_pt, b"display.elf", "display");
|
||||||
|
let input = boot::loader::load_server(&uart, &mut blk, kernel_pt, b"input.elf", "input");
|
||||||
let virtio_base = blk.base();
|
let virtio_base = blk.base();
|
||||||
drop(blk);
|
drop(blk);
|
||||||
|
|
||||||
devices::storage_setup::configure(&uart, init.pid, storage.pid, virtio_base);
|
devices::storage_setup::configure(&uart, init.pid, storage.pid, virtio_base);
|
||||||
devices::gpu_setup::configure(&uart, display.pid);
|
devices::gpu_setup::configure(&uart, display.pid);
|
||||||
boot::caps::grant_all(&uart, init.pid, storage.pid, display.pid);
|
devices::input_setup::configure(&uart, input.pid);
|
||||||
|
boot::caps::grant_all(&uart, init.pid, storage.pid, display.pid, input.pid);
|
||||||
|
|
||||||
uart.write_str("k-os: entering userspace\n");
|
uart.write_str("k-os: entering userspace\n");
|
||||||
boot::loader::enter_userspace(init.pid, init.entry, SHARED_BUF_VA);
|
boot::loader::enter_userspace(init.pid, init.entry, SHARED_BUF_VA);
|
||||||
|
|||||||
@@ -15,3 +15,4 @@ userspace = { workspace = true }
|
|||||||
server-framework = { workspace = true }
|
server-framework = { workspace = true }
|
||||||
display-server = { workspace = true }
|
display-server = { workspace = true }
|
||||||
driver-framebuffer-virtio = { workspace = true }
|
driver-framebuffer-virtio = { workspace = true }
|
||||||
|
port-framebuffer = { workspace = true }
|
||||||
|
|||||||
@@ -8,7 +8,20 @@ use types::bounded::CapSlot;
|
|||||||
|
|
||||||
#[unsafe(no_mangle)]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn main() {
|
extern "C" fn main() {
|
||||||
let fb = VirtioFramebuffer::new();
|
let mut fb = VirtioFramebuffer::new();
|
||||||
|
|
||||||
|
use port_framebuffer::Framebuffer;
|
||||||
|
use types::display::{Color, Dimension, ScreenCoord};
|
||||||
|
|
||||||
|
// Initial clear to dark blue so we know the GPU works
|
||||||
|
fb.clear(Color::rgb(0, 0, 40));
|
||||||
|
fb.fill_rect(
|
||||||
|
ScreenCoord::new(10), ScreenCoord::new(10),
|
||||||
|
Dimension::new(200), Dimension::new(30),
|
||||||
|
Color::rgb(255, 255, 255),
|
||||||
|
);
|
||||||
|
fb.flush();
|
||||||
|
|
||||||
let mut srv = display_server::server::DisplayServer::new(fb);
|
let mut srv = display_server::server::DisplayServer::new(fb);
|
||||||
let ep = CapSlot::new(0).unwrap();
|
let ep = CapSlot::new(0).unwrap();
|
||||||
server_framework::serve(&mut srv, ep);
|
server_framework::serve(&mut srv, ep);
|
||||||
|
|||||||
17
crates/bins/input-qemu/Cargo.toml
Normal file
17
crates/bins/input-qemu/Cargo.toml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
[package]
|
||||||
|
name = "input-qemu"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "input-qemu"
|
||||||
|
test = false
|
||||||
|
bench = false
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
types = { workspace = true }
|
||||||
|
userspace-rt = { workspace = true }
|
||||||
|
userspace = { workspace = true }
|
||||||
|
server-framework = { workspace = true }
|
||||||
|
input-server = { workspace = true }
|
||||||
|
driver-input-uart = { workspace = true }
|
||||||
16
crates/bins/input-qemu/src/main.rs
Normal file
16
crates/bins/input-qemu/src/main.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
|
||||||
|
extern crate userspace_rt;
|
||||||
|
|
||||||
|
use driver_input_uart::UartInputDevice;
|
||||||
|
use types::bounded::CapSlot;
|
||||||
|
|
||||||
|
#[unsafe(no_mangle)]
|
||||||
|
extern "C" fn main() {
|
||||||
|
let device = UartInputDevice::new();
|
||||||
|
let mut srv = input_server::server::InputServer::new(device);
|
||||||
|
let ep = CapSlot::new(0).unwrap();
|
||||||
|
server_framework::serve(&mut srv, ep);
|
||||||
|
loop { userspace::syscall::yield_now(); }
|
||||||
|
}
|
||||||
7
crates/drivers/input/uart/Cargo.toml
Normal file
7
crates/drivers/input/uart/Cargo.toml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "driver-input-uart"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
port-input-device = { workspace = true }
|
||||||
47
crates/drivers/input/uart/src/lib.rs
Normal file
47
crates/drivers/input/uart/src/lib.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
#![no_std]
|
||||||
|
|
||||||
|
use port_input_device::{InputDevice, InputEvent, Key};
|
||||||
|
|
||||||
|
const UART_VA: usize = 0x20000;
|
||||||
|
const UART_LSR_OFFSET: usize = 5;
|
||||||
|
|
||||||
|
pub struct UartInputDevice;
|
||||||
|
|
||||||
|
impl UartInputDevice {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_byte(&self) -> Option<u8> {
|
||||||
|
let lsr = unsafe { core::ptr::read_volatile((UART_VA + UART_LSR_OFFSET) as *const u8) };
|
||||||
|
if lsr & 1 != 0 {
|
||||||
|
Some(unsafe { core::ptr::read_volatile(UART_VA as *const u8) })
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn byte_to_key(byte: u8) -> Key {
|
||||||
|
match byte {
|
||||||
|
b'w' | b'W' => Key::Up,
|
||||||
|
b's' | b'S' => Key::Down,
|
||||||
|
b'a' | b'A' => Key::Left,
|
||||||
|
b'd' | b'D' => Key::Right,
|
||||||
|
b'\r' | b'\n' => Key::Enter,
|
||||||
|
0x1B => Key::Escape,
|
||||||
|
c => Key::Char(c),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InputDevice for UartInputDevice {
|
||||||
|
fn poll(&mut self) -> Option<InputEvent> {
|
||||||
|
let byte = self.read_byte()?;
|
||||||
|
Some(InputEvent::KeyDown(Self::byte_to_key(byte)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn has_event(&self) -> bool {
|
||||||
|
let lsr = unsafe { core::ptr::read_volatile((UART_VA + UART_LSR_OFFSET) as *const u8) };
|
||||||
|
lsr & 1 != 0
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,20 +9,21 @@ pub struct GpuDmaAddrs {
|
|||||||
pub resp_pa: PhysicalAddress,
|
pub resp_pa: PhysicalAddress,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, width: Dimension, height: Dimension) {
|
pub fn create_2d(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, resource_id: u32, width: Dimension, height: Dimension) -> bool {
|
||||||
write_cmd_header(dma.cmd_va, 0x0101);
|
write_cmd_header(dma.cmd_va, 0x0101);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() 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(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(28) as *mut u32, 1);
|
||||||
core::ptr::write_volatile(p.add(32) as *mut u32, width.raw() as u32);
|
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(36) as *mut u32, 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) {
|
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_va, 0x0104);
|
// QEMU 11.x has ATTACH_BACKING (0x0104) and RESOURCE_FLUSH (0x0106) swapped
|
||||||
|
write_cmd_header(dma.cmd_va, 0x0106);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() 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(24) as *mut u32, resource_id);
|
||||||
@@ -30,10 +31,10 @@ pub fn attach_backing(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaA
|
|||||||
core::ptr::write_volatile(p.add(32) as *mut u64, fb_pa.raw() as u64);
|
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);
|
core::ptr::write_volatile(p.add(40) as *mut u32, 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) {
|
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_va, 0x0103);
|
write_cmd_header(dma.cmd_va, 0x0103);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() as *mut u8;
|
let p = dma.cmd_va.raw() as *mut u8;
|
||||||
@@ -42,10 +43,10 @@ pub fn set_scanout(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddr
|
|||||||
core::ptr::write_volatile(p.add(40) as *mut u32, scanout_id);
|
core::ptr::write_volatile(p.add(40) as *mut u32, scanout_id);
|
||||||
core::ptr::write_volatile(p.add(44) as *mut u32, resource_id);
|
core::ptr::write_volatile(p.add(44) as *mut u32, 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) {
|
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_va, 0x0105);
|
write_cmd_header(dma.cmd_va, 0x0105);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() as *mut u8;
|
let p = dma.cmd_va.raw() as *mut u8;
|
||||||
@@ -56,11 +57,12 @@ pub fn transfer_to_host(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDm
|
|||||||
core::ptr::write_volatile(p.add(40) as *mut u64, 0);
|
core::ptr::write_volatile(p.add(40) as *mut u64, 0);
|
||||||
core::ptr::write_volatile(p.add(48) as *mut u32, resource_id);
|
core::ptr::write_volatile(p.add(48) as *mut u32, 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) {
|
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_va, 0x0106);
|
// QEMU 11.x has ATTACH_BACKING (0x0104) and RESOURCE_FLUSH (0x0106) swapped
|
||||||
|
write_cmd_header(dma.cmd_va, 0x0104);
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = dma.cmd_va.raw() 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(24) as *mut u32, x);
|
||||||
@@ -69,21 +71,24 @@ pub fn resource_flush(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaA
|
|||||||
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
core::ptr::write_volatile(p.add(36) as *mut u32, h);
|
||||||
core::ptr::write_volatile(p.add(40) as *mut u32, resource_id);
|
core::ptr::write_volatile(p.add(40) as *mut u32, resource_id);
|
||||||
}
|
}
|
||||||
submit_cmd(mmio, queue, dma, 48);
|
submit_cmd(mmio, queue, dma, 48)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_cmd_header(cmd_va: VirtualAddress, cmd_type: u32) {
|
fn write_cmd_header(cmd_va: VirtualAddress, cmd_type: u32) {
|
||||||
unsafe {
|
unsafe {
|
||||||
let p = cmd_va.raw() as *mut u8;
|
let p = cmd_va.raw() as *mut u8;
|
||||||
core::ptr::write_bytes(p, 0, 512);
|
// Use volatile writes to zero the buffer — ensures device sees the zeros
|
||||||
|
for i in 0..128 {
|
||||||
|
core::ptr::write_volatile((p as *mut u32).add(i), 0);
|
||||||
|
}
|
||||||
core::ptr::write_volatile(p as *mut u32, cmd_type);
|
core::ptr::write_volatile(p as *mut u32, cmd_type);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn submit_cmd(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, cmd_len: u32) {
|
fn submit_cmd(mmio: &impl MmioAccess, queue: &VirtioQueue, dma: &GpuDmaAddrs, cmd_len: u32) -> bool {
|
||||||
let descs = [
|
let descs = [
|
||||||
Descriptor { addr: dma.cmd_pa.raw() as u64, len: cmd_len, writable: false },
|
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 },
|
Descriptor { addr: dma.resp_pa.raw() as u64, len: 24, writable: true },
|
||||||
];
|
];
|
||||||
queue.submit(mmio, &descs);
|
queue.submit(mmio, &descs)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,6 +13,8 @@ const CMD_READ_FILE: usize = 1;
|
|||||||
|
|
||||||
fn create_cap() -> CapSlot { CapSlot::new(0).unwrap() }
|
fn create_cap() -> CapSlot { CapSlot::new(0).unwrap() }
|
||||||
fn storage_ep() -> CapSlot { CapSlot::new(2).unwrap() }
|
fn storage_ep() -> CapSlot { CapSlot::new(2).unwrap() }
|
||||||
|
fn display_ep() -> CapSlot { CapSlot::new(3).unwrap() }
|
||||||
|
fn input_ep() -> CapSlot { CapSlot::new(5).unwrap() }
|
||||||
|
|
||||||
fn debug_print(s: &[u8]) {
|
fn debug_print(s: &[u8]) {
|
||||||
for &b in s {
|
for &b in s {
|
||||||
@@ -124,11 +126,9 @@ struct ServerInfo {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const SERVERS: &[ServerInfo] = &[
|
const SERVERS: &[ServerInfo] = &[
|
||||||
ServerInfo { filename: b"input.elf", name: b"input" },
|
|
||||||
ServerInfo { filename: b"log.elf", name: b"log" },
|
ServerInfo { filename: b"log.elf", name: b"log" },
|
||||||
ServerInfo { filename: b"permis.elf", name: b"permis" },
|
ServerInfo { filename: b"permis.elf", name: b"permis" },
|
||||||
ServerInfo { filename: b"session.elf", name: b"session" },
|
ServerInfo { filename: b"session.elf", name: b"session" },
|
||||||
ServerInfo { filename: b"snake.elf", name: b"snake" },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
pub fn run(shared_buf_va: usize) -> ! {
|
pub fn run(shared_buf_va: usize) -> ! {
|
||||||
@@ -165,6 +165,17 @@ pub fn run(shared_buf_va: usize) -> ! {
|
|||||||
debug_print(b")\n");
|
debug_print(b")\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load snake — give it display endpoint (slot 0) and input endpoint (slot 1)
|
||||||
|
debug_print(b"init: loading snake... ");
|
||||||
|
if let Some(snake) = load_binary(shared_buf, b"snake.elf") {
|
||||||
|
syscall::delegate(display_ep(), snake.process_cap, ipc_perms);
|
||||||
|
syscall::delegate(input_ep(), snake.process_cap, ipc_perms);
|
||||||
|
syscall::spawn_thread_in(snake.process_cap, snake.entry_point, USER_STACK_TOP);
|
||||||
|
debug_print(b"ok\n");
|
||||||
|
} else {
|
||||||
|
debug_print(b"SKIP\n");
|
||||||
|
}
|
||||||
|
|
||||||
debug_print(b"init: all loaded, supervising\n");
|
debug_print(b"init: all loaded, supervising\n");
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ copy init "init.elf"
|
|||||||
copy echo "echo.elf"
|
copy echo "echo.elf"
|
||||||
copy snake "snake.elf"
|
copy snake "snake.elf"
|
||||||
copy display-qemu "display.elf"
|
copy display-qemu "display.elf"
|
||||||
copy input-server "input.elf"
|
copy input-qemu "input.elf"
|
||||||
copy log-server "log.elf"
|
copy log-server "log.elf"
|
||||||
copy permission-manager "permis.elf"
|
copy permission-manager "permis.elf"
|
||||||
copy storage-qemu "storage.elf"
|
copy storage-qemu "storage.elf"
|
||||||
|
|||||||
Reference in New Issue
Block a user