1 /*
2 * Copyright (C) 2006-2019 Anders Gavare. 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 are met:
6 *
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 * 3. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 * SUCH DAMAGE.
26 *
27 *
28 * The interrupt subsystem.
29 *
30 * Interrupts have a "path", e.g. "machine[0].cpu.5". A device which
31 * wishes to cause this interrupt needs to connect to it.
32 *
33 * The possible interrupt paths are registered by CPUs, interrupt controllers,
34 * etc., that have a way of receiving interrupt requests. The physical
35 * version of an interrupt path is usually a "pin" on the CPU, or similar.
36 *
37 * Once connected, the interrupt can be asserted or deasserted.
38 *
39 * For examples on how it is used, see the various devices in src/devices/.
40 */
41
42 #include <stdio.h>
43 #include <stdlib.h>
44 #include <string.h>
45
46 #include "interrupt.h"
47 #include "misc.h"
48
49
50 /* #define INTERRUPT_DEBUG */
51
52
53 struct interrupt_handler {
54 struct interrupt templ;
55 int nr_of_exclusive_users;
56 int nr_of_nonexclusive_users;
57 };
58
59
60 static int nr_of_interrupt_handlers = 0;
61 static struct interrupt_handler *interrupt_handlers = NULL;
62
63
64 /*
65 * Dummy interrupt assert/deassert for "no interrupt" interrupts:
66 */
no_interrupt_assert(struct interrupt * i)67 static void no_interrupt_assert(struct interrupt *i) { }
no_interrupt_deassert(struct interrupt * i)68 static void no_interrupt_deassert(struct interrupt *i) { }
69
70
71 /*
72 * interrupt_handler_register():
73 *
74 * Add an interrupt handler to the interrupt subsystem. The template
75 * needs to have all members set.
76 *
77 * Name is of the form "machine[0].cpu[0].irq[3].isa[14]" etc.
78 *
79 * If there already is a handler with this name, the emulator aborts.
80 */
interrupt_handler_register(struct interrupt * templ)81 void interrupt_handler_register(struct interrupt *templ)
82 {
83 int i;
84
85 #ifdef INTERRUPT_DEBUG
86 printf("interrupt_handler_register(\"%s\")\n", templ->name);
87 #endif
88
89 /* See if the name is already registered: */
90 for (i=0; i<nr_of_interrupt_handlers; i++) {
91 if (strcmp(templ->name,
92 interrupt_handlers[i].templ.name) != 0)
93 continue;
94
95 fatal("\ninterrupt_handler_register(): An interrupt handler"
96 " using the name '%s' is already registered.\n",
97 templ->name);
98 exit(1);
99 }
100
101 nr_of_interrupt_handlers ++;
102 CHECK_ALLOCATION(interrupt_handlers =
103 (struct interrupt_handler *) realloc(interrupt_handlers,
104 nr_of_interrupt_handlers * sizeof(struct interrupt_handler)));
105
106 interrupt_handlers[nr_of_interrupt_handlers-1].templ = *templ;
107 CHECK_ALLOCATION(interrupt_handlers[nr_of_interrupt_handlers-1].
108 templ.name = strdup(templ->name));
109 }
110
111
112 /*
113 * interrupt_handler_remove():
114 *
115 * Remove an interrupt handler from the interrupt subsystem. If there are
116 * still connected users of this interrupt, then an error message is printed
117 * and the emulator aborts.
118 */
interrupt_handler_remove(const char * name)119 void interrupt_handler_remove(const char *name)
120 {
121 int i;
122
123 #ifdef INTERRUPT_DEBUG
124 printf("interrupt_handler_remove(\"%s\")\n", name);
125 #endif
126
127 for (i=0; i<nr_of_interrupt_handlers; i++) {
128 if (strcmp(name, interrupt_handlers[i].templ.name) != 0)
129 continue;
130
131 /*
132 * Remove handler i, and return:
133 */
134 if (interrupt_handlers[i].nr_of_exclusive_users > 0 ||
135 interrupt_handlers[i].nr_of_nonexclusive_users > 0) {
136 fatal("interrupt_handler_remove(): Attempt to "
137 "remove interrupt handler '%s' which has %i "
138 "exclusive and %i non-exclusive users. Aborting.\n",
139 name, interrupt_handlers[i].nr_of_exclusive_users,
140 interrupt_handlers[i].nr_of_nonexclusive_users);
141 exit(1);
142 }
143
144 if (i != nr_of_interrupt_handlers-1)
145 memcpy(&interrupt_handlers[i],
146 &interrupt_handlers[i + 1],
147 nr_of_interrupt_handlers - i - 1);
148
149 nr_of_interrupt_handlers --;
150
151 return;
152 }
153
154 fatal("interrupt_handler_remove(): '%s' not found? Aborting.\n", name);
155 exit(1);
156 }
157
158
159 /*
160 * interrupt_handler_lookup():
161 *
162 * Scans the list of registered interrupt handlers for a given name. If the
163 * name is found, the template is filled with valid data, and 1 is returned.
164 * If the name is not found, 0 is returned.
165 */
interrupt_handler_lookup(const char * name,struct interrupt * templ)166 int interrupt_handler_lookup(const char *name, struct interrupt *templ)
167 {
168 int i;
169
170 #ifdef INTERRUPT_DEBUG
171 printf("interrupt_handler_lookup(\"%s\")\n", name);
172 #endif
173
174 if (name[0] == '\0') {
175 /* No interrupt: */
176 memset(templ, 0, sizeof(struct interrupt));
177 templ->interrupt_assert = no_interrupt_assert;
178 templ->interrupt_deassert = no_interrupt_deassert;
179 }
180
181 for (i=0; i<nr_of_interrupt_handlers; i++) {
182 if (strcmp(name, interrupt_handlers[i].templ.name) != 0)
183 continue;
184
185 *templ = interrupt_handlers[i].templ;
186 return 1;
187 }
188
189 /* The 'if' is an ugly hack to prevent a Compaq CC warning. */
190 if (i >= nr_of_interrupt_handlers) {
191 printf("interrupt_handler_lookup(\"%s\") failed.\n", name);
192
193 printf("Available handler paths are:\n");
194 for (i=0; i<nr_of_interrupt_handlers; i++)
195 printf(" %s\n", interrupt_handlers[i].templ.name);
196
197 printf("Aborting.\n");
198 abort();
199 }
200
201 return 0;
202 }
203
204
205 /*
206 * interrupt_connect():
207 *
208 * Increases the exclusive or nonexclusive nr or users of an interrupt.
209 */
interrupt_connect(struct interrupt * in,int exclusive)210 void interrupt_connect(struct interrupt *in, int exclusive)
211 {
212 int i;
213
214 #ifdef INTERRUPT_DEBUG
215 printf("interrupt_connect(\"%s\")\n", in->name);
216 #endif
217
218 if (in->name == NULL || in->name[0] == '\0')
219 return;
220
221 for (i=0; i<nr_of_interrupt_handlers; i++) {
222 if (strcmp(in->name, interrupt_handlers[i].templ.name) != 0)
223 continue;
224
225 if (exclusive) {
226 interrupt_handlers[i].nr_of_exclusive_users ++;
227 if (interrupt_handlers[i].nr_of_exclusive_users > 1) {
228 fatal("Fatal error in interrupt_connect(): "
229 "more than 1 exclusive user. Dumping "
230 "core for backtrace.\n");
231 abort();
232 }
233 } else {
234 interrupt_handlers[i].nr_of_nonexclusive_users ++;
235 }
236
237 return;
238 }
239
240 fatal("Internal error in interrupt_connect(): name '%s' not "
241 "found? Dumping core for debugging.\n", in->name);
242 abort();
243 }
244
245
246 /*
247 * interrupt_disconnect():
248 *
249 * Decreases the exclusive or nonexclusive nr or users of an interrupt.
250 */
interrupt_disconnect(struct interrupt * in,int exclusive)251 void interrupt_disconnect(struct interrupt *in, int exclusive)
252 {
253 int i;
254
255 if (in->name == NULL || in->name[0] == '\0')
256 return;
257
258 for (i=0; i<nr_of_interrupt_handlers; i++) {
259 if (strcmp(in->name, interrupt_handlers[i].templ.name) != 0)
260 continue;
261
262 if (exclusive) {
263 interrupt_handlers[i].nr_of_exclusive_users --;
264 if (interrupt_handlers[i].nr_of_exclusive_users < 0) {
265 fatal("Fatal error in interrupt_disconnect():"
266 "nr of exclusive users < 0?\n");
267 exit(1);
268 }
269 } else {
270 interrupt_handlers[i].nr_of_nonexclusive_users --;
271 if (interrupt_handlers[i].nr_of_nonexclusive_users<0) {
272 fatal("Fatal error in interrupt_disconnect():"
273 "nr of non-exclusive users < 0?\n");
274 exit(1);
275 }
276 }
277
278 return;
279 }
280
281 fatal("Internal error in interrupt_disconnect(): name '%s' not "
282 "found?\n", in->name);
283 exit(1);
284 }
285
286
287