From c0c4dbc6658853042c40b30f4c51722fee6458eb Mon Sep 17 00:00:00 2001 From: "brice.boisson" Date: Mon, 4 Sep 2023 23:14:06 +0900 Subject: [PATCH] Add: Starting kernel repos --- .gitignore | 1 + Makefile | 17 ++++++ Makefile.common | 6 ++ README.md | 22 +++++++ src/Makefile | 32 ++++++++++ src/boot.S | 109 +++++++++++++++++++++++++++++++++ src/debug.c | 87 +++++++++++++++++++++++++++ src/debug.h | 22 +++++++ src/gdt.c | 140 +++++++++++++++++++++++++++++++++++++++++++ src/gdt.h | 30 ++++++++++ src/idt.c | 100 +++++++++++++++++++++++++++++++ src/idt.h | 28 +++++++++ src/include/types.h | 15 +++++ src/int.S | 83 +++++++++++++++++++++++++ src/int.h | 9 +++ src/io.h | 39 ++++++++++++ src/isr.c | 47 +++++++++++++++ src/kernel.c | 35 +++++++++++ src/kernel.lds | 23 +++++++ src/launch_process.c | 41 +++++++++++++ src/launch_process.h | 6 ++ src/pic_controler.c | 108 +++++++++++++++++++++++++++++++++ src/pic_controler.h | 7 +++ src/serial.c | 88 +++++++++++++++++++++++++++ src/serial.h | 12 ++++ src/syscall.c | 26 ++++++++ src/syscall.h | 7 +++ src/tss.h | 52 ++++++++++++++++ src/userland.c | 26 ++++++++ src/userland.h | 6 ++ tools/create_iso.sh | 8 +++ 31 files changed, 1232 insertions(+) create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 Makefile.common create mode 100644 README.md create mode 100644 src/Makefile create mode 100644 src/boot.S create mode 100644 src/debug.c create mode 100644 src/debug.h create mode 100644 src/gdt.c create mode 100644 src/gdt.h create mode 100644 src/idt.c create mode 100644 src/idt.h create mode 100644 src/include/types.h create mode 100644 src/int.S create mode 100644 src/int.h create mode 100644 src/io.h create mode 100644 src/isr.c create mode 100644 src/kernel.c create mode 100644 src/kernel.lds create mode 100644 src/launch_process.c create mode 100644 src/launch_process.h create mode 100644 src/pic_controler.c create mode 100644 src/pic_controler.h create mode 100644 src/serial.c create mode 100644 src/serial.h create mode 100644 src/syscall.c create mode 100644 src/syscall.h create mode 100644 src/tss.h create mode 100644 src/userland.c create mode 100644 src/userland.h create mode 100755 tools/create_iso.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..89547ba --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +log.txt \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9c577e0 --- /dev/null +++ b/Makefile @@ -0,0 +1,17 @@ +include Makefile.common + +all: k.iso + +k.iso: install + ./tools/create_iso.sh + +install: + mkdir -p $(GRUBDIR) + $(MAKE) -C $(SOURCEDIR) $@ + +clean: + $(MAKE) -C $(SOURCEDIR) $@ + $(RM) kernel.iso + $(RM) -r iso + +.PHONY: $(GRUBDIR) install diff --git a/Makefile.common b/Makefile.common new file mode 100644 index 0000000..095aa11 --- /dev/null +++ b/Makefile.common @@ -0,0 +1,6 @@ +CFLAGS = -std=gnu99 -Os -Wall -Wextra -fno-builtin -ffreestanding -m32 -fno-asynchronous-unwind-tables -fno-common -fno-pie -march=i486 -fno-stack-protector -ffunction-sections -fdata-sections +LDFLAGS = -nostdlib -Wl,--build-id=none,--gc-sections -nostartfiles -static -m32 +ASFLAGS = -m32 +GRUBDIR = iso +SOURCEDIR = src +TARGET = kernel diff --git a/README.md b/README.md new file mode 100644 index 0000000..ba44fdf --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# Introduction + +This kernel is written as a educative project. The goal is to have a kernel able to run a basic shell. To allow that, this kernel have to implement : +- [X] Protected mode (loading gdt) +- [X] Manage interrupt (loading idt and enabling interrupt) +- [X] Manage IRQ (enable PIC) +- [X] Running and Userland (TSS) +- [X] Let the userland call the kernel (syscall) +- [ ] Running multiple userland (ordonnanceur) +- [ ] Running elf binary +- [ ] Having a small libc and needed syscall to run the shell +- [ ] (Bonus) Having a basic visual interface (VGA ?) + +This kernel has been written using : +- The tutorial Pépin OS - Réaliser son propre système (in french): https://michelizza.developpez.com/realiser-son-propre-systeme +- The educative k project from former EPITA (Engineering School) system laboratory LSE: +https://k.lse.epita.fr/ +https://github.com/Galli24/kernel-k-project +- The ressources on the website OSDev: +https://wiki.osdev.org/Main_Page +- The in Intel® 64 and IA-32 Architectures, Software Developer’s Manual, Volume 3A: System Programming Guide, Part 1: +https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html \ No newline at end of file diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..10cdb72 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,32 @@ +include ../Makefile.common + +OBJS = \ + boot.o \ + kernel.o \ + serial.o \ + isr.o \ + debug.o \ + syscall.o \ + int.o \ + gdt.o \ + idt.o \ + pic_controler.o \ + userland.o \ + launch_process.o \ + +DEPS = $(OBJS:.o=.d) + +CPPFLAGS += -MMD -Iinclude +CFLAGS += $(K_EXTRA_CFLAGS) -g -nostdlib +LDFLAGS += -Wl,-Tkernel.lds +LDLIBS = + +all: $(TARGET) $(OBJS) + +$(TARGET): $(OBJS) + +install: $(TARGET) + install $(TARGET) ../$(GRUBDIR)/$(TARGET) + +clean: + $(RM) $(OBJS) $(DEPS) $(TARGET) diff --git a/src/boot.S b/src/boot.S new file mode 100644 index 0000000..d41cdfd --- /dev/null +++ b/src/boot.S @@ -0,0 +1,109 @@ +/* Declare constants for the multiboot header. */ +.set ALIGN, 1<<0 /* align loaded modules on page boundaries */ +.set MEMINFO, 1<<1 /* provide memory map */ +.set FLAGS, ALIGN | MEMINFO /* this is the Multiboot 'flag' field */ +.set MAGIC, 0x1BADB002 /* 'magic number' lets bootloader find the header */ +.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */ + +/* +Declare a multiboot header that marks the program as a kernel. These are magic +values that are documented in the multiboot standard. The bootloader will +search for this signature in the first 8 KiB of the kernel file, aligned at a +32-bit boundary. The signature is in its own section so the header can be +forced to be within the first 8 KiB of the kernel file. +*/ +.section .multiboot +.align 4 +.long MAGIC +.long FLAGS +.long CHECKSUM + +/* +The multiboot standard does not define the value of the stack pointer register +(esp) and it is up to the kernel to provide a stack. This allocates room for a +small stack by creating a symbol at the bottom of it, then allocating 16384 +bytes for it, and finally creating a symbol at the top. The stack grows +downwards on x86. The stack is in its own section so it can be marked nobits, +which means the kernel file is smaller because it does not contain an +uninitialized stack. The stack on x86 must be 16-byte aligned according to the +System V ABI standard and de-facto extensions. The compiler will assume the +stack is properly aligned and failure to align the stack will result in +undefined behavior. +*/ +.section .bss +.align 16 +stack_bottom: +.skip 16384 # 16 KiB +stack_top: + +/* +The linker script specifies _start as the entry point to the kernel and the +bootloader will jump to this position once the kernel has been loaded. It +doesn't make sense to return from this function as the bootloader is gone. +*/ +.section .text +.global _start +.type _start, @function +_start: + /* + The bootloader has loaded us into 32-bit protected mode on a x86 + machine. Interrupts are disabled. Paging is disabled. The processor + state is as defined in the multiboot standard. The kernel has full + control of the CPU. The kernel can only make use of hardware features + and any code it provides as part of itself. There's no printf + function, unless the kernel provides its own header and a + printf implementation. There are no security restrictions, no + safeguards, no debugging mechanisms, only what the kernel provides + itself. It has absolute and complete power over the + machine. + */ + + /* + To set up a stack, we set the esp register to point to the top of the + 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 $stack_top, %esp + + /* + 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 + processor is not fully initialized yet: Features such as floating + point instructions and instruction set extensions are not initialized + yet. The GDT should be loaded here. Paging should be enabled here. + C++ features such as global constructors and exceptions will require + runtime support to work as well. + */ + + /* + Enter the high-level kernel. The ABI requires the stack is 16-byte + aligned at the time of the call instruction (which afterwards pushes + the return pointer of size 4 bytes). The stack was originally 16-byte + aligned above and we've pushed a multiple of 16 bytes to the + stack since (pushed 0 bytes so far), so the alignment has thus been + preserved and the call is well defined. + */ + call kernel_main + + /* + If the system has nothing more to do, put the computer into an + infinite loop. To do that: + 1) Disable interrupts with cli (clear interrupt enable in eflags). + They are already disabled by the bootloader, so this is not needed. + Mind that you might later enable interrupts and return from + kernel_main (which is sort of nonsensical to do). + 2) Wait for the next interrupt to arrive with hlt (halt instruction). + Since they are disabled, this will lock up the computer. + 3) Jump to the hlt instruction if it ever wakes up due to a + non-maskable interrupt occurring or due to system management mode. + */ + cli +1: hlt + jmp 1b + +/* +Set the size of the _start symbol to the current location '.' minus its start. +This is useful when debugging or when you implement call tracing. +*/ +.size _start, . - _start \ No newline at end of file diff --git a/src/debug.c b/src/debug.c new file mode 100644 index 0000000..4770ffd --- /dev/null +++ b/src/debug.c @@ -0,0 +1,87 @@ +#include "debug.h" + +#include + +#include "serial.h" + +int print_variadic(char *msg, va_list args) +{ + while (*msg != '\0') + { + if (*(msg + 1) != '\0' && *msg == '%') + { + switch (*(msg + 1)) + { + case 'd': + write_serial_nb(va_arg(args, int), false); + break; + case 's': + write_serial(va_arg(args, char *)); + break; + case 'b': + write_serial_bin(va_arg(args, char *), false); + break; + case 'c': + write_serial_char(va_arg(args, int)); + break; + default: + write_serial("?"); + break; + } + msg += 2; + } + else + { + write_serial_char(*msg); + msg++; + } + } +} + +int debug_info(char *fnt, char *msg, ...) +{ + write_serial("\033[0;34m[INFO]\033[0m "); + write_serial(fnt); + write_serial("\t: "); + + va_list args; + va_start(args, msg); + print_variadic(msg, args); + va_end(args); + + write_serial("\n"); + + return 0; +} + +int debug_warn(char *fnt, char *msg, ...) +{ + write_serial("\033[0;33m[WARNING]\033[0m "); + write_serial(fnt); + write_serial("\t: "); + + va_list args; + va_start(args, msg); + print_variadic(msg, args); + va_end(args); + + write_serial("\n"); + + return 0; +} + +int debug_err(char *fnt, char *msg, ...) +{ + write_serial("\033[0;31m[ERROR]\033[0m "); + write_serial(fnt); + write_serial("\t: "); + + va_list args; + va_start(args, msg); + print_variadic(msg, args); + va_end(args); + + write_serial("\n"); + + return 0; +} \ No newline at end of file diff --git a/src/debug.h b/src/debug.h new file mode 100644 index 0000000..02f4b49 --- /dev/null +++ b/src/debug.h @@ -0,0 +1,22 @@ +#ifndef DEBUG_H +#define DEBUG_H + +#include + +#define DEBUG + +#ifdef DEBUG + #define DEBUG_INFO(...) debug_info(__func__, ##__VA_ARGS__) + #define DEBUG_WARN(...) debug_warn(__func__, ##__VA_ARGS__) + #define DEBUG_ERR(...) debug_err(__func__, ##__VA_ARGS__) +#else + #define DEBUG_INFO(...) + #define DEBUG_WARN(...) + #define DEBUG_ERR(...) +#endif + +int debug_info(char *fnt, char *msg, ...); +int debug_warn(char *fnt, char *msg, ...); +int debug_err(char *fnt, char *msg, ...); + +#endif /* DEBUG_H */ diff --git a/src/gdt.c b/src/gdt.c new file mode 100644 index 0000000..3a223cf --- /dev/null +++ b/src/gdt.c @@ -0,0 +1,140 @@ +#define __GDT__ +#define __TSS__ + +#include "gdt.h" +#include +#include "serial.h" +#include "debug.h" +#include "tss.h" +#include "userland.h" + +void *memcpy(void *dest, const void *src, size_t n) +{ + const char *s = src; + char *d = dest; + + for (size_t i = 0; i < n; i++) + *d++ = *s++; + + return dest; +} + +struct tss user_land_tss; + +struct segment_desc_param { + u16 Limit_1; + u32 Base; + u8 Type; + u8 S; + u8 DPL; + u8 P; + u8 Limit_2; + u8 AVL; + u8 L; + u8 D_B; + u8 G; +}; + +struct segdesc init_descriptor(struct segment_desc_param param) +{ + struct segdesc descriptor; + + descriptor.Limit_1 = param.Limit_1; + descriptor.Limit_2 = param.Limit_2; + + descriptor.Base_1 = (u16) param.Base; + descriptor.Base_2 = (u8) (param.Base >> 16); + descriptor.Base_3 = (u8) (param.Base >> 24); + + descriptor.Type = (param.Type & 0x000F); + + descriptor.Flag_1 = param.S; + descriptor.Flag_1 |= (param.DPL << 1); + descriptor.Flag_1 |= (param.P << 3); + + descriptor.Flag_2 = param.AVL; + descriptor.Flag_2 |= (param.L << 1); + descriptor.Flag_2 |= (param.D_B << 2); + descriptor.Flag_2 |= (param.G << 3); + + return descriptor; +} + +void init_gdt(void) +{ + DEBUG_INFO("Initializing GDT"); + + DEBUG_INFO("GDT BASE ADDRESS: %d", (u32) &kgdtr); + DEBUG_INFO("GDT LIMIT: %d", sizeof(gdt) - 1); + kgdtr.limit = sizeof(gdt) - 1; + kgdtr.base = (u32) gdt; + + memcpy((char*) 0x30000, &userland, 1000); + + gdt[0] = init_descriptor((struct segment_desc_param) { .Limit_1 = 0, + .Base = 0, .Type = 0, .S = 0, .DPL = 0, .P = 0, + .Limit_2 = 0, .AVL = 0, .L = 0, .D_B = 0, .G = 0 }); + + // Code + gdt[1] = init_descriptor((struct segment_desc_param) { .Limit_1 = 0xFFFF, + .Base = 0, .Type = 0x0B, .S = 1, .DPL = 0, .P = 1, + .Limit_2 = 0x02, .AVL = 0, .L = 0, .D_B = 1, .G = 1 }); + // Data + gdt[2] = init_descriptor((struct segment_desc_param) { .Limit_1 = 0xFFFF, + .Base = 0, .Type = 2, .S = 1, .DPL = 0, .P = 1, + .Limit_2 = 0x02, .AVL = 0, .L = 0, .D_B = 1, .G = 1 }); + + // Code + gdt[3] = init_descriptor((struct segment_desc_param) { .Limit_1 = 0xFFFF, + .Base = 0x30000, .Type = 0x0B, .S = 1, .DPL = 3, .P = 1, + .Limit_2 = 0x0F, .AVL = 0, .L = 0, .D_B = 1, .G = 1 }); + + // Data + gdt[4] = init_descriptor((struct segment_desc_param) { .Limit_1 = 0xFFFF, + .Base = 0x30000, .Type = 2, .S = 1, .DPL = 3, .P = 1, + .Limit_2 = 0x0F, .AVL = 0, .L = 0, .D_B = 1, .G = 1 }); + + user_land_tss.ssp = 0x0; + user_land_tss.io_map_base_address = 0x0; + user_land_tss.ss0 = 0x10; + user_land_tss.esp0 = 0x20000; + + DEBUG_INFO("TSS BASE ADDRESS: %d", (u32) &user_land_tss); + + gdt[5] = init_descriptor((struct segment_desc_param) { .Limit_1 = sizeof(user_land_tss), + .Base = &user_land_tss, .Type = 0x09, .S = 0, .DPL = 3, .P = 1, + .Limit_2 = 0x00, .AVL = 0, .L = 0, .D_B = 0, .G = 0 }); + + for (int i = 0; i < sizeof(gdt) / 8; i++) + { + char *ptr = (char *) &gdt[i]; + DEBUG_INFO("--------------------"); + DEBUG_INFO("GDT[%d] : Entry number : %d", i, i * 8); + DEBUG_INFO("\tLIMIT : %d", (u16) ptr[0] | ((ptr[6] & 0x0F) << 16)); + // TODO : Fix display of base address + DEBUG_INFO("\tBASE : %d", (u32) ptr[2] | ((u32) (((u8) ptr[4]) << 16)) | ((u32) (((u8) ptr[7]) << 24))); + DEBUG_INFO("\tTYPE : %b |Data 0 / Code 1|Expand Down 1|Writable 1|Accessed 1|", (u8) ptr[5] & 0x0F); + DEBUG_INFO("\tFlag : %b |Present 1|Level ring 0 / ring 1|System 0 / Code - Data 1|", (u8) ptr[5] >> 4); + DEBUG_INFO("\tFlag : %b |Granularity 1|16 bits 0 / 32 bits 1|64 bits 1|Available for system 1|", (u8) ptr[6] >> 4); + } + DEBUG_INFO("--------------------"); + + + asm volatile ("lgdt (kgdtr)"); + + asm volatile ( + "movw $0x10, %ax \n\ + movw %ax, %ds \n\ + movw %ax, %es \n\ + movw %ax, %fs \n\ + movw %ax, %gs \n\ + movw %ax, %ss \n\ + ljmp $0x08, $next \n\ + next: \n" + ); + + DEBUG_INFO("SS0 %d", user_land_tss.ss0); + DEBUG_INFO("ESP0 %d", user_land_tss.esp0); + + DEBUG_INFO("GDT LOADED"); +} diff --git a/src/gdt.h b/src/gdt.h new file mode 100644 index 0000000..beb2f3f --- /dev/null +++ b/src/gdt.h @@ -0,0 +1,30 @@ +#ifndef GDT_H +#define GDT_H + +#include + +struct gdtr { + u16 limit; + u32 base; +} __attribute__((packed)); + +struct segdesc { + u16 Limit_1; + u16 Base_1; + u8 Base_2; + u8 Type : 4; + u8 Flag_1 : 4; + u8 Limit_2 : 4; + u8 Flag_2 : 4; + u8 Base_3; +} __attribute__((packed)); + +#ifdef __GDT__ + static struct segdesc gdt[6]; + struct gdtr kgdtr; +#endif + +void init_gdt(void); +void temp_run_process(void); + +#endif /* !GDT_H */ \ No newline at end of file diff --git a/src/idt.c b/src/idt.c new file mode 100644 index 0000000..34433cc --- /dev/null +++ b/src/idt.c @@ -0,0 +1,100 @@ +#define __IDT__ + +#include "idt.h" +#include +#include "serial.h" +#include "pic_controler.h" +#include "debug.h" +#include "int.h" + +/* + Make space for the interrupt descriptor table + Tell the CPU where that space is (see GDT Tutorial: lidt works the very same way as lgdt) + Tell the PIC that you no longer want to use the BIOS defaults (see Programming the PIC chips) + Write a couple of ISR handlers (see Interrupt Service Routines) for both IRQs and exceptions + Put the addresses of the ISR handlers in the appropriate descriptors (in Interrupt Descriptor Table) + Enable all supported interrupts in the IRQ mask (of the PIC) +*/ + +struct interrupt_gate_param { + u32 Offset; + u16 SegSelect; + u8 Type; + u8 D; + u8 DPL; + u8 P; +}; + +struct idt_segdesc init_gate(struct interrupt_gate_param param) +{ + if (param.Type == 5) + { + param.Offset = 0; + param.D = 0; + param.Offset = 0; + } + + struct idt_segdesc descriptor; + + descriptor.Offset = param.Offset; + descriptor.Offset2 = param.Offset >> 16; + + descriptor.SegSelect = param.SegSelect; + + descriptor.empty = 0; + descriptor.bits = 0; + + descriptor.Type = param.Type | (param.D << 3); + + descriptor.Flags |= param.DPL << 1; + descriptor.Flags |= param.P << 3; + + return descriptor; +} + +void init_idt(void) +{ + DEBUG_INFO("Initializing IDT"); + + DEBUG_INFO("IDT BASE ADDRESS: %d", (u32) &kidtr); + DEBUG_INFO("IDT LIMIT: %d", sizeof(idt) - 1); + kidtr.limit = sizeof(idt) - 1; + kidtr.base = (u32) idt; + + for (int i = 0; i < sizeof(idt); i++) + { + idt[i] = init_gate((struct interrupt_gate_param) { .Offset = (u32) _asm_default_int, .SegSelect = 0x08, + .Type = 0x06, .D = 1, .DPL = 0, .P = 1 }); + } + + idt[32] = init_gate((struct interrupt_gate_param) { .Offset = (u32) _asm_irq_0, .SegSelect = 0x08, + .Type = 0x06, .D = 1, .DPL = 0, .P = 1 }); + + idt[33] = init_gate((struct interrupt_gate_param) { .Offset = (u32) _asm_irq_1, .SegSelect = 0x08, + .Type = 0x06, .D = 1, .DPL = 0, .P = 1 }); + + idt[48] = init_gate((struct interrupt_gate_param) { .Offset = (u32) _asm_sycall_handler, .SegSelect = 0x08, + .Type = 0x07, .D = 1, .DPL = 3, .P = 1 }); + + #ifdef DEBUG_IDT + for (int i = 0; i < 49; i++) + { + char *ptr = (char *) &idt[i]; + DEBUG_INFO("--------------------"); + DEBUG_INFO("IDT[%d]:", i); + DEBUG_INFO("\tOffset : %d", (u32) ptr[0] | (ptr[6] << 16)); + DEBUG_INFO("\tSegment Selector : %d | %b | %b |Index|GDT 0 / LDT 1|Level ring 0 / ring 1|", (u16) ptr[2] >> 3, ptr[2] >> 2, ptr[2]); + DEBUG_INFO("\tZeroes : %d", (u8) ptr[4]); + DEBUG_INFO("\tFlag : %b |16 bits 0 / 32 bits 1|Task 101 / Interrupt 110 / Trap 111|", (u8) ptr[5] & 0x0F); + DEBUG_INFO("\tFlag : %b |Present|Level ring 0 / ring 1|Zero|", (u8) ptr[5] >> 4); + } + DEBUG_INFO("--------------------"); + #endif + + asm volatile ("lidtl (kidtr)"); + + DEBUG_INFO("IDT LOADED"); + + DEBUG_INFO("ENABLING STI"); + asm volatile ("sti"); +} diff --git a/src/idt.h b/src/idt.h new file mode 100644 index 0000000..4b476bc --- /dev/null +++ b/src/idt.h @@ -0,0 +1,28 @@ +#ifndef IDT_H +#define IDT_H + +#include + +struct idtr { + u16 limit; + u32 base; +} __attribute__((packed)); + +struct idt_segdesc { + u16 Offset; + u16 SegSelect; + u8 empty : 5; + u8 bits : 3; + u8 Type : 4; + u8 Flags : 4; + u16 Offset2; +} __attribute__((packed)); + +#ifdef __IDT__ + static struct idt_segdesc idt[256]; + struct idtr kidtr; +#endif + +void init_idt(void); + +#endif /* !IDT_H */ \ No newline at end of file diff --git a/src/include/types.h b/src/include/types.h new file mode 100644 index 0000000..1574abf --- /dev/null +++ b/src/include/types.h @@ -0,0 +1,15 @@ +#ifndef TYPES_H +#define TYPES_H + +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned int u32; +typedef unsigned long long u64; + +typedef unsigned long size_t; + +#define bool int +#define false 0 +#define true 1 + +#endif /* !TYPES_H */ diff --git a/src/int.S b/src/int.S new file mode 100644 index 0000000..dfb5c46 --- /dev/null +++ b/src/int.S @@ -0,0 +1,83 @@ +.extern isr_default_int, isr_clock_int, isr_kbd_int, syscall_handler +.global _asm_default_int, _asm_irq_0, _asm_irq_1, _asm_sycall_handler + +_asm_default_int: + pushal + push %ds + push %es + push %fs + push %gs + push %ebx + mov $0x10,%bx + mov %bx,%ds + pop %ebx + call isr_default_int + pop %gs + pop %fs + pop %es + pop %ds + popal + iret + +_asm_irq_0: + pushal + push %ds + push %es + push %fs + push %gs + push %ebx + mov $0x10,%bx + mov %bx,%ds + pop %ebx + call isr_clock_int + movb $0x20,%al + outb %al,$0x20 + pop %gs + pop %fs + pop %es + pop %ds + popal + iret + +_asm_irq_1: + pushal + push %ds + push %es + push %fs + push %gs + push %ebx + mov $0x10,%bx + mov %bx,%ds + pop %ebx + call isr_kbd_int + movb $0x20,%al + outb %al,$0x20 + pop %gs + pop %fs + pop %es + pop %ds + popal + iret + +_asm_sycall_handler: + pushal + push %ds + push %es + push %fs + push %gs + push %ebx + mov $0x10,%bx + mov %bx,%ds + pop %ebx + push %ebx + push %eax + call syscall_handler + pop %ebx + pop %ebx + pop %gs + pop %fs + pop %es + pop %ds + mov %eax,28(%esp) + popal + iret diff --git a/src/int.h b/src/int.h new file mode 100644 index 0000000..cc85126 --- /dev/null +++ b/src/int.h @@ -0,0 +1,9 @@ +#ifndef INT_H +#define INT_H + +void _asm_default_int(void); +void _asm_irq_0(void); +void _asm_irq_1(void); +int _asm_sycall_handler(int eax, int ebx); + +#endif /* !INT_H */ diff --git a/src/io.h b/src/io.h new file mode 100644 index 0000000..068a786 --- /dev/null +++ b/src/io.h @@ -0,0 +1,39 @@ +#ifndef IO_H_ +#define IO_H_ + +#include + +static inline void outb(u16 port, u8 val) +{ + asm volatile ("outb %0, %1" : /* No output */ : "a"(val), "d"(port)); +} + +static inline u8 inb(u16 port) +{ + u8 res; + + asm volatile ("inb %1, %0" : "=&a"(res) : "d"(port)); + + return res; +} + +static inline void outw(u16 port, u16 val) +{ + asm volatile ("outw %0, %1" : /* No output */ : "a"(val), "d"(port)); +} + +static inline u16 inw(u16 port) +{ + u16 res; + + asm volatile ("inw %1, %0" : "=&a"(res) : "d"(port)); + + return res; +} + +static inline void io_wait(void) +{ + outb(0x80, 0); +} + +#endif /* !IO_H_ */ diff --git a/src/isr.c b/src/isr.c new file mode 100644 index 0000000..5740a90 --- /dev/null +++ b/src/isr.c @@ -0,0 +1,47 @@ +#include +#include "io.h" +#include "serial.h" +#include "pic_controler.h" +#include "debug.h" +#include "syscall.h" + +void isr_default_int(void) +{ + DEBUG_INFO("An INT has been raised, entering default interrupt handler."); +} + +void isr_clock_int(void) +{ + DEBUG_INFO("Enterring clock interrupt handler."); +} + +void isr_kbd_int(void) +{ + DEBUG_INFO("Enterring keyboard interrupt handler."); + + int x = inb(0x60); + DEBUG_INFO("Keyboard input: %d", x); +} + +void syscall_handler(int eax, int ebx) +{ + DEBUG_INFO("Syscall %d has been called from the userland with parameter %d", eax, ebx); + + switch (eax) + { + case 1: + char t = *((char *) ebx); + DEBUG_INFO("Syscall write : %d", (int) t); + return write(1, (u32) ebx); + break; + case 2: + DEBUG_INFO("Syscall keyboard"); + return keyboard(); + break; + default: + DEBUG_INFO("No syscall %d", eax); + break; + } + + return 0; +} diff --git a/src/kernel.c b/src/kernel.c new file mode 100644 index 0000000..542a26d --- /dev/null +++ b/src/kernel.c @@ -0,0 +1,35 @@ +#include + +#include "gdt.h" +#include "idt.h" +#include "pic_controler.h" +#include "serial.h" +#include "debug.h" +#include "tss.h" +#include "launch_process.h" + +void main(void) +{ + DEBUG_INFO("Entering Main Function"); + + launch_process(0x28, 0x30000, 0x20, 0x18, 0x20); + + for (;;) + { + DEBUG_INFO("Waiting for an interrupt"); + asm volatile ("hlt"); + } +} + +void kernel_main(void) +{ + init_serial(); + + DEBUG_INFO("Starting kernel"); + + init_gdt(); + init_idt(); + pic_init(); + + main(); +} diff --git a/src/kernel.lds b/src/kernel.lds new file mode 100644 index 0000000..2bb7afc --- /dev/null +++ b/src/kernel.lds @@ -0,0 +1,23 @@ +ENTRY(_start) +OUTPUT_FORMAT("elf32-i386") + +SECTIONS +{ + .text : ALIGN(CONSTANT(MAXPAGESIZE)) { + _TEXT_START_ = .; + *(.multiboot) *(.text) + _TEXT_END_ = .; + } + + .data : ALIGN(CONSTANT(MAXPAGESIZE)) { + _DATA_START_ = .; + *(.data) + _DATA_END_ = .; + } + + .bss : ALIGN(CONSTANT(MAXPAGESIZE)) { + _BSS_START_ = .; + *(.bss) + _BSS_END_ = .; + } +} diff --git a/src/launch_process.c b/src/launch_process.c new file mode 100644 index 0000000..707299a --- /dev/null +++ b/src/launch_process.c @@ -0,0 +1,41 @@ +#include "launch_process.h" + +#include "tss.h" +#include "debug.h" + +void launch_process(int tss, int memory_start, int userland_stack, int userland_code, int userland_data) +{ + // Setting DPL and GDT bits + userland_stack += 3; + userland_code += 3; + userland_data += 3; + + DEBUG_INFO("LAUCHING USER LAND PROCESS"); + + asm volatile (" \n \ + movw %0, %%ax \n \ + ltr %%ax \n \ + movw %%ss, %1 \n \ + movl %%esp, %2 \n \ + cli \n \ + push %3 \n \ + push %4 \n \ + pushfl \n \ + popl %%eax \n \ + orl $0x200, %%eax \n \ + and $0xffffbfff, %%eax \n \ + push %%eax \n \ + push %5 \n \ + push $0x0 \n \ + movl $0x20000, %6 \n \ + movw %7, %%ax \n \ + movw %%ax, %%ds \n \ + iret" : "=m" (tss), + "=m" (user_land_tss.ss0), + "=m" (user_land_tss.esp0), + "=m" (userland_stack), + "=m" (memory_start), + "=m" (userland_code), + "=m" (user_land_tss.esp0), + "=m" (userland_data)); +} diff --git a/src/launch_process.h b/src/launch_process.h new file mode 100644 index 0000000..5951834 --- /dev/null +++ b/src/launch_process.h @@ -0,0 +1,6 @@ +#ifndef LAUNCH_PROCESS_H +#define LAUNCH_PROCESS_H + +void launch_process(int tss, int memory_start, int userland_stack, int userland_code, int userland_data); + +#endif /* !LAUNCH_PROCESS_H */ \ No newline at end of file diff --git a/src/pic_controler.c b/src/pic_controler.c new file mode 100644 index 0000000..92f27ad --- /dev/null +++ b/src/pic_controler.c @@ -0,0 +1,108 @@ +#include + +#include "pic_controler.h" +#include "io.h" +#include "debug.h" + +#define PIC1 0x20 /* IO base address for master PIC */ +#define PIC2 0xA0 /* IO base address for slave PIC */ +#define PIC1_COMMAND PIC1 +#define PIC1_DATA (PIC1+1) +#define PIC2_COMMAND PIC2 +#define PIC2_DATA (PIC2+1) + +#define ICW1_ICW4 0x01 /* Indicates that ICW4 will be present */ +#define ICW1_SINGLE 0x02 /* Single (cascade) mode */ +#define ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */ +#define ICW1_LEVEL 0x08 /* Level triggered (edge) mode */ +#define ICW1_INIT 0x10 /* Initialization - required! */ + +#define ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */ +#define ICW4_AUTO 0x02 /* Auto (normal) EOI */ +#define ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */ +#define ICW4_BUF_MASTER 0x0C /* Buffered mode/master */ +#define ICW4_SFNM 0x10 /* Special fully nested (not) */ + +#define PIC_EOI 0x20 /* End-of-interrupt command code */ + +void PIC_sendEOI(unsigned char irq) +{ + if(irq >= 8) + outb(PIC2_COMMAND,PIC_EOI); + + outb(PIC1_COMMAND,PIC_EOI); +} + +void IRQ_set_mask(unsigned char IRQline) { + u16 port; + u8 value; + + if(IRQline < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + IRQline -= 8; + } + value = inb(port) | (1 << IRQline); + outb(port, value); +} + +void IRQ_clear_mask(unsigned char IRQline) { + u16 port; + u8 value; + + if(IRQline < 8) { + port = PIC1_DATA; + } else { + port = PIC2_DATA; + IRQline -= 8; + } + value = inb(port) & ~(1 << IRQline); + outb(port, value); +} + +void pic_init(void) +{ + DEBUG_INFO("Initializing PIC"); + + unsigned char a1, a2; + + a1 = inb(PIC1_DATA); // save masks + a2 = inb(PIC2_DATA); + + outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode) + io_wait(); + outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4); + io_wait(); + + DEBUG_INFO("Setting PIC 1 IRQ offset to 32"); + outb(PIC1_DATA, 0x20); // ICW2: Master PIC vector offset + io_wait(); + + DEBUG_INFO("Setting PIC 2 IRQ offset to 40"); + outb(PIC2_DATA, 0x28); // ICW2: Slave PIC vector offset + io_wait(); + outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) + io_wait(); + outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010) + io_wait(); + + outb(PIC1_DATA, ICW4_8086); // ICW4: have the PICs use 8086 mode (and not 8080 mode) + io_wait(); + outb(PIC2_DATA, ICW4_8086); + io_wait(); + + outb(PIC1_DATA, a1); // restore saved masks. + outb(PIC2_DATA, a2); + + DEBUG_INFO("Setting all IRQs to be masked"); + for (unsigned char i = 0; i < 16; i++) + { + IRQ_set_mask(i); + } + + DEBUG_INFO("Unmasking IRQ 1 - Keyboard"); + IRQ_clear_mask(1); + + DEBUG_INFO("PIC initialized"); +} diff --git a/src/pic_controler.h b/src/pic_controler.h new file mode 100644 index 0000000..da9cc16 --- /dev/null +++ b/src/pic_controler.h @@ -0,0 +1,7 @@ +#ifndef PIC_CONTROLER_H +#define PIC_CONTROLER_H + +void PIC_sendEOI(unsigned char irq); +void pic_init(void); + +#endif /* !PIC_CONTROLER_H */ \ No newline at end of file diff --git a/src/serial.c b/src/serial.c new file mode 100644 index 0000000..ba59ba2 --- /dev/null +++ b/src/serial.c @@ -0,0 +1,88 @@ +#include "serial.h" + +#include "io.h" + +#define PORT 0x3f8 // COM1 + +int strlen(char *str) +{ + int i = 0; + while (str[i++] != '\0'); + return i; +} + +int init_serial() +{ + outb(PORT + 1, 0x00); // Disable all interrupts + outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud + outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(PORT + 1, 0x00); // (hi byte) + outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit + outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set +} + +int write_serial_nb(int nb, int ln) +{ + if (nb < 10) + { + while ((inb(PORT + 5) & 0x20) == 0); + outb(PORT, nb + '0'); + return 0; + } + + write_serial_nb(nb / 10, false); + + while ((inb(PORT + 5) & 0x20) == 0); + outb(PORT, nb % 10 + '0'); + + if (ln) + { + while ((inb(PORT + 5) & 0x20) == 0); + outb(PORT, '\n'); + } + + return 0; +} + +int write_serial_bin(int nb, int ln) +{ + if (nb < 2) + { + while ((inb(PORT + 5) & 0x20) == 0); + outb(PORT, nb + '0'); + return 0; + } + + write_serial_bin(nb / 2, false); + + while ((inb(PORT + 5) & 0x20) == 0); + outb(PORT, nb % 2 + '0'); + + if (ln) + { + while ((inb(PORT + 5) & 0x20) == 0); + outb(PORT, '\n'); + } + + return 0; +} + +int write_serial_char(char c) +{ + while ((inb(PORT + 5) & 0x20) == 0); + outb(PORT, c); + + return 0; +} + +int write_serial(char * s) +{ + for (size_t i = 0; i < strlen(s); i++) + { + while ((inb(PORT + 5) & 0x20) == 0); + outb(PORT, s[i]); + } + + return 0; +} \ No newline at end of file diff --git a/src/serial.h b/src/serial.h new file mode 100644 index 0000000..a7425b0 --- /dev/null +++ b/src/serial.h @@ -0,0 +1,12 @@ +#ifndef SERIAL_H +#define SERIAL_H + +#include + +int init_serial(); +int write_serial(char *s); +int write_serial_nb(int nb, bool ln); +int write_serial_bin(int nb, int ln); +int write_serial_char(char c); + +#endif /* SERIAL_H */ \ No newline at end of file diff --git a/src/syscall.c b/src/syscall.c new file mode 100644 index 0000000..670f283 --- /dev/null +++ b/src/syscall.c @@ -0,0 +1,26 @@ +#include "syscall.h" +#include "debug.h" +#include "serial.h" + +/* + * Syscall handler for write, currently only serial write is supported. + * Use the fd 1, to make a serial write. + */ +int write(int fd, void *buf) +{ + if (fd != 1) + return -1; + + if (write_serial(buf)) + return -1; + + return 0; +} + +/* + * Syscall handler for keyboard input, this function is currently not implemented. + */ +int keyboard(void) +{ + return 1; +} diff --git a/src/syscall.h b/src/syscall.h new file mode 100644 index 0000000..36c9358 --- /dev/null +++ b/src/syscall.h @@ -0,0 +1,7 @@ +#ifndef SYSCALL_H +#define SYSCALL_H + +int write(int fd, void *buf); +int keyboard(void); + +#endif /* !SYSCALL_H */ \ No newline at end of file diff --git a/src/tss.h b/src/tss.h new file mode 100644 index 0000000..43d6def --- /dev/null +++ b/src/tss.h @@ -0,0 +1,52 @@ +#ifndef TSS_H +#define TSS_H + +#include + +struct tss +{ + u16 previous_task; + u16 reserved_1; + u32 esp0; + u16 ss0; + u16 reserved_2; + u32 esp1; + u16 ss1; + u16 reserved_3; + u32 esp2; + u16 ss2; + u16 reserved_4; + u32 cr3; + u32 eip; + u32 eflags; + u32 eax; + u32 ecx; + u32 edx; + u32 ebx; + u32 esp; + u32 ebp; + u32 esi; + u32 edi; + u16 es; + u16 reserved_5; + u16 cs; + u16 reserved_6; + u16 ss; + u16 reserved_7; + u16 ds; + u16 reserved_8; + u16 fs; + u16 reserved_9; + u16 gs; + u16 reserved_10; + u16 lst_seg_select; + u16 reserved_11; + u16 t_bitmap : 1; + u16 reserved_12 : 15; + u16 io_map_base_address; + u32 ssp; +} __attribute__((packed)); + +extern struct tss user_land_tss; + +#endif /* !TSS_H */ diff --git a/src/userland.c b/src/userland.c new file mode 100644 index 0000000..83c36f2 --- /dev/null +++ b/src/userland.c @@ -0,0 +1,26 @@ +#include "userland.h" + +void userland(void) +{ + int res = 0; + // asm ("mov $1, %0" : "=r" (res)); + // asm volatile ("movl $2, %eax; int $0x30"); + // asm("movl %%eax,%0" : "=r"(res)); + // asm volatile ("int $0x30"); + char *str = (void *) 0x100; + str[0] = 'H'; + str[1] = 'e'; + str[2] = 'l'; + str[3] = 'l'; + str[4] = 'o'; + str[5] = '\0'; + asm volatile ("mov $1, %%eax; movl $0x30100, %%ebx; int $0x30; movl %%eax, %0" : "=r" (res)); + // asm volatile ("mov $1, %%eax; movl %0, %%ebx; int $0x30" : "=m" (str)); + // asm ("mov $1, %eax; int $0x30"); + // asm ("movl %0, %eax; int $0x30" : "=m" (res)); + // asm ("movl %eax, %eax; int $0x30"); + // asm ("movl $28, %eax; movl $5, %ebx; int $0x30"); + // asm ("movl $43, %eax; movl $7, %ebx; int $0x30"); + while (1); + return; /* never go there */ +} \ No newline at end of file diff --git a/src/userland.h b/src/userland.h new file mode 100644 index 0000000..86da88f --- /dev/null +++ b/src/userland.h @@ -0,0 +1,6 @@ +#ifndef USERLAND_H +#define USERLAND_H + +void userland(void); + +#endif /* !USERLAND_H */ diff --git a/tools/create_iso.sh b/tools/create_iso.sh new file mode 100755 index 0000000..b2a778b --- /dev/null +++ b/tools/create_iso.sh @@ -0,0 +1,8 @@ +#!/bin/sh + +mkdir -p iso/boot/grub +echo "menuentry \"kernel\" { + multiboot /kernel +}" > iso/boot/grub/grub.cfg + +grub-mkrescue -o kernel.iso iso