Added most things needed for interrupts

This commit is contained in:
vanten-s 2024-07-16 22:51:41 +02:00
parent ba6f8a3498
commit fc95fb5ff6
Signed by: vanten-s
GPG key ID: DE3060396884D3F2
12 changed files with 293 additions and 67 deletions

View file

@ -1,23 +1,33 @@
ARCHDIR=arch/i686 ARCHDIR=arch/i686
CFLAGS?= CFLAGS?=
ASFLAGS?=
INCLUDEDIR=include INCLUDEDIR=include
CFLAGS:=-O2 \ CFLAGS:=\
-O2 \
-std=gnu99 \ -std=gnu99 \
-ffreestanding \ -ffreestanding \
-Wall -Wextra \ -Wall -Wextra \
-I$(INCLUDEDIR) \ -I$(INCLUDEDIR) \
$(CFLAGS) $(CFLAGS)
ASFLAGS:=\
-I$(INCLUDEDIR) \
-I$(ARCHDIR) \
$(ASFLAGS)
KERNEL_OBJS=\
kernel/kernel.o \
kernel/debugging.o \
KERNEL_OBJS=kernel/kernel.o
LIB_OBJS=\ LIB_OBJS=\
$(ARCHDIR)/bootstrap.o \ $(ARCHDIR)/bootstrap.o \
$(ARCHDIR)/tty.o\ $(ARCHDIR)/tty.o \
$(ARCHDIR)/strlib.o\ $(ARCHDIR)/strlib.o \
$(ARCHDIR)/interrupt.o\ $(ARCHDIR)/interrupt.o \
$(ARCHDIR)/gdt.o \
OBJS=$(KERNEL_OBJS) $(LIB_OBJS) OBJS=$(KERNEL_OBJS) $(LIB_OBJS)
@ -27,40 +37,37 @@ LDFLAGS=$(OBJS) \
-nostdlib \ -nostdlib \
-lgcc -lgcc
.PHONY: all clean myos.iso
.SUFFIXES: .c .o .s .SUFFIXES: .c .o .s
all: all: myos.iso
$(MAKE) run-grub-qemu
clean: clean:
rm -f $(OBJS) rm -f $(OBJS)
rm myos.bin rm -f myos.kernel
rm myos.iso rm -f myos.iso
rm -f isodir/boot/myos.kernel
debug:
mkdir build || true
cp Makefile build
cp -r isodir build
CFLAGS=-g $(MAKE) -C build debug-grub-qemu
.c.o: .c.o:
i686-elf-gcc -c $< -o $@ $(CFLAGS) i686-elf-gcc -c $< -o $@ $(CFLAGS)
.s.o: .s.o:
i686-elf-as $< -o $@ i686-elf-as $< -o $@ $(ASFLAGS)
myos.bin: $(OBJS) myos.kernel: $(OBJS)
i686-elf-gcc -T $(ARCHDIR)/linker.ld -o myos.bin $(LDFLAGS) i686-elf-gcc -T $(ARCHDIR)/linker.ld -o myos.kernel $(LDFLAGS)
grub-file --is-x86-multiboot myos.bin grub-file --is-x86-multiboot myos.kernel
run-grub-qemu: myos.bin myos.iso: myos.kernel
cp myos.bin isodir/boot/myos.bin cp myos.kernel isodir/boot/myos.kernel
grub-mkrescue -o myos.iso isodir grub-mkrescue -o myos.iso isodir
qemu-system-i386 myos.iso
debug-grub-qemu: myos.bin debug: CFLAGS += -g -O0
cp myos.bin isodir/boot/myos.bin debug: ASFLAGS += -g
grub-mkrescue -o myos.iso isodir debug: all
qemu-system-i386 -S -gdb tcp::9000 myos.iso
run: myos.iso
qemu-system-i386 myos.iso -display curses
run-debug: debug
qemu-system-i386 -S -gdb tcp::9000 myos.iso -display curses

View file

@ -65,6 +65,20 @@ _start:
*/ */
mov $stack_top, %esp mov $stack_top, %esp
cli
call gdt_init
movw %ax, 0x10
movw %ds, %ax
movw %es, %ax
movw %fs, %ax
movw %gs, %ax
movw %ss, %ax
# jmp kernel
ljmp $0x08, $kernel
kernel:
/* /*
This is a good place to initialize crucial processor state before the This is a good place to initialize crucial processor state before the
high-level kernel is entered. It's best to minimize the early high-level kernel is entered. It's best to minimize the early
@ -84,8 +98,15 @@ _start:
stack since (pushed 0 bytes so far), so the alignment has thus been stack since (pushed 0 bytes so far), so the alignment has thus been
preserved and the call is well defined. preserved and the call is well defined.
*/ */
mov %esp, %edx
call kernel_main call kernel_main
.global keyboard_test
keyboard_test:
hlt
/* /*
If the system has nothing more to do, put the computer into an If the system has nothing more to do, put the computer into an
infinite loop. To do that: infinite loop. To do that:
@ -106,4 +127,5 @@ _start:
Set the size of the _start symbol to the current location '.' minus its start. 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. This is useful when debugging or when you implement call tracing.
*/ */
.size _start, . - _start .size _start, . - _start

120
arch/i686/gdt.c Normal file
View file

@ -0,0 +1,120 @@
#include <kernel/tty.h>
#include <debugging.h>
#include <stdint.h>
#include <stddef.h>
#define NUM_ENTRIES 3
struct SegmentDescriptor {
uint32_t limit;
uint32_t base;
uint8_t access_byte;
uint8_t flags;
};
struct GDT {
uint16_t size;
uint64_t* base;
} __attribute__((packed));
static uint64_t entries[NUM_ENTRIES];
static size_t current_entry = 0;
struct GDT gdt;
static void encodeGdtEntry(uint8_t *target, struct SegmentDescriptor source)
{
// Check the limit to make sure that it can be encoded
if (source.limit > 0xFFFFF) {
terminal_writestring("WARNING: GDT entry has a limit (");
print_hex_bytes(&source.limit, 4);
terminal_writestring(") that is larger than accepted.");
return;
}
// Encode the limit
target[0] = source.limit & 0xFF;
target[1] = (source.limit >> 8) & 0xFF;
target[6] = (source.limit >> 16) & 0x0F;
// Encode the base
target[2] = source.base & 0xFF;
target[3] = (source.base >> 8) & 0xFF;
target[4] = (source.base >> 16) & 0xFF;
target[7] = (source.base >> 24) & 0xFF;
// Encode the access byte
target[5] = source.access_byte;
// Encode the flags
target[6] |= (source.flags << 4);
print_hex_bytes(target, 8);
}
static void add_entry(struct SegmentDescriptor entry)
{
if (current_entry == NUM_ENTRIES) {
terminal_writestring("WARNING: More GDT entries than NUM_ENTRIES.\n");
return;
}
uint64_t descriptor = 0;
encodeGdtEntry((uint8_t*) &descriptor, entry);
entries[current_entry] = descriptor;
current_entry++;
}
static void load_gdt()
{
gdt.size = NUM_ENTRIES * 8 - 1;
gdt.base = entries;
for (int i = 0; i < NUM_ENTRIES; i++) {
print_hex_bytes(gdt.base + i, 8);
terminal_putchar('\n');
}
asm ( "lgdt %0" : : "m"(gdt) );
}
void gdt_init()
{
terminal_initialize();
struct SegmentDescriptor null;
null.limit = 0;
null.base = 0;
null.access_byte = 0;
null.flags = 0;
terminal_writestring("Kernel Null: ");
add_entry(null);
terminal_putchar('\n');
struct SegmentDescriptor kernel_code;
kernel_code.limit = 0x000003FF;
kernel_code.base = 0x00000000;
kernel_code.access_byte = 0b10011010;
kernel_code.flags = 0b1100;
terminal_writestring("Kernel Code: ");
add_entry(kernel_code);
terminal_putchar('\n');
struct SegmentDescriptor kernel_data;
kernel_code.limit = 0x000003FF;
kernel_data.base = 0x00400000;
kernel_data.access_byte = 0b10010010;
kernel_data.flags = 0b1100;
terminal_writestring("Kernel Data: ");
add_entry(kernel_data);
terminal_putchar('\n');
load_gdt();
}

