pluto/src/kernel/arch/x86/pic.zig

836 lines
23 KiB
Zig
Raw Normal View History

const std = @import("std");
const expect = std.testing.expect;
const expectEqual = std.testing.expectEqual;
const builtin = @import("builtin");
const is_test = builtin.is_test;
const build_options = @import("build_options");
const mock_path = build_options.arch_mock_path;
const arch = if (is_test) @import(mock_path ++ "arch_mock.zig") else @import("arch.zig");
const log = if (is_test) @import(mock_path ++ "log_mock.zig") else @import("../../log.zig");
const panic = if (is_test) @import(mock_path ++ "panic_mock.zig").panic else @import("../../panic.zig").panic;
// ----------
// Port address for the PIC master and slave registers.
// ----------
/// The port address for issuing a command to the master PIC. This is a write only operation.
const MASTER_COMMAND_REG: u16 = 0x20;
/// The port address for reading one of the status register of the master PIC. This can be either
/// the In-Service Register (ISR) or the Interrupt Request Register (IRR). This is a read only
/// operation.
const MASTER_STATUS_REG: u16 = 0x20;
/// The port address for reading or writing to the data register of the master PIC. This can be
/// used in conjunction with the command register to set up the PIC. This can also be used to mask
/// the interrupt lines so interrupts can be issued to the CPU.
const MASTER_DATA_REG: u16 = 0x21;
/// The port address for issuing a command to the slave PIC. This is a write only operation.
const SLAVE_COMMAND_REG: u16 = 0xA0;
/// The port address for reading one of the status register of the slave PIC. This can be either
/// the In-Service Register (ISR) or the Interrupt Request Register (IRR). This is a read only
/// operation.
const SLAVE_STATUS_REG: u16 = 0xA0;
/// The port address for reading or writing to the data register of the status PIC. This can be
/// used in conjunction with the command register to set up the PIC. This can also be used to mask
/// the interrupt lines so interrupts can be issued to the CPU.
const SLAVE_DATA_REG: u16 = 0xA1;
// ----------
// Initialisation control word 1.
// ----------
/// Initialisation control word 1. Primary control word for initialising the PIC. If set, then the
/// PIC expects to receive a initialisation control word 4.
const ICW1_EXPECT_ICW4: u8 = 0x01;
/// If set, then there is only one PIC in the system. If not set, then PIC is cascaded with slave
/// PICs and initialisation control word 3 must be sent to the controller.
const ICW1_SINGLE_CASCADE_MODE: u8 = 0x02;
/// If set, then the internal CALL address is 4. If not set, then is 8. Usually ignored by x86. So
/// default is not set, 0.
const ICW1_CALL_ADDRESS_INTERVAL_4: u8 = 0x04;
/// If set, then operating in level triggered mode. If not set, then operating in edge triggered
/// mode.
const ICW1_LEVEL_TRIGGER_MODE: u8 = 0x08;
/// If set, then the PIC is to be initialised.
const ICW1_INITIALISATION: u8 = 0x10;
// ----------
// Initialisation control word 2.
// ----------
/// Initialisation control word 2. Map the base address of the interrupt vector table. The new port
/// map for the master PIC. IRQs 0-7 mapped to use interrupts 0x20-0x27.
const ICW2_MASTER_REMAP_OFFSET: u8 = 0x20;
/// The new port map for the slave PIC. IRQs 8-15 mapped to use interrupts 0x28-0x2F.
const ICW2_SLAVE_REMAP_OFFSET: u8 = 0x28;
// ----------
// Initialisation control word 3.
// ----------
/// Initialisation control word 3. For Telling the master and slave where the cascading. interrupts
/// are coming from. Tell the slave PIT to send interrupts to the master PIC on IRQ2.
const ICW3_SLAVE_IRQ_MAP_TO_MASTER: u8 = 0x02;
/// Tell the master PIT to receive interrupts from the slave PIC on IRQ2.
const ICW3_MASTER_IRQ_MAP_FROM_SLAVE: u8 = 0x04;
// ----------
// Initialisation control word 4.
// ----------
/// Initialisation control word 4. Tell the master and slave what mode to operate in. If set, then
/// in 80x86 mode. If not set, then in MCS-80/86 mode.
const ICW4_80x86_MODE: u8 = 0x01;
/// If set, then on last interrupt acknowledge pulse the PIC automatically performs end of
/// interrupt operation.
const ICW4_AUTO_END_OF_INTERRUPT: u8 = 0x02;
/// Only use if ICW4_BUFFER_MODE is set. If set, then selects master's buffer. If not set then uses
/// slave's buffer.
const ICW4_BUFFER_SELECT: u8 = 0x04;
/// If set, then PIC operates in buffered mode.
const ICW4_BUFFER_MODE: u8 = 0x08;
/// If set, then the the system had many cascaded PICs. Not supported in x86.
const ICW4_FULLY_NESTED_MODE: u8 = 0x10;
// ----------
// Operation control word 1.
// ----------
/// Operation control word 1. Interrupt masks for IRQ0 and IRQ8.
const OCW1_MASK_IRQ0_8: u8 = 0x01;
/// Operation control word 1. Interrupt masks for IRQ1 and IRQ9.
const OCW1_MASK_IRQ1_9: u8 = 0x02;
/// Operation control word 1. Interrupt masks for IRQ2 and IRQ10.
const OCW1_MASK_IRQ2_10: u8 = 0x04;
/// Operation control word 1. Interrupt masks for IRQ3 and IRQ11.
const OCW1_MASK_IRQ3_11: u8 = 0x08;
/// Operation control word 1. Interrupt masks for IRQ4 and IRQ12.
const OCW1_MASK_IRQ4_12: u8 = 0x10;
/// Operation control word 1. Interrupt masks for IRQ5 and IRQ13.
const OCW1_MASK_IRQ5_13: u8 = 0x20;
/// Operation control word 1. Interrupt masks for IRQ6 and IRQ14.
const OCW1_MASK_IRQ6_14: u8 = 0x40;
/// Operation control word 1. Interrupt masks for IRQ7 and IRQ15.
const OCW1_MASK_IRQ7_15: u8 = 0x80;
// ----------
// Operation control word 2.
// ----------
/// Operation control word 2. Primary commands for the PIC. Interrupt level 1 upon which the
/// controller must react. Interrupt level for the current interrupt.
const OCW2_INTERRUPT_LEVEL_1: u8 = 0x01;
/// Interrupt level 2 upon which the controller must react. Interrupt level for the current
/// interrupt
const OCW2_INTERRUPT_LEVEL_2: u8 = 0x02;
/// Interrupt level 3 upon which the controller must react. Interrupt level for the current
/// interrupt
const OCW2_INTERRUPT_LEVEL_3: u8 = 0x04;
/// The end of interrupt command code.
const OCW2_END_OF_INTERRUPT: u8 = 0x20;
/// Select command.
const OCW2_SELECTION: u8 = 0x40;
/// Rotation command.
const OCW2_ROTATION: u8 = 0x80;
// ----------
// Operation control word 3.
// ----------
/// Operation control word 3.
/// Read the Interrupt Request Register register
const OCW3_READ_IRR: u8 = 0x00;
/// Read the In Service Register register.
const OCW3_READ_ISR: u8 = 0x01;
/// If set, then bit 0 will be acted on, so read ISR or IRR. If not set, then no action taken.
const OCW3_ACT_ON_READ: u8 = 0x02;
/// If set, then poll command issued. If not set, then no pool command issued.
const OCW3_POLL_COMMAND_ISSUED: u8 = 0x04;
/// This must be set for all OCW 3.
const OCW3_DEFAULT: u8 = 0x08;
// Next bit must be zero.
/// If set, then the special mask is set. If not set, then resets special mask.
const OCW3_SPECIAL_MASK: u8 = 0x20;
/// If set, then bit 5 will be acted on, so setting the special mask. If not set, then no action it
/// taken.
const OCW3_ACK_ON_SPECIAL_MASK: u8 = 0x40;
// Last bit must be zero.
// ----------
// The IRQs
// ----------
/// The IRQ for the PIT.
pub const IRQ_PIT: u8 = 0x00;
/// The IRQ for the keyboard.
pub const IRQ_KEYBOARD: u8 = 0x01;
/// The IRQ for the cascade from master to slave.
pub const IRQ_CASCADE_FOR_SLAVE: u8 = 0x02;
/// The IRQ for the serial COM2/4.
pub const IRQ_SERIAL_PORT_2: u8 = 0x03;
/// The IRQ for the serial COM1/3.
pub const IRQ_SERIAL_PORT_1: u8 = 0x04;
/// The IRQ for the parallel port 2.
pub const IRQ_PARALLEL_PORT_2: u8 = 0x05;
/// The IRQ for the floppy disk.
pub const IRQ_DISKETTE_DRIVE: u8 = 0x06;
/// The IRQ for the parallel port 1.
pub const IRQ_PARALLEL_PORT_1: u8 = 0x07;
/// The IRQ for the CMOS real time clock (RTC).
pub const IRQ_REAL_TIME_CLOCK: u8 = 0x08;
/// The IRQ for the CGA vertical retrace.
pub const IRQ_CGA_VERTICAL_RETRACE: u8 = 0x09;
/// Reserved.
pub const IRQ_RESERVED1: u8 = 0x0A;
/// Reserved.
pub const IRQ_RESERVED2: u8 = 0x0B;
// The IRQ for the PS/2 mouse.
pub const IRQ_PS2_MOUSE: u8 = 0x0C;
/// The IRQ for the floating point unit/co-processor.
pub const IRQ_FLOATING_POINT_UNIT: u8 = 0x0D;
/// The IRQ for the primary hard drive controller.
pub const IRQ_PRIMARY_HARD_DISK_CONTROLLER: u8 = 0x0E;
/// The IRQ for the secondary hard drive controller.
pub const IRQ_SECONDARY_HARD_DISK_CONTROLLER: u8 = 0x0F;
/// Keep track of the number of spurious IRQs.
var spurious_irq_counter: u32 = 0;
///
/// Send a command to the master PIC. This will send it to the master command port.
///
/// Arguments:
/// IN cmd: u8 - The command to send.
///
inline fn sendCommandMaster(cmd: u8) void {
arch.outb(MASTER_COMMAND_REG, cmd);
}
///
/// Send a command to the salve PIC. This will send it to the salve command port.
///
/// Arguments:
/// IN cmd: u8 - The command to send.
///
inline fn sendCommandSlave(cmd: u8) void {
arch.outb(SLAVE_COMMAND_REG, cmd);
}
///
/// Send data to the master PIC. This will send it to the master data port.
///
/// Arguments:
/// IN data: u8 - The data to send.
///
inline fn sendDataMaster(data: u8) void {
arch.outb(MASTER_DATA_REG, data);
}
///
/// Send data to the salve PIC. This will send it to the salve data port.
///
/// Arguments:
/// IN data: u8 - The data to send.
///
inline fn sendDataSlave(data: u8) void {
arch.outb(SLAVE_DATA_REG, data);
}
///
/// Read the data from the master data register. This will read from the master data port.
///
/// Return: u8
/// The data that is stored in the master data register.
///
inline fn readDataMaster() u8 {
return arch.inb(MASTER_DATA_REG);
}
///
/// Read the data from the salve data register. This will read from the salve data port.
///
/// Return: u8
/// The data that is stored in the salve data register.
///
inline fn readDataSlave() u8 {
return arch.inb(SLAVE_DATA_REG);
}
///
/// Read the master interrupt request register (IRR).
///
/// Return: u8
/// The data that is stored in the master IRR.
///
inline fn readMasterIrr() u8 {
sendCommandMaster(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_IRR);
return arch.inb(MASTER_STATUS_REG);
}
///
/// Read the slave interrupt request register (IRR).
///
/// Return: u8
/// The data that is stored in the slave IRR.
///
inline fn readSlaveIrr() u8 {
sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_IRR);
return arch.inb(SLAVE_STATUS_REG);
}
///
/// Read the master in-service register (ISR).
///
/// Return: u8
/// The data that is stored in the master ISR.
///
inline fn readMasterIsr() u8 {
sendCommandMaster(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR);
return arch.inb(MASTER_STATUS_REG);
}
///
/// Read the slave in-service register (ISR).
///
/// Return: u8
/// The data that is stored in the slave ISR.
///
inline fn readSlaveIsr() u8 {
sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR);
return arch.inb(SLAVE_STATUS_REG);
}
///
/// Send the end of interrupt (EOI) signal to the PIC. If the IRQ was from the master, then will
/// send the EOI to the master only. If the IRQ came from the slave, then will send the EOI to both
/// the slave and master.
///
/// Arguments:
/// IN irq_num: u8 - The IRQ number to sent the EOI to.
///
pub fn sendEndOfInterrupt(irq_num: u8) void {
if (irq_num >= 8) {
sendCommandSlave(OCW2_END_OF_INTERRUPT);
}
sendCommandMaster(OCW2_END_OF_INTERRUPT);
}
///
/// Check if the interrupt was a fake interrupt. (In short, this stops a race condition between the
/// CPU and PIC. See https://wiki.osdev.org/PIC#Spurious_IRQs for more details). If this returns
/// true, then the IRQ handler must not send a EOI back.
///
/// Arguments:
/// IN irq_num: u8 - The IRQ number to check.
///
/// Return: bool
/// Whether the IRQ provided was spurious.
///
pub fn spuriousIrq(irq_num: u8) bool {
// Only for IRQ 7 and 15
if (irq_num == 7) {
// Read master ISR
// Check the MSB is zero, if so, then is a spurious IRQ
// This is (1 << irq_num) or (1 << 7) to check if it is set for this IRQ
if ((readMasterIsr() & 0x80) == 0) {
spurious_irq_counter += 1;
return true;
}
} else if (irq_num == 15) {
// Read slave ISR
// Check the MSB is zero, if so, then is a spurious irq
if ((readSlaveIsr() & 0x80) == 0) {
// Need to send EOI to the master
sendCommandMaster(OCW2_END_OF_INTERRUPT);
spurious_irq_counter += 1;
return true;
}
}
return false;
}
///
/// Set the mask bit for the provided IRQ. This will prevent interrupts from triggering for this
/// IRQ.
///
/// Arguments:
/// IN irq_num: u8 - The IRQ number to mask.
///
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);
2019-11-10 13:35:08 +01:00
const value: u8 = arch.inb(port) | (@as(u8, 1) << shift);
arch.outb(port, value);
}
///
/// Clear the mask bit for the provided IRQ. This will allow interrupts to triggering for this IRQ.
///
/// Arguments:
/// IN irq_num: u8 - The IRQ number unmask.
///
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);
2019-11-10 13:35:08 +01:00
const value: u8 = arch.inb(port) & ~(@as(u8, 1) << shift);
arch.outb(port, value);
}
///
/// Remap the PIC interrupt lines as initially they conflict with CPU exceptions which are reserved
/// by Intel up to 0x1F. So this will move the IRQs from 0x00-0x0F to 0x20-0x2F.
///
pub fn init() void {
2020-01-01 20:12:36 +01:00
log.logInfo("Init pic\n", .{});
defer log.logInfo("Done pic\n", .{});
// Initiate
sendCommandMaster(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
arch.ioWait();
sendCommandSlave(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
arch.ioWait();
// Offsets
sendDataMaster(ICW2_MASTER_REMAP_OFFSET);
arch.ioWait();
sendDataSlave(ICW2_SLAVE_REMAP_OFFSET);
arch.ioWait();
// IRQ lines
sendDataMaster(ICW3_MASTER_IRQ_MAP_FROM_SLAVE);
arch.ioWait();
sendDataSlave(ICW3_SLAVE_IRQ_MAP_TO_MASTER);
arch.ioWait();
// 80x86 mode
sendDataMaster(ICW4_80x86_MODE);
arch.ioWait();
sendDataSlave(ICW4_80x86_MODE);
arch.ioWait();
// Mask all interrupts
2019-11-10 13:35:08 +01:00
sendDataMaster(0xFF);
arch.ioWait();
2019-11-10 13:35:08 +01:00
sendDataSlave(0xFF);
arch.ioWait();
// Clear the IRQ for the slave
clearMask(IRQ_CASCADE_FOR_SLAVE);
switch (build_options.test_mode) {
.Initialisation => runtimeTests(),
else => {},
}
}
test "sendCommandMaster" {
// Set up
arch.initTest();
defer arch.freeTest();
2019-11-10 13:35:08 +01:00
const cmd: u8 = 10;
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_COMMAND_REG, cmd });
sendCommandMaster(cmd);
}
test "sendCommandSlave" {
// Set up
arch.initTest();
defer arch.freeTest();
2019-11-10 13:35:08 +01:00
const cmd: u8 = 10;
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, cmd });
sendCommandSlave(cmd);
}
test "sendDataMaster" {
// Set up
arch.initTest();
defer arch.freeTest();
2019-11-10 13:35:08 +01:00
const data: u8 = 10;
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_DATA_REG, data });
sendDataMaster(data);
}
test "sendDataSlave" {
// Set up
arch.initTest();
defer arch.freeTest();
2019-11-10 13:35:08 +01:00
const data: u8 = 10;
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ SLAVE_DATA_REG, data });
sendDataSlave(data);
}
test "readDataMaster" {
// Set up
arch.initTest();
defer arch.freeTest();
2020-01-01 20:12:36 +01:00
arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 10) });
2019-11-10 13:35:08 +01:00
expectEqual(@as(u8, 10), readDataMaster());
}
test "readDataSlave" {
// Set up
arch.initTest();
defer arch.freeTest();
2020-01-01 20:12:36 +01:00
arch.addTestParams("inb", .{ SLAVE_DATA_REG, @as(u8, 10) });
2019-11-10 13:35:08 +01:00
expectEqual(@as(u8, 10), readDataSlave());
}
test "readMasterIrr" {
// Set up
arch.initTest();
defer arch.freeTest();
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_COMMAND_REG, @as(u8, 0x0A) });
arch.addTestParams("inb", .{ MASTER_STATUS_REG, @as(u8, 10) });
2019-11-10 13:35:08 +01:00
expectEqual(@as(u8, 10), readMasterIrr());
}
test "readSlaveIrr" {
// Set up
arch.initTest();
defer arch.freeTest();
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, @as(u8, 0x0A) });
arch.addTestParams("inb", .{ SLAVE_STATUS_REG, @as(u8, 10) });
2019-11-10 13:35:08 +01:00
expectEqual(@as(u8, 10), readSlaveIrr());
}
test "readMasterIsr" {
// Set up
arch.initTest();
defer arch.freeTest();
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_COMMAND_REG, @as(u8, 0x0B) });
arch.addTestParams("inb", .{ MASTER_STATUS_REG, @as(u8, 10) });
2019-11-10 13:35:08 +01:00
expectEqual(@as(u8, 10), readMasterIsr());
}
test "readSlaveIsr" {
// Set up
arch.initTest();
defer arch.freeTest();
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, @as(u8, 0x0B) });
arch.addTestParams("inb", .{ SLAVE_STATUS_REG, @as(u8, 10) });
2019-11-10 13:35:08 +01:00
expectEqual(@as(u8, 10), readSlaveIsr());
}
test "sendEndOfInterrupt master only" {
// Set up
arch.initTest();
defer arch.freeTest();
2019-11-10 13:35:08 +01:00
var i: u8 = 0;
while (i < 8) : (i += 1) {
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT });
sendEndOfInterrupt(i);
}
}
test "sendEndOfInterrupt master and slave" {
// Set up
arch.initTest();
defer arch.freeTest();
2019-11-10 13:35:08 +01:00
var i: u8 = 8;
while (i < 16) : (i += 1) {
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, OCW2_END_OF_INTERRUPT });
arch.addTestParams("outb", .{ MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT });
sendEndOfInterrupt(i);
}
}
test "spuriousIrq not spurious IRQ number" {
// Pre testing
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 0), spurious_irq_counter);
2019-11-10 13:35:08 +01:00
var i: u8 = 0;
while (i < 16) : (i += 1) {
if (i != 7 and i != 15) {
expectEqual(false, spuriousIrq(i));
}
}
// Post testing
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 0), spurious_irq_counter);
// Clean up
spurious_irq_counter = 0;
}
test "spuriousIrq spurious master IRQ number not spurious" {
// Set up
arch.initTest();
defer arch.freeTest();
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_COMMAND_REG, @as(u8, 0x0B) });
// Return 0x80 from readMasterIsr() which will mean this was a real IRQ
2020-01-01 20:12:36 +01:00
arch.addTestParams("inb", .{ MASTER_STATUS_REG, @as(u8, 0x80) });
// Pre testing
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 0), spurious_irq_counter);
// Call function
2019-11-10 13:35:08 +01:00
expectEqual(false, spuriousIrq(7));
// Post testing
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 0), spurious_irq_counter);
// Clean up
spurious_irq_counter = 0;
}
test "spuriousIrq spurious master IRQ number spurious" {
// Set up
arch.initTest();
defer arch.freeTest();
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_COMMAND_REG, @as(u8, 0x0B) });
// Return 0x0 from readMasterIsr() which will mean this was a spurious IRQ
2020-01-01 20:12:36 +01:00
arch.addTestParams("inb", .{ MASTER_STATUS_REG, @as(u8, 0x0) });
// Pre testing
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 0), spurious_irq_counter);
// Call function
2019-11-10 13:35:08 +01:00
expectEqual(true, spuriousIrq(7));
// Post testing
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 1), spurious_irq_counter);
// Clean up
spurious_irq_counter = 0;
}
test "spuriousIrq spurious slave IRQ number not spurious" {
// Set up
arch.initTest();
defer arch.freeTest();
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, @as(u8, 0x0B) });
// Return 0x80 from readSlaveIsr() which will mean this was a real IRQ
2020-01-01 20:12:36 +01:00
arch.addTestParams("inb", .{ SLAVE_STATUS_REG, @as(u8, 0x80) });
// Pre testing
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 0), spurious_irq_counter);
// Call function
2019-11-10 13:35:08 +01:00
expectEqual(false, spuriousIrq(15));
// Post testing
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 0), spurious_irq_counter);
// Clean up
spurious_irq_counter = 0;
}
test "spuriousIrq spurious slave IRQ number spurious" {
// Set up
arch.initTest();
defer arch.freeTest();
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ SLAVE_COMMAND_REG, @as(u8, 0x0B) });
// Return 0x0 from readSlaveIsr() which will mean this was a spurious IRQ
2020-01-01 20:12:36 +01:00
arch.addTestParams("inb", .{ SLAVE_STATUS_REG, @as(u8, 0x0) });
// A EOI will be sent for a spurious IRQ 15
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT });
// Pre testing
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 0), spurious_irq_counter);
// Call function
2019-11-10 13:35:08 +01:00
expectEqual(true, spuriousIrq(15));
// Post testing
2019-11-10 13:35:08 +01:00
expectEqual(@as(u32, 1), spurious_irq_counter);
// Clean up
spurious_irq_counter = 0;
}
test "setMask master IRQ masked" {
// Set up
arch.initTest();
defer arch.freeTest();
// Going to assume all bits are masked out
2020-01-01 20:12:36 +01:00
arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFF) });
// Expect the 2nd bit to be set
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_DATA_REG, @as(u8, 0xFF) });
2019-11-10 13:35:08 +01:00
setMask(1);
}
test "setMask master IRQ unmasked" {
// Set up
arch.initTest();
defer arch.freeTest();
// IRQ already unmasked
2020-01-01 20:12:36 +01:00
arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFD) });
// Expect the 2nd bit to be set
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_DATA_REG, @as(u8, 0xFF) });
2019-11-10 13:35:08 +01:00
setMask(1);
}
test "clearMask master IRQ masked" {
// Set up
arch.initTest();
defer arch.freeTest();
// Going to assume all bits are masked out
2020-01-01 20:12:36 +01:00
arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFF) });
// Expect the 2nd bit to be clear
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_DATA_REG, @as(u8, 0xFD) });
2019-11-10 13:35:08 +01:00
clearMask(1);
}
test "clearMask master IRQ unmasked" {
// Set up
arch.initTest();
defer arch.freeTest();
// IRQ already unmasked
2020-01-01 20:12:36 +01:00
arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFD) });
// Expect the 2nd bit to still be clear
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{ MASTER_DATA_REG, @as(u8, 0xFD) });
2019-11-10 13:35:08 +01:00
clearMask(1);
}
test "init" {
// Set up
arch.initTest();
defer arch.freeTest();
arch.addRepeatFunction("ioWait", arch.mock_ioWait);
// Just a long list of OUT instructions setting up the PIC
2020-01-01 20:12:36 +01:00
arch.addTestParams("outb", .{
MASTER_COMMAND_REG,
ICW1_INITIALISATION | ICW1_EXPECT_ICW4,
SLAVE_COMMAND_REG,
ICW1_INITIALISATION | ICW1_EXPECT_ICW4,
MASTER_DATA_REG,
ICW2_MASTER_REMAP_OFFSET,
SLAVE_DATA_REG,
ICW2_SLAVE_REMAP_OFFSET,
MASTER_DATA_REG,
ICW3_MASTER_IRQ_MAP_FROM_SLAVE,
SLAVE_DATA_REG,
ICW3_SLAVE_IRQ_MAP_TO_MASTER,
MASTER_DATA_REG,
ICW4_80x86_MODE,
SLAVE_DATA_REG,
ICW4_80x86_MODE,
MASTER_DATA_REG,
2019-11-10 13:35:08 +01:00
@as(u8, 0xFF),
SLAVE_DATA_REG,
2019-11-10 13:35:08 +01:00
@as(u8, 0xFF),
MASTER_DATA_REG,
@as(u8, 0xFB),
2020-01-01 20:12:36 +01:00
});
arch.addTestParams("inb", .{ MASTER_DATA_REG, @as(u8, 0xFF) });
init();
}
///
/// Test that all the PIC masks are set so no interrupts can fire.
///
fn rt_picAllMasked() void {
// The master will have interrupt 2 clear because this is the link to the slave (third bit)
if (readDataMaster() != 0xFB) {
panic(@errorReturnTrace(), "FAILURE: Master masks are not set, found: {}\n", .{readDataMaster()});
}
if (readDataSlave() != 0xFF) {
panic(@errorReturnTrace(), "FAILURE: Slave masks are not set, found: {}\n", .{readDataSlave()});
}
2020-01-01 20:12:36 +01:00
log.logInfo("PIC: Tested masking\n", .{});
}
///
/// Run all the runtime tests.
///
fn runtimeTests() void {
rt_picAllMasked();
}