summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksa Vučković <aleksav013@gmail.com>2021-10-11 11:35:59 +0200
committerAleksa Vučković <aleksav013@gmail.com>2021-10-11 11:35:59 +0200
commit578d467b80015c52d0c96c8443b4c13936f33365 (patch)
tree05525782bc3baf5a01d8b657f01934e1e598a775
parent3a9ccbd8e762477f75d8b164a1d99383a01414ae (diff)
(IDT + keyboard) finally working
-rw-r--r--.gdbinit2
-rw-r--r--.gitignore2
-rw-r--r--Makefile36
-rw-r--r--README.md18
-rw-r--r--src/boot.s57
-rw-r--r--src/gdt.c13
-rw-r--r--src/idt.c103
-rw-r--r--src/kernel.c86
-rw-r--r--src/keyboard.h74
9 files changed, 342 insertions, 49 deletions
diff --git a/.gdbinit b/.gdbinit
new file mode 100644
index 0000000..5bf175c
--- /dev/null
+++ b/.gdbinit
@@ -0,0 +1,2 @@
+target remote localhost:1234
+file myiso.iso
diff --git a/.gitignore b/.gitignore
index 8f8bb8c..39e998c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,3 @@
build
isodir
-myos.iso
+*.iso
diff --git a/Makefile b/Makefile
index b427918..127e576 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,13 @@
-CC=i686-elf-gcc
-AS=i686-elf-as
-CFLAGS=-ffreestanding -O2 -Wall -Wextra
+ARCH=i686-elf
+
+CC=$(ARCH)-gcc
+AS=$(ARCH)-as
+CFLAGS=-ffreestanding -O2 -Wall -Wextra -ggdb
MKDIR=mkdir -p
RM=rm -rf
CP=cp
+QEMU=qemu-system-x86_64
SOURCE_DIR=src
BUILD_DIR=build
@@ -12,11 +15,16 @@ ISO_DIR=isodir
TARGET=myos
-OBJ_FILES=boot.o kernel.o gdt.o
+OBJ_FILES=boot.o kernel.o gdt.o idt.o
CRTBEGIN_OBJ=$(shell $(CC) -print-file-name=crtbegin.o)
CRTEND_OBJ=$(shell $(CC) -print-file-name=crtend.o)
OBJ=$(BUILD_DIR)/crti.o $(CRTBEGIN_OBJ) $(patsubst %,$(BUILD_DIR)/%,$(OBJ_FILES)) $(CRTEND_OBJ) $(BUILD_DIR)/crtn.o
+
+# Default action is set to making kernel binary
+.PHONY: all
+all: $(BUILD_DIR)/$(TARGET).bin
+
# Creating iso file
$(TARGET).iso: $(BUILD_DIR)/$(TARGET).bin
grub-file --is-x86-multiboot $(BUILD_DIR)/myos.bin
@@ -25,9 +33,8 @@ $(TARGET).iso: $(BUILD_DIR)/$(TARGET).bin
$(CP) $(SOURCE_DIR)/grub.cfg $(ISO_DIR)/boot/grub/grub.cfg
grub-mkrescue -o $(TARGET).iso $(ISO_DIR)
-# Linking object files
+# Linking object files into kernel binary
$(BUILD_DIR)/$(TARGET).bin: $(OBJ)
- $(MKDIR) $(BUILD_DIR)
$(CC) -T $(SOURCE_DIR)/linker.ld -o $@ $(CFLAGS) -nostdlib $^ -lgcc
# Compiling as sources
@@ -40,10 +47,21 @@ $(BUILD_DIR)/%.o: $(SOURCE_DIR)/%.c
$(MKDIR) $(BUILD_DIR)
$(CC) -c $< -o $@ -std=gnu99 $(CFLAGS)
-# Boot kernel in qemu
+# Boot kernel binary in qemu
.PHONY: run
-run: $(TARGET).iso
- qemu-system-x86_64 -cdrom $(TARGET).iso
+run: $(BUILD_DIR)/$(TARGET).bin
+ $(QEMU) -kernel $^
+
+# Boot iso in qemu
+.PHONY: run-iso
+run-iso: $(TARGET).iso
+ $(QEMU) -cdrom $^
+
+# Debug kernel binary in gdb
+.PHONY: debug
+debug: $(TARGET).bin
+ $(QEMU) -kernel $^ -s -S &
+ gdb -x .gdbinit
# Clean build files
.PHONY: clean
diff --git a/README.md b/README.md
index 917cdb3..3723c91 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,16 @@
# mykernel
## i686-elf cross compiler
-First install binutils and gcc from your package manager.
-On Arch based distributions they are available in AUR:
-- [binutils](https://aur.archlinux.org/packages/i686-elf-binutils/)
-- [gcc](https://aur.archlinux.org/packages/i686-elf-gcc/)
+- [binutils](https://aur.archlinux.org/packages/i686-elf-binutils/)
+- [gcc](https://aur.archlinux.org/packages/i686-elf-gcc/)
-## Bare bones kernel in as and C
-[osdev](https://wiki.osdev.org/Bare_Bones)
+## You will also need:
+- grub
+- xorriso(libisoburn)
+- qemu
+- gdb(optional)
+
+## Useful articles
+- [Bare Bones](https://wiki.osdev.org/Bare_Bones)
+- [Global Descriptor Table](https://wiki.osdev.org/Global_Descriptor_Table)
+- [Interrupt Descriptor Table](https://wiki.osdev.org/Interrupt_Descriptor_Table)
diff --git a/src/boot.s b/src/boot.s
index a0f0c2e..b88e8f7 100644
--- a/src/boot.s
+++ b/src/boot.s
@@ -12,33 +12,70 @@
.global _start
.global load_gdt
+.global load_idt
+.global enable_interrupts
+.global keyboard_handler
+.global ioport_in
+.global ioport_out
+
+.extern init_gdt_table
+.extern handle_keyboard_interrupt
+.extern kernel_main
load_gdt:
movl 4(%esp), %edx
lgdt (%edx)
ret
+load_idt:
+ movl 4(%esp), %edx
+ lidt (%edx)
+ sti
+ ret
+
+keyboard_handler:
+ pushal
+ cld
+ call handle_keyboard_interrupt
+ popal
+ iretl
+
+ioport_in:
+ movl 4(%esp),%edx
+ in %dx,%al
+ ret
+
+ioport_out:
+ movl 4(%esp),%edx
+ movl 8(%esp),%eax
+ outb %al,%dx
+ ret
+
+.set CODE_SEGMENT, 0x08
+.set DATA_SEGMENT, 0x10
+
.section .bss
.align 16
stack_bottom:
-.skip 16384 # 16 KiB
+.skip 16384
stack_top:
.section .text
.type _start, @function
_start:
call init_gdt_table
- mov 0x10, %ax
- mov %ds, %ax
- mov %es, %ax
- mov %fs, %ax
- mov %gs, %ax
- mov %ss, %ax
- mov $stack_top, %esp
+ ljmp $CODE_SEGMENT, $next
+ next:
+ movw $DATA_SEGMENT, %ax
+ movw %ax, %ds
+ movw %ax, %es
+ movw %ax, %fs
+ movw %ax, %gs
+ movw %ax, %ss
+ movl $stack_top, %esp
cli
call _init
call kernel_main
-1: hlt
- jmp 1b
+ hlt
.size _start, . - _start
diff --git a/src/gdt.c b/src/gdt.c
index 9f87032..554c2ee 100644
--- a/src/gdt.c
+++ b/src/gdt.c
@@ -24,13 +24,14 @@ extern void load_gdt(struct gdt_pointer *gdtp);
struct gdt_entry gdt[3];
struct gdt_pointer gdtp;
-void init_gdt_entry(size_t num, uint32_t limit, uint32_t base1, uint32_t base2, uint8_t access, uint8_t limit_flags, uint8_t base3)
+void init_gdt_entry(size_t num, uint32_t limit, uint32_t base, uint8_t access, uint8_t limit_flags)
{
gdt[num].limit=limit;
- gdt[num].base1=base1;
+ gdt[num].base1=(base & 0xffff);
+ gdt[num].base2=(base & 0xff0000) >> 16;
gdt[num].access=access;
gdt[num].limit_flags=limit_flags;
- gdt[num].base3=base3;
+ gdt[num].base3=(base & 0xff000000) >> 24;
}
void init_gdt_table()
@@ -39,11 +40,11 @@ void init_gdt_table()
gdtp.offset=(uint32_t)&gdt;
//null
- init_gdt_entry(0,0,0,0,0,0,0);
+ init_gdt_entry(0,0,0,0,0);
//code
- init_gdt_entry(1,0xffff,0x0000,0x00,0b10011010,0b11001111,0x00);
+ init_gdt_entry(1,0xffffffff,0,0b10011010,0b11001111);
//data
- init_gdt_entry(2,0xffff,0x0000,0x00,0b10010010,0b11001111,0x00);
+ init_gdt_entry(2,0xffffffff,0,0b10010010,0b11001111);
load_gdt(&gdtp);
}
diff --git a/src/idt.c b/src/idt.c
new file mode 100644
index 0000000..479d397
--- /dev/null
+++ b/src/idt.c
@@ -0,0 +1,103 @@
+#include<stdbool.h>
+#include<stddef.h>
+#include<stdint.h>
+
+#define INTERRUPT_GATE_32 0x8e
+
+#define KERNEL_CODE 0x08
+#define KERNEL_DATA 0x10
+
+#define PIC1_COMMAND_PORT 0x20
+#define PIC1_DATA_PORT 0x21
+#define PIC2_COMMAND_PORT 0xA0
+#define PIC2_DATA_PORT 0xA1
+
+struct idt_entry
+{
+ uint16_t offset1;
+ uint16_t selector;
+ uint8_t zero;
+ uint8_t type_attr;
+ uint16_t offset2;
+} __attribute__((packed));
+
+struct idt_pointer
+{
+ uint16_t size;
+ uint32_t offset;
+} __attribute__((packed));
+
+// asm function
+extern void load_idt(struct idt_pointer *idtp);
+extern void keyboard_handler();
+extern void ioport_out(uint8_t port, char data);
+
+struct idt_entry idt[256];
+struct idt_pointer idtp;
+
+void init_idt_entry(size_t num, uint32_t offset, uint16_t selector, uint8_t type_attr)
+{
+ idt[num].offset1=(offset & 0xffff);
+ idt[num].selector=selector;
+ idt[num].zero=0;
+ idt[num].type_attr=type_attr;
+ idt[num].offset2=(offset & 0xffff0000)>>16;
+}
+
+void init_idt_table()
+{
+ // Program the PICs - Programmable Interrupt Controllers
+ // Background:
+ // In modern architectures, the PIC is not a separate chip.
+ // It is emulated in the CPU for backwards compatability.
+ // The APIC (Advanced Programmable Interrupt Controller)
+ // is the new version of the PIC that is integrated into the CPU.
+ // Default vector offset for PIC is 8
+ // This maps IRQ0 to interrupt 8, IRQ1 to interrupt 9, etc.
+ // This is a problem. The CPU reserves the first 32 interrupts for
+ // CPU exceptions such as divide by 0, etc.
+ // In programming the PICs, we move this offset to 0x2 (32) so that
+ // we can handle all interrupts coming to the PICs without overlapping
+ // with any CPU exceptions.
+
+ // Send ICWs - Initialization Command Words
+ // PIC1: IO Port 0x20 (command), 0xA0 (data)
+ // PIC2: IO Port 0x21 (command), 0xA1 (data)
+ // ICW1: Initialization command
+ // Send a fixed value of 0x11 to each PIC to tell it to expect ICW2-4
+ // Restart PIC1
+ ioport_out(PIC1_COMMAND_PORT, 0x11);
+ ioport_out(PIC2_COMMAND_PORT, 0x11);
+ // ICW2: Vector Offset (this is what we are fixing)
+ // Start PIC1 at 32 (0x20 in hex) (IRQ0=0x20, ..., IRQ7=0x27)
+ // Start PIC2 right after, at 40 (0x28 in hex)
+ ioport_out(PIC1_DATA_PORT, 0x20);
+ ioport_out(PIC2_DATA_PORT, 0x28);
+ // ICW3: Cascading (how master/slave PICs are wired/daisy chained)
+ // Tell PIC1 there is a slave PIC at IRQ2 (why 4? don't ask me - https://wiki.osdev.org/8259_PIC)
+ // Tell PIC2 "its cascade identity" - again, I'm shaky on this concept. More resources in notes
+ ioport_out(PIC1_DATA_PORT, 0x0);
+ ioport_out(PIC2_DATA_PORT, 0x0);
+ // ICW4: "Gives additional information about the environemnt"
+ // See notes for some potential values
+ // We are using 8086/8088 (MCS-80/85) mode
+ // Not sure if that's relevant, but there it is.
+ // Other modes appear to be special slave/master configurations (see wiki)
+ ioport_out(PIC1_DATA_PORT, 0x1);
+ ioport_out(PIC2_DATA_PORT, 0x1);
+ // Voila! PICs are initialized
+
+ // Mask all interrupts (why? not entirely sure)
+ // 0xff is 16 bits that are all 1.
+ // This masks each of the 16 interrupts for that PIC.
+ ioport_out(PIC1_DATA_PORT, 0xff);
+ ioport_out(PIC2_DATA_PORT, 0xff);
+
+ init_idt_entry(0x21,(uint32_t)keyboard_handler,KERNEL_CODE,INTERRUPT_GATE_32);
+
+ idtp.size=sizeof(struct idt_entry)*256-1;
+ idtp.offset=(uint32_t)&idt;
+
+
+ load_idt(&idtp);
+}
diff --git a/src/kernel.c b/src/kernel.c
index c6d1045..0882580 100644
--- a/src/kernel.c
+++ b/src/kernel.c
@@ -6,6 +6,17 @@
static const size_t VGA_WIDTH = 80;
static const size_t VGA_HEIGHT = 25;
+#define PIC1_COMMAND_PORT 0x20
+#define PIC1_DATA_PORT 0x21
+#define PIC2_COMMAND_PORT 0xA0
+#define PIC2_DATA_PORT 0xA1
+// IO Ports for Keyboard
+#define KEYBOARD_DATA_PORT 0x60
+#define KEYBOARD_STATUS_PORT 0x64
+
+extern char ioport_in(uint8_t port);
+extern void ioport_out(uint8_t port, char data);
+
static inline uint8_t vga_entry_color(enum vga_color fg, enum vga_color bg)
{
return fg | bg << 4;
@@ -23,24 +34,24 @@ uint16_t* terminal_buffer;
void terminal_initialize(void)
{
- terminal_row = 0;
- terminal_column = 0;
- terminal_color = vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
- terminal_buffer = (uint16_t*) 0xB8000;
- for (size_t y = 0; y < VGA_HEIGHT; y++)
+ terminal_row=0;
+ terminal_column=0;
+ terminal_color=vga_entry_color(VGA_COLOR_LIGHT_GREY, VGA_COLOR_BLACK);
+ terminal_buffer=(uint16_t*) 0xB8000;
+ for(size_t y=0;y<VGA_HEIGHT;y++)
{
- for (size_t x = 0; x < VGA_WIDTH; x++)
+ for(size_t x=0;x<VGA_WIDTH;x++)
{
- const size_t index = y * VGA_WIDTH + x;
- terminal_buffer[index] = vga_entry(' ', terminal_color);
+ const size_t index=y*VGA_WIDTH+x;
+ terminal_buffer[index]=vga_entry(' ', terminal_color);
}
}
}
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
{
- const size_t index = y * VGA_WIDTH + x;
- terminal_buffer[index] = vga_entry(c, color);
+ const size_t index=y*VGA_WIDTH+x;
+ terminal_buffer[index]=vga_entry(c, color);
}
void movescreen()
@@ -55,9 +66,9 @@ void terminal_putchar(char c)
else
{
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
- if (++terminal_column == VGA_WIDTH) terminal_column = 0,terminal_row++;
+ if (++terminal_column==VGA_WIDTH) terminal_column=0,terminal_row++;
}
- if (terminal_row == VGA_HEIGHT) movescreen();
+ if (terminal_row==VGA_HEIGHT) movescreen();
}
void terminal_writestring(const char* data)
@@ -65,13 +76,54 @@ void terminal_writestring(const char* data)
for(int i=0;data[i]!='\0';i++) terminal_putchar(data[i]);
}
-void kernel_main(void)
+void init_keyboard()
{
- terminal_initialize();
+ // 0xFD = 1111 1101 in binary. enables only IRQ1
+ // Why IRQ1? Remember, IRQ0 exists, it's 0-based
+ ioport_out(PIC1_DATA_PORT, 0xFD);
+}
+
+#include"keyboard.h"
+void handle_keyboard_interrupt()
+{
+ // Write end of interrupt (EOI)
+ ioport_out(PIC1_COMMAND_PORT, 0x20);
+ unsigned char status = ioport_in(KEYBOARD_STATUS_PORT);
+
+ // Lowest bit of status will be set if buffer not empty
+ // (thanks mkeykernel)
+ if (status & 0x1)
+ {
+ char keycode = ioport_in(KEYBOARD_DATA_PORT);
+ if (keycode < 0 || (uint8_t)keycode >= 128) return;
+
+ if(keycode==14)
+ {
+ if(terminal_column) terminal_column--;
+ terminal_putchar(keyboard[keycode]);
+ terminal_column--;
+ return;
+ }
- for(size_t i=0;i<50;i++)
- {
+ terminal_putchar(keyboard[keycode]);
+
+ }
+}
+void print_message()
+{
+ for(size_t i=0;i<50;i++)
+ {
for(size_t j=0;j<i;j++) terminal_writestring("#");
terminal_writestring("Hello, kernel World!\n");
- }
+ }
+}
+
+void kernel_main(void)
+{
+ terminal_initialize();
+ print_message();
+ init_idt_table();
+ init_keyboard();
+ terminal_writestring("First part is working\n");
+ while(1);
}
diff --git a/src/keyboard.h b/src/keyboard.h
new file mode 100644
index 0000000..53846e6
--- /dev/null
+++ b/src/keyboard.h
@@ -0,0 +1,74 @@
+unsigned char keyboard[128] = {
+ // -------- 0 to 9 --------
+ ' ',
+ ' ', // escape key
+ '1','2','3','4','5','6','7','8',
+ // -------- 10 to 19 --------
+ '9','0','-','=',
+ ' ', // Backspace
+ ' ', // Tab
+ 'q','w','e','r',
+ // -------- 20 to 29 --------
+ 't','y','u','i','o','p','[',']',
+ '\n', // Enter
+ ' ', // left Ctrl
+ // -------- 30 to 39 --------
+ 'a','s','d','f','g','h','j','k','l',';',
+ // -------- 40 to 49 --------
+ ' ','`',
+ ' ', // left Shift
+ ' ','z','x','c','v','b','n',
+ // -------- 50 to 59 --------
+ 'm',',','.',
+ '/', // slash, or numpad slash if preceded by keycode 224
+ ' ', // right Shift
+ '*', // numpad asterisk
+ ' ', // left Alt
+ ' ', // Spacebar
+ ' ',
+ ' ', // F1
+ // -------- 60 to 69 --------
+ ' ', // F2
+ ' ', // F3
+ ' ', // F4
+ ' ', // F5
+ ' ', // F6
+ ' ', // F7
+ ' ', // F8
+ ' ', // F9
+ ' ', // F10
+ ' ',
+ // -------- 70 to 79 --------
+ ' ', // scroll lock
+ '7', // numpad 7, HOME key if preceded by keycode 224
+ '8', // numpad 8, up arrow if preceded by keycode 224
+ '9', // numpad 9, PAGE UP key if preceded by keycode 224
+ '-', // numpad hyphen
+ '4', // numpad 4, left arrow if preceded by keycode 224
+ '5', // numpad 5
+ '6', // numpad 6, right arrow if preceded by keycode 224
+ ' ',
+ '1', // numpad 1, END key if preceded by keycode 224
+ // -------- 80 to 89 --------
+ '2', // numpad 2, down arrow if preceded by keycode 224
+ '3', // numpad 3, PAGE DOWN key if preceded by keycode 224
+ '0', // numpad 0, INSERT key if preceded by keycode 224
+ '.', // numpad dot, DELETE key if preceded by keycode 224
+ ' ',' ',' ',' ',' ',' ',
+ // -------- 90 to 99 --------
+ ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+ // -------- 100 to 109 --------
+ ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+ // -------- 110 to 119 --------
+ ' ',' ',' ',' ',' ',' ',' ',' ',' ',' ',
+ // -------- 120-127 --------
+ ' ',' ',' ',' ',' ',' ',' ',' ',
+};
+// Right control, right alt seem to send
+// keycode 224, then the left control/alt keycode
+// Arrow keys also send two interrupts, one 224 and then their actual code
+// Same for numpad enter
+// 197: Num Lock
+// 157: Pause|Break (followed by 197?)
+// Clicking on screen appears to send keycodes 70, 198
+ // Is this the MARK command or something like that?