Skip to content

Commit

Permalink
ioapic: add IOAPIC device detection and handling
Browse files Browse the repository at this point in the history
Detect all system's IOAPIC devices as advertised by ACPI (MADT) and MP
tables. Up to 16 IOAPIC devices is supported (per ID addressing
range).

Add interface to access MMIO registers of IOAPIC, configure
redirection table entries, mask/unmask IRQs and configure delivery
mode for ISA IRQs.

Signed-off-by: Pawel Wieczorkiewicz <[email protected]>
  • Loading branch information
wipawel committed Oct 22, 2020
1 parent 252be82 commit 9e77b77
Show file tree
Hide file tree
Showing 5 changed files with 265 additions and 0 deletions.
145 changes: 145 additions & 0 deletions arch/x86/ioapic.c
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@
/* MP specification defines bus name as array of 6 characters filled up with blanks */
#define IOAPIC_SYSTEM_BUS_NAME_SIZE 6

static ioapic_t ioapics[MAX_IOAPICS];
static unsigned nr_ioapics;

static list_head_t bus_list = LIST_INIT(bus_list);

static int __get_system_bus_name(uint8_t bus_name[IOAPIC_SYSTEM_BUS_NAME_SIZE],
Expand Down Expand Up @@ -164,3 +167,145 @@ irq_override_t *get_system_pci_bus_irq(uint8_t irq_type, uint32_t irq_src) {
return __get_irq_override(bus, irq_type, irq_src);
}

void init_ioapic(void) {
printk("Initializing IOAPICs\n");

for (unsigned i = 0; i < nr_ioapics; i++) {
ioapic_t *ioapic = &ioapics[i];
ioapic_version_t version;
ioapic_id_t id;

id.reg = ioapic_read32(ioapic, IOAPIC_ID);
if (ioapic->id != id.apic_id) {
panic("IOAPIC with unexpected APIC ID detected: 0x%02x (expected: "
"0x%02x)\n",
id.apic_id, ioapic->id);
}

version.reg = ioapic_read32(ioapic, IOAPIC_VERSION);
ioapic->version = version.version;
ioapic->nr_entries = version.max_redir_entry + 1;

for (unsigned irq = 0; irq < ioapic->nr_entries; irq++)
set_ioapic_irq_mask(ioapic, irq, IOAPIC_INT_MASK);
}
}

ioapic_t *get_ioapic(uint8_t id) {
for (unsigned i = 0; i < nr_ioapics; i++) {
if (ioapics[i].id == id)
return &ioapics[i];
}

return NULL;
}

ioapic_t *add_ioapic(uint8_t id, uint8_t version, bool enabled, uint64_t base_address,
uint32_t gsi_base) {
ioapic_t *ioapic;

ioapic = get_ioapic(id);
if (!ioapic) {
BUG_ON(nr_ioapics > MAX_IOAPICS);
ioapic = &ioapics[nr_ioapics++];
}

ioapic->id = id;
ioapic->version = version;
ioapic->enabled = enabled;
ioapic->base_address = base_address;
ioapic->gsi_base = gsi_base;

ioapic->virt_address =
vmap(paddr_to_virt(ioapic->base_address), paddr_to_mfn(ioapic->base_address),
PAGE_ORDER_4K, L1_PROT);
BUG_ON(!ioapic->virt_address);

return ioapic;
}

int get_ioapic_redirtbl_entry(ioapic_t *ioapic, unsigned n,
ioapic_redirtbl_entry_t *entry) {
ASSERT(ioapic && entry);

if (n >= ioapic->nr_entries)
return -EINVAL;

entry->low = ioapic_read32(ioapic, IOAPIC_REDIRTBL(n));
entry->high = ioapic_read32(ioapic, IOAPIC_REDIRTBL(n) + 1);

return 0;
}

int set_ioapic_redirtbl_entry(ioapic_t *ioapic, unsigned n,
ioapic_redirtbl_entry_t *entry) {
ASSERT(ioapic && entry);

if (n >= ioapic->nr_entries)
return -EINVAL;

ioapic_write32(ioapic, IOAPIC_REDIRTBL(n), entry->low);
ioapic_write32(ioapic, IOAPIC_REDIRTBL(n) + 1, entry->high);

return 0;
}

void set_ioapic_irq_mask(ioapic_t *ioapic, unsigned irq, ioapic_int_mask_t mask) {
ioapic_redirtbl_entry_t entry;

get_ioapic_redirtbl_entry(ioapic, irq, &entry);
entry.int_mask = mask;
set_ioapic_redirtbl_entry(ioapic, irq, &entry);
}

static inline bool is_ioapic_irq(ioapic_t *ioapic, uint32_t irq_src) {
return irq_src >= ioapic->gsi_base && irq_src < ioapic->gsi_base + ioapic->nr_entries;
}

static ioapic_t *find_ioapic_for_irq(uint32_t irq_src) {
for (unsigned i = 0; i < nr_ioapics; i++) {
ioapic_t *ioapic = &ioapics[i];

if (is_ioapic_irq(ioapic, irq_src))
return ioapic;
}

return NULL;
}

void configure_isa_irq(unsigned irq_src, uint8_t vector, ioapic_dest_mode_t dst_mode,
uint8_t dst_ids) {
irq_override_t *irq_override = get_system_isa_bus_irq(IOAPIC_IRQ_TYPE_INT, irq_src);
ioapic_redirtbl_entry_t entry;
ioapic_polarity_t polarity = IOAPIC_POLARITY_AH;
ioapic_trigger_mode_t trigger_mode = IOAPIC_TRIGGER_MODE_EDGE;
ioapic_t *ioapic;

if (irq_override) {
irq_src = irq_override->dst;

if (irq_override->dst_id == IOAPIC_DEST_ID_UNKNOWN)
ioapic = find_ioapic_for_irq(irq_src);
else
ioapic = get_ioapic(irq_override->dst_id);

if (irq_override->polarity == IOAPIC_IRQ_OVR_POLARITY_AL)
polarity = IOAPIC_POLARITY_AL;

if (irq_override->trigger_mode == IOAPIC_IRQ_OVR_TRIGGER_LT)
trigger_mode = IOAPIC_TRIGGER_MODE_LEVEL;
}
else {
ioapic = find_ioapic_for_irq(irq_src);
}

get_ioapic_redirtbl_entry(ioapic, irq_src, &entry);
entry.vector = vector;
entry.deliv_mode = IOAPIC_DELIVERY_MODE_FIXED;
entry.dest_mode = dst_mode;
entry.polarity = polarity;
entry.trigger_mode = trigger_mode;
entry.destination = dst_ids;
entry.int_mask = IOAPIC_INT_UNMASK;
set_ioapic_redirtbl_entry(ioapic, irq_src, &entry);
}
6 changes: 6 additions & 0 deletions common/acpi.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,9 @@ static int process_madt_entries(void) {
case ACPI_MADT_TYPE_IOAPIC: {
acpi_madt_ioapic_t *madt_ioapic = (acpi_madt_ioapic_t *) entry->data;

add_ioapic(madt_ioapic->ioapic_id, 0x00, true, madt_ioapic->base_address,
madt_ioapic->gsi_base);

printk("ACPI: [MADT] IOAPIC ID: %u, Base Address: 0x%08x, GSI Base: 0x%08x\n",
madt_ioapic->ioapic_id, madt_ioapic->base_address,
madt_ioapic->gsi_base);
Expand Down Expand Up @@ -337,6 +340,9 @@ static int process_madt_entries(void) {
case ACPI_MADT_TYPE_IOSAPIC: {
acpi_madt_iosapic_t *madt_iosapic = (acpi_madt_iosapic_t *) entry->data;

add_ioapic(madt_iosapic->ioapic_id, 0x00, true, madt_iosapic->base_address,
madt_iosapic->gsi_base);

printk("ACPI: [MADT] IOSAPIC ID: %u, Base Address: %p, GSI Base: 0x%08x\n",
madt_iosapic->ioapic_id, _ptr(madt_iosapic->base_address),
madt_iosapic->gsi_base);
Expand Down
3 changes: 3 additions & 0 deletions common/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
#include <cmdline.h>
#include <console.h>
#include <cpuid.h>
#include <ioapic.h>
#include <ktf.h>
#include <lib.h>
#include <multiboot.h>
Expand Down Expand Up @@ -219,6 +220,8 @@ void __noreturn __text_init kernel_start(uint32_t multiboot_magic,

init_smp();

init_ioapic();

/* Jump from .text.init section to .text */
asm volatile("push %0; ret" ::"r"(&kernel_main));

Expand Down
108 changes: 108 additions & 0 deletions include/arch/x86/ioapic.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,19 @@
#include <lib.h>
#include <list.h>

/* IOAPIC ID is 4 bits long */
#define MAX_IOAPICS (1 << 4)

/* IOAPIC register offsets */
#define IOAPIC_ID 0x00
#define IOAPIC_VERSION 0x01
#define IOAPIC_ARB 0x02
#define IOAPIC_REDIRTBL(n) (0x10 + 2 * (n))

/* MMIO access registers */
#define IOAPIC_REGSEL 0x00
#define IOAPIC_IOWIN 0x10

#define IOAPIC_DEST_ID_UNKNOWN ((uint8_t) 0xFF)

#ifndef __ASSEMBLY__
Expand Down Expand Up @@ -68,6 +81,54 @@ struct bus {
};
typedef struct bus bus_t;

struct ioapic {
uint8_t id;
uint8_t version;
bool enabled;
uint64_t base_address;
void *virt_address;
uint32_t gsi_base;
unsigned nr_entries;
};
typedef struct ioapic ioapic_t;

union ioapic_id {
struct {
/* clang-format off */
uint32_t rsvd1 : 24,
apic_id : 4,
rsvd2 : 4;
/* clang-format on */
} __packed;
uint32_t reg;
};
typedef union ioapic_id ioapic_id_t;

union ioapic_version {
struct {
/* clang-format off */
uint32_t version : 8,
rsvd1 : 8,
max_redir_entry : 8,
rsvd2 : 8;
/* clang-format on */
} __packed;
uint32_t reg;
};
typedef union ioapic_version ioapic_version_t;

union ioapic_arb {
struct {
/* clang-format off */
uint32_t rsvd1 : 24,
apic_id : 4,
rsvd2 : 4;
/* clang-format on */
} __packed;
uint32_t reg;
};
typedef union ioapic_arb ioapic_arb_t;

enum ioapic_irq_type {
IOAPIC_IRQ_TYPE_INT = 0x00,
IOAPIC_IRQ_TYPE_NMI = 0x01,
Expand Down Expand Up @@ -116,13 +177,60 @@ enum ioapic_int_mask {
};
typedef enum ioapic_int_mask ioapic_int_mask_t;

union ioapic_redirtbl_entry {
struct {
/* clang-format off */
uint64_t vector : 8,
deliv_mode : 3,
dest_mode : 1,
deliv_status : 1,
polarity : 1,
remote_irr : 1,
trigger_mode : 1,
int_mask : 1,
rsvd : 39,
destination : 8;
/* clang-format on */
} __packed;
struct {
uint32_t low;
uint32_t high;
} __packed;
uint64_t reg;
};
typedef union ioapic_redirtbl_entry ioapic_redirtbl_entry_t;

/* External declarations */

extern bus_t *add_system_bus(uint8_t id, const char *name, size_t namelen);
extern int add_system_bus_irq_override(uint8_t bus_id, irq_override_t *irq_override);
extern irq_override_t *get_system_isa_bus_irq(uint8_t irq_type, uint32_t irq_src);
extern irq_override_t *get_system_pci_bus_irq(uint8_t irq_type, uint32_t irq_src);

extern void init_ioapic(void);
extern ioapic_t *get_ioapic(uint8_t id);
extern ioapic_t *add_ioapic(uint8_t id, uint8_t version, bool enabled,
uint64_t base_address, uint32_t gsi_base);
extern int get_ioapic_redirtbl_entry(ioapic_t *ioapic, unsigned n,
ioapic_redirtbl_entry_t *entry);
extern int set_ioapic_redirtbl_entry(ioapic_t *ioapic, unsigned n,
ioapic_redirtbl_entry_t *entry);
extern void set_ioapic_irq_mask(ioapic_t *ioapic, unsigned irq, ioapic_int_mask_t mask);
extern void configure_isa_irq(unsigned irq_src, uint8_t vector,
ioapic_dest_mode_t dst_mode, uint8_t dst_ids);

/* Static declarations */

static inline uint32_t ioapic_read32(ioapic_t *ioapic, uint8_t reg) {
*(volatile uint8_t *) (ioapic->virt_address + IOAPIC_REGSEL) = reg;
return *(volatile uint32_t *) (ioapic->virt_address + IOAPIC_IOWIN);
}

static inline void ioapic_write32(ioapic_t *ioapic, uint8_t reg, uint32_t value) {
*(volatile uint8_t *) (ioapic->virt_address + IOAPIC_REGSEL) = reg;
*(volatile uint32_t *) (ioapic->virt_address + IOAPIC_IOWIN) = value;
}

#endif /* __ASSEMBLY__ */

#endif /* KTF_IOAPIC_H */
3 changes: 3 additions & 0 deletions smp/mptables.c
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ static void process_mpc_entries(mpc_hdr_t *mpc_ptr) {
case MPC_IOAPIC_ENTRY: {
mpc_ioapic_entry_t *mpc_ioapic = (mpc_ioapic_entry_t *) entry_ptr;

add_ioapic(mpc_ioapic->id, mpc_ioapic->version, mpc_ioapic->en,
mpc_ioapic->base_addr, 0x0U);

dump_mpc_ioapic_entry(mpc_ioapic);
entry_ptr += sizeof(*mpc_ioapic);
break;
Expand Down

0 comments on commit 9e77b77

Please sign in to comment.