Preliminary irq registration call + irq capability checking

Need to add irqctrl capabilities and irq bits to device memory
caps.

Also need to initialize irq field of devmem caps.
This commit is contained in:
Bahadir Balban
2009-11-28 19:13:23 +02:00
parent b5e6c66426
commit 6e40a2b601
17 changed files with 380 additions and 110 deletions

View File

@@ -41,9 +41,9 @@ int ipc_full_copy(struct ktcb *to, struct ktcb *from)
return ret;
/* Check that utcb memory accesses won't fault us */
if ((ret = tcb_check_and_lazy_map_utcb(to)) < 0)
if ((ret = tcb_check_and_lazy_map_utcb(to, 1)) < 0)
return ret;
if ((ret = tcb_check_and_lazy_map_utcb(from)) < 0)
if ((ret = tcb_check_and_lazy_map_utcb(from, 1)) < 0)
return ret;
/* Directly copy from one utcb to another */

View File

@@ -10,6 +10,7 @@
#include <l4/lib/bit.h>
#include <l4/drivers/irq/pl190/pl190_vic.h>
#include <l4/generic/irq.h>
/* FIXME: Fix the stupid uart driver and change to single definition of this! */
#if defined(read)
@@ -28,13 +29,16 @@
: clrbit(bitvect, (base + reg)))
/* Returns the irq number on this chip converting the irq bitvector */
int pl190_read_irq(void)
l4id_t pl190_read_irq(void)
{
/* This also correctly returns a negative value for a spurious irq. */
return 31 - __clz(read(PL190_VIC_IRQSTATUS));
l4id_t irq;
if ((irq =__clz(read(PL190_VIC_IRQSTATUS))))
return irq;
else
return IRQ_NIL;
}
void pl190_mask_irq(int irq)
void pl190_mask_irq(l4id_t irq)
{
/* Reading WO registers blows QEMU/PB926.
* setbit((1 << irq), PL190_VIC_INTENCLEAR); */
@@ -42,32 +46,37 @@ void pl190_mask_irq(int irq)
}
/* Ack is same as mask */
void pl190_ack_irq(int irq)
void pl190_ack_irq(l4id_t irq)
{
pl190_mask_irq(irq);
}
void pl190_unmask_irq(int irq)
void pl190_unmask_irq(l4id_t irq)
{
setbit(1 << irq, PL190_VIC_INTENABLE);
}
int pl190_sic_read_irq(void)
l4id_t pl190_sic_read_irq(void)
{
return 32 - __clz(read(PL190_SIC_STATUS));
l4id_t irq;
if ((irq =__clz(read(PL190_SIC_STATUS))))
return irq;
else
return IRQ_NIL;
}
void pl190_sic_mask_irq(int irq)
void pl190_sic_mask_irq(l4id_t irq)
{
write(1 << irq, PL190_SIC_ENCLR);
}
void pl190_sic_ack_irq(int irq)
void pl190_sic_ack_irq(l4id_t irq)
{
pl190_sic_mask_irq(irq);
}
void pl190_sic_unmask_irq(int irq)
void pl190_sic_unmask_irq(l4id_t irq)
{
setbit(1 << irq, PL190_SIC_ENSET);
}

View File

