1/*
2 * Copyright 2017, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13/*-
14 * Copyright (c) 2014 Antti Kantee.  All Rights Reserved.
15 *
16 * Redistribution and use in source and binary forms, with or without
17 * modification, are permitted provided that the following conditions
18 * are met:
19 * 1. Redistributions of source code must retain the above copyright
20 *    notice, this list of conditions and the following disclaimer.
21 * 2. Redistributions in binary form must reproduce the above copyright
22 *    notice, this list of conditions and the following disclaimer in the
23 *    documentation and/or other materials provided with the distribution.
24 *
25 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
26 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
29 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
30 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
31 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
32 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
34 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
35 * SUCH DAMAGE.
36 */
37
38#include <sel4/kernel.h>
39#include <sel4/helpers.h>
40#include <sel4/sel4.h>
41#include <bmk-core/core.h>
42#include <bmk-core/memalloc.h>
43#include <bmk-core/printf.h>
44#include <bmk-core/queue.h>
45#include <bmk-core/sched.h>
46
47#include <bmk-rumpuser/rumpuser.h>
48
49#define INTR_LEVELS (BMK_MAXINTR+1)
50#define INTR_ROUTED BMK_MAXINTR
51
52/* This is arbitrary and is safe to increment */
53#define SOFT_INTRS 1
54
55struct intrhand {
56    int (*ih_fun)(void *);
57    void *ih_arg;
58
59    SLIST_ENTRY(intrhand) ih_entries;
60};
61
62SLIST_HEAD(isr_ihead, intrhand);
63static struct isr_ihead isr_ih[INTR_LEVELS];
64static struct isr_ihead isr_ih_soft[SOFT_INTRS];
65
66static volatile unsigned int isr_todo;
67static volatile unsigned int isr_todo_soft;
68static unsigned int isr_lowest = sizeof(isr_todo) * 8;
69static unsigned int isr_soft_lowest = SOFT_INTRS-1;
70
71static struct bmk_thread *isr_thread;
72
73
74static void process_handlers(isr_type_t type, unsigned int isrcopy, unsigned lowest) {
75    for (int i = lowest; isrcopy; i++) {
76        struct intrhand *ih;
77
78        bmk_assert(i < sizeof(isrcopy) * 8);
79        if ((isrcopy & (BIT(i))) == 0) {
80            continue;
81        }
82        isrcopy &= ~(BIT(i));
83        if (type == HARDWARE_INT) {
84
85            SLIST_FOREACH(ih, &isr_ih[i], ih_entries) {
86                ih->ih_fun(ih->ih_arg);
87            }
88            /* Ack seL4 interrupt now that it has been handled */
89            if (is_hw_pci_config(&env.custom_simple)) {
90                if (!config_set(CONFIG_USE_MSI_ETH)) {
91                    int error = seL4_IRQHandler_Ack(env.caps[i]);
92                    ZF_LOGF_IFERR(error, "seL4_IRQHandler_Ack failed");
93                }
94            } else {
95                env.custom_simple.ethernet_intr_config.eth_irq_acknowledge();
96            }
97        } else if (type == SOFTWARE_EVENT) {
98            SLIST_FOREACH(ih, &isr_ih_soft[i], ih_entries) {
99                ih->ih_fun(ih->ih_arg);
100            }
101        }
102    }
103
104}
105
106/* thread context we use to deliver interrupts to the rump kernel */
107static void
108doisr(void *arg)
109{
110    rumpuser__hyp.hyp_schedule();
111    rumpuser__hyp.hyp_lwproc_newlwp(0);
112    rumpuser__hyp.hyp_unschedule();
113
114    bmk_platform_splhigh();
115    for (;;) {
116        unsigned int isrcopy;
117        int nlocks = 1;
118
119        /* Process hardware interrupt handlers */
120        isrcopy = isr_todo;
121        isr_todo = 0;
122        bmk_platform_splx(0);
123        rumpkern_sched(nlocks, NULL);
124        process_handlers(HARDWARE_INT, isrcopy, isr_lowest);
125        rumpkern_unsched(&nlocks, NULL);
126
127        /* Process software event handlers */
128        bmk_platform_splhigh();
129        isrcopy = isr_todo_soft;
130        isr_todo_soft = 0;
131        bmk_platform_splx(0);
132        rumpkern_sched(nlocks, NULL);
133        process_handlers(SOFTWARE_EVENT, isrcopy, isr_soft_lowest);
134        rumpkern_unsched(&nlocks, NULL);
135
136        bmk_platform_splhigh();
137        if (isr_todo || isr_todo_soft) {
138            continue;
139        }
140
141        /* no interrupts left. block until the next one. */
142        bmk_sched_blockprepare();
143
144        bmk_platform_splx(0);
145        bmk_sched_block();
146        bmk_platform_splhigh();
147    }
148}
149
150static int alloc_number(void) {
151    static int intr_reserved = 0;
152    if (intr_reserved >= SOFT_INTRS) {
153        bmk_platform_halt("No more interrupt slots available.");
154    }
155    intr_reserved++;
156    return intr_reserved -1;
157}
158
159int
160bmk_isr_rumpkernel(int (*func)(void *), void *arg, int intr, isr_type_t type)
161{
162    if (type == SOFTWARE_EVENT) {
163        intr = alloc_number();
164    } else if (type == HARDWARE_INT) {
165
166        if (intr > sizeof(isr_todo) * 8 || intr > BMK_MAXINTR) {
167            bmk_platform_halt("bmk_isr_rumpkernel: intr");
168        }
169
170    }
171    struct intrhand *ih = bmk_xmalloc_bmk(sizeof(*ih));
172    if (!ih) {
173        bmk_platform_halt("bmk_isr_rumpkernel: xmalloc");
174    }
175
176    ih->ih_fun = func;
177    ih->ih_arg = arg;
178    if (type == HARDWARE_INT) {
179
180        SLIST_INSERT_HEAD(&isr_ih[intr], ih, ih_entries);
181        if ((unsigned)intr < isr_lowest) {
182            isr_lowest = intr;
183        }
184    } else if (type == SOFTWARE_EVENT) {
185        SLIST_INSERT_HEAD(&isr_ih_soft[intr], ih, ih_entries);
186        if ((unsigned)intr < isr_soft_lowest) {
187            isr_soft_lowest = intr;
188        }
189    }
190    return intr;
191}
192
193void
194isr(int which, int soft_which)
195{
196    /* schedule the interrupt handler */
197    isr_todo_soft |= soft_which;
198    isr_todo |= which;
199
200    bmk_sched_wake(isr_thread);
201}
202
203void
204intr_init(void)
205{
206    int i;
207
208    for (i = 0; i < INTR_LEVELS; i++) {
209        SLIST_INIT(&isr_ih[i]);
210    }
211
212    for (i = 0; i < SOFT_INTRS; i++) {
213        SLIST_INIT(&isr_ih_soft[i]);
214    }
215
216    isr_thread = bmk_sched_create("isrthr", NULL, 0, doisr, NULL, NULL, 0);
217    if (!isr_thread) {
218        bmk_platform_halt("intr_init");
219    }
220}
221