1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2016, Anish Gupta (anish@freebsd.org)
5 * Copyright (c) 2021 The FreeBSD Foundation
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice unmodified, this list of conditions, and the following
13 * disclaimer.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD: stable/12/sys/amd64/vmm/amd/ivrs_drv.c 373320 2024-04-28 05:23:19Z git2svn $");
32
33 #include "opt_acpi.h"
34 #include <sys/param.h>
35 #include <sys/bus.h>
36 #include <sys/kernel.h>
37 #include <sys/module.h>
38 #include <sys/malloc.h>
39
40 #include <machine/vmparam.h>
41
42 #include <vm/vm.h>
43 #include <vm/pmap.h>
44
45 #include <contrib/dev/acpica/include/acpi.h>
46 #include <contrib/dev/acpica/include/accommon.h>
47 #include <dev/acpica/acpivar.h>
48 #include <dev/pci/pcireg.h>
49 #include <dev/pci/pcivar.h>
50
51 #include "io/iommu.h"
52 #include "amdvi_priv.h"
53
54 device_t *ivhd_devs; /* IVHD or AMD-Vi device list. */
55 int ivhd_count; /* Number of IVHD header. */
56 /*
57 * Cached IVHD header list.
58 * Single entry for each IVHD, filtered the legacy one.
59 */
60 ACPI_IVRS_HARDWARE1 **ivhd_hdrs;
61
62 extern int amdvi_ptp_level; /* Page table levels. */
63
64 typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER *ptr, void *arg);
65 /*
66 * Iterate IVRS table for IVHD and IVMD device type.
67 */
68 static void
ivrs_hdr_iterate_tbl(ivhd_iter_t iter,void * arg)69 ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg)
70 {
71 ACPI_TABLE_IVRS *ivrs;
72 ACPI_IVRS_HEADER *ivrs_hdr, *end;
73 ACPI_STATUS status;
74
75 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
76 if (ACPI_FAILURE(status))
77 return;
78
79 if (ivrs->Header.Length == 0) {
80 return;
81 }
82
83 ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1);
84 end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length);
85
86 while (ivrs_hdr < end) {
87 if ((uint8_t *)ivrs_hdr + ivrs_hdr->Length > (uint8_t *)end) {
88 printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n",
89 ivrs_hdr->Length);
90 break;
91 }
92
93 switch (ivrs_hdr->Type) {
94 case IVRS_TYPE_HARDWARE_LEGACY: /* Legacy */
95 case IVRS_TYPE_HARDWARE_EFR:
96 case IVRS_TYPE_HARDWARE_MIXED:
97 if (!iter(ivrs_hdr, arg))
98 return;
99 break;
100
101 case ACPI_IVRS_TYPE_MEMORY1:
102 case ACPI_IVRS_TYPE_MEMORY2:
103 case ACPI_IVRS_TYPE_MEMORY3:
104 if (!iter(ivrs_hdr, arg))
105 return;
106
107 break;
108
109 default:
110 printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
111
112 }
113
114 ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
115 ivrs_hdr->Length);
116 }
117 }
118
119 static bool
ivrs_is_ivhd(UINT8 type)120 ivrs_is_ivhd(UINT8 type)
121 {
122
123 switch(type) {
124 case IVRS_TYPE_HARDWARE_LEGACY:
125 case IVRS_TYPE_HARDWARE_EFR:
126 case IVRS_TYPE_HARDWARE_MIXED:
127 return (true);
128
129 default:
130 return (false);
131 }
132 }
133
134 /* Count the number of AMD-Vi devices in the system. */
135 static int
ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he,void * arg)136 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
137 {
138 int *count;
139
140 count = (int *)arg;
141 if (ivrs_is_ivhd(ivrs_he->Type))
142 (*count)++;
143
144 return (1);
145 }
146
147 struct find_ivrs_hdr_args {
148 int i;
149 ACPI_IVRS_HEADER *ptr;
150 };
151
152 static int
ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr,void * args)153 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
154 {
155 struct find_ivrs_hdr_args *fi;
156
157 fi = (struct find_ivrs_hdr_args *)args;
158 if (ivrs_is_ivhd(ivrs_hdr->Type)) {
159 if (fi->i == 0) {
160 fi->ptr = ivrs_hdr;
161 return (0);
162 }
163 fi->i--;
164 }
165
166 return (1);
167 }
168
169 static ACPI_IVRS_HARDWARE1 *
ivhd_find_by_index(int idx)170 ivhd_find_by_index(int idx)
171 {
172 struct find_ivrs_hdr_args fi;
173
174 fi.i = idx;
175 fi.ptr = NULL;
176
177 ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
178
179 return ((ACPI_IVRS_HARDWARE1 *)fi.ptr);
180 }
181
182 static void
ivhd_dev_add_entry(struct amdvi_softc * softc,uint32_t start_id,uint32_t end_id,uint8_t cfg,bool ats)183 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
184 uint32_t end_id, uint8_t cfg, bool ats)
185 {
186 struct ivhd_dev_cfg *dev_cfg;
187
188 KASSERT(softc->dev_cfg_cap >= softc->dev_cfg_cnt,
189 ("Impossible case: number of dev_cfg exceeding capacity"));
190 if (softc->dev_cfg_cap == softc->dev_cfg_cnt) {
191 if (softc->dev_cfg_cap == 0)
192 softc->dev_cfg_cap = 1;
193 else
194 softc->dev_cfg_cap <<= 2;
195 softc->dev_cfg = realloc(softc->dev_cfg,
196 sizeof(*softc->dev_cfg) * softc->dev_cfg_cap, M_DEVBUF,
197 M_WAITOK);
198 }
199
200 dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
201 dev_cfg->start_id = start_id;
202 dev_cfg->end_id = end_id;
203 dev_cfg->data = cfg;
204 dev_cfg->enable_ats = ats;
205 }
206
207 /*
208 * Record device attributes as suggested by BIOS.
209 */
210 static int
ivhd_dev_parse(ACPI_IVRS_HARDWARE1 * ivhd,struct amdvi_softc * softc)211 ivhd_dev_parse(ACPI_IVRS_HARDWARE1 *ivhd, struct amdvi_softc *softc)
212 {
213 ACPI_IVRS_DE_HEADER *de;
214 uint8_t *p, *end;
215 int range_start_id = -1, range_end_id = -1, i;
216 uint32_t *extended;
217 uint8_t all_data = 0, range_data = 0;
218 bool range_enable_ats = false, enable_ats;
219
220 switch (ivhd->Header.Type) {
221 case IVRS_TYPE_HARDWARE_LEGACY:
222 p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE1);
223 break;
224
225 case IVRS_TYPE_HARDWARE_EFR:
226 case IVRS_TYPE_HARDWARE_MIXED:
227 p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE2);
228 break;
229
230 default:
231 device_printf(softc->dev,
232 "unknown type: 0x%x\n", ivhd->Header.Type);
233 return (-1);
234 }
235
236 end = (uint8_t *)ivhd + ivhd->Header.Length;
237
238 while (p < end) {
239 de = (ACPI_IVRS_DE_HEADER *)p;
240 switch (de->Type) {
241 case ACPI_IVRS_TYPE_ALL:
242 all_data = de->DataSetting;
243 for (i = 0; i < softc->dev_cfg_cnt; i++)
244 softc->dev_cfg[i].data |= all_data;
245 break;
246
247 case ACPI_IVRS_TYPE_SELECT:
248 case ACPI_IVRS_TYPE_ALIAS_SELECT:
249 case ACPI_IVRS_TYPE_EXT_SELECT:
250 enable_ats = false;
251 if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
252 extended = (uint32_t *)(de + 1);
253 enable_ats =
254 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
255 false : true;
256 }
257 ivhd_dev_add_entry(softc, de->Id, de->Id,
258 de->DataSetting | all_data, enable_ats);
259 break;
260
261 case ACPI_IVRS_TYPE_START:
262 case ACPI_IVRS_TYPE_ALIAS_START:
263 case ACPI_IVRS_TYPE_EXT_START:
264 if (range_start_id != -1) {
265 device_printf(softc->dev,
266 "Unexpected start-of-range device entry\n");
267 return (EINVAL);
268 }
269 range_start_id = de->Id;
270 range_data = de->DataSetting;
271 if (de->Type == ACPI_IVRS_TYPE_EXT_START) {
272 extended = (uint32_t *)(de + 1);
273 range_enable_ats =
274 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
275 false : true;
276 }
277 break;
278
279 case ACPI_IVRS_TYPE_END:
280 if (range_start_id == -1) {
281 device_printf(softc->dev,
282 "Unexpected end-of-range device entry\n");
283 return (EINVAL);
284 }
285 range_end_id = de->Id;
286 if (range_end_id < range_start_id) {
287 device_printf(softc->dev,
288 "Device entry range going backward\n");
289 return (EINVAL);
290 }
291 ivhd_dev_add_entry(softc, range_start_id, range_end_id,
292 range_data | all_data, range_enable_ats);
293 range_start_id = range_end_id = -1;
294 range_data = 0;
295 all_data = 0;
296 break;
297
298 case ACPI_IVRS_TYPE_PAD4:
299 break;
300
301 case ACPI_IVRS_TYPE_SPECIAL:
302 /* HPET or IOAPIC */
303 break;
304 default:
305 if ((de->Type < 5) ||
306 (de->Type >= ACPI_IVRS_TYPE_PAD8))
307 device_printf(softc->dev,
308 "Unknown dev entry:0x%x\n", de->Type);
309 }
310
311 if (de->Type < 0x40)
312 p += sizeof(ACPI_IVRS_DEVICE4);
313 else if (de->Type < 0x80)
314 p += sizeof(ACPI_IVRS_DEVICE8A);
315 else {
316 printf("Variable size IVHD type 0x%x not supported\n",
317 de->Type);
318 break;
319 }
320 }
321
322 return (0);
323 }
324
325 static bool
ivhd_is_newer(ACPI_IVRS_HEADER * old,ACPI_IVRS_HEADER * new)326 ivhd_is_newer(ACPI_IVRS_HEADER *old, ACPI_IVRS_HEADER *new)
327 {
328 if (old->DeviceId == new->DeviceId) {
329 /*
330 * Newer IVRS header type take precedence.
331 */
332 if (old->Type == IVRS_TYPE_HARDWARE_LEGACY &&
333 ((new->Type == IVRS_TYPE_HARDWARE_EFR) ||
334 (new->Type == IVRS_TYPE_HARDWARE_MIXED)))
335 return (true);
336
337 /*
338 * Mixed format IVHD header type take precedence
339 * over fixed format IVHD header types.
340 */
341 if (old->Type == IVRS_TYPE_HARDWARE_EFR &&
342 new->Type == IVRS_TYPE_HARDWARE_MIXED)
343 return (true);
344 }
345
346 return (false);
347 }
348
349 static void
ivhd_identify(driver_t * driver,device_t parent)350 ivhd_identify(driver_t *driver, device_t parent)
351 {
352 ACPI_TABLE_IVRS *ivrs;
353 ACPI_IVRS_HARDWARE1 *ivhd;
354 ACPI_STATUS status;
355 int i, j, count = 0;
356 uint32_t ivrs_ivinfo;
357
358 if (acpi_disabled("ivhd"))
359 return;
360
361 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
362 if (ACPI_FAILURE(status))
363 return;
364
365 if (ivrs->Header.Length == 0) {
366 return;
367 }
368
369 ivrs_ivinfo = ivrs->Info;
370 printf("AMD-Vi: IVRS Info VAsize = %d PAsize = %d GVAsize = %d"
371 " flags:%b\n",
372 REG_BITS(ivrs_ivinfo, 21, 15), REG_BITS(ivrs_ivinfo, 14, 8),
373 REG_BITS(ivrs_ivinfo, 7, 5), REG_BITS(ivrs_ivinfo, 22, 22),
374 "\020\001EFRSup");
375
376 ivrs_hdr_iterate_tbl(ivhd_count_iter, &count);
377 if (!count)
378 return;
379
380 ivhd_hdrs = malloc(sizeof(void *) * count, M_DEVBUF,
381 M_WAITOK | M_ZERO);
382 for (i = 0; i < count; i++) {
383 ivhd = ivhd_find_by_index(i);
384 KASSERT(ivhd, ("ivhd%d is NULL\n", i));
385
386 /*
387 * Scan for presence of legacy and non-legacy device type
388 * for same IOMMU device and override the old one.
389 *
390 * If there is no existing IVHD to the same IOMMU device,
391 * the IVHD header pointer is appended.
392 */
393 for (j = 0; j < ivhd_count; j++) {
394 if (ivhd_is_newer(&ivhd_hdrs[j]->Header, &ivhd->Header))
395 break;
396 }
397 ivhd_hdrs[j] = ivhd;
398 if (j == ivhd_count)
399 ivhd_count++;
400 }
401
402 ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
403 M_WAITOK | M_ZERO);
404 for (i = 0, j = 0; i < ivhd_count; i++) {
405 ivhd = ivhd_hdrs[i];
406 KASSERT(ivhd, ("ivhd%d is NULL\n", i));
407
408 /*
409 * Use a high order to ensure that this driver is probed after
410 * the Host-PCI bridge and the root PCI bus.
411 */
412 ivhd_devs[i] = BUS_ADD_CHILD(parent,
413 ACPI_DEV_BASE_ORDER + 10 * 10, "ivhd", i);
414
415 /*
416 * XXX: In case device was not destroyed before, add will fail.
417 * locate the old device instance.
418 */
419 if (ivhd_devs[i] == NULL) {
420 ivhd_devs[i] = device_find_child(parent, "ivhd", i);
421 if (ivhd_devs[i] == NULL) {
422 printf("AMD-Vi: can't find ivhd%d\n", i);
423 break;
424 }
425 }
426 j++;
427 }
428
429 /*
430 * Update device count in case failed to attach.
431 */
432 ivhd_count = j;
433 }
434
435 static int
ivhd_probe(device_t dev)436 ivhd_probe(device_t dev)
437 {
438 ACPI_IVRS_HARDWARE1 *ivhd;
439 int unit;
440
441 if (acpi_get_handle(dev) != NULL)
442 return (ENXIO);
443
444 unit = device_get_unit(dev);
445 KASSERT((unit < ivhd_count),
446 ("ivhd unit %d > count %d", unit, ivhd_count));
447 ivhd = ivhd_hdrs[unit];
448 KASSERT(ivhd, ("ivhd is NULL"));
449
450 switch (ivhd->Header.Type) {
451 case IVRS_TYPE_HARDWARE_EFR:
452 device_set_desc(dev, "AMD-Vi/IOMMU ivhd with EFR");
453 break;
454
455 case IVRS_TYPE_HARDWARE_MIXED:
456 device_set_desc(dev, "AMD-Vi/IOMMU ivhd in mixed format");
457 break;
458
459 case IVRS_TYPE_HARDWARE_LEGACY:
460 default:
461 device_set_desc(dev, "AMD-Vi/IOMMU ivhd");
462 break;
463 }
464
465 return (BUS_PROBE_NOWILDCARD);
466 }
467
468 static void
ivhd_print_flag(device_t dev,enum IvrsType ivhd_type,uint8_t flag)469 ivhd_print_flag(device_t dev, enum IvrsType ivhd_type, uint8_t flag)
470 {
471 /*
472 * IVHD lgeacy type has two extra high bits in flag which has
473 * been moved to EFR for non-legacy device.
474 */
475 switch (ivhd_type) {
476 case IVRS_TYPE_HARDWARE_LEGACY:
477 device_printf(dev, "Flag:%b\n", flag,
478 "\020"
479 "\001HtTunEn"
480 "\002PassPW"
481 "\003ResPassPW"
482 "\004Isoc"
483 "\005IotlbSup"
484 "\006Coherent"
485 "\007PreFSup"
486 "\010PPRSup");
487 break;
488
489 case IVRS_TYPE_HARDWARE_EFR:
490 case IVRS_TYPE_HARDWARE_MIXED:
491 device_printf(dev, "Flag:%b\n", flag,
492 "\020"
493 "\001HtTunEn"
494 "\002PassPW"
495 "\003ResPassPW"
496 "\004Isoc"
497 "\005IotlbSup"
498 "\006Coherent");
499 break;
500
501 default:
502 device_printf(dev, "Can't decode flag of ivhd type :0x%x\n",
503 ivhd_type);
504 break;
505 }
506 }
507
508 /*
509 * Feature in legacy IVHD type(0x10) and attribute in newer type(0x11 and 0x40).
510 */
511 static void
ivhd_print_feature(device_t dev,enum IvrsType ivhd_type,uint32_t feature)512 ivhd_print_feature(device_t dev, enum IvrsType ivhd_type, uint32_t feature)
513 {
514 switch (ivhd_type) {
515 case IVRS_TYPE_HARDWARE_LEGACY:
516 device_printf(dev, "Features(type:0x%x) HATS = %d GATS = %d"
517 " MsiNumPPR = %d PNBanks= %d PNCounters= %d\n",
518 ivhd_type,
519 REG_BITS(feature, 31, 30),
520 REG_BITS(feature, 29, 28),
521 REG_BITS(feature, 27, 23),
522 REG_BITS(feature, 22, 17),
523 REG_BITS(feature, 16, 13));
524 device_printf(dev, "max PASID = %d GLXSup = %d Feature:%b\n",
525 REG_BITS(feature, 12, 8),
526 REG_BITS(feature, 4, 3),
527 feature,
528 "\020"
529 "\002NXSup"
530 "\003GTSup"
531 "\004<b4>"
532 "\005IASup"
533 "\006GASup"
534 "\007HESup");
535 break;
536
537 /* Fewer features or attributes are reported in non-legacy type. */
538 case IVRS_TYPE_HARDWARE_EFR:
539 case IVRS_TYPE_HARDWARE_MIXED:
540 device_printf(dev, "Features(type:0x%x) MsiNumPPR = %d"
541 " PNBanks= %d PNCounters= %d\n",
542 ivhd_type,
543 REG_BITS(feature, 27, 23),
544 REG_BITS(feature, 22, 17),
545 REG_BITS(feature, 16, 13));
546 break;
547
548 default: /* Other ivhd type features are not decoded. */
549 device_printf(dev, "Can't decode ivhd type :0x%x\n", ivhd_type);
550 }
551 }
552
553 /* Print extended features of IOMMU. */
554 static void
ivhd_print_ext_feature(device_t dev,uint64_t ext_feature)555 ivhd_print_ext_feature(device_t dev, uint64_t ext_feature)
556 {
557 uint32_t ext_low, ext_high;
558
559 if (!ext_feature)
560 return;
561
562 ext_low = ext_feature;
563 device_printf(dev, "Extended features[31:0]:%b "
564 "HATS = 0x%x GATS = 0x%x "
565 "GLXSup = 0x%x SmiFSup = 0x%x SmiFRC = 0x%x "
566 "GAMSup = 0x%x DualPortLogSup = 0x%x DualEventLogSup = 0x%x\n",
567 (int)ext_low,
568 "\020"
569 "\001PreFSup"
570 "\002PPRSup"
571 "\003<b2>"
572 "\004NXSup"
573 "\005GTSup"
574 "\006<b5>"
575 "\007IASup"
576 "\010GASup"
577 "\011HESup"
578 "\012PCSup",
579 REG_BITS(ext_low, 11, 10),
580 REG_BITS(ext_low, 13, 12),
581 REG_BITS(ext_low, 15, 14),
582 REG_BITS(ext_low, 17, 16),
583 REG_BITS(ext_low, 20, 18),
584 REG_BITS(ext_low, 23, 21),
585 REG_BITS(ext_low, 25, 24),
586 REG_BITS(ext_low, 29, 28));
587
588 ext_high = ext_feature >> 32;
589 device_printf(dev, "Extended features[62:32]:%b "
590 "Max PASID: 0x%x DevTblSegSup = 0x%x "
591 "MarcSup = 0x%x\n",
592 (int)(ext_high),
593 "\020"
594 "\006USSup"
595 "\011PprOvrflwEarlySup"
596 "\012PPRAutoRspSup"
597 "\015BlKStopMrkSup"
598 "\016PerfOptSup"
599 "\017MsiCapMmioSup"
600 "\021GIOSup"
601 "\022HASup"
602 "\023EPHSup"
603 "\024AttrFWSup"
604 "\025HDSup"
605 "\027InvIotlbSup",
606 REG_BITS(ext_high, 5, 0),
607 REG_BITS(ext_high, 8, 7),
608 REG_BITS(ext_high, 11, 10));
609 }
610
611 static int
ivhd_print_cap(struct amdvi_softc * softc,ACPI_IVRS_HARDWARE1 * ivhd)612 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE1 * ivhd)
613 {
614 device_t dev;
615 int max_ptp_level;
616
617 dev = softc->dev;
618
619 ivhd_print_flag(dev, softc->ivhd_type, softc->ivhd_flag);
620 ivhd_print_feature(dev, softc->ivhd_type, softc->ivhd_feature);
621 ivhd_print_ext_feature(dev, softc->ext_feature);
622 max_ptp_level = 7;
623 /* Make sure device support minimum page level as requested by user. */
624 if (max_ptp_level < amdvi_ptp_level) {
625 device_printf(dev, "insufficient PTP level:%d\n",
626 max_ptp_level);
627 return (EINVAL);
628 } else {
629 device_printf(softc->dev, "supported paging level:%d, will use only: %d\n",
630 max_ptp_level, amdvi_ptp_level);
631 }
632
633 return (0);
634 }
635
636 static int
ivhd_attach(device_t dev)637 ivhd_attach(device_t dev)
638 {
639 ACPI_IVRS_HARDWARE1 *ivhd;
640 ACPI_IVRS_HARDWARE2 *ivhd_efr;
641 struct amdvi_softc *softc;
642 int status, unit;
643
644 unit = device_get_unit(dev);
645 KASSERT((unit < ivhd_count),
646 ("ivhd unit %d > count %d", unit, ivhd_count));
647 /* Make sure its same device for which attach is called. */
648 KASSERT((ivhd_devs[unit] == dev),
649 ("Not same device old %p new %p", ivhd_devs[unit], dev));
650
651 softc = device_get_softc(dev);
652 softc->dev = dev;
653 ivhd = ivhd_hdrs[unit];
654 KASSERT(ivhd, ("ivhd is NULL"));
655 softc->pci_dev = pci_find_bsf(PCI_RID2BUS(ivhd->Header.DeviceId),
656 PCI_RID2SLOT(ivhd->Header.DeviceId),
657 PCI_RID2FUNC(ivhd->Header.DeviceId));
658
659 softc->ivhd_type = ivhd->Header.Type;
660 softc->pci_seg = ivhd->PciSegmentGroup;
661 softc->pci_rid = ivhd->Header.DeviceId;
662 softc->ivhd_flag = ivhd->Header.Flags;
663 /*
664 * On lgeacy IVHD type(0x10), it is documented as feature
665 * but in newer type it is attribute.
666 */
667 softc->ivhd_feature = ivhd->FeatureReporting;
668 /*
669 * PCI capability has more capabilities that are not part of IVRS.
670 */
671 softc->cap_off = ivhd->CapabilityOffset;
672
673 #ifdef notyet
674 /* IVHD Info bit[4:0] is event MSI/X number. */
675 softc->event_msix = ivhd->Info & 0x1F;
676 #endif
677 switch (ivhd->Header.Type) {
678 case IVRS_TYPE_HARDWARE_EFR:
679 case IVRS_TYPE_HARDWARE_MIXED:
680 ivhd_efr = (ACPI_IVRS_HARDWARE2 *)ivhd;
681 softc->ext_feature = ivhd_efr->EfrRegisterImage;
682 break;
683 }
684
685 softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
686 status = ivhd_dev_parse(ivhd, softc);
687 if (status != 0) {
688 device_printf(dev,
689 "endpoint device parsing error=%d\n", status);
690 goto fail;
691 }
692
693 status = ivhd_print_cap(softc, ivhd);
694 if (status != 0)
695 goto fail;
696
697 status = amdvi_setup_hw(softc);
698 if (status != 0) {
699 device_printf(dev, "couldn't be initialised, error=%d\n",
700 status);
701 goto fail;
702 }
703
704 return (0);
705
706 fail:
707 free(softc->dev_cfg, M_DEVBUF);
708 return (status);
709 }
710
711 static int
ivhd_detach(device_t dev)712 ivhd_detach(device_t dev)
713 {
714 struct amdvi_softc *softc;
715
716 softc = device_get_softc(dev);
717
718 amdvi_teardown_hw(softc);
719 free(softc->dev_cfg, M_DEVBUF);
720
721 /*
722 * XXX: delete the device.
723 * don't allow detach, return EBUSY.
724 */
725 return (0);
726 }
727
728 static int
ivhd_suspend(device_t dev)729 ivhd_suspend(device_t dev)
730 {
731
732 return (0);
733 }
734
735 static int
ivhd_resume(device_t dev)736 ivhd_resume(device_t dev)
737 {
738
739 return (0);
740 }
741
742 static device_method_t ivhd_methods[] = {
743 DEVMETHOD(device_identify, ivhd_identify),
744 DEVMETHOD(device_probe, ivhd_probe),
745 DEVMETHOD(device_attach, ivhd_attach),
746 DEVMETHOD(device_detach, ivhd_detach),
747 DEVMETHOD(device_suspend, ivhd_suspend),
748 DEVMETHOD(device_resume, ivhd_resume),
749 DEVMETHOD_END
750 };
751
752 static driver_t ivhd_driver = {
753 "ivhd",
754 ivhd_methods,
755 sizeof(struct amdvi_softc),
756 };
757
758 static devclass_t ivhd_devclass;
759
760 /*
761 * Load this module at the end after PCI re-probing to configure interrupt.
762 */
763 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
764 SI_ORDER_ANY);
765 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
766 MODULE_DEPEND(ivhd, pci, 1, 1, 1);
767