]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/bhyve/ioapic.c
IFC @ r243164
[FreeBSD/FreeBSD.git] / usr.sbin / bhyve / ioapic.c
1 /*-
2  * Copyright (c) 2012 NetApp, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY NETAPP, INC ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL NETAPP, INC OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/types.h>
33
34 #include <x86/apicreg.h>
35 #include <machine/vmm.h>
36
37 #include <string.h>
38 #include <assert.h>
39 #include <stdbool.h>
40
41 #include <vmmapi.h>
42
43 #include "inout.h"
44 #include "mem.h"
45 #include "instruction_emul.h"
46 #include "fbsdrun.h"
47
48 #include <stdio.h>
49
50 #define IOAPIC_PADDR    0xFEC00000
51
52 #define IOREGSEL        0x00
53 #define IOWIN           0x10
54
55 #define REDIR_ENTRIES   16
56 #define INTR_ASSERTED(ioapic, pin)      ((ioapic)->pinstate[(pin)] == true)
57
58 struct ioapic {
59         int             inited;
60         uint32_t        id;
61         uint64_t        redtbl[REDIR_ENTRIES];
62         bool            pinstate[REDIR_ENTRIES];
63
64         uintptr_t       paddr;          /* gpa where the ioapic is mapped */
65         uint32_t        ioregsel;
66         struct memory_region *region;
67 };
68
69 static struct ioapic ioapics[1];        /* only a single ioapic for now */
70
71 static int ioapic_region_read(struct ioapic *ioapic, uintptr_t paddr,
72                                 int size, uint64_t *data);
73 static int ioapic_region_write(struct ioapic *ioapic, uintptr_t paddr,
74                                 int size, uint64_t data);
75 static int ioapic_region_handler(struct vmctx *vm, int vcpu, int dir,
76                                  uintptr_t paddr, int size, uint64_t *val,
77                                  void *arg1, long arg2);
78
79 static void
80 ioapic_set_pinstate(struct vmctx *ctx, int pin, bool newstate)
81 {
82         int vector, apicid, vcpu;
83         uint32_t low, high;
84         struct ioapic *ioapic;
85         
86         ioapic = &ioapics[0];           /* assume a single ioapic */
87
88         if (pin < 0 || pin >= REDIR_ENTRIES)
89                 return;
90
91         /* Nothing to do if interrupt pin has not changed state */
92         if (ioapic->pinstate[pin] == newstate)
93                 return;
94
95         ioapic->pinstate[pin] = newstate;       /* record it */
96
97         /* Nothing to do if interrupt pin is deasserted */
98         if (!INTR_ASSERTED(ioapic, pin))
99                 return;
100
101         /*
102          * XXX
103          * We only deal with:
104          * - edge triggered interrupts
105          * - physical destination mode
106          * - fixed delivery mode
107          */
108         low = ioapic->redtbl[pin];
109         high = ioapic->redtbl[pin] >> 32;
110         if ((low & IOART_INTMASK) == IOART_INTMCLR &&
111             (low & IOART_TRGRMOD) == IOART_TRGREDG &&
112             (low & IOART_DESTMOD) == IOART_DESTPHY &&
113             (low & IOART_DELMOD) == IOART_DELFIXED) {
114                 vector = low & IOART_INTVEC;
115                 apicid = high >> APIC_ID_SHIFT;
116                 if (apicid != 0xff) {
117                         /* unicast */
118                         vcpu = vm_apicid2vcpu(ctx, apicid);
119                         vm_lapic_irq(ctx, vcpu, vector);
120                 } else {
121                         /* broadcast */
122                         vcpu = 0;
123                         while (vcpu < guest_ncpus) {
124                                 vm_lapic_irq(ctx, vcpu, vector);
125                                 vcpu++;
126                         }
127                 }
128         }
129 }
130
131 void
132 ioapic_deassert_pin(struct vmctx *ctx, int pin)
133 {
134         ioapic_set_pinstate(ctx, pin, false);
135 }
136
137 void
138 ioapic_assert_pin(struct vmctx *ctx, int pin)
139 {
140         ioapic_set_pinstate(ctx, pin, true);
141 }
142
143 void
144 ioapic_init(int which)
145 {
146         struct mem_range memp;
147         struct ioapic *ioapic;
148         int error;
149         int i;
150
151         assert(which == 0);
152
153         ioapic = &ioapics[which];
154         assert(ioapic->inited == 0);
155
156         bzero(ioapic, sizeof(struct ioapic));
157
158         /* Initialize all redirection entries to mask all interrupts */
159         for (i = 0; i < REDIR_ENTRIES; i++)
160                 ioapic->redtbl[i] = 0x0001000000010000UL;
161
162         ioapic->paddr = IOAPIC_PADDR;
163
164         /* Register emulated memory region */
165         memp.name = "ioapic";
166         memp.flags = MEM_F_RW;
167         memp.handler = ioapic_region_handler;
168         memp.arg1 = ioapic;
169         memp.arg2 = which;
170         memp.base = ioapic->paddr;
171         memp.size = sizeof(struct IOAPIC);
172         error = register_mem(&memp);
173
174         assert (error == 0);
175
176         ioapic->inited = 1;
177 }
178
179 static uint32_t
180 ioapic_read(struct ioapic *ioapic, uint32_t addr)
181 {
182         int regnum, pin, rshift;
183
184         assert(ioapic->inited);
185
186         regnum = addr & 0xff;
187         switch (regnum) {
188         case IOAPIC_ID:
189                 return (ioapic->id);
190                 break;
191         case IOAPIC_VER:
192                 return ((REDIR_ENTRIES << MAXREDIRSHIFT) | 0x11);
193                 break;
194         case IOAPIC_ARB:
195                 return (ioapic->id);
196                 break;
197         default:
198                 break;
199         }
200
201         /* redirection table entries */
202         if (regnum >= IOAPIC_REDTBL &&
203             regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
204                 pin = (regnum - IOAPIC_REDTBL) / 2;
205                 if ((regnum - IOAPIC_REDTBL) % 2)
206                         rshift = 32;
207                 else
208                         rshift = 0;
209
210                 return (ioapic->redtbl[pin] >> rshift);
211         }
212
213         return (0);
214 }
215
216 static void
217 ioapic_write(struct ioapic *ioapic, uint32_t addr, uint32_t data)
218 {
219         int regnum, pin, lshift;
220
221         assert(ioapic->inited);
222
223         regnum = addr & 0xff;
224         switch (regnum) {
225         case IOAPIC_ID:
226                 ioapic->id = data & APIC_ID_MASK;
227                 break;
228         case IOAPIC_VER:
229         case IOAPIC_ARB:
230                 /* readonly */
231                 break;
232         default:
233                 break;
234         }
235
236         /* redirection table entries */
237         if (regnum >= IOAPIC_REDTBL &&
238             regnum < IOAPIC_REDTBL + REDIR_ENTRIES * 2) {
239                 pin = (regnum - IOAPIC_REDTBL) / 2;
240                 if ((regnum - IOAPIC_REDTBL) % 2)
241                         lshift = 32;
242                 else
243                         lshift = 0;
244
245                 ioapic->redtbl[pin] &= ~((uint64_t)0xffffffff << lshift);
246                 ioapic->redtbl[pin] |= ((uint64_t)data << lshift);
247         }
248 }
249
250 static int
251 ioapic_region_read(struct ioapic *ioapic, uintptr_t paddr, int size,
252                    uint64_t *data)
253 {
254         int offset;
255
256         offset = paddr - ioapic->paddr;
257
258         /*
259          * The IOAPIC specification allows 32-bit wide accesses to the
260          * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
261          */
262         if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
263 #if 1
264                 printf("invalid access to ioapic%d: size %d, offset %d\n",
265                        (int)(ioapic - ioapics), size, offset);
266 #endif
267                 *data = 0;
268                 return (0);
269         }
270
271         if (offset == IOREGSEL)
272                 *data = ioapic->ioregsel;
273         else
274                 *data = ioapic_read(ioapic, ioapic->ioregsel);
275
276         return (0);
277 }
278
279 static int
280 ioapic_region_write(struct ioapic *ioapic, uintptr_t paddr, int size,
281                     uint64_t data)
282 {
283         int offset;
284
285         offset = paddr - ioapic->paddr;
286
287         /*
288          * The ioapic specification allows 32-bit wide accesses to the
289          * IOREGSEL (offset 0) and IOWIN (offset 16) registers.
290          */
291         if (size != 4 || (offset != IOREGSEL && offset != IOWIN)) {
292 #if 1
293                 printf("invalid access to ioapic%d: size %d, offset %d\n",
294                        (int)(ioapic - ioapics), size, offset);
295 #endif
296                 return (0);
297         }
298
299         if (offset == IOREGSEL)
300                 ioapic->ioregsel = data;
301         else
302                 ioapic_write(ioapic, ioapic->ioregsel, data);
303
304         return (0);
305 }
306
307 static int
308 ioapic_region_handler(struct vmctx *vm, int vcpu, int dir, uintptr_t paddr,
309                       int size, uint64_t *val, void *arg1, long arg2)
310 {
311         struct ioapic *ioapic;
312         int which;
313
314         ioapic = arg1;
315         which = arg2;
316
317         assert(ioapic == &ioapics[which]);
318
319         if (dir == MEM_F_READ)
320                 ioapic_region_read(ioapic, paddr, size, val);
321         else
322                 ioapic_region_write(ioapic, paddr, size, *val);
323
324         return (0);
325 }