root / hw / xen_pt.c @ 9bfa659e
History | View | Annotate | Download (26.1 kB)
1 | eaab4d60 | Allen Kay | /*
|
---|---|---|---|
2 | eaab4d60 | Allen Kay | * Copyright (c) 2007, Neocleus Corporation.
|
3 | eaab4d60 | Allen Kay | * Copyright (c) 2007, Intel Corporation.
|
4 | eaab4d60 | Allen Kay | *
|
5 | eaab4d60 | Allen Kay | * This work is licensed under the terms of the GNU GPL, version 2. See
|
6 | eaab4d60 | Allen Kay | * the COPYING file in the top-level directory.
|
7 | eaab4d60 | Allen Kay | *
|
8 | eaab4d60 | Allen Kay | * Alex Novik <alex@neocleus.com>
|
9 | eaab4d60 | Allen Kay | * Allen Kay <allen.m.kay@intel.com>
|
10 | eaab4d60 | Allen Kay | * Guy Zana <guy@neocleus.com>
|
11 | eaab4d60 | Allen Kay | *
|
12 | eaab4d60 | Allen Kay | * This file implements direct PCI assignment to a HVM guest
|
13 | eaab4d60 | Allen Kay | */
|
14 | eaab4d60 | Allen Kay | |
15 | eaab4d60 | Allen Kay | /*
|
16 | eaab4d60 | Allen Kay | * Interrupt Disable policy:
|
17 | eaab4d60 | Allen Kay | *
|
18 | eaab4d60 | Allen Kay | * INTx interrupt:
|
19 | eaab4d60 | Allen Kay | * Initialize(register_real_device)
|
20 | eaab4d60 | Allen Kay | * Map INTx(xc_physdev_map_pirq):
|
21 | eaab4d60 | Allen Kay | * <fail>
|
22 | eaab4d60 | Allen Kay | * - Set real Interrupt Disable bit to '1'.
|
23 | eaab4d60 | Allen Kay | * - Set machine_irq and assigned_device->machine_irq to '0'.
|
24 | eaab4d60 | Allen Kay | * * Don't bind INTx.
|
25 | eaab4d60 | Allen Kay | *
|
26 | eaab4d60 | Allen Kay | * Bind INTx(xc_domain_bind_pt_pci_irq):
|
27 | eaab4d60 | Allen Kay | * <fail>
|
28 | eaab4d60 | Allen Kay | * - Set real Interrupt Disable bit to '1'.
|
29 | eaab4d60 | Allen Kay | * - Unmap INTx.
|
30 | eaab4d60 | Allen Kay | * - Decrement xen_pt_mapped_machine_irq[machine_irq]
|
31 | eaab4d60 | Allen Kay | * - Set assigned_device->machine_irq to '0'.
|
32 | eaab4d60 | Allen Kay | *
|
33 | eaab4d60 | Allen Kay | * Write to Interrupt Disable bit by guest software(xen_pt_cmd_reg_write)
|
34 | eaab4d60 | Allen Kay | * Write '0'
|
35 | eaab4d60 | Allen Kay | * - Set real bit to '0' if assigned_device->machine_irq isn't '0'.
|
36 | eaab4d60 | Allen Kay | *
|
37 | eaab4d60 | Allen Kay | * Write '1'
|
38 | eaab4d60 | Allen Kay | * - Set real bit to '1'.
|
39 | 3854ca57 | Jiang Yunhong | *
|
40 | 3854ca57 | Jiang Yunhong | * MSI interrupt:
|
41 | 3854ca57 | Jiang Yunhong | * Initialize MSI register(xen_pt_msi_setup, xen_pt_msi_update)
|
42 | 3854ca57 | Jiang Yunhong | * Bind MSI(xc_domain_update_msi_irq)
|
43 | 3854ca57 | Jiang Yunhong | * <fail>
|
44 | 3854ca57 | Jiang Yunhong | * - Unmap MSI.
|
45 | 3854ca57 | Jiang Yunhong | * - Set dev->msi->pirq to '-1'.
|
46 | 3854ca57 | Jiang Yunhong | *
|
47 | 3854ca57 | Jiang Yunhong | * MSI-X interrupt:
|
48 | 3854ca57 | Jiang Yunhong | * Initialize MSI-X register(xen_pt_msix_update_one)
|
49 | 3854ca57 | Jiang Yunhong | * Bind MSI-X(xc_domain_update_msi_irq)
|
50 | 3854ca57 | Jiang Yunhong | * <fail>
|
51 | 3854ca57 | Jiang Yunhong | * - Unmap MSI-X.
|
52 | 3854ca57 | Jiang Yunhong | * - Set entry->pirq to '-1'.
|
53 | eaab4d60 | Allen Kay | */
|
54 | eaab4d60 | Allen Kay | |
55 | eaab4d60 | Allen Kay | #include <sys/ioctl.h> |
56 | eaab4d60 | Allen Kay | |
57 | eaab4d60 | Allen Kay | #include "pci.h" |
58 | eaab4d60 | Allen Kay | #include "xen.h" |
59 | eaab4d60 | Allen Kay | #include "xen_backend.h" |
60 | eaab4d60 | Allen Kay | #include "xen_pt.h" |
61 | eaab4d60 | Allen Kay | #include "range.h" |
62 | eaab4d60 | Allen Kay | |
63 | eaab4d60 | Allen Kay | #define XEN_PT_NR_IRQS (256) |
64 | eaab4d60 | Allen Kay | static uint8_t xen_pt_mapped_machine_irq[XEN_PT_NR_IRQS] = {0}; |
65 | eaab4d60 | Allen Kay | |
66 | eaab4d60 | Allen Kay | void xen_pt_log(const PCIDevice *d, const char *f, ...) |
67 | eaab4d60 | Allen Kay | { |
68 | eaab4d60 | Allen Kay | va_list ap; |
69 | eaab4d60 | Allen Kay | |
70 | eaab4d60 | Allen Kay | va_start(ap, f); |
71 | eaab4d60 | Allen Kay | if (d) {
|
72 | eaab4d60 | Allen Kay | fprintf(stderr, "[%02x:%02x.%d] ", pci_bus_num(d->bus),
|
73 | eaab4d60 | Allen Kay | PCI_SLOT(d->devfn), PCI_FUNC(d->devfn)); |
74 | eaab4d60 | Allen Kay | } |
75 | eaab4d60 | Allen Kay | vfprintf(stderr, f, ap); |
76 | eaab4d60 | Allen Kay | va_end(ap); |
77 | eaab4d60 | Allen Kay | } |
78 | eaab4d60 | Allen Kay | |
79 | eaab4d60 | Allen Kay | /* Config Space */
|
80 | eaab4d60 | Allen Kay | |
81 | eaab4d60 | Allen Kay | static int xen_pt_pci_config_access_check(PCIDevice *d, uint32_t addr, int len) |
82 | eaab4d60 | Allen Kay | { |
83 | eaab4d60 | Allen Kay | /* check offset range */
|
84 | eaab4d60 | Allen Kay | if (addr >= 0xFF) { |
85 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "Failed to access register with offset exceeding 0xFF. "
|
86 | eaab4d60 | Allen Kay | "(addr: 0x%02x, len: %d)\n", addr, len);
|
87 | eaab4d60 | Allen Kay | return -1; |
88 | eaab4d60 | Allen Kay | } |
89 | eaab4d60 | Allen Kay | |
90 | eaab4d60 | Allen Kay | /* check read size */
|
91 | eaab4d60 | Allen Kay | if ((len != 1) && (len != 2) && (len != 4)) { |
92 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "Failed to access register with invalid access length. "
|
93 | eaab4d60 | Allen Kay | "(addr: 0x%02x, len: %d)\n", addr, len);
|
94 | eaab4d60 | Allen Kay | return -1; |
95 | eaab4d60 | Allen Kay | } |
96 | eaab4d60 | Allen Kay | |
97 | eaab4d60 | Allen Kay | /* check offset alignment */
|
98 | eaab4d60 | Allen Kay | if (addr & (len - 1)) { |
99 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "Failed to access register with invalid access size "
|
100 | eaab4d60 | Allen Kay | "alignment. (addr: 0x%02x, len: %d)\n", addr, len);
|
101 | eaab4d60 | Allen Kay | return -1; |
102 | eaab4d60 | Allen Kay | } |
103 | eaab4d60 | Allen Kay | |
104 | eaab4d60 | Allen Kay | return 0; |
105 | eaab4d60 | Allen Kay | } |
106 | eaab4d60 | Allen Kay | |
107 | eaab4d60 | Allen Kay | int xen_pt_bar_offset_to_index(uint32_t offset)
|
108 | eaab4d60 | Allen Kay | { |
109 | eaab4d60 | Allen Kay | int index = 0; |
110 | eaab4d60 | Allen Kay | |
111 | eaab4d60 | Allen Kay | /* check Exp ROM BAR */
|
112 | eaab4d60 | Allen Kay | if (offset == PCI_ROM_ADDRESS) {
|
113 | eaab4d60 | Allen Kay | return PCI_ROM_SLOT;
|
114 | eaab4d60 | Allen Kay | } |
115 | eaab4d60 | Allen Kay | |
116 | eaab4d60 | Allen Kay | /* calculate BAR index */
|
117 | eaab4d60 | Allen Kay | index = (offset - PCI_BASE_ADDRESS_0) >> 2;
|
118 | eaab4d60 | Allen Kay | if (index >= PCI_NUM_REGIONS) {
|
119 | eaab4d60 | Allen Kay | return -1; |
120 | eaab4d60 | Allen Kay | } |
121 | eaab4d60 | Allen Kay | |
122 | eaab4d60 | Allen Kay | return index;
|
123 | eaab4d60 | Allen Kay | } |
124 | eaab4d60 | Allen Kay | |
125 | eaab4d60 | Allen Kay | static uint32_t xen_pt_pci_read_config(PCIDevice *d, uint32_t addr, int len) |
126 | eaab4d60 | Allen Kay | { |
127 | eaab4d60 | Allen Kay | XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); |
128 | eaab4d60 | Allen Kay | uint32_t val = 0;
|
129 | eaab4d60 | Allen Kay | XenPTRegGroup *reg_grp_entry = NULL;
|
130 | eaab4d60 | Allen Kay | XenPTReg *reg_entry = NULL;
|
131 | eaab4d60 | Allen Kay | int rc = 0; |
132 | eaab4d60 | Allen Kay | int emul_len = 0; |
133 | eaab4d60 | Allen Kay | uint32_t find_addr = addr; |
134 | eaab4d60 | Allen Kay | |
135 | eaab4d60 | Allen Kay | if (xen_pt_pci_config_access_check(d, addr, len)) {
|
136 | eaab4d60 | Allen Kay | goto exit;
|
137 | eaab4d60 | Allen Kay | } |
138 | eaab4d60 | Allen Kay | |
139 | eaab4d60 | Allen Kay | /* find register group entry */
|
140 | eaab4d60 | Allen Kay | reg_grp_entry = xen_pt_find_reg_grp(s, addr); |
141 | eaab4d60 | Allen Kay | if (reg_grp_entry) {
|
142 | eaab4d60 | Allen Kay | /* check 0-Hardwired register group */
|
143 | eaab4d60 | Allen Kay | if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) {
|
144 | eaab4d60 | Allen Kay | /* no need to emulate, just return 0 */
|
145 | eaab4d60 | Allen Kay | val = 0;
|
146 | eaab4d60 | Allen Kay | goto exit;
|
147 | eaab4d60 | Allen Kay | } |
148 | eaab4d60 | Allen Kay | } |
149 | eaab4d60 | Allen Kay | |
150 | eaab4d60 | Allen Kay | /* read I/O device register value */
|
151 | eaab4d60 | Allen Kay | rc = xen_host_pci_get_block(&s->real_device, addr, (uint8_t *)&val, len); |
152 | eaab4d60 | Allen Kay | if (rc < 0) { |
153 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
|
154 | eaab4d60 | Allen Kay | memset(&val, 0xff, len);
|
155 | eaab4d60 | Allen Kay | } |
156 | eaab4d60 | Allen Kay | |
157 | eaab4d60 | Allen Kay | /* just return the I/O device register value for
|
158 | eaab4d60 | Allen Kay | * passthrough type register group */
|
159 | eaab4d60 | Allen Kay | if (reg_grp_entry == NULL) { |
160 | eaab4d60 | Allen Kay | goto exit;
|
161 | eaab4d60 | Allen Kay | } |
162 | eaab4d60 | Allen Kay | |
163 | eaab4d60 | Allen Kay | /* adjust the read value to appropriate CFC-CFF window */
|
164 | eaab4d60 | Allen Kay | val <<= (addr & 3) << 3; |
165 | eaab4d60 | Allen Kay | emul_len = len; |
166 | eaab4d60 | Allen Kay | |
167 | eaab4d60 | Allen Kay | /* loop around the guest requested size */
|
168 | eaab4d60 | Allen Kay | while (emul_len > 0) { |
169 | eaab4d60 | Allen Kay | /* find register entry to be emulated */
|
170 | eaab4d60 | Allen Kay | reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr); |
171 | eaab4d60 | Allen Kay | if (reg_entry) {
|
172 | eaab4d60 | Allen Kay | XenPTRegInfo *reg = reg_entry->reg; |
173 | eaab4d60 | Allen Kay | uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; |
174 | eaab4d60 | Allen Kay | uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); |
175 | eaab4d60 | Allen Kay | uint8_t *ptr_val = NULL;
|
176 | eaab4d60 | Allen Kay | |
177 | eaab4d60 | Allen Kay | valid_mask <<= (find_addr - real_offset) << 3;
|
178 | eaab4d60 | Allen Kay | ptr_val = (uint8_t *)&val + (real_offset & 3);
|
179 | eaab4d60 | Allen Kay | |
180 | eaab4d60 | Allen Kay | /* do emulation based on register size */
|
181 | eaab4d60 | Allen Kay | switch (reg->size) {
|
182 | eaab4d60 | Allen Kay | case 1: |
183 | eaab4d60 | Allen Kay | if (reg->u.b.read) {
|
184 | eaab4d60 | Allen Kay | rc = reg->u.b.read(s, reg_entry, ptr_val, valid_mask); |
185 | eaab4d60 | Allen Kay | } |
186 | eaab4d60 | Allen Kay | break;
|
187 | eaab4d60 | Allen Kay | case 2: |
188 | eaab4d60 | Allen Kay | if (reg->u.w.read) {
|
189 | eaab4d60 | Allen Kay | rc = reg->u.w.read(s, reg_entry, |
190 | eaab4d60 | Allen Kay | (uint16_t *)ptr_val, valid_mask); |
191 | eaab4d60 | Allen Kay | } |
192 | eaab4d60 | Allen Kay | break;
|
193 | eaab4d60 | Allen Kay | case 4: |
194 | eaab4d60 | Allen Kay | if (reg->u.dw.read) {
|
195 | eaab4d60 | Allen Kay | rc = reg->u.dw.read(s, reg_entry, |
196 | eaab4d60 | Allen Kay | (uint32_t *)ptr_val, valid_mask); |
197 | eaab4d60 | Allen Kay | } |
198 | eaab4d60 | Allen Kay | break;
|
199 | eaab4d60 | Allen Kay | } |
200 | eaab4d60 | Allen Kay | |
201 | eaab4d60 | Allen Kay | if (rc < 0) { |
202 | eaab4d60 | Allen Kay | xen_shutdown_fatal_error("Internal error: Invalid read "
|
203 | eaab4d60 | Allen Kay | "emulation. (%s, rc: %d)\n",
|
204 | eaab4d60 | Allen Kay | __func__, rc); |
205 | eaab4d60 | Allen Kay | return 0; |
206 | eaab4d60 | Allen Kay | } |
207 | eaab4d60 | Allen Kay | |
208 | eaab4d60 | Allen Kay | /* calculate next address to find */
|
209 | eaab4d60 | Allen Kay | emul_len -= reg->size; |
210 | eaab4d60 | Allen Kay | if (emul_len > 0) { |
211 | eaab4d60 | Allen Kay | find_addr = real_offset + reg->size; |
212 | eaab4d60 | Allen Kay | } |
213 | eaab4d60 | Allen Kay | } else {
|
214 | eaab4d60 | Allen Kay | /* nothing to do with passthrough type register,
|
215 | eaab4d60 | Allen Kay | * continue to find next byte */
|
216 | eaab4d60 | Allen Kay | emul_len--; |
217 | eaab4d60 | Allen Kay | find_addr++; |
218 | eaab4d60 | Allen Kay | } |
219 | eaab4d60 | Allen Kay | } |
220 | eaab4d60 | Allen Kay | |
221 | eaab4d60 | Allen Kay | /* need to shift back before returning them to pci bus emulator */
|
222 | eaab4d60 | Allen Kay | val >>= ((addr & 3) << 3); |
223 | eaab4d60 | Allen Kay | |
224 | eaab4d60 | Allen Kay | exit:
|
225 | eaab4d60 | Allen Kay | XEN_PT_LOG_CONFIG(d, addr, val, len); |
226 | eaab4d60 | Allen Kay | return val;
|
227 | eaab4d60 | Allen Kay | } |
228 | eaab4d60 | Allen Kay | |
229 | eaab4d60 | Allen Kay | static void xen_pt_pci_write_config(PCIDevice *d, uint32_t addr, |
230 | eaab4d60 | Allen Kay | uint32_t val, int len)
|
231 | eaab4d60 | Allen Kay | { |
232 | eaab4d60 | Allen Kay | XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); |
233 | eaab4d60 | Allen Kay | int index = 0; |
234 | eaab4d60 | Allen Kay | XenPTRegGroup *reg_grp_entry = NULL;
|
235 | eaab4d60 | Allen Kay | int rc = 0; |
236 | eaab4d60 | Allen Kay | uint32_t read_val = 0;
|
237 | eaab4d60 | Allen Kay | int emul_len = 0; |
238 | eaab4d60 | Allen Kay | XenPTReg *reg_entry = NULL;
|
239 | eaab4d60 | Allen Kay | uint32_t find_addr = addr; |
240 | eaab4d60 | Allen Kay | XenPTRegInfo *reg = NULL;
|
241 | eaab4d60 | Allen Kay | |
242 | eaab4d60 | Allen Kay | if (xen_pt_pci_config_access_check(d, addr, len)) {
|
243 | eaab4d60 | Allen Kay | return;
|
244 | eaab4d60 | Allen Kay | } |
245 | eaab4d60 | Allen Kay | |
246 | eaab4d60 | Allen Kay | XEN_PT_LOG_CONFIG(d, addr, val, len); |
247 | eaab4d60 | Allen Kay | |
248 | eaab4d60 | Allen Kay | /* check unused BAR register */
|
249 | eaab4d60 | Allen Kay | index = xen_pt_bar_offset_to_index(addr); |
250 | eaab4d60 | Allen Kay | if ((index >= 0) && (val > 0 && val < XEN_PT_BAR_ALLF) && |
251 | eaab4d60 | Allen Kay | (s->bases[index].bar_flag == XEN_PT_BAR_FLAG_UNUSED)) { |
252 | eaab4d60 | Allen Kay | XEN_PT_WARN(d, "Guest attempt to set address to unused Base Address "
|
253 | eaab4d60 | Allen Kay | "Register. (addr: 0x%02x, len: %d)\n", addr, len);
|
254 | eaab4d60 | Allen Kay | } |
255 | eaab4d60 | Allen Kay | |
256 | eaab4d60 | Allen Kay | /* find register group entry */
|
257 | eaab4d60 | Allen Kay | reg_grp_entry = xen_pt_find_reg_grp(s, addr); |
258 | eaab4d60 | Allen Kay | if (reg_grp_entry) {
|
259 | eaab4d60 | Allen Kay | /* check 0-Hardwired register group */
|
260 | eaab4d60 | Allen Kay | if (reg_grp_entry->reg_grp->grp_type == XEN_PT_GRP_TYPE_HARDWIRED) {
|
261 | eaab4d60 | Allen Kay | /* ignore silently */
|
262 | eaab4d60 | Allen Kay | XEN_PT_WARN(d, "Access to 0-Hardwired register. "
|
263 | eaab4d60 | Allen Kay | "(addr: 0x%02x, len: %d)\n", addr, len);
|
264 | eaab4d60 | Allen Kay | return;
|
265 | eaab4d60 | Allen Kay | } |
266 | eaab4d60 | Allen Kay | } |
267 | eaab4d60 | Allen Kay | |
268 | eaab4d60 | Allen Kay | rc = xen_host_pci_get_block(&s->real_device, addr, |
269 | eaab4d60 | Allen Kay | (uint8_t *)&read_val, len); |
270 | eaab4d60 | Allen Kay | if (rc < 0) { |
271 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "pci_read_block failed. return value: %d.\n", rc);
|
272 | eaab4d60 | Allen Kay | memset(&read_val, 0xff, len);
|
273 | eaab4d60 | Allen Kay | } |
274 | eaab4d60 | Allen Kay | |
275 | eaab4d60 | Allen Kay | /* pass directly to the real device for passthrough type register group */
|
276 | eaab4d60 | Allen Kay | if (reg_grp_entry == NULL) { |
277 | eaab4d60 | Allen Kay | goto out;
|
278 | eaab4d60 | Allen Kay | } |
279 | eaab4d60 | Allen Kay | |
280 | eaab4d60 | Allen Kay | memory_region_transaction_begin(); |
281 | eaab4d60 | Allen Kay | pci_default_write_config(d, addr, val, len); |
282 | eaab4d60 | Allen Kay | |
283 | eaab4d60 | Allen Kay | /* adjust the read and write value to appropriate CFC-CFF window */
|
284 | eaab4d60 | Allen Kay | read_val <<= (addr & 3) << 3; |
285 | eaab4d60 | Allen Kay | val <<= (addr & 3) << 3; |
286 | eaab4d60 | Allen Kay | emul_len = len; |
287 | eaab4d60 | Allen Kay | |
288 | eaab4d60 | Allen Kay | /* loop around the guest requested size */
|
289 | eaab4d60 | Allen Kay | while (emul_len > 0) { |
290 | eaab4d60 | Allen Kay | /* find register entry to be emulated */
|
291 | eaab4d60 | Allen Kay | reg_entry = xen_pt_find_reg(reg_grp_entry, find_addr); |
292 | eaab4d60 | Allen Kay | if (reg_entry) {
|
293 | eaab4d60 | Allen Kay | reg = reg_entry->reg; |
294 | eaab4d60 | Allen Kay | uint32_t real_offset = reg_grp_entry->base_offset + reg->offset; |
295 | eaab4d60 | Allen Kay | uint32_t valid_mask = 0xFFFFFFFF >> ((4 - emul_len) << 3); |
296 | eaab4d60 | Allen Kay | uint8_t *ptr_val = NULL;
|
297 | eaab4d60 | Allen Kay | |
298 | eaab4d60 | Allen Kay | valid_mask <<= (find_addr - real_offset) << 3;
|
299 | eaab4d60 | Allen Kay | ptr_val = (uint8_t *)&val + (real_offset & 3);
|
300 | eaab4d60 | Allen Kay | |
301 | eaab4d60 | Allen Kay | /* do emulation based on register size */
|
302 | eaab4d60 | Allen Kay | switch (reg->size) {
|
303 | eaab4d60 | Allen Kay | case 1: |
304 | eaab4d60 | Allen Kay | if (reg->u.b.write) {
|
305 | eaab4d60 | Allen Kay | rc = reg->u.b.write(s, reg_entry, ptr_val, |
306 | eaab4d60 | Allen Kay | read_val >> ((real_offset & 3) << 3), |
307 | eaab4d60 | Allen Kay | valid_mask); |
308 | eaab4d60 | Allen Kay | } |
309 | eaab4d60 | Allen Kay | break;
|
310 | eaab4d60 | Allen Kay | case 2: |
311 | eaab4d60 | Allen Kay | if (reg->u.w.write) {
|
312 | eaab4d60 | Allen Kay | rc = reg->u.w.write(s, reg_entry, (uint16_t *)ptr_val, |
313 | eaab4d60 | Allen Kay | (read_val >> ((real_offset & 3) << 3)), |
314 | eaab4d60 | Allen Kay | valid_mask); |
315 | eaab4d60 | Allen Kay | } |
316 | eaab4d60 | Allen Kay | break;
|
317 | eaab4d60 | Allen Kay | case 4: |
318 | eaab4d60 | Allen Kay | if (reg->u.dw.write) {
|
319 | eaab4d60 | Allen Kay | rc = reg->u.dw.write(s, reg_entry, (uint32_t *)ptr_val, |
320 | eaab4d60 | Allen Kay | (read_val >> ((real_offset & 3) << 3)), |
321 | eaab4d60 | Allen Kay | valid_mask); |
322 | eaab4d60 | Allen Kay | } |
323 | eaab4d60 | Allen Kay | break;
|
324 | eaab4d60 | Allen Kay | } |
325 | eaab4d60 | Allen Kay | |
326 | eaab4d60 | Allen Kay | if (rc < 0) { |
327 | eaab4d60 | Allen Kay | xen_shutdown_fatal_error("Internal error: Invalid write"
|
328 | eaab4d60 | Allen Kay | " emulation. (%s, rc: %d)\n",
|
329 | eaab4d60 | Allen Kay | __func__, rc); |
330 | eaab4d60 | Allen Kay | return;
|
331 | eaab4d60 | Allen Kay | } |
332 | eaab4d60 | Allen Kay | |
333 | eaab4d60 | Allen Kay | /* calculate next address to find */
|
334 | eaab4d60 | Allen Kay | emul_len -= reg->size; |
335 | eaab4d60 | Allen Kay | if (emul_len > 0) { |
336 | eaab4d60 | Allen Kay | find_addr = real_offset + reg->size; |
337 | eaab4d60 | Allen Kay | } |
338 | eaab4d60 | Allen Kay | } else {
|
339 | eaab4d60 | Allen Kay | /* nothing to do with passthrough type register,
|
340 | eaab4d60 | Allen Kay | * continue to find next byte */
|
341 | eaab4d60 | Allen Kay | emul_len--; |
342 | eaab4d60 | Allen Kay | find_addr++; |
343 | eaab4d60 | Allen Kay | } |
344 | eaab4d60 | Allen Kay | } |
345 | eaab4d60 | Allen Kay | |
346 | eaab4d60 | Allen Kay | /* need to shift back before passing them to xen_host_pci_device */
|
347 | eaab4d60 | Allen Kay | val >>= (addr & 3) << 3; |
348 | eaab4d60 | Allen Kay | |
349 | eaab4d60 | Allen Kay | memory_region_transaction_commit(); |
350 | eaab4d60 | Allen Kay | |
351 | eaab4d60 | Allen Kay | out:
|
352 | eaab4d60 | Allen Kay | if (!(reg && reg->no_wb)) {
|
353 | eaab4d60 | Allen Kay | /* unknown regs are passed through */
|
354 | eaab4d60 | Allen Kay | rc = xen_host_pci_set_block(&s->real_device, addr, |
355 | eaab4d60 | Allen Kay | (uint8_t *)&val, len); |
356 | eaab4d60 | Allen Kay | |
357 | eaab4d60 | Allen Kay | if (rc < 0) { |
358 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "pci_write_block failed. return value: %d.\n", rc);
|
359 | eaab4d60 | Allen Kay | } |
360 | eaab4d60 | Allen Kay | } |
361 | eaab4d60 | Allen Kay | } |
362 | eaab4d60 | Allen Kay | |
363 | eaab4d60 | Allen Kay | /* register regions */
|
364 | eaab4d60 | Allen Kay | |
365 | eaab4d60 | Allen Kay | static uint64_t xen_pt_bar_read(void *o, target_phys_addr_t addr, |
366 | eaab4d60 | Allen Kay | unsigned size)
|
367 | eaab4d60 | Allen Kay | { |
368 | eaab4d60 | Allen Kay | PCIDevice *d = o; |
369 | eaab4d60 | Allen Kay | /* if this function is called, that probably means that there is a
|
370 | eaab4d60 | Allen Kay | * misconfiguration of the IOMMU. */
|
371 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "Should not read BAR through QEMU. @0x"TARGET_FMT_plx"\n", |
372 | eaab4d60 | Allen Kay | addr); |
373 | eaab4d60 | Allen Kay | return 0; |
374 | eaab4d60 | Allen Kay | } |
375 | eaab4d60 | Allen Kay | static void xen_pt_bar_write(void *o, target_phys_addr_t addr, uint64_t val, |
376 | eaab4d60 | Allen Kay | unsigned size)
|
377 | eaab4d60 | Allen Kay | { |
378 | eaab4d60 | Allen Kay | PCIDevice *d = o; |
379 | eaab4d60 | Allen Kay | /* Same comment as xen_pt_bar_read function */
|
380 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "Should not write BAR through QEMU. @0x"TARGET_FMT_plx"\n", |
381 | eaab4d60 | Allen Kay | addr); |
382 | eaab4d60 | Allen Kay | } |
383 | eaab4d60 | Allen Kay | |
384 | eaab4d60 | Allen Kay | static const MemoryRegionOps ops = { |
385 | eaab4d60 | Allen Kay | .endianness = DEVICE_NATIVE_ENDIAN, |
386 | eaab4d60 | Allen Kay | .read = xen_pt_bar_read, |
387 | eaab4d60 | Allen Kay | .write = xen_pt_bar_write, |
388 | eaab4d60 | Allen Kay | }; |
389 | eaab4d60 | Allen Kay | |
390 | eaab4d60 | Allen Kay | static int xen_pt_register_regions(XenPCIPassthroughState *s) |
391 | eaab4d60 | Allen Kay | { |
392 | eaab4d60 | Allen Kay | int i = 0; |
393 | eaab4d60 | Allen Kay | XenHostPCIDevice *d = &s->real_device; |
394 | eaab4d60 | Allen Kay | |
395 | eaab4d60 | Allen Kay | /* Register PIO/MMIO BARs */
|
396 | eaab4d60 | Allen Kay | for (i = 0; i < PCI_ROM_SLOT; i++) { |
397 | eaab4d60 | Allen Kay | XenHostPCIIORegion *r = &d->io_regions[i]; |
398 | eaab4d60 | Allen Kay | uint8_t type; |
399 | eaab4d60 | Allen Kay | |
400 | eaab4d60 | Allen Kay | if (r->base_addr == 0 || r->size == 0) { |
401 | eaab4d60 | Allen Kay | continue;
|
402 | eaab4d60 | Allen Kay | } |
403 | eaab4d60 | Allen Kay | |
404 | eaab4d60 | Allen Kay | s->bases[i].access.u = r->base_addr; |
405 | eaab4d60 | Allen Kay | |
406 | eaab4d60 | Allen Kay | if (r->type & XEN_HOST_PCI_REGION_TYPE_IO) {
|
407 | eaab4d60 | Allen Kay | type = PCI_BASE_ADDRESS_SPACE_IO; |
408 | eaab4d60 | Allen Kay | } else {
|
409 | eaab4d60 | Allen Kay | type = PCI_BASE_ADDRESS_SPACE_MEMORY; |
410 | eaab4d60 | Allen Kay | if (r->type & XEN_HOST_PCI_REGION_TYPE_PREFETCH) {
|
411 | eaab4d60 | Allen Kay | type |= PCI_BASE_ADDRESS_MEM_PREFETCH; |
412 | eaab4d60 | Allen Kay | } |
413 | eaab4d60 | Allen Kay | } |
414 | eaab4d60 | Allen Kay | |
415 | eaab4d60 | Allen Kay | memory_region_init_io(&s->bar[i], &ops, &s->dev, |
416 | eaab4d60 | Allen Kay | "xen-pci-pt-bar", r->size);
|
417 | eaab4d60 | Allen Kay | pci_register_bar(&s->dev, i, type, &s->bar[i]); |
418 | eaab4d60 | Allen Kay | |
419 | eaab4d60 | Allen Kay | XEN_PT_LOG(&s->dev, "IO region %i registered (size=0x%08"PRIx64
|
420 | eaab4d60 | Allen Kay | " base_addr=0x%08"PRIx64" type: %#x)\n", |
421 | eaab4d60 | Allen Kay | i, r->size, r->base_addr, type); |
422 | eaab4d60 | Allen Kay | } |
423 | eaab4d60 | Allen Kay | |
424 | eaab4d60 | Allen Kay | /* Register expansion ROM address */
|
425 | eaab4d60 | Allen Kay | if (d->rom.base_addr && d->rom.size) {
|
426 | eaab4d60 | Allen Kay | uint32_t bar_data = 0;
|
427 | eaab4d60 | Allen Kay | |
428 | eaab4d60 | Allen Kay | /* Re-set BAR reported by OS, otherwise ROM can't be read. */
|
429 | eaab4d60 | Allen Kay | if (xen_host_pci_get_long(d, PCI_ROM_ADDRESS, &bar_data)) {
|
430 | eaab4d60 | Allen Kay | return 0; |
431 | eaab4d60 | Allen Kay | } |
432 | eaab4d60 | Allen Kay | if ((bar_data & PCI_ROM_ADDRESS_MASK) == 0) { |
433 | eaab4d60 | Allen Kay | bar_data |= d->rom.base_addr & PCI_ROM_ADDRESS_MASK; |
434 | eaab4d60 | Allen Kay | xen_host_pci_set_long(d, PCI_ROM_ADDRESS, bar_data); |
435 | eaab4d60 | Allen Kay | } |
436 | eaab4d60 | Allen Kay | |
437 | eaab4d60 | Allen Kay | s->bases[PCI_ROM_SLOT].access.maddr = d->rom.base_addr; |
438 | eaab4d60 | Allen Kay | |
439 | eaab4d60 | Allen Kay | memory_region_init_rom_device(&s->rom, NULL, NULL, |
440 | eaab4d60 | Allen Kay | "xen-pci-pt-rom", d->rom.size);
|
441 | eaab4d60 | Allen Kay | pci_register_bar(&s->dev, PCI_ROM_SLOT, PCI_BASE_ADDRESS_MEM_PREFETCH, |
442 | eaab4d60 | Allen Kay | &s->rom); |
443 | eaab4d60 | Allen Kay | |
444 | eaab4d60 | Allen Kay | XEN_PT_LOG(&s->dev, "Expansion ROM registered (size=0x%08"PRIx64
|
445 | eaab4d60 | Allen Kay | " base_addr=0x%08"PRIx64")\n", |
446 | eaab4d60 | Allen Kay | d->rom.size, d->rom.base_addr); |
447 | eaab4d60 | Allen Kay | } |
448 | eaab4d60 | Allen Kay | |
449 | eaab4d60 | Allen Kay | return 0; |
450 | eaab4d60 | Allen Kay | } |
451 | eaab4d60 | Allen Kay | |
452 | eaab4d60 | Allen Kay | static void xen_pt_unregister_regions(XenPCIPassthroughState *s) |
453 | eaab4d60 | Allen Kay | { |
454 | eaab4d60 | Allen Kay | XenHostPCIDevice *d = &s->real_device; |
455 | eaab4d60 | Allen Kay | int i;
|
456 | eaab4d60 | Allen Kay | |
457 | eaab4d60 | Allen Kay | for (i = 0; i < PCI_NUM_REGIONS - 1; i++) { |
458 | eaab4d60 | Allen Kay | XenHostPCIIORegion *r = &d->io_regions[i]; |
459 | eaab4d60 | Allen Kay | |
460 | eaab4d60 | Allen Kay | if (r->base_addr == 0 || r->size == 0) { |
461 | eaab4d60 | Allen Kay | continue;
|
462 | eaab4d60 | Allen Kay | } |
463 | eaab4d60 | Allen Kay | |
464 | eaab4d60 | Allen Kay | memory_region_destroy(&s->bar[i]); |
465 | eaab4d60 | Allen Kay | } |
466 | eaab4d60 | Allen Kay | if (d->rom.base_addr && d->rom.size) {
|
467 | eaab4d60 | Allen Kay | memory_region_destroy(&s->rom); |
468 | eaab4d60 | Allen Kay | } |
469 | eaab4d60 | Allen Kay | } |
470 | eaab4d60 | Allen Kay | |
471 | eaab4d60 | Allen Kay | /* region mapping */
|
472 | eaab4d60 | Allen Kay | |
473 | eaab4d60 | Allen Kay | static int xen_pt_bar_from_region(XenPCIPassthroughState *s, MemoryRegion *mr) |
474 | eaab4d60 | Allen Kay | { |
475 | eaab4d60 | Allen Kay | int i = 0; |
476 | eaab4d60 | Allen Kay | |
477 | eaab4d60 | Allen Kay | for (i = 0; i < PCI_NUM_REGIONS - 1; i++) { |
478 | eaab4d60 | Allen Kay | if (mr == &s->bar[i]) {
|
479 | eaab4d60 | Allen Kay | return i;
|
480 | eaab4d60 | Allen Kay | } |
481 | eaab4d60 | Allen Kay | } |
482 | eaab4d60 | Allen Kay | if (mr == &s->rom) {
|
483 | eaab4d60 | Allen Kay | return PCI_ROM_SLOT;
|
484 | eaab4d60 | Allen Kay | } |
485 | eaab4d60 | Allen Kay | return -1; |
486 | eaab4d60 | Allen Kay | } |
487 | eaab4d60 | Allen Kay | |
488 | eaab4d60 | Allen Kay | /*
|
489 | eaab4d60 | Allen Kay | * This function checks if an io_region overlaps an io_region from another
|
490 | eaab4d60 | Allen Kay | * device. The io_region to check is provided with (addr, size and type)
|
491 | eaab4d60 | Allen Kay | * A callback can be provided and will be called for every region that is
|
492 | eaab4d60 | Allen Kay | * overlapped.
|
493 | eaab4d60 | Allen Kay | * The return value indicates if the region is overlappsed */
|
494 | eaab4d60 | Allen Kay | struct CheckBarArgs {
|
495 | eaab4d60 | Allen Kay | XenPCIPassthroughState *s; |
496 | eaab4d60 | Allen Kay | pcibus_t addr; |
497 | eaab4d60 | Allen Kay | pcibus_t size; |
498 | eaab4d60 | Allen Kay | uint8_t type; |
499 | eaab4d60 | Allen Kay | bool rc;
|
500 | eaab4d60 | Allen Kay | }; |
501 | eaab4d60 | Allen Kay | static void xen_pt_check_bar_overlap(PCIBus *bus, PCIDevice *d, void *opaque) |
502 | eaab4d60 | Allen Kay | { |
503 | eaab4d60 | Allen Kay | struct CheckBarArgs *arg = opaque;
|
504 | eaab4d60 | Allen Kay | XenPCIPassthroughState *s = arg->s; |
505 | eaab4d60 | Allen Kay | uint8_t type = arg->type; |
506 | eaab4d60 | Allen Kay | int i;
|
507 | eaab4d60 | Allen Kay | |
508 | eaab4d60 | Allen Kay | if (d->devfn == s->dev.devfn) {
|
509 | eaab4d60 | Allen Kay | return;
|
510 | eaab4d60 | Allen Kay | } |
511 | eaab4d60 | Allen Kay | |
512 | eaab4d60 | Allen Kay | /* xxx: This ignores bridges. */
|
513 | eaab4d60 | Allen Kay | for (i = 0; i < PCI_NUM_REGIONS; i++) { |
514 | eaab4d60 | Allen Kay | const PCIIORegion *r = &d->io_regions[i];
|
515 | eaab4d60 | Allen Kay | |
516 | eaab4d60 | Allen Kay | if (!r->size) {
|
517 | eaab4d60 | Allen Kay | continue;
|
518 | eaab4d60 | Allen Kay | } |
519 | eaab4d60 | Allen Kay | if ((type & PCI_BASE_ADDRESS_SPACE_IO)
|
520 | eaab4d60 | Allen Kay | != (r->type & PCI_BASE_ADDRESS_SPACE_IO)) { |
521 | eaab4d60 | Allen Kay | continue;
|
522 | eaab4d60 | Allen Kay | } |
523 | eaab4d60 | Allen Kay | |
524 | eaab4d60 | Allen Kay | if (ranges_overlap(arg->addr, arg->size, r->addr, r->size)) {
|
525 | eaab4d60 | Allen Kay | XEN_PT_WARN(&s->dev, |
526 | eaab4d60 | Allen Kay | "Overlapped to device [%02x:%02x.%d] Region: %i"
|
527 | eaab4d60 | Allen Kay | " (addr: %#"FMT_PCIBUS", len: %#"FMT_PCIBUS")\n", |
528 | eaab4d60 | Allen Kay | pci_bus_num(bus), PCI_SLOT(d->devfn), |
529 | eaab4d60 | Allen Kay | PCI_FUNC(d->devfn), i, r->addr, r->size); |
530 | eaab4d60 | Allen Kay | arg->rc = true;
|
531 | eaab4d60 | Allen Kay | } |
532 | eaab4d60 | Allen Kay | } |
533 | eaab4d60 | Allen Kay | } |
534 | eaab4d60 | Allen Kay | |
535 | eaab4d60 | Allen Kay | static void xen_pt_region_update(XenPCIPassthroughState *s, |
536 | eaab4d60 | Allen Kay | MemoryRegionSection *sec, bool adding)
|
537 | eaab4d60 | Allen Kay | { |
538 | eaab4d60 | Allen Kay | PCIDevice *d = &s->dev; |
539 | eaab4d60 | Allen Kay | MemoryRegion *mr = sec->mr; |
540 | eaab4d60 | Allen Kay | int bar = -1; |
541 | eaab4d60 | Allen Kay | int rc;
|
542 | eaab4d60 | Allen Kay | int op = adding ? DPCI_ADD_MAPPING : DPCI_REMOVE_MAPPING;
|
543 | eaab4d60 | Allen Kay | struct CheckBarArgs args = {
|
544 | eaab4d60 | Allen Kay | .s = s, |
545 | eaab4d60 | Allen Kay | .addr = sec->offset_within_address_space, |
546 | eaab4d60 | Allen Kay | .size = sec->size, |
547 | eaab4d60 | Allen Kay | .rc = false,
|
548 | eaab4d60 | Allen Kay | }; |
549 | eaab4d60 | Allen Kay | |
550 | eaab4d60 | Allen Kay | bar = xen_pt_bar_from_region(s, mr); |
551 | 3854ca57 | Jiang Yunhong | if (bar == -1 && (!s->msix || &s->msix->mmio != mr)) { |
552 | 3854ca57 | Jiang Yunhong | return;
|
553 | 3854ca57 | Jiang Yunhong | } |
554 | 3854ca57 | Jiang Yunhong | |
555 | 3854ca57 | Jiang Yunhong | if (s->msix && &s->msix->mmio == mr) {
|
556 | 3854ca57 | Jiang Yunhong | if (adding) {
|
557 | 3854ca57 | Jiang Yunhong | s->msix->mmio_base_addr = sec->offset_within_address_space; |
558 | 3854ca57 | Jiang Yunhong | rc = xen_pt_msix_update_remap(s, s->msix->bar_index); |
559 | 3854ca57 | Jiang Yunhong | } |
560 | eaab4d60 | Allen Kay | return;
|
561 | eaab4d60 | Allen Kay | } |
562 | eaab4d60 | Allen Kay | |
563 | eaab4d60 | Allen Kay | args.type = d->io_regions[bar].type; |
564 | eaab4d60 | Allen Kay | pci_for_each_device(d->bus, pci_bus_num(d->bus), |
565 | eaab4d60 | Allen Kay | xen_pt_check_bar_overlap, &args); |
566 | eaab4d60 | Allen Kay | if (args.rc) {
|
567 | eaab4d60 | Allen Kay | XEN_PT_WARN(d, "Region: %d (addr: %#"FMT_PCIBUS
|
568 | eaab4d60 | Allen Kay | ", len: %#"FMT_PCIBUS") is overlapped.\n", |
569 | eaab4d60 | Allen Kay | bar, sec->offset_within_address_space, sec->size); |
570 | eaab4d60 | Allen Kay | } |
571 | eaab4d60 | Allen Kay | |
572 | eaab4d60 | Allen Kay | if (d->io_regions[bar].type & PCI_BASE_ADDRESS_SPACE_IO) {
|
573 | eaab4d60 | Allen Kay | uint32_t guest_port = sec->offset_within_address_space; |
574 | eaab4d60 | Allen Kay | uint32_t machine_port = s->bases[bar].access.pio_base; |
575 | eaab4d60 | Allen Kay | uint32_t size = sec->size; |
576 | eaab4d60 | Allen Kay | rc = xc_domain_ioport_mapping(xen_xc, xen_domid, |
577 | eaab4d60 | Allen Kay | guest_port, machine_port, size, |
578 | eaab4d60 | Allen Kay | op); |
579 | eaab4d60 | Allen Kay | if (rc) {
|
580 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "%s ioport mapping failed! (rc: %i)\n",
|
581 | eaab4d60 | Allen Kay | adding ? "create new" : "remove old", rc); |
582 | eaab4d60 | Allen Kay | } |
583 | eaab4d60 | Allen Kay | } else {
|
584 | eaab4d60 | Allen Kay | pcibus_t guest_addr = sec->offset_within_address_space; |
585 | eaab4d60 | Allen Kay | pcibus_t machine_addr = s->bases[bar].access.maddr |
586 | eaab4d60 | Allen Kay | + sec->offset_within_region; |
587 | eaab4d60 | Allen Kay | pcibus_t size = sec->size; |
588 | eaab4d60 | Allen Kay | rc = xc_domain_memory_mapping(xen_xc, xen_domid, |
589 | eaab4d60 | Allen Kay | XEN_PFN(guest_addr + XC_PAGE_SIZE - 1),
|
590 | eaab4d60 | Allen Kay | XEN_PFN(machine_addr + XC_PAGE_SIZE - 1),
|
591 | eaab4d60 | Allen Kay | XEN_PFN(size + XC_PAGE_SIZE - 1),
|
592 | eaab4d60 | Allen Kay | op); |
593 | eaab4d60 | Allen Kay | if (rc) {
|
594 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "%s mem mapping failed! (rc: %i)\n",
|
595 | eaab4d60 | Allen Kay | adding ? "create new" : "remove old", rc); |
596 | eaab4d60 | Allen Kay | } |
597 | eaab4d60 | Allen Kay | } |
598 | eaab4d60 | Allen Kay | } |
599 | eaab4d60 | Allen Kay | |
600 | eaab4d60 | Allen Kay | static void xen_pt_begin(MemoryListener *l) |
601 | eaab4d60 | Allen Kay | { |
602 | eaab4d60 | Allen Kay | } |
603 | eaab4d60 | Allen Kay | |
604 | eaab4d60 | Allen Kay | static void xen_pt_commit(MemoryListener *l) |
605 | eaab4d60 | Allen Kay | { |
606 | eaab4d60 | Allen Kay | } |
607 | eaab4d60 | Allen Kay | |
608 | eaab4d60 | Allen Kay | static void xen_pt_region_add(MemoryListener *l, MemoryRegionSection *sec) |
609 | eaab4d60 | Allen Kay | { |
610 | eaab4d60 | Allen Kay | XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, |
611 | eaab4d60 | Allen Kay | memory_listener); |
612 | eaab4d60 | Allen Kay | |
613 | eaab4d60 | Allen Kay | xen_pt_region_update(s, sec, true);
|
614 | eaab4d60 | Allen Kay | } |
615 | eaab4d60 | Allen Kay | |
616 | eaab4d60 | Allen Kay | static void xen_pt_region_del(MemoryListener *l, MemoryRegionSection *sec) |
617 | eaab4d60 | Allen Kay | { |
618 | eaab4d60 | Allen Kay | XenPCIPassthroughState *s = container_of(l, XenPCIPassthroughState, |
619 | eaab4d60 | Allen Kay | memory_listener); |
620 | eaab4d60 | Allen Kay | |
621 | eaab4d60 | Allen Kay | xen_pt_region_update(s, sec, false);
|
622 | eaab4d60 | Allen Kay | } |
623 | eaab4d60 | Allen Kay | |
624 | eaab4d60 | Allen Kay | static void xen_pt_region_nop(MemoryListener *l, MemoryRegionSection *s) |
625 | eaab4d60 | Allen Kay | { |
626 | eaab4d60 | Allen Kay | } |
627 | eaab4d60 | Allen Kay | |
628 | eaab4d60 | Allen Kay | static void xen_pt_log_fns(MemoryListener *l, MemoryRegionSection *s) |
629 | eaab4d60 | Allen Kay | { |
630 | eaab4d60 | Allen Kay | } |
631 | eaab4d60 | Allen Kay | |
632 | eaab4d60 | Allen Kay | static void xen_pt_log_global_fns(MemoryListener *l) |
633 | eaab4d60 | Allen Kay | { |
634 | eaab4d60 | Allen Kay | } |
635 | eaab4d60 | Allen Kay | |
636 | eaab4d60 | Allen Kay | static void xen_pt_eventfd_fns(MemoryListener *l, MemoryRegionSection *s, |
637 | 753d5e14 | Paolo Bonzini | bool match_data, uint64_t data, EventNotifier *n)
|
638 | eaab4d60 | Allen Kay | { |
639 | eaab4d60 | Allen Kay | } |
640 | eaab4d60 | Allen Kay | |
641 | eaab4d60 | Allen Kay | static const MemoryListener xen_pt_memory_listener = { |
642 | eaab4d60 | Allen Kay | .begin = xen_pt_begin, |
643 | eaab4d60 | Allen Kay | .commit = xen_pt_commit, |
644 | eaab4d60 | Allen Kay | .region_add = xen_pt_region_add, |
645 | eaab4d60 | Allen Kay | .region_nop = xen_pt_region_nop, |
646 | eaab4d60 | Allen Kay | .region_del = xen_pt_region_del, |
647 | eaab4d60 | Allen Kay | .log_start = xen_pt_log_fns, |
648 | eaab4d60 | Allen Kay | .log_stop = xen_pt_log_fns, |
649 | eaab4d60 | Allen Kay | .log_sync = xen_pt_log_fns, |
650 | eaab4d60 | Allen Kay | .log_global_start = xen_pt_log_global_fns, |
651 | eaab4d60 | Allen Kay | .log_global_stop = xen_pt_log_global_fns, |
652 | eaab4d60 | Allen Kay | .eventfd_add = xen_pt_eventfd_fns, |
653 | eaab4d60 | Allen Kay | .eventfd_del = xen_pt_eventfd_fns, |
654 | eaab4d60 | Allen Kay | .priority = 10,
|
655 | eaab4d60 | Allen Kay | }; |
656 | eaab4d60 | Allen Kay | |
657 | eaab4d60 | Allen Kay | /* init */
|
658 | eaab4d60 | Allen Kay | |
659 | eaab4d60 | Allen Kay | static int xen_pt_initfn(PCIDevice *d) |
660 | eaab4d60 | Allen Kay | { |
661 | eaab4d60 | Allen Kay | XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); |
662 | eaab4d60 | Allen Kay | int rc = 0; |
663 | eaab4d60 | Allen Kay | uint8_t machine_irq = 0;
|
664 | eaab4d60 | Allen Kay | int pirq = XEN_PT_UNASSIGNED_PIRQ;
|
665 | eaab4d60 | Allen Kay | |
666 | eaab4d60 | Allen Kay | /* register real device */
|
667 | eaab4d60 | Allen Kay | XEN_PT_LOG(d, "Assigning real physical device %02x:%02x.%d"
|
668 | eaab4d60 | Allen Kay | " to devfn %#x\n",
|
669 | eaab4d60 | Allen Kay | s->hostaddr.bus, s->hostaddr.slot, s->hostaddr.function, |
670 | eaab4d60 | Allen Kay | s->dev.devfn); |
671 | eaab4d60 | Allen Kay | |
672 | eaab4d60 | Allen Kay | rc = xen_host_pci_device_get(&s->real_device, |
673 | eaab4d60 | Allen Kay | s->hostaddr.domain, s->hostaddr.bus, |
674 | eaab4d60 | Allen Kay | s->hostaddr.slot, s->hostaddr.function); |
675 | eaab4d60 | Allen Kay | if (rc) {
|
676 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "Failed to \"open\" the real pci device. rc: %i\n", rc);
|
677 | eaab4d60 | Allen Kay | return -1; |
678 | eaab4d60 | Allen Kay | } |
679 | eaab4d60 | Allen Kay | |
680 | eaab4d60 | Allen Kay | s->is_virtfn = s->real_device.is_virtfn; |
681 | eaab4d60 | Allen Kay | if (s->is_virtfn) {
|
682 | eaab4d60 | Allen Kay | XEN_PT_LOG(d, "%04x:%02x:%02x.%d is a SR-IOV Virtual Function\n",
|
683 | eaab4d60 | Allen Kay | s->real_device.domain, bus, slot, func); |
684 | eaab4d60 | Allen Kay | } |
685 | eaab4d60 | Allen Kay | |
686 | eaab4d60 | Allen Kay | /* Initialize virtualized PCI configuration (Extended 256 Bytes) */
|
687 | eaab4d60 | Allen Kay | if (xen_host_pci_get_block(&s->real_device, 0, d->config, |
688 | eaab4d60 | Allen Kay | PCI_CONFIG_SPACE_SIZE) == -1) {
|
689 | eaab4d60 | Allen Kay | xen_host_pci_device_put(&s->real_device); |
690 | eaab4d60 | Allen Kay | return -1; |
691 | eaab4d60 | Allen Kay | } |
692 | eaab4d60 | Allen Kay | |
693 | eaab4d60 | Allen Kay | s->memory_listener = xen_pt_memory_listener; |
694 | eaab4d60 | Allen Kay | |
695 | eaab4d60 | Allen Kay | /* Handle real device's MMIO/PIO BARs */
|
696 | eaab4d60 | Allen Kay | xen_pt_register_regions(s); |
697 | eaab4d60 | Allen Kay | |
698 | 93d7ae8e | Allen Kay | /* reinitialize each config register to be emulated */
|
699 | 93d7ae8e | Allen Kay | if (xen_pt_config_init(s)) {
|
700 | 93d7ae8e | Allen Kay | XEN_PT_ERR(d, "PCI Config space initialisation failed.\n");
|
701 | 93d7ae8e | Allen Kay | xen_host_pci_device_put(&s->real_device); |
702 | 93d7ae8e | Allen Kay | return -1; |
703 | 93d7ae8e | Allen Kay | } |
704 | 93d7ae8e | Allen Kay | |
705 | eaab4d60 | Allen Kay | /* Bind interrupt */
|
706 | eaab4d60 | Allen Kay | if (!s->dev.config[PCI_INTERRUPT_PIN]) {
|
707 | eaab4d60 | Allen Kay | XEN_PT_LOG(d, "no pin interrupt\n");
|
708 | eaab4d60 | Allen Kay | goto out;
|
709 | eaab4d60 | Allen Kay | } |
710 | eaab4d60 | Allen Kay | |
711 | eaab4d60 | Allen Kay | machine_irq = s->real_device.irq; |
712 | eaab4d60 | Allen Kay | rc = xc_physdev_map_pirq(xen_xc, xen_domid, machine_irq, &pirq); |
713 | eaab4d60 | Allen Kay | |
714 | eaab4d60 | Allen Kay | if (rc < 0) { |
715 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "Mapping machine irq %u to pirq %i failed, (rc: %d)\n",
|
716 | eaab4d60 | Allen Kay | machine_irq, pirq, rc); |
717 | eaab4d60 | Allen Kay | |
718 | eaab4d60 | Allen Kay | /* Disable PCI intx assertion (turn on bit10 of devctl) */
|
719 | eaab4d60 | Allen Kay | xen_host_pci_set_word(&s->real_device, |
720 | eaab4d60 | Allen Kay | PCI_COMMAND, |
721 | eaab4d60 | Allen Kay | pci_get_word(s->dev.config + PCI_COMMAND) |
722 | eaab4d60 | Allen Kay | | PCI_COMMAND_INTX_DISABLE); |
723 | eaab4d60 | Allen Kay | machine_irq = 0;
|
724 | eaab4d60 | Allen Kay | s->machine_irq = 0;
|
725 | eaab4d60 | Allen Kay | } else {
|
726 | eaab4d60 | Allen Kay | machine_irq = pirq; |
727 | eaab4d60 | Allen Kay | s->machine_irq = pirq; |
728 | eaab4d60 | Allen Kay | xen_pt_mapped_machine_irq[machine_irq]++; |
729 | eaab4d60 | Allen Kay | } |
730 | eaab4d60 | Allen Kay | |
731 | eaab4d60 | Allen Kay | /* bind machine_irq to device */
|
732 | eaab4d60 | Allen Kay | if (machine_irq != 0) { |
733 | eaab4d60 | Allen Kay | uint8_t e_intx = xen_pt_pci_intx(s); |
734 | eaab4d60 | Allen Kay | |
735 | eaab4d60 | Allen Kay | rc = xc_domain_bind_pt_pci_irq(xen_xc, xen_domid, machine_irq, |
736 | eaab4d60 | Allen Kay | pci_bus_num(d->bus), |
737 | eaab4d60 | Allen Kay | PCI_SLOT(d->devfn), |
738 | eaab4d60 | Allen Kay | e_intx); |
739 | eaab4d60 | Allen Kay | if (rc < 0) { |
740 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "Binding of interrupt %i failed! (rc: %d)\n",
|
741 | eaab4d60 | Allen Kay | e_intx, rc); |
742 | eaab4d60 | Allen Kay | |
743 | eaab4d60 | Allen Kay | /* Disable PCI intx assertion (turn on bit10 of devctl) */
|
744 | eaab4d60 | Allen Kay | xen_host_pci_set_word(&s->real_device, PCI_COMMAND, |
745 | eaab4d60 | Allen Kay | *(uint16_t *)(&s->dev.config[PCI_COMMAND]) |
746 | eaab4d60 | Allen Kay | | PCI_COMMAND_INTX_DISABLE); |
747 | eaab4d60 | Allen Kay | xen_pt_mapped_machine_irq[machine_irq]--; |
748 | eaab4d60 | Allen Kay | |
749 | eaab4d60 | Allen Kay | if (xen_pt_mapped_machine_irq[machine_irq] == 0) { |
750 | eaab4d60 | Allen Kay | if (xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq)) {
|
751 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "Unmapping of machine interrupt %i failed!"
|
752 | eaab4d60 | Allen Kay | " (rc: %d)\n", machine_irq, rc);
|
753 | eaab4d60 | Allen Kay | } |
754 | eaab4d60 | Allen Kay | } |
755 | eaab4d60 | Allen Kay | s->machine_irq = 0;
|
756 | eaab4d60 | Allen Kay | } |
757 | eaab4d60 | Allen Kay | } |
758 | eaab4d60 | Allen Kay | |
759 | eaab4d60 | Allen Kay | out:
|
760 | eaab4d60 | Allen Kay | memory_listener_register(&s->memory_listener, NULL);
|
761 | eaab4d60 | Allen Kay | XEN_PT_LOG(d, "Real physical device %02x:%02x.%d registered successfuly!\n",
|
762 | eaab4d60 | Allen Kay | bus, slot, func); |
763 | eaab4d60 | Allen Kay | |
764 | eaab4d60 | Allen Kay | return 0; |
765 | eaab4d60 | Allen Kay | } |
766 | eaab4d60 | Allen Kay | |
767 | eaab4d60 | Allen Kay | static int xen_pt_unregister_device(PCIDevice *d) |
768 | eaab4d60 | Allen Kay | { |
769 | eaab4d60 | Allen Kay | XenPCIPassthroughState *s = DO_UPCAST(XenPCIPassthroughState, dev, d); |
770 | eaab4d60 | Allen Kay | uint8_t machine_irq = s->machine_irq; |
771 | eaab4d60 | Allen Kay | uint8_t intx = xen_pt_pci_intx(s); |
772 | eaab4d60 | Allen Kay | int rc;
|
773 | eaab4d60 | Allen Kay | |
774 | eaab4d60 | Allen Kay | if (machine_irq) {
|
775 | eaab4d60 | Allen Kay | rc = xc_domain_unbind_pt_irq(xen_xc, xen_domid, machine_irq, |
776 | eaab4d60 | Allen Kay | PT_IRQ_TYPE_PCI, |
777 | eaab4d60 | Allen Kay | pci_bus_num(d->bus), |
778 | eaab4d60 | Allen Kay | PCI_SLOT(s->dev.devfn), |
779 | eaab4d60 | Allen Kay | intx, |
780 | eaab4d60 | Allen Kay | 0 /* isa_irq */); |
781 | eaab4d60 | Allen Kay | if (rc < 0) { |
782 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "unbinding of interrupt INT%c failed."
|
783 | eaab4d60 | Allen Kay | " (machine irq: %i, rc: %d)"
|
784 | eaab4d60 | Allen Kay | " But bravely continuing on..\n",
|
785 | eaab4d60 | Allen Kay | 'a' + intx, machine_irq, rc);
|
786 | eaab4d60 | Allen Kay | } |
787 | eaab4d60 | Allen Kay | } |
788 | eaab4d60 | Allen Kay | |
789 | 3854ca57 | Jiang Yunhong | if (s->msi) {
|
790 | 3854ca57 | Jiang Yunhong | xen_pt_msi_disable(s); |
791 | 3854ca57 | Jiang Yunhong | } |
792 | 3854ca57 | Jiang Yunhong | if (s->msix) {
|
793 | 3854ca57 | Jiang Yunhong | xen_pt_msix_disable(s); |
794 | 3854ca57 | Jiang Yunhong | } |
795 | 3854ca57 | Jiang Yunhong | |
796 | eaab4d60 | Allen Kay | if (machine_irq) {
|
797 | eaab4d60 | Allen Kay | xen_pt_mapped_machine_irq[machine_irq]--; |
798 | eaab4d60 | Allen Kay | |
799 | eaab4d60 | Allen Kay | if (xen_pt_mapped_machine_irq[machine_irq] == 0) { |
800 | eaab4d60 | Allen Kay | rc = xc_physdev_unmap_pirq(xen_xc, xen_domid, machine_irq); |
801 | eaab4d60 | Allen Kay | |
802 | eaab4d60 | Allen Kay | if (rc < 0) { |
803 | eaab4d60 | Allen Kay | XEN_PT_ERR(d, "unmapping of interrupt %i failed. (rc: %d)"
|
804 | eaab4d60 | Allen Kay | " But bravely continuing on..\n",
|
805 | eaab4d60 | Allen Kay | machine_irq, rc); |
806 | eaab4d60 | Allen Kay | } |
807 | eaab4d60 | Allen Kay | } |
808 | eaab4d60 | Allen Kay | } |
809 | eaab4d60 | Allen Kay | |
810 | 93d7ae8e | Allen Kay | /* delete all emulated config registers */
|
811 | 93d7ae8e | Allen Kay | xen_pt_config_delete(s); |
812 | 93d7ae8e | Allen Kay | |
813 | eaab4d60 | Allen Kay | xen_pt_unregister_regions(s); |
814 | eaab4d60 | Allen Kay | memory_listener_unregister(&s->memory_listener); |
815 | eaab4d60 | Allen Kay | |
816 | eaab4d60 | Allen Kay | xen_host_pci_device_put(&s->real_device); |
817 | eaab4d60 | Allen Kay | |
818 | eaab4d60 | Allen Kay | return 0; |
819 | eaab4d60 | Allen Kay | } |
820 | eaab4d60 | Allen Kay | |
821 | eaab4d60 | Allen Kay | static Property xen_pci_passthrough_properties[] = {
|
822 | eaab4d60 | Allen Kay | DEFINE_PROP_PCI_HOST_DEVADDR("hostaddr", XenPCIPassthroughState, hostaddr),
|
823 | eaab4d60 | Allen Kay | DEFINE_PROP_END_OF_LIST(), |
824 | eaab4d60 | Allen Kay | }; |
825 | eaab4d60 | Allen Kay | |
826 | eaab4d60 | Allen Kay | static void xen_pci_passthrough_class_init(ObjectClass *klass, void *data) |
827 | eaab4d60 | Allen Kay | { |
828 | eaab4d60 | Allen Kay | DeviceClass *dc = DEVICE_CLASS(klass); |
829 | eaab4d60 | Allen Kay | PCIDeviceClass *k = PCI_DEVICE_CLASS(klass); |
830 | eaab4d60 | Allen Kay | |
831 | eaab4d60 | Allen Kay | k->init = xen_pt_initfn; |
832 | eaab4d60 | Allen Kay | k->exit = xen_pt_unregister_device; |
833 | eaab4d60 | Allen Kay | k->config_read = xen_pt_pci_read_config; |
834 | eaab4d60 | Allen Kay | k->config_write = xen_pt_pci_write_config; |
835 | eaab4d60 | Allen Kay | dc->desc = "Assign an host PCI device with Xen";
|
836 | eaab4d60 | Allen Kay | dc->props = xen_pci_passthrough_properties; |
837 | eaab4d60 | Allen Kay | }; |
838 | eaab4d60 | Allen Kay | |
839 | eaab4d60 | Allen Kay | static TypeInfo xen_pci_passthrough_info = {
|
840 | eaab4d60 | Allen Kay | .name = "xen-pci-passthrough",
|
841 | eaab4d60 | Allen Kay | .parent = TYPE_PCI_DEVICE, |
842 | eaab4d60 | Allen Kay | .instance_size = sizeof(XenPCIPassthroughState),
|
843 | eaab4d60 | Allen Kay | .class_init = xen_pci_passthrough_class_init, |
844 | eaab4d60 | Allen Kay | }; |
845 | eaab4d60 | Allen Kay | |
846 | eaab4d60 | Allen Kay | static void xen_pci_passthrough_register_types(void) |
847 | eaab4d60 | Allen Kay | { |
848 | eaab4d60 | Allen Kay | type_register_static(&xen_pci_passthrough_info); |
849 | eaab4d60 | Allen Kay | } |
850 | eaab4d60 | Allen Kay | |
851 | eaab4d60 | Allen Kay | type_init(xen_pci_passthrough_register_types) |