Add interrupts [its-broken]
This commit is contained in:
parent
6867765041
commit
b654976ebb
8 changed files with 215 additions and 13 deletions
6
Makefile
6
Makefile
|
@ -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
|
||||
|
|
|
@ -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
27
cpu/idt.c
Normal 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
23
cpu/idt.h
Normal 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
34
cpu/io.h
Normal 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
67
cpu/pic.c
Normal 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
31
cpu/pic.h
Normal 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
|
30
kernel.c
30
kernel.c
|
@ -2,23 +2,43 @@
|
|||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include "vga.h"
|
||||
#include "cpu/pic.h"
|
||||
#include "cpu/idt.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 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");
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue