2 * Copyright (c) 2016, Anish Gupta (anish@freebsd.org)
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice unmodified, this list of conditions, and the following
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.
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.
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
31 #include <sys/param.h>
33 #include <sys/kernel.h>
34 #include <sys/module.h>
35 #include <sys/malloc.h>
37 #include <machine/vmparam.h>
42 #include <contrib/dev/acpica/include/acpi.h>
43 #include <contrib/dev/acpica/include/accommon.h>
44 #include <dev/acpica/acpivar.h>
47 #include "amdvi_priv.h"
49 device_t *ivhd_devs; /* IVHD or AMD-Vi device list. */
50 int ivhd_count; /* Number of IVHD header. */
52 * Cached IVHD header list.
53 * Single entry for each IVHD, filtered the legacy one.
55 ACPI_IVRS_HARDWARE *ivhd_hdrs[10];
57 extern int amdvi_ptp_level; /* Page table levels. */
59 typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER *ptr, void *arg);
61 * Iterate IVRS table for IVHD and IVMD device type.
64 ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg)
66 ACPI_TABLE_IVRS *ivrs;
67 ACPI_IVRS_HEADER *ivrs_hdr, *end;
70 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
71 if (ACPI_FAILURE(status))
74 if (ivrs->Header.Length == 0) {
78 ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1);
79 end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length);
81 while (ivrs_hdr < end) {
82 if ((uint8_t *)ivrs_hdr + ivrs_hdr->Length > (uint8_t *)end) {
83 printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n",
88 switch (ivrs_hdr->Type) {
89 case ACPI_IVRS_TYPE_HARDWARE: /* Legacy */
91 case 0x40: /* ACPI HID */
92 if (!iter(ivrs_hdr, arg))
96 case ACPI_IVRS_TYPE_MEMORY1:
97 case ACPI_IVRS_TYPE_MEMORY2:
98 case ACPI_IVRS_TYPE_MEMORY3:
99 if (!iter(ivrs_hdr, arg))
105 printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
109 ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
115 ivrs_is_ivhd(UINT8 type)
119 case ACPI_IVRS_TYPE_HARDWARE:
120 case ACPI_IVRS_TYPE_HARDWARE_EXT1:
121 case ACPI_IVRS_TYPE_HARDWARE_EXT2:
129 /* Count the number of AMD-Vi devices in the system. */
131 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
134 if (ivrs_is_ivhd(ivrs_he->Type))
140 struct find_ivrs_hdr_args {
142 ACPI_IVRS_HEADER *ptr;
146 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
148 struct find_ivrs_hdr_args *fi;
150 fi = (struct find_ivrs_hdr_args *)args;
151 if (ivrs_is_ivhd(ivrs_hdr->Type)) {
162 static ACPI_IVRS_HARDWARE *
163 ivhd_find_by_index(int idx)
165 struct find_ivrs_hdr_args fi;
170 ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
172 return ((ACPI_IVRS_HARDWARE *)fi.ptr);
176 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
177 uint32_t end_id, uint8_t cfg, bool ats)
179 struct ivhd_dev_cfg *dev_cfg;
181 /* If device doesn't have special data, don't add it. */
185 dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
186 dev_cfg->start_id = start_id;
187 dev_cfg->end_id = end_id;
189 dev_cfg->enable_ats = ats;
193 * Record device attributes as suggested by BIOS.
196 ivhd_dev_parse(ACPI_IVRS_HARDWARE* ivhd, struct amdvi_softc *softc)
198 ACPI_IVRS_DE_HEADER *de;
200 int range_start_id = 0, range_end_id = 0;
202 uint8_t all_data = 0, range_data = 0;
203 bool range_enable_ats = false, enable_ats;
205 softc->start_dev_rid = ~0;
206 softc->end_dev_rid = 0;
208 switch (ivhd->Header.Type) {
209 case ACPI_IVRS_TYPE_HARDWARE_EXT1:
210 case ACPI_IVRS_TYPE_HARDWARE_EXT2:
211 p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE_NEW);
212 de = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
213 sizeof(ACPI_IVRS_HARDWARE_NEW));
216 case ACPI_IVRS_TYPE_HARDWARE:
217 p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE);
218 de = (ACPI_IVRS_DE_HEADER *) ((uint8_t *)ivhd +
219 sizeof(ACPI_IVRS_HARDWARE));
223 device_printf(softc->dev,
224 "unknown type: 0x%x\n", ivhd->Header.Type);
228 end = (uint8_t *)ivhd + ivhd->Header.Length;
231 de = (ACPI_IVRS_DE_HEADER *)p;
232 softc->start_dev_rid = MIN(softc->start_dev_rid, de->Id);
233 softc->end_dev_rid = MAX(softc->end_dev_rid, de->Id);
235 case ACPI_IVRS_TYPE_ALL:
236 all_data = de->DataSetting;
239 case ACPI_IVRS_TYPE_SELECT:
240 case ACPI_IVRS_TYPE_ALIAS_SELECT:
241 case ACPI_IVRS_TYPE_EXT_SELECT:
243 if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
244 extended = (uint32_t *)(de + 1);
246 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
249 ivhd_dev_add_entry(softc, de->Id, de->Id,
250 de->DataSetting | all_data, enable_ats);
253 case ACPI_IVRS_TYPE_START:
254 case ACPI_IVRS_TYPE_ALIAS_START:
255 case ACPI_IVRS_TYPE_EXT_START:
256 range_start_id = de->Id;
257 range_data = de->DataSetting;
258 if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
259 extended = (uint32_t *)(de + 1);
261 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
266 case ACPI_IVRS_TYPE_END:
267 range_end_id = de->Id;
268 ivhd_dev_add_entry(softc, range_start_id, range_end_id,
269 range_data | all_data, range_enable_ats);
270 range_start_id = range_end_id = 0;
275 case ACPI_IVRS_TYPE_PAD4:
278 case ACPI_IVRS_TYPE_SPECIAL:
282 if ((de->Type < 5) ||
283 (de->Type >= ACPI_IVRS_TYPE_PAD8))
284 device_printf(softc->dev,
285 "Unknown dev entry:0x%x\n", de->Type);
288 if (softc->dev_cfg_cnt >
289 (sizeof(softc->dev_cfg) / sizeof(softc->dev_cfg[0]))) {
290 device_printf(softc->dev,
291 "WARN Too many device entries.\n");
295 p += sizeof(ACPI_IVRS_DEVICE4);
296 else if (de->Type < 0x80)
297 p += sizeof(ACPI_IVRS_DEVICE8A);
299 printf("Variable size IVHD type 0x%x not supported\n",
305 KASSERT((softc->end_dev_rid >= softc->start_dev_rid),
306 ("Device end[0x%x] < start[0x%x.\n",
307 softc->end_dev_rid, softc->start_dev_rid));
313 ivhd_is_newer(ACPI_IVRS_HEADER *old, ACPI_IVRS_HEADER *new)
316 * Newer IVRS header type take precedence.
318 if ((old->DeviceId == new->DeviceId) &&
319 (old->Type == ACPI_IVRS_TYPE_HARDWARE) &&
320 ((new->Type == ACPI_IVRS_TYPE_HARDWARE_EXT1) ||
321 (new->Type == ACPI_IVRS_TYPE_HARDWARE_EXT1))) {
329 ivhd_identify(driver_t *driver, device_t parent)
331 ACPI_TABLE_IVRS *ivrs;
332 ACPI_IVRS_HARDWARE *ivhd;
335 uint32_t ivrs_ivinfo;
337 if (acpi_disabled("ivhd"))
340 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
341 if (ACPI_FAILURE(status))
344 if (ivrs->Header.Length == 0) {
348 ivrs_ivinfo = ivrs->Info;
349 printf("AMD-Vi: IVRS Info VAsize = %d PAsize = %d GVAsize = %d"
351 REG_BITS(ivrs_ivinfo, 21, 15), REG_BITS(ivrs_ivinfo, 14, 8),
352 REG_BITS(ivrs_ivinfo, 7, 5), REG_BITS(ivrs_ivinfo, 22, 22),
355 ivrs_hdr_iterate_tbl(ivhd_count_iter, NULL);
359 for (i = 0; i < ivhd_count; i++) {
360 ivhd = ivhd_find_by_index(i);
361 KASSERT(ivhd, ("ivhd%d is NULL\n", i));
366 * Scan for presence of legacy and non-legacy device type
367 * for same AMD-Vi device and override the old one.
369 for (i = ivhd_count - 1 ; i > 0 ; i--){
370 if (ivhd_is_newer(&ivhd_hdrs[i-1]->Header,
371 &ivhd_hdrs[i]->Header)) {
372 ivhd_hdrs[i-1] = ivhd_hdrs[i];
377 ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
379 for (i = 0; i < ivhd_count; i++) {
381 KASSERT(ivhd, ("ivhd%d is NULL\n", i));
384 * Use a high order to ensure that this driver is probed after
385 * the Host-PCI bridge and the root PCI bus.
387 ivhd_devs[i] = BUS_ADD_CHILD(parent,
388 ACPI_DEV_BASE_ORDER + 10 * 10, "ivhd", i);
391 * XXX: In case device was not destroyed before, add will fail.
392 * locate the old device instance.
394 if (ivhd_devs[i] == NULL) {
395 ivhd_devs[i] = device_find_child(parent, "ivhd", i);
396 if (ivhd_devs[i] == NULL) {
397 printf("AMD-Vi: cant find ivhd%d\n", i);
405 * Update device count in case failed to attach.
411 ivhd_probe(device_t dev)
413 ACPI_IVRS_HARDWARE *ivhd;
416 if (acpi_get_handle(dev) != NULL)
419 unit = device_get_unit(dev);
420 KASSERT((unit < ivhd_count),
421 ("ivhd unit %d > count %d", unit, ivhd_count));
422 ivhd = ivhd_hdrs[unit];
423 KASSERT(ivhd, ("ivhd is NULL"));
425 if (ivhd->Header.Type == ACPI_IVRS_TYPE_HARDWARE)
426 device_set_desc(dev, "AMD-Vi/IOMMU ivhd");
428 device_set_desc(dev, "AMD-Vi/IOMMU ivhd with EFR");
430 return (BUS_PROBE_NOWILDCARD);
434 ivhd_print_flag(device_t dev, enum AcpiIvrsType ivhd_type, uint8_t flag)
437 * IVHD lgeacy type has two extra high bits in flag which has
438 * been moved to EFR for non-legacy device.
441 case ACPI_IVRS_TYPE_HARDWARE:
442 device_printf(dev, "Flag:%b\n", flag,
454 case ACPI_IVRS_TYPE_HARDWARE_EXT1:
455 case ACPI_IVRS_TYPE_HARDWARE_EXT2:
456 device_printf(dev, "Flag:%b\n", flag,
467 device_printf(dev, "Can't decode flag of ivhd type :0x%x\n",
474 * Feature in legacy IVHD type(0x10) and attribute in newer type(0x11 and 0x40).
477 ivhd_print_feature(device_t dev, enum AcpiIvrsType ivhd_type, uint32_t feature)
480 case ACPI_IVRS_TYPE_HARDWARE:
481 device_printf(dev, "Features(type:0x%x) HATS = %d GATS = %d"
482 " MsiNumPPR = %d PNBanks= %d PNCounters= %d\n",
484 REG_BITS(feature, 31, 30),
485 REG_BITS(feature, 29, 28),
486 REG_BITS(feature, 27, 23),
487 REG_BITS(feature, 22, 17),
488 REG_BITS(feature, 16, 13));
489 device_printf(dev, "max PASID = %d GLXSup = %d Feature:%b\n",
490 REG_BITS(feature, 12, 8),
491 REG_BITS(feature, 4, 3),
502 /* Fewer features or attributes are reported in non-legacy type. */
503 case ACPI_IVRS_TYPE_HARDWARE_EXT1:
504 case ACPI_IVRS_TYPE_HARDWARE_EXT2:
505 device_printf(dev, "Features(type:0x%x) MsiNumPPR = %d"
506 " PNBanks= %d PNCounters= %d\n",
508 REG_BITS(feature, 27, 23),
509 REG_BITS(feature, 22, 17),
510 REG_BITS(feature, 16, 13));
513 default: /* Other ivhd type features are not decoded. */
514 device_printf(dev, "Can't decode ivhd type :0x%x\n", ivhd_type);
518 /* Print extended features of IOMMU. */
520 ivhd_print_ext_feature(device_t dev, uint64_t ext_feature)
522 uint32_t ext_low, ext_high;
527 ext_low = ext_feature;
528 device_printf(dev, "Extended features[31:0]:%b "
529 "HATS = 0x%x GATS = 0x%x "
530 "GLXSup = 0x%x SmiFSup = 0x%x SmiFRC = 0x%x "
531 "GAMSup = 0x%x DualPortLogSup = 0x%x DualEventLogSup = 0x%x\n",
544 REG_BITS(ext_low, 11, 10),
545 REG_BITS(ext_low, 13, 12),
546 REG_BITS(ext_low, 15, 14),
547 REG_BITS(ext_low, 17, 16),
548 REG_BITS(ext_low, 20, 18),
549 REG_BITS(ext_low, 23, 21),
550 REG_BITS(ext_low, 25, 24),
551 REG_BITS(ext_low, 29, 28));
553 ext_high = ext_feature >> 32;
554 device_printf(dev, "Extended features[62:32]:%b "
555 "Max PASID: 0x%x DevTblSegSup = 0x%x "
560 "\009PprOvrflwEarlySup"
571 REG_BITS(ext_high, 5, 0),
572 REG_BITS(ext_high, 8, 7),
573 REG_BITS(ext_high, 11, 10));
577 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE * ivhd)
584 ivhd_print_flag(dev, softc->ivhd_type, softc->ivhd_flag);
585 ivhd_print_feature(dev, softc->ivhd_type, softc->ivhd_feature);
586 ivhd_print_ext_feature(dev, softc->ext_feature);
588 /* Make sure device support minimum page level as requested by user. */
589 if (max_ptp_level < amdvi_ptp_level) {
590 device_printf(dev, "insufficient PTP level:%d\n",
594 device_printf(softc->dev, "supported paging level:%d, will use only: %d\n",
595 max_ptp_level, amdvi_ptp_level);
598 device_printf(softc->dev, "device range: 0x%x - 0x%x\n",
599 softc->start_dev_rid, softc->end_dev_rid);
605 ivhd_attach(device_t dev)
607 ACPI_IVRS_HARDWARE *ivhd;
608 ACPI_IVRS_HARDWARE_NEW *ivhd1;
609 struct amdvi_softc *softc;
612 unit = device_get_unit(dev);
613 KASSERT((unit < ivhd_count),
614 ("ivhd unit %d > count %d", unit, ivhd_count));
615 /* Make sure its same device for which attach is called. */
616 KASSERT((ivhd_devs[unit] == dev),
617 ("Not same device old %p new %p", ivhd_devs[unit], dev));
619 softc = device_get_softc(dev);
621 ivhd = ivhd_hdrs[unit];
622 KASSERT(ivhd, ("ivhd is NULL"));
624 softc->ivhd_type = ivhd->Header.Type;
625 softc->pci_seg = ivhd->PciSegmentGroup;
626 softc->pci_rid = ivhd->Header.DeviceId;
627 softc->ivhd_flag = ivhd->Header.Flags;
629 * On lgeacy IVHD type(0x10), it is documented as feature
630 * but in newer type it is attribute.
632 softc->ivhd_feature = ivhd->Reserved;
634 * PCI capability has more capabilities that are not part of IVRS.
636 softc->cap_off = ivhd->CapabilityOffset;
639 /* IVHD Info bit[4:0] is event MSI/X number. */
640 softc->event_msix = ivhd->Info & 0x1F;
642 switch (ivhd->Header.Type) {
643 case ACPI_IVRS_TYPE_HARDWARE_EXT1:
644 case ACPI_IVRS_TYPE_HARDWARE_EXT2:
645 ivhd1 = (ACPI_IVRS_HARDWARE_NEW *)ivhd;
646 softc->ext_feature = ivhd1->ExtFR;
651 softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
652 status = ivhd_dev_parse(ivhd, softc);
655 "endpoint device parsing error=%d\n", status);
658 status = ivhd_print_cap(softc, ivhd);
663 status = amdvi_setup_hw(softc);
665 device_printf(dev, "couldn't be initialised, error=%d\n",
674 ivhd_detach(device_t dev)
676 struct amdvi_softc *softc;
678 softc = device_get_softc(dev);
680 amdvi_teardown_hw(softc);
683 * XXX: delete the device.
684 * don't allow detach, return EBUSY.
690 ivhd_suspend(device_t dev)
697 ivhd_resume(device_t dev)
703 static device_method_t ivhd_methods[] = {
704 DEVMETHOD(device_identify, ivhd_identify),
705 DEVMETHOD(device_probe, ivhd_probe),
706 DEVMETHOD(device_attach, ivhd_attach),
707 DEVMETHOD(device_detach, ivhd_detach),
708 DEVMETHOD(device_suspend, ivhd_suspend),
709 DEVMETHOD(device_resume, ivhd_resume),
713 static driver_t ivhd_driver = {
716 sizeof(struct amdvi_softc),
719 static devclass_t ivhd_devclass;
722 * Load this module at the end after PCI re-probing to configure interrupt.
724 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
726 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
727 MODULE_DEPEND(ivhd, pci, 1, 1, 1);