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