/root/src/xen/xen/arch/x86/hvm/io.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * io.c: Handling I/O and interrupts. |
3 | | * |
4 | | * Copyright (c) 2004, Intel Corporation. |
5 | | * Copyright (c) 2005, International Business Machines Corporation. |
6 | | * Copyright (c) 2008, Citrix Systems, Inc. |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify it |
9 | | * under the terms and conditions of the GNU General Public License, |
10 | | * version 2, as published by the Free Software Foundation. |
11 | | * |
12 | | * This program is distributed in the hope it will be useful, but WITHOUT |
13 | | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
14 | | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
15 | | * more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License along with |
18 | | * this program; If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include <xen/init.h> |
22 | | #include <xen/mm.h> |
23 | | #include <xen/lib.h> |
24 | | #include <xen/errno.h> |
25 | | #include <xen/trace.h> |
26 | | #include <xen/event.h> |
27 | | #include <xen/hypercall.h> |
28 | | #include <xen/vpci.h> |
29 | | #include <asm/current.h> |
30 | | #include <asm/cpufeature.h> |
31 | | #include <asm/processor.h> |
32 | | #include <asm/msr.h> |
33 | | #include <asm/apic.h> |
34 | | #include <asm/paging.h> |
35 | | #include <asm/shadow.h> |
36 | | #include <asm/p2m.h> |
37 | | #include <asm/hvm/hvm.h> |
38 | | #include <asm/hvm/ioreq.h> |
39 | | #include <asm/hvm/support.h> |
40 | | #include <asm/hvm/vpt.h> |
41 | | #include <asm/hvm/vpic.h> |
42 | | #include <asm/hvm/vlapic.h> |
43 | | #include <asm/hvm/trace.h> |
44 | | #include <asm/hvm/emulate.h> |
45 | | #include <public/sched.h> |
46 | | #include <xen/iocap.h> |
47 | | #include <public/hvm/ioreq.h> |
48 | | |
49 | | void send_timeoffset_req(unsigned long timeoff) |
50 | 0 | { |
51 | 0 | ioreq_t p = { |
52 | 0 | .type = IOREQ_TYPE_TIMEOFFSET, |
53 | 0 | .size = 8, |
54 | 0 | .count = 1, |
55 | 0 | .dir = IOREQ_WRITE, |
56 | 0 | .data = timeoff, |
57 | 0 | .state = STATE_IOREQ_READY, |
58 | 0 | }; |
59 | 0 |
|
60 | 0 | if ( timeoff == 0 ) |
61 | 0 | return; |
62 | 0 |
|
63 | 0 | if ( hvm_broadcast_ioreq(&p, true) != 0 ) |
64 | 0 | gprintk(XENLOG_ERR, "Unsuccessful timeoffset update\n"); |
65 | 0 | } |
66 | | |
67 | | /* Ask ioemu mapcache to invalidate mappings. */ |
68 | | void send_invalidate_req(void) |
69 | 0 | { |
70 | 0 | ioreq_t p = { |
71 | 0 | .type = IOREQ_TYPE_INVALIDATE, |
72 | 0 | .size = 4, |
73 | 0 | .dir = IOREQ_WRITE, |
74 | 0 | .data = ~0UL, /* flush all */ |
75 | 0 | }; |
76 | 0 |
|
77 | 0 | if ( hvm_broadcast_ioreq(&p, false) != 0 ) |
78 | 0 | gprintk(XENLOG_ERR, "Unsuccessful map-cache invalidate\n"); |
79 | 0 | } |
80 | | |
81 | | bool hvm_emulate_one_insn(hvm_emulate_validate_t *validate, const char *descr) |
82 | 60.1k | { |
83 | 60.1k | struct hvm_emulate_ctxt ctxt; |
84 | 60.1k | struct vcpu *curr = current; |
85 | 60.1k | struct hvm_vcpu_io *vio = &curr->arch.hvm_vcpu.hvm_io; |
86 | 60.1k | int rc; |
87 | 60.1k | |
88 | 60.1k | hvm_emulate_init_once(&ctxt, validate, guest_cpu_user_regs()); |
89 | 60.1k | |
90 | 60.1k | rc = hvm_emulate_one(&ctxt); |
91 | 60.1k | |
92 | 60.1k | if ( hvm_vcpu_io_need_completion(vio) || vio->mmio_retry ) |
93 | 0 | vio->io_completion = HVMIO_mmio_completion; |
94 | 60.1k | else |
95 | 60.1k | vio->mmio_access = (struct npfec){}; |
96 | 60.1k | |
97 | 60.1k | switch ( rc ) |
98 | 60.1k | { |
99 | 0 | case X86EMUL_UNHANDLEABLE: |
100 | 0 | hvm_dump_emulation_state(XENLOG_G_WARNING, descr, &ctxt, rc); |
101 | 0 | return false; |
102 | 0 |
|
103 | 0 | case X86EMUL_UNRECOGNIZED: |
104 | 0 | hvm_dump_emulation_state(XENLOG_G_WARNING, descr, &ctxt, rc); |
105 | 0 | hvm_inject_hw_exception(TRAP_invalid_op, X86_EVENT_NO_EC); |
106 | 0 | break; |
107 | 0 |
|
108 | 0 | case X86EMUL_EXCEPTION: |
109 | 0 | hvm_inject_event(&ctxt.ctxt.event); |
110 | 0 | break; |
111 | 60.1k | } |
112 | 60.1k | |
113 | 60.1k | hvm_emulate_writeback(&ctxt); |
114 | 60.1k | |
115 | 60.1k | return true; |
116 | 60.1k | } |
117 | | |
118 | | bool handle_mmio_with_translation(unsigned long gla, unsigned long gpfn, |
119 | | struct npfec access) |
120 | 60.1k | { |
121 | 60.1k | struct hvm_vcpu_io *vio = ¤t->arch.hvm_vcpu.hvm_io; |
122 | 60.1k | |
123 | 60.1k | vio->mmio_access = access.gla_valid && |
124 | 60.1k | access.kind == npfec_kind_with_gla |
125 | 60.1k | ? access : (struct npfec){}; |
126 | 60.1k | vio->mmio_gla = gla & PAGE_MASK; |
127 | 60.1k | vio->mmio_gpfn = gpfn; |
128 | 60.1k | return handle_mmio(); |
129 | 60.1k | } |
130 | | |
131 | | bool handle_pio(uint16_t port, unsigned int size, int dir) |
132 | 20.1k | { |
133 | 20.1k | struct vcpu *curr = current; |
134 | 20.1k | struct hvm_vcpu_io *vio = &curr->arch.hvm_vcpu.hvm_io; |
135 | 20.1k | unsigned long data; |
136 | 20.1k | int rc; |
137 | 20.1k | |
138 | 20.1k | ASSERT((size - 1) < 4 && size != 3); |
139 | 20.1k | |
140 | 20.1k | if ( dir == IOREQ_WRITE ) |
141 | 58 | data = guest_cpu_user_regs()->eax; |
142 | 20.1k | |
143 | 20.1k | rc = hvmemul_do_pio_buffer(port, size, dir, &data); |
144 | 20.1k | |
145 | 20.1k | if ( hvm_vcpu_io_need_completion(vio) ) |
146 | 0 | vio->io_completion = HVMIO_pio_completion; |
147 | 20.1k | |
148 | 20.1k | switch ( rc ) |
149 | 20.1k | { |
150 | 20.1k | case X86EMUL_OKAY: |
151 | 20.1k | if ( dir == IOREQ_READ ) |
152 | 20.0k | { |
153 | 20.0k | if ( size == 4 ) /* Needs zero extension. */ |
154 | 20.0k | guest_cpu_user_regs()->rax = (uint32_t)data; |
155 | 20.0k | else |
156 | 7 | memcpy(&guest_cpu_user_regs()->rax, &data, size); |
157 | 20.0k | } |
158 | 20.1k | break; |
159 | 20.1k | |
160 | 0 | case X86EMUL_RETRY: |
161 | 0 | /* |
162 | 0 | * We should not advance RIP/EIP if the domain is shutting down or |
163 | 0 | * if X86EMUL_RETRY has been returned by an internal handler. |
164 | 0 | */ |
165 | 0 | if ( curr->domain->is_shutting_down || !hvm_io_pending(curr) ) |
166 | 0 | return false; |
167 | 0 | break; |
168 | 0 |
|
169 | 0 | default: |
170 | 0 | gdprintk(XENLOG_ERR, "Weird HVM ioemulation status %d.\n", rc); |
171 | 0 | domain_crash(curr->domain); |
172 | 0 | return false; |
173 | 20.1k | } |
174 | 20.1k | |
175 | 20.1k | return true; |
176 | 20.1k | } |
177 | | |
178 | | static bool_t g2m_portio_accept(const struct hvm_io_handler *handler, |
179 | | const ioreq_t *p) |
180 | 20.1k | { |
181 | 20.1k | struct vcpu *curr = current; |
182 | 20.1k | const struct hvm_domain *hvm_domain = &curr->domain->arch.hvm_domain; |
183 | 20.1k | struct hvm_vcpu_io *vio = &curr->arch.hvm_vcpu.hvm_io; |
184 | 20.1k | struct g2m_ioport *g2m_ioport; |
185 | 20.1k | unsigned int start, end; |
186 | 20.1k | |
187 | 20.1k | list_for_each_entry( g2m_ioport, &hvm_domain->g2m_ioport_list, list ) |
188 | 0 | { |
189 | 0 | start = g2m_ioport->gport; |
190 | 0 | end = start + g2m_ioport->np; |
191 | 0 | if ( (p->addr >= start) && (p->addr + p->size <= end) ) |
192 | 0 | { |
193 | 0 | vio->g2m_ioport = g2m_ioport; |
194 | 0 | return 1; |
195 | 0 | } |
196 | 0 | } |
197 | 20.1k | |
198 | 20.1k | return 0; |
199 | 20.1k | } |
200 | | |
201 | | static int g2m_portio_read(const struct hvm_io_handler *handler, |
202 | | uint64_t addr, uint32_t size, uint64_t *data) |
203 | 0 | { |
204 | 0 | struct hvm_vcpu_io *vio = ¤t->arch.hvm_vcpu.hvm_io; |
205 | 0 | const struct g2m_ioport *g2m_ioport = vio->g2m_ioport; |
206 | 0 | unsigned int mport = (addr - g2m_ioport->gport) + g2m_ioport->mport; |
207 | 0 |
|
208 | 0 | switch ( size ) |
209 | 0 | { |
210 | 0 | case 1: |
211 | 0 | *data = inb(mport); |
212 | 0 | break; |
213 | 0 | case 2: |
214 | 0 | *data = inw(mport); |
215 | 0 | break; |
216 | 0 | case 4: |
217 | 0 | *data = inl(mport); |
218 | 0 | break; |
219 | 0 | default: |
220 | 0 | BUG(); |
221 | 0 | } |
222 | 0 |
|
223 | 0 | return X86EMUL_OKAY; |
224 | 0 | } |
225 | | |
226 | | static int g2m_portio_write(const struct hvm_io_handler *handler, |
227 | | uint64_t addr, uint32_t size, uint64_t data) |
228 | 0 | { |
229 | 0 | struct hvm_vcpu_io *vio = ¤t->arch.hvm_vcpu.hvm_io; |
230 | 0 | const struct g2m_ioport *g2m_ioport = vio->g2m_ioport; |
231 | 0 | unsigned int mport = (addr - g2m_ioport->gport) + g2m_ioport->mport; |
232 | 0 |
|
233 | 0 | switch ( size ) |
234 | 0 | { |
235 | 0 | case 1: |
236 | 0 | outb(data, mport); |
237 | 0 | break; |
238 | 0 | case 2: |
239 | 0 | outw(data, mport); |
240 | 0 | break; |
241 | 0 | case 4: |
242 | 0 | outl(data, mport); |
243 | 0 | break; |
244 | 0 | default: |
245 | 0 | BUG(); |
246 | 0 | } |
247 | 0 |
|
248 | 0 | return X86EMUL_OKAY; |
249 | 0 | } |
250 | | |
251 | | static const struct hvm_io_ops g2m_portio_ops = { |
252 | | .accept = g2m_portio_accept, |
253 | | .read = g2m_portio_read, |
254 | | .write = g2m_portio_write |
255 | | }; |
256 | | |
257 | | void register_g2m_portio_handler(struct domain *d) |
258 | 1 | { |
259 | 1 | struct hvm_io_handler *handler = hvm_next_io_handler(d); |
260 | 1 | |
261 | 1 | if ( handler == NULL ) |
262 | 0 | return; |
263 | 1 | |
264 | 1 | handler->type = IOREQ_TYPE_PIO; |
265 | 1 | handler->ops = &g2m_portio_ops; |
266 | 1 | } |
267 | | |
268 | | unsigned int hvm_pci_decode_addr(unsigned int cf8, unsigned int addr, |
269 | | pci_sbdf_t *sbdf) |
270 | 32 | { |
271 | 32 | ASSERT(CF8_ENABLED(cf8)); |
272 | 32 | |
273 | 32 | sbdf->bdf = CF8_BDF(cf8); |
274 | 32 | sbdf->seg = 0; |
275 | 32 | /* |
276 | 32 | * NB: the lower 2 bits of the register address are fetched from the |
277 | 32 | * offset into the 0xcfc register when reading/writing to it. |
278 | 32 | */ |
279 | 32 | return CF8_ADDR_LO(cf8) | (addr & 3); |
280 | 32 | } |
281 | | |
282 | | /* Do some sanity checks. */ |
283 | | static bool vpci_access_allowed(unsigned int reg, unsigned int len) |
284 | 58.3k | { |
285 | 58.3k | /* Check access size. */ |
286 | 58.3k | if ( len != 1 && len != 2 && len != 4 && len != 8 ) |
287 | 0 | return false; |
288 | 58.3k | |
289 | 58.3k | /* Check that access is size aligned. */ |
290 | 58.3k | if ( (reg & (len - 1)) ) |
291 | 1 | return false; |
292 | 58.3k | |
293 | 58.3k | return true; |
294 | 58.3k | } |
295 | | |
296 | | /* vPCI config space IO ports handlers (0xcf8/0xcfc). */ |
297 | | static bool vpci_portio_accept(const struct hvm_io_handler *handler, |
298 | | const ioreq_t *p) |
299 | 20.1k | { |
300 | 20.0k | return (p->addr == 0xcf8 && p->size == 4) || (p->addr & ~3) == 0xcfc; |
301 | 20.1k | } |
302 | | |
303 | | static int vpci_portio_read(const struct hvm_io_handler *handler, |
304 | | uint64_t addr, uint32_t size, uint64_t *data) |
305 | 32 | { |
306 | 32 | struct domain *d = current->domain; |
307 | 32 | unsigned int reg; |
308 | 32 | pci_sbdf_t sbdf; |
309 | 32 | uint32_t cf8; |
310 | 32 | |
311 | 32 | *data = ~(uint64_t)0; |
312 | 32 | |
313 | 32 | if ( addr == 0xcf8 ) |
314 | 0 | { |
315 | 0 | ASSERT(size == 4); |
316 | 0 | *data = d->arch.hvm_domain.pci_cf8; |
317 | 0 | return X86EMUL_OKAY; |
318 | 0 | } |
319 | 32 | |
320 | 32 | cf8 = ACCESS_ONCE(d->arch.hvm_domain.pci_cf8); |
321 | 32 | if ( !CF8_ENABLED(cf8) ) |
322 | 0 | return X86EMUL_UNHANDLEABLE; |
323 | 32 | |
324 | 32 | reg = hvm_pci_decode_addr(cf8, addr, &sbdf); |
325 | 32 | |
326 | 32 | if ( !vpci_access_allowed(reg, size) ) |
327 | 0 | return X86EMUL_OKAY; |
328 | 32 | |
329 | 32 | *data = vpci_read(sbdf, reg, size); |
330 | 32 | |
331 | 32 | return X86EMUL_OKAY; |
332 | 32 | } |
333 | | |
334 | | static int vpci_portio_write(const struct hvm_io_handler *handler, |
335 | | uint64_t addr, uint32_t size, uint64_t data) |
336 | 32 | { |
337 | 32 | struct domain *d = current->domain; |
338 | 32 | unsigned int reg; |
339 | 32 | pci_sbdf_t sbdf; |
340 | 32 | uint32_t cf8; |
341 | 32 | |
342 | 32 | if ( addr == 0xcf8 ) |
343 | 32 | { |
344 | 32 | ASSERT(size == 4); |
345 | 32 | d->arch.hvm_domain.pci_cf8 = data; |
346 | 32 | return X86EMUL_OKAY; |
347 | 32 | } |
348 | 32 | |
349 | 0 | cf8 = ACCESS_ONCE(d->arch.hvm_domain.pci_cf8); |
350 | 0 | if ( !CF8_ENABLED(cf8) ) |
351 | 0 | return X86EMUL_UNHANDLEABLE; |
352 | 0 |
|
353 | 0 | reg = hvm_pci_decode_addr(cf8, addr, &sbdf); |
354 | 0 |
|
355 | 0 | if ( !vpci_access_allowed(reg, size) ) |
356 | 0 | return X86EMUL_OKAY; |
357 | 0 |
|
358 | 0 | vpci_write(sbdf, reg, size, data); |
359 | 0 |
|
360 | 0 | return X86EMUL_OKAY; |
361 | 0 | } |
362 | | |
363 | | static const struct hvm_io_ops vpci_portio_ops = { |
364 | | .accept = vpci_portio_accept, |
365 | | .read = vpci_portio_read, |
366 | | .write = vpci_portio_write, |
367 | | }; |
368 | | |
369 | | void register_vpci_portio_handler(struct domain *d) |
370 | 1 | { |
371 | 1 | struct hvm_io_handler *handler; |
372 | 1 | |
373 | 1 | if ( !has_vpci(d) ) |
374 | 0 | return; |
375 | 1 | |
376 | 1 | handler = hvm_next_io_handler(d); |
377 | 1 | if ( !handler ) |
378 | 0 | return; |
379 | 1 | |
380 | 1 | handler->type = IOREQ_TYPE_PIO; |
381 | 1 | handler->ops = &vpci_portio_ops; |
382 | 1 | } |
383 | | |
384 | | struct hvm_mmcfg { |
385 | | struct list_head next; |
386 | | paddr_t addr; |
387 | | unsigned int size; |
388 | | uint16_t segment; |
389 | | uint8_t start_bus; |
390 | | }; |
391 | | |
392 | | /* Handlers to trap PCI MMCFG config accesses. */ |
393 | | static const struct hvm_mmcfg *vpci_mmcfg_find(const struct domain *d, |
394 | | paddr_t addr) |
395 | 596k | { |
396 | 596k | const struct hvm_mmcfg *mmcfg; |
397 | 596k | |
398 | 596k | list_for_each_entry ( mmcfg, &d->arch.hvm_domain.mmcfg_regions, next ) |
399 | 596k | if ( addr >= mmcfg->addr && addr < mmcfg->addr + mmcfg->size ) |
400 | 229k | return mmcfg; |
401 | 596k | |
402 | 366k | return NULL; |
403 | 596k | } |
404 | | |
405 | | static unsigned int vpci_mmcfg_decode_addr(const struct hvm_mmcfg *mmcfg, |
406 | | paddr_t addr, pci_sbdf_t *sbdf) |
407 | 58.3k | { |
408 | 58.3k | addr -= mmcfg->addr; |
409 | 58.3k | sbdf->bdf = MMCFG_BDF(addr); |
410 | 58.3k | sbdf->bus += mmcfg->start_bus; |
411 | 58.3k | sbdf->seg = mmcfg->segment; |
412 | 58.3k | |
413 | 58.3k | return addr & (PCI_CFG_SPACE_EXP_SIZE - 1); |
414 | 58.3k | } |
415 | | |
416 | | static int vpci_mmcfg_accept(struct vcpu *v, unsigned long addr) |
417 | 535k | { |
418 | 535k | struct domain *d = v->domain; |
419 | 535k | bool found; |
420 | 535k | |
421 | 535k | read_lock(&d->arch.hvm_domain.mmcfg_lock); |
422 | 535k | found = vpci_mmcfg_find(d, addr); |
423 | 535k | read_unlock(&d->arch.hvm_domain.mmcfg_lock); |
424 | 535k | |
425 | 535k | return found; |
426 | 535k | } |
427 | | |
428 | | static int vpci_mmcfg_read(struct vcpu *v, unsigned long addr, |
429 | | unsigned int len, unsigned long *data) |
430 | 55.8k | { |
431 | 55.8k | struct domain *d = v->domain; |
432 | 55.8k | const struct hvm_mmcfg *mmcfg; |
433 | 55.8k | unsigned int reg; |
434 | 55.8k | pci_sbdf_t sbdf; |
435 | 55.8k | |
436 | 55.8k | *data = ~0ul; |
437 | 55.8k | |
438 | 55.8k | read_lock(&d->arch.hvm_domain.mmcfg_lock); |
439 | 55.8k | mmcfg = vpci_mmcfg_find(d, addr); |
440 | 55.8k | if ( !mmcfg ) |
441 | 0 | { |
442 | 0 | read_unlock(&d->arch.hvm_domain.mmcfg_lock); |
443 | 0 | return X86EMUL_RETRY; |
444 | 0 | } |
445 | 55.8k | |
446 | 55.8k | reg = vpci_mmcfg_decode_addr(mmcfg, addr, &sbdf); |
447 | 55.8k | read_unlock(&d->arch.hvm_domain.mmcfg_lock); |
448 | 55.8k | |
449 | 55.8k | if ( !vpci_access_allowed(reg, len) || |
450 | 55.8k | (reg + len) > PCI_CFG_SPACE_EXP_SIZE ) |
451 | 1 | return X86EMUL_OKAY; |
452 | 55.8k | |
453 | 55.8k | /* |
454 | 55.8k | * According to the PCIe 3.1A specification: |
455 | 55.8k | * - Configuration Reads and Writes must usually be DWORD or smaller |
456 | 55.8k | * in size. |
457 | 55.8k | * - Because Root Complex implementations are not required to support |
458 | 55.8k | * accesses to a RCRB that cross DW boundaries [...] software |
459 | 55.8k | * should take care not to cause the generation of such accesses |
460 | 55.8k | * when accessing a RCRB unless the Root Complex will support the |
461 | 55.8k | * access. |
462 | 55.8k | * Xen however supports 8byte accesses by splitting them into two |
463 | 55.8k | * 4byte accesses. |
464 | 55.8k | */ |
465 | 55.8k | *data = vpci_read(sbdf, reg, min(4u, len)); |
466 | 55.8k | if ( len == 8 ) |
467 | 0 | *data |= (uint64_t)vpci_read(sbdf, reg + 4, 4) << 32; |
468 | 55.8k | |
469 | 55.8k | return X86EMUL_OKAY; |
470 | 55.8k | } |
471 | | |
472 | | static int vpci_mmcfg_write(struct vcpu *v, unsigned long addr, |
473 | | unsigned int len, unsigned long data) |
474 | 2.50k | { |
475 | 2.50k | struct domain *d = v->domain; |
476 | 2.50k | const struct hvm_mmcfg *mmcfg; |
477 | 2.50k | unsigned int reg; |
478 | 2.50k | pci_sbdf_t sbdf; |
479 | 2.50k | |
480 | 2.50k | read_lock(&d->arch.hvm_domain.mmcfg_lock); |
481 | 2.50k | mmcfg = vpci_mmcfg_find(d, addr); |
482 | 2.50k | if ( !mmcfg ) |
483 | 0 | { |
484 | 0 | read_unlock(&d->arch.hvm_domain.mmcfg_lock); |
485 | 0 | return X86EMUL_RETRY; |
486 | 0 | } |
487 | 2.50k | |
488 | 2.50k | reg = vpci_mmcfg_decode_addr(mmcfg, addr, &sbdf); |
489 | 2.50k | read_unlock(&d->arch.hvm_domain.mmcfg_lock); |
490 | 2.50k | |
491 | 2.50k | if ( !vpci_access_allowed(reg, len) || |
492 | 2.50k | (reg + len) > PCI_CFG_SPACE_EXP_SIZE ) |
493 | 0 | return X86EMUL_OKAY; |
494 | 2.50k | |
495 | 2.50k | vpci_write(sbdf, reg, min(4u, len), data); |
496 | 2.50k | if ( len == 8 ) |
497 | 0 | vpci_write(sbdf, reg + 4, 4, data >> 32); |
498 | 2.50k | |
499 | 2.50k | return X86EMUL_OKAY; |
500 | 2.50k | } |
501 | | |
502 | | static const struct hvm_mmio_ops vpci_mmcfg_ops = { |
503 | | .check = vpci_mmcfg_accept, |
504 | | .read = vpci_mmcfg_read, |
505 | | .write = vpci_mmcfg_write, |
506 | | }; |
507 | | |
508 | | int register_vpci_mmcfg_handler(struct domain *d, paddr_t addr, |
509 | | unsigned int start_bus, unsigned int end_bus, |
510 | | unsigned int seg) |
511 | 1 | { |
512 | 1 | struct hvm_mmcfg *mmcfg, *new = xmalloc(struct hvm_mmcfg); |
513 | 1 | |
514 | 1 | ASSERT(is_hardware_domain(d)); |
515 | 1 | |
516 | 1 | if ( !new ) |
517 | 0 | return -ENOMEM; |
518 | 1 | |
519 | 1 | new->addr = addr + (start_bus << 20); |
520 | 1 | new->start_bus = start_bus; |
521 | 1 | new->segment = seg; |
522 | 1 | new->size = (end_bus - start_bus + 1) << 20;; |
523 | 1 | |
524 | 1 | write_lock(&d->arch.hvm_domain.mmcfg_lock); |
525 | 1 | list_for_each_entry ( mmcfg, &d->arch.hvm_domain.mmcfg_regions, next ) |
526 | 0 | if ( new->addr < mmcfg->addr + mmcfg->size && |
527 | 0 | mmcfg->addr < new->addr + new->size ) |
528 | 0 | { |
529 | 0 | int ret = -EEXIST; |
530 | 0 |
|
531 | 0 | if ( new->addr == mmcfg->addr && |
532 | 0 | new->start_bus == mmcfg->start_bus && |
533 | 0 | new->segment == mmcfg->segment && |
534 | 0 | new->size == mmcfg->size ) |
535 | 0 | ret = 0; |
536 | 0 | write_unlock(&d->arch.hvm_domain.mmcfg_lock); |
537 | 0 | xfree(new); |
538 | 0 | return ret; |
539 | 0 | } |
540 | 1 | |
541 | 1 | if ( list_empty(&d->arch.hvm_domain.mmcfg_regions) ) |
542 | 1 | register_mmio_handler(d, &vpci_mmcfg_ops); |
543 | 1 | |
544 | 1 | list_add(&new->next, &d->arch.hvm_domain.mmcfg_regions); |
545 | 1 | write_unlock(&d->arch.hvm_domain.mmcfg_lock); |
546 | 1 | |
547 | 1 | return 0; |
548 | 1 | } |
549 | | |
550 | | void destroy_vpci_mmcfg(struct list_head *domain_mmcfg) |
551 | 0 | { |
552 | 0 | while ( !list_empty(domain_mmcfg) ) |
553 | 0 | { |
554 | 0 | struct hvm_mmcfg *mmcfg = list_first_entry(domain_mmcfg, |
555 | 0 | struct hvm_mmcfg, next); |
556 | 0 |
|
557 | 0 | list_del(&mmcfg->next); |
558 | 0 | xfree(mmcfg); |
559 | 0 | } |
560 | 0 | } |
561 | | |
562 | | /* |
563 | | * Local variables: |
564 | | * mode: C |
565 | | * c-file-style: "BSD" |
566 | | * c-basic-offset: 4 |
567 | | * tab-width: 4 |
568 | | * indent-tabs-mode: nil |
569 | | * End: |
570 | | */ |