Skip to content

Instantly share code, notes, and snippets.

@FedorLap2006
Last active August 27, 2023 04:18
Show Gist options
  • Save FedorLap2006/0ba8dd66a60f332460246217fbe63f39 to your computer and use it in GitHub Desktop.
Save FedorLap2006/0ba8dd66a60f332460246217fbe63f39 to your computer and use it in GitHub Desktop.
x86 customizable PIC handler
#include "pic.h" // Replace with an actual header
#define PIC_PRIMARY_CMD_PORT 0x20
#define PIC_SECONDARY_CMD_PORT 0x20
#define PIC_PRIMARY_DATA_PORT 0x21
#define PIC_SECONDARY_DATA_PORT 0xA1
static void pic_iowait() { __asm__ volatile("outb %%al, $0x80" : : "a"(0)); }
struct pic_config current_pic_config;
void pic_init(struct pic_config cfg) {
uint8_t primary_mask, secondary_mask;
/**
* Saving old IRQs mask
*/
if (cfg.dirty_mask) {
primary_mask = inb(PIC_PRIMARY_DATA_PORT) | (~(cfg.mask) & 0xFF);
secondary_mask = inb(PIC_SECONDARY_DATA_PORT) | (~(cfg.mask) >> 0x8);
} else {
primary_mask = ~(cfg.mask & 0xFF);
secondary_mask = ~(cfg.mask >> 0x8);
}
/**
* Remaping interrupts table
*/
outb(PIC_PRIMARY_CMD_PORT,
0x11); /* Initialisation signal + ICW4 mode [primary] */
if (cfg.iowait_mode)
pic_iowait();
outb(PIC_SECONDARY_CMD_PORT,
0x11); /* Initialisation signal + ICW4 mode [secondary] */
if (cfg.iowait_mode)
pic_iowait();
outb(PIC_PRIMARY_DATA_PORT,
cfg.primary_offset); /* Interrupts offset [primary] */
if (cfg.iowait_mode)
pic_iowait();
outb(PIC_SECONDARY_DATA_PORT,
cfg.secondary_offset); /* Interrupts offset [secondary] */
if (cfg.iowait_mode)
pic_iowait();
outb(PIC_PRIMARY_DATA_PORT,
cfg.sconn_irq_line); /* IRQ lines where secondary controller is
connected [primary] */
if (cfg.iowait_mode)
pic_iowait();
outb(PIC_SECONDARY_DATA_PORT,
cfg.sec_irq_line_num); /* IRQ line number where secondary
controller is connected [secondary] */
if (cfg.iowait_mode)
pic_iowait();
outb(PIC_PRIMARY_DATA_PORT,
(uint8_t)(0 | ((cfg.mp_mode & 0x1) << 0) | ((cfg.auto_eoi & 0x1) << 1) |
((cfg.bufmode & 0x1) << 2) | ((cfg.ps_bufmode & 0x1) << 3) |
((cfg.sfn_mode & 0x1) << 4)));
if (cfg.iowait_mode)
pic_iowait();
outb(PIC_SECONDARY_DATA_PORT,
(uint8_t)(0 | ((cfg.mp_mode & 0x1) << 0) | ((cfg.auto_eoi & 0x1) << 1) |
((cfg.bufmode & 0x1) << 2) | ((cfg.ps_bufmode & 0x1) << 3) |
((cfg.sfn_mode & 0x1) << 4)));
if (cfg.iowait_mode)
pic_iowait();
outb(PIC_PRIMARY_DATA_PORT, primary_mask);
if (cfg.iowait_mode)
pic_iowait();
outb(PIC_SECONDARY_DATA_PORT, secondary_mask);
if (cfg.iowait_mode)
pic_iowait();
current_pic_config = cfg;
}
void pic_eoi(uint32_t int_num) {
if (current_pic_config.auto_eoi)
return;
if (int_num >= 40) {
outb(0xA0, 0x20);
}
outb(0x20, 0x20);
}
struct pic_config default_pic_config = {
.dirty_mask = false,
.iowait_mode = true,
.sfn_mode = false,
.bufmode = false,
.ps_bufmode = false,
.auto_eoi = false,
.mp_mode = true,
.primary_offset = 0x20,
.secondary_offset = 0x28,
.sec_irq_line_num = 0x2,
.sconn_irq_line = 0x4,
.mask = (uint16_t)IRQCALL,
};
#pragma once
#include <types.h> // Replace with an actual header
typedef struct pic_config {
/**
* Dirty old mask flag
*
* If set new mask will contain result of an OR operation between old and new masks.
* Otherwise, old mask will be cleared and will be replaced by new one.
*/
bool dirty_mask;
/**
* IOwait mode flag
*
* Whether to use iowait mode after every PIC command execution
* NOTE: It must be set to true on an old processor.
*/
bool iowait_mode;
/**
* Special Fully Nested Mode (SFNM) flag
*
* NOTE: It is recommended to leave it unset.
*/
bool sfn_mode;
/**
* Buffered mode
*
* NOTE: It must be set to false for the Intel ICH4
*/
bool bufmode;
/**
* Primary-secondary (master-slave) buffered mode
*
* NOTE: Should always be set to false on the Intel architecture
*/
bool ps_bufmode;
/**
* Auto EOI
*
* Whether to automatically send EOI after the interrupt finishes execution.
*/
bool auto_eoi;
/**
* Microprocessor mode
* NOTE: Must be be set to true on the Intel architecture.
*/
bool mp_mode;
/**
* Offset of the interrupts in the IDT which would controlled by the primary controller
*
* NOTE: Recommended set it to 0x20 on the Intel architecture.
*/
uint8_t primary_offset;
/**
* Offset of the interrupts in the IDT which would controlled by the secondary controller
*
* NOTE: Recommended set it to 0x28
*/
uint8_t secondary_offset;
/**
* IRQ line number on which the secondary controller is connected
*
* NOTE: Must be set to 0x2 on the PC.
*/
uint8_t sec_irq_line_num;
/**
* IRQ lines on which the secondary controller(s) is connected
*
* NOTE: Must be set to 0x4 on the PC.
*/
uint8_t sconn_irq_line;
/**
* Bitmask of enabled IRQ lines
*/
uint16_t mask;
} pic_config_t;
#define IRQC0 (1 << 0)
#define IRQC1 (1 << 1)
#define IRQC2 (1 << 2)
#define IRQC3 (1 << 3)
#define IRQC4 (1 << 4)
#define IRQC5 (1 << 5)
#define IRQC6 (1 << 6)
#define IRQC7 (1 << 7)
#define IRQC8 (1 << 8)
#define IRQC9 (1 << 9)
#define IRQC10 (1 << 10)
#define IRQC11 (1 << 11)
#define IRQC12 (1 << 12)
#define IRQC13 (1 << 13)
#define IRQC14 (1 << 14)
#define IRQC15 (1 << 15)
#define IRQC16 (1 << 16)
#define IRQCALL 0xFFFF // Enable all
#define IRQCDALL 0x0000 // Disable all
void pic_init(struct pic_config cfg);
void pic_eoi(uint32_t int_num);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment