1
2
/*
* QEMU 8259 interrupt controller emulation
ths
authored
18 years ago
3
*
4
* Copyright ( c ) 2003 - 2004 Fabrice Bellard
ths
authored
18 years ago
5
*
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
* Permission is hereby granted , free of charge , to any person obtaining a copy
* of this software and associated documentation files ( the "Software" ), to deal
* in the Software without restriction , including without limitation the rights
* to use , copy , modify , merge , publish , distribute , sublicense , and / or sell
* copies of the Software , and to permit persons to whom the Software is
* furnished to do so , subject to the following conditions :
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software .
*
* THE SOFTWARE IS PROVIDED "AS IS" , WITHOUT WARRANTY OF ANY KIND , EXPRESS OR
* IMPLIED , INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY ,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT . IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM , DAMAGES OR OTHER
* LIABILITY , WHETHER IN AN ACTION OF CONTRACT , TORT OR OTHERWISE , ARISING FROM ,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE .
*/
24
25
26
# include "hw.h"
# include "pc.h"
# include "isa.h"
27
# include "monitor.h"
28
29
30
31
/* debug PIC */
// # define DEBUG_PIC
32
// # define DEBUG_IRQ_LATENCY
33
// # define DEBUG_IRQ_COUNT
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
typedef struct PicState {
uint8_t last_irr ; /* edge detection */
uint8_t irr ; /* interrupt request register */
uint8_t imr ; /* interrupt mask register */
uint8_t isr ; /* interrupt service register */
uint8_t priority_add ; /* highest irq priority */
uint8_t irq_base ;
uint8_t read_reg_select ;
uint8_t poll ;
uint8_t special_mask ;
uint8_t init_state ;
uint8_t auto_eoi ;
uint8_t rotate_on_auto_eoi ;
uint8_t special_fully_nested_mode ;
uint8_t init4 ; /* true if 4 byte init */
ths
authored
18 years ago
50
uint8_t single_mode ; /* true if slave pic is not initialized */
51
52
uint8_t elcr ; /* PIIX edge/trigger selection*/
uint8_t elcr_mask ;
53
PicState2 * pics_state ;
54
55
} PicState ;
56
57
58
59
struct PicState2 {
/* 0 is master pic, 1 is slave pic */
/* XXX: better separation between the two pics */
PicState pics [ 2 ];
60
qemu_irq parent_irq ;
61
void * irq_request_opaque ;
62
63
64
/* IOAPIC callback support */
SetIRQFunc * alt_irq_func ;
void * alt_irq_opaque ;
65
};
66
67
68
69
70
71
72
73
# if defined ( DEBUG_PIC ) || defined ( DEBUG_IRQ_COUNT )
static int irq_level [ 16 ];
# endif
# ifdef DEBUG_IRQ_COUNT
static uint64_t irq_count [ 16 ];
# endif
74
75
76
77
78
/* set irq level. If an edge is detected, then the IRR is set to 1 */
static inline void pic_set_irq1 ( PicState * s , int irq , int level )
{
int mask ;
mask = 1 << irq ;
79
80
81
if ( s -> elcr & mask ) {
/* level triggered */
if ( level ) {
82
s -> irr |= mask ;
83
84
85
86
87
s -> last_irr |= mask ;
} else {
s -> irr &= ~ mask ;
s -> last_irr &= ~ mask ;
}
88
} else {
89
90
91
92
93
94
95
96
/* edge triggered */
if ( level ) {
if (( s -> last_irr & mask ) == 0 )
s -> irr |= mask ;
s -> last_irr |= mask ;
} else {
s -> last_irr &= ~ mask ;
}
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
}
}
/* return the highest priority found in mask ( highest = smallest
number ). Return 8 if no irq */
static inline int get_priority ( PicState * s , int mask )
{
int priority ;
if ( mask == 0 )
return 8 ;
priority = 0 ;
while (( mask & ( 1 << (( priority + s -> priority_add ) & 7 ))) == 0 )
priority ++ ;
return priority ;
}
/* return the pic wanted interrupt. return -1 if none */
static int pic_get_irq ( PicState * s )
{
int mask , cur_priority , priority ;
mask = s -> irr & ~ s -> imr ;
priority = get_priority ( s , mask );
if ( priority == 8 )
return - 1 ;
/* compute current priority . If special fully nested mode on the
master , the IRQ coming from the slave is not taken into account
for the priority computation . */
mask = s -> isr ;
126
127
if ( s -> special_mask )
mask &= ~ s -> imr ;
128
if ( s -> special_fully_nested_mode && s == & s -> pics_state -> pics [ 0 ])
129
130
131
132
133
134
135
136
137
138
139
140
mask &= ~ ( 1 << 2 );
cur_priority = get_priority ( s , mask );
if ( priority < cur_priority ) {
/* higher priority found: an irq should be generated */
return ( priority + s -> priority_add ) & 7 ;
} else {
return - 1 ;
}
}
/* raise irq to CPU if necessary . must be called every time the active
irq may change */
141
142
/* XXX: should not export it, but it is needed for an APIC kludge */
void pic_update_irq ( PicState2 * s )
143
144
145
146
{
int irq2 , irq ;
/* first look at slave pic */
147
irq2 = pic_get_irq ( & s -> pics [ 1 ]);
148
149
if ( irq2 >= 0 ) {
/* if irq request by slave pic, signal master PIC */
150
151
pic_set_irq1 ( & s -> pics [ 0 ], 2 , 1 );
pic_set_irq1 ( & s -> pics [ 0 ], 2 , 0 );
152
153
}
/* look at requested irq */
154
irq = pic_get_irq ( & s -> pics [ 0 ]);
155
156
157
158
159
if ( irq >= 0 ) {
# if defined ( DEBUG_PIC )
{
int i ;
for ( i = 0 ; i < 2 ; i ++ ) {
ths
authored
18 years ago
160
161
printf ( "pic%d: imr=%x irr=%x padd=%d \n " ,
i , s -> pics [ i ]. imr , s -> pics [ i ]. irr ,
162
s -> pics [ i ]. priority_add );
ths
authored
18 years ago
163
164
165
}
}
166
printf ( "pic: cpu_interrupt \n " );
167
# endif
168
qemu_irq_raise ( s -> parent_irq );
169
}
ths
authored
18 years ago
170
171
/* all targets should do this rather than acking the IRQ in the cpu */
172
# if defined ( TARGET_MIPS ) || defined ( TARGET_PPC )
ths
authored
18 years ago
173
else {
174
qemu_irq_lower ( s -> parent_irq );
ths
authored
18 years ago
175
176
}
# endif
177
178
179
180
181
182
}
# ifdef DEBUG_IRQ_LATENCY
int64_t irq_time [ 16 ];
# endif
183
static void i8259_set_irq ( void * opaque , int irq , int level )
184
{
185
186
PicState2 * s = opaque ;
187
# if defined ( DEBUG_PIC ) || defined ( DEBUG_IRQ_COUNT )
188
if ( level != irq_level [ irq ]) {
189
# if defined ( DEBUG_PIC )
190
printf ( "i8259_set_irq: irq=%d level=%d \n " , irq , level );
191
# endif
192
irq_level [ irq ] = level ;
193
194
195
196
# ifdef DEBUG_IRQ_COUNT
if ( level == 1 )
irq_count [ irq ] ++ ;
# endif
197
198
199
200
}
# endif
# ifdef DEBUG_IRQ_LATENCY
if ( level ) {
201
irq_time [ irq ] = qemu_get_clock ( vm_clock );
202
203
}
# endif
204
pic_set_irq1 ( & s -> pics [ irq >> 3 ], irq & 7 , level );
205
206
207
/* used for IOAPIC irqs */
if ( s -> alt_irq_func )
s -> alt_irq_func ( s -> alt_irq_opaque , irq , level );
208
pic_update_irq ( s );
209
210
211
212
213
214
215
216
217
218
219
}
/* acknowledge interrupt 'irq' */
static inline void pic_intack ( PicState * s , int irq )
{
if ( s -> auto_eoi ) {
if ( s -> rotate_on_auto_eoi )
s -> priority_add = ( irq + 1 ) & 7 ;
} else {
s -> isr |= ( 1 << irq );
}
220
221
222
/* We don't clear a level sensitive interrupt here */
if ( ! ( s -> elcr & ( 1 << irq )))
s -> irr &= ~ ( 1 << irq );
223
224
}
225
int pic_read_irq ( PicState2 * s )
226
227
228
{
int irq , irq2 , intno ;
229
irq = pic_get_irq ( & s -> pics [ 0 ]);
230
if ( irq >= 0 ) {
231
pic_intack ( & s -> pics [ 0 ], irq );
232
if ( irq == 2 ) {
233
irq2 = pic_get_irq ( & s -> pics [ 1 ]);
234
if ( irq2 >= 0 ) {
235
pic_intack ( & s -> pics [ 1 ], irq2 );
236
237
238
239
} else {
/* spurious IRQ on slave controller */
irq2 = 7 ;
}
240
intno = s -> pics [ 1 ]. irq_base + irq2 ;
241
242
irq = irq2 + 8 ;
} else {
243
intno = s -> pics [ 0 ]. irq_base + irq ;
244
245
246
247
}
} else {
/* spurious IRQ on host controller */
irq = 7 ;
248
intno = s -> pics [ 0 ]. irq_base + irq ;
249
}
250
pic_update_irq ( s );
ths
authored
18 years ago
251
252
# ifdef DEBUG_IRQ_LATENCY
ths
authored
18 years ago
253
254
printf ( "IRQ%d latency=%0.3fus \n " ,
irq ,
255
( double )( qemu_get_clock ( vm_clock ) - irq_time [ irq ]) * 1000000 . 0 / ticks_per_sec );
256
257
258
259
260
261
262
# endif
# if defined ( DEBUG_PIC )
printf ( "pic_interrupt: irq=%d \n " , irq );
# endif
return intno ;
}
263
264
265
266
static void pic_reset ( void * opaque )
{
PicState * s = opaque ;
267
268
269
270
271
272
273
274
275
276
277
278
279
280
s -> last_irr = 0 ;
s -> irr = 0 ;
s -> imr = 0 ;
s -> isr = 0 ;
s -> priority_add = 0 ;
s -> irq_base = 0 ;
s -> read_reg_select = 0 ;
s -> poll = 0 ;
s -> special_mask = 0 ;
s -> init_state = 0 ;
s -> auto_eoi = 0 ;
s -> rotate_on_auto_eoi = 0 ;
s -> special_fully_nested_mode = 0 ;
s -> init4 = 0 ;
ths
authored
18 years ago
281
s -> single_mode = 0 ;
282
/* Note: ELCR is not reset */
283
284
}
285
static void pic_ioport_write ( void * opaque , uint32_t addr , uint32_t val )
286
{
287
PicState * s = opaque ;
288
int priority , cmd , irq ;
289
290
291
292
293
294
295
296
# ifdef DEBUG_PIC
printf ( "pic_write: addr=0x%02x val=0x%02x \n " , addr , val );
# endif
addr &= 1 ;
if ( addr == 0 ) {
if ( val & 0x10 ) {
/* init */
297
pic_reset ( s );
298
/* deassert a pending interrupt */
299
qemu_irq_lower ( s -> pics_state -> parent_irq );
300
301
s -> init_state = 1 ;
s -> init4 = val & 1 ;
ths
authored
18 years ago
302
s -> single_mode = val & 2 ;
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
if ( val & 0x08 )
hw_error ( "level sensitive irq not supported" );
} else if ( val & 0x08 ) {
if ( val & 0x04 )
s -> poll = 1 ;
if ( val & 0x02 )
s -> read_reg_select = val & 1 ;
if ( val & 0x40 )
s -> special_mask = ( val >> 5 ) & 1 ;
} else {
cmd = val >> 5 ;
switch ( cmd ) {
case 0 :
case 4 :
s -> rotate_on_auto_eoi = cmd >> 2 ;
break ;
case 1 : /* end of interrupt */
case 5 :
priority = get_priority ( s , s -> isr );
if ( priority != 8 ) {
irq = ( priority + s -> priority_add ) & 7 ;
s -> isr &= ~ ( 1 << irq );
if ( cmd == 5 )
s -> priority_add = ( irq + 1 ) & 7 ;
327
pic_update_irq ( s -> pics_state );
328
329
330
331
332
}
break ;
case 3 :
irq = val & 7 ;
s -> isr &= ~ ( 1 << irq );
333
pic_update_irq ( s -> pics_state );
334
335
336
break ;
case 6 :
s -> priority_add = ( val + 1 ) & 7 ;
337
pic_update_irq ( s -> pics_state );
338
339
340
341
342
break ;
case 7 :
irq = val & 7 ;
s -> isr &= ~ ( 1 << irq );
s -> priority_add = ( irq + 1 ) & 7 ;
343
pic_update_irq ( s -> pics_state );
344
345
346
347
348
349
350
351
352
353
354
break ;
default :
/* no operation */
break ;
}
}
} else {
switch ( s -> init_state ) {
case 0 :
/* normal mode */
s -> imr = val ;
355
pic_update_irq ( s -> pics_state );
356
357
358
break ;
case 1 :
s -> irq_base = val & 0xf8 ;
ths
authored
18 years ago
359
s -> init_state = s -> single_mode ? ( s -> init4 ? 3 : 0 ) : 2 ;
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
break ;
case 2 :
if ( s -> init4 ) {
s -> init_state = 3 ;
} else {
s -> init_state = 0 ;
}
break ;
case 3 :
s -> special_fully_nested_mode = ( val >> 4 ) & 1 ;
s -> auto_eoi = ( val >> 1 ) & 1 ;
s -> init_state = 0 ;
break ;
}
}
}
static uint32_t pic_poll_read ( PicState * s , uint32_t addr1 )
{
int ret ;
ret = pic_get_irq ( s );
if ( ret >= 0 ) {
if ( addr1 >> 7 ) {
384
385
s -> pics_state -> pics [ 0 ]. isr &= ~ ( 1 << 2 );
s -> pics_state -> pics [ 0 ]. irr &= ~ ( 1 << 2 );
386
387
388
389
}
s -> irr &= ~ ( 1 << ret );
s -> isr &= ~ ( 1 << ret );
if ( addr1 >> 7 || ret != 2 )
390
pic_update_irq ( s -> pics_state );
391
392
} else {
ret = 0x07 ;
393
pic_update_irq ( s -> pics_state );
394
395
396
397
398
}
return ret ;
}
399
static uint32_t pic_ioport_read ( void * opaque , uint32_t addr1 )
400
{
401
PicState * s = opaque ;
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
unsigned int addr ;
int ret ;
addr = addr1 ;
addr &= 1 ;
if ( s -> poll ) {
ret = pic_poll_read ( s , addr1 );
s -> poll = 0 ;
} else {
if ( addr == 0 ) {
if ( s -> read_reg_select )
ret = s -> isr ;
else
ret = s -> irr ;
} else {
ret = s -> imr ;
}
}
# ifdef DEBUG_PIC
printf ( "pic_read: addr=0x%02x val=0x%02x \n " , addr1 , ret );
# endif
return ret ;
}
/* memory mapped interrupt status */
427
428
/* XXX: may be the same than pic_read_irq() */
uint32_t pic_intack_read ( PicState2 * s )
429
430
431
{
int ret ;
432
ret = pic_poll_read ( & s -> pics [ 0 ], 0x00 );
433
if ( ret == 2 )
434
ret = pic_poll_read ( & s -> pics [ 1 ], 0x80 ) + 8 ;
435
/* Prepare for ISR read */
436
s -> pics [ 0 ]. read_reg_select = 1 ;
ths
authored
18 years ago
437
438
439
440
return ret ;
}
441
442
443
444
445
446
447
448
449
450
451
452
static void elcr_ioport_write ( void * opaque , uint32_t addr , uint32_t val )
{
PicState * s = opaque ;
s -> elcr = val & s -> elcr_mask ;
}
static uint32_t elcr_ioport_read ( void * opaque , uint32_t addr1 )
{
PicState * s = opaque ;
return s -> elcr ;
}
453
454
455
static void pic_save ( QEMUFile * f , void * opaque )
{
PicState * s = opaque ;
ths
authored
18 years ago
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
qemu_put_8s ( f , & s -> last_irr );
qemu_put_8s ( f , & s -> irr );
qemu_put_8s ( f , & s -> imr );
qemu_put_8s ( f , & s -> isr );
qemu_put_8s ( f , & s -> priority_add );
qemu_put_8s ( f , & s -> irq_base );
qemu_put_8s ( f , & s -> read_reg_select );
qemu_put_8s ( f , & s -> poll );
qemu_put_8s ( f , & s -> special_mask );
qemu_put_8s ( f , & s -> init_state );
qemu_put_8s ( f , & s -> auto_eoi );
qemu_put_8s ( f , & s -> rotate_on_auto_eoi );
qemu_put_8s ( f , & s -> special_fully_nested_mode );
qemu_put_8s ( f , & s -> init4 );
ths
authored
18 years ago
471
qemu_put_8s ( f , & s -> single_mode );
472
qemu_put_8s ( f , & s -> elcr );
473
474
475
476
477
}
static int pic_load ( QEMUFile * f , void * opaque , int version_id )
{
PicState * s = opaque ;
ths
authored
18 years ago
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
if ( version_id != 1 )
return - EINVAL ;
qemu_get_8s ( f , & s -> last_irr );
qemu_get_8s ( f , & s -> irr );
qemu_get_8s ( f , & s -> imr );
qemu_get_8s ( f , & s -> isr );
qemu_get_8s ( f , & s -> priority_add );
qemu_get_8s ( f , & s -> irq_base );
qemu_get_8s ( f , & s -> read_reg_select );
qemu_get_8s ( f , & s -> poll );
qemu_get_8s ( f , & s -> special_mask );
qemu_get_8s ( f , & s -> init_state );
qemu_get_8s ( f , & s -> auto_eoi );
qemu_get_8s ( f , & s -> rotate_on_auto_eoi );
qemu_get_8s ( f , & s -> special_fully_nested_mode );
qemu_get_8s ( f , & s -> init4 );
ths
authored
18 years ago
496
qemu_get_8s ( f , & s -> single_mode );
497
qemu_get_8s ( f , & s -> elcr );
498
499
500
501
return 0 ;
}
/* XXX: add generic master/slave system */
502
static void pic_init1 ( int io_addr , int elcr_addr , PicState * s )
503
504
505
{
register_ioport_write ( io_addr , 2 , 1 , pic_ioport_write , s );
register_ioport_read ( io_addr , 2 , 1 , pic_ioport_read , s );
506
507
508
509
if ( elcr_addr >= 0 ) {
register_ioport_write ( elcr_addr , 1 , 1 , elcr_ioport_write , s );
register_ioport_read ( elcr_addr , 1 , 1 , elcr_ioport_read , s );
}
510
register_savevm ( "i8259" , io_addr , 1 , pic_save , pic_load , s );
511
qemu_register_reset ( pic_reset , s );
512
513
}
514
void pic_info ( Monitor * mon )
515
516
517
{
int i ;
PicState * s ;
ths
authored
18 years ago
518
519
520
if ( ! isa_pic )
return ;
521
522
for ( i = 0 ; i < 2 ; i ++ ) {
523
s = & isa_pic -> pics [ i ];
524
525
526
527
528
monitor_printf ( mon , "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d "
"irq_base=%02x rr_sel=%d elcr=%02x fnm=%d \n " ,
i , s -> irr , s -> imr , s -> isr , s -> priority_add ,
s -> irq_base , s -> read_reg_select , s -> elcr ,
s -> special_fully_nested_mode );
529
530
531
}
}
532
void irq_info ( Monitor * mon )
533
534
{
# ifndef DEBUG_IRQ_COUNT
535
monitor_printf ( mon , "irq statistic code not compiled. \n " );
536
537
538
539
# else
int i ;
int64_t count ;
540
monitor_printf ( mon , "IRQ statistics: \n " );
541
542
543
for ( i = 0 ; i < 16 ; i ++ ) {
count = irq_count [ i ];
if ( count > 0 )
544
monitor_printf ( mon , "%2d: %" PRId64 " \n " , i , count );
545
546
547
}
# endif
}
548
549
qemu_irq * i8259_init ( qemu_irq parent_irq )
550
{
551
PicState2 * s ;
552
553
554
555
556
557
s = qemu_mallocz ( sizeof ( PicState2 ));
pic_init1 ( 0x20 , 0x4d0 , & s -> pics [ 0 ]);
pic_init1 ( 0xa0 , 0x4d1 , & s -> pics [ 1 ]);
s -> pics [ 0 ]. elcr_mask = 0xf8 ;
s -> pics [ 1 ]. elcr_mask = 0xde ;
558
s -> parent_irq = parent_irq ;
559
560
s -> pics [ 0 ]. pics_state = s ;
s -> pics [ 1 ]. pics_state = s ;
561
562
isa_pic = s ;
return qemu_allocate_irqs ( i8259_set_irq , s , 16 );
563
}
564
565
566
567
568
569
570
void pic_set_alt_irq_func ( PicState2 * s , SetIRQFunc * alt_irq_func ,
void * alt_irq_opaque )
{
s -> alt_irq_func = alt_irq_func ;
s -> alt_irq_opaque = alt_irq_opaque ;
}