kernel-zig/src/pci/pci.zig

156 lines
4.9 KiB
Zig

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();
}
}
}
}
}