Merge pull request #29 from SamTebbs33/feature/create-gdt-idt-irq-isr
Added GDT, IDT, IRQ, updated build.zig
This commit is contained in:
commit
144dffe628
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 Step = @import("std").build.Step;
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const os = std.os;
|
||||||
const ArrayList = std.ArrayList;
|
const ArrayList = std.ArrayList;
|
||||||
const warn = std.debug.warn;
|
const warn = std.debug.warn;
|
||||||
const mem = std.mem;
|
const mem = std.mem;
|
||||||
|
|
||||||
var src_files: ArrayList([]const u8) = undefined;
|
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 {
|
fn concat(allocator: *std.mem.Allocator, str: []const u8, str2: []const u8) !std.Buffer {
|
||||||
var b = try std.Buffer.init(allocator, str);
|
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 {
|
pub fn build(b: *Builder) void {
|
||||||
src_files = ArrayList([]const u8).init(b.allocator);
|
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;
|
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 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 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";
|
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 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;
|
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;
|
var arch_boot = concat(b.allocator, "kernel/arch/", target) catch unreachable;
|
||||||
arch_boot.append("/boot") catch unreachable;
|
arch_boot.append("/boot") catch unreachable;
|
||||||
src_files.append(arch_boot.toSlice()) 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 objects_steps = buildObjects(b, builtin_target, build_path, src_path);
|
||||||
var link_step = buildLink(b, builtin_target, build_path);
|
var link_step = buildLink(b, builtin_target, build_path);
|
||||||
const iso_step = buildISO(b, build_path, iso_path.toSlice());
|
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(file) catch unreachable;
|
||||||
file_obj.append(".o") catch unreachable;
|
file_obj.append(".o") catch unreachable;
|
||||||
exec.addObjectFile(file_obj.toSlice());
|
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;
|
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;
|
var file_src = concat(b.allocator, src_path2.toSlice(), file) catch unreachable;
|
||||||
file_src.append(".zig") catch unreachable;
|
file_src.append(".zig") catch unreachable;
|
||||||
const obj = b.addObject(file, file_src.toSlice());
|
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.setOutputDir(build_path);
|
||||||
obj.setTarget(target, builtin.Os.freestanding, builtin.Abi.gnu);
|
obj.setTarget(target, builtin.Os.freestanding, builtin.Abi.gnu);
|
||||||
objects.append(&obj.step) catch unreachable;
|
objects.append(&obj.step) catch unreachable;
|
||||||
|
|
|
@ -1,11 +1,54 @@
|
||||||
// Zig version: 0.4.0
|
// 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
|
/// 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.
|
/// 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 {
|
pub fn ioWait() void {
|
||||||
outb(0x80, 0);
|
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 tty = @import("tty.zig");
|
||||||
const vga = @import("vga.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 {
|
pub fn panic(msg: []const u8, error_return_trace: ?*builtin.StackTrace) noreturn {
|
||||||
@setCold(true);
|
@setCold(true);
|
||||||
tty.print("\nKERNEL PANIC: {}\n", msg);
|
arch.disableInterrupts();
|
||||||
while (true) {}
|
panic_root.panicFmt(error_return_trace, "{}", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub export fn kmain(mb_info: *multiboot.multiboot_info_t, mb_magic: u32) void {
|
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();
|
arch.init();
|
||||||
vga.init();
|
vga.init();
|
||||||
tty.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();
|
printLogo();
|
||||||
displayPageNumber();
|
displayPageNumber();
|
||||||
|
updateCursor();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resetGlobals() void {
|
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
|
// The indexes that is passed to the address port to select the register for the data to be
|
||||||
// read or written to.
|
// read or written to.
|
||||||
|
|
||||||
const REG_HORIZONTAL_TOTAL: u8 = 0x00;
|
const REG_HORIZONTAL_TOTAL: u8 = 0x00;
|
||||||
const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01;
|
const REG_HORIZONTAL_DISPLAY_ENABLE_END: u8 = 0x01;
|
||||||
const REG_START_HORIZONTAL_BLINKING: u8 = 0x02;
|
const REG_START_HORIZONTAL_BLINKING: u8 = 0x02;
|
||||||
|
@ -144,7 +143,7 @@ pub fn updateCursor(x: u16, y: u16) void {
|
||||||
var pos_lower: u16 = undefined;
|
var pos_lower: u16 = undefined;
|
||||||
|
|
||||||
// Make sure new cursor position is within the screen
|
// 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;
|
pos = y * WIDTH + x;
|
||||||
} else {
|
} else {
|
||||||
// If not within the screen, then just put the cursor at the very end
|
// 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
|
// Set by default the underline cursor
|
||||||
setCursorShape(CursorShape.UNDERLINE);
|
setCursorShape(CursorShape.UNDERLINE);
|
||||||
cursor_scanline_start = CURSOR_SCANLINE_MIDDLE;
|
|
||||||
cursor_scanline_end = CURSOR_SCANLINE_END;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
test "entryColour" {
|
test "entryColour" {
|
||||||
|
|
Loading…
Reference in a new issue