I'm currently developing an operating system that uses the limine bootloader and follows the limine boot protocol. However, I've encountered an issue: when I call init_PIC()
, the CPU resets immediately on the first line inside PIC_remap()
. I suspect this might be due to a fatal error during PIC remapping.
Below is the relevant code:
pic.c
#include "pic.h"
#include "../drivers/ports.h"
#define PIC1 0x20 // master PIC
#define PIC2 0xA0 // slave PIC
#define PIC1_COMMAND PIC1
#define PIC1_DATA (PIC1 + 1)
#define PIC2_COMMAND PIC2
#define PIC2_DATA (PIC2 + 1)
#define ICW1_ICW4 0x01
#define ICW1_SINGLE 0x02
#define ICW1_INTERVAL4 0x04
#define ICW1_LEVEL 0x08
#define ICW1_INIT 0x10
#define ICW4_8086 0x01
#define ICW4_AUTO 0x02
#define ICW4_BUF_SLAVE 0x08
#define ICW4_BUF_MASTER 0x0C
#define ICW4_SFNM 0x10
static inline void io_wait(void) { __asm__ __volatile__("outb %%al, $0x80" : : "a"(0)); }
static void PIC_remap(int master_off, int slave_off) {
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4);
io_wait();
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
io_wait();
outb(PIC1_DATA, master_off); // ICW2: Master PIC
io_wait();
outb(PIC2_DATA, slave_off); // ICW2: Slave PIC
io_wait();
outb(PIC1_DATA, 4); // ICW3: tell Master PIC that there is a slave PIC at IRQ2
io_wait();
outb(PIC2_DATA, 2); // ICW3: tell Slave PIC its cascade identity
io_wait();
outb(PIC1_DATA, ICW4_8086); // ICW4: have the PICs use 8086 mode
io_wait();
outb(PIC2_DATA, ICW4_8086);
io_wait();
// Unmask both PICs
outb(PIC1_DATA, 0);
outb(PIC2_DATA, 0);
}
void init_PIC(void) {
__asm__ __volatile__("cli"); // disable interrupts
PIC_remap(0x20, 0x28);
__asm__ __volatile__("sti"); // enable interrupts
}
ports.c
(driver for port I/O)
#include "ports.h"
#include <stdint.h>
inline uint8_t inb(uint16_t port) {
uint8_t ret;
__asm__ __volatile__("inb %w1, %b0" : "=a"(ret) : "Nd"(port) : "memory");
return ret;
}
inline void outb(uint16_t port, uint8_t val) {
__asm__ __volatile__("outb %b0, %w1" : : "a"(val), "Nd"(port) : "memory");
}
kmain.c
#include <stddef.h>
#include "drivers/screen.h"
#include "interrupts/idt.h"
#include "interrupts/pic.h"
#include "limine.h"
#include "misc.h"
__attribute__((used,
section(".limine_requests_start"))) static volatile LIMINE_REQUESTS_START_MARKER;
__attribute__((used, section(".limine_requests_end"))) static volatile LIMINE_REQUESTS_END_MARKER;
__attribute__((used, section(".limine_requests"))) static volatile LIMINE_BASE_REVISION(3);
__attribute__((used, section(".limine_requests"))) static volatile struct limine_framebuffer_request
framebuffer_request = {.id = LIMINE_FRAMEBUFFER_REQUEST, .revision = 0};
void kmain(void) {
if (!LIMINE_BASE_REVISION_SUPPORTED) {
stop();
}
// don't support multiple framebuffers
if (!framebuffer_request.response || framebuffer_request.response->framebuffer_count > 1) {
stop();
}
init_screen(framebuffer_request.response->framebuffers[0]);
set_font_scale(2);
init_idt();
init_PIC();
stop();
}
Additional Details
- The reset occurs immediately when entering
PIC_remap()
, specifically on the firstoutb
call. - Interrupts are disabled before calling
PIC_remap()
, so that shouldn't be the issue.
For an MCVE, please refer to the following GitHub repo.