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