Merge pull request #29 from SamTebbs33/feature/create-gdt-idt-irq-isr

Added GDT, IDT, IRQ, updated build.zig
This commit is contained in:
Edward Dean 2019-05-31 07:42:22 +01:00 committed by GitHub
commit 144dffe628
12 changed files with 1009 additions and 11 deletions

View file

@ -4,11 +4,13 @@ const Builder = @import("std").build.Builder;
const Step = @import("std").build.Step;
const builtin = @import("builtin");
const std = @import("std");
const os = std.os;
const ArrayList = std.ArrayList;
const warn = std.debug.warn;
const mem = std.mem;
var src_files: ArrayList([]const u8) = undefined;
var src_files_asm: ArrayList([]const u8) = undefined;
fn concat(allocator: *std.mem.Allocator, str: []const u8, str2: []const u8) !std.Buffer {
var b = try std.Buffer.init(allocator, str);
@ -18,21 +20,38 @@ fn concat(allocator: *std.mem.Allocator, str: []const u8, str2: []const u8) !std
pub fn build(b: *Builder) void {
src_files = ArrayList([]const u8).init(b.allocator);
src_files_asm = ArrayList([]const u8).init(b.allocator);
const debug = b.option(bool, "debug", "build with debug symbols / make qemu wait for a debug connection") orelse false;
var build_path = b.option([]const u8, "build-path", "path to build to") orelse "bin";
var src_path = b.option([]const u8, "source-path", "path to source") orelse "src";
var target = b.option([]const u8, "target", "target to build/run for") orelse "x86";
const builtin_target = if (mem.eql(u8, target, "x86")) builtin.Arch.i386 else unreachable;
const iso_path = concat(b.allocator, build_path, "/pluto.iso") catch unreachable;
b.makePath(build_path) catch unreachable;
var grub_path = concat(b.allocator, build_path, "/iso/boot/grub") catch unreachable;
var kern_path = concat(b.allocator, build_path, "/kernel") catch unreachable;
var a_path = concat(b.allocator, build_path, "/kernel/arch/") catch unreachable;
a_path = concat(b.allocator, a_path.toSlice(), target) catch unreachable;
b.makePath(grub_path.toSlice()) catch unreachable;
b.makePath(kern_path.toSlice()) catch unreachable;
b.makePath(a_path.toSlice()) catch unreachable;
src_files.append("kernel/kmain") catch unreachable;
// Add the architecture init file to the source files
// Add the assemblies
switch (builtin_target) {
builtin.Arch.i386 => {
src_files_asm.append("kernel/arch/x86/irq_asm") catch unreachable;
src_files_asm.append("kernel/arch/x86/isr_asm") catch unreachable;
},
else => {},
}
var arch_boot = concat(b.allocator, "kernel/arch/", target) catch unreachable;
arch_boot.append("/boot") catch unreachable;
src_files.append(arch_boot.toSlice()) catch unreachable;
const iso_path = concat(b.allocator, build_path, "/pluto.iso") catch unreachable;
var objects_steps = buildObjects(b, builtin_target, build_path, src_path);
var link_step = buildLink(b, builtin_target, build_path);
const iso_step = buildISO(b, build_path, iso_path.toSlice());
@ -114,6 +133,13 @@ fn buildLink(b: *Builder, target: builtin.Arch, build_path: []const u8) *Step {
file_obj.append(file) catch unreachable;
file_obj.append(".o") catch unreachable;
exec.addObjectFile(file_obj.toSlice());
exec.setMainPkgPath(".");
}
for (src_files_asm.toSlice()) |file| {
var file_obj = concat(b.allocator, build_path, "/") catch unreachable;
file_obj.append(file) catch unreachable;
file_obj.append(".o") catch unreachable;
exec.addObjectFile(file_obj.toSlice());
}
return &exec.step;
}
@ -125,6 +151,15 @@ fn buildObjects(b: *Builder, target: builtin.Arch, build_path: []const u8, src_p
var file_src = concat(b.allocator, src_path2.toSlice(), file) catch unreachable;
file_src.append(".zig") catch unreachable;
const obj = b.addObject(file, file_src.toSlice());
obj.setMainPkgPath(".");
obj.setOutputDir(build_path);
obj.setTarget(target, builtin.Os.freestanding, builtin.Abi.gnu);
objects.append(&obj.step) catch unreachable;
}
for (src_files_asm.toSlice()) |file| {
var file_src = concat(b.allocator, src_path2.toSlice(), file) catch unreachable;
file_src.append(".s") catch unreachable;
const obj = b.addAssemble(file, file_src.toSlice());
obj.setOutputDir(build_path);
obj.setTarget(target, builtin.Os.freestanding, builtin.Abi.gnu);
objects.append(&obj.step) catch unreachable;

View file

@ -1,11 +1,54 @@
// Zig version: 0.4.0
const is_test = @import("builtin").is_test;
const builtin = @import("builtin");
const gdt = @import("gdt.zig");
const idt = @import("idt.zig");
const irq = @import("irq.zig");
const isr = @import("isr.zig");
pub const InterruptContext = struct {
// Extra segments
gs: u32,
fs: u32,
es: u32,
ds: u32,
// Destination, source, base pointer
edi: u32,
esi: u32,
ebp: u32,
esp: u32,
// General registers
ebx: u32,
edx: u32,
ecx: u32,
eax: u32,
// Interrupt number and error code
int_num: u32,
error_code: u32,
// Instruction pointer, code segment and flags
eip: u32,
cs: u32,
eflags: u32,
user_esp: u32,
ss: u32,
};
///
/// Initialise the architecture
///
pub fn init() void {}
pub fn init() void {
disableInterrupts();
gdt.init();
idt.init();
isr.init();
irq.init();
}
///
/// Inline assembly to write to a given port with a byte of data.
@ -45,3 +88,81 @@ pub fn inb(port: u16) u8 {
pub fn ioWait() void {
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
/// registers.
///
/// Arguments:
/// IN gdt_ptr: *gdt.GdtPtr - The address to the GDT.
///
pub fn lgdt(gdt_ptr: *const gdt.GdtPtr) void {
// Load the GDT into the CPU
asm volatile ("lgdt (%%eax)" : : [gdt_ptr] "{eax}" (gdt_ptr));
// Load the kernel data segment, index into the GDT
asm volatile ("mov %%bx, %%ds" : : [KERNEL_DATA_OFFSET] "{bx}" (gdt.KERNEL_DATA_OFFSET));
asm volatile ("mov %%bx, %%es");
asm volatile ("mov %%bx, %%fs");
asm volatile ("mov %%bx, %%gs");
asm volatile ("mov %%bx, %%ss");
// Load the kernel code segment into the CS register
asm volatile (
\\ljmp $0x08, $1f
\\1:
);
}
///
/// Load the TSS into the CPU.
///
pub fn ltr() void {
asm volatile ("ltr %%ax" : : [TSS_OFFSET] "{ax}" (gdt.TSS_OFFSET));
}
/// Load the IDT into the CPU.
pub fn lidt(idt_ptr: *const idt.IdtPtr) void {
asm volatile ("lidt (%%eax)" : : [idt_ptr] "{eax}" (idt_ptr));
}
///
/// Enable interrupts
///
pub fn enableInterrupts() void {
asm volatile ("sti");
}
///
/// Disable interrupts
///
pub fn disableInterrupts() void {
asm volatile ("cli");
}
///
/// Halt the CPU, but interrupts will still be called
///
pub fn halt() void {
asm volatile ("hlt");
}
///
/// Wait the kernel but still can handle interrupts.
///
pub fn spinWait() noreturn {
while (true) {
enableInterrupts();
halt();
disableInterrupts();
}
}
///
/// Halt the kernel.
///
pub fn haltNoInterrupts() noreturn {
while (true) {
disableInterrupts();
halt();
}
}

293
src/kernel/arch/x86/gdt.zig Normal file
View file

@ -0,0 +1,293 @@
// Zig version: 0.4.0
const arch = @import("arch.zig");
const NUMBER_OF_ENTRIES: u16 = 0x06;
const TABLE_SIZE: u16 = @sizeOf(GdtEntry) * NUMBER_OF_ENTRIES - 1;
// The indexes into the GDT where each segment resides.
/// The index of the NULL GDT entry.
const NULL_INDEX: u16 = 0x00;
/// The index of the kernel code GDT entry.
const KERNEL_CODE_INDEX: u16 = 0x01;
/// The index of the kernel data GDT entry.
const KERNEL_DATA_INDEX: u16 = 0x02;
/// The index of the user code GDT entry.
const USER_CODE_INDEX: u16 = 0x03;
/// The index of the user data GDT entry.
const USER_DATA_INDEX: u16 = 0x04;
/// The index of the task state segment GDT entry.
const TSS_INDEX: u16 = 0x05;
// The offsets into the GDT where each segment resides.
/// The offset of the NULL GDT entry.
pub const NULL_OFFSET: u16 = 0x00;
/// The offset of the kernel code GDT entry.
pub const KERNEL_CODE_OFFSET: u16 = 0x08;
/// The offset of the kernel data GDT entry.
pub const KERNEL_DATA_OFFSET: u16 = 0x10;
/// The offset of the user code GDT entry.
pub const USER_CODE_OFFSET: u16 = 0x18;
/// The offset of the user data GDT entry.
pub const USER_DATA_OFFSET: u16 = 0x20;
/// The offset of the TTS GDT entry.
pub const TSS_OFFSET: u16 = 0x28;
// The access bits
const ACCESSED_BIT = 0x01; // 00000001
const WRITABLE_BIT = 0x02; // 00000010
const DIRECTION_CONFORMING_BIT = 0x04; // 00000100
const EXECUTABLE_BIT = 0x08; // 00001000
const DESCRIPTOR_BIT = 0x10; // 00010000
const PRIVILEGE_RING_0 = 0x00; // 00000000
const PRIVILEGE_RING_1 = 0x20; // 00100000
const PRIVILEGE_RING_2 = 0x40; // 01000000
const PRIVILEGE_RING_3 = 0x60; // 01100000
const PRESENT_BIT = 0x80; // 10000000
const KERNEL_SEGMENT = PRESENT_BIT | PRIVILEGE_RING_0 | DESCRIPTOR_BIT;
const USER_SEGMENT = PRESENT_BIT | PRIVILEGE_RING_3 | DESCRIPTOR_BIT;
const CODE_SEGMENT = EXECUTABLE_BIT | WRITABLE_BIT;
const DATA_SEGMENT = WRITABLE_BIT;
const TSS_SEGMENT = PRESENT_BIT | EXECUTABLE_BIT | ACCESSED_BIT;
// The flag bits
const IS_64_BIT = 0x02; // 0010
const IS_32_BIT = 0x04; // 0100
const IS_LIMIT_4K_BIT = 0x08; // 1000
/// The structure that contains all the information that each GDT entry needs.
const GdtEntry = packed struct {
/// The lower 16 bits of the limit address. Describes the size of memory that can be addressed.
limit_low: u16,
/// The lower 24 bits of the base address. Describes the start of memory for the entry.
base_low: u24,
/// Bit 0 : accessed - The CPU will set this when the GDT entry is accessed.
/// Bit 1 : writable - The writable bit to say if the memory region is writable. If set, then memory region is readable and writable. If not set, then the memory region is just readable.
/// Bit 2 : direction_conforming - For a code segment: if set (1), then the code segment can be executed from a lower ring level. If unset (0), then the code segment can only be executed from the same ring level in the privilege flag. For the data segment: if set (1), then the data segment grows downwards. If unset (0), then the data segment grows upwards.
/// Bit 3 : executable - The execution bit to say that the memory region is executable.
/// Bit 4 : descriptor_bit - The descriptor bit.
/// Bit 5-6 : privilege - The ring level of the memory region.
/// Bit 7 : present - The present bit to tell that this GDT entry is present.
access: u8,
/// The upper 4 bits of the limit address. Describes the size of memory that can be addressed.
limit_high: u4,
/// Bit 0 : reserved_zero - This must always be zero.
/// Bit 1 : is_64bits - Whether this is a 64 bit system.
/// Bit 2 : is_32bits - Whether this is a 32 bit system.
/// Bit 3 : is_limit_4K - Whether paging is turned on, and each address is addressed as if it is a page number not physical/logical linear address.
flags: u4,
/// The upper 8 bits of the base address. Describes the start of memory for the entry.
base_high: u8,
};
/// The GDT pointer structure that contains the pointer to the beginning of the GDT and the number
/// of the table (minus 1). Used to load the GDT with LGDT instruction.
pub const GdtPtr = packed struct {
/// 16bit entry for the number of entries (minus 1).
limit: u16,
/// 32bit entry for the base address for the GDT.
base: *GdtEntry,
};
const TtsEntry = packed struct {
/// Pointer to the previous TSS entry
prev_tss: u32,
/// Ring 0 32 bit stack pointer.
esp0: u32,
/// Ring 0 32 bit stack pointer.
ss0: u32,
/// Ring 1 32 bit stack pointer.
esp1: u32,
/// Ring 1 32 bit stack pointer.
ss1: u32,
/// Ring 2 32 bit stack pointer.
esp2: u32,
/// Ring 2 32 bit stack pointer.
ss2: u32,
/// The CR3 control register 3.
cr3: u32,
/// 32 bit instruction pointer.
eip: u32,
/// 32 bit flags register.
eflags: u32,
/// 32 bit accumulator register.
eax: u32,
/// 32 bit counter register.
ecx: u32,
/// 32 bit data register.
edx: u32,
/// 32 bit base register.
ebx: u32,
/// 32 bit stack pointer register.
esp: u32,
/// 32 bit base pointer register.
ebp: u32,
/// 32 bit source register.
esi: u32,
/// 32 bit destination register.
edi: u32,
/// The extra segment.
es: u32,
/// The code segment.
cs: u32,
/// The stack segment.
ss: u32,
/// The data segment.
ds: u32,
/// A extra segment FS.
fs: u32,
/// A extra segment GS.
gs: u32,
/// The local descriptor table register.
ldtr: u32,
/// ?
trap: u16,
/// A pointer to a I/O port bitmap for the current task which specifies individual ports the program should have access to.
io_permissions_base_offset: u16,
};
///
/// Make a GDT entry.
///
/// Arguments:
/// IN access: u8 - The access bits for the descriptor.
/// IN flags: u4 - The flag bits for the descriptor.
///
/// Return:
/// A new GDT entry with the give access and flag bits set with the base at 0x00000000 and limit at 0xFFFFF.
///
fn makeEntry(base: u32, limit: u20, access: u8, flags: u4) GdtEntry {
return GdtEntry {
.limit_low = @truncate(u16, limit),
.base_low = @truncate(u24, base),
.access = access,
.limit_high = @truncate(u4, limit >> 16),
.flags = flags,
.base_high = @truncate(u8, base >> 24),
};
}
/// The GDT entry table of NUMBER_OF_ENTRIES entries.
var gdt_entries: [NUMBER_OF_ENTRIES]GdtEntry = []GdtEntry {
// Null descriptor
makeEntry(0, 0, 0, 0),
// Kernel Code
makeEntry(0, 0xFFFFF, KERNEL_SEGMENT | CODE_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
// Kernel Data
makeEntry(0, 0xFFFFF, KERNEL_SEGMENT | DATA_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
// User Code
makeEntry(0, 0xFFFFF, USER_SEGMENT | CODE_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
// User Data
makeEntry(0, 0xFFFFF, USER_SEGMENT | DATA_SEGMENT, IS_32_BIT | IS_LIMIT_4K_BIT),
// Fill in TSS at runtime
makeEntry(0, 0, 0, 0),
};
/// The GDT pointer that the CPU is loaded with that contains the base address of the GDT and the size.
const gdt_ptr: GdtPtr = GdtPtr {
.limit = TABLE_SIZE,
.base = &gdt_entries[0],
};
/// The task state segment entry.
var tss: TtsEntry = TtsEntry {
.prev_tss = 0,
.esp0 = undefined,
.ss0 = KERNEL_DATA_OFFSET,
.esp1 = 0,
.ss1 = 0,
.esp2 = 0,
.ss2 = 0,
.cr3 = 0,
.eip = 0,
.eflags = 0,
.eax = 0,
.ecx = 0,
.edx = 0,
.ebx = 0,
.esp = 0,
.ebp = 0,
.esi = 0,
.edi = 0,
.es = 0,
.cs = 0,
.ss = 0,
.ds = 0,
.fs = 0,
.gs = 0,
.ldtr = 0,
.trap = 0,
.io_permissions_base_offset = @sizeOf(TtsEntry),
};
pub fn setTssStack(esp0: u32) void {
tss.esp0 = esp0;
}
pub fn init() void {
// Initiate TSS
gdt_entries[TSS_INDEX] = makeEntry(@ptrToInt(&tss), @sizeOf(TtsEntry) - 1, TSS_SEGMENT, 0);
// Load the GDT
arch.lgdt(&gdt_ptr);
// Load the TSS
arch.ltr();
}

View file

@ -0,0 +1,85 @@
// Zig version: 0.4.0
const gdt = @import("gdt.zig");
const arch = @import("arch.zig");
const NUMBER_OF_ENTRIES: u16 = 256;
const TABLE_SIZE: u16 = @sizeOf(IdtEntry) * NUMBER_OF_ENTRIES - 1;
// The different gate types
const TASK_GATE_32BIT: u4 = 0x5;
const INTERRUPT_GATE_16BIT: u4 = 0x6;
const TRAP_GATE_16BIT: u4 = 0x7;
const INTERRUPT_GATE_32BIT: u4 = 0xE;
const TRAP_GATE_32BIT: u4 = 0xF;
const PRIVILEGE_RING_0: u2 = 0x0;
const PRIVILEGE_RING_1: u2 = 0x1;
const PRIVILEGE_RING_2: u2 = 0x2;
const PRIVILEGE_RING_3: u2 = 0x3;
const IdtEntry = packed struct {
/// The lower 16 bits of the base address of the interrupt handler offset.
base_low: u16,
/// The code segment in the GDT which the handlers will be held.
selector: u16,
/// Must be zero, unused.
zero: u8,
/// The IDT gate type.
gate_type: u4,
/// Must be 0 for interrupt and trap gates.
storage_segment: u1,
/// The minimum ring level that the calling code must have to run the handler. So user code may not be able to run some interrupts.
privilege: u2,
/// Whether the IDT entry is present.
present: u1,
/// The upper 16 bits of the base address of the interrupt handler offset.
base_high: u16,
};
pub const IdtPtr = packed struct {
/// The total size of the IDT (minus 1) in bytes.
limit: u16,
/// The base address where the IDT is located.
base: *IdtEntry,
};
var idt: [NUMBER_OF_ENTRIES]IdtEntry = []IdtEntry{makeEntry(0, 0, 0, 0, 0)} ** NUMBER_OF_ENTRIES;
const idt_ptr: IdtPtr = IdtPtr {
.limit = TABLE_SIZE,
.base = &idt[0],
};
fn makeEntry(base: u32, selector: u16, gate_type: u4, privilege: u2, present: u1) IdtEntry {
return IdtEntry {
.base_low = @truncate(u16, base),
.selector = selector,
.zero = 0,
.gate_type = gate_type,
.storage_segment = 0,
.privilege = privilege,
.present = present,
.base_high = @truncate(u16, base >> 16),
};
}
pub fn openInterruptGate(index: u8, base: extern fn()void) void {
idt[index] = makeEntry(@ptrToInt(base), gdt.KERNEL_CODE_OFFSET, INTERRUPT_GATE_32BIT, PRIVILEGE_RING_0, 1);
}
pub fn closeInterruptGate(index: u8) void {
idt[index] = makeEntry(0, 0, 0, 0, 0);
}
pub fn init() void {
arch.lidt(&idt_ptr);
}

155
src/kernel/arch/x86/irq.zig Normal file
View file

@ -0,0 +1,155 @@
// Zig version: 0.4.0
const panic = @import("../../panic.zig");
const tty = @import("../../tty.zig");
const idt = @import("idt.zig");
const arch = @import("arch.zig");
const NUMBER_OF_ENTRIES: u16 = 16;
const IRQ_OFFSET: u16 = 32;
extern fn irq0() void;
extern fn irq1() void;
extern fn irq2() void;
extern fn irq3() void;
extern fn irq4() void;
extern fn irq5() void;
extern fn irq6() void;
extern fn irq7() void;
extern fn irq8() void;
extern fn irq9() void;
extern fn irq10() void;
extern fn irq11() void;
extern fn irq12() void;
extern fn irq13() void;
extern fn irq14() void;
extern fn irq15() void;
// Keep track of the number of spurious irq's
var spurious_irq_counter: u32 = 0;
var irq_handlers: [NUMBER_OF_ENTRIES]fn(*arch.InterruptContext)void = []fn(*arch.InterruptContext)void {unhandled} ** NUMBER_OF_ENTRIES;
fn unhandled(context: *arch.InterruptContext) void {
const interrupt_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET);
panic.panicFmt(null, "Unhandled IRQ number {}", interrupt_num);
}
// TODO Move to PIC
fn sendEndOfInterrupt(irq_num: u8) void {
if (irq_num >= 8) {
arch.outb(0xA0, 0x20);
}
arch.outb(0x20, 0x20);
}
// TODO Move to PIC
fn spuriousIrq(irq_num: u8) bool {
// Only for IRQ 7 and 15
if(irq_num == 7) {
// Read master ISR
arch.outb(0x20, 0x0B);
const in_service = arch.inb(0x21);
// Check the MSB is zero, if so, then is a spurious irq
if ((in_service & 0x80) == 0) {
spurious_irq_counter += 1;
return true;
}
} else if (irq_num == 15) {
// Read slave ISR
arch.outb(0xA0, 0x0B);
const in_service = arch.inb(0xA1);
// Check the MSB is zero, if so, then is a spurious irq
if ((in_service & 0x80) == 0) {
// Need to send EOI to the master
arch.outb(0x20, 0x20);
spurious_irq_counter += 1;
return true;
}
}
return false;
}
export fn irqHandler(context: *arch.InterruptContext) void {
const irq_num: u8 = @truncate(u8, context.int_num - IRQ_OFFSET);
// Make sure it isn't a spurious irq
if (!spuriousIrq(irq_num)) {
irq_handlers[irq_num](context);
// Send the end of interrupt command
sendEndOfInterrupt(irq_num);
}
}
pub fn registerIrq(irq_num: u16, handler: fn(*arch.InterruptContext)void) void {
irq_handlers[irq_num] = handler;
clearMask(irq_num);
}
pub fn unregisterIrq(irq_num: u16) void {
irq_handlers[irq_num] = unhandled;
setMask(irq_num);
}
pub fn setMask(irq_num: u16) void {
// TODO Change to PIC constants
const port: u16 = if (irq_num < 8) 0x20 else 0xA0;
const value = arch.inb(port) | (1 << irq_num);
arch.outb(port, value);
}
pub fn clearMask(irq_num: u16) void {
// TODO Change to PIC constants
const port: u16 = if (irq_num < 8) 0x20 else 0xA0;
const value = arch.inb(port) & ~(1 << irq_num);
arch.outb(port, value);
}
// TODO Move this to the PIC once got one
fn remapIrq() void {
// Initiate
arch.outb(0x20, 0x11);
arch.outb(0xA0, 0x11);
// Offsets
arch.outb(0x21, IRQ_OFFSET);
arch.outb(0xA1, IRQ_OFFSET + 8);
// IRQ lines
arch.outb(0x21, 0x04);
arch.outb(0xA1, 0x02);
// 80x86 mode
arch.outb(0x21, 0x01);
arch.outb(0xA1, 0x01);
// Mask
arch.outb(0x21, 0xFF);
arch.outb(0xA1, 0xFF);
}
pub fn init() void {
// Remap the PIC IRQ so not to overlap with other exceptions
remapIrq();
// Open all the IRQ's
idt.openInterruptGate(32, irq0);
idt.openInterruptGate(33, irq1);
idt.openInterruptGate(34, irq2);
idt.openInterruptGate(35, irq3);
idt.openInterruptGate(36, irq4);
idt.openInterruptGate(37, irq5);
idt.openInterruptGate(38, irq6);
idt.openInterruptGate(39, irq7);
idt.openInterruptGate(40, irq8);
idt.openInterruptGate(41, irq9);
idt.openInterruptGate(42, irq10);
idt.openInterruptGate(43, irq11);
idt.openInterruptGate(44, irq12);
idt.openInterruptGate(45, irq13);
idt.openInterruptGate(46, irq14);
idt.openInterruptGate(47, irq15);
}

View file

@ -0,0 +1,70 @@
.macro irqGenerator n
.align 4
.type irq\n, @function
.global irq\n
irq\n:
cli
push $0
push $\n+32
jmp irqCommonStub
.endmacro
irqCommonStub:
// Push all the registers
pusha
// Push the additional segment regiters
push %ds
push %es
push %fs
push %gs
// Set the kernel data segment
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
// Push the stack, this is where all the registers are sported, points the interuptContect
mov %esp, %eax
push %eax
// Call the handler
call irqHandler
// Pop stack pointer to point to the registers pushed
pop %eax
// Pop segment regiters inorder
pop %gs
pop %fs
pop %es
pop %ds
// Pop all general registers
popa
// Pop the error code and interrupt number
add $0x8, %esp
// Pops 5 things at once: cs, eip, eflags, ss, and esp
iret
.type irqCommonStub, @function
irqGenerator 0
irqGenerator 1
irqGenerator 2
irqGenerator 3
irqGenerator 4
irqGenerator 5
irqGenerator 6
irqGenerator 7
irqGenerator 8
irqGenerator 9
irqGenerator 10
irqGenerator 11
irqGenerator 12
irqGenerator 13
irqGenerator 14
irqGenerator 15

131
src/kernel/arch/x86/isr.zig Normal file
View file

@ -0,0 +1,131 @@
// Zig version: 0.4.0
const panic = @import("../../panic.zig");
const tty = @import("../../tty.zig");
const idt = @import("idt.zig");
const arch = @import("arch.zig");
const NUMBER_OF_ENTRIES: u16 = 32;
extern fn isr0() void;
extern fn isr1() void;
extern fn isr2() void;
extern fn isr3() void;
extern fn isr4() void;
extern fn isr5() void;
extern fn isr6() void;
extern fn isr7() void;
extern fn isr8() void;
extern fn isr9() void;
extern fn isr10() void;
extern fn isr11() void;
extern fn isr12() void;
extern fn isr13() void;
extern fn isr14() void;
extern fn isr15() void;
extern fn isr16() void;
extern fn isr17() void;
extern fn isr18() void;
extern fn isr19() void;
extern fn isr20() void;
extern fn isr21() void;
extern fn isr22() void;
extern fn isr23() void;
extern fn isr24() void;
extern fn isr25() void;
extern fn isr26() void;
extern fn isr27() void;
extern fn isr28() void;
extern fn isr29() void;
extern fn isr30() void;
extern fn isr31() void;
const exception_msg: [NUMBER_OF_ENTRIES][]const u8 = [NUMBER_OF_ENTRIES][]const u8 {
"Divide By Zero",
"Single Step (Debugger)",
"Non Maskable Interrupt",
"Breakpoint (Debugger)",
"Overflow",
"Bound Range Exceeded",
"Invalid Opcode",
"No Coprocessor, Device Not Available",
"Double Fault",
"Coprocessor Segment Overrun",
"Invalid Task State Segment (TSS)",
"Segment Not Present",
"Stack Segment Overrun",
"General Protection Fault",
"Page Fault",
"Unknown Interrupt",
"x87 FPU Floating Point Error",
"Alignment Check",
"Machine Check",
"SIMD Floating Point",
"Virtualization",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Reserved",
"Security",
"Reserved"
};
var isr_handlers: [NUMBER_OF_ENTRIES]fn(*arch.InterruptContext)void = []fn(*arch.InterruptContext)void{unhandled} ** NUMBER_OF_ENTRIES;
fn unhandled(context: *arch.InterruptContext) void {
const interrupt_num = context.int_num;
panic.panicFmt(null, "Unhandled exception: {}, number {}", exception_msg[interrupt_num], interrupt_num);
}
export fn isrHandler(context: *arch.InterruptContext) void {
const isr_num = context.int_num;
isr_handlers[isr_num](context);
}
pub fn registerIsr(isr_num: u16, handler: fn(*arch.InterruptContext)void) void {
isr_handlers[isr_num] = handler;
}
pub fn unregisterIsr(isr_num: u16) void {
isr_handlers[isr_num] = unhandled;
}
pub fn init() void {
idt.openInterruptGate(0, isr0);
idt.openInterruptGate(1, isr1);
idt.openInterruptGate(2, isr2);
idt.openInterruptGate(3, isr3);
idt.openInterruptGate(4, isr4);
idt.openInterruptGate(5, isr5);
idt.openInterruptGate(6, isr6);
idt.openInterruptGate(7, isr7);
idt.openInterruptGate(8, isr8);
idt.openInterruptGate(9, isr9);
idt.openInterruptGate(10, isr10);
idt.openInterruptGate(11, isr11);
idt.openInterruptGate(12, isr12);
idt.openInterruptGate(13, isr13);
idt.openInterruptGate(14, isr14);
idt.openInterruptGate(15, isr15);
idt.openInterruptGate(16, isr16);
idt.openInterruptGate(17, isr17);
idt.openInterruptGate(18, isr18);
idt.openInterruptGate(19, isr19);
idt.openInterruptGate(20, isr20);
idt.openInterruptGate(21, isr21);
idt.openInterruptGate(22, isr22);
idt.openInterruptGate(23, isr23);
idt.openInterruptGate(24, isr24);
idt.openInterruptGate(25, isr25);
idt.openInterruptGate(26, isr26);
idt.openInterruptGate(27, isr27);
idt.openInterruptGate(28, isr28);
idt.openInterruptGate(29, isr29);
idt.openInterruptGate(30, isr30);
idt.openInterruptGate(31, isr31);
}

View file

@ -0,0 +1,89 @@
.macro isrGenerator n
.align 4
.type isr\n, @function
.global isr\n
isr\n:
cli
// Push 0 if there is no interrupt error code
.if (\n != 8 && !(\n >= 10 && \n <= 14) && \n != 17)
push $0
.endif
push $\n
jmp isrCommonStub
.endmacro
isrCommonStub:
// Push all the registers
pusha
// Push the additional segment regiters
push %ds
push %es
push %fs
push %gs
// Set the kernel data segment
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
// Push the stack, this is where all the registers are sported, points the interuptContect
mov %esp, %eax
push %eax
// Call the handler
call isrHandler
// Pop stack pointer to point to the registers pushed
pop %eax
// Pop segment regiters inorder
pop %gs
pop %fs
pop %es
pop %ds
// Pop all general registers
popa
// Pop the error code and interrupt number
add $0x8, %esp
// Pops 5 things at once: cs, eip, eflags, ss, and esp
iret
.type isrCommonStub, @function
isrGenerator 0
isrGenerator 1
isrGenerator 2
isrGenerator 3
isrGenerator 4
isrGenerator 5
isrGenerator 6
isrGenerator 7
isrGenerator 8
isrGenerator 9
isrGenerator 10
isrGenerator 11
isrGenerator 12
isrGenerator 13
isrGenerator 14
isrGenerator 15
isrGenerator 16
isrGenerator 17
isrGenerator 18
isrGenerator 19
isrGenerator 20
isrGenerator 21
isrGenerator 22
isrGenerator 23
isrGenerator 24
isrGenerator 25
isrGenerator 26
isrGenerator 27
isrGenerator 28
isrGenerator 29
isrGenerator 30
isrGenerator 31

View file

@ -6,10 +6,15 @@ const multiboot = @import("multiboot.zig");
const tty = @import("tty.zig");
const vga = @import("vga.zig");
// Need to import this as we need the panic to be in the root source file, or zig will just use the
// builtin panic and just loop, which is what we don't want
const panic_root = @import("panic.zig");
// Just call the panic function, as this need to be in the root source file
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
@setCold(true);
tty.print("\nKERNEL PANIC: {}\n", msg);
while (true) {}
arch.disableInterrupts();
panic_root.panicFmt(error_return_trace, "{}", msg);
}
pub export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void {
@ -18,6 +23,9 @@ pub export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void {
arch.init();
vga.init();
tty.init();
tty.print("\nHello Pluto from kernel :)\n");
tty.print("Hello Pluto from kernel :)\n");
// Enable interrupts
arch.enableInterrupts();
}
}

13
src/kernel/panic.zig Normal file
View file

@ -0,0 +1,13 @@
// Zig version: 0.4.0
const builtin = @import("builtin");
const tty = @import("tty.zig");
const arch = if (builtin.is_test) @import("../../test/kernel/arch_mock.zig") else @import("arch.zig").internals;
pub fn panicFmt(trace: ?*builtin.StackTrace, comptime format: []const u8, args: ...) noreturn {
@setCold(true);
tty.print("KERNEL PANIC\n");
tty.print(format, args);
tty.print("\nHALTING\n");
arch.haltNoInterrupts();
}

View file

@ -641,6 +641,7 @@ pub fn init() void {
printLogo();
displayPageNumber();
updateCursor();
}
fn resetGlobals() void {

View file

@ -14,7 +14,6 @@ const PORT_DATA: u16 = 0x03D5;
// The indexes that is passed to the address port to select the register for the data to be
// read or written to.
const REG_HORIZONTAL_TOTAL: u8 = 0x00;
const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01;
const REG_START_HORIZONTAL_BLINKING: u8 = 0x02;
@ -144,7 +143,7 @@ pub fn updateCursor(x: u16, y: u16) void {
var pos_lower: u16 = undefined;
// Make sure new cursor position is within the screen
if (x < HEIGHT and y < WIDTH) {
if (x < WIDTH and y < HEIGHT) {
pos = y * WIDTH + x;
} else {
// If not within the screen, then just put the cursor at the very end
@ -240,8 +239,6 @@ pub fn init() void {
// Set by default the underline cursor
setCursorShape(CursorShape.UNDERLINE);
cursor_scanline_start = CURSOR_SCANLINE_MIDDLE;
cursor_scanline_end = CURSOR_SCANLINE_END;
}
test "entryColour" {