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