pub usingnamespace @import("../index.zig"); pub const virtio = @import("virtio.zig"); const PCI_CONFIG_ADDRESS = 0xCF8; const PCI_CONFIG_DATA = 0xCFC; // https://wiki.osdev.org/Pci pub const PciAddress = packed struct { offset: u8, function: u3, slot: u5, bus: u8, reserved: u7, enable: u1, }; pub const PciDevice = struct { bus: u8, slot: u5, function: u3, vendor: u16 = undefined, pub fn init(bus: u8, slot: u5, function: u3) ?PciDevice { var dev = PciDevice{ .bus = bus, .slot = slot, .function = function }; dev.vendor = dev.vendor_id(); if (dev.vendor == 0xffff) return null; return dev; } pub fn address(self: PciDevice, offset: u8) u32 { var addr = PciAddress{ .enable = 1, .reserved = 0, .bus = self.bus, .slot = self.slot, .function = self.function, .offset = offset, }; return @bitCast(u32, addr); } pub fn format(self: PciDevice) void { print("{}:{}.{}", self.bus, self.slot, self.function); print(" {x},{x:2}", self.class(), self.subclass()); print(" 0x{x},0x{x}", self.vendor, self.device()); if (self.driver()) |d| print(" {}", d.name); println(""); } pub fn driver(self: PciDevice) ?Driver { var i: usize = 0; while (i < Drivers.len) : (i += 1) { var drv = Drivers[i]; if (self.class() != drv.class or self.subclass() != drv.subclass) continue; if (drv.vendor) |v| if (self.vendor != v) continue; if (drv.subsystem) |ss| if (self.subsystem() != ss) continue; return drv; } return null; } // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | vendor ID | device ID | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | command | status | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | revision ID | prog IF | subclass | class | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |cache line size| latency timer | header type | bist | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ pub fn vendor_id(self: PciDevice) u16 { return self.config_read(u16, 0x0); } pub fn device(self: PciDevice) u16 { return self.config_read(u16, 0x2); } pub fn subclass(self: PciDevice) u8 { return self.config_read(u8, 0xa); } pub fn class(self: PciDevice) u8 { return self.config_read(u8, 0xb); } pub fn header_type(self: PciDevice) u8 { return self.config_read(u8, 0xe); } // only for header_type == 0 pub fn subsystem(self: PciDevice) u16 { return self.config_read(u8, 0x2e); } pub inline fn config_read(self: PciDevice, comptime size: type, comptime offset: u8) size { // ask for access before reading config x86.outl(PCI_CONFIG_ADDRESS, self.address(offset)); switch (size) { // read the correct size u8 => return x86.inb(PCI_CONFIG_DATA), u16 => return x86.inw(PCI_CONFIG_DATA), u32 => return x86.inl(PCI_CONFIG_DATA), else => @compileError("pci config space only supports reading u8, u16, u32."), } } }; const Driver = struct { name: [*]u8, class: u8, subclass: u8, vendor: ?u16 = null, subsystem: ?u16 = null, init: fn (PciDevice) void, }; const name = "virtio-blk"; pub var Drivers: [1]Driver = [_]Driver{Driver{ .name = &name, .class = 0x1, .subclass = 0x0, .vendor = 0x1af4, .subsystem = 0x2, .init = virtio.init }}; // TODO: factor 2 functions when anonymous fn is released pub fn scan() void { var slot: u5 = 0; // 0..31 while (slot < 31) : (slot += 1) { if (PciDevice.init(0, slot, 0)) |dev| { var function: u3 = 0; // 0..7 while (function < 7) : (function += 1) { if (PciDevice.init(0, slot, function)) |vf| { if (vf.driver()) |d| d.init(vf); } } } } } pub fn lspci() void { var slot: u5 = 0; println("b:s.f c, s v d drv"); while (slot < 31) : (slot += 1) { if (PciDevice.init(0, slot, 0)) |dev| { var function: u3 = 0; // 0..7 while (function < 7) : (function += 1) { if (PciDevice.init(0, slot, function)) |vf| { vf.format(); } } } } }