2 * SPDX-License-Identifier: BSD-2-Clause
4 * Copyright (c) 2020 Beckhoff Automation GmbH & Co. KG
5 * Author: Corvin Köhne <c.koehne@beckhoff.com>
10 #include <sys/sysctl.h>
12 #include <dev/pci/pcireg.h>
21 #include "pci_gvt-d-opregion.h"
22 #include "pci_passthru.h"
25 #define MB (1024 * KB)
26 #define GB (1024 * MB)
29 #define _PATH_MEM "/dev/mem"
32 #define PCI_VENDOR_INTEL 0x8086
34 #define PCIR_BDSM 0x5C /* Base of Data Stolen Memory register */
35 #define PCIR_ASLS_CTL 0xFC /* Opregion start address register */
37 #define PCIM_BDSM_GSM_ALIGNMENT \
38 0x00100000 /* Graphics Stolen Memory is 1 MB aligned */
40 #define GVT_D_MAP_GSM 0
41 #define GVT_D_MAP_OPREGION 1
44 gvt_d_probe(struct pci_devinst *const pi)
46 struct passthru_softc *sc;
52 vendor = read_config(passthru_get_sel(sc), PCIR_VENDOR, 0x02);
53 if (vendor != PCI_VENDOR_INTEL)
56 class = read_config(passthru_get_sel(sc), PCIR_CLASS, 0x01);
57 if (class != PCIC_DISPLAY)
64 gvt_d_alloc_mmio_memory(const vm_paddr_t host_address, const vm_paddr_t length,
65 const vm_paddr_t alignment, const enum e820_memory_type type)
69 /* Try to reuse host address. */
70 address = e820_alloc(host_address, length, E820_ALIGNMENT_NONE, type,
71 E820_ALLOCATE_SPECIFIC);
77 * We're not able to reuse the host address. Fall back to the highest usable
81 e820_alloc(4 * GB, length, alignment, type, E820_ALLOCATE_HIGHEST));
85 * Note that the graphics stolen memory is somehow confusing. On the one hand
86 * the Intel Open Source HD Graphics Programmers' Reference Manual states that
87 * it's only GPU accessible. As the CPU can't access the area, the guest
88 * shouldn't need it. On the other hand, the Intel GOP driver refuses to work
89 * properly, if it's not set to a proper address.
91 * Intel itself maps it into the guest by EPT [1]. At the moment, we're not
92 * aware of any situation where this EPT mapping is required, so we don't do it
95 * Intel also states that the Windows driver for Tiger Lake reads the address of
96 * the graphics stolen memory [2]. As the GVT-d code doesn't support Tiger Lake
97 * in its first implementation, we can't check how it behaves. We should keep an
101 * https://github.com/projectacrn/acrn-hypervisor/blob/e28d6fbfdfd556ff1bc3ff330e41d4ddbaa0f897/devicemodel/hw/pci/passthrough.c#L655-L657
103 * https://github.com/projectacrn/acrn-hypervisor/blob/e28d6fbfdfd556ff1bc3ff330e41d4ddbaa0f897/devicemodel/hw/pci/passthrough.c#L626-L629
106 gvt_d_setup_gsm(struct pci_devinst *const pi)
108 struct passthru_softc *sc;
109 struct passthru_mmio_mapping *gsm;
116 gsm = passthru_get_mmio(sc, GVT_D_MAP_GSM);
118 warnx("%s: Unable to access gsm", __func__);
122 sysctl_len = sizeof(gsm->hpa);
123 error = sysctlbyname("hw.intel_graphics_stolen_base", &gsm->hpa,
124 &sysctl_len, NULL, 0);
126 warn("%s: Unable to get graphics stolen memory base",
130 sysctl_len = sizeof(gsm->len);
131 error = sysctlbyname("hw.intel_graphics_stolen_size", &gsm->len,
132 &sysctl_len, NULL, 0);
134 warn("%s: Unable to get graphics stolen memory length",
138 gsm->hva = NULL; /* unused */
139 gsm->gva = NULL; /* unused */
140 gsm->gpa = gvt_d_alloc_mmio_memory(gsm->hpa, gsm->len,
141 PCIM_BDSM_GSM_ALIGNMENT, E820_TYPE_RESERVED);
144 "%s: Unable to add Graphics Stolen Memory to E820 table (hpa 0x%lx len 0x%lx)",
145 __func__, gsm->hpa, gsm->len);
149 if (gsm->gpa != gsm->hpa) {
151 * ACRN source code implies that graphics driver for newer Intel
152 * platforms like Tiger Lake will read the Graphics Stolen Memory
153 * address from an MMIO register. We have three options to solve this
155 * 1. Patch the value in the MMIO register
156 * This could have unintended side effects. Without any
157 * documentation how this register is used by the GPU, don't do
159 * 2. Trap the MMIO register
160 * It's not possible to trap a single MMIO register. We need to
161 * trap a whole page. Trapping a bunch of MMIO register could
162 * degrade the performance noticeably. We have to test it.
163 * 3. Use an 1:1 host to guest mapping
164 * Maybe not always possible. As far as we know, no supported
165 * platform requires a 1:1 mapping. For that reason, just log a
169 "Warning: Unable to reuse host address of Graphics Stolen Memory. GPU passthrough might not work properly.");
172 bdsm = read_config(passthru_get_sel(sc), PCIR_BDSM, 4);
173 pci_set_cfgdata32(pi, PCIR_BDSM,
174 gsm->gpa | (bdsm & (PCIM_BDSM_GSM_ALIGNMENT - 1)));
176 return (set_pcir_handler(sc, PCIR_BDSM, 4, passthru_cfgread_emulate,
177 passthru_cfgwrite_emulate));
181 gvt_d_setup_opregion(struct pci_devinst *const pi)
183 struct passthru_softc *sc;
184 struct passthru_mmio_mapping *opregion;
185 struct igd_opregion_header *header;
191 memfd = open(_PATH_MEM, O_RDONLY, 0);
193 warn("%s: Failed to open %s", __func__, _PATH_MEM);
197 opregion = passthru_get_mmio(sc, GVT_D_MAP_OPREGION);
198 if (opregion == NULL) {
199 warnx("%s: Unable to access opregion", __func__);
204 asls = read_config(passthru_get_sel(sc), PCIR_ASLS_CTL, 4);
206 header = mmap(NULL, sizeof(*header), PROT_READ, MAP_SHARED, memfd,
208 if (header == MAP_FAILED) {
209 warn("%s: Unable to map OpRegion header", __func__);
213 if (memcmp(header->sign, IGD_OPREGION_HEADER_SIGN,
214 sizeof(header->sign)) != 0) {
215 warnx("%s: Invalid OpRegion signature", __func__);
216 munmap(header, sizeof(*header));
221 opregion->hpa = asls;
222 opregion->len = header->size * KB;
223 munmap(header, sizeof(header));
225 opregion->hva = mmap(NULL, opregion->len * KB, PROT_READ, MAP_SHARED,
226 memfd, opregion->hpa);
227 if (opregion->hva == MAP_FAILED) {
228 warn("%s: Unable to map host OpRegion", __func__);
234 opregion->gpa = gvt_d_alloc_mmio_memory(opregion->hpa, opregion->len,
235 E820_ALIGNMENT_NONE, E820_TYPE_NVS);
236 if (opregion->gpa == 0) {
238 "%s: Unable to add OpRegion to E820 table (hpa 0x%lx len 0x%lx)",
239 __func__, opregion->hpa, opregion->len);
243 opregion->gva = vm_map_gpa(pi->pi_vmctx, opregion->gpa, opregion->len);
244 if (opregion->gva == NULL) {
245 warnx("%s: Unable to map guest OpRegion", __func__);
248 if (opregion->gpa != opregion->hpa) {
250 * A 1:1 host to guest mapping is not required but this could
251 * change in the future.
254 "Warning: Unable to reuse host address of OpRegion. GPU passthrough might not work properly.");
257 memcpy(opregion->gva, opregion->hva, opregion->len);
259 pci_set_cfgdata32(pi, PCIR_ASLS_CTL, opregion->gpa);
261 return (set_pcir_handler(sc, PCIR_ASLS_CTL, 4, passthru_cfgread_emulate,
262 passthru_cfgwrite_emulate));
266 gvt_d_init(struct pci_devinst *const pi, nvlist_t *const nvl __unused)
270 if ((error = gvt_d_setup_gsm(pi)) != 0) {
271 warnx("%s: Unable to setup Graphics Stolen Memory", __func__);
275 if ((error = gvt_d_setup_opregion(pi)) != 0) {
276 warnx("%s: Unable to setup OpRegion", __func__);
285 gvt_d_deinit(struct pci_devinst *const pi)
287 struct passthru_softc *sc;
288 struct passthru_mmio_mapping *opregion;
292 opregion = passthru_get_mmio(sc, GVT_D_MAP_OPREGION);
294 /* HVA is only set, if it's initialized */
296 munmap((void *)opregion->hva, opregion->len);
299 static struct passthru_dev gvt_d_dev = {
300 .probe = gvt_d_probe,
302 .deinit = gvt_d_deinit,
304 PASSTHRU_DEV_SET(gvt_d_dev);