reorg: extract library/device/pci — the claimed-function view
New pci logic module (library/device/pci/pci.zig): a device driver's view of the one PCI function it has claimed — Function.map (config space = resource 0), vendorId/deviceId/command/status, enableMemoryAndBusMaster, mapBar (BAR decode + resource correlation + mmio_map, cached), and a capabilities() iterator. The generic PCI mechanics every leaf PCI driver used to re-derive inline. The config-space layout it needs — header offsets, the command MEM|bus-master bits, the status capabilities-list bit, the capability-pointer mask, and the BAR bit fields — is named in the pci-class data module (a "Configuration-space layout" section), so the bus enumerator can share the same constants later. virtio-gpu is the first consumer: its inline mapBar + walkCapabilities + config header reads are gone, replaced by pci.Function; only the virtio-specific cfg_type dispatch (and the virtio common-config cfgRead/cfgWrite, which are NOT PCI config space) stay in the driver. The generic display driver will use the same module. pci-bus's enumerator (arbitrary-function probing) is untouched. zig build + test green; virtio-gpu, display-native, display-reattach, pci-scan pass.
This commit is contained in:
@@ -436,6 +436,19 @@ pub fn build(b: *std.Build) void {
|
|||||||
.root_source_file = b.path("library/mmio/mmio.zig"),
|
.root_source_file = b.path("library/mmio/mmio.zig"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// A device driver's view of its claimed PCI function: config-space header fields, BAR
|
||||||
|
// decode + map, and the capability walk (library/device/pci/pci.zig). The generic PCI
|
||||||
|
// mechanics every leaf PCI driver used to re-derive inline. Imports runtime (device
|
||||||
|
// access) + mmio + the pci-class data module (config-space layout constants).
|
||||||
|
const pci_module = b.addModule("pci", .{
|
||||||
|
.root_source_file = b.path("library/device/pci/pci.zig"),
|
||||||
|
.imports = &.{
|
||||||
|
.{ .name = "runtime", .module = runtime_module },
|
||||||
|
.{ .name = "mmio", .module = mmio_module },
|
||||||
|
.{ .name = "pci-class", .module = pci_class_module },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
// Keyboard layouts compiled from the X11 xkeyboard-config database into native Zig
|
// Keyboard layouts compiled from the X11 xkeyboard-config database into native Zig
|
||||||
// (keycode + modifiers -> keysym/character). The `layouts` tables are generated by
|
// (keycode + modifiers -> keysym/character). The `layouts` tables are generated by
|
||||||
// tools/make-xkeyboard-config.py; `xkeyboard-config` is the hand-written API over them.
|
// tools/make-xkeyboard-config.py; `xkeyboard-config` is the hand-written API over them.
|
||||||
@@ -550,6 +563,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
const display_exe = addThreadedUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "display", "system/services/display/display.zig");
|
const display_exe = addThreadedUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "display", "system/services/display/display.zig");
|
||||||
const display_demo_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "display-demo", "system/services/display-demo/display-demo.zig");
|
const display_demo_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "display-demo", "system/services/display-demo/display-demo.zig");
|
||||||
const virtio_gpu_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "virtio-gpu", "system/drivers/virtio-gpu/virtio-gpu.zig");
|
const virtio_gpu_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "virtio-gpu", "system/drivers/virtio-gpu/virtio-gpu.zig");
|
||||||
|
programModule(virtio_gpu_exe).addImport("pci", pci_module); // library/device/pci — the claimed-function view
|
||||||
const shared_memory_server_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "shared-memory-server", "system/services/shared-memory-server/shared-memory-server.zig");
|
const shared_memory_server_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "shared-memory-server", "system/services/shared-memory-server/shared-memory-server.zig");
|
||||||
const shared_memory_client_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "shared-memory-client", "system/services/shared-memory-client/shared-memory-client.zig");
|
const shared_memory_client_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "shared-memory-client", "system/services/shared-memory-client/shared-memory-client.zig");
|
||||||
const fat_test_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "fat-test", "system/services/fat/fat-test.zig");
|
const fat_test_exe = addUserBinary(b, kernel_target, runtime_module, mmio_module, xkeyboard_config_module, acpi_ids_module, "fat-test", "system/services/fat/fat-test.zig");
|
||||||
|
|||||||
@@ -41,6 +41,35 @@ pub const ClassCode = struct {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// --- Configuration-space layout ---------------------------------------------------------
|
||||||
|
// The offsets and bit layouts of the PCI configuration header (PCI spec; see
|
||||||
|
// https://wiki.osdev.org/PCI). Pure data — named here so both a device driver's view of
|
||||||
|
// its own claimed function (library/device/pci/pci.zig) and the bus enumerator name the
|
||||||
|
// same bytes instead of scattering bare 0x04/0x34/0xFFFF_FFF0 magic across the tree.
|
||||||
|
|
||||||
|
/// Header field offsets (byte offsets into the 256-byte configuration space).
|
||||||
|
pub const config_vendor_id: usize = 0x00;
|
||||||
|
pub const config_device_id: usize = 0x02;
|
||||||
|
pub const config_command: usize = 0x04;
|
||||||
|
pub const config_status: usize = 0x06;
|
||||||
|
pub const config_capabilities_pointer: usize = 0x34;
|
||||||
|
pub const config_bar0: usize = 0x10; // BAR0; BAR n is at config_bar0 + n*4
|
||||||
|
|
||||||
|
/// Command register: Memory-Space enable (bit 1) | Bus-Master enable (bit 2).
|
||||||
|
pub const command_memory_and_bus_master: u16 = 0x06;
|
||||||
|
/// Status register bit 4: a capability list is present at config_capabilities_pointer.
|
||||||
|
pub const status_capabilities_list: u16 = 0x10;
|
||||||
|
/// Capability pointers are dword-aligned; the low two bits are reserved.
|
||||||
|
pub const capability_pointer_mask: u8 = 0xFC;
|
||||||
|
|
||||||
|
/// BAR bit layout: bit 0 selects I/O (1) vs memory (0) space; for a memory BAR, bits 2:1
|
||||||
|
/// give the type (00 = 32-bit, 10 = 64-bit spanning the next BAR), and the base address is
|
||||||
|
/// the dword with the low 4 flag bits masked off.
|
||||||
|
pub const bar_io_space: u32 = 0x1;
|
||||||
|
pub const bar_type_mask: u32 = 0x6;
|
||||||
|
pub const bar_type_64bit: u32 = 0x4;
|
||||||
|
pub const bar_memory_base_mask: u32 = 0xFFFF_FFF0;
|
||||||
|
|
||||||
/// Base class (config byte 0x0B). Non-exhaustive: an unlisted code is a real but
|
/// Base class (config byte 0x0B). Non-exhaustive: an unlisted code is a real but
|
||||||
/// unnamed class, decoded as "Unknown" rather than rejected.
|
/// unnamed class, decoded as "Unknown" rather than rejected.
|
||||||
pub const BaseClass = enum(u8) {
|
pub const BaseClass = enum(u8) {
|
||||||
|
|||||||
@@ -0,0 +1,109 @@
|
|||||||
|
//! library/device/pci/pci.zig — a device driver's view of the ONE PCI function it has
|
||||||
|
//! claimed. Config space is mapped as resource 0; this gives header-field accessors, BAR
|
||||||
|
//! decode + map, and a capability-list iterator, so a driver never re-derives the
|
||||||
|
//! config-space layout by hand.
|
||||||
|
//!
|
||||||
|
//! This is the *device-owned* view: read my own function's live config, map my own BARs.
|
||||||
|
//! The bus enumerator's view — probing arbitrary, not-yet-claimed functions and sizing
|
||||||
|
//! their BARs — is a different mechanism and lives in the pci-bus driver. The pure
|
||||||
|
//! config-space layout both need (offsets, BAR bit fields) is named once in the `pci-class`
|
||||||
|
//! data module; this logic module adds the parts that need `mmio` + `runtime.device`.
|
||||||
|
|
||||||
|
const std = @import("std");
|
||||||
|
const runtime = @import("runtime");
|
||||||
|
const mmio = @import("mmio");
|
||||||
|
const pci_class = @import("pci-class");
|
||||||
|
const device = runtime.device;
|
||||||
|
|
||||||
|
/// A claimed PCI function whose configuration space is mapped (resource 0). `descriptor`
|
||||||
|
/// must outlive the Function — the driver's `device.enumerate` buffer does, for the whole
|
||||||
|
/// bring-up. Header reads and the capability walk hit live config space; `mapBar` caches.
|
||||||
|
pub const Function = struct {
|
||||||
|
device_id: u64,
|
||||||
|
descriptor: *const device.DeviceDescriptor,
|
||||||
|
config: usize, // virtual base of mapped resource 0
|
||||||
|
bar_virtual: [6]usize = .{ 0, 0, 0, 0, 0, 0 }, // per-BAR mmio_map cache
|
||||||
|
|
||||||
|
/// Map config space (resource 0) of the already-claimed `device_id`. null if the map
|
||||||
|
/// fails (not claimed, or no config resource).
|
||||||
|
pub fn map(device_id: u64, descriptor: *const device.DeviceDescriptor) ?Function {
|
||||||
|
const base = device.mmioMap(device_id, 0) orelse return null;
|
||||||
|
return .{ .device_id = device_id, .descriptor = descriptor, .config = base };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn vendorId(self: *const Function) u16 {
|
||||||
|
return mmio.read(u16, self.config + pci_class.config_vendor_id);
|
||||||
|
}
|
||||||
|
pub fn deviceId(self: *const Function) u16 {
|
||||||
|
return mmio.read(u16, self.config + pci_class.config_device_id);
|
||||||
|
}
|
||||||
|
pub fn command(self: *const Function) u16 {
|
||||||
|
return mmio.read(u16, self.config + pci_class.config_command);
|
||||||
|
}
|
||||||
|
pub fn status(self: *const Function) u16 {
|
||||||
|
return mmio.read(u16, self.config + pci_class.config_status);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set Memory-Space + Bus-Master enable in the command register. Firmware often leaves
|
||||||
|
/// a secondary display's decode off; a bus-mastering device must enable both.
|
||||||
|
pub fn enableMemoryAndBusMaster(self: *const Function) void {
|
||||||
|
const at = self.config + pci_class.config_command;
|
||||||
|
mmio.write(u16, at, mmio.read(u16, at) | pci_class.command_memory_and_bus_master);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Decode BAR `bar` (0..5) and map it: read the BAR register, reject I/O-space BARs,
|
||||||
|
/// combine the high dword for a 64-bit BAR, mask the base, then correlate that physical
|
||||||
|
/// base with one of the descriptor's memory resources and `mmio_map` it — a BAR names a
|
||||||
|
/// *number*, while `mmio_map` takes a *resource index*, and gaps/config-space shift the
|
||||||
|
/// numbering. Cached per BAR. null if the BAR is I/O-space or is not a mapped resource.
|
||||||
|
pub fn mapBar(self: *Function, bar: u8) ?usize {
|
||||||
|
if (bar >= 6) return null;
|
||||||
|
if (self.bar_virtual[bar] != 0) return self.bar_virtual[bar];
|
||||||
|
|
||||||
|
const low = mmio.read(u32, self.config + pci_class.config_bar0 + @as(usize, bar) * 4);
|
||||||
|
if (low & pci_class.bar_io_space != 0) return null; // an I/O-space BAR
|
||||||
|
var base: u64 = low & pci_class.bar_memory_base_mask;
|
||||||
|
if ((low & pci_class.bar_type_mask) == pci_class.bar_type_64bit) { // 64-bit: high half is the next dword
|
||||||
|
const high = mmio.read(u32, self.config + pci_class.config_bar0 + (@as(usize, bar) + 1) * 4);
|
||||||
|
base |= @as(u64, high) << 32;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (self.descriptor.resources[0..@intCast(self.descriptor.resource_count)], 0..) |resource, index| {
|
||||||
|
if (resource.kind == @intFromEnum(device.ResourceKind.memory) and resource.start == base) {
|
||||||
|
const v = device.mmioMap(self.device_id, index) orelse return null;
|
||||||
|
self.bar_virtual[bar] = v;
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Iterate the capability list. Empty when the function advertises none.
|
||||||
|
pub fn capabilities(self: *const Function) CapabilityIterator {
|
||||||
|
const present = self.status() & pci_class.status_capabilities_list != 0;
|
||||||
|
const first = if (present)
|
||||||
|
mmio.read(u8, self.config + pci_class.config_capabilities_pointer) & pci_class.capability_pointer_mask
|
||||||
|
else
|
||||||
|
0;
|
||||||
|
return .{ .config = self.config, .cursor = first };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/// One capability header. `offset` is the ABSOLUTE virtual address of the header, so the
|
||||||
|
/// caller reads its body with `mmio.read(T, cap.offset + n)`.
|
||||||
|
pub const Capability = struct { id: u8, offset: usize };
|
||||||
|
|
||||||
|
pub const CapabilityIterator = struct {
|
||||||
|
config: usize,
|
||||||
|
cursor: u8,
|
||||||
|
guard: u32 = 0, // bounds a malformed/looping chain (48 = the 256-byte space in dwords)
|
||||||
|
|
||||||
|
pub fn next(self: *CapabilityIterator) ?Capability {
|
||||||
|
if (self.cursor == 0 or self.guard >= 48) return null;
|
||||||
|
self.guard += 1;
|
||||||
|
const at = self.config + self.cursor;
|
||||||
|
const id = mmio.read(u8, at + 0);
|
||||||
|
self.cursor = mmio.read(u8, at + 1) & pci_class.capability_pointer_mask;
|
||||||
|
return .{ .id = id, .offset = at };
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -16,6 +16,7 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const runtime = @import("runtime");
|
const runtime = @import("runtime");
|
||||||
const mmio = @import("mmio");
|
const mmio = @import("mmio");
|
||||||
|
const pci = @import("pci");
|
||||||
const device = runtime.device;
|
const device = runtime.device;
|
||||||
const dma = runtime.dma;
|
const dma = runtime.dma;
|
||||||
const shared_memory = runtime.shared_memory;
|
const shared_memory = runtime.shared_memory;
|
||||||
@@ -84,10 +85,6 @@ var notify_base: usize = 0;
|
|||||||
var notify_multiplier: u32 = 0;
|
var notify_multiplier: u32 = 0;
|
||||||
var notify_addr: usize = 0;
|
var notify_addr: usize = 0;
|
||||||
|
|
||||||
// Per-BAR mapping cache: several capabilities usually share one BAR, and mmio_map must not
|
|
||||||
// be asked to map the same resource twice.
|
|
||||||
var bar_virtual: [6]usize = .{ 0, 0, 0, 0, 0, 0 };
|
|
||||||
|
|
||||||
// DMA memory: the virtqueue rings and the command scratch.
|
// DMA memory: the virtqueue rings and the command scratch.
|
||||||
var ring: dma.Region = undefined;
|
var ring: dma.Region = undefined;
|
||||||
var command: dma.Region = undefined;
|
var command: dma.Region = undefined;
|
||||||
@@ -121,75 +118,6 @@ fn orStatus(bit: u8) void {
|
|||||||
cfgWrite(u8, "device_status", cfgRead(u8, "device_status") | bit);
|
cfgWrite(u8, "device_status", cfgRead(u8, "device_status") | bit);
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- PCI config-space capability walk (config space is resource 0) ---------------------
|
|
||||||
|
|
||||||
/// Map the BAR numbered `bar` (0..5) and return its virtual base, correlating the BAR's
|
|
||||||
/// physical address (read from config space) with one of our device resources — because a
|
|
||||||
/// virtio capability names a BAR *number*, while `mmio_map` takes a *resource index* (and
|
|
||||||
/// resource 0 is config space, so BAR resources are re-numbered and gaps skipped).
|
|
||||||
fn mapBar(config: usize, descriptor: *const device.DeviceDescriptor, bar: u8) ?usize {
|
|
||||||
if (bar >= 6) return null;
|
|
||||||
if (bar_virtual[bar] != 0) return bar_virtual[bar];
|
|
||||||
|
|
||||||
const low = mmio.read(u32, config + 0x10 + @as(usize, bar) * 4);
|
|
||||||
if (low & 0x1 != 0) return null; // an I/O-space BAR — virtio structures are in memory BARs
|
|
||||||
var base: u64 = low & 0xFFFF_FFF0;
|
|
||||||
if ((low & 0x6) == 0x4) { // 64-bit memory BAR: the high half is the next dword
|
|
||||||
const high = mmio.read(u32, config + 0x10 + (@as(usize, bar) + 1) * 4);
|
|
||||||
base |= @as(u64, high) << 32;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (descriptor.resources[0..@intCast(descriptor.resource_count)], 0..) |resource, index| {
|
|
||||||
if (resource.kind == @intFromEnum(device.ResourceKind.memory) and resource.start == base) {
|
|
||||||
const v = device.mmioMap(device_id, index) orelse return null;
|
|
||||||
bar_virtual[bar] = v;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
std.log.info("BAR {d} (physical 0x{x}) is not a mapped resource", .{ bar, base });
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Walk the PCI capability list from mapped config space, recording the common-config and
|
|
||||||
/// notify structures (the only two V3 needs). Returns false if either is missing.
|
|
||||||
fn walkCapabilities(config: usize, descriptor: *const device.DeviceDescriptor) bool {
|
|
||||||
if (mmio.read(u16, config + 0x06) & 0x10 == 0) { // Status bit 4: capabilities list present
|
|
||||||
std.log.info("device has no PCI capability list", .{});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
var cap: u8 = @as(u8, @truncate(mmio.read(u8, config + 0x34))) & 0xFC;
|
|
||||||
var guard: u32 = 0;
|
|
||||||
while (cap != 0 and guard < 48) : (guard += 1) {
|
|
||||||
const at = config + cap;
|
|
||||||
const id = mmio.read(u8, at + 0);
|
|
||||||
const next = mmio.read(u8, at + 1) & 0xFC;
|
|
||||||
// Only map BARs for the structures V3 uses (common + notify). The other virtio
|
|
||||||
// capabilities (isr, device, and especially the cfg_pci back-door, which carries a
|
|
||||||
// placeholder bar=0/offset=0) reference BARs we never touch, so mapping them would
|
|
||||||
// just log spurious "not a mapped resource" noise.
|
|
||||||
if (id == vp.pci_cap_vendor) {
|
|
||||||
const cfg_type = mmio.read(u8, at + 3);
|
|
||||||
if (cfg_type == vp.cfg_common or cfg_type == vp.cfg_notify) {
|
|
||||||
const bar = mmio.read(u8, at + 4);
|
|
||||||
const offset = mmio.read(u32, at + 8);
|
|
||||||
if (mapBar(config, descriptor, bar)) |bar_base| {
|
|
||||||
if (cfg_type == vp.cfg_common) {
|
|
||||||
common_base = bar_base + offset;
|
|
||||||
} else {
|
|
||||||
notify_base = bar_base + offset;
|
|
||||||
notify_multiplier = mmio.read(u32, at + 16); // virtio_pci_notify_cap tail
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cap = next;
|
|
||||||
}
|
|
||||||
if (common_base == 0 or notify_base == 0) {
|
|
||||||
std.log.info("missing common-config or notify capability", .{});
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- the control virtqueue -------------------------------------------------------------
|
// --- the control virtqueue -------------------------------------------------------------
|
||||||
|
|
||||||
@@ -283,19 +211,43 @@ fn initialise(endpoint: ipc.Handle) bool {
|
|||||||
// Config space is resource 0. Confirm it really is a virtio-gpu, then enable memory-space
|
// Config space is resource 0. Confirm it really is a virtio-gpu, then enable memory-space
|
||||||
// decode + bus mastering (the device DMAs the ring and backing out of RAM); pci-bus only
|
// decode + bus mastering (the device DMAs the ring and backing out of RAM); pci-bus only
|
||||||
// preserves whatever the firmware left, and a secondary display is often left disabled.
|
// preserves whatever the firmware left, and a secondary display is often left disabled.
|
||||||
const config = device.mmioMap(device_id, 0) orelse {
|
var function = pci.Function.map(device_id, descriptor) orelse {
|
||||||
std.log.info("config-space map failed", .{});
|
std.log.info("config-space map failed", .{});
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
const vendor = mmio.read(u16, config + 0x00);
|
const vendor = function.vendorId();
|
||||||
const dev = mmio.read(u16, config + 0x02);
|
const dev = function.deviceId();
|
||||||
if (vendor != virtio_vendor or dev != virtio_gpu_device) {
|
if (vendor != virtio_vendor or dev != virtio_gpu_device) {
|
||||||
std.log.info("not a virtio-gpu (vendor 0x{x} device 0x{x})", .{ vendor, dev });
|
std.log.info("not a virtio-gpu (vendor 0x{x} device 0x{x})", .{ vendor, dev });
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
mmio.write(u16, config + 0x04, mmio.read(u16, config + 0x04) | 0x06); // MEM + bus master
|
function.enableMemoryAndBusMaster();
|
||||||
|
|
||||||
if (!walkCapabilities(config, descriptor)) return false;
|
// Walk the capability list for the virtio common-config and notify structures (V3 needs
|
||||||
|
// only those two). The generic PCI mechanics — header fields, BAR decode, the capability
|
||||||
|
// chain — are library/device/pci; the virtio cfg_type dispatch stays here. We map only the
|
||||||
|
// common/notify BARs (the other virtio caps, and the cfg_pci back-door's placeholder
|
||||||
|
// bar=0/offset=0, reference BARs we never touch, so mapping them would only log noise).
|
||||||
|
var caps = function.capabilities();
|
||||||
|
while (caps.next()) |cap| {
|
||||||
|
if (cap.id != vp.pci_cap_vendor) continue;
|
||||||
|
const cfg_type = mmio.read(u8, cap.offset + 3);
|
||||||
|
if (cfg_type != vp.cfg_common and cfg_type != vp.cfg_notify) continue;
|
||||||
|
const bar = mmio.read(u8, cap.offset + 4);
|
||||||
|
const offset = mmio.read(u32, cap.offset + 8);
|
||||||
|
if (function.mapBar(bar)) |bar_base| {
|
||||||
|
if (cfg_type == vp.cfg_common) {
|
||||||
|
common_base = bar_base + offset;
|
||||||
|
} else {
|
||||||
|
notify_base = bar_base + offset;
|
||||||
|
notify_multiplier = mmio.read(u32, cap.offset + 16); // virtio_pci_notify_cap tail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (common_base == 0 or notify_base == 0) {
|
||||||
|
std.log.info("missing common-config or notify capability", .{});
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Reset, then the modern feature handshake: acknowledge, take driver ownership, require
|
// Reset, then the modern feature handshake: acknowledge, take driver ownership, require
|
||||||
// VERSION_1 and offer nothing else, and confirm the device accepts that.
|
// VERSION_1 and offer nothing else, and confirm the device accepts that.
|
||||||
|
|||||||
Reference in New Issue
Block a user