]>
Commit | Line | Data |
---|---|---|
79814179 TC |
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 | ||
5cec8aa3 TC |
8 | static unsigned long igd_guest_opregion; |
9 | static unsigned long igd_host_opregion; | |
10 | ||
11 | #define XEN_PCI_INTEL_OPREGION_MASK 0xfff | |
12 | ||
79814179 TC |
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; | |
5cec8aa3 | 89 | int ret = 0; |
79814179 TC |
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 | ||
5cec8aa3 TC |
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 | ||
79814179 TC |
127 | return 0; |
128 | } | |
881213f1 TC |
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 | ||
5226bb59 C |
164 | void xen_pt_setup_vga(XenPCIPassthroughState *s, XenHostPCIDevice *dev, |
165 | Error **errp) | |
881213f1 TC |
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)) { | |
5226bb59 C |
176 | error_setg(errp, "Need to enable igd-passthrough"); |
177 | return; | |
881213f1 TC |
178 | } |
179 | ||
180 | bios = get_vgabios(s, &bios_size, dev); | |
181 | if (!bios) { | |
5226bb59 C |
182 | error_setg(errp, "VGA: Can't get VBIOS"); |
183 | return; | |
881213f1 TC |
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); | |
881213f1 | 208 | } |
5cec8aa3 TC |
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 | } |