View file

@ -1,16 +1,88 @@
#include <kernel/tty.h> #include <kernel/tty.h>
#include <kernel/interrupt.h> #include <debugging.h>
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
const uint16_t NUM_ID = 0; #define NUM_ENTRIES 0
void load_idt(struct InterruptDescriptorTable *idt) struct InterruptDescriptor {
uint32_t offset;
uint32_t selector;
uint8_t gate_type;
uint8_t ring;
bool present;
};
struct IDT {
uint16_t size;
uint64_t* base;
} __attribute__((packed));
static uint64_t entries[NUM_ENTRIES];
static size_t current_entry = 0;
struct IDT idt;
static void encodeIdtEntry(uint8_t *target, struct InterruptDescriptor source)
{ {
asm("lidt %0" : : "m"(idt)); // Encode the present bit
if (source.present) {
target[5] |= 0xF0;
} else {
// Not present, return
return;
}
// Encode the offset
target[0] = (source.offset >> 0x00) & 0xFF;
target[1] = (source.offset >> 0x08) & 0xFF;
target[6] = (source.offset >> 0x10) & 0xFF;
target[7] = (source.offset >> 0x18) & 0xFF;
// Encode the base
target[2] = (source.selector >> 0x00) & 0xFF;
target[3] = (source.selector >> 0x08) & 0xFF;
// Encode the gate type
target[5] |= source.gate_type;
// Encode the DPL
target[5] |= (source.ring >> 5);
print_hex_bytes(target, 8);
} }
void interrupt_initialize(struct InterruptDescriptorTable IDT) static void add_entry(struct InterruptDescriptor entry)
{ {
terminal_writestring("Loading IDT\n"); if (current_entry == NUM_ENTRIES) {
load_idt(&IDT); terminal_writestring("WARNING: More IDT entries than NUM_ENTRIES.\n");
return;
}
uint64_t descriptor = 0;
encodeIdtEntry((uint8_t*) &descriptor, entry);
entries[current_entry] = descriptor;
current_entry++;
}
static void load_idt()
{
idt.size = NUM_ENTRIES * 8 - 1;
idt.base = entries;
for (int i = 0; i < NUM_ENTRIES; i++) {
print_hex_bytes(idt.base + i, 8);
terminal_putchar('\n');
}
asm ( "lidt %0" : : "m"(idt) );
}
void idt_init()
{
terminal_writestring("TODO: Add interrupts");
terminal_putchar('\n');
// load_idt();
} }

View file

@ -22,14 +22,14 @@ SECTIONS
/* First put the multiboot header, as it is required to be put very early /* First put the multiboot header, as it is required to be put very early
in the image or the bootloader won't recognize the file format. in the image or the bootloader won't recognize the file format.
Next we'll put the .text section. */ Next we'll put the .text section. */
.text BLOCK(4K) : ALIGN(4K) .text BLOCK (4K) : ALIGN(4K)
{ {
*(.multiboot) *(.multiboot)
*(.text) *(.text)
} }
/* Read-only data. */ /* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K) .rodata BLOCK(4K) : ALIGN(4M)
{ {
*(.rodata) *(.rodata)
} }

View file

@ -3,12 +3,6 @@
#include <stdint.h> #include <stdint.h>
#include <strlib.h> #include <strlib.h>
/* Check if the compiler thinks you are targeting the wrong operating system. */
#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif
/* This tutorial will only work for the 32-bit ix86 targets. */
#if !defined(__i386__) #if !defined(__i386__)
#error "This tutorial needs to be compiled with a ix86-elf compiler" #error "This tutorial needs to be compiled with a ix86-elf compiler"
#endif #endif

