Added GDT, IDT, IRQ, updated build.zig
Added new build.zig Interrupts no work :( Added isr Interrupts work Added spurious irq Code review comments New name Refactor Build asm
This commit is contained in:
parent
d22b79816b
commit
995268b04e
12 changed files with 1009 additions and 11 deletions
39
build.zig
39
build.zig
|
@ -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;
|
||||
|
|
|
@ -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
293
src/kernel/arch/x86/gdt.zig
Normal 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();
|
||||
}
|
85
src/kernel/arch/x86/idt.zig
Normal file
85
src/kernel/arch/x86/idt.zig
Normal 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
155
src/kernel/arch/x86/irq.zig
Normal 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);
|
||||
}
|
70
src/kernel/arch/x86/irq_asm.s
Normal file
70
src/kernel/arch/x86/irq_asm.s
Normal 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
131
src/kernel/arch/x86/isr.zig
Normal 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);
|
||||
}
|
89
src/kernel/arch/x86/isr_asm.s
Normal file
89
src/kernel/arch/x86/isr_asm.s
Normal 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
|
|
@ -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
13
src/kernel/panic.zig
Normal 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();
|
||||
}
|
|
@ -641,6 +641,7 @@ pub fn init() void {
|
|||
|
||||
printLogo();
|
||||
displayPageNumber();
|
||||
updateCursor();
|
||||
}
|
||||
|
||||
fn resetGlobals() void {
|
||||
|
|
|
@ -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" {
|
||||
|
|
Loading…
Reference in a new issue