1/*-
2 * Copyright (c) 2014 Antti Kantee.  All Rights Reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
14 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <hw/kernel.h>
27
28#include <bmk-core/core.h>
29#include <bmk-core/memalloc.h>
30#include <bmk-core/printf.h>
31#include <bmk-core/queue.h>
32#include <bmk-core/sched.h>
33
34#include <bmk-rumpuser/core_types.h>
35#include <bmk-rumpuser/rumpuser.h>
36
37#define INTR_LEVELS (BMK_MAXINTR+1)
38#define INTR_ROUTED BMK_MAXINTR
39
40struct intrhand {
41	int (*ih_fun)(void *);
42	void *ih_arg;
43
44	SLIST_ENTRY(intrhand) ih_entries;
45};
46
47SLIST_HEAD(isr_ihead, intrhand);
48static struct isr_ihead isr_ih[INTR_LEVELS];
49static int isr_routed[INTR_LEVELS];
50#define INTR_ROUTED_NOIDEA	0
51#define INTR_ROUTED_YES		1
52#define INTR_ROUTED_NO		2
53
54static volatile unsigned int isr_todo;
55static unsigned int isr_lowest = sizeof(isr_todo)*8;
56
57static struct bmk_thread *isr_thread;
58
59static int
60routeintr(int i)
61{
62
63#ifdef BMK_SCREW_INTERRUPT_ROUTING
64	return INTR_ROUTED;
65#else
66	return i;
67#endif
68}
69
70/* thread context we use to deliver interrupts to the rump kernel */
71static void
72doisr(void *arg)
73{
74	int i, totwork = 0;
75
76	rumpuser__hyp.hyp_schedule();
77	rumpuser__hyp.hyp_lwproc_newlwp(0);
78	rumpuser__hyp.hyp_unschedule();
79
80	splhigh();
81	for (;;) {
82		unsigned int isrcopy;
83		int nlocks = 1;
84
85		isrcopy = isr_todo;
86		isr_todo = 0;
87		spl0();
88
89		totwork |= isrcopy;
90
91		rumpkern_sched(nlocks, NULL);
92		for (i = isr_lowest; isrcopy; i++) {
93			struct intrhand *ih;
94
95			bmk_assert(i < sizeof(isrcopy)*8);
96			if ((isrcopy & (1<<i)) == 0)
97				continue;
98			isrcopy &= ~(1<<i);
99
100			if (isr_routed[i] == INTR_ROUTED_YES)
101				i = routeintr(i);
102
103			SLIST_FOREACH(ih, &isr_ih[i], ih_entries) {
104				ih->ih_fun(ih->ih_arg);
105			}
106		}
107		rumpkern_unsched(&nlocks, NULL);
108
109		splhigh();
110		if (isr_todo)
111			continue;
112
113		cpu_intr_ack(totwork);
114
115		/* no interrupts left. block until the next one. */
116		bmk_sched_blockprepare();
117
118		spl0();
119
120		bmk_sched_block();
121		totwork = 0;
122		splhigh();
123	}
124}
125
126void
127bmk_isr_rumpkernel(int (*func)(void *), void *arg, int intr, int flags)
128{
129	struct intrhand *ih;
130	int error, icheck, routedintr;
131
132	if (intr > sizeof(isr_todo)*8 || intr > BMK_MAXINTR)
133		bmk_platform_halt("bmk_isr_rumpkernel: intr");
134
135	if ((flags & ~BMK_INTR_ROUTED) != 0)
136		bmk_platform_halt("bmk_isr_rumpkernel: flags");
137
138	ih = bmk_xmalloc_bmk(sizeof(*ih));
139	if (!ih)
140		bmk_platform_halt("bmk_isr_rumpkernel: xmalloc");
141
142	/* check for conflicts */
143	if (flags & BMK_INTR_ROUTED) {
144		if (isr_routed[intr] == INTR_ROUTED_NOIDEA)
145			isr_routed[intr] = INTR_ROUTED_YES;
146		icheck = INTR_ROUTED_YES;
147		routedintr = routeintr(intr);
148	} else {
149		if (isr_routed[intr] == INTR_ROUTED_NOIDEA)
150			isr_routed[intr] = INTR_ROUTED_NO;
151		icheck = INTR_ROUTED_NO;
152		routedintr = intr;
153	}
154	if (isr_routed[intr] != icheck)
155		bmk_platform_halt("bmk_isr_rumpkernel: routed intr mismatch");
156
157	if ((error = cpu_intr_init(intr)) != 0) {
158		bmk_printf("%d", intr);
159		bmk_platform_halt("bmk_isr_rumpkernel: cpu_intr_init");
160	}
161	ih->ih_fun = func;
162	ih->ih_arg = arg;
163
164	SLIST_INSERT_HEAD(&isr_ih[routedintr], ih, ih_entries);
165	if ((unsigned)intr < isr_lowest)
166		isr_lowest = intr;
167}
168
169#if (defined(__i386__) || defined(__x86_64__))
170void serialcons_putc(int c);
171unsigned char getDebugChar(void);
172#endif
173
174void
175isr(int which)
176{
177	if ((which & 1<<4) != 0) {
178#if (defined(__i386__) || defined(__x86_64__))
179		serialcons_putc(getDebugChar());
180#endif
181		cpu_intr_ack(1<<4);
182		which &= ~(1<<4);
183	}
184
185	/* schedule the interrupt handler */
186	isr_todo |= which;
187		if (isr_todo == 0) {
188		return;
189	}
190
191	bmk_sched_wake(isr_thread);
192}
193
194void
195intr_init(void)
196{
197	int i;
198
199	for (i = 0; i < INTR_LEVELS; i++) {
200		SLIST_INIT(&isr_ih[i]);
201	}
202
203	isr_thread = bmk_sched_create("isrthr", NULL, 0, doisr, NULL, NULL, 0);
204	if (!isr_thread)
205		bmk_platform_halt("intr_init");
206}
207