aos/kernel/arch/arm/irq.c
Daniel Schwyn 6d444bf552 Main handout
Signed-off-by: Daniel Schwyn <daniel.schwyn@inf.ethz.ch>
2022-03-03 14:57:51 +01:00

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
}