gdt.c
Code: Select all
/*
*gdt.c by
*This file contains everything related to GDT and stuff.
*
* The GDT is a table, or rather an array of elements
*called "segment descriptors". Segment descriptors are basically structs which contain information about the segments itself(where it
*begins, its size, whether its available for use etc), and the permissions (kernel level, or userlevel) etc.
*What we need to do is call upon the holy instruction "lgdtr DWORD PTR address_of_the_first_element_in_the_array", then we'll be fine.
*Before we proceed though, let's see what a segment is made up of-
*=========================================================================================
*| Base | G | size | R | avl | Seg. Lim | Present | Privilege | DType | SType | Base |
*| 31:24 | | | | | 19:16 | | | | | 23:16 |
*=========================================================================================
*^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^
*63 55 54 53 52 51 47 46 44 43 39 32
*
*========================
*| Base addr | Seg. Lim|
*| 15:00 | 15:00 |
*========================
*^ ^ ^
*31 15 0
*Base/Base addr -> The base of the segment
*G -> Granularity (0 => 1 byte, 1 => 4 KiB)
*size -> Default size of operation (0 => 16-bit segment, 1 =>32-bit segment)
*R -> Reserved (Available for 64 bit OSes, look it up in the intel manual Volume 3), set to 0
*avl -> Whether the segment is available for use
*Seg. Lim -> The Limit of the Segment (It's size - 1 byte)
*Present -> Whether the segment is in memory (do you have some hardware mapped to that address space, or is it empty?)
*Previlege -> Kernel level code execution or User level execution
*DType -> Type of descriptor (0 => Segment, 1 => Code/Data)
*SType -> Type of Segment
**/
#include "descriptors.h"
#include <stdint.h>
#define KERNEL_LEVEL 0b00
#define USER_LEVEL 0b11
extern void lgdt(void* gdt_ptr);//This is the function which will actually load our GDT, it's an asm only instruction. I find asm() too ugly, so I'll define it in a separate .s file
extern void jump_gdt();//Use this to far jump to the gdt Code segement
struct G_descriptor_t{
uint16_t seg_lim_low;//The lower 16 bits of the segment limit
uint16_t base_low;//The lower 16 bits of the base address
uint8_t base_mid;//Bits 16:23 of the base address
uint8_t flags;//Refer to the block diagram
/*uint8_t lim_high : 4;//The higher 4 bits of the segment limit
uint8_t avl : 1;//The availability flags
uint8_t R : 1;//Reserved
uint8_t size : 1;//The default size of operations*/
uint8_t gran;
uint8_t base_high;//The highest 8 bits of the base address
}__attribute__((packed));
typedef struct G_descriptor_t G_descriptor;
struct gdt_ptr_t{
uint16_t limit;
uint32_t base;
}__attribute((packed));
typedef struct gdt_ptr_t gdt_ptr;
//We don't have a memory manager on us right now, so forget about malloc-ing for dynamic memory. But this will change once a memory manager has been implemented
//TODO: Change to dynamic memory model.
G_descriptor G_descriptors[4];
gdt_ptr gdt_pointer;
uint8_t make_flags(uint8_t present, uint8_t privilege, uint8_t DType, uint8_t SType){
return (present << 7) | (privilege << 5) | (DType << 4) | (SType);
}
void init_gdt(){
asm volatile("cli");
G_descriptors[0] = (G_descriptor){0, 0, 0, 0, 0, /*0, 0, 0, */0};//Null descriptor. Usefule for debugging
G_descriptors[1] = (G_descriptor){0xFFFF, 0x00, 0x00, make_flags(1, KERNEL_LEVEL, 1, 0xA), /*0xF, 1, 0, 1*/0xCF, 0x00};//Code segment
G_descriptors[2] = (G_descriptor){0xFFFF, 0x00, 0x00, make_flags(1, KERNEL_LEVEL, 1, 0x2), /*0xF, 1, 0, 1*/0xCF, 0x00};//Text segment
G_descriptors[3] = (G_descriptor){0, 0, 0, 0, 0, 0, /*0, 0, 0*/};//Null descriptor 'cuz this where the TSS wil go. TODO:Implement TSS
gdt_pointer = (gdt_ptr){sizeof(G_descriptor)*4 - 1, G_descriptors};
lgdt(&gdt_pointer);
asm volatile("sti");
//jump_gdt();
}
Code: Select all
;;; gdt.s
;;; The function lgdt and jump_gdt is defined here.
global lgdt
section .text
lgdt:
push ebp
mov ebp, esp
; lgdt [ebp + 8]
; pop ebp
; ret
mov eax, [ebp + 8] ; Get the pointer to the GDT, passed as a parameter.
lgdt [eax] ; Load the new GDT pointer
mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment
mov ds, ax ; Load all data segment selectors
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:.flush ; 0x08 is the offset to our code segment: Far jump!
.flush:
pop ebp
ret
global jump_gdt
jump_gdt:
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
jmp 0x08:.flush
.flush:
ret
Code: Select all
/**
idt.c
This file contains all the information needed to fill the IDT.
*/
#include "descriptors.h"
#include <stddef.h>
#include <stdint.h>
#include <common.h>
#include <libc/string/string.h>
extern void lidt(void* idt_ptr);
//Interrupts
extern void reserved_handler();
extern void divide_error_handler();
extern void debug_exception_handler();
extern void nmi_handler();
extern void breakpoint_handler();
extern void overflow_handler();
extern void bound_exceeded_handler();
extern void undefined_opcode_handler();
extern void device_NA_handler();
extern void double_fault_handler();
extern void coprocessor_segment_overrun_handler();
extern void invalid_tss_handler();
extern void segment_NA_handler();
extern void stack_segment_fault_handler();
extern void general_protection_fault_handler();
extern void page_fault_handler();
extern void math_fault_handler();
extern void machine_check_abort_handler();
extern void alignment_check_handler();
extern void simd_exception_handler();
typedef struct I_descriptor_T{
uint16_t address_low;//The lower word of the address to the entry point of the interrupt
uint16_t selector;//Points to a valid selector in the GDT
uint8_t zero;//Must always be zero
uint8_t type_attrib;//See above
uint16_t address_high;//Higher word of the address to the entry point of tyhe ISR
}__attribute__((packed)) I_descriptor;
typedef struct idt_ptr_T{
uint32_t base_address;//Base address of the IDT
uint16_t limit;//Limit of the IDT
}__attribute__((packed)) idt_ptr;
I_descriptor idt_entries[256];
idt_ptr idt_pointer;
static uint8_t make_flags(uint8_t present, uint8_t dpl, uint8_t isInterrupt, uint8_t size){
return (present << 7) | (dpl << 5) | (isInterrupt?(0b110):(0b111)) | ((size & 0x01) << 3) ;
}
I_descriptor make_interrupt_gate(uint32_t interrupt_handler, uint16_t segment, uint8_t type_attrib){
return (I_descriptor){interrupt_handler & 0xFFFF, segment, 0, type_attrib, ((interrupt_handler & (0xFFFF0000)) >> 16)};
}
I_descriptor make_null_gate(){
//TODO Make a null interrupt handler
return make_interrupt_gate(0, 0, 0);
}
void init_idt(){
asm volatile("cli");
idt_pointer.base_address = idt_entries;
idt_pointer.limit = sizeof(I_descriptor)*256 - 1;
//strlen("Hello!");
memset(idt_entries, 0, sizeof(idt_entries));
unsigned int i = 0;
idt_entries[i++] = make_interrupt_gate((uint32_t)divide_error_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)debug_exception_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)nmi_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)breakpoint_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)overflow_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)bound_exceeded_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)undefined_opcode_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)device_NA_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)double_fault_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)coprocessor_segment_overrun_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)invalid_tss_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)segment_NA_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)stack_segment_fault_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)general_protection_fault_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)page_fault_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)math_fault_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)machine_check_abort_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)alignment_check_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)simd_exception_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
idt_entries[i++] = make_interrupt_gate((uint32_t)reserved_handler, 0x08, make_flags(1, 0, 1, 1));
asm volatile("sti");
lidt(&idt_pointer);
}
void test_idt(){
asm volatile("int $0x3");
asm volatile("cli");//NOTE:DEBUG ONLY
}
Code: Select all
#include <stddef.h>
#include <stdint.h>
#include <common.h>
#include <vga.h>
void null_isr_handler(uint8_t error_code){
WARN("ISR handler run - With error code");
}
void null_isr_handler_no_error(){
WARN("ISR handler run - With error code");
}
Code: Select all
ALIGN 4
section .data
message:
db "Recieved interrupt", 0x00
section .text
extern null_isr_handler
extern term_print
extern null_isr_handler_no_error
%macro NULL_ISR_NOERROR 1
global %1
%1:
; pushad
; cld
; push message
; call term_print
; popad
; iret
cli
push byte 0
push byte 0
jmp isr_common_stub
%endmacro
%macro NULL_ISR_ERROR 1
global %1
%1:
; pushad
; cld
; push message
; call term_print
; popad
; iret
cli
push byte 0
jmp isr_common_stub
%endmacro
%macro ISR_HANDLER_NOERROR 1
global %1
%1:
call [%1]_c
iret
%endmacro
isr_common_stub:
pusha ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax
mov ax, ds ; Lower 16-bits of eax = ds.
push eax ; save the data segment descriptor
mov ax, 0x10 ; load the kernel data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call null_isr_handler_no_error
pop eax ; reload the original data segment descriptor
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa ; Pops edi,esi,ebp...
add esp, 8 ; Cleans up the pushed error code and pushed ISR number
sti
iret ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
NULL_ISR_NOERROR divide_error_handler
NULL_ISR_NOERROR debug_exception_handler
NULL_ISR_NOERROR nmi_handler
NULL_ISR_NOERROR breakpoint_handler
NULL_ISR_NOERROR overflow_handler
NULL_ISR_NOERROR bound_exceeded_handler
NULL_ISR_NOERROR undefined_opcode_handler
NULL_ISR_NOERROR device_NA_handler
NULL_ISR_ERROR double_fault_handler
NULL_ISR_NOERROR coprocessor_segment_overrun_handler
NULL_ISR_ERROR invalid_tss_handler
NULL_ISR_ERROR segment_NA_handler
NULL_ISR_ERROR stack_segment_fault_handler
NULL_ISR_ERROR general_protection_fault_handler
NULL_ISR_ERROR page_fault_handler
NULL_ISR_NOERROR math_fault_handler
NULL_ISR_NOERROR machine_check_abort_handler
NULL_ISR_ERROR alignment_check_handler
NULL_ISR_NOERROR simd_exception_handler
NULL_ISR_NOERROR reserved_handler
WARN btw, is nothing more than a macro which sets colors, prints the parameter, and then resets the colors to their original values. It's enclosed in curly braces, so it runs in its own scope. In the last piece of code, I've tried replacing popa/pusha with popad/pushad, but it doesn't seem to work. My interrupt asm code didn't work, so the current one is a modified version of James Molloy's tutorial. The stuff that's commented out in the nasm macros are the original ISRs, written by me, btw.
Thanks