Added unit tests for PIC

Removed I/O wait as it isn't needed and uses the syscall interrupt.
Added runtime tests

Fixed styling for TTY
Fixed runtime tests


Now they are fixed
This commit is contained in:
ED 2019-10-01 17:59:42 +01:00
parent 25af35f726
commit 2a0c2e4708
7 changed files with 791 additions and 239 deletions

View file

@ -3,13 +3,13 @@ const Allocator = std.mem.Allocator;
const builtin = @import("builtin");
const gdt = @import("gdt.zig");
const idt = @import("idt.zig");
const pic = @import("pic.zig");
const irq = @import("irq.zig");
const isr = @import("isr.zig");
const pit = @import("pit.zig");
const paging = @import("paging.zig");
const syscalls = @import("syscalls.zig");
const mem = @import("../../mem.zig");
const log = @import("../../log.zig");
const MemProfile = mem.MemProfile;
/// The interrupt context that is given to a interrupt handler. It contains most of the registers
@ -76,15 +76,6 @@ pub fn inb(port: u16) u8 {
);
}
///
/// A simple way of waiting for I/O event to happen by doing an I/O event to flush the I/O
/// event being waited.
///
pub fn ioWait() void {
// Port 0x80 is free to use
outb(0x80, 0);
}
///
/// Load the GDT and refreshing the code segment with the code segment offset of the kernel as we
/// are still in kernel land. Also loads the kernel data segment into all the other segment
@ -230,6 +221,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *Allocator, comptime opti
gdt.init();
idt.init();
pic.init();
isr.init();
irq.init();
@ -245,6 +237,7 @@ pub fn init(mem_profile: *const MemProfile, allocator: *Allocator, comptime opti
test "" {
_ = @import("gdt.zig");
_ = @import("idt.zig");
_ = @import("pic.zig");
_ = @import("syscalls.zig");
_ = @import("paging.zig");
}

View file

@ -5,8 +5,9 @@ const builtin = @import("builtin");
const is_test = builtin.is_test;
const build_options = @import("build_options");
const arch = if (is_test) @import(build_options.arch_mock_path ++ "arch_mock.zig") else @import("arch.zig");
const log = if (is_test) @import(build_options.arch_mock_path ++ "log_mock.zig") else @import("../../log.zig");
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");
/// The access bits for a GDT entry.
const AccessBits = packed struct {

View file

@ -6,9 +6,10 @@ const builtin = @import("builtin");
const is_test = builtin.is_test;
const build_options = @import("build_options");
const gdt = if (is_test) @import(build_options.arch_mock_path ++ "gdt_mock.zig") else @import("gdt.zig");
const arch = if (is_test) @import(build_options.arch_mock_path ++ "arch_mock.zig") else @import("arch.zig");
const log = if (is_test) @import(build_options.arch_mock_path ++ "log_mock.zig") else @import("../../log.zig");
const mock_path = build_options.arch_mock_path;
const gdt = if (is_test) @import(mock_path ++ "gdt_mock.zig") else @import("gdt.zig");
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");
/// The structure that contains all the information that each IDT entry needs.
const IdtEntry = packed struct {
@ -326,6 +327,7 @@ fn rt_loadedIDTSuccess() void {
const loaded_idt = arch.sidt();
expect(idt_ptr.limit == loaded_idt.limit);
expect(idt_ptr.base == loaded_idt.base);
log.logInfo("IDT: Tested loading IDT\n");
}
///
@ -333,5 +335,4 @@ fn rt_loadedIDTSuccess() void {
///
fn runtimeTests() void {
rt_loadedIDTSuccess();
log.logInfo("IDT: Tested loading IDT\n");
}

View file

@ -79,9 +79,9 @@ fn openIrq(index: u8, handler: idt.InterruptHandler) void {
/// mask bit in the PIC so interrupts can happen.
///
/// Arguments:
/// IN irq_num: u16 - The IRQ number to register.
/// IN irq_num: u8 - The IRQ number to register.
///
pub fn registerIrq(irq_num: u16, handler: fn (*arch.InterruptContext) void) void {
pub fn registerIrq(irq_num: u8, handler: fn (*arch.InterruptContext) void) void {
irq_handlers[irq_num] = handler;
pic.clearMask(irq_num);
}
@ -103,9 +103,6 @@ pub fn unregisterIrq(irq_num: u16) void {
/// the IDT interrupt gates for each IRQ.
///
pub fn init() void {
// Remap the PIC IRQ so not to overlap with other exceptions
pic.remapIrq();
// Open all the IRQ's
openIrq(32, irq0);
openIrq(33, irq1);

View file

@ -1,192 +1,362 @@
// Zig version: 0.4.0
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;
const arch = @import("arch.zig");
// ----------
// Port address for the PIC master and slave registers.
// ----------
// Port address for the PIC master and slave registers
const MASTER_COMMAND_REG: u16 = 0x20; // (Write only).
const MASTER_STATUS_REG: u16 = 0x20; // (Read only).
const MASTER_DATA_REG: u16 = 0x21;
const MASTER_INTERRUPT_MASK_REG: u16 = 0x21;
const SLAVE_COMMAND_REG: u16 = 0xA0; // (Write only).
const SLAVE_STATUS_REG: u16 = 0xA0; // (Read only).
const SLAVE_DATA_REG: u16 = 0xA1;
const SLAVE_INTERRUPT_MASK_REG: u16 = 0xA1;
/// The port address for issuing a command to the master PIC. This is a write only operation.
const MASTER_COMMAND_REG: u16 = 0x20;
// 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;
/// 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;
// If set, then there is only one PIC in the system. If not set, then PIC is cascaded with slave
// PIC's and initialisation control word 3 must be sent to the controller.
const ICW1_SINGLE_CASCADE_MODE: u8 = 0x02;
/// 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;
// 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;
/// The port address for issuing a command to the slave PIC. This is a write only operation.
const SLAVE_COMMAND_REG: u16 = 0xA0;
// If set, then operating in level triggered mode. If not set, then operating in edge triggered
// mode.
const ICW1_LEVEL_TRIGGER_MODE: u8 = 0x08;
/// 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;
// If set, then the PIC is to be initialised.
const ICW1_INITIALISATION: u8 = 0x10;
/// 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 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;
/// 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;
// The new port map for the slave PIC. IRQs 8-15 mapped to use interrupts 0x28-0x36
const ICW2_SLAVE_REMAP_OFFSET: u8 = 0x28;
/// 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;
// 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;
/// If set, then operating in level triggered mode. If not set, then operating in edge triggered
/// mode.
const ICW1_LEVEL_TRIGGER_MODE: u8 = 0x08;
// Tell the master PIT to receive interrupts from the slave PIC on IRQ2
const ICW3_MASTER_IRQ_MAP_FROM_SLAVE: u8 = 0x04;
/// If set, then the PIC is to be initialised.
const ICW1_INITIALISATION: u8 = 0x10;
// ----------
// Initialisation control word 2.
// ----------
// 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;
/// 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;
// If set, then on last interrupt acknowledge pulse the PIC automatically performs end of
// interrupt operation.
const ICW4_AUTO_END_OF_INTERRUPT: u8 = 0x02;
/// The new port map for the slave PIC. IRQs 8-15 mapped to use interrupts 0x28-0x2F.
const ICW2_SLAVE_REMAP_OFFSET: u8 = 0x28;
// 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;
// ----------
// Initialisation control word 3.
// ----------
// If set, then PIC operates in buffered mode.
const ICW4_BUFFER_MODE: u8 = 0x08;
/// 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;
// If set, then the the system had many cascaded PIC's. Not supported in x86.
const ICW4_FULLY_NESTED_MODE: u8 = 0x10;
/// 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.
// ----------
// Operation control word 1. Interrupt masks.
const OCW1_MASK_IRQ0: u8 = 0x01;
const OCW1_MASK_IRQ1: u8 = 0x02;
const OCW1_MASK_IRQ2: u8 = 0x04;
const OCW1_MASK_IRQ3: u8 = 0x08;
const OCW1_MASK_IRQ4: u8 = 0x10;
const OCW1_MASK_IRQ5: u8 = 0x20;
const OCW1_MASK_IRQ6: u8 = 0x40;
const OCW1_MASK_IRQ7: u8 = 0x80;
/// 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;
// 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;
/// If set, then on last interrupt acknowledge pulse the PIC automatically performs end of
/// interrupt operation.
const ICW4_AUTO_END_OF_INTERRUPT: u8 = 0x02;
// Interrupt level 2 upon which the controller must react. Interrupt level for the current interrupt
const OCW2_INTERRUPT_LEVEL_2: 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;
// Interrupt level 3 upon which the controller must react. Interrupt level for the current interrupt
const OCW2_INTERRUPT_LEVEL_3: u8 = 0x04;
/// If set, then PIC operates in buffered mode.
const ICW4_BUFFER_MODE: u8 = 0x08;
// The end of interrupt command code.
const OCW2_END_OF_INTERRUPT: u8 = 0x20;
/// If set, then the the system had many cascaded PICs. Not supported in x86.
const ICW4_FULLY_NESTED_MODE: u8 = 0x10;
// Select command.
const OCW2_SELECTION: u8 = 0x40;
// ----------
// Operation control word 1.
// ----------
// Rotation command.
const OCW2_ROTATION: u8 = 0x80;
/// 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.
// Read the Interrupt Request Register register
const OCW3_READ_IRR: u8 = 0x00;
// ----------
// Read the In Service Register register.
const OCW3_READ_ISR: u8 = 0x01;
/// Operation control word 3.
/// Read the Interrupt Request Register register
const OCW3_READ_IRR: u8 = 0x00;
// 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;
/// Read the In Service Register register.
const OCW3_READ_ISR: u8 = 0x01;
// If set, then poll command issued. If not set, then no pool command issued.
const OCW3_POLL_COMMAND_ISSUED: u8 = 0x04;
/// 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;
// This must be set for all OCW 3.
const OCW3_DEFAULT: u8 = 0x08;
/// If set, then poll command issued. If not set, then no pool command issued.
const OCW3_POLL_COMMAND_ISSUED: u8 = 0x04;
// If set, then the special mask is set. If not set, then resets special mask.
const OCW3_SPECIAL_MASK: u8 = 0x20;
/// This must be set for all OCW 3.
const OCW3_DEFAULT: u8 = 0x08;
// 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;
// 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;
// IRQ's numbers for the PIC.
pub const IRQ_PIT: u8 = 0x00;
pub const IRQ_KEYBOARD: u8 = 0x01;
pub const IRQ_CASCADE_FOR_SLAVE: u8 = 0x02;
pub const IRQ_SERIAL_PORT_2: u8 = 0x03;
pub const IRQ_SERIAL_PORT_1: u8 = 0x04;
pub const IRQ_PARALLEL_PORT_2: u8 = 0x05;
pub const IRQ_DISKETTE_DRIVE: u8 = 0x06;
pub const IRQ_PARALLEL_PORT_1: u8 = 0x07;
pub const IRQ_REAL_TIME_CLOCK: u8 = 0x08;
pub const IRQ_CGA_VERTICAL_RETRACE: u8 = 0x09;
/// 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;
pub const IRQ_AUXILIARY_DEVICE: u8 = 0x0C;
pub const IRQ_FLOATING_POINT_UNIT: u8 = 0x0D;
pub const IRQ_HARD_DISK_CONTROLLER: u8 = 0x0E;
// Last bit must be zero.
// ----------
// The IRQs
// ----------
// Keep track of the number of spurious IRQ's
/// 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);
}
inline fn sendDataMaster(cmd: u8) void {
arch.outb(MASTER_DATA_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);
}
inline fn sendDataSlave(cmd: u8) void {
arch.outb(SLAVE_DATA_REG, cmd);
///
/// 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 {
sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_IRR);
return arch.inb(SLAVE_STATUS_REG);
}
inline fn readSlaveIrr() u8 {
sendCommandMaster(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_IRR);
return arch.inb(MASTER_STATUS_REG);
}
inline fn readMasterIsr() u8 {
sendCommandSlave(OCW3_DEFAULT | OCW3_ACT_ON_READ | OCW3_READ_ISR);
///
/// 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);
}
inline fn readSlaveIsr() u8 {
///
/// 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);
@ -195,11 +365,22 @@ pub fn sendEndOfInterrupt(irq_num: u8) void {
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) {
if (irq_num == 7) {
// Read master ISR
// Check the MSB is zero, if so, then is a spurious irq
// 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;
@ -219,21 +400,40 @@ pub fn spuriousIrq(irq_num: u8) bool {
return false;
}
pub fn setMask(irq_num: u16) void {
///
/// 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);
const value: u8 = arch.inb(port) | (u8(1) << shift);
arch.outb(port, value);
}
pub fn clearMask(irq_num: u16) void {
///
/// 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);
const value: u8 = arch.inb(port) & ~(u8(1) << shift);
arch.outb(port, value);
}
pub fn remapIrq() void {
///
/// 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 {
log.logInfo("Init pic\n");
// Initiate
sendCommandMaster(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
sendCommandSlave(ICW1_INITIALISATION | ICW1_EXPECT_ICW4);
@ -250,7 +450,365 @@ pub fn remapIrq() void {
sendDataMaster(ICW4_80x86_MODE);
sendDataSlave(ICW4_80x86_MODE);
// Mask
arch.outb(0x21, 0xFF);
arch.outb(0xA1, 0xFF);
// Mask all interrupts
sendDataMaster(u8(0xFF));
sendDataSlave(u8(0xFF));
log.logInfo("Done\n");
if (build_options.rt_test) runtimeTests();
}
test "sendCommandMaster" {
// Set up
arch.initTest();
defer arch.freeTest();
const cmd = u8(10);
arch.addTestParams("outb", MASTER_COMMAND_REG, cmd);
sendCommandMaster(cmd);
}
test "sendCommandSlave" {
// Set up
arch.initTest();
defer arch.freeTest();
const cmd = u8(10);
arch.addTestParams("outb", SLAVE_COMMAND_REG, cmd);
sendCommandSlave(cmd);
}
test "sendDataMaster" {
// Set up
arch.initTest();
defer arch.freeTest();
const data = u8(10);
arch.addTestParams("outb", MASTER_DATA_REG, data);
sendDataMaster(data);
}
test "sendDataSlave" {
// Set up
arch.initTest();
defer arch.freeTest();
const data = u8(10);
arch.addTestParams("outb", SLAVE_DATA_REG, data);
sendDataSlave(data);
}
test "readDataMaster" {
// Set up
arch.initTest();
defer arch.freeTest();
arch.addTestParams("inb", MASTER_DATA_REG, u8(10));
expectEqual(u8(10), readDataMaster());
}
test "readDataSlave" {
// Set up
arch.initTest();
defer arch.freeTest();
arch.addTestParams("inb", SLAVE_DATA_REG, u8(10));
expectEqual(u8(10), readDataSlave());
}
test "readMasterIrr" {
// Set up
arch.initTest();
defer arch.freeTest();
arch.addTestParams("outb", MASTER_COMMAND_REG, u8(0x0A));
arch.addTestParams("inb", MASTER_STATUS_REG, u8(10));
expectEqual(u8(10), readMasterIrr());
}
test "readSlaveIrr" {
// Set up
arch.initTest();
defer arch.freeTest();
arch.addTestParams("outb", SLAVE_COMMAND_REG, u8(0x0A));
arch.addTestParams("inb", SLAVE_STATUS_REG, u8(10));
expectEqual(u8(10), readSlaveIrr());
}
test "readMasterIsr" {
// Set up
arch.initTest();
defer arch.freeTest();
arch.addTestParams("outb", MASTER_COMMAND_REG, u8(0x0B));
arch.addTestParams("inb", MASTER_STATUS_REG, u8(10));
expectEqual(u8(10), readMasterIsr());
}
test "readSlaveIsr" {
// Set up
arch.initTest();
defer arch.freeTest();
arch.addTestParams("outb", SLAVE_COMMAND_REG, u8(0x0B));
arch.addTestParams("inb", SLAVE_STATUS_REG, u8(10));
expectEqual(u8(10), readSlaveIsr());
}
test "sendEndOfInterrupt master only" {
// Set up
arch.initTest();
defer arch.freeTest();
var i = u8(0);
while (i < 8) : (i += 1) {
arch.addTestParams("outb", MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT);
sendEndOfInterrupt(i);
}
}
test "sendEndOfInterrupt master and slave" {
// Set up
arch.initTest();
defer arch.freeTest();
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);
sendEndOfInterrupt(i);
}
}
test "spuriousIrq not spurious IRQ number" {
// Pre testing
expectEqual(u32(0), spurious_irq_counter);
var i = u8(0);
while (i < 16) : (i += 1) {
if (i != 7 and i != 15) {
expectEqual(false, spuriousIrq(i));
}
}
// Post testing
expectEqual(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();
arch.addTestParams("outb", MASTER_COMMAND_REG, u8(0x0B));
// Return 0x80 from readMasterIsr() which will mean this was a real IRQ
arch.addTestParams("inb", MASTER_STATUS_REG, u8(0x80));
// Pre testing
expectEqual(u32(0), spurious_irq_counter);
// Call function
expectEqual(false, spuriousIrq(u8(7)));
// Post testing
expectEqual(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();
arch.addTestParams("outb", MASTER_COMMAND_REG, u8(0x0B));
// Return 0x0 from readMasterIsr() which will mean this was a spurious IRQ
arch.addTestParams("inb", MASTER_STATUS_REG, u8(0x0));
// Pre testing
expectEqual(u32(0), spurious_irq_counter);
// Call function
expectEqual(true, spuriousIrq(u8(7)));
// Post testing
expectEqual(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();
arch.addTestParams("outb", SLAVE_COMMAND_REG, u8(0x0B));
// Return 0x80 from readSlaveIsr() which will mean this was a real IRQ
arch.addTestParams("inb", SLAVE_STATUS_REG, u8(0x80));
// Pre testing
expectEqual(u32(0), spurious_irq_counter);
// Call function
expectEqual(false, spuriousIrq(u8(15)));
// Post testing
expectEqual(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();
arch.addTestParams("outb", SLAVE_COMMAND_REG, u8(0x0B));
// Return 0x0 from readSlaveIsr() which will mean this was a spurious IRQ
arch.addTestParams("inb", SLAVE_STATUS_REG, u8(0x0));
// A EOI will be sent for a spurious IRQ 15
arch.addTestParams("outb", MASTER_COMMAND_REG, OCW2_END_OF_INTERRUPT);
// Pre testing
expectEqual(u32(0), spurious_irq_counter);
// Call function
expectEqual(true, spuriousIrq(u8(15)));
// Post testing
expectEqual(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
arch.addTestParams("inb", MASTER_DATA_REG, u8(0xFF));
// Expect the 2nd bit to be set
arch.addTestParams("outb", MASTER_DATA_REG, u8(0xFF));
setMask(u8(1));
}
test "setMask master IRQ unmasked" {
// Set up
arch.initTest();
defer arch.freeTest();
// IRQ already unmasked
arch.addTestParams("inb", MASTER_DATA_REG, u8(0xFD));
// Expect the 2nd bit to be set
arch.addTestParams("outb", MASTER_DATA_REG, u8(0xFF));
setMask(u8(1));
}
test "clearMask master IRQ masked" {
// Set up
arch.initTest();
defer arch.freeTest();
// Going to assume all bits are masked out
arch.addTestParams("inb", MASTER_DATA_REG, u8(0xFF));
// Expect the 2nd bit to be clear
arch.addTestParams("outb", MASTER_DATA_REG, u8(0xFD));
clearMask(u8(1));
}
test "clearMask master IRQ unmasked" {
// Set up
arch.initTest();
defer arch.freeTest();
// IRQ already unmasked
arch.addTestParams("inb", MASTER_DATA_REG, u8(0xFD));
// Expect the 2nd bit to still be clear
arch.addTestParams("outb", MASTER_DATA_REG, u8(0xFD));
clearMask(u8(1));
}
test "init" {
// Set up
arch.initTest();
defer arch.freeTest();
// Just a long list of OUT instructions setting up the PIC
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,
u8(0xFF),
SLAVE_DATA_REG,
u8(0xFF),
);
init();
}
///
/// Test that all the PIC masks are set so no interrupts can fire.
///
fn rt_picAllMasked() void {
if (readDataMaster() != 0xFF) {
panic(@errorReturnTrace(), "Master masks are not set, found: {}\n", readDataMaster());
}
if (readDataSlave() != 0xFF) {
panic(@errorReturnTrace(), "Slave masks are not set, found: {}\n", readDataSlave());
}
log.logInfo("PIC: Tested masking\n");
}
///
/// Run all the runtime tests.
///
fn runtimeTests() void {
rt_picAllMasked();
}

View file

@ -633,100 +633,6 @@ pub fn init() void {
if (build_options.rt_test) runtimeTests();
}
///
/// Test the init function set up everything properly.
///
fn rt_initialisedGlobals() void {
if (@ptrToInt(video_buffer.ptr) != @ptrToInt(&KERNEL_ADDR_OFFSET) + 0xB8000) {
panic(@errorReturnTrace(), "Video buffer not at correct virtual address, found: {}\n", @ptrToInt(video_buffer.ptr));
}
if (page_index != 0) {
panic(@errorReturnTrace(), "Page index not at zero, found: {}\n", page_index);
}
if (colour != vga.entryColour(vga.COLOUR_LIGHT_GREY, vga.COLOUR_BLACK)) {
panic(@errorReturnTrace(), "Colour not set up properly, found: {}\n", colour);
}
if (blank != vga.entry(0, colour)) {
panic(@errorReturnTrace(), "Blank not set up properly, found: {}\n", blank);
}
// Make sure the screen isn't all blank
var all_blank = true;
for (video_buffer) |buf| {
if (buf != blank and buf != 0) {
all_blank = false;
break;
}
}
if (all_blank) {
panic(@errorReturnTrace(), "Screen all blank, should have logo and page number\n");
}
log.logInfo("TTY: Tested globals\n");
}
///
/// Test printing a string will output to the screen. This will check both the video memory and
/// the pages.
///
fn rt_printString() void {
const text = "abcdefg";
const clear_text = "\x08" ** text.len;
print(text);
// Check the video memory
var counter = u32(0);
for (video_buffer) |buf| {
if (counter < text.len and buf == vga.entry(text[counter], colour)) {
counter += 1;
} else if (counter == text.len) {
// Found all the text
break;
} else {
counter = 0;
}
}
if (counter != text.len) {
panic(@errorReturnTrace(), "Didn't find the printed text in video memory\n");
}
// Check the pages
counter = 0;
for (pages[0]) |c| {
if (counter < text.len and c == vga.entry(text[counter], colour)) {
counter += 1;
} else if (counter == text.len) {
// Found all the text
break;
} else {
counter = 0;
}
}
if (counter != text.len) {
panic(@errorReturnTrace(), "Didn't find the printed text in pages\n");
}
// Clear the text
print(clear_text);
log.logInfo("TTY: Tested printing\n");
}
///
/// Run all the runtime tests.
///
fn runtimeTests() void {
rt_initialisedGlobals();
rt_printString();
}
const test_colour: u8 = vga.orig_entryColour(vga.COLOUR_LIGHT_GREY, vga.COLOUR_BLACK);
var test_video_buffer: [VIDEO_BUFFER_SIZE]u16 = [_]u16{0} ** VIDEO_BUFFER_SIZE;
@ -2196,3 +2102,97 @@ test "init not 0,0" {
// Tear down
resetGlobals();
}
///
/// Test the init function set up everything properly.
///
fn rt_initialisedGlobals() void {
if (@ptrToInt(video_buffer.ptr) != @ptrToInt(&KERNEL_ADDR_OFFSET) + 0xB8000) {
panic(@errorReturnTrace(), "Video buffer not at correct virtual address, found: {}\n", @ptrToInt(video_buffer.ptr));
}
if (page_index != 0) {
panic(@errorReturnTrace(), "Page index not at zero, found: {}\n", page_index);
}
if (colour != vga.entryColour(vga.COLOUR_LIGHT_GREY, vga.COLOUR_BLACK)) {
panic(@errorReturnTrace(), "Colour not set up properly, found: {}\n", colour);
}
if (blank != vga.entry(0, colour)) {
panic(@errorReturnTrace(), "Blank not set up properly, found: {}\n", blank);
}
// Make sure the screen isn't all blank
var all_blank = true;
for (video_buffer) |buf| {
if (buf != blank and buf != 0) {
all_blank = false;
break;
}
}
if (all_blank) {
panic(@errorReturnTrace(), "Screen all blank, should have logo and page number\n");
}
log.logInfo("TTY: Tested globals\n");
}
///
/// Test printing a string will output to the screen. This will check both the video memory and
/// the pages.
///
fn rt_printString() void {
const text = "abcdefg";
const clear_text = "\x08" ** text.len;
print(text);
// Check the video memory
var counter = u32(0);
for (video_buffer) |buf| {
if (counter < text.len and buf == vga.entry(text[counter], colour)) {
counter += 1;
} else if (counter == text.len) {
// Found all the text
break;
} else {
counter = 0;
}
}
if (counter != text.len) {
panic(@errorReturnTrace(), "Didn't find the printed text in video memory\n");
}
// Check the pages
counter = 0;
for (pages[0]) |c| {
if (counter < text.len and c == vga.entry(text[counter], colour)) {
counter += 1;
} else if (counter == text.len) {
// Found all the text
break;
} else {
counter = 0;
}
}
if (counter != text.len) {
panic(@errorReturnTrace(), "Didn't find the printed text in pages\n");
}
// Clear the text
print(clear_text);
log.logInfo("TTY: Tested printing\n");
}
///
/// Run all the runtime tests.
///
fn runtimeTests() void {
rt_initialisedGlobals();
rt_printString();
}

View file

@ -4,7 +4,9 @@ def get_test_cases(TestCase):
TestCase("GDT tests", [r"GDT: Tested loading GDT"]),
TestCase("IDT init", [r"Init idt", r"Done"]),
TestCase("IDT tests", [r"IDT: Tested loading IDT"]),
TestCase("PIT init", [r"Init pit", r".+", "Done"]),
TestCase("Syscalls init", [r"Init syscalls", "Done"]),
TestCase("PIC init", [r"Init pic", r"Done"]),
TestCase("PIC tests", [r"PIC: Tested masking"]),
TestCase("PIT init", [r"Init pit", r".+", r"Done"]),
TestCase("Syscalls init", [r"Init syscalls", r"Done"]),
TestCase("Syscall tests", [r"Syscalls: Tested no args", r"Syscalls: Tested 1 arg", r"Syscalls: Tested 2 args", r"Syscalls: Tested 3 args", r"Syscalls: Tested 4 args", r"Syscalls: Tested 5 args"])
]