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
27
# include "hw.h"
# include "pc.h"
# include "isa.h"
# include "console.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
if ( s -> special_fully_nested_mode && s == & s -> pics_state -> pics [ 0 ])
127
128
129
130
131
132
133
134
135
136
137
138
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 */
139
140
/* XXX: should not export it, but it is needed for an APIC kludge */
void pic_update_irq ( PicState2 * s )
141
142
143
144
{
int irq2 , irq ;
/* first look at slave pic */
145
irq2 = pic_get_irq ( & s -> pics [ 1 ]);
146
147
if ( irq2 >= 0 ) {
/* if irq request by slave pic, signal master PIC */
148
149
pic_set_irq1 ( & s -> pics [ 0 ], 2 , 1 );
pic_set_irq1 ( & s -> pics [ 0 ], 2 , 0 );
150
151
}
/* look at requested irq */
152
irq = pic_get_irq ( & s -> pics [ 0 ]);
153
154
155
156
157
if ( irq >= 0 ) {
# if defined ( DEBUG_PIC )
{
int i ;
for ( i = 0 ; i < 2 ; i ++ ) {
ths
authored
18 years ago
158
159
printf ( "pic%d: imr=%x irr=%x padd=%d \n " ,
i , s -> pics [ i ]. imr , s -> pics [ i ]. irr ,
160
s -> pics [ i ]. priority_add );
ths
authored
18 years ago
161
162
163
}
}
164
printf ( "pic: cpu_interrupt \n " );
165
# endif
166
qemu_irq_raise ( s -> parent_irq );
167
}
ths
authored
18 years ago
168
169
/* all targets should do this rather than acking the IRQ in the cpu */
170
# if defined ( TARGET_MIPS ) || defined ( TARGET_PPC )
ths
authored
18 years ago
171
else {
172
qemu_irq_lower ( s -> parent_irq );
ths
authored
18 years ago
173
174
}
# endif
175
176
177
178
179
180
}
# ifdef DEBUG_IRQ_LATENCY
int64_t irq_time [ 16 ];
# endif
181
static void i8259_set_irq ( void * opaque , int irq , int level )
182
{
183
184
PicState2 * s = opaque ;
185
# if defined ( DEBUG_PIC ) || defined ( DEBUG_IRQ_COUNT )
186
if ( level != irq_level [ irq ]) {
187
# if defined ( DEBUG_PIC )
188
printf ( "i8259_set_irq: irq=%d level=%d \n " , irq , level );
189
# endif
190
irq_level [ irq ] = level ;
191
192
193
194
# ifdef DEBUG_IRQ_COUNT
if ( level == 1 )
irq_count [ irq ] ++ ;
# endif
195
196
197
198
}
# endif
# ifdef DEBUG_IRQ_LATENCY
if ( level ) {
199
irq_time [ irq ] = qemu_get_clock ( vm_clock );
200
201
}
# endif
202
pic_set_irq1 ( & s -> pics [ irq >> 3 ], irq & 7 , level );
203
204
205
/* used for IOAPIC irqs */
if ( s -> alt_irq_func )
s -> alt_irq_func ( s -> alt_irq_opaque , irq , level );
206
pic_update_irq ( s );
207
208
209
210
211
212
213
214
215
216
217
}
/* 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 );
}
218
219
220
/* We don't clear a level sensitive interrupt here */
if ( ! ( s -> elcr & ( 1 << irq )))
s -> irr &= ~ ( 1 << irq );
221
222
}
223
int pic_read_irq ( PicState2 * s )
224
225
226
{
int irq , irq2 , intno ;
227
irq = pic_get_irq ( & s -> pics [ 0 ]);
228
if ( irq >= 0 ) {
229
pic_intack ( & s -> pics [ 0 ], irq );
230
if ( irq == 2 ) {
231
irq2 = pic_get_irq ( & s -> pics [ 1 ]);
232
if ( irq2 >= 0 ) {
233
pic_intack ( & s -> pics [ 1 ], irq2 );
234
235
236
237
} else {
/* spurious IRQ on slave controller */
irq2 = 7 ;
}
238
intno = s -> pics [ 1 ]. irq_base + irq2 ;
239
240
irq = irq2 + 8 ;
} else {
241
intno = s -> pics [ 0 ]. irq_base + irq ;
242
243
244
245
}
} else {
/* spurious IRQ on host controller */
irq = 7 ;
246
intno = s -> pics [ 0 ]. irq_base + irq ;
247
}
248
pic_update_irq ( s );
ths
authored
18 years ago
249
250
# ifdef DEBUG_IRQ_LATENCY
ths
authored
18 years ago
251
252
printf ( "IRQ%d latency=%0.3fus \n " ,
irq ,
253
( double )( qemu_get_clock ( vm_clock ) - irq_time [ irq ]) * 1000000 . 0 / ticks_per_sec );
254
255
256
257
258
259
260
# endif
# if defined ( DEBUG_PIC )
printf ( "pic_interrupt: irq=%d \n " , irq );
# endif
return intno ;
}
261
262
263
264
static void pic_reset ( void * opaque )
{
PicState * s = opaque ;
265
266
267
268
269
270
271
272
273
274
275
276
277
278
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
279
s -> single_mode = 0 ;
280
/* Note: ELCR is not reset */
281
282
}
283
static void pic_ioport_write ( void * opaque , uint32_t addr , uint32_t val )
284
{
285
PicState * s = opaque ;
286
int priority , cmd , irq ;
287
288
289
290
291
292
293
294
# 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 */
295
pic_reset ( s );
296
/* deassert a pending interrupt */
297
qemu_irq_lower ( s -> pics_state -> parent_irq );
298
299
s -> init_state = 1 ;
s -> init4 = val & 1 ;
ths
authored
18 years ago
300
s -> single_mode = val & 2 ;
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
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 ;
325
pic_update_irq ( s -> pics_state );
326
327
328
329
330
}
break ;
case 3 :
irq = val & 7 ;
s -> isr &= ~ ( 1 << irq );
331
pic_update_irq ( s -> pics_state );
332
333
334
break ;
case 6 :
s -> priority_add = ( val + 1 ) & 7 ;
335
pic_update_irq ( s -> pics_state );
336
337
338
339
340
break ;
case 7 :
irq = val & 7 ;
s -> isr &= ~ ( 1 << irq );
s -> priority_add = ( irq + 1 ) & 7 ;
341
pic_update_irq ( s -> pics_state );
342
343
344
345
346
347
348
349
350
351
352
break ;
default :
/* no operation */
break ;
}
}
} else {
switch ( s -> init_state ) {
case 0 :
/* normal mode */
s -> imr = val ;
353
pic_update_irq ( s -> pics_state );
354
355
356
break ;
case 1 :
s -> irq_base = val & 0xf8 ;
ths
authored
18 years ago
357
s -> init_state = s -> single_mode ? ( s -> init4 ? 3 : 0 ) : 2 ;
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
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 ) {
382
383
s -> pics_state -> pics [ 0 ]. isr &= ~ ( 1 << 2 );
s -> pics_state -> pics [ 0 ]. irr &= ~ ( 1 << 2 );
384
385
386
387
}
s -> irr &= ~ ( 1 << ret );
s -> isr &= ~ ( 1 << ret );
if ( addr1 >> 7 || ret != 2 )
388
pic_update_irq ( s -> pics_state );
389
390
} else {
ret = 0x07 ;
391
pic_update_irq ( s -> pics_state );
392
393
394
395
396
}
return ret ;
}
397
static uint32_t pic_ioport_read ( void * opaque , uint32_t addr1 )
398
{
399
PicState * s = opaque ;
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
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 */
425
426
/* XXX: may be the same than pic_read_irq() */
uint32_t pic_intack_read ( PicState2 * s )
427
428
429
{
int ret ;
430
ret = pic_poll_read ( & s -> pics [ 0 ], 0x00 );
431
if ( ret == 2 )
432
ret = pic_poll_read ( & s -> pics [ 1 ], 0x80 ) + 8 ;
433
/* Prepare for ISR read */
434
s -> pics [ 0 ]. read_reg_select = 1 ;
ths
authored
18 years ago
435
436
437
438
return ret ;
}
439
440
441
442
443
444
445
446
447
448
449
450
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 ;
}
451
452
453
static void pic_save ( QEMUFile * f , void * opaque )
{
PicState * s = opaque ;
ths
authored
18 years ago
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
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
469
qemu_put_8s ( f , & s -> single_mode );
470
qemu_put_8s ( f , & s -> elcr );
471
472
473
474
475
}
static int pic_load ( QEMUFile * f , void * opaque , int version_id )
{
PicState * s = opaque ;
ths
authored
18 years ago
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
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
494
qemu_get_8s ( f , & s -> single_mode );
495
qemu_get_8s ( f , & s -> elcr );
496
497
498
499
return 0 ;
}
/* XXX: add generic master/slave system */
500
static void pic_init1 ( int io_addr , int elcr_addr , PicState * s )
501
502
503
{
register_ioport_write ( io_addr , 2 , 1 , pic_ioport_write , s );
register_ioport_read ( io_addr , 2 , 1 , pic_ioport_read , s );
504
505
506
507
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 );
}
508
register_savevm ( "i8259" , io_addr , 1 , pic_save , pic_load , s );
509
qemu_register_reset ( pic_reset , s );
510
511
}
512
513
514
515
void pic_info ( void )
{
int i ;
PicState * s ;
ths
authored
18 years ago
516
517
518
if ( ! isa_pic )
return ;
519
520
for ( i = 0 ; i < 2 ; i ++ ) {
521
s = & isa_pic -> pics [ i ];
522
term_printf ( "pic%d: irr=%02x imr=%02x isr=%02x hprio=%d irq_base=%02x rr_sel=%d elcr=%02x fnm=%d \n " ,
ths
authored
18 years ago
523
524
i , s -> irr , s -> imr , s -> isr , s -> priority_add ,
s -> irq_base , s -> read_reg_select , s -> elcr ,
525
s -> special_fully_nested_mode );
526
527
528
}
}
529
530
531
532
533
534
535
536
537
538
539
540
void irq_info ( void )
{
# ifndef DEBUG_IRQ_COUNT
term_printf ( "irq statistic code not compiled. \n " );
# else
int i ;
int64_t count ;
term_printf ( "IRQ statistics: \n " );
for ( i = 0 ; i < 16 ; i ++ ) {
count = irq_count [ i ];
if ( count > 0 )
541
term_printf ( "%2d: %" PRId64 " \n " , i , count );
542
543
544
}
# endif
}
545
546
qemu_irq * i8259_init ( qemu_irq parent_irq )
547
{
548
PicState2 * s ;
549
550
551
552
553
554
555
556
s = qemu_mallocz ( sizeof ( PicState2 ));
if ( ! s )
return NULL ;
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 ;
557
s -> parent_irq = parent_irq ;
558
559
s -> pics [ 0 ]. pics_state = s ;
s -> pics [ 1 ]. pics_state = s ;
560
561
isa_pic = s ;
return qemu_allocate_irqs ( i8259_set_irq , s , 16 );
562
}
563
564
565
566
567
568
569
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 ;
}