]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/amd64/vmm/amd/ivrs_drv.c
MFV r329799, r329800:
[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 header. */
51 /* 
52  * Cached IVHD header list.
53  * Single entry for each IVHD, filtered the legacy one.
54  */
55 ACPI_IVRS_HARDWARE *ivhd_hdrs[10];      
56
57 extern int amdvi_ptp_level;             /* Page table levels. */
58
59 typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER *ptr, void *arg);
60 /*
61  * Iterate IVRS table for IVHD and IVMD device type.
62  */
63 static void
64 ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg)
65 {
66         ACPI_TABLE_IVRS *ivrs;
67         ACPI_IVRS_HEADER *ivrs_hdr, *end;
68         ACPI_STATUS status;
69
70         status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
71         if (ACPI_FAILURE(status))
72                 return;
73
74         if (ivrs->Header.Length == 0) {
75                 return;
76         }
77
78         ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1);
79         end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length);
80
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",
84                             ivrs_hdr->Length);
85                         break;
86                 }
87
88                 switch (ivrs_hdr->Type) {
89                 case ACPI_IVRS_TYPE_HARDWARE:   /* Legacy */
90                 case 0x11:
91                 case 0x40:                      /* ACPI HID */
92                         if (!iter(ivrs_hdr, arg))
93                                 return;
94                         break;
95
96                 case ACPI_IVRS_TYPE_MEMORY1:
97                 case ACPI_IVRS_TYPE_MEMORY2:
98                 case ACPI_IVRS_TYPE_MEMORY3:
99                         if (!iter(ivrs_hdr, arg))
100                                 return;
101
102                         break;
103
104                 default:
105                         printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type);
106
107                 }
108
109                 ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr +
110                         ivrs_hdr->Length);
111         }
112 }
113
114 static bool
115 ivrs_is_ivhd(UINT8 type)
116 {
117
118         switch(type) {
119         case ACPI_IVRS_TYPE_HARDWARE:
120         case ACPI_IVRS_TYPE_HARDWARE_EXT1:
121         case ACPI_IVRS_TYPE_HARDWARE_EXT2:
122                 return (true);
123
124         default:
125                 return (false);
126         }
127 }
128
129 /* Count the number of AMD-Vi devices in the system. */
130 static int
131 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg)
132 {
133
134         if (ivrs_is_ivhd(ivrs_he->Type))
135                 ivhd_count++;
136
137         return (1);
138 }
139
140 struct find_ivrs_hdr_args {
141         int     i;
142         ACPI_IVRS_HEADER *ptr;
143 };
144
145 static int
146 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args)
147 {
148         struct find_ivrs_hdr_args *fi;
149
150         fi = (struct find_ivrs_hdr_args *)args;
151         if (ivrs_is_ivhd(ivrs_hdr->Type)) {
152                 if (fi->i == 0) {
153                         fi->ptr = ivrs_hdr;
154                         return (0);
155                 }
156                 fi->i--;
157         }
158
159         return (1);
160 }
161
162 static ACPI_IVRS_HARDWARE *
163 ivhd_find_by_index(int idx)
164 {
165         struct find_ivrs_hdr_args fi;
166
167         fi.i = idx;
168         fi.ptr = NULL;
169
170         ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi);
171
172         return ((ACPI_IVRS_HARDWARE *)fi.ptr);
173 }
174
175 static void
176 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id,
177     uint32_t end_id, uint8_t cfg, bool ats)
178 {
179         struct ivhd_dev_cfg *dev_cfg;
180
181         /* If device doesn't have special data, don't add it. */
182         if (!cfg)
183                 return;
184
185         dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++];
186         dev_cfg->start_id = start_id;
187         dev_cfg->end_id = end_id;
188         dev_cfg->data = cfg;
189         dev_cfg->enable_ats = ats;
190 }
191
192 /*
193  * Record device attributes as suggested by BIOS.
194  */
195 static int
196 ivhd_dev_parse(ACPI_IVRS_HARDWARE* ivhd, struct amdvi_softc *softc)
197 {
198         ACPI_IVRS_DE_HEADER *de;
199         uint8_t *p, *end;
200         int range_start_id = 0, range_end_id = 0;
201         uint32_t *extended;
202         uint8_t all_data = 0, range_data = 0;
203         bool range_enable_ats = false, enable_ats;
204
205         softc->start_dev_rid = ~0;
206         softc->end_dev_rid = 0;
207
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));
214                         break;
215
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));
220                         break;
221
222                 default:
223                         device_printf(softc->dev, 
224                                 "unknown type: 0x%x\n", ivhd->Header.Type);
225                         return (-1);
226         }
227
228         end = (uint8_t *)ivhd + ivhd->Header.Length;
229
230         while (p < end) {
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);
234                 switch (de->Type) {
235                 case ACPI_IVRS_TYPE_ALL:
236                         all_data = de->DataSetting;
237                         break;
238
239                 case ACPI_IVRS_TYPE_SELECT:
240                 case ACPI_IVRS_TYPE_ALIAS_SELECT:
241                 case ACPI_IVRS_TYPE_EXT_SELECT:
242                         enable_ats = false;
243                         if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) {
244                                 extended = (uint32_t *)(de + 1);
245                                 enable_ats =
246                                     (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
247                                         false : true;
248                         }
249                         ivhd_dev_add_entry(softc, de->Id, de->Id,
250                             de->DataSetting | all_data, enable_ats);
251                         break;
252
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);
260                                 range_enable_ats =
261                                     (*extended & IVHD_DEV_EXT_ATS_DISABLE) ?
262                                         false : true;
263                         }
264                         break;
265
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;
271                         range_data = 0;
272                         all_data = 0;
273                         break;
274
275                 case ACPI_IVRS_TYPE_PAD4:
276                         break;
277
278                 case ACPI_IVRS_TYPE_SPECIAL:
279                         /* HPET or IOAPIC */
280                         break;
281                 default:
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);
286                 }
287
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");
292                         return (EINVAL);
293                 }
294                 if (de->Type < 0x40)
295                         p += sizeof(ACPI_IVRS_DEVICE4);
296                 else if (de->Type < 0x80)
297                         p += sizeof(ACPI_IVRS_DEVICE8A);
298                 else {
299                         printf("Variable size IVHD type 0x%x not supported\n",
300                             de->Type);
301                         break;
302                 }
303         }
304
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));
308
309         return (0);
310 }
311
312 static bool
313 ivhd_is_newer(ACPI_IVRS_HEADER *old, ACPI_IVRS_HEADER  *new)
314 {
315         /*
316          * Newer IVRS header type take precedence.
317          */
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))) {
322                 return (true);
323         }
324
325         return (false);
326 }
327
328 static void
329 ivhd_identify(driver_t *driver, device_t parent)
330 {
331         ACPI_TABLE_IVRS *ivrs;
332         ACPI_IVRS_HARDWARE *ivhd;
333         ACPI_STATUS status;
334         int i, count = 0;
335         uint32_t ivrs_ivinfo;
336
337         if (acpi_disabled("ivhd"))
338                 return;
339
340         status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs);
341         if (ACPI_FAILURE(status))
342                 return;
343
344         if (ivrs->Header.Length == 0) {
345                 return;
346         }
347
348         ivrs_ivinfo = ivrs->Info;
349         printf("AMD-Vi: IVRS Info VAsize = %d PAsize = %d GVAsize = %d"
350                " flags:%b\n",
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),
353                 "\020\001EFRSup");
354
355         ivrs_hdr_iterate_tbl(ivhd_count_iter, NULL);
356         if (!ivhd_count)
357                 return;
358
359         for (i = 0; i < ivhd_count; i++) {
360                 ivhd = ivhd_find_by_index(i);
361                 KASSERT(ivhd, ("ivhd%d is NULL\n", i));
362                 ivhd_hdrs[i] = ivhd;
363         }
364
365         /* 
366          * Scan for presence of legacy and non-legacy device type
367          * for same AMD-Vi device and override the old one.
368          */
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];
373                         ivhd_count--;
374                 }
375        }               
376
377         ivhd_devs = malloc(sizeof(device_t) * ivhd_count, M_DEVBUF,
378                 M_WAITOK | M_ZERO);
379         for (i = 0; i < ivhd_count; i++) {
380                 ivhd = ivhd_hdrs[i];
381                 KASSERT(ivhd, ("ivhd%d is NULL\n", i));
382
383                 /*
384                  * Use a high order to ensure that this driver is probed after
385                  * the Host-PCI bridge and the root PCI bus.
386                  */
387                 ivhd_devs[i] = BUS_ADD_CHILD(parent,
388                     ACPI_DEV_BASE_ORDER + 10 * 10, "ivhd", i);
389
390                 /*
391                  * XXX: In case device was not destroyed before, add will fail.
392                  * locate the old device instance.
393                  */
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);
398                                 break;
399                         }
400                 }
401                 count++;
402         }
403
404         /*
405          * Update device count in case failed to attach.
406          */
407         ivhd_count = count;
408 }
409
410 static int
411 ivhd_probe(device_t dev)
412 {
413         ACPI_IVRS_HARDWARE *ivhd;
414         int unit;
415
416         if (acpi_get_handle(dev) != NULL)
417                 return (ENXIO);
418
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"));
424
425         if (ivhd->Header.Type == ACPI_IVRS_TYPE_HARDWARE)
426                 device_set_desc(dev, "AMD-Vi/IOMMU ivhd");
427         else    
428                 device_set_desc(dev, "AMD-Vi/IOMMU ivhd with EFR");
429
430         return (BUS_PROBE_NOWILDCARD);
431 }
432
433 static void
434 ivhd_print_flag(device_t dev, enum AcpiIvrsType ivhd_type, uint8_t flag)
435 {
436         /*
437          * IVHD lgeacy type has two extra high bits in flag which has
438          * been moved to EFR for non-legacy device.
439          */
440         switch (ivhd_type) {
441         case ACPI_IVRS_TYPE_HARDWARE:
442                 device_printf(dev, "Flag:%b\n", flag,
443                         "\020"
444                         "\001HtTunEn"
445                         "\002PassPW"
446                         "\003ResPassPW"
447                         "\004Isoc"
448                         "\005IotlbSup"
449                         "\006Coherent"
450                         "\007PreFSup"
451                         "\008PPRSup");
452                 break;
453
454         case ACPI_IVRS_TYPE_HARDWARE_EXT1:
455         case ACPI_IVRS_TYPE_HARDWARE_EXT2:
456                 device_printf(dev, "Flag:%b\n", flag,
457                         "\020"
458                         "\001HtTunEn"
459                         "\002PassPW"
460                         "\003ResPassPW"
461                         "\004Isoc"
462                         "\005IotlbSup"
463                         "\006Coherent");
464                 break;
465
466         default:
467                 device_printf(dev, "Can't decode flag of ivhd type :0x%x\n",
468                         ivhd_type);
469                 break;
470         }
471 }
472
473 /*
474  * Feature in legacy IVHD type(0x10) and attribute in newer type(0x11 and 0x40).
475  */
476 static void
477 ivhd_print_feature(device_t dev, enum AcpiIvrsType ivhd_type, uint32_t feature) 
478 {
479         switch (ivhd_type) {
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",
483                         ivhd_type,
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),
492                         feature,
493                         "\020"
494                         "\002NXSup"
495                         "\003GTSup"
496                         "\004<b4>"
497                         "\005IASup"
498                         "\006GASup"
499                         "\007HESup");
500                 break;
501
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",
507                         ivhd_type,
508                         REG_BITS(feature, 27, 23),
509                         REG_BITS(feature, 22, 17),
510                         REG_BITS(feature, 16, 13));
511                 break;
512
513         default: /* Other ivhd type features are not decoded. */
514                 device_printf(dev, "Can't decode ivhd type :0x%x\n", ivhd_type);
515         }
516 }
517
518 /* Print extended features of IOMMU. */
519 static void
520 ivhd_print_ext_feature(device_t dev, uint64_t ext_feature)
521 {
522         uint32_t ext_low, ext_high;
523
524         if (!ext_feature)
525                 return;
526
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",
532                 (int)ext_low,
533                 "\020"
534                 "\001PreFSup"
535                 "\002PPRSup"
536                 "\003<b2>"
537                 "\004NXSup"
538                 "\005GTSup"
539                 "\006<b5>"
540                 "\007IASup"
541                 "\008GASup"
542                 "\009HESup"
543                 "\010PCSup",
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));
552
553         ext_high = ext_feature >> 32;
554         device_printf(dev, "Extended features[62:32]:%b "
555                 "Max PASID: 0x%x DevTblSegSup = 0x%x "
556                 "MarcSup = 0x%x\n",
557                 (int)(ext_high),
558                 "\020"
559                 "\006USSup"
560                 "\009PprOvrflwEarlySup"
561                 "\010PPRAutoRspSup"
562                 "\013BlKStopMrkSup"
563                 "\014PerfOptSup"
564                 "\015MsiCapMmioSup"
565                 "\017GIOSup"
566                 "\018HASup"
567                 "\019EPHSup"
568                 "\020AttrFWSup"
569                 "\021HDSup"
570                 "\023InvIotlbSup",
571                 REG_BITS(ext_high, 5, 0),
572                 REG_BITS(ext_high, 8, 7),
573                 REG_BITS(ext_high, 11, 10));
574 }
575
576 static int
577 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE * ivhd)
578 {
579         device_t dev;
580         int max_ptp_level;
581
582         dev = softc->dev;
583         
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);
587         max_ptp_level = 7;
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",
591                         max_ptp_level);
592                 return (EINVAL);
593         } else {
594                 device_printf(softc->dev, "supported paging level:%d, will use only: %d\n",
595                         max_ptp_level, amdvi_ptp_level);
596         }
597
598         device_printf(softc->dev, "device range: 0x%x - 0x%x\n",
599                         softc->start_dev_rid, softc->end_dev_rid);
600
601         return (0);
602 }
603
604 static int
605 ivhd_attach(device_t dev)
606 {
607         ACPI_IVRS_HARDWARE *ivhd;
608         ACPI_IVRS_HARDWARE_NEW *ivhd1;
609         struct amdvi_softc *softc;
610         int status, unit;
611
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));
618
619         softc = device_get_softc(dev);
620         softc->dev = dev;
621         ivhd = ivhd_hdrs[unit];
622         KASSERT(ivhd, ("ivhd is NULL"));
623
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;
628         /* 
629          * On lgeacy IVHD type(0x10), it is documented as feature
630          * but in newer type it is attribute.
631          */
632         softc->ivhd_feature = ivhd->Reserved;
633         /* 
634          * PCI capability has more capabilities that are not part of IVRS.
635          */
636         softc->cap_off = ivhd->CapabilityOffset;
637
638 #ifdef notyet
639         /* IVHD Info bit[4:0] is event MSI/X number. */
640         softc->event_msix = ivhd->Info & 0x1F;
641 #endif
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;
647                         break;
648
649         }
650
651         softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress);
652         status = ivhd_dev_parse(ivhd, softc);
653         if (status != 0) {
654                 device_printf(dev,
655                     "endpoint device parsing error=%d\n", status);
656         }
657
658         status = ivhd_print_cap(softc, ivhd);
659         if (status != 0) {
660                 return (status);
661         }
662
663         status = amdvi_setup_hw(softc);
664         if (status != 0) {
665                 device_printf(dev, "couldn't be initialised, error=%d\n", 
666                     status);
667                 return (status);
668         }
669
670         return (0);
671 }
672
673 static int
674 ivhd_detach(device_t dev)
675 {
676         struct amdvi_softc *softc;
677
678         softc = device_get_softc(dev);
679
680         amdvi_teardown_hw(softc);
681
682         /*
683          * XXX: delete the device.
684          * don't allow detach, return EBUSY.
685          */
686         return (0);
687 }
688
689 static int
690 ivhd_suspend(device_t dev)
691 {
692
693         return (0);
694 }
695
696 static int
697 ivhd_resume(device_t dev)
698 {
699
700         return (0);
701 }
702
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),
710         DEVMETHOD_END
711 };
712
713 static driver_t ivhd_driver = {
714         "ivhd",
715         ivhd_methods,
716         sizeof(struct amdvi_softc),
717 };
718
719 static devclass_t ivhd_devclass;
720
721 /*
722  * Load this module at the end after PCI re-probing to configure interrupt.
723  */
724 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0,
725                       SI_ORDER_ANY);
726 MODULE_DEPEND(ivhd, acpi, 1, 1, 1);
727 MODULE_DEPEND(ivhd, pci, 1, 1, 1);