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