diff --git a/boot.o b/boot.o deleted file mode 100644 index 68fc8c6..0000000 Binary files a/boot.o and /dev/null differ diff --git a/gdt.o b/gdt.o deleted file mode 100644 index 6034c8b..0000000 Binary files a/gdt.o and /dev/null differ diff --git a/include/gdt.h b/include/gdt.h index 8b239dd..51a7112 100644 --- a/include/gdt.h +++ b/include/gdt.h @@ -14,4 +14,8 @@ struct GDT_ptr { uint32_t base; } __attribute((packed)); +uint8_t gdt_entries[8 * 3]; // (8 bytes per entry) * (num entries) + +void encodeGDT(uint8_t* gdtEntry, struct GDT source); +void initializeGDT(); #endif //GDT_H diff --git a/include/idt.h b/include/idt.h new file mode 100644 index 0000000..c08cf75 --- /dev/null +++ b/include/idt.h @@ -0,0 +1,56 @@ +#ifndef IDT_H +#define IDT_H + +#include "types.h" + +struct IDT { + uint32_t base; + uint16_t selector; + uint8_t flags; +} __attribute__((packed)); + +struct IDT_ptr { + uint16_t limit; + uint32_t base; +} __attribute__((packed)); + +uint8_t idt_entries[8 * 256]; // 8 bytes per entry * 256 entries + +struct IDT createIDT(uint32_t base, uint16_t selector, uint8_t flags); +void encodeIDT(uint8_t* idtEntry, struct IDT source); +void initializeIDT(); + +extern void isr0 (); +extern void isr1 (); +extern void isr2 (); +extern void isr3 (); +extern void isr4 (); +extern void isr5 (); +extern void isr6 (); +extern void isr7 (); +extern void isr8 (); +extern void isr9 (); +extern void isr10(); +extern void isr11(); +extern void isr12(); +extern void isr13(); +extern void isr14(); +extern void isr15(); +extern void isr16(); +extern void isr17(); +extern void isr18(); +extern void isr19(); +extern void isr20(); +extern void isr21(); +extern void isr22(); +extern void isr23(); +extern void isr24(); +extern void isr25(); +extern void isr26(); +extern void isr27(); +extern void isr28(); +extern void isr29(); +extern void isr30(); +extern void isr31(); + +#endif // IDT_H diff --git a/include/isr.h b/include/isr.h new file mode 100644 index 0000000..831a2f4 --- /dev/null +++ b/include/isr.h @@ -0,0 +1,13 @@ +#ifndef ISR_H +#define ISR_H + +#include "types.h" + +struct registers{ + uint32_t ds; + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; + uint32_t int_no, err_code; + uint32_t eip, cs, eflags, useresp, ss; +} __attribute__((packed)); + +#endif // ISR_H diff --git a/include/port.h b/include/port.h new file mode 100644 index 0000000..d3e6897 --- /dev/null +++ b/include/port.h @@ -0,0 +1,11 @@ +#ifndef PORT_H +#define PORT_H + +#include "types.h" + +uint8_t port_byte_in (uint8_t port); +void port_byte_out(uint8_t port, uint8_t data); +uint16_t port_word_in (uint16_t port); +void port_word_out(uint16_t port, uint16_t data); + +#endif // PORT_H diff --git a/include/print.h b/include/print.h index c46843c..421b1f0 100644 --- a/include/print.h +++ b/include/print.h @@ -11,9 +11,14 @@ typedef struct TextOutput { uint16_t* vid_mem; }__attribute__((packed)) TextOutput; +TextOutput monitor; + TextOutput createOutput(const int max_row, const int max_column, uint16_t* vid_mem); void scrollText(TextOutput* textOutput); void putChar(uint8_t character, uint8_t background, uint8_t foreground, TextOutput* textOutput); void print(char* string, uint8_t background, uint8_t foreground, TextOutput* textOutput); +char* itoa(int value, char* buffer, int base); +void printToMonitor(char* string); +void printIntToMonitor(int num, int base); #endif // PRINT_H diff --git a/makefile b/makefile index acda2c3..b36e070 100644 --- a/makefile +++ b/makefile @@ -3,7 +3,7 @@ CXX = i386-elf-gcc CFLAGS = -std=gnu99 -ffreestanding -O2 -Wall -Wextra -nostdlib -Iinclude ASM = nasm -OBJECTS = gdt.o boot.o print.o kernel.o +OBJECTS = boot.o gdt.o isr.o idt.o port.o print.o kernel.o %.o : src/%.c $(CXX) $(CFLAGS) -o $@ -c $< diff --git a/print.o b/print.o deleted file mode 100644 index 1e31a59..0000000 Binary files a/print.o and /dev/null differ diff --git a/src/boot.s b/src/boot.s index 2472103..2022ea3 100644 --- a/src/boot.s +++ b/src/boot.s @@ -38,19 +38,100 @@ stack_top: ; Declare _start as a function symbol with the given symbol size. section .text -global _gdt_flush ; Allows the C code to link to this -extern _gp ; Says that '_gp' is in another file -_gdt_flush: - lgdt [_gp] ; Load the GDT with our '_gp' which is a special pointer - mov ax, 0x10 ; 0x10 is the offset in the GDT to our data segment - mov ds, ax - mov es, ax - mov fs, ax - mov gs, ax - mov ss, ax - jmp 0x08:flush2 ; 0x08 is the offset to our code segment: Far jump! -flush2: - ret ; Returns back to the C code! +global reloadSegments ; Flush GDT +reloadSegments: + ; Reload CS register containing code selector: + JMP 0x08:reload_CS ; 0x08 points at the new code selector +reload_CS: + ; Reload data segment registers: + MOV AX, 0x10 ; 0x10 points at the new data selector + MOV DS, AX + MOV ES, AX + MOV FS, AX + MOV GS, AX + MOV SS, AX + RET + +%macro ISR_NOERRCODE 1 ; define a macro, taking one parameter + [GLOBAL isr%1] ; %1 accesses the first parameter. + isr%1: + cli + push byte 0 + push byte %1 + jmp isr_common_stub +%endmacro + +%macro ISR_ERRCODE 1 + [GLOBAL isr%1] + isr%1: + cli + push byte %1 + jmp isr_common_stub +%endmacro + +ISR_NOERRCODE 0 +ISR_NOERRCODE 1 +ISR_NOERRCODE 2 +ISR_NOERRCODE 3 +ISR_NOERRCODE 4 +ISR_NOERRCODE 5 +ISR_NOERRCODE 6 +ISR_NOERRCODE 7 +ISR_NOERRCODE 8 +ISR_NOERRCODE 9 +ISR_NOERRCODE 10 +ISR_NOERRCODE 11 +ISR_NOERRCODE 12 +ISR_NOERRCODE 13 +ISR_NOERRCODE 14 +ISR_NOERRCODE 15 +ISR_NOERRCODE 16 +ISR_NOERRCODE 17 +ISR_NOERRCODE 18 +ISR_NOERRCODE 19 +ISR_NOERRCODE 20 +ISR_NOERRCODE 21 +ISR_NOERRCODE 22 +ISR_NOERRCODE 23 +ISR_NOERRCODE 24 +ISR_NOERRCODE 25 +ISR_NOERRCODE 26 +ISR_NOERRCODE 27 +ISR_NOERRCODE 28 +ISR_NOERRCODE 29 +ISR_NOERRCODE 30 +ISR_NOERRCODE 31 + + +[EXTERN isr_handler] + +; This is our common ISR stub. It saves the processor state, sets +; up for kernel mode segments, calls the C-level fault handler, +; and finally restores the stack frame. +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 isr_handler + + 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 global _start:function (_start.end - _start) _start: @@ -69,7 +150,8 @@ _start: ; stack (as it grows downwards on x86 systems). This is necessarily done ; in assembly as languages such as C cannot function without a stack. mov esp, stack_top - + + ; This is a good place to initialize crucial processor state before the ; high-level kernel is entered. It's best to minimize the early ; environment where crucial features are offline. Note that the diff --git a/src/gdt.c b/src/gdt.c index a9808aa..fa52f38 100644 --- a/src/gdt.c +++ b/src/gdt.c @@ -1,5 +1,7 @@ #include "gdt.h" +extern void reloadSegments(); + void encodeGDT(uint8_t* gdtEntry, struct GDT source) { if ((source.limit > 65536) && ((source.limit & 0xFFF) == 0xFFF)) { // Set the GDT to use paging @@ -34,3 +36,32 @@ void encodeGDT(uint8_t* gdtEntry, struct GDT source) { // where most of the ideas for this function are taken from shamelessly gdtEntry[5] = source.type; } + +void initializeGDT() { + struct GDT nullEntry; + nullEntry.limit = 0; + nullEntry.base = 0; + nullEntry.type = 0; + + struct GDT codeSection; + codeSection.limit = 0xFFFFFFFF; + codeSection.base = 0; + codeSection.type = 0x9A; + + struct GDT dataSection; + dataSection.limit = 0xFFFFFFFF; + dataSection.base = 0; + dataSection.type = 0x92; + + encodeGDT(gdt_entries , nullEntry ); + encodeGDT(gdt_entries + 8 , codeSection); + encodeGDT(gdt_entries + 16, dataSection); + + struct GDT_ptr gdt_ptr; + gdt_ptr.limit = 8*3; + gdt_ptr.base = (uint32_t)(&gdt_entries); + + asm("lgdt (%0)" : :"r" ((uint8_t*)(&gdt_ptr))); + + reloadSegments(); +} diff --git a/src/idt.c b/src/idt.c new file mode 100644 index 0000000..cb53408 --- /dev/null +++ b/src/idt.c @@ -0,0 +1,67 @@ +#include "idt.h" + +struct IDT createIDT(uint32_t base, uint16_t selector, uint8_t flags) { + struct IDT result; + result.base = base; + result.selector = selector; + result.flags = flags; + return result; +} + +void encodeIDT(uint8_t* idtEntry, struct IDT source) { + // Low base bytes + idtEntry[0] = source.base & 0xFF; + idtEntry[1] = (source.base >> 8) & 0xFF; + // Selector bytes + idtEntry[2] = source.selector & 0xFF; + idtEntry[3] = (source.selector >> 8) & 0xFF; + idtEntry[4] = 0; // Null byte + idtEntry[5] = source.flags; // Flag bytes + // High base bytes + idtEntry[6] = (source.base >> 16) & 0xFF; + idtEntry[7] = (source.base >> 24) & 0xFF; +} + +void initializeIDT() { + struct IDT_ptr idt_ptr; + idt_ptr.limit = 8*256 - 1; + idt_ptr.base = (uint32_t)&idt_entries; + + for (int i = 0; i < 256*8; i++) + idt_entries[i] = 0; // Just to be safe, set IDT to all 0 + + encodeIDT(&idt_entries[0] , createIDT((uint32_t)isr0 , 0x08, 0x8E)); + encodeIDT(&idt_entries[1*8 - 1] , createIDT((uint32_t)isr1 , 0x08, 0x8E)); + encodeIDT(&idt_entries[2*8 - 1] , createIDT((uint32_t)isr2 , 0x08, 0x8E)); + encodeIDT(&idt_entries[3*8 - 1] , createIDT((uint32_t)isr3 , 0x08, 0x8E)); + encodeIDT(&idt_entries[4*8 - 1] , createIDT((uint32_t)isr4 , 0x08, 0x8E)); + encodeIDT(&idt_entries[5*8 - 1] , createIDT((uint32_t)isr5 , 0x08, 0x8E)); + encodeIDT(&idt_entries[6*8 - 1] , createIDT((uint32_t)isr6 , 0x08, 0x8E)); + encodeIDT(&idt_entries[7*8 - 1] , createIDT((uint32_t)isr7 , 0x08, 0x8E)); + encodeIDT(&idt_entries[8*8 - 1] , createIDT((uint32_t)isr8 , 0x08, 0x8E)); + encodeIDT(&idt_entries[9*8 - 1] , createIDT((uint32_t)isr9 , 0x08, 0x8E)); + encodeIDT(&idt_entries[10*8 - 1], createIDT((uint32_t)isr10, 0x08, 0x8E)); + encodeIDT(&idt_entries[11*8 - 1], createIDT((uint32_t)isr11, 0x08, 0x8E)); + encodeIDT(&idt_entries[12*8 - 1], createIDT((uint32_t)isr12, 0x08, 0x8E)); + encodeIDT(&idt_entries[13*8 - 1], createIDT((uint32_t)isr13, 0x08, 0x8E)); + encodeIDT(&idt_entries[14*8 - 1], createIDT((uint32_t)isr14, 0x08, 0x8E)); + encodeIDT(&idt_entries[15*8 - 1], createIDT((uint32_t)isr15, 0x08, 0x8E)); + encodeIDT(&idt_entries[16*8 - 1], createIDT((uint32_t)isr16, 0x08, 0x8E)); + encodeIDT(&idt_entries[17*8 - 1], createIDT((uint32_t)isr17, 0x08, 0x8E)); + encodeIDT(&idt_entries[18*8 - 1], createIDT((uint32_t)isr18, 0x08, 0x8E)); + encodeIDT(&idt_entries[19*8 - 1], createIDT((uint32_t)isr19, 0x08, 0x8E)); + encodeIDT(&idt_entries[20*8 - 1], createIDT((uint32_t)isr20, 0x08, 0x8E)); + encodeIDT(&idt_entries[21*8 - 1], createIDT((uint32_t)isr21, 0x08, 0x8E)); + encodeIDT(&idt_entries[22*8 - 1], createIDT((uint32_t)isr22, 0x08, 0x8E)); + encodeIDT(&idt_entries[23*8 - 1], createIDT((uint32_t)isr23, 0x08, 0x8E)); + encodeIDT(&idt_entries[24*8 - 1], createIDT((uint32_t)isr24, 0x08, 0x8E)); + encodeIDT(&idt_entries[25*8 - 1], createIDT((uint32_t)isr25, 0x08, 0x8E)); + encodeIDT(&idt_entries[26*8 - 1], createIDT((uint32_t)isr26, 0x08, 0x8E)); + encodeIDT(&idt_entries[27*8 - 1], createIDT((uint32_t)isr27, 0x08, 0x8E)); + encodeIDT(&idt_entries[28*8 - 1], createIDT((uint32_t)isr28, 0x08, 0x8E)); + encodeIDT(&idt_entries[29*8 - 1], createIDT((uint32_t)isr29, 0x08, 0x8E)); + encodeIDT(&idt_entries[30*8 - 1], createIDT((uint32_t)isr30, 0x08, 0x8E)); + encodeIDT(&idt_entries[31*8 - 1], createIDT((uint32_t)isr31, 0x08, 0x8E)); + + asm("lidt (%0)" : :"r" ((uint8_t*)(&idt_ptr))); +} diff --git a/src/isr.c b/src/isr.c new file mode 100644 index 0000000..fbc61dc --- /dev/null +++ b/src/isr.c @@ -0,0 +1,8 @@ +#include "isr.h" +#include "print.h" + +void isr_handler(struct registers regs) { + printToMonitor("recieved interrupt: \n"); + printIntToMonitor(regs.int_no, 10); + printToMonitor("\n"); +} diff --git a/src/kernel.c b/src/kernel.c index d24c24e..653cd4e 100644 --- a/src/kernel.c +++ b/src/kernel.c @@ -1,22 +1,21 @@ #include "gdt.h" +#include "idt.h" +#include "isr.h" #include "types.h" #include "print.h" +#include "port.h" + +// inline function to swap two numbers #define FOREGROUND 0x0 #define BACKGROUND 0xF -void PrintWithScreenFill(char* string, TextOutput* output_stream) { - // Print a string and fill the screen - print(string, BACKGROUND, FOREGROUND, output_stream); - int row = output_stream->terminal_row; - while (output_stream->terminal_row < output_stream->max_row) { - putChar('\n', BACKGROUND, FOREGROUND, output_stream); - } - output_stream->terminal_row = row; -} - void kernel_main(void) { - TextOutput output_stream = createOutput(25,80,(uint16_t*)0xB8000); - PrintWithScreenFill("Hello, Logan World!\n", &output_stream); + initializeGDT(); + initializeIDT(); + printToMonitor("Hello\n"); + printIntToMonitor(10, 10); + asm ("int $0x3"); + asm ("int $0x4"); } diff --git a/src/port.c b/src/port.c new file mode 100644 index 0000000..2a19315 --- /dev/null +++ b/src/port.c @@ -0,0 +1,21 @@ +#include "port.h" + +uint8_t port_byte_in (uint8_t port) { + uint8_t result; + __asm__("in %%dx, %%al" : "=a" (result) : "d" (port)); + return result; +} + +void port_byte_out(uint8_t port, uint8_t data) { + __asm__("out %%al, %%dx" : :"a" (data), "d" (port)); +} + +uint16_t port_word_in (uint16_t port) { + uint16_t result; + __asm__("in %%dx, %%ax" : "=a" (result) : "d" (port)); + return result; +} + +void port_word_out (uint16_t port, uint16_t data) { + __asm__("out %%ax, %%dx" : :"a" (data), "d" (port)); +} diff --git a/src/print.c b/src/print.c index 0a0220c..3c03577 100644 --- a/src/print.c +++ b/src/print.c @@ -1,5 +1,8 @@ #include "print.h" +#define FOREGROUND 0x0 +#define BACKGROUND 0xF + TextOutput createOutput(const int max_row, const int max_column, uint16_t* vid_mem) { // Create a new TextOutput interface TextOutput output; @@ -66,8 +69,77 @@ void putChar(uint8_t character, uint8_t background, uint8_t foreground, void print(char* string, uint8_t background, uint8_t foreground, TextOutput* textOutput) { // Print a string - for (string; *string; string++) { + while (*string) { putChar(*string, background, foreground, textOutput); + string++; } } +void PrintWithScreenFill(char* string, TextOutput* output_stream) { + // Print a string and fill the screen + print(string, BACKGROUND, FOREGROUND, output_stream); + int row = output_stream->terminal_row; + while (output_stream->terminal_row < output_stream->max_row) { + putChar('\n', BACKGROUND, FOREGROUND, output_stream); + } + output_stream->terminal_row = row; +} + + +inline void swap(char *x, char *y) { + char t = *x; *x = *y; *y = t; +} + +int abs(int value) { + return (value < 0 ? -value : value); +} + +// function to reverse buffer[i..j] +char* reverse(char *buffer, int i, int j) +{ + while (i < j) + swap(&buffer[i++], &buffer[j--]); + + return buffer; +} + +// Iterative function to implement itoa() function in C +char* itoa(int value, char* buffer, int base) +{ + if (base < 2 || base > 32) + return buffer; + int n = abs(value); + int i = 0; + while (n) + { + int r = n % base; + if (r >= 10) + buffer[i++] = 65 + (r - 10); + else + buffer[i++] = 48 + r; + n = n / base; + } + if (i == 0) + buffer[i++] = '0'; + if (value < 0 && base == 10) + buffer[i++] = '-'; + buffer[i] = '\0'; + return reverse(buffer, 0, i - 1); +} + +int haveFilledScreen = 0; + +void printToMonitor(char* string) { + if (!haveFilledScreen) { + monitor = createOutput(25, 80, (uint16_t*)(0xB8000)); + PrintWithScreenFill(string, &monitor); + haveFilledScreen = 1; + } + else + print(string, BACKGROUND, FOREGROUND, &monitor); +} + +void printIntToMonitor(int num, int base) { + char* buffer; + printToMonitor(itoa(num, buffer, base)); +}