diff --git a/sys/kern/subr_intr.c b/sys/kern/subr_intr.c --- a/sys/kern/subr_intr.c +++ b/sys/kern/subr_intr.c @@ -94,13 +94,16 @@ MALLOC_DEFINE(M_INTRNG, "intr", "intr interrupt handling"); /* Main interrupt handler called from assembler -> 'hidden' for C code. */ +void intr_irq_handler_type(struct trapframe *tf, uint32_t type); void intr_irq_handler(struct trapframe *tf); /* Root interrupt controller stuff. */ device_t intr_irq_root_dev; +static intr_irq_type_filter_t *irq_root_type_filter; static intr_irq_filter_t *irq_root_filter; static void *irq_root_arg; static u_int irq_root_ipicount; +static uint32_t irq_root_typemask; struct intr_pic_child { SLIST_ENTRY(intr_pic_child) pc_next; @@ -337,12 +340,23 @@ * from the assembler, where CPU interrupt is served. */ void -intr_irq_handler(struct trapframe *tf) +intr_irq_handler_type(struct trapframe *tf, uint32_t type) { struct trapframe * oldframe; struct thread * td; - KASSERT(irq_root_filter != NULL, ("%s: no filter", __func__)); + KASSERT(irq_root_type_filter != NULL, ("%s: no filter", __func__)); + KASSERT((irq_root_typemask & type) != 0, + ("%s: unknown irq type (%x)", __func__, type)); + + /* + * We could allow this possibility, but it doesn't make sense at the + * moment so we should defend against it early on. Root filter + * implementations should be more carefully considered if we start to + * allow multiple types firing at once for some reason. + */ + KASSERT((type & ~(1 << (ffs(type) - 1))) == 0, + ("%s: multiple irq types specified (%x)", __func__, type)); kasan_mark(tf, sizeof(*tf), sizeof(*tf), 0); @@ -351,7 +365,7 @@ td = curthread; oldframe = td->td_intr_frame; td->td_intr_frame = tf; - irq_root_filter(irq_root_arg); + irq_root_type_filter(irq_root_arg, type); td->td_intr_frame = oldframe; critical_exit(); #ifdef HWPMC_HOOKS @@ -361,6 +375,12 @@ #endif } +void +intr_irq_handler(struct trapframe *tf) +{ + intr_irq_handler_type(tf, INTR_TYPE_IRQ); +} + int intr_child_irq_handler(struct intr_pic *parent, uintptr_t irq) { @@ -883,8 +903,9 @@ * an interrupts property and thus no explicit interrupt parent." */ int -intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, - void *arg, u_int ipicount) +intr_pic_claim_root_type(device_t dev, intptr_t xref, + intr_irq_type_filter_t *filter, void *arg, u_int ipicount, + uint32_t typemask) { struct intr_pic *pic; @@ -903,6 +924,11 @@ return (EINVAL); } + if (typemask == 0) { + device_printf(dev, "typemask must be specified\n"); + return (EINVAL); + } + /* * Only one interrupt controllers could be on the root for now. * Note that we further suppose that there is not threaded interrupt @@ -914,14 +940,44 @@ } intr_irq_root_dev = dev; - irq_root_filter = filter; + irq_root_type_filter = filter; irq_root_arg = arg; irq_root_ipicount = ipicount; + irq_root_typemask = typemask; debugf("irq root set to %s\n", device_get_nameunit(dev)); return (0); } +/* + * Shim from a typed filter to an untyped filter. It's expected that if an + * interrupt controller used intr_pic_claim_root(), they'll go through this shim + * that specifically only handles the basic irq type. This maintains KBI to + * avoid needing to touch existing drivers, they can still use a driver_intr_t + * compatible filter as long as they don't care about types (i.e., all existing + * drivers). + */ +static int +intr_pic_root_type_filter(void *arg, uint32_t type __diagused) +{ + + MPASS(type == INTR_TYPE_IRQ); + return (irq_root_filter(arg)); +} + +int +intr_pic_claim_root(device_t dev, intptr_t xref, intr_irq_filter_t *filter, + void *arg, u_int ipicount) +{ + int error; + + error = intr_pic_claim_root_type(dev, xref, intr_pic_root_type_filter, + arg, ipicount, INTR_TYPE_IRQ); + if (error == 0) + irq_root_filter = filter; + return (error); +} + /* * Add a handler to manage a sub range of a parents interrupts. */ diff --git a/sys/sys/intr.h b/sys/sys/intr.h --- a/sys/sys/intr.h +++ b/sys/sys/intr.h @@ -63,14 +63,31 @@ #ifdef notyet #define INTR_SOLO INTR_MD1 +typedef int intr_irq_type_filter_t(void *arg, uint32_t type, struct trapframe *tf); typedef int intr_irq_filter_t(void *arg, struct trapframe *tf); #else +typedef int intr_irq_type_filter_t(void *arg, uint32_t type); typedef int intr_irq_filter_t(void *arg); #endif typedef int intr_child_irq_filter_t(void *arg, uintptr_t irq); #define INTR_ISRC_NAMELEN (MAXCOMLEN + 1) +/* + * For INTR types, the top two bits are reserved for INTRNG usage. Only one of + * the two bits is used at this time. The rest of the space is divided in two, + * the lower half for machdep reservations and the upper half for platform + * reservations. + */ +#define INTR_TYPE_MD_FIRST 0x00000001 +#define INTR_TYPE_MD_LAST 0x00004000 +#define INTR_TYPE_PLATFORM_FIRST 0x00008000 +#define INTR_TYPE_PLATFORM_LAST 0x20000000 +#define INTR_TYPE_INTRNG_FIRST 0x40000000 + +/* INTRNG's slice */ +#define INTR_TYPE_IRQ (INTR_TYPE_INTRNG_FIRST << 0) + #define INTR_ISRCF_IPI 0x01 /* IPI interrupt */ #define INTR_ISRCF_PPI 0x02 /* PPI interrupt */ #define INTR_ISRCF_BOUND 0x04 /* bound to a CPU */ @@ -110,6 +127,8 @@ struct intr_pic *intr_pic_register(device_t, intptr_t); int intr_pic_deregister(device_t, intptr_t); +int intr_pic_claim_root_type(device_t, intptr_t, intr_irq_type_filter_t *, void *, + u_int, uint32_t); int intr_pic_claim_root(device_t, intptr_t, intr_irq_filter_t *, void *, u_int); int intr_pic_add_handler(device_t, struct intr_pic *, intr_child_irq_filter_t *, void *, uintptr_t, uintptr_t);