diff options
author | Carsten Otte <cotte@de.ibm.com> | 2007-10-30 13:44:25 -0400 |
---|---|---|
committer | Avi Kivity <avi@qumranet.com> | 2008-01-30 10:52:59 -0500 |
commit | de7d789acd7f373268194bb48dc0690c975ab8e6 (patch) | |
tree | c2e9c4e1bb8d127e7a53459f9ed83c711901f31d /drivers/kvm/x86.c | |
parent | bbd9b64e37aff5aa715ec5e168425790f5983bf1 (diff) |
KVM: Portability: Move pio emulation functions to x86.c
This patch moves implementation of the following functions from
kvm_main.c to x86.c:
free_pio_guest_pages, vcpu_find_pio_dev, pio_copy_data, complete_pio,
kernel_pio, pio_string_write, kvm_emulate_pio, kvm_emulate_pio_string
The function inject_gp, which was duplicated by yesterday's patch
series, is removed from kvm_main.c now because it is not needed anymore.
Signed-off-by: Carsten Otte <cotte@de.ibm.com>
Acked-by: Hollis Blanchard <hollisb@us.ibm.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>
Diffstat (limited to 'drivers/kvm/x86.c')
-rw-r--r-- | drivers/kvm/x86.c | 243 |
1 files changed, 243 insertions, 0 deletions
diff --git a/drivers/kvm/x86.c b/drivers/kvm/x86.c index fe3733d8ece..f75e7d7d9ea 100644 --- a/drivers/kvm/x86.c +++ b/drivers/kvm/x86.c | |||
@@ -1341,6 +1341,249 @@ int emulate_instruction(struct kvm_vcpu *vcpu, | |||
1341 | } | 1341 | } |
1342 | EXPORT_SYMBOL_GPL(emulate_instruction); | 1342 | EXPORT_SYMBOL_GPL(emulate_instruction); |
1343 | 1343 | ||
1344 | static void free_pio_guest_pages(struct kvm_vcpu *vcpu) | ||
1345 | { | ||
1346 | int i; | ||
1347 | |||
1348 | for (i = 0; i < ARRAY_SIZE(vcpu->pio.guest_pages); ++i) | ||
1349 | if (vcpu->pio.guest_pages[i]) { | ||
1350 | kvm_release_page(vcpu->pio.guest_pages[i]); | ||
1351 | vcpu->pio.guest_pages[i] = NULL; | ||
1352 | } | ||
1353 | } | ||
1354 | |||
1355 | static int pio_copy_data(struct kvm_vcpu *vcpu) | ||
1356 | { | ||
1357 | void *p = vcpu->pio_data; | ||
1358 | void *q; | ||
1359 | unsigned bytes; | ||
1360 | int nr_pages = vcpu->pio.guest_pages[1] ? 2 : 1; | ||
1361 | |||
1362 | q = vmap(vcpu->pio.guest_pages, nr_pages, VM_READ|VM_WRITE, | ||
1363 | PAGE_KERNEL); | ||
1364 | if (!q) { | ||
1365 | free_pio_guest_pages(vcpu); | ||
1366 | return -ENOMEM; | ||
1367 | } | ||
1368 | q += vcpu->pio.guest_page_offset; | ||
1369 | bytes = vcpu->pio.size * vcpu->pio.cur_count; | ||
1370 | if (vcpu->pio.in) | ||
1371 | memcpy(q, p, bytes); | ||
1372 | else | ||
1373 | memcpy(p, q, bytes); | ||
1374 | q -= vcpu->pio.guest_page_offset; | ||
1375 | vunmap(q); | ||
1376 | free_pio_guest_pages(vcpu); | ||
1377 | return 0; | ||
1378 | } | ||
1379 | |||
1380 | int complete_pio(struct kvm_vcpu *vcpu) | ||
1381 | { | ||
1382 | struct kvm_pio_request *io = &vcpu->pio; | ||
1383 | long delta; | ||
1384 | int r; | ||
1385 | |||
1386 | kvm_x86_ops->cache_regs(vcpu); | ||
1387 | |||
1388 | if (!io->string) { | ||
1389 | if (io->in) | ||
1390 | memcpy(&vcpu->regs[VCPU_REGS_RAX], vcpu->pio_data, | ||
1391 | io->size); | ||
1392 | } else { | ||
1393 | if (io->in) { | ||
1394 | r = pio_copy_data(vcpu); | ||
1395 | if (r) { | ||
1396 | kvm_x86_ops->cache_regs(vcpu); | ||
1397 | return r; | ||
1398 | } | ||
1399 | } | ||
1400 | |||
1401 | delta = 1; | ||
1402 | if (io->rep) { | ||
1403 | delta *= io->cur_count; | ||
1404 | /* | ||
1405 | * The size of the register should really depend on | ||
1406 | * current address size. | ||
1407 | */ | ||
1408 | vcpu->regs[VCPU_REGS_RCX] -= delta; | ||
1409 | } | ||
1410 | if (io->down) | ||
1411 | delta = -delta; | ||
1412 | delta *= io->size; | ||
1413 | if (io->in) | ||
1414 | vcpu->regs[VCPU_REGS_RDI] += delta; | ||
1415 | else | ||
1416 | vcpu->regs[VCPU_REGS_RSI] += delta; | ||
1417 | } | ||
1418 | |||
1419 | kvm_x86_ops->decache_regs(vcpu); | ||
1420 | |||
1421 | io->count -= io->cur_count; | ||
1422 | io->cur_count = 0; | ||
1423 | |||
1424 | return 0; | ||
1425 | } | ||
1426 | |||
1427 | static void kernel_pio(struct kvm_io_device *pio_dev, | ||
1428 | struct kvm_vcpu *vcpu, | ||
1429 | void *pd) | ||
1430 | { | ||
1431 | /* TODO: String I/O for in kernel device */ | ||
1432 | |||
1433 | mutex_lock(&vcpu->kvm->lock); | ||
1434 | if (vcpu->pio.in) | ||
1435 | kvm_iodevice_read(pio_dev, vcpu->pio.port, | ||
1436 | vcpu->pio.size, | ||
1437 | pd); | ||
1438 | else | ||
1439 | kvm_iodevice_write(pio_dev, vcpu->pio.port, | ||
1440 | vcpu->pio.size, | ||
1441 | pd); | ||
1442 | mutex_unlock(&vcpu->kvm->lock); | ||
1443 | } | ||
1444 | |||
1445 | static void pio_string_write(struct kvm_io_device *pio_dev, | ||
1446 | struct kvm_vcpu *vcpu) | ||
1447 | { | ||
1448 | struct kvm_pio_request *io = &vcpu->pio; | ||
1449 | void *pd = vcpu->pio_data; | ||
1450 | int i; | ||
1451 | |||
1452 | mutex_lock(&vcpu->kvm->lock); | ||
1453 | for (i = 0; i < io->cur_count; i++) { | ||
1454 | kvm_iodevice_write(pio_dev, io->port, | ||
1455 | io->size, | ||
1456 | pd); | ||
1457 | pd += io->size; | ||
1458 | } | ||
1459 | mutex_unlock(&vcpu->kvm->lock); | ||
1460 | } | ||
1461 | |||
1462 | static struct kvm_io_device *vcpu_find_pio_dev(struct kvm_vcpu *vcpu, | ||
1463 | gpa_t addr) | ||
1464 | { | ||
1465 | return kvm_io_bus_find_dev(&vcpu->kvm->pio_bus, addr); | ||
1466 | } | ||
1467 | |||
1468 | int kvm_emulate_pio(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, | ||
1469 | int size, unsigned port) | ||
1470 | { | ||
1471 | struct kvm_io_device *pio_dev; | ||
1472 | |||
1473 | vcpu->run->exit_reason = KVM_EXIT_IO; | ||
1474 | vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT; | ||
1475 | vcpu->run->io.size = vcpu->pio.size = size; | ||
1476 | vcpu->run->io.data_offset = KVM_PIO_PAGE_OFFSET * PAGE_SIZE; | ||
1477 | vcpu->run->io.count = vcpu->pio.count = vcpu->pio.cur_count = 1; | ||
1478 | vcpu->run->io.port = vcpu->pio.port = port; | ||
1479 | vcpu->pio.in = in; | ||
1480 | vcpu->pio.string = 0; | ||
1481 | vcpu->pio.down = 0; | ||
1482 | vcpu->pio.guest_page_offset = 0; | ||
1483 | vcpu->pio.rep = 0; | ||
1484 | |||
1485 | kvm_x86_ops->cache_regs(vcpu); | ||
1486 | memcpy(vcpu->pio_data, &vcpu->regs[VCPU_REGS_RAX], 4); | ||
1487 | kvm_x86_ops->decache_regs(vcpu); | ||
1488 | |||
1489 | kvm_x86_ops->skip_emulated_instruction(vcpu); | ||
1490 | |||
1491 | pio_dev = vcpu_find_pio_dev(vcpu, port); | ||
1492 | if (pio_dev) { | ||
1493 | kernel_pio(pio_dev, vcpu, vcpu->pio_data); | ||
1494 | complete_pio(vcpu); | ||
1495 | return 1; | ||
1496 | } | ||
1497 | return 0; | ||
1498 | } | ||
1499 | EXPORT_SYMBOL_GPL(kvm_emulate_pio); | ||
1500 | |||
1501 | int kvm_emulate_pio_string(struct kvm_vcpu *vcpu, struct kvm_run *run, int in, | ||
1502 | int size, unsigned long count, int down, | ||
1503 | gva_t address, int rep, unsigned port) | ||
1504 | { | ||
1505 | unsigned now, in_page; | ||
1506 | int i, ret = 0; | ||
1507 | int nr_pages = 1; | ||
1508 | struct page *page; | ||
1509 | struct kvm_io_device *pio_dev; | ||
1510 | |||
1511 | vcpu->run->exit_reason = KVM_EXIT_IO; | ||
1512 | vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT; | ||
1513 | vcpu->run->io.size = vcpu->pio.size = size; | ||
1514 | vcpu->run->io.data_offset = KVM_PIO_PAGE_OFFSET * PAGE_SIZE; | ||
1515 | vcpu->run->io.count = vcpu->pio.count = vcpu->pio.cur_count = count; | ||
1516 | vcpu->run->io.port = vcpu->pio.port = port; | ||
1517 | vcpu->pio.in = in; | ||
1518 | vcpu->pio.string = 1; | ||
1519 | vcpu->pio.down = down; | ||
1520 | vcpu->pio.guest_page_offset = offset_in_page(address); | ||
1521 | vcpu->pio.rep = rep; | ||
1522 | |||
1523 | if (!count) { | ||
1524 | kvm_x86_ops->skip_emulated_instruction(vcpu); | ||
1525 | return 1; | ||
1526 | } | ||
1527 | |||
1528 | if (!down) | ||
1529 | in_page = PAGE_SIZE - offset_in_page(address); | ||
1530 | else | ||
1531 | in_page = offset_in_page(address) + size; | ||
1532 | now = min(count, (unsigned long)in_page / size); | ||
1533 | if (!now) { | ||
1534 | /* | ||
1535 | * String I/O straddles page boundary. Pin two guest pages | ||
1536 | * so that we satisfy atomicity constraints. Do just one | ||
1537 | * transaction to avoid complexity. | ||
1538 | */ | ||
1539 | nr_pages = 2; | ||
1540 | now = 1; | ||
1541 | } | ||
1542 | if (down) { | ||
1543 | /* | ||
1544 | * String I/O in reverse. Yuck. Kill the guest, fix later. | ||
1545 | */ | ||
1546 | pr_unimpl(vcpu, "guest string pio down\n"); | ||
1547 | inject_gp(vcpu); | ||
1548 | return 1; | ||
1549 | } | ||
1550 | vcpu->run->io.count = now; | ||
1551 | vcpu->pio.cur_count = now; | ||
1552 | |||
1553 | if (vcpu->pio.cur_count == vcpu->pio.count) | ||
1554 | kvm_x86_ops->skip_emulated_instruction(vcpu); | ||
1555 | |||
1556 | for (i = 0; i < nr_pages; ++i) { | ||
1557 | mutex_lock(&vcpu->kvm->lock); | ||
1558 | page = gva_to_page(vcpu, address + i * PAGE_SIZE); | ||
1559 | vcpu->pio.guest_pages[i] = page; | ||
1560 | mutex_unlock(&vcpu->kvm->lock); | ||
1561 | if (!page) { | ||
1562 | inject_gp(vcpu); | ||
1563 | free_pio_guest_pages(vcpu); | ||
1564 | return 1; | ||
1565 | } | ||
1566 | } | ||
1567 | |||
1568 | pio_dev = vcpu_find_pio_dev(vcpu, port); | ||
1569 | if (!vcpu->pio.in) { | ||
1570 | /* string PIO write */ | ||
1571 | ret = pio_copy_data(vcpu); | ||
1572 | if (ret >= 0 && pio_dev) { | ||
1573 | pio_string_write(pio_dev, vcpu); | ||
1574 | complete_pio(vcpu); | ||
1575 | if (vcpu->pio.count == 0) | ||
1576 | ret = 1; | ||
1577 | } | ||
1578 | } else if (pio_dev) | ||
1579 | pr_unimpl(vcpu, "no string pio read support yet, " | ||
1580 | "port %x size %d count %ld\n", | ||
1581 | port, size, count); | ||
1582 | |||
1583 | return ret; | ||
1584 | } | ||
1585 | EXPORT_SYMBOL_GPL(kvm_emulate_pio_string); | ||
1586 | |||
1344 | __init void kvm_arch_init(void) | 1587 | __init void kvm_arch_init(void) |
1345 | { | 1588 | { |
1346 | kvm_init_msr_list(); | 1589 | kvm_init_msr_list(); |