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 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…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 ED
						ED