]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/vmm/amd/ivrs_drv.c
Add AMD IOMMU/AMD-Vi support in bhyve for passthrough/direct assignment to VMs. To...
[FreeBSD/FreeBSD.git] / sys / amd64 / vmm / amd / ivrs_drv.c
1 /*-
2  * Copyright (c) 2016, Anish Gupta (anish@freebsd.org)
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 unmodified, this list of conditions, and the following
10  *    disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include "opt_acpi.h"
31 #include <sys/param.h>
32 #include <sys/bus.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/malloc.h>
36
37 #include <machine/vmparam.h>
38
39 #include <vm/vm.h>
40 #include <vm/pmap.h>
41
42 #include <contrib/dev/acpica/include/acpi.h>
43 #include <contrib/dev/acpica/include/accommon.h>
44 #include <dev/acpica/acpivar.h>
45
46 #include "io/iommu.h"
47 #include "amdvi_priv.h"
48
49 device_t *ivhd_devs;                    /* IVHD or AMD-Vi device list. */
50 int     ivhd_count;                     /* Number of IVHD or AMD-Vi devices. */
51
52 extern int amdvi_ptp_level;             /* Page table levels. */
53
54 typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER * ptr, void *arg);
55
56 /*
57  * Iterate IVRS table for IVHD and IVMD device type.
58  */
59 static void
60 ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg)
61 {
62         ACPI_TABLE_IVRS *ivrs;
63         ACPI_IVRS_HEADER *ivrs_hdr, *end;
64         ACPI_STATUS status;
65
66         status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
67         if (ACPI_FAILURE(status))
68                 return;
69
70         if (ivrs->Header.Length == 0) {
71                 return;
72         }
73
74         ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1);
75         end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length);
76
77         while (ivrs_hdr < end) {
78                 switch (ivrs_hdr->Type) {
79                 case ACPI_IVRS_TYPE_HARDWARE:   /* Legacy */
80                 case 0x11:
81                 case 0x40:                      /* ACPI HID */
82                         if (!iter(ivrs_hdr, arg))
83                                 return;
84                         break;
85                 
86                 case ACPI_IVRS_TYPE_MEMORY1:
87                 case ACPI_IVRS_TYPE_MEMORY2:
88                 case ACPI_IVRS_TYPE_MEMORY3:
89                         if (!iter(ivrs_hdr, arg))
90                                 return;
91
92                         break;
93                 
94                 default:
95                         printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
96
97                 }
98
99                 ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
100                         ivrs_hdr->Length);
101                 if (ivrs_hdr->Length < 0) {
102                         printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n", ivrs_hdr->Length);
103                         break;
104                 }
105         }
106 }
107
108 static  int
109 ivrs_is_ivhd(UINT8 type)
110 {
111
112         if ((type == ACPI_IVRS_TYPE_HARDWARE) || (type == 0x11) || (type == 0x40))
113                 return (1);
114
115         return (0);
116 }
117
118 /* Count the number of AMD-Vi devices in the system. */
119 static int
120 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
121 {
122
123         if (ivrs_is_ivhd(ivrs_he->Type))
124                 ivhd_count++;
125
126         return (1);
127 }
128
129 struct find_ivrs_hdr_args {
130         int     i;
131         ACPI_IVRS_HEADER *ptr;
132 };
133
134 static int
135 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
136 {
137         struct find_ivrs_hdr_args *fi;
138
139         fi = (struct find_ivrs_hdr_args *)args;
140         if (ivrs_is_ivhd(ivrs_hdr->Type)) {
141                 if (fi->i == 0) {
142                         fi->ptr = ivrs_hdr;
143                         return (0);
144                 }
145                 fi->i--;
146         }
147
148         return (1);
149 }
150
151 static ACPI_IVRS_HARDWARE *
152 ivhd_find_by_index(int idx)
153 {
154         struct find_ivrs_hdr_args fi;
155
156         fi.i = idx;
157         fi.ptr = NULL;
158
159         ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
160
161         return ((ACPI_IVRS_HARDWARE *)fi.ptr);
162 }
163
164 static void
165 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
166     uint32_t end_id, uint8_t cfg, bool ats)
167 {
168         struct ivhd_dev_cfg *dev_cfg;
169
170         /* If device doesn't have special data, don't add it. */
171         if (!cfg)
172                 return;
173
174         dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
175         dev_cfg->start_id = start_id;
176         dev_cfg->end_id = end_id;
177         dev_cfg->data = cfg;
178         dev_cfg->enable_ats = ats;
179 }
180
181 /*
182  * Record device attributes as suggested by BIOS.
183  */
184 static int
185 ivhd_dev_parse(ACPI_IVRS_HARDWARE * ivhd, struct amdvi_softc *softc)
186 {
187         ACPI_IVRS_DE_HEADER *de, *end;
188         int range_start_id = 0, range_end_id = 0;
189         uint32_t *extended;
190         uint8_t all_data = 0, range_data = 0;
191         bool range_enable_ats = false, enable_ats;
192
193         softc->start_dev_rid = ~0;
194         softc->end_dev_rid = 0;
195
196         de = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
197             sizeof(ACPI_IVRS_HARDWARE));
198         end = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
199             ivhd->Header.Length);
200
201         while (de < (ACPI_IVRS_DE_HEADER *) end) {
202                 softc->start_dev_rid = MIN(softc->start_dev_rid, de->Id);
203                 softc->end_dev_rid = MAX(softc->end_dev_rid, de->Id);
204                 switch (de->Type) {
205                 case ACPI_IVRS_TYPE_ALL:
206                         all_data = de->DataSetting;
207                         break;
208
209                 case ACPI_IVRS_TYPE_SELECT:
210                 case ACPI_IVRS_TYPE_ALIAS_SELECT:
211                 case ACPI_IVRS_TYPE_EXT_SELECT:
212                         enable_ats = false;
213                         if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
214                                 extended = (uint32_t *)(de + 1);
215                                 enable_ats =
216                                     (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
217                                         false : true;
218                         }
219                         ivhd_dev_add_entry(softc, de->Id, de->Id,
220                             de->DataSetting | all_data, enable_ats);
221                         break;
222
223                 case ACPI_IVRS_TYPE_START:
224                 case ACPI_IVRS_TYPE_ALIAS_START:
225                 case ACPI_IVRS_TYPE_EXT_START:
226                         range_start_id = de->Id;
227                         range_data = de->DataSetting;
228                         if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
229                                 extended = (uint32_t *)(de + 1);
230                                 range_enable_ats =
231                                     (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
232                                         false : true;
233                         }
234                         break;
235
236                 case ACPI_IVRS_TYPE_END:
237                         range_end_id = de->Id;
238                         ivhd_dev_add_entry(softc, range_start_id, range_end_id,
239                                 range_data | all_data, range_enable_ats);
240                         range_start_id = range_end_id = 0;
241                         range_data = 0;
242                         all_data = 0;
243                         break;
244
245                 case ACPI_IVRS_TYPE_PAD4:
246                         break;
247
248                 case ACPI_IVRS_TYPE_SPECIAL:
249                         /* HPET or IOAPIC */
250                         break;
251                 default:
252                         if ((de->Type < 5) ||
253                             (de->Type >= ACPI_IVRS_TYPE_PAD8))
254                                 device_printf(softc->dev,
255                                     "Unknown dev entry:0x%x\n", de->Type);
256                 }
257
258                 if (softc->dev_cfg_cnt >
259                         (sizeof(softc->dev_cfg) / sizeof(softc->dev_cfg[0]))) {
260                         device_printf(softc->dev,
261                             "WARN Too many device entries.\n");
262                         return (EINVAL);
263                 }
264                 de++;
265         }
266
267         KASSERT((softc->end_dev_rid >= softc->start_dev_rid),
268             ("Device end[0x%x] < start[0x%x.\n",
269             softc->end_dev_rid, softc->start_dev_rid));
270
271         return (0);
272 }
273
274 static void
275 ivhd_identify(driver_t *driver, device_t parent)
276 {
277         ACPI_TABLE_IVRS *ivrs;
278         ACPI_IVRS_HARDWARE *ivhd;
279         ACPI_STATUS status;
280         uint32_t info;
281         int i, count = 0;
282
283         if (acpi_disabled("ivhd"))
284                 return;
285
286         status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
287         if (ACPI_FAILURE(status))
288                 return;
289
290         if (ivrs->Header.Length == 0) {
291                 return;
292         }
293
294         info = ivrs->Info;
295         printf("AMD-Vi IVRS VAsize = %d PAsize = %d GVAsize = %d flags:%b\n",
296                 REG_BITS(info, 21, 15), REG_BITS(info, 14, 8), 
297                 REG_BITS(info, 7, 5), REG_BITS(info, 22, 22),
298                 "\020\001HtAtsResv");
299
300         ivrs_hdr_iterate_tbl(ivhd_count_iter, NULL);
301         if (!ivhd_count)
302                 return;
303
304         ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
305                 M_WAITOK | M_ZERO);
306         for (i = 0; i < ivhd_count; i++) {
307                 ivhd = ivhd_find_by_index(i);
308                 if (ivhd == NULL) {
309                         printf("Can't find IVHD entry%d\n", i);
310                         continue;
311                 }
312
313                 ivhd_devs[i] = BUS_ADD_CHILD(parent, 1, "ivhd", i);
314                 /*
315                  * XXX: In case device was not destroyed before, add will fail.
316                  * locate the old device instance.
317                  */
318                 if (ivhd_devs[i] == NULL) {
319                         ivhd_devs[i] = device_find_child(parent, "ivhd", i);
320                         if (ivhd_devs[i] == NULL) {
321                                 printf("AMD-Vi: cant find AMD-Vi dev%d\n", i);
322                                 break;
323                         }
324                 }
325                 count++;
326         }
327
328         /*
329          * Update device count in case failed to attach.
330          */
331         ivhd_count = count;
332 }
333
334 static int
335 ivhd_probe(device_t dev)
336 {
337
338         if (acpi_get_handle(dev) != NULL)
339                 return (ENXIO);
340         device_set_desc(dev, "AMD-Vi/IOMMU or ivhd");
341
342         return (BUS_PROBE_NOWILDCARD);
343 }
344
345 static int
346 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE * ivhd)
347 {
348         device_t dev;
349         int max_ptp_level;
350
351         dev = softc->dev;
352         device_printf(dev, "Flag:%b\n", softc->ivhd_flag,
353             "\020\001HtTunEn\002PassPW\003ResPassPW\004Isoc\005IotlbSup"
354             "\006Coherent\007PreFSup\008PPRSup");
355         /*
356          * If no extended feature[EFR], its rev1 with maximum paging level as 7.
357          */
358         max_ptp_level = 7;
359         if (softc->ivhd_efr) {
360                 device_printf(dev, "EFR HATS = %d GATS = %d GLXSup = %d "
361                     "MsiNumPr = %d PNBanks= %d PNCounters= %d\n"
362                     "max PASID = %d EFR: %b \n",
363                     REG_BITS(softc->ivhd_efr, 31, 30),
364                     REG_BITS(softc->ivhd_efr, 29, 28),
365                     REG_BITS(softc->ivhd_efr, 4, 3),
366                     REG_BITS(softc->ivhd_efr, 27, 23),
367                     REG_BITS(softc->ivhd_efr, 22, 17),
368                     REG_BITS(softc->ivhd_efr, 16, 13),
369                     REG_BITS(softc->ivhd_efr, 12, 8),
370                     softc->ivhd_efr, "\020\001XTSup\002NXSup\003GTSup\005IASup"
371                     "\006GASup\007HESup\008PPRSup");
372
373                 max_ptp_level = REG_BITS(softc->ivhd_efr, 31, 30) + 4;
374         }
375
376         /* Make sure device support minimum page level as requested by user. */
377         if (max_ptp_level < amdvi_ptp_level) {
378                 device_printf(dev, "Insufficient PTP level:%d\n",
379                     max_ptp_level);
380                 return (EINVAL);
381         }
382
383         device_printf(softc->dev, "max supported paging level:%d restricting to: %d\n",
384             max_ptp_level, amdvi_ptp_level);
385         device_printf(softc->dev, "device supported range "
386             "[0x%x - 0x%x]\n", softc->start_dev_rid, softc->end_dev_rid);
387
388         return (0);
389 }
390
391 static int
392 ivhd_attach(device_t dev)
393 {
394         ACPI_IVRS_HARDWARE *ivhd;
395         struct amdvi_softc *softc;
396         int status, unit;
397
398         unit = device_get_unit(dev);
399         /* Make sure its same device for which attach is called. */
400         if (ivhd_devs[unit] != dev)
401                 panic("Not same device old %p new %p", ivhd_devs[unit], dev);
402
403         softc = device_get_softc(dev);
404         softc->dev = dev;
405         ivhd = ivhd_find_by_index(unit);
406         if (ivhd == NULL)
407                 return (EINVAL);
408
409         softc->pci_seg = ivhd->PciSegmentGroup;
410         softc->pci_rid = ivhd->Header.DeviceId;
411         softc->ivhd_flag = ivhd->Header.Flags;
412         softc->ivhd_efr = ivhd->Reserved;
413         /* 
414          * PCI capability has more capabilities that are not part of IVRS.
415          */
416         softc->cap_off = ivhd->CapabilityOffset;
417
418 #ifdef notyet
419         /* IVHD Info bit[4:0] is event MSI/X number. */
420         softc->event_msix = ivhd->Info & 0x1F;
421 #endif
422         softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
423         status = ivhd_dev_parse(ivhd, softc);
424         if (status != 0) {
425                 device_printf(dev,
426                     "endpoint device parsing error=%d\n", status);
427         }
428
429         status = ivhd_print_cap(softc, ivhd);
430         if (status != 0) {
431                 return (status);
432         }
433
434         status = amdvi_setup_hw(softc);
435         if (status != 0) {
436                 device_printf(dev, "couldn't be initialised, error=%d\n", 
437                     status);
438                 return (status);
439         }
440
441         return (0);
442 }
443
444 static int
445 ivhd_detach(device_t dev)
446 {
447         struct amdvi_softc *softc;
448
449         softc = device_get_softc(dev);
450
451         amdvi_teardown_hw(softc);
452
453         /*
454          * XXX: delete the device.
455          * don't allow detach, return EBUSY.
456          */
457         return (0);
458 }
459
460 static int
461 ivhd_suspend(device_t dev)
462 {
463
464         return (0);
465 }
466
467 static int
468 ivhd_resume(device_t dev)
469 {
470
471         return (0);
472 }
473
474 static device_method_t ivhd_methods[] = {
475         DEVMETHOD(device_identify, ivhd_identify),
476         DEVMETHOD(device_probe, ivhd_probe),
477         DEVMETHOD(device_attach, ivhd_attach),
478         DEVMETHOD(device_detach, ivhd_detach),
479         DEVMETHOD(device_suspend, ivhd_suspend),
480         DEVMETHOD(device_resume, ivhd_resume),
481         DEVMETHOD_END
482 };
483
484 static driver_t ivhd_driver = {
485         "ivhd",
486         ivhd_methods,
487         sizeof(struct amdvi_softc),
488 };
489
490 static devclass_t ivhd_devclass;
491
492 /*
493  * Load this module at the end after PCI re-probing to configure interrupt.
494  */
495 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
496                       SI_ORDER_ANY);
497 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
498 MODULE_DEPEND(ivhd, pci, 1, 1, 1);