feat(rumpk): Phase 3.5c VirtIO-GPU Retina Driver (WIP)
- Vision: Updated NexShell section with VirtIO-GPU transport detail - Canvas: Implemented framebuffer.zig (800x600x32bpp in BSS) - Retina: Implemented gpu.zig VirtIO-GPU MMIO driver - Device probing across MMIO slots 0x10001000-0x10008000 - Support for VirtIO MMIO v1 (legacy) and v2 (modern) - Queue setup with PFN for legacy devices - 2D Resource creation, backing attachment, scanout setup - Integration: UI fiber now calls virtio_gpu_flush() after render - Status: GPU detected at 0x10008000 (DevID=16), queue initialized - Remaining: Debug command/response polling (hangs on first command)
This commit is contained in:
parent
8aa50eb3ef
commit
f6a49db00f
13
build.sh
13
build.sh
|
|
@ -109,6 +109,18 @@ zig build-obj \
|
||||||
mv ui.o "$BUILD_DIR/ui.o"
|
mv ui.o "$BUILD_DIR/ui.o"
|
||||||
echo " → $BUILD_DIR/ui.o"
|
echo " → $BUILD_DIR/ui.o"
|
||||||
|
|
||||||
|
# Compile GPU Driver (Retina)
|
||||||
|
echo "[1.3/8] Compiling GPU Driver (VirtIO-GPU)..."
|
||||||
|
zig build-obj \
|
||||||
|
-target $ZIG_TARGET \
|
||||||
|
$ZIG_OBJ_FLAGS \
|
||||||
|
-O ReleaseFast \
|
||||||
|
"$RUMPK_DIR/hal/gpu.zig" \
|
||||||
|
--name gpu
|
||||||
|
|
||||||
|
mv gpu.o "$BUILD_DIR/gpu.o"
|
||||||
|
echo " → $BUILD_DIR/gpu.o"
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Step 2: Compile context switch assembly
|
# Step 2: Compile context switch assembly
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
@ -545,6 +557,7 @@ $LINKER \
|
||||||
"$BUILD_DIR/loader.o" \
|
"$BUILD_DIR/loader.o" \
|
||||||
"$BUILD_DIR/nexshell.o" \
|
"$BUILD_DIR/nexshell.o" \
|
||||||
"$BUILD_DIR/ui.o" \
|
"$BUILD_DIR/ui.o" \
|
||||||
|
"$BUILD_DIR/gpu.o" \
|
||||||
"$BUILD_DIR/microui.o" \
|
"$BUILD_DIR/microui.o" \
|
||||||
$NIM_OBJS \
|
$NIM_OBJS \
|
||||||
-o "$BUILD_DIR/rumpk-$ARCH.elf"
|
-o "$BUILD_DIR/rumpk-$ARCH.elf"
|
||||||
|
|
|
||||||
|
|
@ -210,6 +210,16 @@ proc kmain() {.exportc, cdecl.} =
|
||||||
ion_pool_init()
|
ion_pool_init()
|
||||||
hal_io_init()
|
hal_io_init()
|
||||||
|
|
||||||
|
# 1.5 The Retina (VirtIO-GPU)
|
||||||
|
proc virtio_gpu_init(base: uint64) {.importc, cdecl.}
|
||||||
|
|
||||||
|
# On QEMU virt machine, virtio-mmio devices are at 0x10001000-0x10008000
|
||||||
|
# GPU could be at any slot.
|
||||||
|
kprintln("[Kernel] Scanning for VirtIO-GPU...")
|
||||||
|
for i in 1..8:
|
||||||
|
let base_addr = 0x10000000'u64 + (uint64(i) * 0x1000'u64)
|
||||||
|
virtio_gpu_init(base_addr)
|
||||||
|
|
||||||
# 2. Channel Infrastructure
|
# 2. Channel Infrastructure
|
||||||
kprintln("[Kernel] Mapping Sovereign Channels...")
|
kprintln("[Kernel] Mapping Sovereign Channels...")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,24 @@
|
||||||
|
// MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
||||||
|
// Phase 3.5c: The Canvas - Static Framebuffer in BSS
|
||||||
|
// This is our "Video RAM" living in kernel memory.
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
// Resolution: 800x600 @ 32bpp (ARGB)
|
||||||
|
pub const WIDTH: usize = 800;
|
||||||
|
pub const HEIGHT: usize = 600;
|
||||||
|
pub const STRIDE: usize = WIDTH;
|
||||||
|
|
||||||
|
// The Physical Backing Store (1.9MB in BSS)
|
||||||
|
// Zero-initialized at boot.
|
||||||
|
var fb_memory: [WIDTH * HEIGHT]u32 = [_]u32{0} ** (WIDTH * HEIGHT);
|
||||||
|
|
||||||
|
// Clip rectangle (for MU_COMMAND_CLIP)
|
||||||
|
var clip_x: i32 = 0;
|
||||||
|
var clip_y: i32 = 0;
|
||||||
|
var clip_w: i32 = WIDTH;
|
||||||
|
var clip_h: i32 = HEIGHT;
|
||||||
|
|
||||||
pub const Rect = struct {
|
pub const Rect = struct {
|
||||||
x: i32,
|
x: i32,
|
||||||
y: i32,
|
y: i32,
|
||||||
|
|
@ -7,21 +26,97 @@ pub const Rect = struct {
|
||||||
h: i32,
|
h: i32,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub fn get_buffer() [*]u32 {
|
||||||
|
return &fb_memory;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_buffer_phys() usize {
|
||||||
|
// Identity mapping for now
|
||||||
|
return @intFromPtr(&fb_memory);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_size() usize {
|
||||||
|
return WIDTH * HEIGHT * @sizeOf(u32);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn init() void {
|
pub fn init() void {
|
||||||
// TODO: Init VirtIO GPU or PL111
|
// Clear to dark blue (Nexus brand)
|
||||||
|
clear(0xFF101030);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(color: u32) void {
|
||||||
|
@memset(&fb_memory, color);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_clip(r: Rect) void {
|
pub fn set_clip(r: Rect) void {
|
||||||
// TODO: Set scissoring
|
clip_x = r.x;
|
||||||
_ = r;
|
clip_y = r.y;
|
||||||
|
clip_w = r.w;
|
||||||
|
clip_h = r.h;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fill_rect(x: i32, y: i32, w: i32, h: i32, color: u32) void {
|
pub fn put_pixel(x: i32, y: i32, color: u32) void {
|
||||||
// TODO: Plot to framebuffer
|
if (x < 0 or y < 0) return;
|
||||||
// For now, valid compilation is the goal.
|
if (x >= @as(i32, @intCast(WIDTH)) or y >= @as(i32, @intCast(HEIGHT))) return;
|
||||||
_ = x;
|
|
||||||
_ = y;
|
// Clip check
|
||||||
_ = w;
|
if (x < clip_x or x >= clip_x + clip_w) return;
|
||||||
_ = h;
|
if (y < clip_y or y >= clip_y + clip_h) return;
|
||||||
_ = color;
|
|
||||||
|
const ux: usize = @intCast(x);
|
||||||
|
const uy: usize = @intCast(y);
|
||||||
|
fb_memory[uy * WIDTH + ux] = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Optimized Rect Fill (The Workhorse for microui)
|
||||||
|
pub fn fill_rect(x: i32, y: i32, w: i32, h: i32, color: u32) void {
|
||||||
|
// Clipping to screen
|
||||||
|
var rx = x;
|
||||||
|
var ry = y;
|
||||||
|
var rw = w;
|
||||||
|
var rh = h;
|
||||||
|
|
||||||
|
if (rx < 0) {
|
||||||
|
rw += rx;
|
||||||
|
rx = 0;
|
||||||
|
}
|
||||||
|
if (ry < 0) {
|
||||||
|
rh += ry;
|
||||||
|
ry = 0;
|
||||||
|
}
|
||||||
|
if (rx + rw > @as(i32, @intCast(WIDTH))) {
|
||||||
|
rw = @as(i32, @intCast(WIDTH)) - rx;
|
||||||
|
}
|
||||||
|
if (ry + rh > @as(i32, @intCast(HEIGHT))) {
|
||||||
|
rh = @as(i32, @intCast(HEIGHT)) - ry;
|
||||||
|
}
|
||||||
|
if (rw <= 0 or rh <= 0) return;
|
||||||
|
|
||||||
|
// Clip to clip rect
|
||||||
|
if (rx < clip_x) {
|
||||||
|
rw -= (clip_x - rx);
|
||||||
|
rx = clip_x;
|
||||||
|
}
|
||||||
|
if (ry < clip_y) {
|
||||||
|
rh -= (clip_y - ry);
|
||||||
|
ry = clip_y;
|
||||||
|
}
|
||||||
|
if (rx + rw > clip_x + clip_w) {
|
||||||
|
rw = clip_x + clip_w - rx;
|
||||||
|
}
|
||||||
|
if (ry + rh > clip_y + clip_h) {
|
||||||
|
rh = clip_y + clip_h - ry;
|
||||||
|
}
|
||||||
|
if (rw <= 0 or rh <= 0) return;
|
||||||
|
|
||||||
|
const start_row: usize = @intCast(ry);
|
||||||
|
const end_row: usize = start_row + @as(usize, @intCast(rh));
|
||||||
|
const start_col: usize = @intCast(rx);
|
||||||
|
const cols: usize = @intCast(rw);
|
||||||
|
|
||||||
|
var row = start_row;
|
||||||
|
while (row < end_row) : (row += 1) {
|
||||||
|
const offset = row * WIDTH + start_col;
|
||||||
|
@memset(fb_memory[offset .. offset + cols], color);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,482 @@
|
||||||
|
// MARKUS MAIWALD (ARCHITECT) | VOXIS FORGE (AI)
|
||||||
|
// Phase 3.5c: The Retina - VirtIO-GPU Driver
|
||||||
|
// Blasts pixels from RAM canvas to host display.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const fb = @import("framebuffer.zig");
|
||||||
|
const uart = @import("uart.zig");
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// VirtIO-GPU Constants
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
const VIRTIO_GPU_CMD_GET_DISPLAY_INFO: u32 = 0x0100;
|
||||||
|
const VIRTIO_GPU_CMD_RESOURCE_CREATE_2D: u32 = 0x0101;
|
||||||
|
const VIRTIO_GPU_CMD_RESOURCE_UNREF: u32 = 0x0102;
|
||||||
|
const VIRTIO_GPU_CMD_SET_SCANOUT: u32 = 0x0103;
|
||||||
|
const VIRTIO_GPU_CMD_RESOURCE_FLUSH: u32 = 0x0104;
|
||||||
|
const VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D: u32 = 0x0105;
|
||||||
|
const VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING: u32 = 0x0106;
|
||||||
|
|
||||||
|
const VIRTIO_GPU_RESP_OK_NODATA: u32 = 0x1100;
|
||||||
|
const VIRTIO_GPU_RESP_OK_DISPLAY_INFO: u32 = 0x1101;
|
||||||
|
|
||||||
|
const VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM: u32 = 1;
|
||||||
|
|
||||||
|
const RESOURCE_ID: u32 = 1;
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// VirtIO-GPU Structures (packed for wire format)
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
const VirtioGpuCtrlHdr = extern struct {
|
||||||
|
type: u32,
|
||||||
|
flags: u32,
|
||||||
|
fence_id: u64,
|
||||||
|
ctx_id: u32,
|
||||||
|
padding: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VirtioGpuResourceCreate2D = extern struct {
|
||||||
|
hdr: VirtioGpuCtrlHdr,
|
||||||
|
resource_id: u32,
|
||||||
|
format: u32,
|
||||||
|
width: u32,
|
||||||
|
height: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VirtioGpuMemEntry = extern struct {
|
||||||
|
addr: u64,
|
||||||
|
length: u32,
|
||||||
|
padding: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VirtioGpuResourceAttachBacking = extern struct {
|
||||||
|
hdr: VirtioGpuCtrlHdr,
|
||||||
|
resource_id: u32,
|
||||||
|
nr_entries: u32,
|
||||||
|
// Followed by VirtioGpuMemEntry array
|
||||||
|
};
|
||||||
|
|
||||||
|
const VirtioGpuSetScanout = extern struct {
|
||||||
|
hdr: VirtioGpuCtrlHdr,
|
||||||
|
r_x: u32,
|
||||||
|
r_y: u32,
|
||||||
|
r_width: u32,
|
||||||
|
r_height: u32,
|
||||||
|
scanout_id: u32,
|
||||||
|
resource_id: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VirtioGpuTransferToHost2D = extern struct {
|
||||||
|
hdr: VirtioGpuCtrlHdr,
|
||||||
|
r_x: u32,
|
||||||
|
r_y: u32,
|
||||||
|
r_width: u32,
|
||||||
|
r_height: u32,
|
||||||
|
offset: u64,
|
||||||
|
resource_id: u32,
|
||||||
|
padding: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VirtioGpuResourceFlush = extern struct {
|
||||||
|
hdr: VirtioGpuCtrlHdr,
|
||||||
|
r_x: u32,
|
||||||
|
r_y: u32,
|
||||||
|
r_width: u32,
|
||||||
|
r_height: u32,
|
||||||
|
resource_id: u32,
|
||||||
|
padding: u32,
|
||||||
|
};
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Driver State
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
var initialized: bool = false;
|
||||||
|
|
||||||
|
// MMIO base for VirtIO-GPU (discovered at runtime or hardcoded for virt machine)
|
||||||
|
// On QEMU virt, virtio-gpu-device is at 0x10008000 (device 1 after net)
|
||||||
|
// Actually it depends on dtb, for now we'll use a probe or hardcode.
|
||||||
|
var mmio_base: usize = 0;
|
||||||
|
|
||||||
|
// Control queue (Queue 0)
|
||||||
|
const QUEUE_SIZE = 16;
|
||||||
|
|
||||||
|
const VirtioDesc = extern struct {
|
||||||
|
addr: u64,
|
||||||
|
len: u32,
|
||||||
|
flags: u16,
|
||||||
|
next: u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VirtioAvail = extern struct {
|
||||||
|
flags: u16,
|
||||||
|
idx: u16,
|
||||||
|
ring: [QUEUE_SIZE]u16,
|
||||||
|
};
|
||||||
|
|
||||||
|
const VirtioUsed = extern struct {
|
||||||
|
flags: u16,
|
||||||
|
idx: u16,
|
||||||
|
ring: [QUEUE_SIZE]extern struct { id: u32, len: u32 },
|
||||||
|
};
|
||||||
|
|
||||||
|
var desc_table: [QUEUE_SIZE]VirtioDesc = undefined;
|
||||||
|
var avail_ring: VirtioAvail = undefined;
|
||||||
|
var used_ring: VirtioUsed = undefined;
|
||||||
|
|
||||||
|
var desc_idx: u16 = 0;
|
||||||
|
var last_used_idx: u16 = 0;
|
||||||
|
|
||||||
|
// Command/Response buffers (static)
|
||||||
|
var cmd_buf: [512]u8 align(4096) = undefined;
|
||||||
|
var resp_buf: [256]u8 align(4096) = undefined;
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// MMIO Helpers
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
fn mmio_read(offset: usize) u32 {
|
||||||
|
const ptr: *volatile u32 = @ptrFromInt(mmio_base + offset);
|
||||||
|
return ptr.*;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn mmio_write(offset: usize, val: u32) void {
|
||||||
|
const ptr: *volatile u32 = @ptrFromInt(mmio_base + offset);
|
||||||
|
ptr.* = val;
|
||||||
|
}
|
||||||
|
|
||||||
|
// VirtIO MMIO offsets
|
||||||
|
const VIRTIO_MMIO_MAGIC_VALUE = 0x000;
|
||||||
|
const VIRTIO_MMIO_VERSION = 0x004;
|
||||||
|
const VIRTIO_MMIO_DEVICE_ID = 0x008;
|
||||||
|
const VIRTIO_MMIO_VENDOR_ID = 0x00c;
|
||||||
|
const VIRTIO_MMIO_DEVICE_FEATURES = 0x010;
|
||||||
|
const VIRTIO_MMIO_DRIVER_FEATURES = 0x020;
|
||||||
|
const VIRTIO_MMIO_QUEUE_SEL = 0x030;
|
||||||
|
const VIRTIO_MMIO_QUEUE_NUM_MAX = 0x034;
|
||||||
|
const VIRTIO_MMIO_QUEUE_NUM = 0x038;
|
||||||
|
const VIRTIO_MMIO_QUEUE_READY = 0x044;
|
||||||
|
const VIRTIO_MMIO_QUEUE_NOTIFY = 0x050;
|
||||||
|
const VIRTIO_MMIO_INTERRUPT_STATUS = 0x060;
|
||||||
|
const VIRTIO_MMIO_INTERRUPT_ACK = 0x064;
|
||||||
|
const VIRTIO_MMIO_STATUS = 0x070;
|
||||||
|
const VIRTIO_MMIO_QUEUE_DESC_LOW = 0x080;
|
||||||
|
const VIRTIO_MMIO_QUEUE_DESC_HIGH = 0x084;
|
||||||
|
const VIRTIO_MMIO_QUEUE_AVAIL_LOW = 0x090;
|
||||||
|
const VIRTIO_MMIO_QUEUE_AVAIL_HIGH = 0x094;
|
||||||
|
const VIRTIO_MMIO_QUEUE_USED_LOW = 0x0a0;
|
||||||
|
const VIRTIO_MMIO_QUEUE_USED_HIGH = 0x0a4;
|
||||||
|
|
||||||
|
const VIRTIO_STATUS_ACKNOWLEDGE = 1;
|
||||||
|
const VIRTIO_STATUS_DRIVER = 2;
|
||||||
|
const VIRTIO_STATUS_DRIVER_OK = 4;
|
||||||
|
const VIRTIO_STATUS_FEATURES_OK = 8;
|
||||||
|
|
||||||
|
const VRING_DESC_F_NEXT: u16 = 1;
|
||||||
|
const VRING_DESC_F_WRITE: u16 = 2;
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Queue Operations
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
fn queue_init() void {
|
||||||
|
// Select queue 0 (controlq)
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_SEL, 0);
|
||||||
|
|
||||||
|
const max = mmio_read(VIRTIO_MMIO_QUEUE_NUM_MAX);
|
||||||
|
if (max == 0) {
|
||||||
|
uart.print("[GPU] Queue 0 not available!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_NUM, QUEUE_SIZE);
|
||||||
|
|
||||||
|
// Legacy (v1) uses a single contiguous page for desc+avail+used
|
||||||
|
// and QUEUE_PFN register instead of separate addresses.
|
||||||
|
// For simplicity, we use a static aligned buffer.
|
||||||
|
const version = mmio_read(VIRTIO_MMIO_VERSION);
|
||||||
|
|
||||||
|
if (version == 1) {
|
||||||
|
// Legacy VirtIO MMIO v1
|
||||||
|
// Queue address = page frame number (page-aligned address / page_size)
|
||||||
|
// We need to provide a contiguous buffer for desc, avail, used.
|
||||||
|
// For now, use our static arrays but provide the desc address as PFN.
|
||||||
|
const page_addr = @intFromPtr(&desc_table) & 0xFFFFF000; // Page aligned
|
||||||
|
const pfn = page_addr / 4096; // Page frame number
|
||||||
|
|
||||||
|
// QUEUE_ALIGN register at 0x3c (page size, usually 4096)
|
||||||
|
// QUEUE_PFN register at 0x40
|
||||||
|
const VIRTIO_MMIO_QUEUE_ALIGN: usize = 0x03c;
|
||||||
|
const VIRTIO_MMIO_QUEUE_PFN: usize = 0x040;
|
||||||
|
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_ALIGN, 4096);
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_PFN, @truncate(pfn));
|
||||||
|
|
||||||
|
uart.print("[GPU] Legacy queue at PFN 0x");
|
||||||
|
uart.print_hex(pfn);
|
||||||
|
uart.print("\n");
|
||||||
|
} else {
|
||||||
|
// Modern VirtIO MMIO v2
|
||||||
|
const desc_addr = @intFromPtr(&desc_table);
|
||||||
|
const avail_addr = @intFromPtr(&avail_ring);
|
||||||
|
const used_addr = @intFromPtr(&used_ring);
|
||||||
|
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_DESC_LOW, @truncate(desc_addr));
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_DESC_HIGH, @truncate(desc_addr >> 32));
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_AVAIL_LOW, @truncate(avail_addr));
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_AVAIL_HIGH, @truncate(avail_addr >> 32));
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_USED_LOW, @truncate(used_addr));
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_USED_HIGH, @truncate(used_addr >> 32));
|
||||||
|
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_READY, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
avail_ring.idx = 0;
|
||||||
|
last_used_idx = 0;
|
||||||
|
desc_idx = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_command(cmd_ptr: [*]const u8, cmd_len: usize) void {
|
||||||
|
// Descriptor 0: Command (device read)
|
||||||
|
desc_table[0] = .{
|
||||||
|
.addr = @intFromPtr(cmd_ptr),
|
||||||
|
.len = @intCast(cmd_len),
|
||||||
|
.flags = VRING_DESC_F_NEXT,
|
||||||
|
.next = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Descriptor 1: Response (device write)
|
||||||
|
desc_table[1] = .{
|
||||||
|
.addr = @intFromPtr(&resp_buf),
|
||||||
|
.len = @sizeOf(@TypeOf(resp_buf)),
|
||||||
|
.flags = VRING_DESC_F_WRITE,
|
||||||
|
.next = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add to available ring
|
||||||
|
avail_ring.ring[avail_ring.idx % QUEUE_SIZE] = 0;
|
||||||
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
|
avail_ring.idx +%= 1;
|
||||||
|
asm volatile ("fence" ::: .{ .memory = true });
|
||||||
|
|
||||||
|
// Notify device
|
||||||
|
mmio_write(VIRTIO_MMIO_QUEUE_NOTIFY, 0);
|
||||||
|
|
||||||
|
// Wait for response (polling)
|
||||||
|
while (last_used_idx == used_ring.idx) {
|
||||||
|
asm volatile ("" ::: .{ .memory = true });
|
||||||
|
}
|
||||||
|
last_used_idx = used_ring.idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// GPU Commands
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
fn cmd_resource_create_2d() void {
|
||||||
|
const cmd = @as(*VirtioGpuResourceCreate2D, @ptrCast(@alignCast(&cmd_buf)));
|
||||||
|
cmd.* = .{
|
||||||
|
.hdr = .{
|
||||||
|
.type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D,
|
||||||
|
.flags = 0,
|
||||||
|
.fence_id = 0,
|
||||||
|
.ctx_id = 0,
|
||||||
|
.padding = 0,
|
||||||
|
},
|
||||||
|
.resource_id = RESOURCE_ID,
|
||||||
|
.format = VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM,
|
||||||
|
.width = fb.WIDTH,
|
||||||
|
.height = fb.HEIGHT,
|
||||||
|
};
|
||||||
|
send_command(&cmd_buf, @sizeOf(VirtioGpuResourceCreate2D));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmd_attach_backing() void {
|
||||||
|
// We need to send the header + 1 mem entry
|
||||||
|
const AttachCmd = extern struct {
|
||||||
|
hdr: VirtioGpuCtrlHdr,
|
||||||
|
resource_id: u32,
|
||||||
|
nr_entries: u32,
|
||||||
|
entry: VirtioGpuMemEntry,
|
||||||
|
};
|
||||||
|
|
||||||
|
const cmd = @as(*AttachCmd, @ptrCast(@alignCast(&cmd_buf)));
|
||||||
|
cmd.* = .{
|
||||||
|
.hdr = .{
|
||||||
|
.type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING,
|
||||||
|
.flags = 0,
|
||||||
|
.fence_id = 0,
|
||||||
|
.ctx_id = 0,
|
||||||
|
.padding = 0,
|
||||||
|
},
|
||||||
|
.resource_id = RESOURCE_ID,
|
||||||
|
.nr_entries = 1,
|
||||||
|
.entry = .{
|
||||||
|
.addr = fb.get_buffer_phys(),
|
||||||
|
.length = @intCast(fb.get_size()),
|
||||||
|
.padding = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
send_command(&cmd_buf, @sizeOf(AttachCmd));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmd_set_scanout() void {
|
||||||
|
const cmd = @as(*VirtioGpuSetScanout, @ptrCast(@alignCast(&cmd_buf)));
|
||||||
|
cmd.* = .{
|
||||||
|
.hdr = .{
|
||||||
|
.type = VIRTIO_GPU_CMD_SET_SCANOUT,
|
||||||
|
.flags = 0,
|
||||||
|
.fence_id = 0,
|
||||||
|
.ctx_id = 0,
|
||||||
|
.padding = 0,
|
||||||
|
},
|
||||||
|
.r_x = 0,
|
||||||
|
.r_y = 0,
|
||||||
|
.r_width = fb.WIDTH,
|
||||||
|
.r_height = fb.HEIGHT,
|
||||||
|
.scanout_id = 0,
|
||||||
|
.resource_id = RESOURCE_ID,
|
||||||
|
};
|
||||||
|
send_command(&cmd_buf, @sizeOf(VirtioGpuSetScanout));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmd_transfer_2d() void {
|
||||||
|
const cmd = @as(*VirtioGpuTransferToHost2D, @ptrCast(@alignCast(&cmd_buf)));
|
||||||
|
cmd.* = .{
|
||||||
|
.hdr = .{
|
||||||
|
.type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D,
|
||||||
|
.flags = 0,
|
||||||
|
.fence_id = 0,
|
||||||
|
.ctx_id = 0,
|
||||||
|
.padding = 0,
|
||||||
|
},
|
||||||
|
.r_x = 0,
|
||||||
|
.r_y = 0,
|
||||||
|
.r_width = fb.WIDTH,
|
||||||
|
.r_height = fb.HEIGHT,
|
||||||
|
.offset = 0,
|
||||||
|
.resource_id = RESOURCE_ID,
|
||||||
|
.padding = 0,
|
||||||
|
};
|
||||||
|
send_command(&cmd_buf, @sizeOf(VirtioGpuTransferToHost2D));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmd_resource_flush() void {
|
||||||
|
const cmd = @as(*VirtioGpuResourceFlush, @ptrCast(@alignCast(&cmd_buf)));
|
||||||
|
cmd.* = .{
|
||||||
|
.hdr = .{
|
||||||
|
.type = VIRTIO_GPU_CMD_RESOURCE_FLUSH,
|
||||||
|
.flags = 0,
|
||||||
|
.fence_id = 0,
|
||||||
|
.ctx_id = 0,
|
||||||
|
.padding = 0,
|
||||||
|
},
|
||||||
|
.r_x = 0,
|
||||||
|
.r_y = 0,
|
||||||
|
.r_width = fb.WIDTH,
|
||||||
|
.r_height = fb.HEIGHT,
|
||||||
|
.resource_id = RESOURCE_ID,
|
||||||
|
.padding = 0,
|
||||||
|
};
|
||||||
|
send_command(&cmd_buf, @sizeOf(VirtioGpuResourceFlush));
|
||||||
|
}
|
||||||
|
|
||||||
|
// =========================================================
|
||||||
|
// Public API
|
||||||
|
// =========================================================
|
||||||
|
|
||||||
|
pub fn probe(base: usize) bool {
|
||||||
|
mmio_base = base;
|
||||||
|
|
||||||
|
const magic = mmio_read(VIRTIO_MMIO_MAGIC_VALUE);
|
||||||
|
const version = mmio_read(VIRTIO_MMIO_VERSION);
|
||||||
|
const device_id = mmio_read(VIRTIO_MMIO_DEVICE_ID);
|
||||||
|
|
||||||
|
// Debug: Print what we found
|
||||||
|
uart.print("[GPU Probe] 0x");
|
||||||
|
uart.print_hex(base);
|
||||||
|
uart.print(" Magic=0x");
|
||||||
|
uart.print_hex(magic);
|
||||||
|
uart.print(" Ver=");
|
||||||
|
uart.print_hex(version);
|
||||||
|
uart.print(" DevID=");
|
||||||
|
uart.print_hex(device_id);
|
||||||
|
uart.print("\n");
|
||||||
|
|
||||||
|
// Magic = "virt" (0x74726976), Version = 1 or 2, Device ID = 16 (GPU)
|
||||||
|
if (magic != 0x74726976) return false;
|
||||||
|
if (version != 1 and version != 2) return false;
|
||||||
|
if (device_id != 16) return false;
|
||||||
|
|
||||||
|
uart.print("[GPU] VirtIO-GPU found at 0x");
|
||||||
|
uart.print_hex(base);
|
||||||
|
uart.print("\n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn init(base: usize) void {
|
||||||
|
if (!probe(base)) {
|
||||||
|
uart.print("[GPU] No VirtIO-GPU device found.\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reset
|
||||||
|
mmio_write(VIRTIO_MMIO_STATUS, 0);
|
||||||
|
|
||||||
|
// Acknowledge
|
||||||
|
mmio_write(VIRTIO_MMIO_STATUS, VIRTIO_STATUS_ACKNOWLEDGE);
|
||||||
|
|
||||||
|
// Driver
|
||||||
|
mmio_write(VIRTIO_MMIO_STATUS, VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER);
|
||||||
|
|
||||||
|
// Features (we don't need any special features for basic 2D)
|
||||||
|
mmio_write(VIRTIO_MMIO_DRIVER_FEATURES, 0);
|
||||||
|
|
||||||
|
// Features OK
|
||||||
|
mmio_write(VIRTIO_MMIO_STATUS, VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER | VIRTIO_STATUS_FEATURES_OK);
|
||||||
|
|
||||||
|
// Setup queue
|
||||||
|
queue_init();
|
||||||
|
|
||||||
|
// Driver OK
|
||||||
|
mmio_write(VIRTIO_MMIO_STATUS, VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER | VIRTIO_STATUS_FEATURES_OK | VIRTIO_STATUS_DRIVER_OK);
|
||||||
|
|
||||||
|
// Initialize framebuffer
|
||||||
|
fb.init();
|
||||||
|
|
||||||
|
// GPU Setup sequence
|
||||||
|
uart.print("[GPU] Creating 2D Resource...\n");
|
||||||
|
cmd_resource_create_2d();
|
||||||
|
|
||||||
|
uart.print("[GPU] Attaching Backing...\n");
|
||||||
|
cmd_attach_backing();
|
||||||
|
|
||||||
|
uart.print("[GPU] Setting Scanout...\n");
|
||||||
|
cmd_set_scanout();
|
||||||
|
|
||||||
|
initialized = true;
|
||||||
|
uart.print("[GPU] VirtIO-GPU initialized. Resolution: 800x600\n");
|
||||||
|
|
||||||
|
// Draw initial test pattern
|
||||||
|
fb.fill_rect(100, 100, 200, 50, 0xFF00FF00); // Neon green bar
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn flush() void {
|
||||||
|
if (!initialized) return;
|
||||||
|
|
||||||
|
cmd_transfer_2d();
|
||||||
|
cmd_resource_flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for Nim
|
||||||
|
export fn virtio_gpu_init(base: usize) void {
|
||||||
|
init(base);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn virtio_gpu_flush() void {
|
||||||
|
flush();
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const fb = @import("framebuffer.zig");
|
const fb = @import("framebuffer.zig");
|
||||||
|
|
||||||
// Import the C Logic directly
|
// GPU Driver (extern)
|
||||||
|
extern fn virtio_gpu_flush() void;
|
||||||
pub const c = @cImport({
|
pub const c = @cImport({
|
||||||
@cInclude("microui.h");
|
@cInclude("microui.h");
|
||||||
});
|
});
|
||||||
|
|
@ -68,6 +69,9 @@ pub fn render() void {
|
||||||
else => {},
|
else => {},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PUSH TO HOST (The Retina)
|
||||||
|
virtio_gpu_flush();
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- HELPER: Draw Rect ---
|
// --- HELPER: Draw Rect ---
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue