From f1341d3d721571fc539c0a0ec97f7ea8dc8769b9 Mon Sep 17 00:00:00 2001 From: Mathieu Maret Date: Fri, 24 Nov 2017 23:41:14 +0100 Subject: [PATCH] Add some "kernel" code written in C Draw few message on VGA Setup Interruption Description Table Setup IRQ --- Makefile | 6 +++--- idt.c | 44 ++++++++++++++++++++++++++++++++++++++ idt.h | 44 ++++++++++++++++++++++++++++++++++++++ io.h | 21 +++++++++++++++++++ irq.c | 8 +++++++ irq.h | 21 +++++++++++++++++++ main.c | 37 ++++++++++++++++++++++++++++++++ mbr.asm | 8 +++++++ pic.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ pic.h | 18 ++++++++++++++++ types.h | 36 +++++++++++++++++++++++++++++++ vga.c | 30 ++++++++++++++++++++++++++ vga.h | 19 +++++++++++++++++ 13 files changed, 353 insertions(+), 3 deletions(-) create mode 100644 idt.c create mode 100644 idt.h create mode 100644 io.h create mode 100644 irq.c create mode 100644 irq.h create mode 100644 main.c create mode 100644 pic.c create mode 100644 pic.h create mode 100644 types.h create mode 100644 vga.c create mode 100644 vga.h 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);