From abc712233b41f55bc70756f2fcf0d6f45e2d677c Mon Sep 17 00:00:00 2001 From: DrDeano Date: Sat, 10 Oct 2020 00:35:20 +0100 Subject: [PATCH] Initial PCI interface Closes #244 Move PCI to arch Plus spelling Added new out and in functions Added new out and in to mocking Return pci devices as a list Improved comment Removed mask for the return Removed type for OUT Added new types --- src/kernel/arch/x86/arch.zig | 79 ++++-- src/kernel/arch/x86/cmos.zig | 44 ++-- src/kernel/arch/x86/keyboard.zig | 2 +- src/kernel/arch/x86/pci.zig | 359 ++++++++++++++++++++++++++++ src/kernel/arch/x86/pic.zig | 100 ++++---- src/kernel/arch/x86/pit.zig | 20 +- src/kernel/arch/x86/serial.zig | 14 +- src/kernel/arch/x86/vga.zig | 44 ++-- src/kernel/heap.zig | 2 +- src/kernel/kmain.zig | 13 +- test/gen_types.zig | 4 +- test/mock/kernel/arch_mock.zig | 15 +- test/mock/kernel/mock_framework.zig | 20 +- test/mock/kernel/pci_mock.zig | 108 +++++++++ 14 files changed, 677 insertions(+), 147 deletions(-) create mode 100644 src/kernel/arch/x86/pci.zig create mode 100644 test/mock/kernel/pci_mock.zig diff --git a/src/kernel/arch/x86/arch.zig b/src/kernel/arch/x86/arch.zig index aac2a94..256f37a 100644 --- a/src/kernel/arch/x86/arch.zig +++ b/src/kernel/arch/x86/arch.zig @@ -9,6 +9,7 @@ const irq = @import("irq.zig"); const isr = @import("isr.zig"); const paging = @import("paging.zig"); const pic = @import("pic.zig"); +const pci = @import("pci.zig"); const pit = @import("pit.zig"); const rtc = @import("rtc.zig"); const serial = @import("serial.zig"); @@ -25,6 +26,9 @@ const TTY = @import("../../tty.zig").TTY; const Keyboard = @import("../../keyboard.zig").Keyboard; const MemProfile = mem.MemProfile; +/// The type of a device. +pub const Device = pci.PciDeviceInfo; + /// The virtual end of the kernel code. extern var KERNEL_VADDR_END: *u32; @@ -101,42 +105,67 @@ pub const MEMORY_BLOCK_SIZE: usize = paging.PAGE_SIZE_4KB; /// The default stack size of a task. Currently this is set to a page size. pub const STACK_SIZE: u32 = MEMORY_BLOCK_SIZE / @sizeOf(u32); -/// -/// Assembly to write to a given port with a byte of data. -/// -/// Arguments: -/// IN port: u16 - The port to write to. -/// IN data: u8 - The byte of data that will be sent. -/// -pub fn outb(port: u16, data: u8) void { - asm volatile ("outb %[data], %[port]" - : - : [port] "{dx}" (port), - [data] "{al}" (data) - ); -} - /// /// Assembly that reads data from a given port and returns its value. /// /// Arguments: -/// IN port: u16 - The port to read data from. +/// IN comptime Type: type - The type of the data. This can only be u8, u16 or u32. +/// IN port: u16 - The port to read data from. /// -/// Return: u8 +/// Return: Type /// The data that the port returns. /// -pub fn inb(port: u16) u8 { - return asm volatile ("inb %[port], %[result]" - : [result] "={al}" (-> u8) - : [port] "N{dx}" (port) - ); +pub fn in(comptime Type: type, port: u16) Type { + return switch (Type) { + u8 => asm volatile ("inb %[port], %[result]" + : [result] "={al}" (-> Type) + : [port] "N{dx}" (port) + ), + u16 => asm volatile ("inw %[port], %[result]" + : [result] "={ax}" (-> Type) + : [port] "N{dx}" (port) + ), + u32 => asm volatile ("inl %[port], %[result]" + : [result] "={eax}" (-> Type) + : [port] "N{dx}" (port) + ), + else => @compileError("Invalid data type. Only u8, u16 or u32, found: " ++ @typeName(Type)), + }; +} + +/// +/// Assembly to write to a given port with a give type of data. +/// +/// Arguments: +/// IN port: u16 - The port to write to. +/// IN data: anytype - The data that will be sent This must be a u8, u16 or u32 type. +/// +pub fn out(port: u16, data: anytype) void { + switch (@TypeOf(data)) { + u8 => asm volatile ("outb %[data], %[port]" + : + : [port] "{dx}" (port), + [data] "{al}" (data) + ), + u16 => asm volatile ("outw %[data], %[port]" + : + : [port] "{dx}" (port), + [data] "{ax}" (data) + ), + u32 => asm volatile ("outl %[data], %[port]" + : + : [port] "{dx}" (port), + [data] "{eax}" (data) + ), + else => @compileError("Invalid data type. Only u8, u16 or u32, found: " ++ @typeName(@TypeOf(data))), + } } /// /// Force the CPU to wait for an I/O operation to compete. Use port 0x80 as this is unused. /// pub fn ioWait() void { - outb(0x80, 0); + out(0x80, @as(u8, 0)); } /// @@ -516,6 +545,10 @@ pub fn initTaskStack(entry_point: usize, allocator: *Allocator) Allocator.Error! return ret; } +pub fn getDevices(allocator: *Allocator) Allocator.Error![]Device { + return pci.getDevices(allocator); +} + /// /// Initialise the architecture /// diff --git a/src/kernel/arch/x86/cmos.zig b/src/kernel/arch/x86/cmos.zig index a7deece..6151618 100644 --- a/src/kernel/arch/x86/cmos.zig +++ b/src/kernel/arch/x86/cmos.zig @@ -139,9 +139,9 @@ pub const RtcRegister = enum { /// inline fn selectRegister(reg: u8, comptime disable_nmi: bool) void { if (disable_nmi) { - arch.outb(ADDRESS, reg | NMI_BIT); + arch.out(ADDRESS, reg | NMI_BIT); } else { - arch.outb(ADDRESS, reg); + arch.out(ADDRESS, reg); } } @@ -152,7 +152,7 @@ inline fn selectRegister(reg: u8, comptime disable_nmi: bool) void { /// IN data: u8 - The data to write to the selected register. /// inline fn writeRegister(data: u8) void { - arch.outb(DATA, data); + arch.out(DATA, data); } /// @@ -162,7 +162,7 @@ inline fn writeRegister(data: u8) void { /// The value in the selected register. /// inline fn readRegister() u8 { - return arch.inb(DATA); + return arch.in(u8, DATA); } /// @@ -240,7 +240,7 @@ test "selectRegister" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ ADDRESS, STATUS_REGISTER_A }); + arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_A }); const reg = STATUS_REGISTER_A; @@ -251,7 +251,7 @@ test "selectRegister no NMI" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ ADDRESS, STATUS_REGISTER_A | NMI_BIT }); + arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_A | NMI_BIT }); const reg = STATUS_REGISTER_A; @@ -262,7 +262,7 @@ test "writeRegister" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ DATA, @as(u8, 0xAA) }); + arch.addTestParams("out", .{ DATA, @as(u8, 0xAA) }); const data = @as(u8, 0xAA); @@ -273,7 +273,7 @@ test "readRegister" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("inb", .{ DATA, @as(u8, 0x55) }); + arch.addTestParams("in", .{ DATA, @as(u8, 0x55) }); const expected = @as(u8, 0x55); const actual = readRegister(); @@ -285,8 +285,8 @@ test "selectAndReadRegister NMI" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ ADDRESS, STATUS_REGISTER_C }); - arch.addTestParams("inb", .{ DATA, @as(u8, 0x44) }); + arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_C }); + arch.addTestParams("in", .{ DATA, @as(u8, 0x44) }); arch.addConsumeFunction("ioWait", arch.mock_ioWait); const reg = STATUS_REGISTER_C; @@ -301,8 +301,8 @@ test "selectAndReadRegister no NMI" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ ADDRESS, STATUS_REGISTER_C | NMI_BIT }); - arch.addTestParams("inb", .{ DATA, @as(u8, 0x44) }); + arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_C | NMI_BIT }); + arch.addTestParams("in", .{ DATA, @as(u8, 0x44) }); arch.addConsumeFunction("ioWait", arch.mock_ioWait); const reg = STATUS_REGISTER_C; @@ -317,7 +317,7 @@ test "selectAndWriteRegister NMI" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ ADDRESS, STATUS_REGISTER_C, DATA, @as(u8, 0x88) }); + arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_C, DATA, @as(u8, 0x88) }); arch.addConsumeFunction("ioWait", arch.mock_ioWait); const reg = STATUS_REGISTER_C; @@ -330,7 +330,7 @@ test "selectAndWriteRegister no NMI" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ ADDRESS, STATUS_REGISTER_C | NMI_BIT, DATA, @as(u8, 0x88) }); + arch.addTestParams("out", .{ ADDRESS, STATUS_REGISTER_C | NMI_BIT, DATA, @as(u8, 0x88) }); arch.addConsumeFunction("ioWait", arch.mock_ioWait); const reg = STATUS_REGISTER_C; @@ -358,8 +358,8 @@ test "readRtcRegister" { .CENTURY => REGISTER_CENTURY, }; - arch.addTestParams("outb", .{ ADDRESS, r }); - arch.addTestParams("inb", .{ DATA, @as(u8, 0x44) }); + arch.addTestParams("out", .{ ADDRESS, r }); + arch.addTestParams("in", .{ DATA, @as(u8, 0x44) }); const expected = @as(u8, 0x44); const actual = readRtcRegister(reg); @@ -383,8 +383,8 @@ test "readStatusRegister NMI" { .C => STATUS_REGISTER_C, }; - arch.addTestParams("outb", .{ ADDRESS, r }); - arch.addTestParams("inb", .{ DATA, @as(u8, 0x78) }); + arch.addTestParams("out", .{ ADDRESS, r }); + arch.addTestParams("in", .{ DATA, @as(u8, 0x78) }); const expected = @as(u8, 0x78); const actual = readStatusRegister(reg, false); @@ -408,8 +408,8 @@ test "readStatusRegister no NMI" { .C => STATUS_REGISTER_C, }; - arch.addTestParams("outb", .{ ADDRESS, r | NMI_BIT }); - arch.addTestParams("inb", .{ DATA, @as(u8, 0x78) }); + arch.addTestParams("out", .{ ADDRESS, r | NMI_BIT }); + arch.addTestParams("in", .{ DATA, @as(u8, 0x78) }); const expected = @as(u8, 0x78); const actual = readStatusRegister(reg, true); @@ -433,7 +433,7 @@ test "writeStatusRegister NMI" { .C => STATUS_REGISTER_C, }; - arch.addTestParams("outb", .{ ADDRESS, r, DATA, @as(u8, 0x43) }); + arch.addTestParams("out", .{ ADDRESS, r, DATA, @as(u8, 0x43) }); const data = @as(u8, 0x43); writeStatusRegister(reg, data, false); @@ -455,7 +455,7 @@ test "writeStatusRegister no NMI" { .C => STATUS_REGISTER_C, }; - arch.addTestParams("outb", .{ ADDRESS, r | NMI_BIT, DATA, @as(u8, 0x43) }); + arch.addTestParams("out", .{ ADDRESS, r | NMI_BIT, DATA, @as(u8, 0x43) }); const data = @as(u8, 0x43); writeStatusRegister(reg, data, true); diff --git a/src/kernel/arch/x86/keyboard.zig b/src/kernel/arch/x86/keyboard.zig index a62b171..5a05f3d 100644 --- a/src/kernel/arch/x86/keyboard.zig +++ b/src/kernel/arch/x86/keyboard.zig @@ -32,7 +32,7 @@ var on_print_screen = false; /// The byte waiting in the keyboard buffer /// fn readKeyboardBuffer() u8 { - return arch.inb(0x60); + return arch.in(u8, 0x60); } /// diff --git a/src/kernel/arch/x86/pci.zig b/src/kernel/arch/x86/pci.zig new file mode 100644 index 0000000..e78247d --- /dev/null +++ b/src/kernel/arch/x86/pci.zig @@ -0,0 +1,359 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const is_test = builtin.is_test; +const expectEqual = std.testing.expectEqual; +const build_options = @import("build_options"); +const mock_path = build_options.arch_mock_path; +const Allocator = std.mem.Allocator; +const ArrayList = std.ArrayList; +const logger = std.log.scoped(.pci); +const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig"); + +/// The port address for selecting a 32bit register in the PCI configuration space. +const CONFIG_ADDRESS: u16 = 0x0CF8; + +/// The port address for read/writing to the selected address. +const CONFIG_DATA: u16 = 0x0CFC; + +/// The register offsets for PCI. Currently there is no check for valid register offsets for the +/// header type. The names are self explanatory. Further information can be found here: +/// https://wiki.osdev.org/PCI. +const PciRegisters = enum(u8) { + VenderId = 0x00, + DeviceId = 0x02, + Command = 0x04, + Status = 0x06, + RevisionId = 0x08, + ProgrammingInterface = 0x09, + Subclass = 0x0A, + ClassCode = 0x0B, + CacheLineSize = 0x0C, + LatencyTimer = 0x0D, + HeaderType = 0x0E, + BIST = 0x0F, + + // The next set of registers are for the 0x00 (standard) header. + // This currently uses only the common registers above that are available to all header types. + + BaseAddr0 = 0x10, + BaseAddr1 = 0x14, + BaseAddr2 = 0x18, + BaseAddr3 = 0x1C, + BaseAddr4 = 0x20, + BaseAddr5 = 0x24, + CardbusCISPtr = 0x28, + SubsystemVenderId = 0x2C, + SubsystemId = 0x2E, + ExpansionROMBaseAddr = 0x30, + CapabilitiesPtr = 0x34, + InterruptLine = 0x3C, + InterruptPin = 0x3D, + MinGrant = 0x3E, + MaxLatency = 0x3F, + + /// + /// Get the type the represents the width of the register. This can be either u8, u16 or u32. + /// + /// Argument: + /// IN comptime pci_reg: PciRegisters - The register to get the width for. + /// + /// Return: type + /// The width type. + /// + pub fn getWidth(comptime pci_reg: PciRegisters) type { + return switch (pci_reg) { + .RevisionId, .ProgrammingInterface, .Subclass, .ClassCode, .CacheLineSize, .LatencyTimer, .HeaderType, .BIST, .InterruptLine, .InterruptPin, .MinGrant, .MaxLatency, .CapabilitiesPtr => u8, + .VenderId, .DeviceId, .Command, .Status, .SubsystemVenderId, .SubsystemId => u16, + .BaseAddr0, .BaseAddr1, .BaseAddr2, .BaseAddr3, .BaseAddr4, .BaseAddr5, .CardbusCISPtr, .ExpansionROMBaseAddr => u32, + }; + } +}; + +/// The PCI address used for sending to the address port. +const PciAddress = packed struct { + register_offset: u8, + function: u3, + device: u5, + bus: u8, + reserved: u7 = 0, + enable: u1 = 1, +}; + +/// A PCI device. This will be unique to a bus and device number. +const PciDevice = struct { + /// The bus on which the device is on + bus: u8, + + /// The device number. + device: u5, + + const Self = @This(); + + /// + /// Get the PCI address for this device and for a function and register. + /// + /// Argument: + /// IN self: Self - This device. + /// IN function: u3 - The function. + /// IN comptime pci_reg: PciRegisters - The register. + /// + /// Return: PciAddress + /// The PCI address that can be used to read the register offset for this device and function. + /// + pub fn getAddress(self: Self, function: u3, comptime pci_reg: PciRegisters) PciAddress { + return PciAddress{ + .bus = self.bus, + .device = self.device, + .function = function, + .register_offset = @enumToInt(pci_reg), + }; + } + + /// + /// Read the configuration register data from this device, function and register. PCI configure + /// reads will return a u32 value, but the register may not be u32 is size so this will return + /// the correctly typed value depending on the size of the register. + /// + /// Argument: + /// IN self: Self - This device. + /// IN function: u3 - The function. + /// IN comptime pci_reg: PciRegisters - The register. + /// + /// Return: PciRegisters.getWidth() + /// Depending on the register, the type of the return value maybe u8, u16 or u32. See + /// PciRegisters.getWidth(). + /// + pub fn configReadData(self: Self, function: u3, comptime pci_reg: PciRegisters) pci_reg.getWidth() { + const address = self.getAddress(function, pci_reg); + // Last 2 bits of offset must be zero + // This is because we are requesting a integer (4 bytes) and cannot request a + // single byte that isn't 4 bytes aligned + // Write the address + arch.out(CONFIG_ADDRESS, @bitCast(u32, address) & 0xFFFFFFFC); + // Read the data + const result = arch.in(u32, CONFIG_DATA); + // Return the size the user wants + const shift = switch (pci_reg.getWidth()) { + u8 => (@intCast(u5, address.register_offset & 0x3)) * 8, + u16 => (@intCast(u5, address.register_offset & 0x2)) * 8, + u32 => 0, + else => @compileError("Invalid read size. Only u8, u16 and u32 allowed."), + }; + return @truncate(pci_reg.getWidth(), (result >> shift)); + } + + test "configReadData u8" { + arch.initTest(); + defer arch.freeTest(); + + // The bus, device and function values can be any value as we are testing the shifting and masking + // Have chosen bus = 0, device = 1 and function = 2. + // We only change the register as they will have different but widths. + + { + const device = PciDevice{ + .bus = 0, + .device = 1, + }; + + arch.addTestParams("out", .{ CONFIG_ADDRESS, @bitCast(u32, device.getAddress(2, .RevisionId)) & 0xFFFFFFFC }); + arch.addTestParams("in", .{ CONFIG_DATA, @as(u32, 0xABCDEF12) }); + + // RevisionId is a u8 width, offset 0 + const res = device.configReadData(2, .RevisionId); + expectEqual(res, 0x12); + } + + { + const device = PciDevice{ + .bus = 0, + .device = 1, + }; + + arch.addTestParams("out", .{ CONFIG_ADDRESS, @bitCast(u32, device.getAddress(2, .ProgrammingInterface)) & 0xFFFFFFFC }); + arch.addTestParams("in", .{ CONFIG_DATA, @as(u32, 0xABCDEF12) }); + + // ProgrammingInterface is a u8 width, offset 8 + const res = device.configReadData(2, .ProgrammingInterface); + expectEqual(res, 0xEF); + } + + { + const device = PciDevice{ + .bus = 0, + .device = 1, + }; + + arch.addTestParams("out", .{ CONFIG_ADDRESS, @bitCast(u32, device.getAddress(2, .Subclass)) & 0xFFFFFFFC }); + arch.addTestParams("in", .{ CONFIG_DATA, @as(u32, 0xABCDEF12) }); + + // Subclass is a u8 width, offset 16 + const res = device.configReadData(2, .Subclass); + expectEqual(res, 0xCD); + } + + { + const device = PciDevice{ + .bus = 0, + .device = 1, + }; + + arch.addTestParams("out", .{ CONFIG_ADDRESS, @bitCast(u32, device.getAddress(2, .ClassCode)) & 0xFFFFFFFC }); + arch.addTestParams("in", .{ CONFIG_DATA, @as(u32, 0xABCDEF12) }); + + // ClassCode is a u8 width, offset 24 + const res = device.configReadData(2, .ClassCode); + expectEqual(res, 0xAB); + } + } + + test "configReadData u16" { + arch.initTest(); + defer arch.freeTest(); + + // The bus, device and function values can be any value as we are testing the shifting and masking + // Have chosen bus = 0, device = 1 and function = 2. + // We only change the register as they will have different but widths. + + { + const device = PciDevice{ + .bus = 0, + .device = 1, + }; + + arch.addTestParams("out", .{ CONFIG_ADDRESS, @bitCast(u32, device.getAddress(2, .VenderId)) & 0xFFFFFFFC }); + arch.addTestParams("in", .{ CONFIG_DATA, @as(u32, 0xABCDEF12) }); + + // VenderId is a u16 width, offset 0 + const res = device.configReadData(2, .VenderId); + expectEqual(res, 0xEF12); + } + + { + const device = PciDevice{ + .bus = 0, + .device = 1, + }; + + arch.addTestParams("out", .{ CONFIG_ADDRESS, @bitCast(u32, device.getAddress(2, .DeviceId)) & 0xFFFFFFFC }); + arch.addTestParams("in", .{ CONFIG_DATA, @as(u32, 0xABCDEF12) }); + + // DeviceId is a u16 width, offset 16 + const res = device.configReadData(2, .DeviceId); + expectEqual(res, 0xABCD); + } + } + + test "configReadData u32" { + arch.initTest(); + defer arch.freeTest(); + + // The bus, device and function values can be any value as we are testing the shifting and masking + // Have chosen bus = 0, device = 1 and function = 2. + // We only change the register as they will have different but widths. + + { + const device = PciDevice{ + .bus = 0, + .device = 1, + }; + + arch.addTestParams("out", .{ CONFIG_ADDRESS, @bitCast(u32, device.getAddress(2, .BaseAddr0)) & 0xFFFFFFFC }); + arch.addTestParams("in", .{ CONFIG_DATA, @as(u32, 0xABCDEF12) }); + + // BaseAddr0 is a u32 width, offset 0 + const res = device.configReadData(2, .BaseAddr0); + expectEqual(res, 0xABCDEF12); + } + } +}; + +pub const PciDeviceInfo = struct { + pci_device: PciDevice, + function: u3, + vender_id: u16, + device_id: u16, + subclass: u8, + class_code: u8, + + /// The error set. + pub const Error = error{ + /// There is no functions available for the given function number for a given PCI device. + NoFunction, + }; + + pub fn create(pci_device: PciDevice, function: u3) Error!PciDeviceInfo { + const vender_id = pci_device.configReadData(function, .VenderId); + + // No function available, try the next + if (vender_id == 0xFFFF) { + return Error.NoFunction; + } + + return PciDeviceInfo{ + .pci_device = pci_device, + .function = function, + .vender_id = vender_id, + .device_id = pci_device.configReadData(function, .DeviceId), + .subclass = pci_device.configReadData(function, .Subclass), + .class_code = pci_device.configReadData(function, .ClassCode), + }; + } + + pub fn print(device: arch.Device) void { + logger.info("BUS: 0x{X}, DEV: 0x{X}, FUN: 0x{X}, VID: 0x{X}, DID: 0x{X}, SC: 0x{X}, CC: 0x{X}\n", .{ + device.pci_device.bus, + device.pci_device.device, + device.function, + device.vender_id, + device.device_id, + device.subclass, + device.class_code, + }); + } +}; + +/// +/// Get a list of all the PCI device. The returned list will needed to be freed by the caller. +/// +/// Arguments: +/// IN allocator: *Allocator - An allocator used for creating the list. +/// +/// Return: []PciDeviceInfo +/// The list of PCI devices information. +/// +/// Error: Allocator.Error +/// error.OutOfMemory - If there isn't enough memory to create the info list. +/// +pub fn getDevices(allocator: *Allocator) Allocator.Error![]PciDeviceInfo { + // Create an array list for the devices. + var pci_device_infos = ArrayList(PciDeviceInfo).init(allocator); + defer pci_device_infos.deinit(); + + // Iterate through all the possible devices + var _bus: u32 = 0; + while (_bus < 8) : (_bus += 1) { + const bus = @intCast(u8, _bus); + var _device: u32 = 0; + while (_device < 32) : (_device += 1) { + const device = @intCast(u5, _device); + // Devices have at least 1 function + const pci_device = PciDevice{ + .bus = bus, + .device = device, + }; + var num_functions: u32 = if (pci_device.configReadData(0, .HeaderType) & 0x80 != 0) 8 else 1; + var _function: u32 = 0; + while (_function < num_functions) : (_function += 1) { + const function = @intCast(u3, _function); + const device_info = PciDeviceInfo.create(pci_device, function) catch |e| switch (e) { + error.NoFunction => continue, + }; + + try pci_device_infos.append(device_info); + } + } + } + + return pci_device_infos.toOwnedSlice(); +} diff --git a/src/kernel/arch/x86/pic.zig b/src/kernel/arch/x86/pic.zig index 3003bd8..ecdd0af 100644 --- a/src/kernel/arch/x86/pic.zig +++ b/src/kernel/arch/x86/pic.zig @@ -252,7 +252,7 @@ var spurious_irq_counter: u32 = 0; /// IN cmd: u8 - The command to send. /// inline fn sendCommandMaster(cmd: u8) void { - arch.outb(MASTER_COMMAND_REG, cmd); + arch.out(MASTER_COMMAND_REG, cmd); } /// @@ -262,7 +262,7 @@ inline fn sendCommandMaster(cmd: u8) void { /// IN cmd: u8 - The command to send. /// inline fn sendCommandSlave(cmd: u8) void { - arch.outb(SLAVE_COMMAND_REG, cmd); + arch.out(SLAVE_COMMAND_REG, cmd); } /// @@ -272,7 +272,7 @@ inline fn sendCommandSlave(cmd: u8) void { /// IN data: u8 - The data to send. /// inline fn sendDataMaster(data: u8) void { - arch.outb(MASTER_DATA_REG, data); + arch.out(MASTER_DATA_REG, data); } /// @@ -282,7 +282,7 @@ inline fn sendDataMaster(data: u8) void { /// IN data: u8 - The data to send. /// inline fn sendDataSlave(data: u8) void { - arch.outb(SLAVE_DATA_REG, data); + arch.out(SLAVE_DATA_REG, data); } /// @@ -292,7 +292,7 @@ inline fn sendDataSlave(data: u8) void { /// The data that is stored in the master data register. /// inline fn readDataMaster() u8 { - return arch.inb(MASTER_DATA_REG); + return arch.in(u8, MASTER_DATA_REG); } /// @@ -302,7 +302,7 @@ inline fn readDataMaster() u8 { /// The data that is stored in the salve data register. /// inline fn readDataSlave() u8 { - return arch.inb(SLAVE_DATA_REG); + return arch.in(u8, SLAVE_DATA_REG); } /// @@ -313,7 +313,7 @@ inline fn readDataSlave() u8 { /// inline fn readMasterIrr() u8 { sendCommandMaster(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_IRR); - return arch.inb(MASTER_STATUS_REG); + return arch.in(u8, MASTER_STATUS_REG); } /// @@ -324,7 +324,7 @@ inline fn readMasterIrr() u8 { /// inline fn readSlaveIrr() u8 { sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_IRR); - return arch.inb(SLAVE_STATUS_REG); + return arch.in(u8, SLAVE_STATUS_REG); } /// @@ -335,7 +335,7 @@ inline fn readSlaveIrr() u8 { /// inline fn readMasterIsr() u8 { sendCommandMaster(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR); - return arch.inb(MASTER_STATUS_REG); + return arch.in(u8, MASTER_STATUS_REG); } /// @@ -346,7 +346,7 @@ inline fn readMasterIsr() u8 { /// inline fn readSlaveIsr() u8 { sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR); - return arch.inb(SLAVE_STATUS_REG); + return arch.in(u8, SLAVE_STATUS_REG); } /// @@ -410,8 +410,8 @@ pub fn spuriousIrq(irq_num: u8) bool { pub fn setMask(irq_num: u8) void { const port: u16 = if (irq_num < 8) MASTER_DATA_REG else SLAVE_DATA_REG; const shift = @intCast(u3, irq_num % 8); - const value: u8 = arch.inb(port) | (@as(u8, 1) << shift); - arch.outb(port, value); + const value: u8 = arch.in(u8, port) | (@as(u8, 1) << shift); + arch.out(port, value); } /// @@ -423,8 +423,8 @@ pub fn setMask(irq_num: u8) void { pub fn clearMask(irq_num: u8) void { const port: u16 = if (irq_num < 8) MASTER_DATA_REG else SLAVE_DATA_REG; const shift = @intCast(u3, irq_num % 8); - const value: u8 = arch.inb(port) & ~(@as(u8, 1) << shift); - arch.outb(port, value); + const value: u8 = arch.in(u8, port) & ~(@as(u8, 1) << shift); + arch.out(port, value); } /// @@ -481,7 +481,7 @@ test "sendCommandMaster" { const cmd: u8 = 10; - arch.addTestParams("outb", .{ MASTER_COMMAND_REG, cmd }); + arch.addTestParams("out", .{ MASTER_COMMAND_REG, cmd }); sendCommandMaster(cmd); } @@ -493,7 +493,7 @@ test "sendCommandSlave" { const cmd: u8 = 10; - arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, cmd }); + arch.addTestParams("out", .{ SLAVE_COMMAND_REG, cmd }); sendCommandSlave(cmd); } @@ -505,7 +505,7 @@ test "sendDataMaster" { const data: u8 = 10; - arch.addTestParams("outb", .{ MASTER_DATA_REG, data }); + arch.addTestParams("out", .{ MASTER_DATA_REG, data }); sendDataMaster(data); } @@ -517,7 +517,7 @@ test "sendDataSlave" { const data: u8 = 10; - arch.addTestParams("outb", .{ SLAVE_DATA_REG, data }); + arch.addTestParams("out", .{ SLAVE_DATA_REG, data }); sendDataSlave(data); } @@ -527,7 +527,7 @@ test "readDataMaster" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 10) }); + arch.addTestParams("in", .{ MASTER_DATA_REG, @as(u8, 10) }); expectEqual(@as(u8, 10), readDataMaster()); } @@ -537,7 +537,7 @@ test "readDataSlave" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("inb", .{ SLAVE_DATA_REG, @as(u8, 10) }); + arch.addTestParams("in", .{ SLAVE_DATA_REG, @as(u8, 10) }); expectEqual(@as(u8, 10), readDataSlave()); } @@ -547,8 +547,8 @@ test "readMasterIrr" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ MASTER_COMMAND_REG, @as(u8, 0x0A) }); - arch.addTestParams("inb", .{ MASTER_STATUS_REG, @as(u8, 10) }); + arch.addTestParams("out", .{ MASTER_COMMAND_REG, @as(u8, 0x0A) }); + arch.addTestParams("in", .{ MASTER_STATUS_REG, @as(u8, 10) }); expectEqual(@as(u8, 10), readMasterIrr()); } @@ -558,8 +558,8 @@ test "readSlaveIrr" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, @as(u8, 0x0A) }); - arch.addTestParams("inb", .{ SLAVE_STATUS_REG, @as(u8, 10) }); + arch.addTestParams("out", .{ SLAVE_COMMAND_REG, @as(u8, 0x0A) }); + arch.addTestParams("in", .{ SLAVE_STATUS_REG, @as(u8, 10) }); expectEqual(@as(u8, 10), readSlaveIrr()); } @@ -569,8 +569,8 @@ test "readMasterIsr" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ MASTER_COMMAND_REG, @as(u8, 0x0B) }); - arch.addTestParams("inb", .{ MASTER_STATUS_REG, @as(u8, 10) }); + arch.addTestParams("out", .{ MASTER_COMMAND_REG, @as(u8, 0x0B) }); + arch.addTestParams("in", .{ MASTER_STATUS_REG, @as(u8, 10) }); expectEqual(@as(u8, 10), readMasterIsr()); } @@ -580,8 +580,8 @@ test "readSlaveIsr" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, @as(u8, 0x0B) }); - arch.addTestParams("inb", .{ SLAVE_STATUS_REG, @as(u8, 10) }); + arch.addTestParams("out", .{ SLAVE_COMMAND_REG, @as(u8, 0x0B) }); + arch.addTestParams("in", .{ SLAVE_STATUS_REG, @as(u8, 10) }); expectEqual(@as(u8, 10), readSlaveIsr()); } @@ -593,7 +593,7 @@ test "sendEndOfInterrupt master only" { var i: u8 = 0; while (i < 8) : (i += 1) { - arch.addTestParams("outb", .{ MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT }); + arch.addTestParams("out", .{ MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT }); sendEndOfInterrupt(i); } @@ -606,8 +606,8 @@ test "sendEndOfInterrupt master and slave" { var i: u8 = 8; while (i < 16) : (i += 1) { - arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, OCW2_END_OF_INTERRUPT }); - arch.addTestParams("outb", .{ MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT }); + arch.addTestParams("out", .{ SLAVE_COMMAND_REG, OCW2_END_OF_INTERRUPT }); + arch.addTestParams("out", .{ MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT }); sendEndOfInterrupt(i); } @@ -636,9 +636,9 @@ test "spuriousIrq spurious master IRQ number not spurious" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ MASTER_COMMAND_REG, @as(u8, 0x0B) }); + arch.addTestParams("out", .{ MASTER_COMMAND_REG, @as(u8, 0x0B) }); // Return 0x80 from readMasterIsr() which will mean this was a real IRQ - arch.addTestParams("inb", .{ MASTER_STATUS_REG, @as(u8, 0x80) }); + arch.addTestParams("in", .{ MASTER_STATUS_REG, @as(u8, 0x80) }); // Pre testing expectEqual(@as(u32, 0), spurious_irq_counter); @@ -658,9 +658,9 @@ test "spuriousIrq spurious master IRQ number spurious" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ MASTER_COMMAND_REG, @as(u8, 0x0B) }); + arch.addTestParams("out", .{ MASTER_COMMAND_REG, @as(u8, 0x0B) }); // Return 0x0 from readMasterIsr() which will mean this was a spurious IRQ - arch.addTestParams("inb", .{ MASTER_STATUS_REG, @as(u8, 0x0) }); + arch.addTestParams("in", .{ MASTER_STATUS_REG, @as(u8, 0x0) }); // Pre testing expectEqual(@as(u32, 0), spurious_irq_counter); @@ -680,9 +680,9 @@ test "spuriousIrq spurious slave IRQ number not spurious" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, @as(u8, 0x0B) }); + arch.addTestParams("out", .{ SLAVE_COMMAND_REG, @as(u8, 0x0B) }); // Return 0x80 from readSlaveIsr() which will mean this was a real IRQ - arch.addTestParams("inb", .{ SLAVE_STATUS_REG, @as(u8, 0x80) }); + arch.addTestParams("in", .{ SLAVE_STATUS_REG, @as(u8, 0x80) }); // Pre testing expectEqual(@as(u32, 0), spurious_irq_counter); @@ -702,11 +702,11 @@ test "spuriousIrq spurious slave IRQ number spurious" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, @as(u8, 0x0B) }); + arch.addTestParams("out", .{ SLAVE_COMMAND_REG, @as(u8, 0x0B) }); // Return 0x0 from readSlaveIsr() which will mean this was a spurious IRQ - arch.addTestParams("inb", .{ SLAVE_STATUS_REG, @as(u8, 0x0) }); + arch.addTestParams("in", .{ SLAVE_STATUS_REG, @as(u8, 0x0) }); // A EOI will be sent for a spurious IRQ 15 - arch.addTestParams("outb", .{ MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT }); + arch.addTestParams("out", .{ MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT }); // Pre testing expectEqual(@as(u32, 0), spurious_irq_counter); @@ -727,9 +727,9 @@ test "setMask master IRQ masked" { defer arch.freeTest(); // Going to assume all bits are masked out - arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFF) }); + arch.addTestParams("in", .{ MASTER_DATA_REG, @as(u8, 0xFF) }); // Expect the 2nd bit to be set - arch.addTestParams("outb", .{ MASTER_DATA_REG, @as(u8, 0xFF) }); + arch.addTestParams("out", .{ MASTER_DATA_REG, @as(u8, 0xFF) }); setMask(1); } @@ -740,9 +740,9 @@ test "setMask master IRQ unmasked" { defer arch.freeTest(); // IRQ already unmasked - arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFD) }); + arch.addTestParams("in", .{ MASTER_DATA_REG, @as(u8, 0xFD) }); // Expect the 2nd bit to be set - arch.addTestParams("outb", .{ MASTER_DATA_REG, @as(u8, 0xFF) }); + arch.addTestParams("out", .{ MASTER_DATA_REG, @as(u8, 0xFF) }); setMask(1); } @@ -753,9 +753,9 @@ test "clearMask master IRQ masked" { defer arch.freeTest(); // Going to assume all bits are masked out - arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFF) }); + arch.addTestParams("in", .{ MASTER_DATA_REG, @as(u8, 0xFF) }); // Expect the 2nd bit to be clear - arch.addTestParams("outb", .{ MASTER_DATA_REG, @as(u8, 0xFD) }); + arch.addTestParams("out", .{ MASTER_DATA_REG, @as(u8, 0xFD) }); clearMask(1); } @@ -766,9 +766,9 @@ test "clearMask master IRQ unmasked" { defer arch.freeTest(); // IRQ already unmasked - arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFD) }); + arch.addTestParams("in", .{ MASTER_DATA_REG, @as(u8, 0xFD) }); // Expect the 2nd bit to still be clear - arch.addTestParams("outb", .{ MASTER_DATA_REG, @as(u8, 0xFD) }); + arch.addTestParams("out", .{ MASTER_DATA_REG, @as(u8, 0xFD) }); clearMask(1); } @@ -781,7 +781,7 @@ test "init" { arch.addRepeatFunction("ioWait", arch.mock_ioWait); // Just a long list of OUT instructions setting up the PIC - arch.addTestParams("outb", .{ + arch.addTestParams("out", .{ MASTER_COMMAND_REG, ICW1_INITIALISATION | ICW1_EXPECT_ICW4, SLAVE_COMMAND_REG, @@ -806,7 +806,7 @@ test "init" { @as(u8, 0xFB), }); - arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFF) }); + arch.addTestParams("in", .{ MASTER_DATA_REG, @as(u8, 0xFF) }); init(); } diff --git a/src/kernel/arch/x86/pit.zig b/src/kernel/arch/x86/pit.zig index a1ec3f5..286508f 100644 --- a/src/kernel/arch/x86/pit.zig +++ b/src/kernel/arch/x86/pit.zig @@ -199,7 +199,7 @@ var time_under_1_ns: u32 = undefined; /// IN cmd: u8 - The command to send to the PIT. /// inline fn sendCommand(cmd: u8) void { - arch.outb(COMMAND_REGISTER, cmd); + arch.out(COMMAND_REGISTER, cmd); } /// @@ -213,7 +213,7 @@ inline fn sendCommand(cmd: u8) void { /// inline fn readBackCommand(counter: CounterSelect) u8 { sendCommand(0xC2); - return 0x3F & arch.inb(counter.getRegister()); + return 0x3F & arch.in(u8, counter.getRegister()); } /// @@ -224,7 +224,7 @@ inline fn readBackCommand(counter: CounterSelect) u8 { /// IN data: u8 - The data to send. /// inline fn sendDataToCounter(counter: CounterSelect, data: u8) void { - arch.outb(counter.getRegister(), data); + arch.out(counter.getRegister(), data); } /// @@ -396,7 +396,7 @@ test "sendCommand" { const cmd: u8 = 10; - arch.addTestParams("outb", .{ COMMAND_REGISTER, cmd }); + arch.addTestParams("out", .{ COMMAND_REGISTER, cmd }); sendCommand(cmd); } @@ -407,8 +407,8 @@ test "readBackCommand" { const cmd: u8 = 0xC2; - arch.addTestParams("outb", .{ COMMAND_REGISTER, cmd }); - arch.addTestParams("inb", .{ COUNTER_0_REGISTER, @as(u8, 0x20) }); + arch.addTestParams("out", .{ COMMAND_REGISTER, cmd }); + arch.addTestParams("in", .{ COUNTER_0_REGISTER, @as(u8, 0x20) }); const actual = readBackCommand(CounterSelect.Counter0); @@ -421,7 +421,7 @@ test "sendDataToCounter" { const data: u8 = 10; - arch.addTestParams("outb", .{ COUNTER_0_REGISTER, data }); + arch.addTestParams("out", .{ COUNTER_0_REGISTER, data }); sendDataToCounter(CounterSelect.Counter0, data); } @@ -445,7 +445,7 @@ test "setupCounter lowest frequency" { const command = mode | OCW_READ_LOAD_DATA | counter.getCounterOCW(); while (freq <= 18) : (freq += 1) { - // arch.addTestParams("outb", COMMAND_REGISTER, command, port, @truncate(u8, expected_reload_value), port, @truncate(u8, expected_reload_value >> 8)); + // arch.addTestParams("out", COMMAND_REGISTER, command, port, @truncate(u8, expected_reload_value), port, @truncate(u8, expected_reload_value >> 8)); expectError(PitError.InvalidFrequency, setupCounter(counter, freq, mode)); // expectEqual(u32(0), ticks); @@ -482,7 +482,7 @@ test "setupCounter highest frequency" { const mode = OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_BINARY_COUNT_BINARY; const command = mode | OCW_READ_LOAD_DATA | counter.getCounterOCW(); - // arch.addTestParams("outb", COMMAND_REGISTER, command, port, @truncate(u8, expected_reload_value), port, @truncate(u8, expected_reload_value >> 8)); + // arch.addTestParams("out", COMMAND_REGISTER, command, port, @truncate(u8, expected_reload_value), port, @truncate(u8, expected_reload_value >> 8)); expectError(PitError.InvalidFrequency, setupCounter(counter, freq, mode)); @@ -515,7 +515,7 @@ test "setupCounter normal frequency" { const mode = OCW_MODE_SQUARE_WAVE_GENERATOR | OCW_BINARY_COUNT_BINARY; const command = mode | OCW_READ_LOAD_DATA | counter.getCounterOCW(); - arch.addTestParams("outb", .{ COMMAND_REGISTER, command, port, @truncate(u8, expected_reload_value), port, @truncate(u8, expected_reload_value >> 8) }); + arch.addTestParams("out", .{ COMMAND_REGISTER, command, port, @truncate(u8, expected_reload_value), port, @truncate(u8, expected_reload_value >> 8) }); setupCounter(counter, freq, mode) catch unreachable; diff --git a/src/kernel/arch/x86/serial.zig b/src/kernel/arch/x86/serial.zig index 5163571..4b25ba1 100644 --- a/src/kernel/arch/x86/serial.zig +++ b/src/kernel/arch/x86/serial.zig @@ -93,7 +93,7 @@ fn baudDivisor(baud: u32) SerialError!u16 { /// If the transmission buffer is empty. /// fn transmitIsEmpty(port: Port) bool { - return arch.inb(@enumToInt(port) + 5) & 0x20 > 0; + return arch.in(u8, @enumToInt(port) + 5) & 0x20 > 0; } /// @@ -107,7 +107,7 @@ pub fn write(char: u8, port: Port) void { while (!transmitIsEmpty(port)) { arch.halt(); } - arch.outb(@enumToInt(port), char); + arch.out(@enumToInt(port), char); } /// @@ -125,19 +125,19 @@ pub fn init(baud: u32, port: Port) SerialError!void { const divisor: u16 = try baudDivisor(baud); const port_int = @enumToInt(port); // Send a byte to start setting the baudrate - arch.outb(port_int + LCR, lcrValue(0, false, false, 1) catch |e| { + arch.out(port_int + LCR, lcrValue(0, false, false, 1) catch |e| { panic(@errorReturnTrace(), "Failed to initialise serial output setup: {}", .{e}); }); // Send the divisor's lsb - arch.outb(port_int, @truncate(u8, divisor)); + arch.out(port_int, @truncate(u8, divisor)); // Send the divisor's msb - arch.outb(port_int + 1, @truncate(u8, divisor >> 8)); + arch.out(port_int + 1, @truncate(u8, divisor >> 8)); // Send the properties to use - arch.outb(port_int + LCR, lcrValue(CHAR_LEN, SINGLE_STOP_BIT, PARITY_BIT, 0) catch |e| { + arch.out(port_int + LCR, lcrValue(CHAR_LEN, SINGLE_STOP_BIT, PARITY_BIT, 0) catch |e| { panic(@errorReturnTrace(), "Failed to setup serial properties: {}", .{e}); }); // Stop initialisation - arch.outb(port_int + 1, 0); + arch.out(port_int + 1, @as(u8, 0)); } test "lcrValue computes the correct value" { diff --git a/src/kernel/arch/x86/vga.zig b/src/kernel/arch/x86/vga.zig index 1b840b5..4b4fd27 100644 --- a/src/kernel/arch/x86/vga.zig +++ b/src/kernel/arch/x86/vga.zig @@ -122,7 +122,7 @@ var cursor_scanline_end: u8 = undefined; /// to. /// inline fn sendPort(index: u8) void { - arch.outb(PORT_ADDRESS, index); + arch.out(PORT_ADDRESS, index); } /// @@ -132,7 +132,7 @@ inline fn sendPort(index: u8) void { /// IN data: u8 - The data to send to the selected register. /// inline fn sendData(data: u8) void { - arch.outb(PORT_DATA, data); + arch.out(PORT_DATA, data); } /// @@ -142,7 +142,7 @@ inline fn sendData(data: u8) void { /// The data in the selected register. /// inline fn getData() u8 { - return arch.inb(PORT_DATA); + return arch.in(u8, PORT_DATA); } /// /// Set the VGA register port to write to and sending data to that VGA register port. @@ -345,7 +345,7 @@ test "updateCursor width out of bounds" { defer arch.freeTest(); // Mocking out the arch.outb calls for changing the hardware cursor: - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); updateCursor(x, y); } @@ -362,7 +362,7 @@ test "updateCursor height out of bounds" { defer arch.freeTest(); // Mocking out the arch.outb calls for changing the hardware cursor: - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); updateCursor(x, y); } @@ -379,7 +379,7 @@ test "updateCursor width and height out of bounds" { defer arch.freeTest(); // Mocking out the arch.outb calls for changing the hardware cursor: - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); updateCursor(x, y); } @@ -396,7 +396,7 @@ test "updateCursor width-1 and height out of bounds" { defer arch.freeTest(); // Mocking out the arch.outb calls for changing the hardware cursor: - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); updateCursor(x, y); } @@ -413,7 +413,7 @@ test "updateCursor width and height-1 out of bounds" { defer arch.freeTest(); // Mocking out the arch.outb calls for changing the hardware cursor: - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); updateCursor(x, y); } @@ -430,7 +430,7 @@ test "updateCursor in bounds" { defer arch.freeTest(); // Mocking out the arch.outb calls for changing the hardware cursor: - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW, PORT_DATA, expected_lower, PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH, PORT_DATA, expected_upper }); updateCursor(x, y); } @@ -441,10 +441,10 @@ test "getCursor 1: 10" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW }); - arch.addTestParams("inb", .{ PORT_DATA, @as(u8, 10) }); - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH }); - arch.addTestParams("inb", .{ PORT_DATA, @as(u8, 0) }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW }); + arch.addTestParams("in", .{ PORT_DATA, @as(u8, 10) }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH }); + arch.addTestParams("in", .{ PORT_DATA, @as(u8, 0) }); const actual = getCursor(); expectEqual(expect, actual); @@ -457,10 +457,10 @@ test "getCursor 2: 0xBEEF" { arch.initTest(); defer arch.freeTest(); - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW }); - arch.addTestParams("inb", .{ PORT_DATA, @as(u8, 0xEF) }); - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH }); - arch.addTestParams("inb", .{ PORT_DATA, @as(u8, 0xBE) }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_LOW }); + arch.addTestParams("in", .{ PORT_DATA, @as(u8, 0xEF) }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_LOCATION_HIGH }); + arch.addTestParams("in", .{ PORT_DATA, @as(u8, 0xBE) }); const actual = getCursor(); expectEqual(expect, actual); @@ -471,7 +471,7 @@ test "enableCursor" { defer arch.freeTest(); // Need to init the cursor start and end positions, so call the init() to set this up - arch.addTestParams("outb", .{ + arch.addTestParams("out", .{ PORT_ADDRESS, REG_MAXIMUM_SCAN_LINE, PORT_DATA, CURSOR_SCANLINE_END, PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END, // Mocking out the arch.outb calls for enabling the cursor: // These are the default cursor positions from init() @@ -487,7 +487,7 @@ test "disableCursor" { defer arch.freeTest(); // Mocking out the arch.outb calls for disabling the cursor: - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_DISABLE }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_DISABLE }); disableCursor(); } @@ -498,7 +498,7 @@ test "setCursorShape UNDERLINE" { // Mocking out the arch.outb calls for setting the cursor shape to underline: // This will also check that the scan line variables were set properly as these are using in // the arch.outb call - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END }); setCursorShape(CursorShape.UNDERLINE); } @@ -510,7 +510,7 @@ test "setCursorShape BLOCK" { // Mocking out the arch.outb calls for setting the cursor shape to block: // This will also check that the scan line variables were set properly as these are using in // the arch.outb call - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_START, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_START, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END }); setCursorShape(CursorShape.BLOCK); } @@ -522,7 +522,7 @@ test "init" { // Mocking out the arch.outb calls for setting the cursor max scan line and the shape to block: // This will also check that the scan line variables were set properly as these are using in // the arch.outb call for setting the cursor shape. - arch.addTestParams("outb", .{ PORT_ADDRESS, REG_MAXIMUM_SCAN_LINE, PORT_DATA, CURSOR_SCANLINE_END, PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END }); + arch.addTestParams("out", .{ PORT_ADDRESS, REG_MAXIMUM_SCAN_LINE, PORT_DATA, CURSOR_SCANLINE_END, PORT_ADDRESS, REG_CURSOR_START, PORT_DATA, CURSOR_SCANLINE_MIDDLE, PORT_ADDRESS, REG_CURSOR_END, PORT_DATA, CURSOR_SCANLINE_END }); init(); } diff --git a/src/kernel/heap.zig b/src/kernel/heap.zig index f6699c8..7285e6d 100644 --- a/src/kernel/heap.zig +++ b/src/kernel/heap.zig @@ -9,7 +9,7 @@ const mock_path = build_options.mock_path; const vmm = if (is_test) @import(mock_path ++ "vmm_mock.zig") else @import("vmm.zig"); const panic = @import("panic.zig").panic; -const FreeListAllocator = struct { +pub const FreeListAllocator = struct { const Error = error{TooSmall}; const Header = struct { size: usize, diff --git a/src/kernel/kmain.zig b/src/kernel/kmain.zig index 6c0d75b..1bf08a0 100644 --- a/src/kernel/kmain.zig +++ b/src/kernel/kmain.zig @@ -19,6 +19,7 @@ const scheduler = @import("scheduler.zig"); const vfs = @import("filesystem/vfs.zig"); const initrd = @import("filesystem/initrd.zig"); const keyboard = @import("keyboard.zig"); +const Allocator = std.mem.Allocator; comptime { if (!is_test) { @@ -57,6 +58,8 @@ pub fn log( log_root.log(level, "(" ++ @tagName(scope) ++ "): " ++ format, args); } +var kernel_heap: heap.FreeListAllocator = undefined; + export fn kmain(boot_payload: arch.BootPayload) void { const serial_stream = serial.init(boot_payload); @@ -86,7 +89,7 @@ export fn kmain(boot_payload: arch.BootPayload) void { if (!std.math.isPowerOfTwo(heap_size)) { heap_size = std.math.floorPowerOfTwo(usize, heap_size); } - var kernel_heap = heap.init(arch.VmmPayload, kernel_vmm, vmm.Attributes{ .kernel = true, .writable = true, .cachable = true }, heap_size) catch |e| { + kernel_heap = heap.init(arch.VmmPayload, kernel_vmm, vmm.Attributes{ .kernel = true, .writable = true, .cachable = true }, heap_size) catch |e| { panic_root.panic(@errorReturnTrace(), "Failed to initialise kernel heap: {}\n", .{e}); }; @@ -169,6 +172,14 @@ fn initStage2() noreturn { tty.print("Hello Pluto from kernel :)\n", .{}); + const devices = arch.getDevices(&kernel_heap.allocator) catch |e| { + panic_root.panic(@errorReturnTrace(), "Unable to get device list: {}\n", .{e}); + }; + + for (devices) |device| { + device.print(); + } + switch (build_options.test_mode) { .Initialisation => { logger.info("SUCCESS\n", .{}); diff --git a/test/gen_types.zig b/test/gen_types.zig index 8432b04..62fecb6 100644 --- a/test/gen_types.zig +++ b/test/gen_types.zig @@ -63,9 +63,9 @@ const types = .{ .{ "fn (u8) void", "FN_IU8_OVOID", "", "", "" }, .{ "fn (u8) bool", "FN_IU8_OBOOL", "", "", "" }, - .{ "fn (u8, fn () callconv(.Naked) void) IdtError!void", "FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID", "", "", "" }, .{ "fn (u16) void", "FN_IU16_OVOID", "", "", "" }, .{ "fn (u16) u8", "FN_IU16_OU8", "", "", "" }, + .{ "fn (u16) u32", "FN_IU16_OU32", "", "", "" }, .{ "fn (usize) bool", "FN_IUSIZE_OBOOL", "", "", "" }, .{ "fn (RtcRegister) u8", "FN_IRTCREGISTER_OU8", "", "", "" }, .{ "fn (IdtEntry) bool", "FN_IIDTENTRY_OBOOL", "idt_mock", "", "IdtEntry" }, @@ -74,8 +74,10 @@ const types = .{ .{ "fn (u4, u4) u8", "FN_IU4_IU4_OU8", "", "", "" }, .{ "fn (u8, u8) u16", "FN_IU8_IU8_OU16", "", "", "" }, + .{ "fn (u8, fn () callconv(.Naked) void) IdtError!void", "FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID", "", "", "" }, .{ "fn (u16, u8) void", "FN_IU16_IU8_OVOID", "", "", "" }, .{ "fn (u16, u16) void", "FN_IU16_IU16_OVOID", "", "", "" }, + .{ "fn (u16, u32) void", "FN_IU16_IU32_OVOID", "", "", "" }, .{ "fn (StatusRegister, bool) u8", "FN_ISTATUSREGISTER_IBOOL_OU8", "", "", "" }, .{ "fn (*Task, usize) void", "FN_IPTRTASK_IUSIZE_OVOID", "", "", "" }, .{ "fn (*Task, *Allocator) void", "FN_IPTRTASK_IPTRALLOCATOR_OVOID", "", "", "" }, diff --git a/test/mock/kernel/arch_mock.zig b/test/mock/kernel/arch_mock.zig index b8e36df..a4fcd72 100644 --- a/test/mock/kernel/arch_mock.zig +++ b/test/mock/kernel/arch_mock.zig @@ -2,6 +2,7 @@ const std = @import("std"); const Allocator = std.mem.Allocator; const mem = @import("mem_mock.zig"); const MemProfile = mem.MemProfile; +const pci = @import("pci_mock.zig"); const gdt = @import("gdt_mock.zig"); const idt = @import("idt_mock.zig"); const vmm = @import("vmm_mock.zig"); @@ -12,6 +13,8 @@ const Keyboard = @import("../../../src/kernel/keyboard.zig").Keyboard; pub const task = @import("task_mock.zig"); +pub const Device = pci.PciDeviceInfo; + const mock_framework = @import("mock_framework.zig"); pub const initTest = mock_framework.initTest; pub const freeTest = mock_framework.freeTest; @@ -57,12 +60,12 @@ var KERNEL_VADDR_START: u32 = 0xC0100000; var KERNEL_VADDR_END: u32 = 0xC1100000; var KERNEL_ADDR_OFFSET: u32 = 0xC0000000; -pub fn outb(port: u16, data: u8) void { - return mock_framework.performAction("outb", void, .{ port, data }); +pub fn out(port: u16, data: anytype) void { + return mock_framework.performAction("out", void, .{ port, data }); } -pub fn inb(port: u16) u8 { - return mock_framework.performAction("inb", u8, .{port}); +pub fn in(comptime Type: type, port: u16) Type { + return mock_framework.performAction("in", Type, .{port}); } pub fn ioWait() void { @@ -147,6 +150,10 @@ pub fn initKeyboard(allocator: *Allocator) Allocator.Error!?*Keyboard { return null; } +pub fn getDevices(allocator: *Allocator) Allocator.Error![]Device { + return &[_]Device{}; +} + pub fn init(mem_profile: *const MemProfile) void { // I'll get back to this as this doesn't effect the current testing. // When I come on to the mem.zig testing, I'll fix :) diff --git a/test/mock/kernel/mock_framework.zig b/test/mock/kernel/mock_framework.zig index 10ef082..789bf31 100644 --- a/test/mock/kernel/mock_framework.zig +++ b/test/mock/kernel/mock_framework.zig @@ -46,9 +46,9 @@ const DataElementType = enum { FN_OIDTPTR, FN_IU8_OVOID, FN_IU8_OBOOL, - FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID, FN_IU16_OVOID, FN_IU16_OU8, + FN_IU16_OU32, FN_IUSIZE_OBOOL, FN_IRTCREGISTER_OU8, FN_IIDTENTRY_OBOOL, @@ -56,8 +56,10 @@ const DataElementType = enum { FN_IPTRCONSTIDTPTR_OVOID, FN_IU4_IU4_OU8, FN_IU8_IU8_OU16, + FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID, FN_IU16_IU8_OVOID, FN_IU16_IU16_OVOID, + FN_IU16_IU32_OVOID, FN_ISTATUSREGISTER_IBOOL_OU8, FN_IPTRTASK_IUSIZE_OVOID, FN_IPTRTASK_IPTRALLOCATOR_OVOID, @@ -98,9 +100,9 @@ const DataElement = union(DataElementType) { FN_OIDTPTR: fn () IdtPtr, FN_IU8_OVOID: fn (u8) void, FN_IU8_OBOOL: fn (u8) bool, - FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID: fn (u8, fn () callconv(.Naked) void) IdtError!void, FN_IU16_OVOID: fn (u16) void, FN_IU16_OU8: fn (u16) u8, + FN_IU16_OU32: fn (u16) u32, FN_IUSIZE_OBOOL: fn (usize) bool, FN_IRTCREGISTER_OU8: fn (RtcRegister) u8, FN_IIDTENTRY_OBOOL: fn (IdtEntry) bool, @@ -108,8 +110,10 @@ const DataElement = union(DataElementType) { FN_IPTRCONSTIDTPTR_OVOID: fn (*const IdtPtr) void, FN_IU4_IU4_OU8: fn (u4, u4) u8, FN_IU8_IU8_OU16: fn (u8, u8) u16, + FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID: fn (u8, fn () callconv(.Naked) void) IdtError!void, FN_IU16_IU8_OVOID: fn (u16, u8) void, FN_IU16_IU16_OVOID: fn (u16, u16) void, + FN_IU16_IU32_OVOID: fn (u16, u32) void, FN_ISTATUSREGISTER_IBOOL_OU8: fn (StatusRegister, bool) u8, FN_IPTRTASK_IUSIZE_OVOID: fn (*Task, usize) void, FN_IPTRTASK_IPTRALLOCATOR_OVOID: fn (*Task, *Allocator) void, @@ -214,9 +218,9 @@ fn Mock() type { fn () IdtPtr => DataElement{ .FN_OIDTPTR = arg }, fn (u8) void => DataElement{ .FN_IU8_OVOID = arg }, fn (u8) bool => DataElement{ .FN_IU8_OBOOL = arg }, - fn (u8, fn () callconv(.Naked) void) IdtError!void => DataElement{ .FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID = arg }, fn (u16) void => DataElement{ .FN_IU16_OVOID = arg }, fn (u16) u8 => DataElement{ .FN_IU16_OU8 = arg }, + fn (u16) u32 => DataElement{ .FN_IU16_OU32 = arg }, fn (usize) bool => DataElement{ .FN_IUSIZE_OBOOL = arg }, fn (RtcRegister) u8 => DataElement{ .FN_IRTCREGISTER_OU8 = arg }, fn (IdtEntry) bool => DataElement{ .FN_IIDTENTRY_OBOOL = arg }, @@ -224,8 +228,10 @@ fn Mock() type { fn (*const IdtPtr) void => DataElement{ .FN_IPTRCONSTIDTPTR_OVOID = arg }, fn (u4, u4) u8 => DataElement{ .FN_IU4_IU4_OU8 = arg }, fn (u8, u8) u16 => DataElement{ .FN_IU8_IU8_OU16 = arg }, + fn (u8, fn () callconv(.Naked) void) IdtError!void => DataElement{ .FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID = arg }, fn (u16, u8) void => DataElement{ .FN_IU16_IU8_OVOID = arg }, fn (u16, u16) void => DataElement{ .FN_IU16_IU16_OVOID = arg }, + fn (u16, u32) void => DataElement{ .FN_IU16_IU32_OVOID = arg }, fn (StatusRegister, bool) u8 => DataElement{ .FN_ISTATUSREGISTER_IBOOL_OU8 = arg }, fn (*Task, usize) void => DataElement{ .FN_IPTRTASK_IUSIZE_OVOID = arg }, fn (*Task, *Allocator) void => DataElement{ .FN_IPTRTASK_IPTRALLOCATOR_OVOID = arg }, @@ -270,9 +276,9 @@ fn Mock() type { fn () IdtPtr => DataElement.FN_OIDTPTR, fn (u8) void => DataElement.FN_IU8_OVOID, fn (u8) bool => DataElement.FN_IU8_OBOOL, - fn (u8, fn () callconv(.Naked) void) IdtError!void => DataElement.FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID, fn (u16) void => DataElement.FN_IU16_OVOID, fn (u16) u8 => DataElement.FN_IU16_OU8, + fn (u16) u32 => DataElement.FN_IU16_OU32, fn (usize) bool => DataElement.FN_IUSIZE_OBOOL, fn (RtcRegister) u8 => DataElement.FN_IRTCREGISTER_OU8, fn (IdtEntry) bool => DataElement.FN_IIDTENTRY_OBOOL, @@ -280,8 +286,10 @@ fn Mock() type { fn (*const IdtPtr) void => DataElement.FN_IPTRCONSTIDTPTR_OVOID, fn (u4, u4) u8 => DataElement.FN_IU4_IU4_OU8, fn (u8, u8) u16 => DataElement.FN_IU8_IU8_OU16, + fn (u8, fn () callconv(.Naked) void) IdtError!void => DataElement.FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID, fn (u16, u8) void => DataElement.FN_IU16_IU8_OVOID, fn (u16, u16) void => DataElement.FN_IU16_IU16_OVOID, + fn (u16, u32) void => DataElement.FN_IU16_IU32_OVOID, fn (StatusRegister, bool) u8 => DataElement.FN_ISTATUSREGISTER_IBOOL_OU8, fn (*Task, usize) void => DataElement.FN_IPTRTASK_IUSIZE_OVOID, fn (*Task, *Allocator) void => DataElement.FN_IPTRTASK_IPTRALLOCATOR_OVOID, @@ -328,9 +336,9 @@ fn Mock() type { fn () IdtPtr => element.FN_OIDTPTR, fn (u8) void => element.FN_IU8_OVOID, fn (u8) bool => element.FN_IU8_OBOOL, - fn (u8, fn () callconv(.Naked) void) IdtError!void => element.FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID, fn (u16) void => element.FN_IU16_OVOID, fn (u16) u8 => element.FN_IU16_OU8, + fn (u16) u32 => element.FN_IU16_OU32, fn (usize) bool => element.FN_IUSIZE_OBOOL, fn (RtcRegister) u8 => element.FN_IRTCREGISTER_OU8, fn (IdtEntry) bool => element.FN_IIDTENTRY_OBOOL, @@ -338,8 +346,10 @@ fn Mock() type { fn (*const IdtPtr) void => element.FN_IPTRCONSTIDTPTR_OVOID, fn (u4, u4) u8 => element.FN_IU4_IU4_OU8, fn (u8, u8) u16 => element.FN_IU8_IU8_OU16, + fn (u8, fn () callconv(.Naked) void) IdtError!void => element.FN_IU8_IFNCCNAKEDOVOID_EIDTERROR_OVOID, fn (u16, u8) void => element.FN_IU16_IU8_OVOID, fn (u16, u16) void => element.FN_IU16_IU16_OVOID, + fn (u16, u32) void => element.FN_IU16_IU32_OVOID, fn (StatusRegister, bool) u8 => element.FN_ISTATUSREGISTER_IBOOL_OU8, fn (*Task, usize) void => element.FN_IPTRTASK_IUSIZE_OVOID, fn (*Task, *Allocator) void => element.FN_IPTRTASK_IPTRALLOCATOR_OVOID, diff --git a/test/mock/kernel/pci_mock.zig b/test/mock/kernel/pci_mock.zig new file mode 100644 index 0000000..5760f8a --- /dev/null +++ b/test/mock/kernel/pci_mock.zig @@ -0,0 +1,108 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; +const arch = @import("arch_mock.zig"); + +const mock_framework = @import("mock_framework.zig"); +pub const initTest = mock_framework.initTest; +pub const freeTest = mock_framework.freeTest; +pub const addTestParams = mock_framework.addTestParams; +pub const addConsumeFunction = mock_framework.addConsumeFunction; +pub const addRepeatFunction = mock_framework.addRepeatFunction; + +const PciRegisters = enum(u8) { + VenderId = 0x00, + DeviceId = 0x02, + Command = 0x04, + Status = 0x06, + RevisionId = 0x08, + ProgrammingInterface = 0x09, + Subclass = 0x0A, + ClassCode = 0x0B, + CacheLineSize = 0x0C, + LatencyTimer = 0x0D, + HeaderType = 0x0E, + BIST = 0x0F, + BaseAddr0 = 0x10, + BaseAddr1 = 0x14, + BaseAddr2 = 0x18, + BaseAddr3 = 0x1C, + BaseAddr4 = 0x20, + BaseAddr5 = 0x24, + CardbusCISPtr = 0x28, + SubsystemVenderId = 0x2C, + SubsystemId = 0x2E, + ExpansionROMBaseAddr = 0x30, + CapabilitiesPtr = 0x34, + InterruptLine = 0x3C, + InterruptPin = 0x3D, + MinGrant = 0x3E, + MaxLatency = 0x3F, + + pub fn getWidth(comptime pci_reg: PciRegisters) type { + return switch (pci_reg) { + .RevisionId, .ProgrammingInterface, .Subclass, .ClassCode, .CacheLineSize, .LatencyTimer, .HeaderType, .BIST, .InterruptLine, .InterruptPin, .MinGrant, .MaxLatency, .CapabilitiesPtr => u8, + .VenderId, .DeviceId, .Command, .Status, .SubsystemVenderId, .SubsystemId => u16, + .BaseAddr0, .BaseAddr1, .BaseAddr2, .BaseAddr3, .BaseAddr4, .BaseAddr5, .CardbusCISPtr, .ExpansionROMBaseAddr => u32, + }; + } +}; + +const PciAddress = packed struct { + register_offset: u8, + function: u3, + device: u5, + bus: u8, + reserved: u7 = 0, + enable: u1 = 1, +}; + +const PciDevice = struct { + bus: u8, + device: u5, + + const Self = @This(); + + pub fn getAddress(self: Self, function: u3, comptime pci_reg: PciRegisters) PciAddress { + return PciAddress{ + .bus = self.bus, + .device = self.device, + .function = function, + .register_offset = @enumToInt(pci_reg), + }; + } + + pub fn configReadData(self: Self, function: u3, comptime pci_reg: PciRegisters) pci_reg.getWidth() { + return mock_framework.performAction("PciDevice.configReadData", pci_reg.getWidth(), .{ self, function, pci_reg }); + } +}; + +pub const PciDeviceInfo = struct { + pci_device: PciDevice, + function: u3, + vender_id: u16, + device_id: u16, + subclass: u8, + class_code: u8, + + pub const Error = error{NoFunction}; + + pub fn create(pci_device: PciDevice, function: u3) Error!PciDeviceInfo { + return mock_framework.performAction("PciDeviceInfo.create", Error!PciDeviceInfo, .{ pci_device, function }); + } + + pub fn print(device: arch.Device) void { + std.debug.print("BUS: 0x{X}, DEV: 0x{X}, FUN: 0x{X}, VID: 0x{X}, DID: 0x{X}, SC: 0x{X}, CC: 0x{X}\n", .{ + device.pci_device.bus, + device.pci_device.device, + device.function, + device.vender_id, + device.device_id, + device.subclass, + device.class_code, + }); + } +}; + +pub fn getDevices(allocator: *Allocator) Allocator.Error![]PciDeviceInfo { + return mock_framework.performAction("getDevices", Allocator.Error![]PciDeviceInfo, .{allocator}); +}