redox-drm virtio: implement feature negotiation + display detection

Replaced hardcoded 1280x720 with real VirtIO GPU config space
display detection:

- Added VirtIO MMIO transport register constants (spec v1.2 §4.2.2)
- Added VirtIO GPU config space offsets (scanout dimensions)
- Implemented feature negotiation:
  ACKNOWLEDGE → DRIVER → read device features →
  acknowledge VIRGL + EDID → FEATURES_OK → DRIVER_OK
- Read display dimensions from GPU config space at BAR+0x100:
  num_scanouts → scanout[0] enabled/width/height
- Fallback to 1280x720 if scanout is disabled or absent
- Renamed _mmio→mmio (was unused underscore-prefixed field)

Previously: hardcoded 1280x720, no virtio negotiation,
_mmio field stored but never accessed. Now: reads real
display dimensions from the virtio-gpu device config.
This commit is contained in:
2026-07-09 15:53:32 +03:00
parent cfb6d7fc8a
commit 9836626793
@@ -13,9 +13,39 @@ use crate::kms::connector::{synthetic_edid, Connector};
use crate::kms::crtc::Crtc;
use crate::kms::{ConnectorInfo, ConnectorStatus, ConnectorType, ModeInfo};
// VirtIO MMIO transport register offsets (VirtIO spec v1.2 §4.2.2).
const VIRTIO_MMIO_MAGIC: usize = 0x000;
const VIRTIO_MMIO_VERSION: usize = 0x004;
const VIRTIO_MMIO_DEVICE_ID: usize = 0x008;
const VIRTIO_MMIO_DEVICE_FEATURES: usize = 0x010;
const VIRTIO_MMIO_DEVICE_FEATURES_SEL: usize = 0x014;
const VIRTIO_MMIO_DRIVER_FEATURES: usize = 0x020;
const VIRTIO_MMIO_DRIVER_FEATURES_SEL: usize = 0x024;
const VIRTIO_MMIO_GUEST_PAGE_SIZE: usize = 0x028;
const VIRTIO_MMIO_STATUS: usize = 0x070;
const VIRTIO_MMIO_CONFIG_GENERATION: usize = 0x0FC;
// VirtIO GPU config space offsets (after virtio MMIO transport).
// The GPU config region starts at offset 0x100 from the BAR base.
const VIRTIO_GPU_CONFIG_OFFSET: usize = 0x100;
const VIRTIO_GPU_NUM_SCANOUTS: usize = 0x00;
const VIRTIO_GPU_SCANOUT_WIDTH: usize = 0x04;
const VIRTIO_GPU_SCANOUT_HEIGHT: usize = 0x08;
const VIRTIO_GPU_SCANOUT_ENABLED: usize = 0x0C;
// VirtIO device status bits (§2.1).
const VIRTIO_STATUS_ACKNOWLEDGE: u32 = 1;
const VIRTIO_STATUS_DRIVER: u32 = 2;
const VIRTIO_STATUS_DRIVER_OK: u32 = 4;
const VIRTIO_STATUS_FEATURES_OK: u32 = 8;
// VirtIO GPU feature bits.
const VIRTIO_GPU_F_VIRGL: u32 = 1 << 0;
const VIRTIO_GPU_F_EDID: u32 = 1 << 1;
pub struct VirtioDriver {
info: PciDeviceInfo,
_mmio: MmioRegion,
mmio: MmioRegion,
irq_handle: Mutex<Option<InterruptHandle>>,
width: u32,
height: u32,
@@ -53,7 +83,7 @@ impl VirtioDriver {
}
let fb_bar = find_fb_bar(&info)?;
let _mmio = map_bar(&fb_bar, "VirtIO FB BAR")?;
let mmio = map_bar(&fb_bar, "VirtIO FB BAR")?;
info!(
"redox-drm: VirtIO GPU at {}: {} MiB BAR at {:#x}",
@@ -62,12 +92,49 @@ impl VirtioDriver {
fb_bar.addr,
);
// Negotiate VirtIO features: ACKNOWLEDGE → DRIVER → FEATURES_OK → DRIVER_OK
mmio.write32(VIRTIO_MMIO_STATUS, VIRTIO_STATUS_ACKNOWLEDGE);
mmio.write32(VIRTIO_MMIO_STATUS, VIRTIO_STATUS_DRIVER);
// Read device features and acknowledge Virgl + EDID support.
mmio.write32(VIRTIO_MMIO_DEVICE_FEATURES_SEL, 0);
let dev_features = mmio.read32(VIRTIO_MMIO_DEVICE_FEATURES);
let driver_features = dev_features & (VIRTIO_GPU_F_VIRGL | VIRTIO_GPU_F_EDID);
mmio.write32(VIRTIO_MMIO_DRIVER_FEATURES_SEL, 0);
mmio.write32(VIRTIO_MMIO_DRIVER_FEATURES, driver_features);
mmio.write32(VIRTIO_MMIO_STATUS, VIRTIO_STATUS_FEATURES_OK);
// Read the device status back to confirm negotiation.
let status = mmio.read32(VIRTIO_MMIO_STATUS);
if status & VIRTIO_STATUS_FEATURES_OK == 0 {
warn!("redox-drm: VirtIO GPU feature negotiation failed (status={:#x})", status);
}
mmio.write32(VIRTIO_MMIO_STATUS, VIRTIO_STATUS_DRIVER_OK);
// Read display dimensions from the VirtIO GPU config space.
let gpu_config = VIRTIO_GPU_CONFIG_OFFSET;
let num_scanouts = mmio.read32(gpu_config + VIRTIO_GPU_NUM_SCANOUTS);
let (width, height) = if num_scanouts > 0 {
let enabled = mmio.read32(gpu_config + VIRTIO_GPU_SCANOUT_ENABLED);
if enabled != 0 {
let w = mmio.read32(gpu_config + VIRTIO_GPU_SCANOUT_WIDTH);
let h = mmio.read32(gpu_config + VIRTIO_GPU_SCANOUT_HEIGHT);
info!("redox-drm: VirtIO GPU scanout 0: {}x{} ({} scanouts, enabled={})",
w, h, num_scanouts, enabled);
(w.max(640), h.max(480))
} else {
(1280, 720)
}
} else {
(1280, 720)
};
Ok(Self {
info,
_mmio,
mmio,
irq_handle: Mutex::new(None),
width: 1280,
height: 720,
width,
height,
gem: Mutex::new(GemManager::new()),
connectors: Mutex::new(Vec::new()),
crtcs: Mutex::new(Vec::new()),