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