Add interrupts [its-broken]

This commit is contained in:
hippoz 2022-02-22 21:20:57 +02:00
parent 6867765041
commit b654976ebb
Signed by: hippoz
GPG key ID: 7C52899193467641
8 changed files with 215 additions and 13 deletions

View file

@ -3,13 +3,13 @@ CC=i686-elf-gcc
.PHONY: iso run clean
%.o: %.c
$(CC) -c $< -o $@ -std=gnu99 -ffreestanding -O2 -Wall -Wextra -pedantic -I.
$(CC) -c $< -o $@ -std=gnu99 -mgeneral-regs-only -ffreestanding -O2 -Wall -Wextra -pedantic -I.
bootstrap.o: bootstrap.s
i686-elf-as bootstrap.s -o bootstrap.o
kernel.bin: bootstrap.o kernel.o vga.o std/std.o
$(CC) -T linker.ld -o kernel.bin -ffreestanding -O2 -nostdlib bootstrap.o kernel.o vga.o std/std.o -lgcc
kernel.bin: bootstrap.o vga.o std/std.o cpu/idt.o cpu/pic.o kernel.o
$(CC) -T linker.ld -o $@ -ffreestanding -O2 -nostdlib -lgcc $^
iso: kernel.bin
mkdir -p isodir/boot/grub

View file

