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
CFLAGS?=
ASFLAGS?=
INCLUDEDIR=include
CFLAGS:=-O2 \
CFLAGS:=\
-O2 \
-std=gnu99 \
-ffreestanding \
-Wall -Wextra \
-I$(INCLUDEDIR) \
$(CFLAGS)
ASFLAGS:=\
-I$(INCLUDEDIR) \
-I$(ARCHDIR) \
$(ASFLAGS)
KERNEL_OBJS=\
kernel/kernel.o \
kernel/debugging.o \
KERNEL_OBJS=kernel/kernel.o
LIB_OBJS=\
$(ARCHDIR)/bootstrap.o \
$(ARCHDIR)/tty.o\
$(ARCHDIR)/strlib.o\
$(ARCHDIR)/interrupt.o\
$(ARCHDIR)/tty.o \
$(ARCHDIR)/strlib.o \
$(ARCHDIR)/interrupt.o \
$(ARCHDIR)/gdt.o \
OBJS=$(KERNEL_OBJS) $(LIB_OBJS)
@ -27,40 +37,37 @@ LDFLAGS=$(OBJS) \
-nostdlib \
-lgcc
.PHONY: all clean myos.iso
.SUFFIXES: .c .o .s
all:
$(MAKE) run-grub-qemu
all: myos.iso
clean:
rm -f $(OBJS)
rm myos.bin
rm myos.iso
debug:
mkdir build || true
cp Makefile build
cp -r isodir build
CFLAGS=-g $(MAKE) -C build debug-grub-qemu
rm -f myos.kernel
rm -f myos.iso
rm -f isodir/boot/myos.kernel
.c.o:
i686-elf-gcc -c $< -o $@ $(CFLAGS)
.s.o:
i686-elf-as $< -o $@
i686-elf-as $< -o $@ $(ASFLAGS)
myos.bin: $(OBJS)
i686-elf-gcc -T $(ARCHDIR)/linker.ld -o myos.bin $(LDFLAGS)
grub-file --is-x86-multiboot myos.bin
myos.kernel: $(OBJS)
i686-elf-gcc -T $(ARCHDIR)/linker.ld -o myos.kernel $(LDFLAGS)
grub-file --is-x86-multiboot myos.kernel
run-grub-qemu: myos.bin
cp myos.bin isodir/boot/myos.bin
myos.iso: myos.kernel
cp myos.kernel isodir/boot/myos.kernel
grub-mkrescue -o myos.iso isodir
qemu-system-i386 myos.iso
debug-grub-qemu: myos.bin
cp myos.bin isodir/boot/myos.bin
grub-mkrescue -o myos.iso isodir
qemu-system-i386 -S -gdb tcp::9000 myos.iso
debug: CFLAGS += -g -O0
debug: ASFLAGS += -g
debug: all
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
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
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
preserved and the call is well defined.
*/
mov %esp, %edx
call kernel_main
.global keyboard_test
keyboard_test:
hlt
/*
If the system has nothing more to do, put the computer into an
infinite loop. To do that:
@ -106,4 +127,5 @@ _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.
*/
.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/interrupt.h>
#include <debugging.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");
load_idt(&IDT);
if (current_entry == NUM_ENTRIES) {
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
in the image or the bootloader won't recognize the file format.
Next we'll put the .text section. */
.text BLOCK(4K) : ALIGN(4K)
.text BLOCK (4K) : ALIGN(4K)
{
*(.multiboot)
*(.text)
}
/* Read-only data. */
.rodata BLOCK(4K) : ALIGN(4K)
.rodata BLOCK(4K) : ALIGN(4M)
{
*(.rodata)
}

View file

@ -3,12 +3,6 @@
#include <stdint.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__)
#error "This tutorial needs to be compiled with a ix86-elf compiler"
#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 <stdbool.h>
struct InterruptDescriptor
{
};
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);
void idt_init();

View file

@ -1,3 +1,3 @@
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;
}
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) {
return scancode - 2 + '1';
}
@ -118,7 +119,8 @@ static unsigned char keyboard_char(unsigned char scancode) {
return -1;
}
char get_last_key_pressed() {
char get_last_key_pressed()
{
char scancode = inb(0x60);
if (is_in_shift) {
@ -128,7 +130,8 @@ char get_last_key_pressed() {
return to_lower_char(keyboard_char(scancode));
}
void print_hex_digit(uint8_t digit) {
void print_hex_digit(uint8_t digit)
{
digit = digit & 0xf;
if (digit < 0xA) {
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 lower = byte & 0x0F;
print_hex_digit(upper);
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/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)
{
/* Initialize terminal interface */
terminal_initialize();
// TODO: Add interrupts
// idt_init();
terminal_putchar(get_last_key_pressed());
char current_character = get_last_key_pressed();
@ -17,11 +25,4 @@ void kernel_main(void)
}
current_character = tmp;
}
struct InterruptDescriptorTable IDTR = {
};
interrupt_initialize(IDTR);
terminal_writestring("Hello World!");
}