Merge claude/vigilant-swanson-073c72: retire dead kernel AML device-building path (M20.3 cleanup)
This commit is contained in:
commit
60da667b42
|
|
@ -17,7 +17,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const boot_handoff = @import("boot-handoff");
|
const boot_handoff = @import("boot-handoff");
|
||||||
const abi = @import("abi");
|
const abi = @import("abi");
|
||||||
const acpi_ids = @import("acpi-ids");
|
|
||||||
const parameters = @import("parameters");
|
const parameters = @import("parameters");
|
||||||
const device_model = @import("device-model.zig");
|
const device_model = @import("device-model.zig");
|
||||||
const aml = @import("aml/aml.zig");
|
const aml = @import("aml/aml.zig");
|
||||||
|
|
@ -409,9 +408,7 @@ pub fn discover(rsdp_physical: u64, memory_regions: []const boot_handoff.MemoryR
|
||||||
// tree (M20.3): the ring-3 acpi service claims the acpi-tables node
|
// tree (M20.3): the ring-3 acpi service claims the acpi-tables node
|
||||||
// (published below), re-parses the same blobs, and registers + reports
|
// (published below), re-parses the same blobs, and registers + reports
|
||||||
// the _HID devices itself. The kernel keeps the namespace only for the
|
// the _HID devices itself. The kernel keeps the namespace only for the
|
||||||
// \_S5 sleep type above. The device-building helpers below
|
// \_S5 sleep type above.
|
||||||
// (wireAcpiDevices and friends) are retained but unreferenced — a
|
|
||||||
// focused dead-code sweep follows the migration.
|
|
||||||
} else |_| {
|
} else |_| {
|
||||||
// AML parse failed (e.g. out of memory); power stays best-effort with
|
// AML parse failed (e.g. out of memory); power stays best-effort with
|
||||||
// whatever the FADT alone provided.
|
// whatever the FADT alone provided.
|
||||||
|
|
@ -816,342 +813,6 @@ fn parseDmar(hal: Hal, header: *const SystemDescriptorTableHeader) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- AML namespace -> generic device tree -----------------------------------
|
|
||||||
|
|
||||||
/// The PCI bus context while descending the ACPI namespace: the generic host
|
|
||||||
/// bridge whose children ACPI address (`_ADR`) devices resolve against, and the bus number.
|
|
||||||
const PciContext = struct { bridge: *device_model.Device, bus: u8 };
|
|
||||||
|
|
||||||
/// Mirror the ACPI namespace's Device objects into the generic tree, *merging*
|
|
||||||
/// them with the PCI-enumerated nodes: a PCI root bridge (`PNP0A03`/`PNP0A08`)
|
|
||||||
/// folds onto the existing `pci_host_bridge`, and each addressed (`_ADR`) device folds onto
|
|
||||||
/// the matching PCI function (annotating it with the ACPI hardware ID (`_HID`) and nesting the
|
|
||||||
/// ACPI-only children — keyboard, RTC, … — beneath it). Namespace devices with no
|
|
||||||
/// PCI match land under a synthetic `acpi` node.
|
|
||||||
fn wireAcpiDevices(device_tree: *DeviceTree, aml_namespace: *aml.Namespace, hal: Hal) !void {
|
|
||||||
var arena = std.heap.ArenaAllocator.init(device_tree.allocator);
|
|
||||||
defer arena.deinit();
|
|
||||||
var interpreter = aml.Interpreter.init(aml_namespace, .{
|
|
||||||
.mapMmio = hal.mapMmio,
|
|
||||||
.pioRead = hal.pioRead,
|
|
||||||
.pioWrite = hal.pioWrite,
|
|
||||||
}, arena.allocator());
|
|
||||||
|
|
||||||
const acpi_root = try device_tree.addChild(device_tree.root, .unknown, "acpi");
|
|
||||||
try mirrorDevices(device_tree, aml_namespace.root, acpi_root, null, &interpreter);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mirrorDevices(device_tree: *DeviceTree, node: *aml.Node, parent_device: *device_model.Device, context: ?PciContext, interpreter: *aml.Interpreter) (error{OutOfMemory})!void {
|
|
||||||
var child = node.first_child;
|
|
||||||
while (child) |c| : (child = c.next_sibling) {
|
|
||||||
if (c.kind != .device) {
|
|
||||||
// A scope — the System Bus (\_SB), General Purpose Events (\_GPE), … —
|
|
||||||
// descend without adding a node.
|
|
||||||
try mirrorDevices(device_tree, c, parent_device, context, interpreter);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip devices the firmware reports as not present (via a device-status (`_STA`) method),
|
|
||||||
// along with their whole subtree — per the ACPI rules.
|
|
||||||
if (!devicePresent(interpreter, c)) continue;
|
|
||||||
|
|
||||||
var mirrored_device: *device_model.Device = undefined;
|
|
||||||
var child_context = context;
|
|
||||||
|
|
||||||
if (isPciRootNode(c)) {
|
|
||||||
// The PCI root bridge folds onto the generic host bridge.
|
|
||||||
mirrored_device = matchHostBridge(device_tree) orelse
|
|
||||||
try device_tree.addChild(parent_device, .acpi_device, &c.segment);
|
|
||||||
child_context = .{ .bridge = mirrored_device, .bus = 0 };
|
|
||||||
} else {
|
|
||||||
// An addressed device folds onto its matching PCI function; anything
|
|
||||||
// else becomes a fresh node under the current parent.
|
|
||||||
mirrored_device = pick: {
|
|
||||||
if (context) |pc| {
|
|
||||||
if (readAdr(c)) |adr| {
|
|
||||||
if (findPciNode(pc.bridge, pc.bus, adr)) |pnode| break :pick pnode;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break :pick try device_tree.addChild(parent_device, .acpi_device, &c.segment);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
applyHid(mirrored_device, c, interpreter);
|
|
||||||
applyCrs(mirrored_device, c, interpreter);
|
|
||||||
try mirrorDevices(device_tree, c, mirrored_device, child_context, interpreter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate a device's status (`_STA`) to decide if it is present. An absent status
|
|
||||||
/// (`_STA`) means present by default; an evaluation failure is treated as present too (we'd
|
|
||||||
/// rather over-report than hide a device we couldn't introspect).
|
|
||||||
fn devicePresent(interpreter: *aml.Interpreter, node: *aml.Node) bool {
|
|
||||||
const sta = aml.Namespace.childOf(node, seg4("_STA")) orelse return true;
|
|
||||||
const obj = interpreter.evaluate(sta, &.{}) catch return true;
|
|
||||||
const status = obj.asInteger() catch return true;
|
|
||||||
return (status & 0x01) != 0; // bit 0 = present
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The first PCI host bridge in the generic tree (segment 0).
|
|
||||||
fn matchHostBridge(device_tree: *DeviceTree) ?*device_model.Device {
|
|
||||||
var c = device_tree.root.first_child;
|
|
||||||
while (c) |ch| : (c = ch.next_sibling) {
|
|
||||||
if (ch.class == .pci_host_bridge) return ch;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The PCI function node under `bridge` at the address the device's address object
|
|
||||||
/// (`_ADR`) names (device/function on
|
|
||||||
/// `bus`), or null.
|
|
||||||
fn findPciNode(bridge: *device_model.Device, bus: u8, adr: u32) ?*device_model.Device {
|
|
||||||
const device: u16 = @truncate((adr >> 16) & 0x1F);
|
|
||||||
const function: u16 = @truncate(adr & 0x7);
|
|
||||||
const target: u16 = (@as(u16, bus) << 8) | (device << 3) | function;
|
|
||||||
var c = bridge.first_child;
|
|
||||||
while (c) |ch| : (c = ch.next_sibling) {
|
|
||||||
if (ch.ids.pci_bdf) |bdf| {
|
|
||||||
if (bdf == target) return ch;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A device's address (`_ADR`) — a static integer Name — or null.
|
|
||||||
fn readAdr(node: *aml.Node) ?u32 {
|
|
||||||
const n = aml.Namespace.childOf(node, seg4("_ADR")) orelse return null;
|
|
||||||
if (n.kind != .name) return null;
|
|
||||||
var p: usize = 0;
|
|
||||||
return @truncate(readIntObj(n.value, &p) orelse return null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether a `_HID` string names a PCI(e) host bridge.
|
|
||||||
fn isPciRootHid(hid: []const u8) bool {
|
|
||||||
const id = acpi_ids.HardwareId.fromHid(hid) orelse return false;
|
|
||||||
return id == .pci_bus or id == .pci_express_root_bridge;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Whether a namespace device is a PCI(e) host bridge. A packed EISA id is decoded
|
|
||||||
/// to its string form first, so both encodings answer through the one registry.
|
|
||||||
fn isPciRootNode(node: *aml.Node) bool {
|
|
||||||
const hid = aml.Namespace.childOf(node, seg4("_HID")) orelse return false;
|
|
||||||
if (hid.kind != .name or hid.value.len == 0) return false;
|
|
||||||
switch (hid.value[0]) {
|
|
||||||
0x00, 0x01, 0xFF, 0x0A, 0x0B, 0x0C, 0x0E => {
|
|
||||||
var p: usize = 0;
|
|
||||||
const n = readIntObj(hid.value, &p) orelse return false;
|
|
||||||
var buffer: [8]u8 = undefined;
|
|
||||||
return isPciRootHid(eisaIdToStr(@truncate(n), &buffer));
|
|
||||||
},
|
|
||||||
0x0D => return isPciRootHid(cstr(hid.value[1..])),
|
|
||||||
else => return false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read a device's hardware ID (`_HID`) into the generic device: an integer decodes as an EISA
|
|
||||||
/// id ("PNP0A03"), a string is taken verbatim. Handles both the common static
|
|
||||||
/// Name form and a Method form (evaluated).
|
|
||||||
fn applyHid(device: *device_model.Device, node: *aml.Node, interpreter: *aml.Interpreter) void {
|
|
||||||
const hid = aml.Namespace.childOf(node, seg4("_HID")) orelse return;
|
|
||||||
if (hid.kind == .method) {
|
|
||||||
const obj = interpreter.evaluate(hid, &.{}) catch return;
|
|
||||||
switch (obj) {
|
|
||||||
.integer => |n| setEisaHid(device, @truncate(n)),
|
|
||||||
.string => |s| device.setHid(s),
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (hid.kind != .name or hid.value.len == 0) return;
|
|
||||||
const v = hid.value;
|
|
||||||
switch (v[0]) {
|
|
||||||
0x00, 0x01, 0xFF, 0x0A, 0x0B, 0x0C, 0x0E => {
|
|
||||||
var p: usize = 0;
|
|
||||||
const n = readIntObj(v, &p) orelse return;
|
|
||||||
setEisaHid(device, @truncate(n));
|
|
||||||
},
|
|
||||||
0x0D => device.setHid(cstr(v[1..])), // StringPrefix
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn setEisaHid(device: *device_model.Device, id: u32) void {
|
|
||||||
device.ids.acpi_hid = id;
|
|
||||||
var buffer: [8]u8 = undefined;
|
|
||||||
device.setHid(eisaIdToStr(id, &buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse a device's current resource settings (`_CRS`). The evaluator handles both the static
|
|
||||||
/// `Buffer` form (a `Name`) and the method form uniformly, yielding the
|
|
||||||
/// ResourceTemplate bytes we then decode.
|
|
||||||
fn applyCrs(device: *device_model.Device, node: *aml.Node, interpreter: *aml.Interpreter) void {
|
|
||||||
const crs = aml.Namespace.childOf(node, seg4("_CRS")) orelse return;
|
|
||||||
const obj = interpreter.evaluate(crs, &.{}) catch return;
|
|
||||||
const buffer = switch (obj) {
|
|
||||||
.buffer => |b| b,
|
|
||||||
else => return,
|
|
||||||
};
|
|
||||||
parseResourceTemplate(device, buffer);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Walk a ResourceTemplate byte list, adding recognised descriptors as resources.
|
|
||||||
fn parseResourceTemplate(device: *device_model.Device, bytes: []const u8) void {
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < bytes.len) {
|
|
||||||
const tag = bytes[i];
|
|
||||||
if (tag & 0x80 == 0) {
|
|
||||||
// Small descriptor: length in low 3 bits, type in bits [6:3].
|
|
||||||
const len: usize = tag & 0x07;
|
|
||||||
const body = i + 1;
|
|
||||||
if (body + len > bytes.len) break;
|
|
||||||
switch ((tag >> 3) & 0x0F) {
|
|
||||||
0x04 => if (len >= 2) { // IRQ: a 16-bit mask, one resource per set bit
|
|
||||||
const mask = @as(u16, bytes[body]) | (@as(u16, bytes[body + 1]) << 8);
|
|
||||||
var b: usize = 0;
|
|
||||||
while (b < 16) : (b += 1) {
|
|
||||||
if (mask & (@as(u16, 1) << @intCast(b)) != 0) _ = device.addResource(.irq, b, 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
0x08 => if (len >= 7) { // IO port: minimum at +1, length at +6
|
|
||||||
_ = device.addResource(.io_port, rd16(bytes, body + 1), bytes[body + 6]);
|
|
||||||
},
|
|
||||||
0x09 => if (len >= 3) { // Fixed IO: base at +0, length at +2
|
|
||||||
_ = device.addResource(.io_port, rd16(bytes, body), bytes[body + 2]);
|
|
||||||
},
|
|
||||||
0x0F => break, // EndTag
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
i = body + len;
|
|
||||||
} else {
|
|
||||||
// Large descriptor: 16-bit length follows the tag.
|
|
||||||
if (i + 3 > bytes.len) break;
|
|
||||||
const len: usize = @intCast(rd16(bytes, i + 1));
|
|
||||||
const body = i + 3;
|
|
||||||
if (body + len > bytes.len) break;
|
|
||||||
switch (tag) {
|
|
||||||
0x85 => if (len >= 17) { // Memory32: minimum at +1, length at +13
|
|
||||||
_ = device.addResource(.memory, rd32(bytes, body + 1), rd32(bytes, body + 13));
|
|
||||||
},
|
|
||||||
0x86 => if (len >= 9) { // Memory32Fixed: base at +1, length at +5
|
|
||||||
_ = device.addResource(.memory, rd32(bytes, body + 1), rd32(bytes, body + 5));
|
|
||||||
},
|
|
||||||
0x89 => if (len >= 2) { // Extended IRQ: count at +1, then count u32s
|
|
||||||
const count = bytes[body + 1];
|
|
||||||
var k: usize = 0;
|
|
||||||
while (k < count and body + 2 + k * 4 + 4 <= body + len) : (k += 1) {
|
|
||||||
_ = device.addResource(.irq, rd32(bytes, body + 2 + k * 4), 1);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
0x87, 0x88, 0x8A => parseAddressSpace(device, tag, bytes[body .. body + len]),
|
|
||||||
else => {},
|
|
||||||
}
|
|
||||||
i = body + len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Word/DWord/QWord address-space descriptors: resource type at [0], then
|
|
||||||
/// granularity/minimum/maximum/translation/length, each of width `w`.
|
|
||||||
fn parseAddressSpace(device: *device_model.Device, tag: u8, body: []const u8) void {
|
|
||||||
const w: usize = switch (tag) {
|
|
||||||
0x88 => 2, // Word
|
|
||||||
0x87 => 4, // DWord
|
|
||||||
else => 8, // QWord (0x8A)
|
|
||||||
};
|
|
||||||
if (body.len < 3 + 5 * w) return;
|
|
||||||
const minimum = readN(body, 3 + w, w);
|
|
||||||
const length = readN(body, 3 + 4 * w, w);
|
|
||||||
const kind: device_model.ResourceKind = switch (body[0]) {
|
|
||||||
0 => .memory,
|
|
||||||
1 => .io_port,
|
|
||||||
else => .bus_range,
|
|
||||||
};
|
|
||||||
_ = device.addResource(kind, minimum, length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Decode a packed EISA id into its 7-char string (e.g. 0x030AD041 -> "PNP0A03").
|
|
||||||
fn eisaIdToStr(id: u32, buffer: *[8]u8) []const u8 {
|
|
||||||
const b0: u16 = @intCast(id & 0xFF);
|
|
||||||
const b1: u16 = @intCast((id >> 8) & 0xFF);
|
|
||||||
const b2: u8 = @truncate(id >> 16);
|
|
||||||
const b3: u8 = @truncate(id >> 24);
|
|
||||||
const mfg = (b0 << 8) | b1;
|
|
||||||
buffer[0] = '@' + @as(u8, @intCast((mfg >> 10) & 0x1F));
|
|
||||||
buffer[1] = '@' + @as(u8, @intCast((mfg >> 5) & 0x1F));
|
|
||||||
buffer[2] = '@' + @as(u8, @intCast(mfg & 0x1F));
|
|
||||||
buffer[3] = hexDigit((b2 >> 4) & 0xF);
|
|
||||||
buffer[4] = hexDigit(b2 & 0xF);
|
|
||||||
buffer[5] = hexDigit((b3 >> 4) & 0xF);
|
|
||||||
buffer[6] = hexDigit(b3 & 0xF);
|
|
||||||
return buffer[0..7];
|
|
||||||
}
|
|
||||||
|
|
||||||
fn hexDigit(n: u8) u8 {
|
|
||||||
return if (n < 10) '0' + n else 'A' + (n - 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn seg4(comptime s: *const [4:0]u8) [4]u8 {
|
|
||||||
return s[0..4].*;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cstr(bytes: []const u8) []const u8 {
|
|
||||||
const index = std.mem.indexOfScalar(u8, bytes, 0) orelse bytes.len;
|
|
||||||
return bytes[0..index];
|
|
||||||
}
|
|
||||||
|
|
||||||
const PkgLen = struct { value: usize, size: usize };
|
|
||||||
|
|
||||||
fn packageLength(bytes: []const u8, p: usize) ?PkgLen {
|
|
||||||
if (p >= bytes.len) return null;
|
|
||||||
const lead = bytes[p];
|
|
||||||
const follow: usize = lead >> 6;
|
|
||||||
if (p + 1 + follow > bytes.len) return null;
|
|
||||||
if (follow == 0) return .{ .value = lead & 0x3F, .size = 1 };
|
|
||||||
var value: usize = lead & 0x0F;
|
|
||||||
var i: usize = 0;
|
|
||||||
while (i < follow) : (i += 1) value |= @as(usize, bytes[p + 1 + i]) << @intCast(4 + i * 8);
|
|
||||||
return .{ .value = value, .size = 1 + follow };
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Read an AML integer object at `p`, advancing `p` past it.
|
|
||||||
fn readIntObj(bytes: []const u8, p: *usize) ?u64 {
|
|
||||||
if (p.* >= bytes.len) return null;
|
|
||||||
const opcode = bytes[p.*];
|
|
||||||
p.* += 1;
|
|
||||||
return switch (opcode) {
|
|
||||||
0x00 => 0,
|
|
||||||
0x01 => 1,
|
|
||||||
0xFF => 0xFF,
|
|
||||||
0x0A => readLE(bytes, p, 1),
|
|
||||||
0x0B => readLE(bytes, p, 2),
|
|
||||||
0x0C => readLE(bytes, p, 4),
|
|
||||||
0x0E => readLE(bytes, p, 8),
|
|
||||||
else => null,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn readLE(bytes: []const u8, p: *usize, n: usize) ?u64 {
|
|
||||||
if (p.* + n > bytes.len) return null;
|
|
||||||
const v = readN(bytes, p.*, n);
|
|
||||||
p.* += n;
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn readN(bytes: []const u8, off: usize, n: usize) u64 {
|
|
||||||
var v: u64 = 0;
|
|
||||||
var k: usize = 0;
|
|
||||||
while (k < n and off + k < bytes.len) : (k += 1) v |= @as(u64, bytes[off + k]) << @intCast(k * 8);
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rd16(bytes: []const u8, off: usize) u64 {
|
|
||||||
return readN(bytes, off, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rd32(bytes: []const u8, off: usize) u64 {
|
|
||||||
return readN(bytes, off, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- helpers ----------------------------------------------------------------
|
// --- helpers ----------------------------------------------------------------
|
||||||
|
|
||||||
/// Sum `len` bytes; an ACPI table/pointer is valid when the low 8 bits are zero.
|
/// Sum `len` bytes; an ACPI table/pointer is valid when the low 8 bits are zero.
|
||||||
|
|
@ -1192,42 +853,9 @@ fn readCntRegister(base: [*]align(1) const u8, len: usize, xoff: usize, legacy_o
|
||||||
return .{ .mmio = false, .address = port, .width = width };
|
return .{ .mmio = false, .address = port, .width = width };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The mapped configuration space of one PCI function (its 4 KiB ECAM page). Mapped
|
|
||||||
/// writable so BAR sizing can probe it; reads and writes both go through here.
|
|
||||||
/// Read a little-endian integer at `off` from a (possibly unaligned) byte pointer.
|
/// Read a little-endian integer at `off` from a (possibly unaligned) byte pointer.
|
||||||
/// x86 is little-endian and native, so an unaligned load suffices.
|
/// x86 is little-endian and native, so an unaligned load suffices.
|
||||||
fn rd(comptime T: type, bytes: [*]align(1) const u8, off: usize) T {
|
fn rd(comptime T: type, bytes: [*]align(1) const u8, off: usize) T {
|
||||||
const p: *align(1) const T = @ptrCast(bytes + off);
|
const p: *align(1) const T = @ptrCast(bytes + off);
|
||||||
return p.*;
|
return p.*;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- tests ------------------------------------------------------------------
|
|
||||||
|
|
||||||
test "eisaIdToStr decodes a packed EISA id" {
|
|
||||||
var buffer: [8]u8 = undefined;
|
|
||||||
// 0x030AD041 is the well-known encoding of "PNP0A03" (PCI root bridge).
|
|
||||||
try std.testing.expectEqualStrings("PNP0A03", eisaIdToStr(0x030AD041, &buffer));
|
|
||||||
}
|
|
||||||
|
|
||||||
test "parseResourceTemplate extracts IO, IRQ, and fixed memory" {
|
|
||||||
// ResourceTemplate { IO(minimum 0x60, len 8), IRQ(4), Memory32Fixed(0xFED00000, 0x1000) }
|
|
||||||
const runtime = [_]u8{
|
|
||||||
0x47, 0x01, 0x60, 0x00, 0x60, 0x00, 0x01, 0x08, // small IO descriptor
|
|
||||||
0x22, 0x10, 0x00, // small IRQ descriptor (mask bit 4 -> IRQ 4)
|
|
||||||
0x86, 0x09, 0x00, 0x01, 0x00, 0x00, 0xD0, 0xFE, 0x00, 0x10, 0x00, 0x00, // Memory32Fixed
|
|
||||||
0x79, 0x00, // EndTag
|
|
||||||
};
|
|
||||||
var device = device_model.Device{};
|
|
||||||
parseResourceTemplate(&device, &runtime);
|
|
||||||
|
|
||||||
try std.testing.expectEqual(@as(u8, 3), device.resource_count);
|
|
||||||
const rs = device.resources[0..device.resource_count];
|
|
||||||
try std.testing.expectEqual(device_model.ResourceKind.io_port, rs[0].kind);
|
|
||||||
try std.testing.expectEqual(@as(u64, 0x60), rs[0].start);
|
|
||||||
try std.testing.expectEqual(@as(u64, 8), rs[0].len);
|
|
||||||
try std.testing.expectEqual(device_model.ResourceKind.irq, rs[1].kind);
|
|
||||||
try std.testing.expectEqual(@as(u64, 4), rs[1].start);
|
|
||||||
try std.testing.expectEqual(device_model.ResourceKind.memory, rs[2].kind);
|
|
||||||
try std.testing.expectEqual(@as(u64, 0xFED00000), rs[2].start);
|
|
||||||
try std.testing.expectEqual(@as(u64, 0x1000), rs[2].len);
|
|
||||||
}
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue