From 68b90e82b2697c9d6acc358c76689a96a5958797 Mon Sep 17 00:00:00 2001 From: Sepherosa Ziehau Date: Wed, 23 Mar 2011 20:04:15 +0800 Subject: [PATCH] ioapic: Rework I/O APIC APIC ID allocation - I/O APIC's ID field in ID register contains only 4bits, so only 16 I/O APICs should be allowed. - All I/O APIC's APIC ID's lower 4bits (what we wrote to I/O APIC ID register) must be different from each other, this should be done even on xAPIC (e.g. Pentium 4), where I/O APIC's APIC IDs are not required to be different from what LAPIC's. --- sys/platform/pc32/apic/mpapic.c | 140 ++++++++++++++++++++++++++++---- sys/platform/pc64/apic/mpapic.c | 140 ++++++++++++++++++++++++++++---- 2 files changed, 246 insertions(+), 34 deletions(-) diff --git a/sys/platform/pc32/apic/mpapic.c b/sys/platform/pc32/apic/mpapic.c index f7467cf1ca..30d0512503 100644 --- a/sys/platform/pc32/apic/mpapic.c +++ b/sys/platform/pc32/apic/mpapic.c @@ -42,6 +42,9 @@ #include +#define IOAPIC_COUNT_MAX 16 +#define IOAPIC_ID_MASK (IOAPIC_COUNT_MAX - 1) + /* XXX */ extern pt_entry_t *SMPpt; @@ -81,7 +84,10 @@ static void lapic_timer_intr_enable(struct cputimer_intr *); static void lapic_timer_intr_restart(struct cputimer_intr *); static void lapic_timer_intr_pmfixup(struct cputimer_intr *); +static int lapic_unused_apic_id(int); + static void ioapic_setup(const struct ioapic_info *); +static int ioapic_alloc_apic_id(int); static void ioapic_set_apic_id(const struct ioapic_info *); static void ioapic_gsi_setup(int); static const struct ioapic_info * @@ -1070,6 +1076,18 @@ u_sleep(int count) /* spin */ ; } +static int +lapic_unused_apic_id(int start) +{ + int i; + + for (i = start; i < NAPICID; ++i) { + if (ID_TO_CPU(i) == -1) + return i; + } + return NAPICID; +} + void lapic_map(vm_offset_t lapic_addr) { @@ -1087,7 +1105,10 @@ void lapic_config(void) { struct lapic_enumerator *e; - int error; + int error, i; + + for (i = 0; i < NAPICID; ++i) + ID_TO_CPU(i) = -1; TAILQ_FOREACH(e, &lapic_enumerators, lapic_link) { error = e->lapic_probe(e); @@ -1161,27 +1182,52 @@ ioapic_config(void) if (!ioapic_use_old) { struct ioapic_info *info; + int start_apic_id = 0; /* - * Fixup the rest of the fields of ioapic_info + * Setup index */ i = 0; - TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) { - const struct ioapic_info *prev_info; - + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) info->io_idx = i++; - info->io_apic_id = info->io_idx + lapic_id_max + 1; - if (bootverbose) { - kprintf("IOAPIC: idx %d, apic id %d, " - "gsi base %d, npin %d\n", - info->io_idx, - info->io_apic_id, - info->io_gsi_base, - info->io_npin); + if (i > IOAPIC_COUNT_MAX) /* XXX magic number */ + panic("ioapic_config: more than 16 I/O APIC\n"); + + /* + * Setup APIC ID + */ + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) { + int apic_id; + + apic_id = ioapic_alloc_apic_id(start_apic_id); + if (apic_id == NAPICID) { + kprintf("IOAPIC: can't alloc APIC ID for " + "%dth I/O APIC\n", info->io_idx); + break; } + info->io_apic_id = apic_id; + + start_apic_id = apic_id + 1; + } + if (info != NULL) { + /* + * xAPIC allows I/O APIC's APIC ID to be same + * as the LAPIC's APIC ID + */ + kprintf("IOAPIC: use xAPIC model to alloc APIC ID " + "for I/O APIC\n"); + + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) + info->io_apic_id = info->io_idx; + } + + /* + * Warning about any GSI holes + */ + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) { + const struct ioapic_info *prev_info; - /* Warning about possible GSI hole */ prev_info = TAILQ_PREV(info, ioapic_info_list, io_link); if (prev_info != NULL) { if (info->io_gsi_base != @@ -1195,6 +1241,17 @@ ioapic_config(void) } } + if (bootverbose) { + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) { + kprintf("IOAPIC: idx %d, apic id %d, " + "gsi base %d, npin %d\n", + info->io_idx, + info->io_apic_id, + info->io_gsi_base, + info->io_npin); + } + } + /* * Setup all I/O APIC */ @@ -1248,6 +1305,7 @@ ioapic_add(void *addr, int gsi_base, int npin) ninfo->io_addr = addr; ninfo->io_npin = npin; ninfo->io_gsi_base = gsi_base; + ninfo->io_apic_id = -1; /* * Create IOAPIC list in ascending order of GSI base @@ -1288,6 +1346,7 @@ static void ioapic_set_apic_id(const struct ioapic_info *info) { uint32_t id; + int apic_id; id = ioapic_read(info->io_addr, IOAPIC_ID); @@ -1300,9 +1359,15 @@ ioapic_set_apic_id(const struct ioapic_info *info) * Re-read && test */ id = ioapic_read(info->io_addr, IOAPIC_ID); - if (((id & APIC_ID_MASK) >> 24) != info->io_apic_id) { - panic("ioapic_set_apic_id: can't set apic id to %d\n", - info->io_apic_id); + apic_id = (id & APIC_ID_MASK) >> 24; + + /* + * I/O APIC ID is a 4bits field + */ + if ((apic_id & IOAPIC_ID_MASK) != + (info->io_apic_id & IOAPIC_ID_MASK)) { + panic("ioapic_set_apic_id: can't set apic id to %d, " + "currently set to %d\n", info->io_apic_id, apic_id); } } @@ -1500,3 +1565,44 @@ ioapic_setup(const struct ioapic_info *info) for (i = 0; i < info->io_npin; ++i) ioapic_gsi_setup(info->io_gsi_base + i); } + +static int +ioapic_alloc_apic_id(int start) +{ + for (;;) { + const struct ioapic_info *info; + int apic_id, apic_id16; + + apic_id = lapic_unused_apic_id(start); + if (apic_id == NAPICID) { + kprintf("IOAPIC: can't find unused APIC ID\n"); + return apic_id; + } + apic_id16 = apic_id & IOAPIC_ID_MASK; + + /* + * Check against other I/O APIC's APIC ID's lower 4bits. + * + * The new APIC ID will have to be different from others + * in the lower 4bits, no matter whether xAPIC is used + * or not. + */ + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) { + if (info->io_apic_id == -1) { + info = NULL; + break; + } + if ((info->io_apic_id & IOAPIC_ID_MASK) == apic_id16) + break; + } + if (info == NULL) + return apic_id; + + kprintf("IOAPIC: APIC ID %d has same lower 4bits as " + "%dth I/O APIC, keep searching...\n", + apic_id, info->io_idx); + + start = apic_id + 1; + } + panic("ioapic_unused_apic_id: never reached\n"); +} diff --git a/sys/platform/pc64/apic/mpapic.c b/sys/platform/pc64/apic/mpapic.c index edbb1356eb..2a57f61304 100644 --- a/sys/platform/pc64/apic/mpapic.c +++ b/sys/platform/pc64/apic/mpapic.c @@ -43,6 +43,9 @@ #include "apicvar.h" +#define IOAPIC_COUNT_MAX 16 +#define IOAPIC_ID_MASK (IOAPIC_COUNT_MAX - 1) + /* EISA Edge/Level trigger control registers */ #define ELCR0 0x4d0 /* eisa irq 0-7 */ #define ELCR1 0x4d1 /* eisa irq 8-15 */ @@ -82,7 +85,10 @@ static void lapic_timer_intr_enable(struct cputimer_intr *); static void lapic_timer_intr_restart(struct cputimer_intr *); static void lapic_timer_intr_pmfixup(struct cputimer_intr *); +static int lapic_unused_apic_id(int); + static void ioapic_setup(const struct ioapic_info *); +static int ioapic_alloc_apic_id(int); static void ioapic_set_apic_id(const struct ioapic_info *); static void ioapic_gsi_setup(int); static const struct ioapic_info * @@ -1134,6 +1140,18 @@ u_sleep(int count) /* spin */ ; } +static int +lapic_unused_apic_id(int start) +{ + int i; + + for (i = start; i < NAPICID; ++i) { + if (ID_TO_CPU(i) == -1) + return i; + } + return NAPICID; +} + void lapic_map(vm_offset_t lapic_addr) { @@ -1149,7 +1167,10 @@ void lapic_config(void) { struct lapic_enumerator *e; - int error; + int error, i; + + for (i = 0; i < NAPICID; ++i) + ID_TO_CPU(i) = -1; TAILQ_FOREACH(e, &lapic_enumerators, lapic_link) { error = e->lapic_probe(e); @@ -1223,27 +1244,52 @@ ioapic_config(void) if (!ioapic_use_old) { struct ioapic_info *info; + int start_apic_id = 0; /* - * Fixup the rest of the fields of ioapic_info + * Setup index */ i = 0; - TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) { - const struct ioapic_info *prev_info; - + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) info->io_idx = i++; - info->io_apic_id = info->io_idx + lapic_id_max + 1; - if (bootverbose) { - kprintf("IOAPIC: idx %d, apic id %d, " - "gsi base %d, npin %d\n", - info->io_idx, - info->io_apic_id, - info->io_gsi_base, - info->io_npin); + if (i > IOAPIC_COUNT_MAX) /* XXX magic number */ + panic("ioapic_config: more than 16 I/O APIC\n"); + + /* + * Setup APIC ID + */ + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) { + int apic_id; + + apic_id = ioapic_alloc_apic_id(start_apic_id); + if (apic_id == NAPICID) { + kprintf("IOAPIC: can't alloc APIC ID for " + "%dth I/O APIC\n", info->io_idx); + break; } + info->io_apic_id = apic_id; + + start_apic_id = apic_id + 1; + } + if (info != NULL) { + /* + * xAPIC allows I/O APIC's APIC ID to be same + * as the LAPIC's APIC ID + */ + kprintf("IOAPIC: use xAPIC model to alloc APIC ID " + "for I/O APIC\n"); + + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) + info->io_apic_id = info->io_idx; + } + + /* + * Warning about any GSI holes + */ + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) { + const struct ioapic_info *prev_info; - /* Warning about possible GSI hole */ prev_info = TAILQ_PREV(info, ioapic_info_list, io_link); if (prev_info != NULL) { if (info->io_gsi_base != @@ -1257,6 +1303,17 @@ ioapic_config(void) } } + if (bootverbose) { + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) { + kprintf("IOAPIC: idx %d, apic id %d, " + "gsi base %d, npin %d\n", + info->io_idx, + info->io_apic_id, + info->io_gsi_base, + info->io_npin); + } + } + /* * Setup all I/O APIC */ @@ -1310,6 +1367,7 @@ ioapic_add(void *addr, int gsi_base, int npin) ninfo->io_addr = addr; ninfo->io_npin = npin; ninfo->io_gsi_base = gsi_base; + ninfo->io_apic_id = -1; /* * Create IOAPIC list in ascending order of GSI base @@ -1350,6 +1408,7 @@ static void ioapic_set_apic_id(const struct ioapic_info *info) { uint32_t id; + int apic_id; id = ioapic_read(info->io_addr, IOAPIC_ID); @@ -1362,9 +1421,15 @@ ioapic_set_apic_id(const struct ioapic_info *info) * Re-read && test */ id = ioapic_read(info->io_addr, IOAPIC_ID); - if (((id & APIC_ID_MASK) >> 24) != info->io_apic_id) { - panic("ioapic_set_apic_id: can't set apic id to %d\n", - info->io_apic_id); + apic_id = (id & APIC_ID_MASK) >> 24; + + /* + * I/O APIC ID is a 4bits field + */ + if ((apic_id & IOAPIC_ID_MASK) != + (info->io_apic_id & IOAPIC_ID_MASK)) { + panic("ioapic_set_apic_id: can't set apic id to %d, " + "currently set to %d\n", info->io_apic_id, apic_id); } } @@ -1562,3 +1627,44 @@ ioapic_setup(const struct ioapic_info *info) for (i = 0; i < info->io_npin; ++i) ioapic_gsi_setup(info->io_gsi_base + i); } + +static int +ioapic_alloc_apic_id(int start) +{ + for (;;) { + const struct ioapic_info *info; + int apic_id, apic_id16; + + apic_id = lapic_unused_apic_id(start); + if (apic_id == NAPICID) { + kprintf("IOAPIC: can't find unused APIC ID\n"); + return apic_id; + } + apic_id16 = apic_id & IOAPIC_ID_MASK; + + /* + * Check against other I/O APIC's APIC ID's lower 4bits. + * + * The new APIC ID will have to be different from others + * in the lower 4bits, no matter whether xAPIC is used + * or not. + */ + TAILQ_FOREACH(info, &ioapic_conf.ioc_list, io_link) { + if (info->io_apic_id == -1) { + info = NULL; + break; + } + if ((info->io_apic_id & IOAPIC_ID_MASK) == apic_id16) + break; + } + if (info == NULL) + return apic_id; + + kprintf("IOAPIC: APIC ID %d has same lower 4bits as " + "%dth I/O APIC, keep searching...\n", + apic_id, info->io_idx); + + start = apic_id + 1; + } + panic("ioapic_unused_apic_id: never reached\n"); +} -- 2.41.0