diff --git a/Makefile b/Makefile index b244601..78c5085 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ AS=nasm ASFLAGS += -f elf32 -LDFLAGS += -m32 -nostdlib -static -fno-common -fno-use-cxa-atexit -fno-exceptions -fno-non-call-exceptions -fno-weak -fno-rtti -CFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -CXXFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-rtti +LDFLAGS += -m32 -nostdlib -static -fno-common -fno-use-cxa-atexit -fno-exceptions -fno-non-call-exceptions -fno-weak -fno-rtti -fno-stack-protector +CFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-pie -fno-stack-protector +CXXFLAGS += -m32 -Wall -Wextra -Werror -ffreestanding -fno-exceptions -fno-rtti -fno-pie asmsrc=$(wildcard *.asm) diff --git a/idt.c b/idt.c new file mode 100644 index 0000000..6ea8a02 --- /dev/null +++ b/idt.c @@ -0,0 +1,44 @@ +#include "idt.h" +static struct idtEntry idt[IDT_NUM]; + + +int idtSetup() +{ + struct idtRegister idtr; + + for (int i = 0; i < IDT_NUM; i++) { + struct idtEntry *idte = idt + i; + + /* Setup an empty IDTE interrupt gate, see figure 5-2 in Intel + x86 doc, vol 3 */ + idte->seg_sel = BUILD_SEGMENT_SELECTOR(RING_0, 0, SEGMENT_IDX_CODE); + idte->reserved = 0; + idte->flags = 0; + idte->type = 0x6; /* Interrupt gate (110b) */ + idte->op_size = 1; /* 32bits instructions */ + + /* Disabled it for now */ + idte->zero = 0; + idte->offset_low = 0; + idte->offset_high = 0; + idte->dpl = 0; + idte->present = 0; + } + + /* + * Setup the IDT register, see Intel x86 doc vol 3, section 5.8. + */ + + /* Address of the IDT */ + idtr.base_addr = (uint32_t) idt; + + /* The limit is the maximum offset in bytes from the base address of + the IDT */ + idtr.limit = sizeof(idt) - 1; + + /* Commit the IDT into the CPU */ + asm volatile ("lidt %0\n"::"m"(idtr):"memory"); + + return 0; +} + diff --git a/idt.h b/idt.h new file mode 100644 index 0000000..35ac63d --- /dev/null +++ b/idt.h @@ -0,0 +1,44 @@ +#pragma once +#include "types.h" + +#define IDT_NUM 256 + +#define RING_0 0 +#define RING_1 1 +#define RING_2 2 +#define RING_3 3 + +#define SEGMENT_IDX_NULL 0 +#define SEGMENT_IDX_CODE 1 +#define SEGMENT_IDX_DATA 2 + +struct idtEntry { + uint16_t offset_low; + uint16_t seg_sel; + uint8_t reserved : 5; + uint8_t flags : 3; + uint8_t type : 3; + uint8_t op_size : 1; + uint8_t zero : 1; + uint8_t dpl : 2; + uint8_t present : 1; + uint16_t offset_high; +} __attribute__((packed)); + +/** + * The IDT register, which stores the address and size of the + * IDT. + * + * @see Intel x86 doc vol 3, section 2.4, figure 2-4 + */ +struct idtRegister { + uint16_t limit; + uint32_t base_addr; +} __attribute__((packed, aligned(8))); + +/* Build segment http://wiki.osdev.org/Selector*/ +#define BUILD_SEGMENT_SELECTOR(desc_privilege, in_ldt, index) \ + ((((desc_privilege)&0x3) << 0) | (((in_ldt) ? 1 : 0) << 2) | \ + ((index) << 3)) + +int idtSetup(); diff --git a/io.h b/io.h new file mode 100644 index 0000000..9f0a62b --- /dev/null +++ b/io.h @@ -0,0 +1,21 @@ +#pragma once +#include "types.h" + +// NIH http://wiki.osdev.org/Inline_Assembly/Examples#I.2FO_access +static inline void outb(uint16_t port, uint8_t val) +{ + asm volatile ( "outb %0, %1" : : "a"(val), "Nd"(port) ); + /* There's an outb %al, $imm8 encoding, for compile-time constant port numbers that fit in 8b. (N constraint). + * Wider immediate constants would be truncated at assemble-time (e.g. "i" constraint). + * The outb %al, %dx encoding is the only option for all other cases. + * %1 expands to %dx because port is a uint16_t. %w1 could be used if we had the port number a wider C type */ +} + +static inline uint8_t inb(uint16_t port) +{ + uint8_t ret; + asm volatile ( "inb %1, %0" + : "=a"(ret) + : "Nd"(port) ); + return ret; +} diff --git a/irq.c b/irq.c new file mode 100644 index 0000000..de6fe45 --- /dev/null +++ b/irq.c @@ -0,0 +1,8 @@ +#include "irq.h" +#include "pic.h" + +int irqSetup() +{ + initPic(); + return 0; +} diff --git a/irq.h b/irq.h new file mode 100644 index 0000000..3e3b608 --- /dev/null +++ b/irq.h @@ -0,0 +1,21 @@ +#pragma once + +#define IRQ_TIMER 0 // MASTER IRQ +#define IRQ_KEYBOARD 1 +#define IRQ_SLAVE_PIC 2 +#define IRQ_COM2 3 +#define IRQ_COM1 4 +#define IRQ_LPT2 5 +#define IRQ_FLOPPY 6 +#define IRQ_LPT1 7 +#define IRQ_8_NOT_DEFINED 8 // SLAVE +#define IRQ_RESERVED_1 9 // SLAVE IRQ +#define IRQ_RESERVED_2 10 +#define IRQ_RESERVED_3 11 +#define IRQ_RESERVED_4 12 +#define IRQ_COPROCESSOR 13 +#define IRQ_HARDDISK 14 +#define IRQ_RESERVED_5 15 + +typedef void (*irq_handler)(int irq); +int irqSetup(); diff --git a/main.c b/main.c new file mode 100644 index 0000000..a6884c0 --- /dev/null +++ b/main.c @@ -0,0 +1,37 @@ +#include "idt.h" +#include "io.h" +#include "irq.h" +#include "types.h" +#include "vga.h" + +char getScancode() +{ + char c = 0; + do { + if (inb(0x60) != c) { + c = inb(0x60); + if (c > 0) + return c; + } + } while (1); +} + +void cpuid(int code, uint32_t *a, uint32_t *d) +{ + asm volatile("cpuid" : "=a"(*a), "=d"(*d) : "0"(code) : "ebx", "ecx"); +} + +void kmain() +{ + const short color = GREEN; + clearScreen(BLACK); + printString("Setting up IDT", color, BLACK, 0, 0); + idtSetup(); + printString("Setting up IRQ", color, BLACK, 0, 1); + irqSetup(); + + while (1) { + char c = getScancode(); + printChar(c, color, BLACK, 0, 5); + } +} diff --git a/mbr.asm b/mbr.asm index bc1ff9d..577597e 100644 --- a/mbr.asm +++ b/mbr.asm @@ -152,6 +152,14 @@ boot2: add ebx,2 jmp .loop32 halt: + mov esp,kernel_stack_top + extern kmain + call kmain cli hlt hello32: db "Hello 32 bits world!",0 +section .bss +align 4 +kernel_stack_bottom: equ $ + resb 16384 ; 16 KB +kernel_stack_top: diff --git a/pic.c b/pic.c new file mode 100644 index 0000000..4c7b279 --- /dev/null +++ b/pic.c @@ -0,0 +1,64 @@ +#include "pic.h" +#include "io.h" + +#define ICW1_ICW4 0x01 /* ICW4 (not) needed */ +#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) */ + +void initPic(void) +{ + /* Send CMD: Init + senquence in 4 DATA */ + outb(ICW1_INIT + ICW1_ICW4, PIC_MASTER_CMD); + outb(ICW1_INIT + ICW1_ICW4, PIC_SLAVE_CMD); + + /* Send ICW2: ctrl base address. Remap IRQ from interupt range 0x0-0xF to 0x20-0x2F as + * intel + * reserve interupt 0x0-0x1F in protected mode (e.g. 0-7 are CPU exception) */ + outb(0x20, PIC_MASTER_DATA); + outb(0x28, PIC_SLAVE_DATA); + + /* Send ICW3 master: mask where slaves are connected */ + outb(0x4, PIC_MASTER_DATA); + /* Send ICW3 slave: index where the slave is connected on master */ + outb(0x2, PIC_SLAVE_DATA); + + /* Send ICW4: 8086 mode, fully nested, not buffered, no implicit EOI */ + outb(ICW4_8086, PIC_MASTER_DATA); + outb(ICW4_8086, PIC_SLAVE_DATA); + + /* Send OCW1: + * Closing all IRQs : waiting for a correct handler The only IRQ + * enabled is the cascade (that's why we use 0xFB for the master) */ + outb(0xFB, PIC_MASTER_DATA); + outb(0xFF, PIC_SLAVE_DATA); +} + +void enableIrq(int irq) +{ + if (irq < 8) { + uint8_t status = inb(PIC_MASTER_DATA); + outb((status | (1 << irq)), PIC_MASTER_DATA); + } else { + uint8_t status = inb(PIC_SLAVE_DATA); + outb((status | (1 << irq)), PIC_SLAVE_DATA); + } +} + +void disableIrq(int irq) +{ + if (irq < 8) { + uint8_t status = inb(PIC_MASTER_DATA); + outb((status & ~(1 << irq)), PIC_MASTER_DATA); + } else { + uint8_t status = inb(PIC_SLAVE_DATA); + outb((status & ~(1 << irq)), PIC_SLAVE_DATA); + } +} diff --git a/pic.h b/pic.h new file mode 100644 index 0000000..0b7c616 --- /dev/null +++ b/pic.h @@ -0,0 +1,18 @@ +#pragma once +//2 PIC 8259 are available on x86 +// +// Master - command: 0x20, data: 0x21 +// Slave - command: 0xA0, data: 0xA1 +// +// http://www.jamesmolloy.co.uk/tutorial_html/5.-IRQs%20and%20the%20PIT.html +// SimpleOS art2 +// http://wiki.osdev.org/PIC + +#define PIC_MASTER_CMD 0x20 +#define PIC_SLAVE_CMD 0xa0 +#define PIC_MASTER_DATA 0x21 +#define PIC_SLAVE_DATA 0xa0 + +void initPic(void); +void enableIrq(int irq); +void disableIrq(int irq); diff --git a/types.h b/types.h new file mode 100644 index 0000000..1d00f76 --- /dev/null +++ b/types.h @@ -0,0 +1,36 @@ +#pragma once + +typedef __signed__ char __s8; +typedef unsigned char __u8; + +typedef __signed__ short __s16; +typedef unsigned short __u16; + +typedef __signed__ int __s32; +typedef unsigned int __u32; + +#ifdef __GNUC__ +__extension__ typedef __signed__ long long __s64; +__extension__ typedef unsigned long long __u64; +#else +typedef __signed__ long long __s64; +typedef unsigned long long __u64; +#endif + +/* sysv */ +typedef unsigned char unchar; +typedef unsigned short ushort; +typedef unsigned int uint; +typedef unsigned long ulong; + +typedef __s8 int8_t; +typedef __s16 int16_t; +typedef __s32 int32_t; + +typedef __u8 uint8_t; +typedef __u16 uint16_t; +typedef __u32 uint32_t; + +typedef __u64 uint64_t; +typedef __u64 u_int64_t; +typedef __s64 int64_t; diff --git a/vga.c b/vga.c new file mode 100644 index 0000000..0925e02 --- /dev/null +++ b/vga.c @@ -0,0 +1,30 @@ +#include "vga.h" + +void clearScreen(int bgColor) +{ + volatile short *vga = (short *)VGA_ADDR; + long int colorAttr = bgColor << 12; + for (int i = 0; i < VGA_WIDTH * VGA_HEIGHT; i++) { + vga[i] = colorAttr; + } +} + +void printChar(const char str, int color, int bgColor, int startX, int startY) +{ + volatile short *vga = (short *)VGA_ADDR; + long int colorAttr = (bgColor << 4 | (color & 0x0f)) << 8; + vga[80 * startY + startX] = colorAttr | str; +} + +void printString(const char *str, int color, int bgColor, int startX, + int startY) +{ + volatile short *vga = (short *)VGA_ADDR; + int i = 0; + long int colorAttr = (bgColor << 4 | (color & 0x0f)) << 8; + while (*str) { + vga[80 * startY + startX + i] = colorAttr | *str; + str++; + i++; + } +} diff --git a/vga.h b/vga.h new file mode 100644 index 0000000..d32d6a3 --- /dev/null +++ b/vga.h @@ -0,0 +1,19 @@ +#pragma once + +#define BLACK 0x00 +#define BLUE 0x01 +#define GREEN 0x02 +#define CYAN 0x03 +#define RED 0x04 +#define MAGENTA 0x05 +#define BROWN 0x06 +#define GREY 0x07 +#define WHITE 0x0F + +#define VGA_ADDR 0xB8000 +#define VGA_WIDTH 80 +#define VGA_HEIGHT 15 + +void clearScreen(int bgColor); +void printChar(const char str, int color, int bgColor, int startX, int startY); +void printString(const char *str, int color, int bgColor, int startX, int startY);