@ -100,7 +100,7 @@ _start:
stack since (pushed 0 bytes so far), so the alignment has thus been
preserved and the call is well defined.
*/
call kernel_main
call kmain
/*
If the system has nothing more to do, put the computer into an
@ -108,7 +108,7 @@ _start:
1) Disable interrupts with cli (clear interrupt enable in eflags).
They are already disabled by the bootloader, so this is not needed.
Mind that you might later enable interrupts and return from
kernel_main (which is sort of nonsensical to do).
kmain (which is sort of nonsensical to do).
2) Wait for the next interrupt to arrive with hlt (halt instruction).
Since they are disabled, this will lock up the computer.
3) Jump to the hlt instruction if it ever wakes up due to a

27
cpu/idt.c Normal file
View file

@ -0,0 +1,27 @@
#include <stdbool.h>
#include <stddef.h>
#include "cpu/idt.h"
struct interrupt_descriptor_32 idt[256];
static size_t idt_ptr[2];
void idt_init() {
size_t idt_address = (size_t)idt;
idt_ptr[0] = (sizeof (struct interrupt_descriptor_32) * 256) + ((idt_address & 0xffff) << 16);
idt_ptr[1] = idt_address >> 16;
__asm__ __volatile__(
"lidt %0\n\t"
"sti\n\t" : : "m"(idt_ptr)
);
}
void idt_register_handler(uint8_t interrupt, size_t address) {
idt[interrupt].offset_1 = address & 0xffff;
idt[interrupt].selector = KERNEL_CODE_SEGMENT_OFFSET;
idt[interrupt].zero = 0;
idt[interrupt].type_attributes = INTERRUPT_GATE;
idt[interrupt].offset_2 = (address & 0xffff0000) >> 16;
}

23
cpu/idt.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef _IDT_H
#define _IDT_H
#include <stddef.h>
#include <stdint.h>
#define KERNEL_CODE_SEGMENT_OFFSET 0x08
#define INTERRUPT_GATE 0x8e
// https://wiki.osdev.org/Interrupt_Descriptor_Table
struct __attribute__((__packed__)) interrupt_descriptor_32 {
uint16_t offset_1; // offset bits 0..15
uint16_t selector; // a code segment selector in GDT or LDT
uint8_t zero; // unused, set to 0
uint8_t type_attributes; // gate type, dpl, and p fields
uint16_t offset_2; // offset bits 16..31
};
void idt_init();
void idt_register_handler(uint8_t interrupt, size_t address);
#endif

34
cpu/io.h Normal file
View file

@ -0,0 +1,34 @@
static inline void outb(const uint16_t port, const uint8_t val) {
asm volatile("outb %0, %1" : : "a" (val), "Nd" (port));
}
static inline void outw(const uint16_t port, const uint16_t val) {
asm volatile("outw %0, %1" : : "a" (val), "Nd" (port));
}
static inline void outl(const uint16_t port, const uint32_t val) {
asm volatile("outl %0, %1" : : "a" (val), "Nd" (port));
}
static inline uint8_t inb(const uint16_t port) {
volatile uint8_t ret;
asm volatile("inb %1, %0" : "=a" (ret) : "Nd" (port));
return ret;
}
static inline uint16_t inw(const uint16_t port) {
volatile uint16_t ret;
asm volatile("inw %1, %0" : "=a" (ret) : "Nd" (port));
return ret;
}
static inline uint32_t inl(const uint16_t port) {
volatile uint32_t ret;
asm volatile("inl %1, %0" : "=a" (ret) : "Nd" (port));
return ret;
}
static inline void io_wait() {
outb(0x80, 0);
}

67
cpu/pic.c Normal file
View file

@ -0,0 +1,67 @@
#include "cpu/pic.h"
#include "cpu/io.h"
void pic_remap(uint8_t offset1, uint8_t offset2) {
uint8_t a1, a2;
a1 = inb(PIC1_DATA); // save masks
a2 = inb(PIC2_DATA);
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4); // starts the initialization sequence (in cascade mode)
io_wait();
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
io_wait();
outb(PIC1_DATA, offset1); // ICW2: Master PIC vector offset
io_wait();
outb(PIC2_DATA, offset2); // ICW2: Slave PIC vector offset
io_wait();
outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100)
io_wait();
outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity (0000 0010)
io_wait();
outb(PIC1_DATA, ICW4_8086);
io_wait();
outb(PIC2_DATA, ICW4_8086);
io_wait();
outb(PIC1_DATA, a1); // restore saved masks.
outb(PIC2_DATA, a2);
}
void pic_send_eoi(uint8_t irq)
{
if(irq >= 8)
outb(PIC2_COMMAND,PIC_EOI);
outb(PIC1_COMMAND,PIC_EOI);
}
void pic_irq_set_mask(uint8_t IRQline) {
uint16_t port;
uint8_t value;
if(IRQline < 8) {
port = PIC1_DATA;
} else {
port = PIC2_DATA;
IRQline -= 8;
}
value = inb(port) | (1 << IRQline);
outb(port, value);
}
void pic_irq_clear_mask(uint8_t IRQline) {
uint16_t port;
uint8_t value;
if(IRQline < 8) {
port = PIC1_DATA;
} else {
port = PIC2_DATA;
IRQline -= 8;
}
value = inb(port) & ~(1 << IRQline);
outb(port, value);
}

31
cpu/pic.h Normal file
View file

@ -0,0 +1,31 @@
#ifndef _PIC_H
#define _PIC_H
#include <stdint.h>
#include <stddef.h>
#define PIC1 0x20 /* IO base address for master PIC */
#define PIC2 0xA0 /* IO base address for slave PIC */
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1+1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2+1)
#define PIC_EOI 0x20 /* End-of-interrupt command code */
#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 pic_send_eoi(uint8_t irq);
void pic_remap(uint8_t offset1, uint8_t offset2);
void pic_irq_set_mask(uint8_t IRQline);
void pic_irq_clear_mask(uint8_t IRQline);
#endif

View file

@ -2,23 +2,43 @@
#include <stddef.h>
#include <stdint.h>
#include "vga.h"
/* Check if the compiler thinks you are targeting the wrong operating system. */
#include "cpu/pic.h"
#include "cpu/idt.h"
#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 OS needs to be compiled with a ix86-elf compiler"
#endif
void kernel_main(void)
struct interrupt_frame
{
/* Initialize terminal interface */
size_t ip;
size_t cs;
size_t flags;
size_t sp;
size_t ss;
} __attribute__((packed));
__attribute__((interrupt))
static void isr0(struct interrupt_frame *frame) {
terminal_writestring("division by zero");
pic_send_eoi(0);
}
void kmain(void) {
terminal_initialize();
/* Newline support is left as an exercise. */
terminal_writestring("start");
pic_remap(0x20, 0x28);
idt_register_handler(0, (size_t)isr0);
idt_init();
volatile int a = 0/0;
terminal_writestring("Hello, kernel World!\n");
}