Lluís Vilanova writes: > Stefan Hajnoczi writes: >> On Mon, Sep 26, 2011 at 12:23:21AM +0200, Lluís Vilanova wrote: >>> 1) Cannot start QEMU with KVM when the device is enabled: >>> kvm_set_phys_mem: error registering slot: Invalid argument >>> >>> 2) The driver never gets called on a read/write to its memory
>> If I add #include "pci.h" at the top of the file, change >> TARGET_PAGE_SIZE to 4096 (generic device code is not supposed to be >> target-dependent), and throw foo.o into Makefile.objs it builds here. >> It launches successfully with -enable-kvm and I can see the device >> inside a VM. My qemu.git/HEAD is >> 1ce9ce6a6184f0192015ba9adf1123d89cd47a7b. > Well, my intent is to implement the backdoor communication channel I rambled > about some time ago. Now, instead of overloading some unused opcodes, I use a > virtual device that is target agnostic (as someone wisely suggested). > As the device is mmap'ed into userspace to interact with it, I thought it made > sense to use TARGET_PAGE_SIZE to establish the device's mappable ranges. > Note that the device is not compiled with all the libhw code, so > TARGET_PAGE_SIZE is not poisoned. > In any case, now I've just seen that the code I sent does indeed (almost) > work... the offending code is in the part I did not send. The second bar uses > a > simple malloc'ed piece of data that the user can use as a scratchpad for the > communication with QEMU, so that the flow is: > - guest writes data into data channel > - guest writes anything into the control channel > - do in QEMU whatever is encoded in the data channel > It is the second bar that generates the problems with KVM: s-> data_ptr = g_malloc(s->size); > memory_region_init_ram_ptr(&s->data, &s->dev.qdev, "backdoor.data", s-> size, s->data_ptr); > pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->data); >> I only had a few minutes and couldn't test reading from BAR0, but you >> might want to get 32-bit reads working first before trying 64-bit. > The next problem is using 64bit reads and writes... if I set min_access_size > and > max_access_size to 4, I get the calls to 'control_io_read', but not with 8... > I've attached the real code I'm using, just to make it easier to test. Sorry, I forgot the attachment. The attached virtual device is just a proxy to the user-provided functions 'qemu_backdoor_init' and 'qemu_backdoor', which implement the semantics of the backdoor channel. The code in "#if 1" is the piece that offends KVM. Thanks, Lluis
/* * Virtual device for user-defined guest<->QEMU communication. * * Copyright (C) 2011 Lluís Vilanova <vilan...@ac.upc.edu> * * This work is licensed under the terms of the GNU GPL, version 2 or later. * See the COPYING file in the top-level directory. */ #include "hw/pci.h" #include "backdoor/qemu/qemu-backdoor.h" #define PAGE_SIZE TARGET_PAGE_SIZE #define CTRL_BYTES sizeof(uint64_t) typedef struct State { PCIDevice dev; uint8_t pages; uint64_t size; uint64_t cmd; void *data_ptr; MemoryRegion data; MemoryRegion control; } State; static uint64_t control_io_read(void *opaque, target_phys_addr_t addr, unsigned size) { State *s = opaque; uint64_t res = ldq_p(&s->size); uint8_t *resb = (uint8_t*)&res; return resb[addr % CTRL_BYTES]; } static void control_io_write(void *opaque, target_phys_addr_t addr, uint64_t data, unsigned size) { State *s = opaque; uint8_t *cmdb = (uint8_t*)&s->cmd; cmdb[addr % CTRL_BYTES] = (uint8_t)data; if ((addr + size) % CTRL_BYTES == 0) { qemu_backdoor(ldq_p(&s->cmd), s->data_ptr); } } static const MemoryRegionOps control_ops = { .read = control_io_read, .write = control_io_write, .endianness = DEVICE_NATIVE_ENDIAN, .impl = { .min_access_size = 1, .max_access_size = 1, }, }; static int init(PCIDevice *dev) { State *s = DO_UPCAST(State, dev, dev); if (s->pages < 1) { fprintf(stderr, "error: backdoor: " "the data channel must have one or more pages\n"); return -1; } s->size = s->pages * PAGE_SIZE; pci_set_word(s->dev.config + PCI_COMMAND, PCI_COMMAND_IO | PCI_COMMAND_MEMORY); memory_region_init_io(&s->control, &control_ops, s, "backdoor.control", PAGE_SIZE); pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->control); #if 1 /* KVM doesn't like it */ s->data_ptr = g_malloc(s->size); memory_region_init_ram_ptr(&s->data, &s->dev.qdev, "backdoor.data", s->size, s->data_ptr); pci_register_bar(&s->dev, 1, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->data); #endif qemu_backdoor_init(s->size); return 0; } static int fini(PCIDevice *dev) { State *s = DO_UPCAST(State, dev, dev); g_free(s->data_ptr); memory_region_destroy(&s->data); memory_region_destroy(&s->control); return 0; } static PCIDeviceInfo info = { .qdev.name = "backdoor", .qdev.desc = "Backdoor communication channel", .qdev.size = sizeof(State), .init = init, .exit = fini, .vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET, .device_id = PCI_DEVICE_ID_BACKDOOR, .class_id = PCI_CLASS_MEMORY_RAM, .qdev.props = (Property[]) { DEFINE_PROP_UINT8("pages", State, pages, 1), DEFINE_PROP_END_OF_LIST(), } }; static void register_device(void) { pci_qdev_register(&info); } device_init(register_device)
-- "And it's much the same thing with knowledge, for whenever you learn something new, the whole world becomes that much richer." -- The Princess of Pure Reason, as told by Norton Juster in The Phantom Tollbooth