Route IOAPIC interrupts via ISA bus
[qemu] / hw / ioapic.c
1 /*
2  *  ioapic.c IOAPIC emulation logic
3  *
4  *  Copyright (c) 2004-2005 Fabrice Bellard
5  *
6  *  Split the ioapic logic from apic.c
7  *  Xiantao Zhang <xiantao.zhang@intel.com>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
21  */
22
23 #include "hw.h"
24 #include "pc.h"
25 #include "qemu-timer.h"
26 #include "host-utils.h"
27
28 //#define DEBUG_IOAPIC
29
30 #define IOAPIC_NUM_PINS                 0x18
31 #define IOAPIC_LVT_MASKED               (1<<16)
32
33 #define IOAPIC_TRIGGER_EDGE             0
34 #define IOAPIC_TRIGGER_LEVEL            1
35
36 /*io{apic,sapic} delivery mode*/
37 #define IOAPIC_DM_FIXED                 0x0
38 #define IOAPIC_DM_LOWEST_PRIORITY       0x1
39 #define IOAPIC_DM_PMI                   0x2
40 #define IOAPIC_DM_NMI                   0x4
41 #define IOAPIC_DM_INIT                  0x5
42 #define IOAPIC_DM_SIPI                  0x5
43 #define IOAPIC_DM_EXTINT                0x7
44
45 struct IOAPICState {
46     uint8_t id;
47     uint8_t ioregsel;
48
49     uint32_t irr;
50     uint64_t ioredtbl[IOAPIC_NUM_PINS];
51 };
52
53 static void ioapic_service(IOAPICState *s)
54 {
55     uint8_t i;
56     uint8_t trig_mode;
57     uint8_t vector;
58     uint8_t delivery_mode;
59     uint32_t mask;
60     uint64_t entry;
61     uint8_t dest;
62     uint8_t dest_mode;
63     uint8_t polarity;
64
65     for (i = 0; i < IOAPIC_NUM_PINS; i++) {
66         mask = 1 << i;
67         if (s->irr & mask) {
68             entry = s->ioredtbl[i];
69             if (!(entry & IOAPIC_LVT_MASKED)) {
70                 trig_mode = ((entry >> 15) & 1);
71                 dest = entry >> 56;
72                 dest_mode = (entry >> 11) & 1;
73                 delivery_mode = (entry >> 8) & 7;
74                 polarity = (entry >> 13) & 1;
75                 if (trig_mode == IOAPIC_TRIGGER_EDGE)
76                     s->irr &= ~mask;
77                 if (delivery_mode == IOAPIC_DM_EXTINT)
78                     vector = pic_read_irq(isa_pic);
79                 else
80                     vector = entry & 0xff;
81
82                 apic_deliver_irq(dest, dest_mode, delivery_mode,
83                                  vector, polarity, trig_mode);
84             }
85         }
86     }
87 }
88
89 void ioapic_set_irq(void *opaque, int vector, int level)
90 {
91     IOAPICState *s = opaque;
92
93     /* ISA IRQs map to GSI 1-1 except for IRQ0 which maps
94      * to GSI 2.  GSI maps to ioapic 1-1.  This is not
95      * the cleanest way of doing it but it should work. */
96
97     if (vector == 0)
98         vector = 2;
99
100     if (vector >= 0 && vector < IOAPIC_NUM_PINS) {
101         uint32_t mask = 1 << vector;
102         uint64_t entry = s->ioredtbl[vector];
103
104         if ((entry >> 15) & 1) {
105             /* level triggered */
106             if (level) {
107                 s->irr |= mask;
108                 ioapic_service(s);
109             } else {
110                 s->irr &= ~mask;
111             }
112         } else {
113             /* edge triggered */
114             if (level) {
115                 s->irr |= mask;
116                 ioapic_service(s);
117             }
118         }
119     }
120 }
121
122 static uint32_t ioapic_mem_readl(void *opaque, target_phys_addr_t addr)
123 {
124     IOAPICState *s = opaque;
125     int index;
126     uint32_t val = 0;
127
128     addr &= 0xff;
129     if (addr == 0x00) {
130         val = s->ioregsel;
131     } else if (addr == 0x10) {
132         switch (s->ioregsel) {
133             case 0x00:
134                 val = s->id << 24;
135                 break;
136             case 0x01:
137                 val = 0x11 | ((IOAPIC_NUM_PINS - 1) << 16); /* version 0x11 */
138                 break;
139             case 0x02:
140                 val = 0;
141                 break;
142             default:
143                 index = (s->ioregsel - 0x10) >> 1;
144                 if (index >= 0 && index < IOAPIC_NUM_PINS) {
145                     if (s->ioregsel & 1)
146                         val = s->ioredtbl[index] >> 32;
147                     else
148                         val = s->ioredtbl[index] & 0xffffffff;
149                 }
150         }
151 #ifdef DEBUG_IOAPIC
152         printf("I/O APIC read: %08x = %08x\n", s->ioregsel, val);
153 #endif
154     }
155     return val;
156 }
157
158 static void ioapic_mem_writel(void *opaque, target_phys_addr_t addr, uint32_t val)
159 {
160     IOAPICState *s = opaque;
161     int index;
162
163     addr &= 0xff;
164     if (addr == 0x00)  {
165         s->ioregsel = val;
166         return;
167     } else if (addr == 0x10) {
168 #ifdef DEBUG_IOAPIC
169         printf("I/O APIC write: %08x = %08x\n", s->ioregsel, val);
170 #endif
171         switch (s->ioregsel) {
172             case 0x00:
173                 s->id = (val >> 24) & 0xff;
174                 return;
175             case 0x01:
176             case 0x02:
177                 return;
178             default:
179                 index = (s->ioregsel - 0x10) >> 1;
180                 if (index >= 0 && index < IOAPIC_NUM_PINS) {
181                     if (s->ioregsel & 1) {
182                         s->ioredtbl[index] &= 0xffffffff;
183                         s->ioredtbl[index] |= (uint64_t)val << 32;
184                     } else {
185                         s->ioredtbl[index] &= ~0xffffffffULL;
186                         s->ioredtbl[index] |= val;
187                     }
188                     ioapic_service(s);
189                 }
190         }
191     }
192 }
193
194 static void ioapic_save(QEMUFile *f, void *opaque)
195 {
196     IOAPICState *s = opaque;
197     int i;
198
199     qemu_put_8s(f, &s->id);
200     qemu_put_8s(f, &s->ioregsel);
201     for (i = 0; i < IOAPIC_NUM_PINS; i++) {
202         qemu_put_be64s(f, &s->ioredtbl[i]);
203     }
204 }
205
206 static int ioapic_load(QEMUFile *f, void *opaque, int version_id)
207 {
208     IOAPICState *s = opaque;
209     int i;
210
211     if (version_id != 1)
212         return -EINVAL;
213
214     qemu_get_8s(f, &s->id);
215     qemu_get_8s(f, &s->ioregsel);
216     for (i = 0; i < IOAPIC_NUM_PINS; i++) {
217         qemu_get_be64s(f, &s->ioredtbl[i]);
218     }
219     return 0;
220 }
221
222 static void ioapic_reset(void *opaque)
223 {
224     IOAPICState *s = opaque;
225     int i;
226
227     memset(s, 0, sizeof(*s));
228     for(i = 0; i < IOAPIC_NUM_PINS; i++)
229         s->ioredtbl[i] = 1 << 16; /* mask LVT */
230 }
231
232 static CPUReadMemoryFunc *ioapic_mem_read[3] = {
233     ioapic_mem_readl,
234     ioapic_mem_readl,
235     ioapic_mem_readl,
236 };
237
238 static CPUWriteMemoryFunc *ioapic_mem_write[3] = {
239     ioapic_mem_writel,
240     ioapic_mem_writel,
241     ioapic_mem_writel,
242 };
243
244 qemu_irq *ioapic_init(void)
245 {
246     IOAPICState *s;
247     qemu_irq *irq;
248     int io_memory;
249
250     s = qemu_mallocz(sizeof(IOAPICState));
251     ioapic_reset(s);
252
253     io_memory = cpu_register_io_memory(ioapic_mem_read,
254                                        ioapic_mem_write, s);
255     cpu_register_physical_memory(0xfec00000, 0x1000, io_memory);
256
257     register_savevm("ioapic", 0, 1, ioapic_save, ioapic_load, s);
258     qemu_register_reset(ioapic_reset, s);
259     irq = qemu_allocate_irqs(ioapic_set_irq, s, IOAPIC_NUM_PINS);
260
261     return irq;
262 }