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

835 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 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 {
std.log.info(.pic, "Init\n", .{});
defer std.log.info(.pic, "Done\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()});
}
std.log.info(.pic, "Tested masking\n", .{});
}
///
/// Run all the runtime tests.
///
Initial scheduler Fix TSS Also change to .{} syntax where appropriate. Added the SS segment Fixed spelling Refactoring GDT Multitasking working for now WIP scheduler Refactored Bitmap a bit WIP still Task switching working Handlers return the stack pointer that will be used to restore the tasks stack, normal handlers will return the same stack pointer it was called with where task switching will return the stack pointer of the next task and restore its state using the interrupt stub. Initial scheduler done Created a stage 2 init task Change u32 to usize Move Task to arch specific WIP WIP2 Removed esp from task, replaced with stack_pointer Removed the debug logs Fixed init task stack Change pickNextTask to pointer manipulation This allows less allocations so faster switching Temporary enable interrupts for some runtime tests PIT and RTC need interrupts enabled to run their runtime tests Renamed schedule => pickNextTask, comptime bitmap for pids not task init And some other stuff: No pub for the task anymore Use the leak detector allocator Fmt Fix unit tests And some other stuff :P PR review Moved Task out of arch and have the stack init in the arch file Mocking clean up Removed commented code Renamed createTask to scheduleTask where the user will have to provide a task to schedule Removed redundant pub in log runtime test Removed global allocator for scheduler Cleaner assembly in paging Fmt Added new Scheduler test mode Added new test mode to CI Removed one of the prints Added doc comment, task test for i386 Removed test WIP Runtime tests work Have a global set in one task and reacted to in another. Also test that local variables are preserved after a task switch. Removed new lines Increased line length Move the allocation of the bool above the task creation
2020-07-18 23:46:24 +02:00
pub fn runtimeTests() void {
rt_picAllMasked();
}