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