192 lines
5.8 KiB
C
192 lines
5.8 KiB
C
/*
|
|
* Copyright (c) 2007, 2008, 2009, 2010, 2011, ETH Zurich.
|
|
* All rights reserved.
|
|
*
|
|
* This file is distributed under the terms in the attached LICENSE file.
|
|
* If you do not find this file, copies can be found by writing to:
|
|
* ETH Zurich D-INFK, Universitaetstrasse 6, CH-8092 Zurich. Attn: Systems Group.
|
|
*/
|
|
#include <kernel.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <arch/arm/arm.h>
|
|
#include <arch/arm/platform.h>
|
|
#include <barrelfish_kpi/lmp.h>
|
|
#include <barrelfish_kpi/syscalls.h>
|
|
#include <barrelfish_kpi/sys_debug.h>
|
|
|
|
#include <irq.h>
|
|
|
|
#include <paging_kernel_arch.h>
|
|
#include <dispatch.h>
|
|
#include <exec.h>
|
|
#include <stdio.h>
|
|
#include <syscall.h>
|
|
#include <arch/arm/syscall_arm.h>
|
|
#include <kcb.h>
|
|
|
|
/**
|
|
* \brief User-space IRQ dispatch table.
|
|
*
|
|
* This is essentially a big CNode holding #NDISPATCH capability
|
|
* entries to local endpoints of user-space applications listening to
|
|
* the interrupts.
|
|
*/
|
|
static struct cte irq_dispatch[NDISPATCH];
|
|
|
|
errval_t irq_table_set(unsigned int nidt, capaddr_t endpoint)
|
|
{
|
|
errval_t err;
|
|
struct cte *recv;
|
|
|
|
err = caps_lookup_slot(&dcb_current->cspace.cap, endpoint,
|
|
2, &recv, CAPRIGHTS_WRITE);
|
|
if (err_is_fail(err)) {
|
|
return err_push(err, SYS_ERR_IRQ_LOOKUP);
|
|
}
|
|
|
|
assert(recv != NULL);
|
|
|
|
// Return w/error if cap is not an endpoint
|
|
if (recv->cap.type != ObjType_EndPointLMP) {
|
|
return SYS_ERR_IRQ_NOT_ENDPOINT;
|
|
}
|
|
|
|
// Return w/error if no listener on endpoint
|
|
if (recv->cap.u.endpointlmp.listener == NULL) {
|
|
return SYS_ERR_IRQ_NO_LISTENER;
|
|
}
|
|
|
|
if (nidt < NDISPATCH) {
|
|
// check that we don't overwrite someone else's handler
|
|
if (irq_dispatch[nidt].cap.type != ObjType_Null) {
|
|
printf("kernel: replacing handler for IRQ %d\n", nidt);
|
|
}
|
|
err = caps_copy_to_cte(&irq_dispatch[nidt], recv, false, 0, 0);
|
|
|
|
// Correct interrupt forwarding by the distributor must be ensured
|
|
// in userspace.
|
|
|
|
return err;
|
|
}
|
|
|
|
return SYS_ERR_IRQ_INVALID;
|
|
}
|
|
|
|
errval_t irq_connect(struct capability *irq_dest, capaddr_t endpoint) {
|
|
assert(irq_dest->type == ObjType_IRQDest);
|
|
return irq_table_set(irq_dest->u.irqdest.vector, endpoint);
|
|
}
|
|
|
|
errval_t irq_table_delete(unsigned int nidt)
|
|
{
|
|
if (nidt < NDISPATCH) {
|
|
irq_dispatch[nidt].cap.type = ObjType_Null;
|
|
|
|
/* todo: gic disable irq */
|
|
|
|
return SYS_ERR_OK;
|
|
}
|
|
return SYS_ERR_IRQ_INVALID;
|
|
}
|
|
|
|
errval_t irq_table_alloc_dest_cap(uint8_t dcn_level, capaddr_t dcn,
|
|
capaddr_t out_cap_addr, int vec_hint)
|
|
{
|
|
errval_t err;
|
|
if(vec_hint < 0){
|
|
printk(LOG_WARN, "irq: vec_hint must be provided on ARM\n", vec_hint);
|
|
return SYS_ERR_IRQ_INVALID;
|
|
}
|
|
if(vec_hint >= NDISPATCH){
|
|
printk(LOG_WARN, "irq: vec_hint (%d) invalid\n", vec_hint);
|
|
return SYS_ERR_IRQ_INVALID;
|
|
}
|
|
|
|
// TODO: Keep track of allocations
|
|
struct cte * cn;
|
|
err = caps_lookup_slot(&dcb_current->cspace.cap, dcn, dcn_level,
|
|
&cn, CAPRIGHTS_WRITE);
|
|
if(err_is_fail(err)){
|
|
return err;
|
|
}
|
|
|
|
struct cte out_cap;
|
|
memset(&out_cap, 0, sizeof(struct cte));
|
|
out_cap.cap.type = ObjType_IRQDest;
|
|
out_cap.cap.u.irqdest.cpu = my_core_id;
|
|
out_cap.cap.u.irqdest.vector = vec_hint;
|
|
caps_copy_to_cnode(cn, out_cap_addr, &out_cap, 0, 0, 0);
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
errval_t irq_table_notify_domains(struct kcb *kcb)
|
|
{
|
|
uintptr_t msg[] = { 1 };
|
|
for (int i = 0; i < NDISPATCH; i++) {
|
|
if (kcb->irq_dispatch[i].cap.type == ObjType_EndPointLMP) {
|
|
struct capability *cap = &kcb->irq_dispatch[i].cap;
|
|
// 1 word message as notification
|
|
errval_t err = lmp_deliver_payload(cap, NULL, msg, 1, false, false);
|
|
if (err_is_fail(err)) {
|
|
if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) {
|
|
struct dispatcher_shared_generic *disp =
|
|
get_dispatcher_shared_generic(cap->u.endpointlmp.listener->disp);
|
|
printk(LOG_DEBUG, "%.*s: IRQ message buffer overflow\n",
|
|
DISP_NAME_LEN, disp->name);
|
|
} else {
|
|
printk(LOG_ERR, "Unexpected error delivering IRQ\n");
|
|
}
|
|
}
|
|
}
|
|
kcb->irq_dispatch[i].cap.type = ObjType_Null;
|
|
}
|
|
return SYS_ERR_OK;
|
|
}
|
|
|
|
/**
|
|
* \brief Send interrupt notification to user-space listener.
|
|
*
|
|
* Sends an interrupt notification IDC to a local endpoint that
|
|
* listens for IRQ notifications.
|
|
*
|
|
* \param irq IRQ# to send in notification.
|
|
*/
|
|
void send_user_interrupt(int irq)
|
|
{
|
|
assert(irq >= 0 && irq < NDISPATCH);
|
|
struct capability *cap = &irq_dispatch[irq].cap;
|
|
|
|
// Return on null cap (unhandled interrupt)
|
|
if (cap->type == ObjType_Null) {
|
|
printk(LOG_WARN, "unhandled IRQ %d\n", irq);
|
|
return;
|
|
}
|
|
|
|
// Otherwise, cap needs to be an endpoint
|
|
assert(cap->type == ObjType_EndPointLMP);
|
|
errval_t err = lmp_deliver_notification(cap);
|
|
if (err_is_fail(err)) {
|
|
if (err_no(err) == SYS_ERR_LMP_BUF_OVERFLOW) {
|
|
struct dispatcher_shared_generic *disp =
|
|
get_dispatcher_shared_generic(
|
|
cap->u.endpointlmp.listener->disp);
|
|
printk(LOG_DEBUG, "%.*s: IRQ message buffer overflow\n",
|
|
DISP_NAME_LEN, disp->name);
|
|
} else {
|
|
printk(LOG_ERR, "Unexpected error delivering IRQ\n");
|
|
}
|
|
}
|
|
|
|
#ifdef SCHEDULER_RR
|
|
/* XXX: run the handler dispatcher immediately
|
|
* we shouldn't do this (we should let the scheduler decide), but because
|
|
* our default scheduler is braindead, this is a quick hack to make sure
|
|
* that mostly-sane things happen
|
|
*/
|
|
dispatch(cap->u.endpointlmp.listener);
|
|
#else
|
|
dispatch(schedule());
|
|
#endif
|
|
}
|