]>
Commit | Line | Data |
---|---|---|
80cabfad FB |
1 | /* |
2 | * QEMU 8259 interrupt controller emulation | |
3 | * | |
4 | * Copyright (c) 2003-2004 Fabrice Bellard | |
5 | * | |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
7 | * of this software and associated documentation files (the "Software"), to deal | |
8 | * in the Software without restriction, including without limitation the rights | |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
10 | * copies of the Software, and to permit persons to whom the Software is | |
11 | * furnished to do so, subject to the following conditions: | |
12 | * | |
13 | * The above copyright notice and this permission notice shall be included in | |
14 | * all copies or substantial portions of the Software. | |
15 | * | |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
22 | * THE SOFTWARE. | |
23 | */ | |
24 | #include <stdlib.h> | |
25 | #include <stdio.h> | |
26 | #include <stdarg.h> | |
27 | #include <string.h> | |
28 | #include <getopt.h> | |
29 | #include <inttypes.h> | |
30 | #include <unistd.h> | |
31 | #include <sys/mman.h> | |
32 | #include <fcntl.h> | |
33 | #include <signal.h> | |
34 | #include <time.h> | |
35 | #include <sys/time.h> | |
36 | #include <malloc.h> | |
37 | #include <termios.h> | |
38 | #include <sys/poll.h> | |
39 | #include <errno.h> | |
40 | #include <sys/wait.h> | |
41 | #include <netinet/in.h> | |
42 | ||
43 | #include "cpu.h" | |
44 | #include "vl.h" | |
45 | ||
46 | /* debug PIC */ | |
47 | //#define DEBUG_PIC | |
48 | ||
49 | typedef struct PicState { | |
50 | uint8_t last_irr; /* edge detection */ | |
51 | uint8_t irr; /* interrupt request register */ | |
52 | uint8_t imr; /* interrupt mask register */ | |
53 | uint8_t isr; /* interrupt service register */ | |
54 | uint8_t priority_add; /* highest irq priority */ | |
55 | uint8_t irq_base; | |
56 | uint8_t read_reg_select; | |
57 | uint8_t poll; | |
58 | uint8_t special_mask; | |
59 | uint8_t init_state; | |
60 | uint8_t auto_eoi; | |
61 | uint8_t rotate_on_auto_eoi; | |
62 | uint8_t special_fully_nested_mode; | |
63 | uint8_t init4; /* true if 4 byte init */ | |
64 | } PicState; | |
65 | ||
66 | /* 0 is master pic, 1 is slave pic */ | |
67 | PicState pics[2]; | |
68 | int pic_irq_requested; | |
69 | ||
70 | /* set irq level. If an edge is detected, then the IRR is set to 1 */ | |
71 | static inline void pic_set_irq1(PicState *s, int irq, int level) | |
72 | { | |
73 | int mask; | |
74 | mask = 1 << irq; | |
75 | if (level) { | |
76 | if ((s->last_irr & mask) == 0) | |
77 | s->irr |= mask; | |
78 | s->last_irr |= mask; | |
79 | } else { | |
80 | s->last_irr &= ~mask; | |
81 | } | |
82 | } | |
83 | ||
84 | /* return the highest priority found in mask (highest = smallest | |
85 | number). Return 8 if no irq */ | |
86 | static inline int get_priority(PicState *s, int mask) | |
87 | { | |
88 | int priority; | |
89 | if (mask == 0) | |
90 | return 8; | |
91 | priority = 0; | |
92 | while ((mask & (1 << ((priority + s->priority_add) & 7))) == 0) | |
93 | priority++; | |
94 | return priority; | |
95 | } | |
96 | ||
97 | /* return the pic wanted interrupt. return -1 if none */ | |
98 | static int pic_get_irq(PicState *s) | |
99 | { | |
100 | int mask, cur_priority, priority; | |
101 | ||
102 | mask = s->irr & ~s->imr; | |
103 | priority = get_priority(s, mask); | |
104 | if (priority == 8) | |
105 | return -1; | |
106 | /* compute current priority. If special fully nested mode on the | |
107 | master, the IRQ coming from the slave is not taken into account | |
108 | for the priority computation. */ | |
109 | mask = s->isr; | |
110 | if (s->special_fully_nested_mode && s == &pics[0]) | |
111 | mask &= ~(1 << 2); | |
112 | cur_priority = get_priority(s, mask); | |
113 | if (priority < cur_priority) { | |
114 | /* higher priority found: an irq should be generated */ | |
115 | return (priority + s->priority_add) & 7; | |
116 | } else { | |
117 | return -1; | |
118 | } | |
119 | } | |
120 | ||
121 | /* raise irq to CPU if necessary. must be called every time the active | |
122 | irq may change */ | |
123 | void pic_update_irq(void) | |
124 | { | |
125 | int irq2, irq; | |
126 | ||
127 | /* first look at slave pic */ | |
128 | irq2 = pic_get_irq(&pics[1]); | |
129 | if (irq2 >= 0) { | |
130 | /* if irq request by slave pic, signal master PIC */ | |
131 | pic_set_irq1(&pics[0], 2, 1); | |
132 | pic_set_irq1(&pics[0], 2, 0); | |
133 | } | |
134 | /* look at requested irq */ | |
135 | irq = pic_get_irq(&pics[0]); | |
136 | if (irq >= 0) { | |
137 | if (irq == 2) { | |
138 | /* from slave pic */ | |
139 | pic_irq_requested = 8 + irq2; | |
140 | } else { | |
141 | /* from master pic */ | |
142 | pic_irq_requested = irq; | |
143 | } | |
144 | #if defined(DEBUG_PIC) | |
145 | { | |
146 | int i; | |
147 | for(i = 0; i < 2; i++) { | |
148 | printf("pic%d: imr=%x irr=%x padd=%d\n", | |
149 | i, pics[i].imr, pics[i].irr, pics[i].priority_add); | |
150 | ||
151 | } | |
152 | } | |
153 | printf("pic: cpu_interrupt req=%d\n", pic_irq_requested); | |
154 | #endif | |
155 | cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HARD); | |
156 | } | |
157 | } | |
158 | ||
159 | #ifdef DEBUG_IRQ_LATENCY | |
160 | int64_t irq_time[16]; | |
161 | int64_t cpu_get_ticks(void); | |
162 | #endif | |
163 | #if defined(DEBUG_PIC) | |
164 | int irq_level[16]; | |
165 | #endif | |
166 | ||
167 | void pic_set_irq(int irq, int level) | |
168 | { | |
169 | #if defined(DEBUG_PIC) | |
170 | if (level != irq_level[irq]) { | |
171 | printf("pic_set_irq: irq=%d level=%d\n", irq, level); | |
172 | irq_level[irq] = level; | |
173 | } | |
174 | #endif | |
175 | #ifdef DEBUG_IRQ_LATENCY | |
176 | if (level) { | |
177 | irq_time[irq] = cpu_get_ticks(); | |
178 | } | |
179 | #endif | |
180 | pic_set_irq1(&pics[irq >> 3], irq & 7, level); | |
181 | pic_update_irq(); | |
182 | } | |
183 | ||
184 | /* acknowledge interrupt 'irq' */ | |
185 | static inline void pic_intack(PicState *s, int irq) | |
186 | { | |
187 | if (s->auto_eoi) { | |
188 | if (s->rotate_on_auto_eoi) | |
189 | s->priority_add = (irq + 1) & 7; | |
190 | } else { | |
191 | s->isr |= (1 << irq); | |
192 | } | |
193 | s->irr &= ~(1 << irq); | |
194 | } | |
195 | ||
196 | int cpu_x86_get_pic_interrupt(CPUState *env) | |
197 | { | |
198 | int irq, irq2, intno; | |
199 | ||
200 | /* signal the pic that the irq was acked by the CPU */ | |
201 | irq = pic_irq_requested; | |
202 | #ifdef DEBUG_IRQ_LATENCY | |
203 | printf("IRQ%d latency=%0.3fus\n", | |
204 | irq, | |
205 | (double)(cpu_get_ticks() - irq_time[irq]) * 1000000.0 / ticks_per_sec); | |
206 | #endif | |
207 | #if defined(DEBUG_PIC) | |
208 | printf("pic_interrupt: irq=%d\n", irq); | |
209 | #endif | |
210 | ||
211 | if (irq >= 8) { | |
212 | irq2 = irq & 7; | |
213 | pic_intack(&pics[1], irq2); | |
214 | irq = 2; | |
215 | intno = pics[1].irq_base + irq2; | |
216 | } else { | |
217 | intno = pics[0].irq_base + irq; | |
218 | } | |
219 | pic_intack(&pics[0], irq); | |
220 | return intno; | |
221 | } | |
222 | ||
223 | void pic_ioport_write(CPUState *env, uint32_t addr, uint32_t val) | |
224 | { | |
225 | PicState *s; | |
226 | int priority, cmd, irq; | |
227 | ||
228 | #ifdef DEBUG_PIC | |
229 | printf("pic_write: addr=0x%02x val=0x%02x\n", addr, val); | |
230 | #endif | |
231 | s = &pics[addr >> 7]; | |
232 | addr &= 1; | |
233 | if (addr == 0) { | |
234 | if (val & 0x10) { | |
235 | /* init */ | |
236 | memset(s, 0, sizeof(PicState)); | |
237 | s->init_state = 1; | |
238 | s->init4 = val & 1; | |
239 | if (val & 0x02) | |
240 | hw_error("single mode not supported"); | |
241 | if (val & 0x08) | |
242 | hw_error("level sensitive irq not supported"); | |
243 | } else if (val & 0x08) { | |
244 | if (val & 0x04) | |
245 | s->poll = 1; | |
246 | if (val & 0x02) | |
247 | s->read_reg_select = val & 1; | |
248 | if (val & 0x40) | |
249 | s->special_mask = (val >> 5) & 1; | |
250 | } else { | |
251 | cmd = val >> 5; | |
252 | switch(cmd) { | |
253 | case 0: | |
254 | case 4: | |
255 | s->rotate_on_auto_eoi = cmd >> 2; | |
256 | break; | |
257 | case 1: /* end of interrupt */ | |
258 | case 5: | |
259 | priority = get_priority(s, s->isr); | |
260 | if (priority != 8) { | |
261 | irq = (priority + s->priority_add) & 7; | |
262 | s->isr &= ~(1 << irq); | |
263 | if (cmd == 5) | |
264 | s->priority_add = (irq + 1) & 7; | |
265 | pic_update_irq(); | |
266 | } | |
267 | break; | |
268 | case 3: | |
269 | irq = val & 7; | |
270 | s->isr &= ~(1 << irq); | |
271 | pic_update_irq(); | |
272 | break; | |
273 | case 6: | |
274 | s->priority_add = (val + 1) & 7; | |
275 | pic_update_irq(); | |
276 | break; | |
277 | case 7: | |
278 | irq = val & 7; | |
279 | s->isr &= ~(1 << irq); | |
280 | s->priority_add = (irq + 1) & 7; | |
281 | pic_update_irq(); | |
282 | break; | |
283 | default: | |
284 | /* no operation */ | |
285 | break; | |
286 | } | |
287 | } | |
288 | } else { | |
289 | switch(s->init_state) { | |
290 | case 0: | |
291 | /* normal mode */ | |
292 | s->imr = val; | |
293 | pic_update_irq(); | |
294 | break; | |
295 | case 1: | |
296 | s->irq_base = val & 0xf8; | |
297 | s->init_state = 2; | |
298 | break; | |
299 | case 2: | |
300 | if (s->init4) { | |
301 | s->init_state = 3; | |
302 | } else { | |
303 | s->init_state = 0; | |
304 | } | |
305 | break; | |
306 | case 3: | |
307 | s->special_fully_nested_mode = (val >> 4) & 1; | |
308 | s->auto_eoi = (val >> 1) & 1; | |
309 | s->init_state = 0; | |
310 | break; | |
311 | } | |
312 | } | |
313 | } | |
314 | ||
315 | static uint32_t pic_poll_read (PicState *s, uint32_t addr1) | |
316 | { | |
317 | int ret; | |
318 | ||
319 | ret = pic_get_irq(s); | |
320 | if (ret >= 0) { | |
321 | if (addr1 >> 7) { | |
322 | pics[0].isr &= ~(1 << 2); | |
323 | pics[0].irr &= ~(1 << 2); | |
324 | } | |
325 | s->irr &= ~(1 << ret); | |
326 | s->isr &= ~(1 << ret); | |
327 | if (addr1 >> 7 || ret != 2) | |
328 | pic_update_irq(); | |
329 | } else { | |
330 | ret = 0x07; | |
331 | pic_update_irq(); | |
332 | } | |
333 | ||
334 | return ret; | |
335 | } | |
336 | ||
337 | uint32_t pic_ioport_read(CPUState *env, uint32_t addr1) | |
338 | { | |
339 | PicState *s; | |
340 | unsigned int addr; | |
341 | int ret; | |
342 | ||
343 | addr = addr1; | |
344 | s = &pics[addr >> 7]; | |
345 | addr &= 1; | |
346 | if (s->poll) { | |
347 | ret = pic_poll_read(s, addr1); | |
348 | s->poll = 0; | |
349 | } else { | |
350 | if (addr == 0) { | |
351 | if (s->read_reg_select) | |
352 | ret = s->isr; | |
353 | else | |
354 | ret = s->irr; | |
355 | } else { | |
356 | ret = s->imr; | |
357 | } | |
358 | } | |
359 | #ifdef DEBUG_PIC | |
360 | printf("pic_read: addr=0x%02x val=0x%02x\n", addr1, ret); | |
361 | #endif | |
362 | return ret; | |
363 | } | |
364 | ||
365 | /* memory mapped interrupt status */ | |
366 | uint32_t pic_intack_read(CPUState *env) | |
367 | { | |
368 | int ret; | |
369 | ||
370 | ret = pic_poll_read(&pics[0], 0x00); | |
371 | if (ret == 2) | |
372 | ret = pic_poll_read(&pics[1], 0x80) + 8; | |
373 | /* Prepare for ISR read */ | |
374 | pics[0].read_reg_select = 1; | |
375 | ||
376 | return ret; | |
377 | } | |
378 | ||
379 | void pic_init(void) | |
380 | { | |
381 | #if defined (TARGET_I386) || defined (TARGET_PPC) | |
382 | register_ioport_write(0x20, 2, pic_ioport_write, 1); | |
383 | register_ioport_read(0x20, 2, pic_ioport_read, 1); | |
384 | register_ioport_write(0xa0, 2, pic_ioport_write, 1); | |
385 | register_ioport_read(0xa0, 2, pic_ioport_read, 1); | |
386 | #endif | |
387 | } | |
388 |