@@ -15,6 +15,7 @@
#include <l4/api/thread.h>
#include <l4/api/exregs.h>
#include <l4/api/ipc.h>
#include <l4/api/irq.h>
#include INC_GLUE(message.h)
#include INC_GLUE(ipc.h)
@@ -642,6 +643,119 @@ struct capability *cap_match_mem(struct capability *cap,
return cap;
}
struct sys_irqctrl_args {
struct ktcb *registrant;
unsigned int req;
unsigned int flags;
l4id_t irq;
};
/*
* CAP_TYPE_MAP already matched upon entry.
*
* Match only device-specific details, e.g. irq registration
* capability
*/
struct capability *cap_match_devmem(struct capability *cap,
void *args_ptr)
{
struct sys_irqctrl_args *args = args_ptr;
struct ktcb *target = args->registrant;
unsigned int perms;
/* It must be a physmem type */
if (cap_type(cap) != CAP_TYPE_MAP_PHYSMEM)
return 0;
/* It must be a device */
if (!cap_is_devmem(cap))
return 0;
/* Irq numbers should match */
if (cap->irq != args->irq)
return 0;
/* Check permissions, we only check irq specific */
switch (args->req) {
case IRQ_CONTROL_REGISTER:
perms = CAP_IRQCTRL_REGISTER;
if ((cap->access & perms) != perms)
return 0;
break;
default:
/* Anything else is an invalid/unrecognised argument */
return 0;
}
/*
* Check that irq registration to target is covered
* by the capability containment rules.
*/
switch (cap_rtype(cap)) {
case CAP_RTYPE_THREAD:
if (target->tid != cap->resid)
return 0;
break;
case CAP_RTYPE_SPACE:
if (target->space->spid != cap->resid)
return 0;
break;
case CAP_RTYPE_CONTAINER:
if (target->container->cid != cap->resid)
return 0;
break;
default:
BUG(); /* Unknown cap type is a bug */
}
return cap;
}
/*
* CAP_TYPE_IRQCTRL already matched
*/
struct capability *cap_match_irqctrl(struct capability *cap,
void *args_ptr)
{
struct sys_irqctrl_args *args = args_ptr;
struct ktcb *target = args->registrant;
/* Check operation privileges */
switch (args->req) {
case IRQ_CONTROL_REGISTER:
if (!(cap->access & CAP_IRQCTRL_REGISTER))
return 0;
break;
default:
/* We refuse to accept anything else */
return 0;
}
/*
* Target thread is the thread that is going to
* handle the irqs. Check if capability matches
* the target in any of its containment level.
*/
switch (cap_rtype(cap)) {
case CAP_RTYPE_THREAD:
if (target->tid != cap->resid)
return 0;
break;
case CAP_RTYPE_SPACE:
if (target->space->spid != cap->resid)
return 0;
break;
case CAP_RTYPE_CONTAINER:
if (target->container->cid != cap->resid)
return 0;
break;
default:
BUG(); /* Unknown cap type is a bug */
}
return cap;
}
#if defined(CONFIG_CAPABILITIES)
int cap_mutex_check(unsigned long mutex_address, int mutex_op)
{
@@ -761,6 +875,34 @@ int cap_thread_check(struct ktcb *task,
return 0;
}
int cap_irq_check(struct ktcb *registrant, unsigned int req,
unsigned int flags, l4id_t irq)
{
struct sys_irqctrl_args args = {
.registrant = registrant,
.req = req,
.flags = flags,
.irq = irq,
};
/* Find the irq control capability of caller */
if (!(cap_find(current, cap_match_irqctrl,
&args, CAP_TYPE_IRQCTRL)))
return -ENOCAP;
/*
* Find the device capability and
* check that it allows irq registration
*/
if (!(cap_find(current, cap_match_devmem,
&args, CAP_TYPE_MAP_PHYSMEM)))
return -ENOCAP;
return 0;
}
#else /* Meaning !CONFIG_CAPABILITIES */
int cap_mutex_check(unsigned long mutex_address, int mutex_op)
{
@@ -795,4 +937,11 @@ int cap_thread_check(struct ktcb *task,
{
return 0;
}
int cap_irq_check(struct ktcb *registrant, unsigned int req,
unsigned int flags, l4id_t irq)
{
return 0;
}
#endif /* End of !CONFIG_CAPABILITIES */

View File

@@ -1,9 +1,8 @@
/*
* Kernel irq handling (core irqs like timer). Also hope to add thread-level
* irq handling in the future.
*
* Copyright (C) 2007 Bahadir Balban
* Kernel irq handling (core irqs like timer).
* Also thread-level irq handling.
*
* Copyright (C) 2007 - 2009 Bahadir Balban
*/
#include <l4/config.h>
#include <l4/macros.h>
@@ -16,7 +15,33 @@
#include INC_PLAT(irq.h)
#include INC_ARCH(exception.h)
/* This enables the lower chip on the current chip, if such chaining exists. */
/*
* Registers a userspace thread as an irq handler.
*/
int irq_register(struct ktcb *task, int notify_slot,
l4id_t irq_index, irq_handler_t handler)
{
struct irq_desc *this_desc = irq_desc_array + irq_index;
struct irq_chip *current_chip = irq_chip_array;
for (int i = 0; i < IRQ_CHIPS_MAX; i++) {
if (irq_index >= current_chip->start &&
irq_index < current_chip->end) {
this_desc->chip = current_chip;
break;
}
}
/* Setup the handler */
this_desc->handler = handler;
/* Setup the slot to notify the task */
this_desc->task_notify_slot = notify_slot;
return 0;
}
/* If there is cascading, enable it. */
static inline void cascade_irq_chip(struct irq_chip *this_chip)
{
if (this_chip->cascade >= 0) {
@@ -31,51 +56,86 @@ void irq_controllers_init(void)
for (int i = 0; i < IRQ_CHIPS_MAX; i++) {
this_chip = irq_chip_array + i;
/* Initialise the irq chips (e.g. reset all registers) */
/* Initialise the irq chip (e.g. reset all registers) */
this_chip->ops.init();
/* Enable cascaded irqs if needed */
/* Enable cascaded irq on this chip if it exists */
cascade_irq_chip(this_chip);
}
}
int global_irq_index(void)
/*
* Finds the global irq number by looping over irq chips.
*
* Global irq number =
* Unique irq chip_offset defined by us + irq number local to chip
*/
l4id_t global_irq_index(void)
{
struct irq_chip *this_chip;
int irq_index = 0;
l4id_t irq_index = 0;
/* Loop over irq chips from top to bottom until
* the actual irq on the lowest chip is found */
/*
* Loop over all chips, starting from the top
* (i.e. nearest to the cpu)
*/
for (int i = 0; i < IRQ_CHIPS_MAX; i++) {
/* Get the chip */
this_chip = irq_chip_array + i;
BUG_ON((irq_index = this_chip->ops.read_irq()) < 0);
if (irq_index != this_chip->cascade) {
irq_index += this_chip->offset;
/* Found the real irq, return */
break;
}
/* Hit the cascading irq. Continue on next irq chip. */
/* Find local irq that is triggered on this chip */
BUG_ON((irq_index =
this_chip->ops.read_irq()) == IRQ_NIL);
/* See if this irq is a cascaded irq */
if (irq_index == this_chip->cascade)
continue; /* Yes, continue to next chip */
/*
* Irq was initiated from this chip. Add this chip's
* global irq offset and return it
*/
irq_index += this_chip->start;
return irq_index;
}
return irq_index;
/*
* Cascaded irq detected, but no lower chips
* left to process. This should not happen
*/
BUG();
return IRQ_NIL;
}
void do_irq(void)
{
int irq_index = global_irq_index();
l4id_t irq_index = global_irq_index();
struct irq_desc *this_irq = irq_desc_array + irq_index;
/* TODO: This can be easily done few instructions quicker by some
* immediate read/disable/enable_all(). We stick with this clear
* implementation for now. */
/*
* Note, this can be easily done a few instructions
* quicker by some immediate read/disable/enable_all().
*
* We currently stick with it as it is clearer.
*/
irq_disable(irq_index);
/* Re-enable all irqs */
enable_irqs();
/* TODO: Call irq_thread_notify(irq_index) for threaded irqs. */
/* Handle the irq */
BUG_ON(!this_irq->handler);
if (this_irq->handler() != IRQ_HANDLED) {
printk("Spurious or broken irq\n"); BUG();
if (this_irq->handler(this_irq) != IRQ_HANDLED) {
printk("Spurious or broken irq\n");
BUG();
}
irq_enable(irq_index);
}

View File

@@ -345,9 +345,9 @@ int memcap_request_device(struct cap_list *cap_list,
printk("%s: FATAL: Device memory requested "
"does not match any available device "
"capabilities start=0x%lx, end=0x%lx "
"uattr=0x%x\n", __KERNELNAME__,
"attr=0x%x\n", __KERNELNAME__,
__pfn_to_addr(devcap->start),
__pfn_to_addr(devcap->end), devcap->uattr[0]);
__pfn_to_addr(devcap->end), devcap->attr);
BUG();
}

View File

@@ -221,16 +221,19 @@ void task_update_utcb(struct ktcb *task)
* upon an ipc that requires the kernel to access that utcb, in other
* words foreign utcbs are mapped lazily.
*/
int tcb_check_and_lazy_map_utcb(struct ktcb *task)
int tcb_check_and_lazy_map_utcb(struct ktcb *task, int page_in)
{
unsigned int phys;
int ret;
BUG_ON(!task->utcb_address);
if (!task->utcb_address)
return -ENOUTCB;
/*
* If task == current && not mapped,
* If task == current && not mapped && page_in,
* page-in, if not return -EFAULT
* If task == current && not mapped && !page_in,
* return -EFAULT
* If task != current && not mapped,
* return -EFAULT since can't page-in on behalf of it.
* If task != current && task mapped,
@@ -239,10 +242,16 @@ int tcb_check_and_lazy_map_utcb(struct ktcb *task)
* but mapped == current mapped, return 0
*/
/* FIXME:
*
* Do the check_access part without distinguishing current/non-current
* Do the rest (i.e. mapping the value to the current table) only if the utcb is non-current
*/
if (current == task) {
/* Check own utcb, if not there, page it in */
if ((ret = check_access(task->utcb_address, UTCB_SIZE,
MAP_SVC_RW_FLAGS, 1)) < 0)
MAP_SVC_RW_FLAGS, page_in)) < 0)
return -EFAULT;
else
return 0;

View File

@@ -17,7 +17,8 @@ struct irq_chip irq_chip_array[IRQ_CHIPS_MAX] = {
.name = "Vectored irq controller",
.level = 0,
.cascade = IRQ_SIC,
.offset = 0,
.start = VIC_CHIP_OFFSET,
.end = VIC_CHIP_OFFSET + VIC_IRQS_MAX,
.ops = {
.init = pl190_vic_init,
.read_irq = pl190_read_irq,
@@ -29,7 +30,8 @@ struct irq_chip irq_chip_array[IRQ_CHIPS_MAX] = {
.name = "Secondary irq controller",
.level = 1,
.cascade = IRQ_NIL,
.offset = SIRQ_CHIP_OFFSET,
.start = SIC_CHIP_OFFSET,
.end = SIC_CHIP_OFFSET + SIC_IRQS_MAX,
.ops = {
.init = pl190_sic_init,
.read_irq = pl190_sic_read_irq,
@@ -39,7 +41,7 @@ struct irq_chip irq_chip_array[IRQ_CHIPS_MAX] = {
},
};
static int platform_timer_handler(void)
static int platform_timer_handler(struct irq_desc *desc)
{
/*
* Microkernel is using just TIMER0,