xref: /freebsd/usr.sbin/bhyve/amd64/pci_gvt-d.c (revision b2221534a7bc16ea879c9fbb1a1fe4b337d2623b)
14ab7aea8SMark Johnston /*-
24ab7aea8SMark Johnston  * SPDX-License-Identifier: BSD-2-Clause
34ab7aea8SMark Johnston  *
44ab7aea8SMark Johnston  * Copyright (c) 2020 Beckhoff Automation GmbH & Co. KG
54ab7aea8SMark Johnston  * Author: Corvin Köhne <c.koehne@beckhoff.com>
64ab7aea8SMark Johnston  */
74ab7aea8SMark Johnston 
84ab7aea8SMark Johnston #include <sys/types.h>
94ab7aea8SMark Johnston #include <sys/mman.h>
104ab7aea8SMark Johnston #include <sys/sysctl.h>
114ab7aea8SMark Johnston 
124ab7aea8SMark Johnston #include <dev/pci/pcireg.h>
134ab7aea8SMark Johnston 
144ab7aea8SMark Johnston #include <err.h>
154ab7aea8SMark Johnston #include <errno.h>
164ab7aea8SMark Johnston #include <fcntl.h>
174ab7aea8SMark Johnston #include <string.h>
184ab7aea8SMark Johnston #include <unistd.h>
194ab7aea8SMark Johnston 
204ab7aea8SMark Johnston #include "amd64/e820.h"
214ab7aea8SMark Johnston #include "pci_gvt-d-opregion.h"
224ab7aea8SMark Johnston #include "pci_passthru.h"
234ab7aea8SMark Johnston 
244ab7aea8SMark Johnston #define KB (1024UL)
254ab7aea8SMark Johnston #define MB (1024 * KB)
264ab7aea8SMark Johnston #define GB (1024 * MB)
274ab7aea8SMark Johnston 
284ab7aea8SMark Johnston #ifndef _PATH_MEM
294ab7aea8SMark Johnston #define _PATH_MEM "/dev/mem"
304ab7aea8SMark Johnston #endif
314ab7aea8SMark Johnston 
324ab7aea8SMark Johnston #define PCI_VENDOR_INTEL 0x8086
334ab7aea8SMark Johnston 
344ab7aea8SMark Johnston #define PCIR_BDSM 0x5C	   /* Base of Data Stolen Memory register */
354ab7aea8SMark Johnston #define PCIR_ASLS_CTL 0xFC /* Opregion start address register */
364ab7aea8SMark Johnston 
374ab7aea8SMark Johnston #define PCIM_BDSM_GSM_ALIGNMENT \
384ab7aea8SMark Johnston 	0x00100000 /* Graphics Stolen Memory is 1 MB aligned */
394ab7aea8SMark Johnston 
404ab7aea8SMark Johnston #define GVT_D_MAP_GSM 0
414ab7aea8SMark Johnston #define GVT_D_MAP_OPREGION 1
42*b2221534SCorvin Köhne #define GVT_D_MAP_VBT 2
434ab7aea8SMark Johnston 
444ab7aea8SMark Johnston static int
gvt_d_probe(struct pci_devinst * const pi)454ab7aea8SMark Johnston gvt_d_probe(struct pci_devinst *const pi)
464ab7aea8SMark Johnston {
474ab7aea8SMark Johnston 	struct passthru_softc *sc;
484ab7aea8SMark Johnston 	uint16_t vendor;
494ab7aea8SMark Johnston 	uint8_t class;
504ab7aea8SMark Johnston 
514ab7aea8SMark Johnston 	sc = pi->pi_arg;
524ab7aea8SMark Johnston 
534ab7aea8SMark Johnston 	vendor = pci_host_read_config(passthru_get_sel(sc), PCIR_VENDOR, 0x02);
544ab7aea8SMark Johnston 	if (vendor != PCI_VENDOR_INTEL)
554ab7aea8SMark Johnston 		return (ENXIO);
564ab7aea8SMark Johnston 
574ab7aea8SMark Johnston 	class = pci_host_read_config(passthru_get_sel(sc), PCIR_CLASS, 0x01);
584ab7aea8SMark Johnston 	if (class != PCIC_DISPLAY)
594ab7aea8SMark Johnston 		return (ENXIO);
604ab7aea8SMark Johnston 
614ab7aea8SMark Johnston 	return (0);
624ab7aea8SMark Johnston }
634ab7aea8SMark Johnston 
644ab7aea8SMark Johnston static vm_paddr_t
gvt_d_alloc_mmio_memory(const vm_paddr_t host_address,const vm_paddr_t length,const vm_paddr_t alignment,const enum e820_memory_type type)654ab7aea8SMark Johnston gvt_d_alloc_mmio_memory(const vm_paddr_t host_address, const vm_paddr_t length,
664ab7aea8SMark Johnston     const vm_paddr_t alignment, const enum e820_memory_type type)
674ab7aea8SMark Johnston {
684ab7aea8SMark Johnston 	vm_paddr_t address;
694ab7aea8SMark Johnston 
704ab7aea8SMark Johnston 	/* Try to reuse host address. */
714ab7aea8SMark Johnston 	address = e820_alloc(host_address, length, E820_ALIGNMENT_NONE, type,
724ab7aea8SMark Johnston 	    E820_ALLOCATE_SPECIFIC);
734ab7aea8SMark Johnston 	if (address != 0) {
744ab7aea8SMark Johnston 		return (address);
754ab7aea8SMark Johnston 	}
764ab7aea8SMark Johnston 
774ab7aea8SMark Johnston 	/*
784ab7aea8SMark Johnston 	 * We're not able to reuse the host address. Fall back to the highest usable
794ab7aea8SMark Johnston 	 * address below 4 GB.
804ab7aea8SMark Johnston 	 */
814ab7aea8SMark Johnston 	return (
824ab7aea8SMark Johnston 	    e820_alloc(4 * GB, length, alignment, type, E820_ALLOCATE_HIGHEST));
834ab7aea8SMark Johnston }
844ab7aea8SMark Johnston 
854ab7aea8SMark Johnston /*
864ab7aea8SMark Johnston  * Note that the graphics stolen memory is somehow confusing. On the one hand
874ab7aea8SMark Johnston  * the Intel Open Source HD Graphics Programmers' Reference Manual states that
884ab7aea8SMark Johnston  * it's only GPU accessible. As the CPU can't access the area, the guest
894ab7aea8SMark Johnston  * shouldn't need it. On the other hand, the Intel GOP driver refuses to work
904ab7aea8SMark Johnston  * properly, if it's not set to a proper address.
914ab7aea8SMark Johnston  *
924ab7aea8SMark Johnston  * Intel itself maps it into the guest by EPT [1]. At the moment, we're not
934ab7aea8SMark Johnston  * aware of any situation where this EPT mapping is required, so we don't do it
944ab7aea8SMark Johnston  * yet.
954ab7aea8SMark Johnston  *
964ab7aea8SMark Johnston  * Intel also states that the Windows driver for Tiger Lake reads the address of
974ab7aea8SMark Johnston  * the graphics stolen memory [2]. As the GVT-d code doesn't support Tiger Lake
984ab7aea8SMark Johnston  * in its first implementation, we can't check how it behaves. We should keep an
994ab7aea8SMark Johnston  * eye on it.
1004ab7aea8SMark Johnston  *
1014ab7aea8SMark Johnston  * [1]
1024ab7aea8SMark Johnston  * https://github.com/projectacrn/acrn-hypervisor/blob/e28d6fbfdfd556ff1bc3ff330e41d4ddbaa0f897/devicemodel/hw/pci/passthrough.c#L655-L657
1034ab7aea8SMark Johnston  * [2]
1044ab7aea8SMark Johnston  * https://github.com/projectacrn/acrn-hypervisor/blob/e28d6fbfdfd556ff1bc3ff330e41d4ddbaa0f897/devicemodel/hw/pci/passthrough.c#L626-L629
1054ab7aea8SMark Johnston  */
1064ab7aea8SMark Johnston static int
gvt_d_setup_gsm(struct pci_devinst * const pi)1074ab7aea8SMark Johnston gvt_d_setup_gsm(struct pci_devinst *const pi)
1084ab7aea8SMark Johnston {
1094ab7aea8SMark Johnston 	struct passthru_softc *sc;
1104ab7aea8SMark Johnston 	struct passthru_mmio_mapping *gsm;
1114ab7aea8SMark Johnston 	size_t sysctl_len;
1124ab7aea8SMark Johnston 	uint32_t bdsm;
1134ab7aea8SMark Johnston 	int error;
1144ab7aea8SMark Johnston 
1154ab7aea8SMark Johnston 	sc = pi->pi_arg;
1164ab7aea8SMark Johnston 
1174ab7aea8SMark Johnston 	gsm = passthru_get_mmio(sc, GVT_D_MAP_GSM);
1184ab7aea8SMark Johnston 	if (gsm == NULL) {
1194ab7aea8SMark Johnston 		warnx("%s: Unable to access gsm", __func__);
1204ab7aea8SMark Johnston 		return (-1);
1214ab7aea8SMark Johnston 	}
1224ab7aea8SMark Johnston 
1234ab7aea8SMark Johnston 	sysctl_len = sizeof(gsm->hpa);
1244ab7aea8SMark Johnston 	error = sysctlbyname("hw.intel_graphics_stolen_base", &gsm->hpa,
1254ab7aea8SMark Johnston 	    &sysctl_len, NULL, 0);
1264ab7aea8SMark Johnston 	if (error) {
1274ab7aea8SMark Johnston 		warn("%s: Unable to get graphics stolen memory base",
1284ab7aea8SMark Johnston 		    __func__);
1294ab7aea8SMark Johnston 		return (-1);
1304ab7aea8SMark Johnston 	}
1314ab7aea8SMark Johnston 	sysctl_len = sizeof(gsm->len);
1324ab7aea8SMark Johnston 	error = sysctlbyname("hw.intel_graphics_stolen_size", &gsm->len,
1334ab7aea8SMark Johnston 	    &sysctl_len, NULL, 0);
1344ab7aea8SMark Johnston 	if (error) {
1354ab7aea8SMark Johnston 		warn("%s: Unable to get graphics stolen memory length",
1364ab7aea8SMark Johnston 		    __func__);
1374ab7aea8SMark Johnston 		return (-1);
1384ab7aea8SMark Johnston 	}
1394ab7aea8SMark Johnston 	gsm->hva = NULL; /* unused */
1404ab7aea8SMark Johnston 	gsm->gva = NULL; /* unused */
1414ab7aea8SMark Johnston 	gsm->gpa = gvt_d_alloc_mmio_memory(gsm->hpa, gsm->len,
1424ab7aea8SMark Johnston 	    PCIM_BDSM_GSM_ALIGNMENT, E820_TYPE_RESERVED);
1434ab7aea8SMark Johnston 	if (gsm->gpa == 0) {
1444ab7aea8SMark Johnston 		warnx(
1454ab7aea8SMark Johnston 		    "%s: Unable to add Graphics Stolen Memory to E820 table (hpa 0x%lx len 0x%lx)",
1464ab7aea8SMark Johnston 		    __func__, gsm->hpa, gsm->len);
1474ab7aea8SMark Johnston 		e820_dump_table();
1484ab7aea8SMark Johnston 		return (-1);
1494ab7aea8SMark Johnston 	}
1504ab7aea8SMark Johnston 	if (gsm->gpa != gsm->hpa) {
1514ab7aea8SMark Johnston 		/*
1524ab7aea8SMark Johnston 		 * ACRN source code implies that graphics driver for newer Intel
1534ab7aea8SMark Johnston 		 * platforms like Tiger Lake will read the Graphics Stolen Memory
1544ab7aea8SMark Johnston 		 * address from an MMIO register. We have three options to solve this
1554ab7aea8SMark Johnston 		 * issue:
1564ab7aea8SMark Johnston 		 *    1. Patch the value in the MMIO register
1574ab7aea8SMark Johnston 		 *       This could have unintended side effects. Without any
1584ab7aea8SMark Johnston 		 *       documentation how this register is used by the GPU, don't do
1594ab7aea8SMark Johnston 		 *       it.
1604ab7aea8SMark Johnston 		 *    2. Trap the MMIO register
1614ab7aea8SMark Johnston 		 *       It's not possible to trap a single MMIO register. We need to
1624ab7aea8SMark Johnston 		 *       trap a whole page. Trapping a bunch of MMIO register could
1634ab7aea8SMark Johnston 		 *       degrade the performance noticeably. We have to test it.
1644ab7aea8SMark Johnston 		 *    3. Use an 1:1 host to guest mapping
1654ab7aea8SMark Johnston 		 *       Maybe not always possible. As far as we know, no supported
1664ab7aea8SMark Johnston 		 *       platform requires a 1:1 mapping. For that reason, just log a
1674ab7aea8SMark Johnston 		 *       warning.
1684ab7aea8SMark Johnston 		 */
1694ab7aea8SMark Johnston 		warnx(
1704ab7aea8SMark Johnston 		    "Warning: Unable to reuse host address of Graphics Stolen Memory. GPU passthrough might not work properly.");
1714ab7aea8SMark Johnston 	}
1724ab7aea8SMark Johnston 
1734ab7aea8SMark Johnston 	bdsm = pci_host_read_config(passthru_get_sel(sc), PCIR_BDSM, 4);
1744ab7aea8SMark Johnston 	pci_set_cfgdata32(pi, PCIR_BDSM,
1754ab7aea8SMark Johnston 	    gsm->gpa | (bdsm & (PCIM_BDSM_GSM_ALIGNMENT - 1)));
1764ab7aea8SMark Johnston 
1774ab7aea8SMark Johnston 	return (set_pcir_handler(sc, PCIR_BDSM, 4, passthru_cfgread_emulate,
1784ab7aea8SMark Johnston 	    passthru_cfgwrite_emulate));
1794ab7aea8SMark Johnston }
1804ab7aea8SMark Johnston 
1814ab7aea8SMark Johnston static int
gvt_d_setup_vbt(struct pci_devinst * const pi,int memfd,uint64_t vbt_hpa,uint64_t vbt_len,vm_paddr_t * vbt_gpa)182*b2221534SCorvin Köhne gvt_d_setup_vbt(struct pci_devinst *const pi, int memfd, uint64_t vbt_hpa,
183*b2221534SCorvin Köhne     uint64_t vbt_len, vm_paddr_t *vbt_gpa)
184*b2221534SCorvin Köhne {
185*b2221534SCorvin Köhne 	struct passthru_softc *sc;
186*b2221534SCorvin Köhne 	struct passthru_mmio_mapping *vbt;
187*b2221534SCorvin Köhne 
188*b2221534SCorvin Köhne 	sc = pi->pi_arg;
189*b2221534SCorvin Köhne 
190*b2221534SCorvin Köhne 	vbt = passthru_get_mmio(sc, GVT_D_MAP_VBT);
191*b2221534SCorvin Köhne 	if (vbt == NULL) {
192*b2221534SCorvin Köhne 		warnx("%s: Unable to access VBT", __func__);
193*b2221534SCorvin Köhne 		return (-1);
194*b2221534SCorvin Köhne 	}
195*b2221534SCorvin Köhne 
196*b2221534SCorvin Köhne 	vbt->hpa = vbt_hpa;
197*b2221534SCorvin Köhne 	vbt->len = vbt_len;
198*b2221534SCorvin Köhne 
199*b2221534SCorvin Köhne 	vbt->hva = mmap(NULL, vbt->len, PROT_READ, MAP_SHARED, memfd, vbt->hpa);
200*b2221534SCorvin Köhne 	if (vbt->hva == MAP_FAILED) {
201*b2221534SCorvin Köhne 		warn("%s: Unable to map VBT", __func__);
202*b2221534SCorvin Köhne 		return (-1);
203*b2221534SCorvin Köhne 	}
204*b2221534SCorvin Köhne 
205*b2221534SCorvin Köhne 	vbt->gpa = gvt_d_alloc_mmio_memory(vbt->hpa, vbt->len,
206*b2221534SCorvin Köhne 	    E820_ALIGNMENT_NONE, E820_TYPE_NVS);
207*b2221534SCorvin Köhne 	if (vbt->gpa == 0) {
208*b2221534SCorvin Köhne 		warnx(
209*b2221534SCorvin Köhne 		    "%s: Unable to add VBT to E820 table (hpa 0x%lx len 0x%lx)",
210*b2221534SCorvin Köhne 		    __func__, vbt->hpa, vbt->len);
211*b2221534SCorvin Köhne 		munmap(vbt->hva, vbt->len);
212*b2221534SCorvin Köhne 		e820_dump_table();
213*b2221534SCorvin Köhne 		return (-1);
214*b2221534SCorvin Köhne 	}
215*b2221534SCorvin Köhne 	vbt->gva = vm_map_gpa(pi->pi_vmctx, vbt->gpa, vbt->len);
216*b2221534SCorvin Köhne 	if (vbt->gva == NULL) {
217*b2221534SCorvin Köhne 		warnx("%s: Unable to map guest VBT", __func__);
218*b2221534SCorvin Köhne 		munmap(vbt->hva, vbt->len);
219*b2221534SCorvin Köhne 		return (-1);
220*b2221534SCorvin Köhne 	}
221*b2221534SCorvin Köhne 
222*b2221534SCorvin Köhne 	if (vbt->gpa != vbt->hpa) {
223*b2221534SCorvin Köhne 		/*
224*b2221534SCorvin Köhne 		 * A 1:1 host to guest mapping is not required but this could
225*b2221534SCorvin Köhne 		 * change in the future.
226*b2221534SCorvin Köhne 		 */
227*b2221534SCorvin Köhne 		warnx(
228*b2221534SCorvin Köhne 		    "Warning: Unable to reuse host address of VBT. GPU passthrough might not work properly.");
229*b2221534SCorvin Köhne 	}
230*b2221534SCorvin Köhne 
231*b2221534SCorvin Köhne 	memcpy(vbt->gva, vbt->hva, vbt->len);
232*b2221534SCorvin Köhne 
233*b2221534SCorvin Köhne 	/*
234*b2221534SCorvin Köhne 	 * Return the guest physical address. It's used to patch the OpRegion
235*b2221534SCorvin Köhne 	 * properly.
236*b2221534SCorvin Köhne 	 */
237*b2221534SCorvin Köhne 	*vbt_gpa = vbt->gpa;
238*b2221534SCorvin Köhne 
239*b2221534SCorvin Köhne 	return (0);
240*b2221534SCorvin Köhne }
241*b2221534SCorvin Köhne 
242*b2221534SCorvin Köhne static int
gvt_d_setup_opregion(struct pci_devinst * const pi)2434ab7aea8SMark Johnston gvt_d_setup_opregion(struct pci_devinst *const pi)
2444ab7aea8SMark Johnston {
2454ab7aea8SMark Johnston 	struct passthru_softc *sc;
2464ab7aea8SMark Johnston 	struct passthru_mmio_mapping *opregion;
247*b2221534SCorvin Köhne 	struct igd_opregion *opregion_ptr;
2484ab7aea8SMark Johnston 	struct igd_opregion_header *header;
249*b2221534SCorvin Köhne 	vm_paddr_t vbt_gpa = 0;
250*b2221534SCorvin Köhne 	vm_paddr_t vbt_hpa;
2514ab7aea8SMark Johnston 	uint64_t asls;
252*b2221534SCorvin Köhne 	int error = 0;
2534ab7aea8SMark Johnston 	int memfd;
2544ab7aea8SMark Johnston 
2554ab7aea8SMark Johnston 	sc = pi->pi_arg;
2564ab7aea8SMark Johnston 
2574ab7aea8SMark Johnston 	memfd = open(_PATH_MEM, O_RDONLY, 0);
2584ab7aea8SMark Johnston 	if (memfd < 0) {
2594ab7aea8SMark Johnston 		warn("%s: Failed to open %s", __func__, _PATH_MEM);
2604ab7aea8SMark Johnston 		return (-1);
2614ab7aea8SMark Johnston 	}
2624ab7aea8SMark Johnston 
2634ab7aea8SMark Johnston 	opregion = passthru_get_mmio(sc, GVT_D_MAP_OPREGION);
2644ab7aea8SMark Johnston 	if (opregion == NULL) {
2654ab7aea8SMark Johnston 		warnx("%s: Unable to access opregion", __func__);
2664ab7aea8SMark Johnston 		close(memfd);
2674ab7aea8SMark Johnston 		return (-1);
2684ab7aea8SMark Johnston 	}
2694ab7aea8SMark Johnston 
2704ab7aea8SMark Johnston 	asls = pci_host_read_config(passthru_get_sel(sc), PCIR_ASLS_CTL, 4);
2714ab7aea8SMark Johnston 
2724ab7aea8SMark Johnston 	header = mmap(NULL, sizeof(*header), PROT_READ, MAP_SHARED, memfd,
2734ab7aea8SMark Johnston 	    asls);
2744ab7aea8SMark Johnston 	if (header == MAP_FAILED) {
2754ab7aea8SMark Johnston 		warn("%s: Unable to map OpRegion header", __func__);
2764ab7aea8SMark Johnston 		close(memfd);
2774ab7aea8SMark Johnston 		return (-1);
2784ab7aea8SMark Johnston 	}
2794ab7aea8SMark Johnston 	if (memcmp(header->sign, IGD_OPREGION_HEADER_SIGN,
2804ab7aea8SMark Johnston 	    sizeof(header->sign)) != 0) {
2814ab7aea8SMark Johnston 		warnx("%s: Invalid OpRegion signature", __func__);
2824ab7aea8SMark Johnston 		munmap(header, sizeof(*header));
2834ab7aea8SMark Johnston 		close(memfd);
2844ab7aea8SMark Johnston 		return (-1);
2854ab7aea8SMark Johnston 	}
2864ab7aea8SMark Johnston 
2874ab7aea8SMark Johnston 	opregion->hpa = asls;
2884ab7aea8SMark Johnston 	opregion->len = header->size * KB;
28919dbf72aSPierre Pronchery 	munmap(header, sizeof(*header));
2904ab7aea8SMark Johnston 
291e425e601SCorvin Köhne 	if (opregion->len != sizeof(struct igd_opregion)) {
292e425e601SCorvin Köhne 		warnx("%s: Invalid OpRegion size of 0x%lx", __func__,
293e425e601SCorvin Köhne 		    opregion->len);
294e425e601SCorvin Köhne 		close(memfd);
295e425e601SCorvin Köhne 		return (-1);
296e425e601SCorvin Köhne 	}
297e425e601SCorvin Köhne 
2985e09c5a1SCorvin Köhne 	opregion->hva = mmap(NULL, opregion->len, PROT_READ, MAP_SHARED, memfd,
2995e09c5a1SCorvin Köhne 	    opregion->hpa);
3004ab7aea8SMark Johnston 	if (opregion->hva == MAP_FAILED) {
3014ab7aea8SMark Johnston 		warn("%s: Unable to map host OpRegion", __func__);
3024ab7aea8SMark Johnston 		close(memfd);
3034ab7aea8SMark Johnston 		return (-1);
3044ab7aea8SMark Johnston 	}
305*b2221534SCorvin Köhne 
306*b2221534SCorvin Köhne 	opregion_ptr = (struct igd_opregion *)opregion->hva;
307*b2221534SCorvin Köhne 	if (opregion_ptr->mbox3.rvda != 0) {
308*b2221534SCorvin Köhne 		/*
309*b2221534SCorvin Köhne 		 * OpRegion v2.0 contains a physical address to the VBT. This
310*b2221534SCorvin Köhne 		 * address is useless in a guest environment. It's possible to
311*b2221534SCorvin Köhne 		 * patch that but we don't support that yet. So, the only thing
312*b2221534SCorvin Köhne 		 * we can do is give up.
313*b2221534SCorvin Köhne 		 */
314*b2221534SCorvin Köhne 		if (opregion_ptr->header.over == 0x02000000) {
315*b2221534SCorvin Köhne 			warnx(
316*b2221534SCorvin Köhne 			    "%s: VBT lays outside OpRegion. That's not yet supported for a version 2.0 OpRegion",
317*b2221534SCorvin Köhne 			    __func__);
318*b2221534SCorvin Köhne 			close(memfd);
319*b2221534SCorvin Köhne 			return (-1);
320*b2221534SCorvin Köhne 		}
321*b2221534SCorvin Köhne 		vbt_hpa = opregion->hpa + opregion_ptr->mbox3.rvda;
322*b2221534SCorvin Köhne 		if (vbt_hpa < opregion->hpa) {
323*b2221534SCorvin Köhne 			warnx(
324*b2221534SCorvin Köhne 			    "%s: overflow when calculating VBT address (OpRegion @ 0x%lx, RVDA = 0x%lx)",
325*b2221534SCorvin Köhne 			    __func__, opregion->hpa, opregion_ptr->mbox3.rvda);
326*b2221534SCorvin Köhne 			close(memfd);
327*b2221534SCorvin Köhne 			return (-1);
328*b2221534SCorvin Köhne 		}
329*b2221534SCorvin Köhne 
330*b2221534SCorvin Köhne 		if ((error = gvt_d_setup_vbt(pi, memfd, vbt_hpa,
331*b2221534SCorvin Köhne 		    opregion_ptr->mbox3.rvds, &vbt_gpa)) != 0) {
332*b2221534SCorvin Köhne 			close(memfd);
333*b2221534SCorvin Köhne 			return (error);
334*b2221534SCorvin Köhne 		}
335*b2221534SCorvin Köhne 	}
336*b2221534SCorvin Köhne 
3374ab7aea8SMark Johnston 	close(memfd);
3384ab7aea8SMark Johnston 
3394ab7aea8SMark Johnston 	opregion->gpa = gvt_d_alloc_mmio_memory(opregion->hpa, opregion->len,
3404ab7aea8SMark Johnston 	    E820_ALIGNMENT_NONE, E820_TYPE_NVS);
3414ab7aea8SMark Johnston 	if (opregion->gpa == 0) {
3424ab7aea8SMark Johnston 		warnx(
3434ab7aea8SMark Johnston 		    "%s: Unable to add OpRegion to E820 table (hpa 0x%lx len 0x%lx)",
3444ab7aea8SMark Johnston 		    __func__, opregion->hpa, opregion->len);
3454ab7aea8SMark Johnston 		e820_dump_table();
3464ab7aea8SMark Johnston 		return (-1);
3474ab7aea8SMark Johnston 	}
3484ab7aea8SMark Johnston 	opregion->gva = vm_map_gpa(pi->pi_vmctx, opregion->gpa, opregion->len);
3494ab7aea8SMark Johnston 	if (opregion->gva == NULL) {
3504ab7aea8SMark Johnston 		warnx("%s: Unable to map guest OpRegion", __func__);
3514ab7aea8SMark Johnston 		return (-1);
3524ab7aea8SMark Johnston 	}
3534ab7aea8SMark Johnston 	if (opregion->gpa != opregion->hpa) {
3544ab7aea8SMark Johnston 		/*
3554ab7aea8SMark Johnston 		 * A 1:1 host to guest mapping is not required but this could
3564ab7aea8SMark Johnston 		 * change in the future.
3574ab7aea8SMark Johnston 		 */
3584ab7aea8SMark Johnston 		warnx(
3594ab7aea8SMark Johnston 		    "Warning: Unable to reuse host address of OpRegion. GPU passthrough might not work properly.");
3604ab7aea8SMark Johnston 	}
3614ab7aea8SMark Johnston 
3624ab7aea8SMark Johnston 	memcpy(opregion->gva, opregion->hva, opregion->len);
3634ab7aea8SMark Johnston 
364*b2221534SCorvin Köhne 	/*
365*b2221534SCorvin Köhne 	 * Patch the VBT address to match our guest physical address.
366*b2221534SCorvin Köhne 	 */
367*b2221534SCorvin Köhne 	if (vbt_gpa != 0) {
368*b2221534SCorvin Köhne 		if (vbt_gpa < opregion->gpa) {
369*b2221534SCorvin Köhne 			warnx(
370*b2221534SCorvin Köhne 			    "%s: invalid guest VBT address 0x%16lx (OpRegion @ 0x%16lx)",
371*b2221534SCorvin Köhne 			    __func__, vbt_gpa, opregion->gpa);
372*b2221534SCorvin Köhne 			return (-1);
373*b2221534SCorvin Köhne 		}
374*b2221534SCorvin Köhne 
375*b2221534SCorvin Köhne 		((struct igd_opregion *)opregion->gva)->mbox3.rvda = vbt_gpa - opregion->gpa;
376*b2221534SCorvin Köhne 	}
377*b2221534SCorvin Köhne 
3784ab7aea8SMark Johnston 	pci_set_cfgdata32(pi, PCIR_ASLS_CTL, opregion->gpa);
3794ab7aea8SMark Johnston 
3804ab7aea8SMark Johnston 	return (set_pcir_handler(sc, PCIR_ASLS_CTL, 4, passthru_cfgread_emulate,
3814ab7aea8SMark Johnston 	    passthru_cfgwrite_emulate));
3824ab7aea8SMark Johnston }
3834ab7aea8SMark Johnston 
3844ab7aea8SMark Johnston static int
gvt_d_init(struct pci_devinst * const pi,nvlist_t * const nvl __unused)3854ab7aea8SMark Johnston gvt_d_init(struct pci_devinst *const pi, nvlist_t *const nvl __unused)
3864ab7aea8SMark Johnston {
3874ab7aea8SMark Johnston 	int error;
3884ab7aea8SMark Johnston 
3894ab7aea8SMark Johnston 	if ((error = gvt_d_setup_gsm(pi)) != 0) {
3904ab7aea8SMark Johnston 		warnx("%s: Unable to setup Graphics Stolen Memory", __func__);
3914ab7aea8SMark Johnston 		goto done;
3924ab7aea8SMark Johnston 	}
3934ab7aea8SMark Johnston 
3944ab7aea8SMark Johnston 	if ((error = gvt_d_setup_opregion(pi)) != 0) {
3954ab7aea8SMark Johnston 		warnx("%s: Unable to setup OpRegion", __func__);
3964ab7aea8SMark Johnston 		goto done;
3974ab7aea8SMark Johnston 	}
3984ab7aea8SMark Johnston 
3994ab7aea8SMark Johnston done:
4004ab7aea8SMark Johnston 	return (error);
4014ab7aea8SMark Johnston }
4024ab7aea8SMark Johnston 
4034ab7aea8SMark Johnston static void
gvt_d_deinit(struct pci_devinst * const pi)4044ab7aea8SMark Johnston gvt_d_deinit(struct pci_devinst *const pi)
4054ab7aea8SMark Johnston {
4064ab7aea8SMark Johnston 	struct passthru_softc *sc;
4074ab7aea8SMark Johnston 	struct passthru_mmio_mapping *opregion;
4084ab7aea8SMark Johnston 
4094ab7aea8SMark Johnston 	sc = pi->pi_arg;
4104ab7aea8SMark Johnston 
4114ab7aea8SMark Johnston 	opregion = passthru_get_mmio(sc, GVT_D_MAP_OPREGION);
4124ab7aea8SMark Johnston 
4134ab7aea8SMark Johnston 	/* HVA is only set, if it's initialized */
4144ab7aea8SMark Johnston 	if (opregion->hva)
4154ab7aea8SMark Johnston 		munmap((void *)opregion->hva, opregion->len);
4164ab7aea8SMark Johnston }
4174ab7aea8SMark Johnston 
4184ab7aea8SMark Johnston static struct passthru_dev gvt_d_dev = {
4194ab7aea8SMark Johnston 	.probe = gvt_d_probe,
4204ab7aea8SMark Johnston 	.init = gvt_d_init,
4214ab7aea8SMark Johnston 	.deinit = gvt_d_deinit,
4224ab7aea8SMark Johnston };
4234ab7aea8SMark Johnston PASSTHRU_DEV_SET(gvt_d_dev);
424