1 |
|
|
/* $OpenBSD: i8259.c,v 1.15 2017/09/18 00:05:15 dlg Exp $ */ |
2 |
|
|
/* |
3 |
|
|
* Copyright (c) 2016 Mike Larkin <mlarkin@openbsd.org> |
4 |
|
|
* |
5 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
6 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
7 |
|
|
* copyright notice and this permission notice appear in all copies. |
8 |
|
|
* |
9 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
10 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
11 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
12 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
13 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
14 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
15 |
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
16 |
|
|
*/ |
17 |
|
|
|
18 |
|
|
#include <string.h> |
19 |
|
|
|
20 |
|
|
#include <sys/types.h> |
21 |
|
|
|
22 |
|
|
#include <dev/isa/isareg.h> |
23 |
|
|
|
24 |
|
|
#include <machine/vmmvar.h> |
25 |
|
|
|
26 |
|
|
#include <unistd.h> |
27 |
|
|
#include <pthread.h> |
28 |
|
|
#include "proc.h" |
29 |
|
|
#include "i8259.h" |
30 |
|
|
#include "vmm.h" |
31 |
|
|
#include "atomicio.h" |
32 |
|
|
#include "vmd.h" |
33 |
|
|
|
34 |
|
|
struct i8259 { |
35 |
|
|
uint8_t irr; |
36 |
|
|
uint8_t imr; |
37 |
|
|
uint8_t isr; |
38 |
|
|
uint8_t smm; |
39 |
|
|
uint8_t poll; |
40 |
|
|
uint8_t cur_icw; |
41 |
|
|
uint8_t init_mode; |
42 |
|
|
uint8_t vec; |
43 |
|
|
uint8_t irq_conn; |
44 |
|
|
uint8_t next_ocw_read; |
45 |
|
|
uint8_t auto_eoi; |
46 |
|
|
uint8_t rotate_auto_eoi; |
47 |
|
|
uint8_t lowest_pri; |
48 |
|
|
uint8_t asserted; |
49 |
|
|
}; |
50 |
|
|
|
51 |
|
|
#define PIC_IRR 0 |
52 |
|
|
#define PIC_ISR 1 |
53 |
|
|
|
54 |
|
|
/* Master and slave PICs */ |
55 |
|
|
struct i8259 pics[2]; |
56 |
|
|
pthread_mutex_t pic_mtx; |
57 |
|
|
|
58 |
|
|
/* |
59 |
|
|
* i8259_pic_name |
60 |
|
|
* |
61 |
|
|
* Converts a pic ID (MASTER, SLAVE} to a string, suitable for printing in |
62 |
|
|
* debug or log messages. |
63 |
|
|
* |
64 |
|
|
* Parameters: |
65 |
|
|
* picid: PIC ID |
66 |
|
|
* |
67 |
|
|
* Return value: |
68 |
|
|
* string representation of the PIC ID supplied |
69 |
|
|
*/ |
70 |
|
|
static const char * |
71 |
|
|
i8259_pic_name(uint8_t picid) |
72 |
|
|
{ |
73 |
|
|
switch (picid) { |
74 |
|
|
case MASTER: return "master"; |
75 |
|
|
case SLAVE: return "slave"; |
76 |
|
|
default: return "unknown"; |
77 |
|
|
} |
78 |
|
|
} |
79 |
|
|
|
80 |
|
|
/* |
81 |
|
|
* i8259_init |
82 |
|
|
* |
83 |
|
|
* Initialize the emulated i8259 PIC. |
84 |
|
|
*/ |
85 |
|
|
void |
86 |
|
|
i8259_init(void) |
87 |
|
|
{ |
88 |
|
|
memset(&pics, 0, sizeof(pics)); |
89 |
|
|
pics[MASTER].cur_icw = 1; |
90 |
|
|
pics[SLAVE].cur_icw = 1; |
91 |
|
|
|
92 |
|
|
if (pthread_mutex_init(&pic_mtx, NULL) != 0) |
93 |
|
|
fatalx("unable to create pic mutex"); |
94 |
|
|
} |
95 |
|
|
|
96 |
|
|
/* |
97 |
|
|
* i8259_is_pending |
98 |
|
|
* |
99 |
|
|
* Determine if an IRQ is pending on either the slave or master PIC. |
100 |
|
|
* |
101 |
|
|
* Return Values: |
102 |
|
|
* 1 if an IRQ (any IRQ) is pending, 0 otherwise |
103 |
|
|
*/ |
104 |
|
|
uint8_t |
105 |
|
|
i8259_is_pending(void) |
106 |
|
|
{ |
107 |
|
|
uint8_t master_pending; |
108 |
|
|
uint8_t slave_pending; |
109 |
|
|
|
110 |
|
|
mutex_lock(&pic_mtx); |
111 |
|
|
master_pending = pics[MASTER].irr & ~(pics[MASTER].imr | (1 << 2)); |
112 |
|
|
slave_pending = pics[SLAVE].irr & ~pics[SLAVE].imr; |
113 |
|
|
mutex_unlock(&pic_mtx); |
114 |
|
|
|
115 |
|
|
return (master_pending || slave_pending); |
116 |
|
|
} |
117 |
|
|
|
118 |
|
|
/* |
119 |
|
|
* i8259_ack |
120 |
|
|
* |
121 |
|
|
* This function is called when the vcpu exits and is ready to accept an |
122 |
|
|
* interrupt. |
123 |
|
|
* |
124 |
|
|
* Return values: |
125 |
|
|
* interrupt vector to inject, 0xFFFF if no irq pending |
126 |
|
|
*/ |
127 |
|
|
uint16_t |
128 |
|
|
i8259_ack(void) |
129 |
|
|
{ |
130 |
|
|
uint8_t high_prio_m, high_prio_s; |
131 |
|
|
uint8_t i; |
132 |
|
|
uint16_t ret; |
133 |
|
|
|
134 |
|
|
ret = 0xFFFF; |
135 |
|
|
|
136 |
|
|
mutex_lock(&pic_mtx); |
137 |
|
|
|
138 |
|
|
if (pics[MASTER].asserted == 0 && pics[SLAVE].asserted == 0) { |
139 |
|
|
log_warnx("%s: i8259 ack without assert?", __func__); |
140 |
|
|
goto ret; |
141 |
|
|
} |
142 |
|
|
|
143 |
|
|
high_prio_m = pics[MASTER].lowest_pri + 1; |
144 |
|
|
if (high_prio_m > 7) |
145 |
|
|
high_prio_m = 0; |
146 |
|
|
|
147 |
|
|
high_prio_s = pics[SLAVE].lowest_pri + 1; |
148 |
|
|
if (high_prio_s > 7) |
149 |
|
|
high_prio_s = 0; |
150 |
|
|
|
151 |
|
|
i = high_prio_m; |
152 |
|
|
do { |
153 |
|
|
if ((pics[MASTER].irr & (1 << i)) && i != 2 && |
154 |
|
|
!(pics[MASTER].imr & (1 << i))) { |
155 |
|
|
/* Master PIC has highest prio and ready IRQ */ |
156 |
|
|
pics[MASTER].irr &= ~(1 << i); |
157 |
|
|
pics[MASTER].isr |= (1 << i); |
158 |
|
|
|
159 |
|
|
if (pics[MASTER].irr == 0) |
160 |
|
|
pics[MASTER].asserted = 0; |
161 |
|
|
|
162 |
|
|
ret = i + pics[MASTER].vec; |
163 |
|
|
goto ret; |
164 |
|
|
} |
165 |
|
|
|
166 |
|
|
i++; |
167 |
|
|
|
168 |
|
|
if (i > 7) |
169 |
|
|
i = 0; |
170 |
|
|
|
171 |
|
|
} while (i != high_prio_m); |
172 |
|
|
|
173 |
|
|
i = high_prio_s; |
174 |
|
|
do { |
175 |
|
|
if ((pics[SLAVE].irr & (1 << i)) && |
176 |
|
|
!(pics[SLAVE].imr & (1 << i))) { |
177 |
|
|
/* Slave PIC has highest prio and ready IRQ */ |
178 |
|
|
pics[SLAVE].irr &= ~(1 << i); |
179 |
|
|
pics[MASTER].irr &= ~(1 << 2); |
180 |
|
|
|
181 |
|
|
pics[SLAVE].isr |= (1 << i); |
182 |
|
|
pics[MASTER].isr |= (1 << 2); |
183 |
|
|
|
184 |
|
|
if (pics[SLAVE].irr == 0) { |
185 |
|
|
pics[SLAVE].asserted = 0; |
186 |
|
|
if (pics[MASTER].irr == 0) |
187 |
|
|
pics[MASTER].asserted = 0; |
188 |
|
|
} |
189 |
|
|
|
190 |
|
|
ret = i + pics[SLAVE].vec; |
191 |
|
|
goto ret; |
192 |
|
|
} |
193 |
|
|
|
194 |
|
|
i++; |
195 |
|
|
|
196 |
|
|
if (i > 7) |
197 |
|
|
i = 0; |
198 |
|
|
} while (i != high_prio_s); |
199 |
|
|
|
200 |
|
|
log_warnx("%s: ack without pending irq?", __func__); |
201 |
|
|
ret: |
202 |
|
|
mutex_unlock(&pic_mtx); |
203 |
|
|
return (ret); |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
/* |
207 |
|
|
* i8259_assert_irq |
208 |
|
|
* |
209 |
|
|
* Asserts the IRQ specified |
210 |
|
|
* |
211 |
|
|
* Parameters: |
212 |
|
|
* irq: the IRQ to assert |
213 |
|
|
*/ |
214 |
|
|
void |
215 |
|
|
i8259_assert_irq(uint8_t irq) |
216 |
|
|
{ |
217 |
|
|
mutex_lock(&pic_mtx); |
218 |
|
|
if (irq <= 7) { |
219 |
|
|
if (!ISSET(pics[MASTER].imr, 1 << irq)) { |
220 |
|
|
SET(pics[MASTER].irr, 1 << irq); |
221 |
|
|
pics[MASTER].asserted = 1; |
222 |
|
|
} |
223 |
|
|
} else { |
224 |
|
|
irq -= 8; |
225 |
|
|
if (!ISSET(pics[SLAVE].imr, 1 << irq)) { |
226 |
|
|
SET(pics[SLAVE].irr, irq); |
227 |
|
|
pics[SLAVE].asserted = 1; |
228 |
|
|
|
229 |
|
|
/* Assert cascade IRQ on master PIC */ |
230 |
|
|
SET(pics[MASTER].irr, 1 << 2); |
231 |
|
|
pics[MASTER].asserted = 1; |
232 |
|
|
} |
233 |
|
|
} |
234 |
|
|
mutex_unlock(&pic_mtx); |
235 |
|
|
} |
236 |
|
|
|
237 |
|
|
/* |
238 |
|
|
* i8259_deassert_irq |
239 |
|
|
* |
240 |
|
|
* Deasserts the IRQ specified |
241 |
|
|
* |
242 |
|
|
* Parameters: |
243 |
|
|
* irq: the IRQ to deassert |
244 |
|
|
*/ |
245 |
|
|
void |
246 |
|
|
i8259_deassert_irq(uint8_t irq) |
247 |
|
|
{ |
248 |
|
|
mutex_lock(&pic_mtx); |
249 |
|
|
if (irq <= 7) |
250 |
|
|
CLR(pics[MASTER].irr, 1 << irq); |
251 |
|
|
else { |
252 |
|
|
irq -= 8; |
253 |
|
|
CLR(pics[SLAVE].irr, 1 << irq); |
254 |
|
|
|
255 |
|
|
/* Deassert cascade IRQ on master if no IRQs on slave */ |
256 |
|
|
if (pics[SLAVE].irr == 0) |
257 |
|
|
CLR(pics[MASTER].irr, 1 << 2); |
258 |
|
|
} |
259 |
|
|
mutex_unlock(&pic_mtx); |
260 |
|
|
} |
261 |
|
|
|
262 |
|
|
/* |
263 |
|
|
* i8259_write_datareg |
264 |
|
|
* |
265 |
|
|
* Write to a specified data register in the emulated PIC during PIC |
266 |
|
|
* initialization. The data write follows the state model in the i8259 (in |
267 |
|
|
* other words, data is expected to be written in a specific order). |
268 |
|
|
* |
269 |
|
|
* Parameters: |
270 |
|
|
* n: PIC to write to (MASTER/SLAVE) |
271 |
|
|
* data: data to write |
272 |
|
|
*/ |
273 |
|
|
static void |
274 |
|
|
i8259_write_datareg(uint8_t n, uint8_t data) |
275 |
|
|
{ |
276 |
|
|
struct i8259 *pic = &pics[n]; |
277 |
|
|
|
278 |
|
|
if (pic->init_mode == 1) { |
279 |
|
|
if (pic->cur_icw == 2) { |
280 |
|
|
/* Set vector */ |
281 |
|
|
log_debug("%s: %s pic, reset IRQ vector to 0x%x", |
282 |
|
|
__func__, i8259_pic_name(n), data); |
283 |
|
|
pic->vec = data; |
284 |
|
|
} else if (pic->cur_icw == 3) { |
285 |
|
|
/* Set IRQ interconnects */ |
286 |
|
|
if (n == SLAVE && (data & 0xf8)) { |
287 |
|
|
log_warnx("%s: %s pic invalid icw2 0x%x", |
288 |
|
|
__func__, i8259_pic_name(n), data); |
289 |
|
|
return; |
290 |
|
|
} |
291 |
|
|
pic->irq_conn = data; |
292 |
|
|
} else if (pic->cur_icw == 4) { |
293 |
|
|
if (!(data & ICW4_UP)) { |
294 |
|
|
log_warnx("%s: %s pic init error: x86 bit " |
295 |
|
|
"clear", __func__, i8259_pic_name(n)); |
296 |
|
|
return; |
297 |
|
|
} |
298 |
|
|
|
299 |
|
|
if (data & ICW4_AEOI) { |
300 |
|
|
log_warnx("%s: %s pic: aeoi mode set", |
301 |
|
|
__func__, i8259_pic_name(n)); |
302 |
|
|
pic->auto_eoi = 1; |
303 |
|
|
return; |
304 |
|
|
} |
305 |
|
|
|
306 |
|
|
if (data & ICW4_MS) { |
307 |
|
|
log_warnx("%s: %s pic init error: M/S mode", |
308 |
|
|
__func__, i8259_pic_name(n)); |
309 |
|
|
return; |
310 |
|
|
} |
311 |
|
|
|
312 |
|
|
if (data & ICW4_BUF) { |
313 |
|
|
log_warnx("%s: %s pic init error: buf mode", |
314 |
|
|
__func__, i8259_pic_name(n)); |
315 |
|
|
return; |
316 |
|
|
} |
317 |
|
|
|
318 |
|
|
if (data & 0xe0) { |
319 |
|
|
log_warnx("%s: %s pic init error: invalid icw4 " |
320 |
|
|
" 0x%x", __func__, i8259_pic_name(n), data); |
321 |
|
|
return; |
322 |
|
|
} |
323 |
|
|
} |
324 |
|
|
|
325 |
|
|
pic->cur_icw++; |
326 |
|
|
if (pic->cur_icw == 5) { |
327 |
|
|
pic->cur_icw = 1; |
328 |
|
|
pic->init_mode = 0; |
329 |
|
|
} |
330 |
|
|
} else |
331 |
|
|
pic->imr = data; |
332 |
|
|
} |
333 |
|
|
|
334 |
|
|
/* |
335 |
|
|
* i8259_specific_eoi |
336 |
|
|
* |
337 |
|
|
* Handles specific end of interrupt commands |
338 |
|
|
* |
339 |
|
|
* Parameters: |
340 |
|
|
* n: PIC to deliver this EOI to |
341 |
|
|
* data: interrupt to EOI |
342 |
|
|
*/ |
343 |
|
|
static void |
344 |
|
|
i8259_specific_eoi(uint8_t n, uint8_t data) |
345 |
|
|
{ |
346 |
|
|
if (!(pics[n].isr & (1 << (data & 0x7)))) { |
347 |
|
|
log_warnx("%s: %s pic specific eoi irq %d while not in" |
348 |
|
|
" service", __func__, i8259_pic_name(n), (data & 0x7)); |
349 |
|
|
} |
350 |
|
|
|
351 |
|
|
pics[n].isr &= ~(1 << (data & 0x7)); |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
/* |
355 |
|
|
* i8259_nonspecific_eoi |
356 |
|
|
* |
357 |
|
|
* Handles nonspecific end of interrupt commands |
358 |
|
|
* XXX not implemented |
359 |
|
|
*/ |
360 |
|
|
static void |
361 |
|
|
i8259_nonspecific_eoi(uint8_t n, uint8_t data) |
362 |
|
|
{ |
363 |
|
|
int i = 0; |
364 |
|
|
|
365 |
|
|
while (i < 8) { |
366 |
|
|
if ((pics[n].isr & (1 << (i & 0x7)))) { |
367 |
|
|
i8259_specific_eoi(n, i); |
368 |
|
|
return; |
369 |
|
|
} |
370 |
|
|
i++; |
371 |
|
|
} |
372 |
|
|
} |
373 |
|
|
|
374 |
|
|
/* |
375 |
|
|
* i8259_rotate_priority |
376 |
|
|
* |
377 |
|
|
* Rotates the interrupt priority on the specified PIC |
378 |
|
|
* |
379 |
|
|
* Parameters: |
380 |
|
|
* n: PIC whose priority should be rotated |
381 |
|
|
*/ |
382 |
|
|
static void |
383 |
|
|
i8259_rotate_priority(uint8_t n) |
384 |
|
|
{ |
385 |
|
|
pics[n].lowest_pri++; |
386 |
|
|
if (pics[n].lowest_pri > 7) |
387 |
|
|
pics[n].lowest_pri = 0; |
388 |
|
|
} |
389 |
|
|
|
390 |
|
|
/* |
391 |
|
|
* i8259_write_cmdreg |
392 |
|
|
* |
393 |
|
|
* Write to the PIC command register |
394 |
|
|
* |
395 |
|
|
* Parameters: |
396 |
|
|
* n: PIC whose command register should be written to |
397 |
|
|
* data: data to write |
398 |
|
|
*/ |
399 |
|
|
static void |
400 |
|
|
i8259_write_cmdreg(uint8_t n, uint8_t data) |
401 |
|
|
{ |
402 |
|
|
struct i8259 *pic = &pics[n]; |
403 |
|
|
|
404 |
|
|
if (data & ICW1_INIT) { |
405 |
|
|
/* Validate init params */ |
406 |
|
|
if (!(data & ICW1_ICW4)) { |
407 |
|
|
log_warnx("%s: %s pic init error: no ICW4 request", |
408 |
|
|
__func__, i8259_pic_name(n)); |
409 |
|
|
return; |
410 |
|
|
} |
411 |
|
|
|
412 |
|
|
if (data & (ICW1_IVA1 | ICW1_IVA2 | ICW1_IVA3)) { |
413 |
|
|
log_warnx("%s: %s pic init error: IVA specified", |
414 |
|
|
__func__, i8259_pic_name(n)); |
415 |
|
|
return; |
416 |
|
|
} |
417 |
|
|
|
418 |
|
|
if (data & ICW1_SNGL) { |
419 |
|
|
log_warnx("%s: %s pic init error: single pic mode", |
420 |
|
|
__func__, i8259_pic_name(n)); |
421 |
|
|
return; |
422 |
|
|
} |
423 |
|
|
|
424 |
|
|
if (data & ICW1_ADI) { |
425 |
|
|
log_warnx("%s: %s pic init error: address interval", |
426 |
|
|
__func__, i8259_pic_name(n)); |
427 |
|
|
return; |
428 |
|
|
} |
429 |
|
|
|
430 |
|
|
if (data & ICW1_LTIM) { |
431 |
|
|
log_warnx("%s: %s pic init error: level trigger mode", |
432 |
|
|
__func__, i8259_pic_name(n)); |
433 |
|
|
return; |
434 |
|
|
} |
435 |
|
|
|
436 |
|
|
pic->init_mode = 1; |
437 |
|
|
pic->cur_icw = 2; |
438 |
|
|
pic->imr = 0; |
439 |
|
|
pic->isr = 0; |
440 |
|
|
pic->irr = 0; |
441 |
|
|
pic->asserted = 0; |
442 |
|
|
pic->lowest_pri = 7; |
443 |
|
|
pic->rotate_auto_eoi = 0; |
444 |
|
|
return; |
445 |
|
|
} else if (data & OCW_SELECT) { |
446 |
|
|
/* OCW3 */ |
447 |
|
|
if (data & OCW3_ACTION) { |
448 |
|
|
if (data & OCW3_RR) { |
449 |
|
|
if (data & OCW3_RIS) |
450 |
|
|
pic->next_ocw_read = PIC_ISR; |
451 |
|
|
else |
452 |
|
|
pic->next_ocw_read = PIC_IRR; |
453 |
|
|
} |
454 |
|
|
} |
455 |
|
|
|
456 |
|
|
if (data & OCW3_SMACTION) { |
457 |
|
|
if (data & OCW3_SMM) { |
458 |
|
|
pic->smm = 1; |
459 |
|
|
/* XXX update intr here */ |
460 |
|
|
} else |
461 |
|
|
pic->smm = 0; |
462 |
|
|
} |
463 |
|
|
|
464 |
|
|
if (data & OCW3_POLL) { |
465 |
|
|
pic->poll = 1; |
466 |
|
|
/* XXX update intr here */ |
467 |
|
|
} |
468 |
|
|
|
469 |
|
|
return; |
470 |
|
|
} else { |
471 |
|
|
/* OCW2 */ |
472 |
|
|
if (data & OCW2_EOI) { |
473 |
|
|
/* |
474 |
|
|
* An EOI command was received. It could be one of |
475 |
|
|
* several different varieties: |
476 |
|
|
* |
477 |
|
|
* Nonspecific EOI (0x20) |
478 |
|
|
* Specific EOI (0x60..0x67) |
479 |
|
|
* Nonspecific EOI + rotate (0xA0) |
480 |
|
|
* Specific EOI + rotate (0xE0..0xE7) |
481 |
|
|
*/ |
482 |
|
|
switch (data) { |
483 |
|
|
case OCW2_EOI: |
484 |
|
|
i8259_nonspecific_eoi(n, data); |
485 |
|
|
break; |
486 |
|
|
case OCW2_SEOI ... OCW2_SEOI + 7: |
487 |
|
|
i8259_specific_eoi(n, data); |
488 |
|
|
break; |
489 |
|
|
case OCW2_ROTATE_NSEOI: |
490 |
|
|
i8259_nonspecific_eoi(n, data); |
491 |
|
|
i8259_rotate_priority(n); |
492 |
|
|
break; |
493 |
|
|
case OCW2_ROTATE_SEOI ... OCW2_ROTATE_SEOI + 7: |
494 |
|
|
i8259_specific_eoi(n, data); |
495 |
|
|
i8259_rotate_priority(n); |
496 |
|
|
break; |
497 |
|
|
} |
498 |
|
|
return; |
499 |
|
|
} |
500 |
|
|
|
501 |
|
|
if (data == OCW2_NOP) |
502 |
|
|
return; |
503 |
|
|
|
504 |
|
|
if ((data & OCW2_SET_LOWPRIO) == OCW2_SET_LOWPRIO) { |
505 |
|
|
/* Set low priority value (bits 0-2) */ |
506 |
|
|
pic->lowest_pri = data & 0x7; |
507 |
|
|
return; |
508 |
|
|
} |
509 |
|
|
|
510 |
|
|
if (data == OCW2_ROTATE_AEOI_CLEAR) { |
511 |
|
|
pic->rotate_auto_eoi = 0; |
512 |
|
|
return; |
513 |
|
|
} |
514 |
|
|
|
515 |
|
|
if (data == OCW2_ROTATE_AEOI_SET) { |
516 |
|
|
pic->rotate_auto_eoi = 1; |
517 |
|
|
return; |
518 |
|
|
} |
519 |
|
|
|
520 |
|
|
return; |
521 |
|
|
} |
522 |
|
|
} |
523 |
|
|
|
524 |
|
|
/* |
525 |
|
|
* i8259_read_datareg |
526 |
|
|
* |
527 |
|
|
* Read the PIC's IMR |
528 |
|
|
* |
529 |
|
|
* Parameters: |
530 |
|
|
* n: PIC to read |
531 |
|
|
* |
532 |
|
|
* Return value: |
533 |
|
|
* selected PIC's IMR |
534 |
|
|
*/ |
535 |
|
|
static uint8_t |
536 |
|
|
i8259_read_datareg(uint8_t n) |
537 |
|
|
{ |
538 |
|
|
struct i8259 *pic = &pics[n]; |
539 |
|
|
|
540 |
|
|
return (pic->imr); |
541 |
|
|
} |
542 |
|
|
|
543 |
|
|
/* |
544 |
|
|
* i8259_read_cmdreg |
545 |
|
|
* |
546 |
|
|
* Read the PIC's IRR or ISR, depending on the current PIC mode (value |
547 |
|
|
* selected via the OCW3 command) |
548 |
|
|
* |
549 |
|
|
* Parameters: |
550 |
|
|
* n: PIC to read |
551 |
|
|
* |
552 |
|
|
* Return value: |
553 |
|
|
* selected PIC's IRR/ISR |
554 |
|
|
*/ |
555 |
|
|
static uint8_t |
556 |
|
|
i8259_read_cmdreg(uint8_t n) |
557 |
|
|
{ |
558 |
|
|
struct i8259 *pic = &pics[n]; |
559 |
|
|
|
560 |
|
|
if (pic->next_ocw_read == PIC_IRR) |
561 |
|
|
return (pic->irr); |
562 |
|
|
else if (pic->next_ocw_read == PIC_ISR) |
563 |
|
|
return (pic->isr); |
564 |
|
|
|
565 |
|
|
fatal("%s: invalid PIC config during cmdreg read", __func__); |
566 |
|
|
} |
567 |
|
|
|
568 |
|
|
/* |
569 |
|
|
* i8259_io_write |
570 |
|
|
* |
571 |
|
|
* Callback to handle write I/O to the emulated PICs in the VM |
572 |
|
|
* |
573 |
|
|
* Parameters: |
574 |
|
|
* vei: vm exit info for this I/O |
575 |
|
|
*/ |
576 |
|
|
static void |
577 |
|
|
i8259_io_write(union vm_exit *vei) |
578 |
|
|
{ |
579 |
|
|
uint16_t port = vei->vei.vei_port; |
580 |
|
|
uint32_t data; |
581 |
|
|
uint8_t n = 0; |
582 |
|
|
|
583 |
|
|
get_input_data(vei, &data); |
584 |
|
|
|
585 |
|
|
switch (port) { |
586 |
|
|
case IO_ICU1: |
587 |
|
|
case IO_ICU1 + 1: |
588 |
|
|
n = MASTER; |
589 |
|
|
break; |
590 |
|
|
case IO_ICU2: |
591 |
|
|
case IO_ICU2 + 1: |
592 |
|
|
n = SLAVE; |
593 |
|
|
break; |
594 |
|
|
default: |
595 |
|
|
fatal("%s: invalid port 0x%x", __func__, port); |
596 |
|
|
} |
597 |
|
|
|
598 |
|
|
mutex_lock(&pic_mtx); |
599 |
|
|
if (port == IO_ICU1 + 1 || port == IO_ICU2 + 1) |
600 |
|
|
i8259_write_datareg(n, data); |
601 |
|
|
else |
602 |
|
|
i8259_write_cmdreg(n, data); |
603 |
|
|
mutex_unlock(&pic_mtx); |
604 |
|
|
} |
605 |
|
|
|
606 |
|
|
/* |
607 |
|
|
* i8259_io_read |
608 |
|
|
* |
609 |
|
|
* Callback to handle read I/O to the emulated PICs in the VM |
610 |
|
|
* |
611 |
|
|
* Parameters: |
612 |
|
|
* vei: vm exit info for this I/O |
613 |
|
|
* |
614 |
|
|
* Return values: |
615 |
|
|
* data that was read, based on the port information in 'vei' |
616 |
|
|
*/ |
617 |
|
|
static uint8_t |
618 |
|
|
i8259_io_read(union vm_exit *vei) |
619 |
|
|
{ |
620 |
|
|
uint16_t port = vei->vei.vei_port; |
621 |
|
|
uint8_t n = 0; |
622 |
|
|
uint8_t rv; |
623 |
|
|
|
624 |
|
|
switch (port) { |
625 |
|
|
case IO_ICU1: |
626 |
|
|
case IO_ICU1 + 1: |
627 |
|
|
n = MASTER; |
628 |
|
|
break; |
629 |
|
|
case IO_ICU2: |
630 |
|
|
case IO_ICU2 + 1: |
631 |
|
|
n = SLAVE; |
632 |
|
|
break; |
633 |
|
|
default: |
634 |
|
|
fatal("%s: invalid port 0x%x", __func__, port); |
635 |
|
|
} |
636 |
|
|
|
637 |
|
|
mutex_lock(&pic_mtx); |
638 |
|
|
if (port == IO_ICU1 + 1 || port == IO_ICU2 + 1) |
639 |
|
|
rv = i8259_read_datareg(n); |
640 |
|
|
else |
641 |
|
|
rv = i8259_read_cmdreg(n); |
642 |
|
|
mutex_unlock(&pic_mtx); |
643 |
|
|
|
644 |
|
|
return (rv); |
645 |
|
|
} |
646 |
|
|
|
647 |
|
|
/* |
648 |
|
|
* vcpu_exit_i8259 |
649 |
|
|
* |
650 |
|
|
* Top level exit handler for PIC operations |
651 |
|
|
* |
652 |
|
|
* Parameters: |
653 |
|
|
* vrp: VCPU run parameters (contains exit information) for this PIC operation |
654 |
|
|
* |
655 |
|
|
* Return value: |
656 |
|
|
* Always 0xFF (PIC read/writes don't generate interrupts directly) |
657 |
|
|
*/ |
658 |
|
|
uint8_t |
659 |
|
|
vcpu_exit_i8259(struct vm_run_params *vrp) |
660 |
|
|
{ |
661 |
|
|
union vm_exit *vei = vrp->vrp_exit; |
662 |
|
|
|
663 |
|
|
if (vei->vei.vei_dir == VEI_DIR_OUT) { |
664 |
|
|
i8259_io_write(vei); |
665 |
|
|
} else { |
666 |
|
|
set_return_data(vei, i8259_io_read(vei)); |
667 |
|
|
} |
668 |
|
|
|
669 |
|
|
return (0xFF); |
670 |
|
|
} |
671 |
|
|
|
672 |
|
|
int |
673 |
|
|
i8259_dump(int fd) |
674 |
|
|
{ |
675 |
|
|
log_debug("%s: sending PIC", __func__); |
676 |
|
|
if (atomicio(vwrite, fd, &pics, sizeof(pics)) != sizeof(pics)) { |
677 |
|
|
log_warnx("%s: error writing PIC to fd", __func__); |
678 |
|
|
return (-1); |
679 |
|
|
} |
680 |
|
|
return (0); |
681 |
|
|
} |
682 |
|
|
|
683 |
|
|
int |
684 |
|
|
i8259_restore(int fd) |
685 |
|
|
{ |
686 |
|
|
log_debug("%s: restoring PIC", __func__); |
687 |
|
|
if (atomicio(read, fd, &pics, sizeof(pics)) != sizeof(pics)) { |
688 |
|
|
log_warnx("%s: error reading PIC from fd", __func__); |
689 |
|
|
return (-1); |
690 |
|
|
} |
691 |
|
|
|
692 |
|
|
if (pthread_mutex_init(&pic_mtx, NULL) != 0) |
693 |
|
|
fatalx("unable to create pic mutex"); |
694 |
|
|
|
695 |
|
|
return (0); |
696 |
|
|
} |