xref: /openbmc/qemu/hw/xen/xen_pt_graphics.c (revision 911a4efd)
1 /*
2  * graphics passthrough
3  */
4 #include "xen_pt.h"
5 #include "xen-host-pci-device.h"
6 #include "hw/xen/xen_backend.h"
7 
8 static unsigned long igd_guest_opregion;
9 static unsigned long igd_host_opregion;
10 
11 #define XEN_PCI_INTEL_OPREGION_MASK 0xfff
12 
13 typedef struct VGARegion {
14     int type;           /* Memory or port I/O */
15     uint64_t guest_base_addr;
16     uint64_t machine_base_addr;
17     uint64_t size;    /* size of the region */
18     int rc;
19 } VGARegion;
20 
21 #define IORESOURCE_IO           0x00000100
22 #define IORESOURCE_MEM          0x00000200
23 
24 static struct VGARegion vga_args[] = {
25     {
26         .type = IORESOURCE_IO,
27         .guest_base_addr = 0x3B0,
28         .machine_base_addr = 0x3B0,
29         .size = 0xC,
30         .rc = -1,
31     },
32     {
33         .type = IORESOURCE_IO,
34         .guest_base_addr = 0x3C0,
35         .machine_base_addr = 0x3C0,
36         .size = 0x20,
37         .rc = -1,
38     },
39     {
40         .type = IORESOURCE_MEM,
41         .guest_base_addr = 0xa0000 >> XC_PAGE_SHIFT,
42         .machine_base_addr = 0xa0000 >> XC_PAGE_SHIFT,
43         .size = 0x20,
44         .rc = -1,
45     },
46 };
47 
48 /*
49  * register VGA resources for the domain with assigned gfx
50  */
51 int xen_pt_register_vga_regions(XenHostPCIDevice *dev)
52 {
53     int i = 0;
54 
55     if (!is_igd_vga_passthrough(dev)) {
56         return 0;
57     }
58 
59     for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) {
60         if (vga_args[i].type == IORESOURCE_IO) {
61             vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid,
62                             vga_args[i].guest_base_addr,
63                             vga_args[i].machine_base_addr,
64                             vga_args[i].size, DPCI_ADD_MAPPING);
65         } else {
66             vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid,
67                             vga_args[i].guest_base_addr,
68                             vga_args[i].machine_base_addr,
69                             vga_args[i].size, DPCI_ADD_MAPPING);
70         }
71 
72         if (vga_args[i].rc) {
73             XEN_PT_ERR(NULL, "VGA %s mapping failed! (rc: %i)\n",
74                     vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory",
75                     vga_args[i].rc);
76             return vga_args[i].rc;
77         }
78     }
79 
80     return 0;
81 }
82 
83 /*
84  * unregister VGA resources for the domain with assigned gfx
85  */
86 int xen_pt_unregister_vga_regions(XenHostPCIDevice *dev)
87 {
88     int i = 0;
89     int ret = 0;
90 
91     if (!is_igd_vga_passthrough(dev)) {
92         return 0;
93     }
94 
95     for (i = 0 ; i < ARRAY_SIZE(vga_args); i++) {
96         if (vga_args[i].type == IORESOURCE_IO) {
97             vga_args[i].rc = xc_domain_ioport_mapping(xen_xc, xen_domid,
98                             vga_args[i].guest_base_addr,
99                             vga_args[i].machine_base_addr,
100                             vga_args[i].size, DPCI_REMOVE_MAPPING);
101         } else {
102             vga_args[i].rc = xc_domain_memory_mapping(xen_xc, xen_domid,
103                             vga_args[i].guest_base_addr,
104                             vga_args[i].machine_base_addr,
105                             vga_args[i].size, DPCI_REMOVE_MAPPING);
106         }
107 
108         if (vga_args[i].rc) {
109             XEN_PT_ERR(NULL, "VGA %s unmapping failed! (rc: %i)\n",
110                     vga_args[i].type == IORESOURCE_IO ? "ioport" : "memory",
111                     vga_args[i].rc);
112             return vga_args[i].rc;
113         }
114     }
115 
116     if (igd_guest_opregion) {
117         ret = xc_domain_memory_mapping(xen_xc, xen_domid,
118                 (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT),
119                 (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
120                 3,
121                 DPCI_REMOVE_MAPPING);
122         if (ret) {
123             return ret;
124         }
125     }
126 
127     return 0;
128 }
129 
130 static void *get_vgabios(XenPCIPassthroughState *s, int *size,
131                        XenHostPCIDevice *dev)
132 {
133     return pci_assign_dev_load_option_rom(&s->dev, OBJECT(&s->dev), size,
134                                           dev->domain, dev->bus,
135                                           dev->dev, dev->func);
136 }
137 
138 /* Refer to Seabios. */
139 struct rom_header {
140     uint16_t signature;
141     uint8_t size;
142     uint8_t initVector[4];
143     uint8_t reserved[17];
144     uint16_t pcioffset;
145     uint16_t pnpoffset;
146 } __attribute__((packed));
147 
148 struct pci_data {
149     uint32_t signature;
150     uint16_t vendor;
151     uint16_t device;
152     uint16_t vitaldata;
153     uint16_t dlen;
154     uint8_t drevision;
155     uint8_t class_lo;
156     uint16_t class_hi;
157     uint16_t ilen;
158     uint16_t irevision;
159     uint8_t type;
160     uint8_t indicator;
161     uint16_t reserved;
162 } __attribute__((packed));
163 
164 void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev,
165                      Error **errp)
166 {
167     unsigned char *bios = NULL;
168     struct rom_header *rom;
169     int bios_size;
170     char *c = NULL;
171     char checksum = 0;
172     uint32_t len = 0;
173     struct pci_data *pd = NULL;
174 
175     if (!is_igd_vga_passthrough(dev)) {
176         error_setg(errp, "Need to enable igd-passthrough");
177         return;
178     }
179 
180     bios = get_vgabios(s, &bios_size, dev);
181     if (!bios) {
182         error_setg(errp, "VGA: Can't get VBIOS");
183         return;
184     }
185 
186     /* Currently we fixed this address as a primary. */
187     rom = (struct rom_header *)bios;
188     pd = (void *)(bios + (unsigned char)rom->pcioffset);
189 
190     /* We may need to fixup Device Identification. */
191     if (pd->device != s->real_device.device_id) {
192         pd->device = s->real_device.device_id;
193 
194         len = rom->size * 512;
195         /* Then adjust the bios checksum */
196         for (c = (char *)bios; c < ((char *)bios + len); c++) {
197             checksum += *c;
198         }
199         if (checksum) {
200             bios[len - 1] -= checksum;
201             XEN_PT_LOG(&s->dev, "vga bios checksum is adjusted %x!\n",
202                        checksum);
203         }
204     }
205 
206     /* Currently we fixed this address as a primary for legacy BIOS. */
207     cpu_physical_memory_rw(0xc0000, bios, bios_size, 1);
208 }
209 
210 uint32_t igd_read_opregion(XenPCIPassthroughState *s)
211 {
212     uint32_t val = 0;
213 
214     if (!igd_guest_opregion) {
215         return val;
216     }
217 
218     val = igd_guest_opregion;
219 
220     XEN_PT_LOG(&s->dev, "Read opregion val=%x\n", val);
221     return val;
222 }
223 
224 #define XEN_PCI_INTEL_OPREGION_PAGES 0x3
225 #define XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED 0x1
226 void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val)
227 {
228     int ret;
229 
230     if (igd_guest_opregion) {
231         XEN_PT_LOG(&s->dev, "opregion register already been set, ignoring %x\n",
232                    val);
233         return;
234     }
235 
236     /* We just work with LE. */
237     xen_host_pci_get_block(&s->real_device, XEN_PCI_INTEL_OPREGION,
238             (uint8_t *)&igd_host_opregion, 4);
239     igd_guest_opregion = (unsigned long)(val & ~XEN_PCI_INTEL_OPREGION_MASK)
240                             | (igd_host_opregion & XEN_PCI_INTEL_OPREGION_MASK);
241 
242     ret = xc_domain_iomem_permission(xen_xc, xen_domid,
243             (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
244             XEN_PCI_INTEL_OPREGION_PAGES,
245             XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED);
246 
247     if (ret) {
248         XEN_PT_ERR(&s->dev, "[%d]:Can't enable to access IGD host opregion:"
249                     " 0x%lx.\n", ret,
250                     (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT)),
251         igd_guest_opregion = 0;
252         return;
253     }
254 
255     ret = xc_domain_memory_mapping(xen_xc, xen_domid,
256             (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT),
257             (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
258             XEN_PCI_INTEL_OPREGION_PAGES,
259             DPCI_ADD_MAPPING);
260 
261     if (ret) {
262         XEN_PT_ERR(&s->dev, "[%d]:Can't map IGD host opregion:0x%lx to"
263                     " guest opregion:0x%lx.\n", ret,
264                     (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
265                     (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT));
266         igd_guest_opregion = 0;
267         return;
268     }
269 
270     XEN_PT_LOG(&s->dev, "Map OpRegion: 0x%lx -> 0x%lx\n",
271                     (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
272                     (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT));
273 }
274