8
include/debugging.h Normal file
View file

@ -0,0 +1,8 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
char get_last_key_pressed();
void print_hex_digit(uint8_t digit);
void print_hex_byte(uint8_t byte);
void print_hex_bytes(void* bytes, size_t len);

View file

@ -1,14 +1,4 @@
#include <stdint.h> #include <stdint.h>
#include <stdbool.h>
struct InterruptDescriptor void idt_init();
{
};
struct InterruptDescriptorTable
{
uint16_t limit; /* Size of IDT array - 1 */
struct InterruptDescriptor* base; /* Pointer to IDT array */
} __attribute__((packed));
void interrupt_initialize(struct InterruptDescriptorTable IDT);

View file

@ -1,3 +1,3 @@
menuentry "myos" { menuentry "myos" {
multiboot /boot/myos.bin multiboot /boot/myos.kernel
} }

Binary file not shown.

View file

@ -13,9 +13,10 @@ static inline uint8_t inb(uint16_t port)
return ret; return ret;
} }
bool is_in_shift; static bool is_in_shift;
static unsigned char keyboard_char(unsigned char scancode) { static unsigned char keyboard_char(unsigned char scancode)
{
if (0x01 < scancode && scancode < 0x0B) { if (0x01 < scancode && scancode < 0x0B) {
return scancode - 2 + '1'; return scancode - 2 + '1';
} }
@ -118,7 +119,8 @@ static unsigned char keyboard_char(unsigned char scancode) {
return -1; return -1;
} }
char get_last_key_pressed() { char get_last_key_pressed()
{
char scancode = inb(0x60); char scancode = inb(0x60);
if (is_in_shift) { if (is_in_shift) {
@ -128,7 +130,8 @@ char get_last_key_pressed() {
return to_lower_char(keyboard_char(scancode)); return to_lower_char(keyboard_char(scancode));
} }
void print_hex_digit(uint8_t digit) { void print_hex_digit(uint8_t digit)
{
digit = digit & 0xf; digit = digit & 0xf;
if (digit < 0xA) { if (digit < 0xA) {
terminal_putchar(digit + 0x30); terminal_putchar(digit + 0x30);
@ -137,10 +140,19 @@ void print_hex_digit(uint8_t digit) {
} }
} }
void print_hex_byte(uint8_t byte) { void print_hex_byte(uint8_t byte)
{
int upper = byte >> 4; int upper = byte >> 4;
int lower = byte & 0x0F; int lower = byte & 0x0F;
print_hex_digit(upper); print_hex_digit(upper);
print_hex_digit(lower); print_hex_digit(lower);
} }
void print_hex_bytes(void* bytes, size_t len)
{
uint8_t* value = bytes;
for (size_t i = len; i > 0; i--) {
print_hex_byte(value[i - 1]);
}
}

View file

@ -1,12 +1,20 @@
#include <kernel/tty.h> #include <kernel/tty.h>
#include <kernel/interrupt.h> #include <kernel/interrupt.h>
#include <debugging.c> #include <debugging.h>
/* Check if the compiler thinks you are targeting the wrong operating system. */
#if defined(__linux__)
#error "You are not using a cross-compiler, you will most certainly run into trouble"
#endif
void kernel_main(void) void kernel_main(void)
{ {
/* Initialize terminal interface */ /* Initialize terminal interface */
terminal_initialize(); terminal_initialize();
// TODO: Add interrupts
// idt_init();
terminal_putchar(get_last_key_pressed()); terminal_putchar(get_last_key_pressed());
char current_character = get_last_key_pressed(); char current_character = get_last_key_pressed();
@ -17,11 +25,4 @@ void kernel_main(void)
} }
current_character = tmp; current_character = tmp;
} }
struct InterruptDescriptorTable IDTR = {
};
interrupt_initialize(IDTR);
terminal_writestring("Hello World!");
} }