xref: /openbmc/qemu/hw/xen/xen_pt_graphics.c (revision c11b0583)
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 int xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev)
165 {
166     unsigned char *bios = NULL;
167     struct rom_header *rom;
168     int bios_size;
169     char *c = NULL;
170     char checksum = 0;
171     uint32_t len = 0;
172     struct pci_data *pd = NULL;
173 
174     if (!is_igd_vga_passthrough(dev)) {
175         return -1;
176     }
177 
178     bios = get_vgabios(s, &bios_size, dev);
179     if (!bios) {
180         XEN_PT_ERR(&s->dev, "VGA: Can't getting VBIOS!\n");
181         return -1;
182     }
183 
184     /* Currently we fixed this address as a primary. */
185     rom = (struct rom_header *)bios;
186     pd = (void *)(bios + (unsigned char)rom->pcioffset);
187 
188     /* We may need to fixup Device Identification. */
189     if (pd->device != s->real_device.device_id) {
190         pd->device = s->real_device.device_id;
191 
192         len = rom->size * 512;
193         /* Then adjust the bios checksum */
194         for (c = (char *)bios; c < ((char *)bios + len); c++) {
195             checksum += *c;
196         }
197         if (checksum) {
198             bios[len - 1] -= checksum;
199             XEN_PT_LOG(&s->dev, "vga bios checksum is adjusted %x!\n",
200                        checksum);
201         }
202     }
203 
204     /* Currently we fixed this address as a primary for legacy BIOS. */
205     cpu_physical_memory_rw(0xc0000, bios, bios_size, 1);
206     return 0;
207 }
208 
209 uint32_t igd_read_opregion(XenPCIPassthroughState *s)
210 {
211     uint32_t val = 0;
212 
213     if (!igd_guest_opregion) {
214         return val;
215     }
216 
217     val = igd_guest_opregion;
218 
219     XEN_PT_LOG(&s->dev, "Read opregion val=%x\n", val);
220     return val;
221 }
222 
223 #define XEN_PCI_INTEL_OPREGION_PAGES 0x3
224 #define XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED 0x1
225 void igd_write_opregion(XenPCIPassthroughState *s, uint32_t val)
226 {
227     int ret;
228 
229     if (igd_guest_opregion) {
230         XEN_PT_LOG(&s->dev, "opregion register already been set, ignoring %x\n",
231                    val);
232         return;
233     }
234 
235     /* We just work with LE. */
236     xen_host_pci_get_block(&s->real_device, XEN_PCI_INTEL_OPREGION,
237             (uint8_t *)&igd_host_opregion, 4);
238     igd_guest_opregion = (unsigned long)(val & ~XEN_PCI_INTEL_OPREGION_MASK)
239                             | (igd_host_opregion & XEN_PCI_INTEL_OPREGION_MASK);
240 
241     ret = xc_domain_iomem_permission(xen_xc, xen_domid,
242             (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
243             XEN_PCI_INTEL_OPREGION_PAGES,
244             XEN_PCI_INTEL_OPREGION_ENABLE_ACCESSED);
245 
246     if (ret) {
247         XEN_PT_ERR(&s->dev, "[%d]:Can't enable to access IGD host opregion:"
248                     " 0x%lx.\n", ret,
249                     (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT)),
250         igd_guest_opregion = 0;
251         return;
252     }
253 
254     ret = xc_domain_memory_mapping(xen_xc, xen_domid,
255             (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT),
256             (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
257             XEN_PCI_INTEL_OPREGION_PAGES,
258             DPCI_ADD_MAPPING);
259 
260     if (ret) {
261         XEN_PT_ERR(&s->dev, "[%d]:Can't map IGD host opregion:0x%lx to"
262                     " guest opregion:0x%lx.\n", ret,
263                     (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
264                     (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT));
265         igd_guest_opregion = 0;
266         return;
267     }
268 
269     XEN_PT_LOG(&s->dev, "Map OpRegion: 0x%lx -> 0x%lx\n",
270                     (unsigned long)(igd_host_opregion >> XC_PAGE_SHIFT),
271                     (unsigned long)(igd_guest_opregion >> XC_PAGE_SHIFT));
272 }
273