|
|
1.1 ! root 1: /* ! 2: * Inter-VM Shared Memory PCI device. ! 3: * ! 4: * Author: ! 5: * Cam Macdonell <[email protected]> ! 6: * ! 7: * Based On: cirrus_vga.c ! 8: * Copyright (c) 2004 Fabrice Bellard ! 9: * Copyright (c) 2004 Makoto Suzuki (suzu) ! 10: * ! 11: * and rtl8139.c ! 12: * Copyright (c) 2006 Igor Kovalenko ! 13: * ! 14: * This code is licensed under the GNU GPL v2. ! 15: */ ! 16: #include "hw.h" ! 17: #include "pc.h" ! 18: #include "pci.h" ! 19: #include "msix.h" ! 20: #include "kvm.h" ! 21: ! 22: #include <sys/mman.h> ! 23: #include <sys/types.h> ! 24: ! 25: #define IVSHMEM_IOEVENTFD 0 ! 26: #define IVSHMEM_MSI 1 ! 27: ! 28: #define IVSHMEM_PEER 0 ! 29: #define IVSHMEM_MASTER 1 ! 30: ! 31: #define IVSHMEM_REG_BAR_SIZE 0x100 ! 32: ! 33: //#define DEBUG_IVSHMEM ! 34: #ifdef DEBUG_IVSHMEM ! 35: #define IVSHMEM_DPRINTF(fmt, ...) \ ! 36: do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0) ! 37: #else ! 38: #define IVSHMEM_DPRINTF(fmt, ...) ! 39: #endif ! 40: ! 41: typedef struct Peer { ! 42: int nb_eventfds; ! 43: int *eventfds; ! 44: } Peer; ! 45: ! 46: typedef struct EventfdEntry { ! 47: PCIDevice *pdev; ! 48: int vector; ! 49: } EventfdEntry; ! 50: ! 51: typedef struct IVShmemState { ! 52: PCIDevice dev; ! 53: uint32_t intrmask; ! 54: uint32_t intrstatus; ! 55: uint32_t doorbell; ! 56: ! 57: CharDriverState **eventfd_chr; ! 58: CharDriverState *server_chr; ! 59: int ivshmem_mmio_io_addr; ! 60: ! 61: pcibus_t mmio_addr; ! 62: pcibus_t shm_pci_addr; ! 63: uint64_t ivshmem_offset; ! 64: uint64_t ivshmem_size; /* size of shared memory region */ ! 65: int shm_fd; /* shared memory file descriptor */ ! 66: ! 67: Peer *peers; ! 68: int nb_peers; /* how many guests we have space for */ ! 69: int max_peer; /* maximum numbered peer */ ! 70: ! 71: int vm_id; ! 72: uint32_t vectors; ! 73: uint32_t features; ! 74: EventfdEntry *eventfd_table; ! 75: ! 76: char * shmobj; ! 77: char * sizearg; ! 78: char * role; ! 79: int role_val; /* scalar to avoid multiple string comparisons */ ! 80: } IVShmemState; ! 81: ! 82: /* registers for the Inter-VM shared memory device */ ! 83: enum ivshmem_registers { ! 84: INTRMASK = 0, ! 85: INTRSTATUS = 4, ! 86: IVPOSITION = 8, ! 87: DOORBELL = 12, ! 88: }; ! 89: ! 90: static inline uint32_t ivshmem_has_feature(IVShmemState *ivs, ! 91: unsigned int feature) { ! 92: return (ivs->features & (1 << feature)); ! 93: } ! 94: ! 95: static inline bool is_power_of_two(uint64_t x) { ! 96: return (x & (x - 1)) == 0; ! 97: } ! 98: ! 99: static void ivshmem_map(PCIDevice *pci_dev, int region_num, ! 100: pcibus_t addr, pcibus_t size, int type) ! 101: { ! 102: IVShmemState *s = DO_UPCAST(IVShmemState, dev, pci_dev); ! 103: ! 104: s->shm_pci_addr = addr; ! 105: ! 106: if (s->ivshmem_offset > 0) { ! 107: cpu_register_physical_memory(s->shm_pci_addr, s->ivshmem_size, ! 108: s->ivshmem_offset); ! 109: } ! 110: ! 111: IVSHMEM_DPRINTF("guest pci addr = %" FMT_PCIBUS ", guest h/w addr = %" ! 112: PRIu64 ", size = %" FMT_PCIBUS "\n", addr, s->ivshmem_offset, size); ! 113: ! 114: } ! 115: ! 116: /* accessing registers - based on rtl8139 */ ! 117: static void ivshmem_update_irq(IVShmemState *s, int val) ! 118: { ! 119: int isr; ! 120: isr = (s->intrstatus & s->intrmask) & 0xffffffff; ! 121: ! 122: /* don't print ISR resets */ ! 123: if (isr) { ! 124: IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n", ! 125: isr ? 1 : 0, s->intrstatus, s->intrmask); ! 126: } ! 127: ! 128: qemu_set_irq(s->dev.irq[0], (isr != 0)); ! 129: } ! 130: ! 131: static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val) ! 132: { ! 133: IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val); ! 134: ! 135: s->intrmask = val; ! 136: ! 137: ivshmem_update_irq(s, val); ! 138: } ! 139: ! 140: static uint32_t ivshmem_IntrMask_read(IVShmemState *s) ! 141: { ! 142: uint32_t ret = s->intrmask; ! 143: ! 144: IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret); ! 145: ! 146: return ret; ! 147: } ! 148: ! 149: static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val) ! 150: { ! 151: IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val); ! 152: ! 153: s->intrstatus = val; ! 154: ! 155: ivshmem_update_irq(s, val); ! 156: return; ! 157: } ! 158: ! 159: static uint32_t ivshmem_IntrStatus_read(IVShmemState *s) ! 160: { ! 161: uint32_t ret = s->intrstatus; ! 162: ! 163: /* reading ISR clears all interrupts */ ! 164: s->intrstatus = 0; ! 165: ! 166: ivshmem_update_irq(s, 0); ! 167: ! 168: return ret; ! 169: } ! 170: ! 171: static void ivshmem_io_writew(void *opaque, target_phys_addr_t addr, ! 172: uint32_t val) ! 173: { ! 174: ! 175: IVSHMEM_DPRINTF("We shouldn't be writing words\n"); ! 176: } ! 177: ! 178: static void ivshmem_io_writel(void *opaque, target_phys_addr_t addr, ! 179: uint32_t val) ! 180: { ! 181: IVShmemState *s = opaque; ! 182: ! 183: uint64_t write_one = 1; ! 184: uint16_t dest = val >> 16; ! 185: uint16_t vector = val & 0xff; ! 186: ! 187: addr &= 0xfc; ! 188: ! 189: IVSHMEM_DPRINTF("writing to addr " TARGET_FMT_plx "\n", addr); ! 190: switch (addr) ! 191: { ! 192: case INTRMASK: ! 193: ivshmem_IntrMask_write(s, val); ! 194: break; ! 195: ! 196: case INTRSTATUS: ! 197: ivshmem_IntrStatus_write(s, val); ! 198: break; ! 199: ! 200: case DOORBELL: ! 201: /* check that dest VM ID is reasonable */ ! 202: if (dest > s->max_peer) { ! 203: IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest); ! 204: break; ! 205: } ! 206: ! 207: /* check doorbell range */ ! 208: if (vector < s->peers[dest].nb_eventfds) { ! 209: IVSHMEM_DPRINTF("Writing %" PRId64 " to VM %d on vector %d\n", ! 210: write_one, dest, vector); ! 211: if (write(s->peers[dest].eventfds[vector], ! 212: &(write_one), 8) != 8) { ! 213: IVSHMEM_DPRINTF("error writing to eventfd\n"); ! 214: } ! 215: } ! 216: break; ! 217: default: ! 218: IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest); ! 219: } ! 220: } ! 221: ! 222: static void ivshmem_io_writeb(void *opaque, target_phys_addr_t addr, ! 223: uint32_t val) ! 224: { ! 225: IVSHMEM_DPRINTF("We shouldn't be writing bytes\n"); ! 226: } ! 227: ! 228: static uint32_t ivshmem_io_readw(void *opaque, target_phys_addr_t addr) ! 229: { ! 230: ! 231: IVSHMEM_DPRINTF("We shouldn't be reading words\n"); ! 232: return 0; ! 233: } ! 234: ! 235: static uint32_t ivshmem_io_readl(void *opaque, target_phys_addr_t addr) ! 236: { ! 237: ! 238: IVShmemState *s = opaque; ! 239: uint32_t ret; ! 240: ! 241: switch (addr) ! 242: { ! 243: case INTRMASK: ! 244: ret = ivshmem_IntrMask_read(s); ! 245: break; ! 246: ! 247: case INTRSTATUS: ! 248: ret = ivshmem_IntrStatus_read(s); ! 249: break; ! 250: ! 251: case IVPOSITION: ! 252: /* return my VM ID if the memory is mapped */ ! 253: if (s->shm_fd > 0) { ! 254: ret = s->vm_id; ! 255: } else { ! 256: ret = -1; ! 257: } ! 258: break; ! 259: ! 260: default: ! 261: IVSHMEM_DPRINTF("why are we reading " TARGET_FMT_plx "\n", addr); ! 262: ret = 0; ! 263: } ! 264: ! 265: return ret; ! 266: } ! 267: ! 268: static uint32_t ivshmem_io_readb(void *opaque, target_phys_addr_t addr) ! 269: { ! 270: IVSHMEM_DPRINTF("We shouldn't be reading bytes\n"); ! 271: ! 272: return 0; ! 273: } ! 274: ! 275: static CPUReadMemoryFunc * const ivshmem_mmio_read[3] = { ! 276: ivshmem_io_readb, ! 277: ivshmem_io_readw, ! 278: ivshmem_io_readl, ! 279: }; ! 280: ! 281: static CPUWriteMemoryFunc * const ivshmem_mmio_write[3] = { ! 282: ivshmem_io_writeb, ! 283: ivshmem_io_writew, ! 284: ivshmem_io_writel, ! 285: }; ! 286: ! 287: static void ivshmem_receive(void *opaque, const uint8_t *buf, int size) ! 288: { ! 289: IVShmemState *s = opaque; ! 290: ! 291: ivshmem_IntrStatus_write(s, *buf); ! 292: ! 293: IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf); ! 294: } ! 295: ! 296: static int ivshmem_can_receive(void * opaque) ! 297: { ! 298: return 8; ! 299: } ! 300: ! 301: static void ivshmem_event(void *opaque, int event) ! 302: { ! 303: IVSHMEM_DPRINTF("ivshmem_event %d\n", event); ! 304: } ! 305: ! 306: static void fake_irqfd(void *opaque, const uint8_t *buf, int size) { ! 307: ! 308: EventfdEntry *entry = opaque; ! 309: PCIDevice *pdev = entry->pdev; ! 310: ! 311: IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector); ! 312: msix_notify(pdev, entry->vector); ! 313: } ! 314: ! 315: static CharDriverState* create_eventfd_chr_device(void * opaque, int eventfd, ! 316: int vector) ! 317: { ! 318: /* create a event character device based on the passed eventfd */ ! 319: IVShmemState *s = opaque; ! 320: CharDriverState * chr; ! 321: ! 322: chr = qemu_chr_open_eventfd(eventfd); ! 323: ! 324: if (chr == NULL) { ! 325: fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd); ! 326: exit(-1); ! 327: } ! 328: ! 329: /* if MSI is supported we need multiple interrupts */ ! 330: if (ivshmem_has_feature(s, IVSHMEM_MSI)) { ! 331: s->eventfd_table[vector].pdev = &s->dev; ! 332: s->eventfd_table[vector].vector = vector; ! 333: ! 334: qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd, ! 335: ivshmem_event, &s->eventfd_table[vector]); ! 336: } else { ! 337: qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive, ! 338: ivshmem_event, s); ! 339: } ! 340: ! 341: return chr; ! 342: ! 343: } ! 344: ! 345: static int check_shm_size(IVShmemState *s, int fd) { ! 346: /* check that the guest isn't going to try and map more memory than the ! 347: * the object has allocated return -1 to indicate error */ ! 348: ! 349: struct stat buf; ! 350: ! 351: fstat(fd, &buf); ! 352: ! 353: if (s->ivshmem_size > buf.st_size) { ! 354: fprintf(stderr, ! 355: "IVSHMEM ERROR: Requested memory size greater" ! 356: " than shared object size (%" PRIu64 " > %" PRIu64")\n", ! 357: s->ivshmem_size, (uint64_t)buf.st_size); ! 358: return -1; ! 359: } else { ! 360: return 0; ! 361: } ! 362: } ! 363: ! 364: /* create the shared memory BAR when we are not using the server, so we can ! 365: * create the BAR and map the memory immediately */ ! 366: static void create_shared_memory_BAR(IVShmemState *s, int fd) { ! 367: ! 368: void * ptr; ! 369: ! 370: s->shm_fd = fd; ! 371: ! 372: ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); ! 373: ! 374: s->ivshmem_offset = qemu_ram_alloc_from_ptr(&s->dev.qdev, "ivshmem.bar2", ! 375: s->ivshmem_size, ptr); ! 376: ! 377: /* region for shared memory */ ! 378: pci_register_bar(&s->dev, 2, s->ivshmem_size, ! 379: PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map); ! 380: } ! 381: ! 382: static void close_guest_eventfds(IVShmemState *s, int posn) ! 383: { ! 384: int i, guest_curr_max; ! 385: ! 386: guest_curr_max = s->peers[posn].nb_eventfds; ! 387: ! 388: for (i = 0; i < guest_curr_max; i++) { ! 389: kvm_set_ioeventfd_mmio_long(s->peers[posn].eventfds[i], ! 390: s->mmio_addr + DOORBELL, (posn << 16) | i, 0); ! 391: close(s->peers[posn].eventfds[i]); ! 392: } ! 393: ! 394: qemu_free(s->peers[posn].eventfds); ! 395: s->peers[posn].nb_eventfds = 0; ! 396: } ! 397: ! 398: static void setup_ioeventfds(IVShmemState *s) { ! 399: ! 400: int i, j; ! 401: ! 402: for (i = 0; i <= s->max_peer; i++) { ! 403: for (j = 0; j < s->peers[i].nb_eventfds; j++) { ! 404: kvm_set_ioeventfd_mmio_long(s->peers[i].eventfds[j], ! 405: s->mmio_addr + DOORBELL, (i << 16) | j, 1); ! 406: } ! 407: } ! 408: } ! 409: ! 410: /* this function increase the dynamic storage need to store data about other ! 411: * guests */ ! 412: static void increase_dynamic_storage(IVShmemState *s, int new_min_size) { ! 413: ! 414: int j, old_nb_alloc; ! 415: ! 416: old_nb_alloc = s->nb_peers; ! 417: ! 418: while (new_min_size >= s->nb_peers) ! 419: s->nb_peers = s->nb_peers * 2; ! 420: ! 421: IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers); ! 422: s->peers = qemu_realloc(s->peers, s->nb_peers * sizeof(Peer)); ! 423: ! 424: /* zero out new pointers */ ! 425: for (j = old_nb_alloc; j < s->nb_peers; j++) { ! 426: s->peers[j].eventfds = NULL; ! 427: s->peers[j].nb_eventfds = 0; ! 428: } ! 429: } ! 430: ! 431: static void ivshmem_read(void *opaque, const uint8_t * buf, int flags) ! 432: { ! 433: IVShmemState *s = opaque; ! 434: int incoming_fd, tmp_fd; ! 435: int guest_max_eventfd; ! 436: long incoming_posn; ! 437: ! 438: memcpy(&incoming_posn, buf, sizeof(long)); ! 439: /* pick off s->server_chr->msgfd and store it, posn should accompany msg */ ! 440: tmp_fd = qemu_chr_get_msgfd(s->server_chr); ! 441: IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd); ! 442: ! 443: /* make sure we have enough space for this guest */ ! 444: if (incoming_posn >= s->nb_peers) { ! 445: increase_dynamic_storage(s, incoming_posn); ! 446: } ! 447: ! 448: if (tmp_fd == -1) { ! 449: /* if posn is positive and unseen before then this is our posn*/ ! 450: if ((incoming_posn >= 0) && ! 451: (s->peers[incoming_posn].eventfds == NULL)) { ! 452: /* receive our posn */ ! 453: s->vm_id = incoming_posn; ! 454: return; ! 455: } else { ! 456: /* otherwise an fd == -1 means an existing guest has gone away */ ! 457: IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn); ! 458: close_guest_eventfds(s, incoming_posn); ! 459: return; ! 460: } ! 461: } ! 462: ! 463: /* because of the implementation of get_msgfd, we need a dup */ ! 464: incoming_fd = dup(tmp_fd); ! 465: ! 466: if (incoming_fd == -1) { ! 467: fprintf(stderr, "could not allocate file descriptor %s\n", ! 468: strerror(errno)); ! 469: return; ! 470: } ! 471: ! 472: /* if the position is -1, then it's shared memory region fd */ ! 473: if (incoming_posn == -1) { ! 474: ! 475: void * map_ptr; ! 476: ! 477: s->max_peer = 0; ! 478: ! 479: if (check_shm_size(s, incoming_fd) == -1) { ! 480: exit(-1); ! 481: } ! 482: ! 483: /* mmap the region and map into the BAR2 */ ! 484: map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, ! 485: incoming_fd, 0); ! 486: s->ivshmem_offset = qemu_ram_alloc_from_ptr(&s->dev.qdev, ! 487: "ivshmem.bar2", s->ivshmem_size, map_ptr); ! 488: ! 489: IVSHMEM_DPRINTF("guest pci addr = %" FMT_PCIBUS ", guest h/w addr = %" ! 490: PRIu64 ", size = %" PRIu64 "\n", s->shm_pci_addr, ! 491: s->ivshmem_offset, s->ivshmem_size); ! 492: ! 493: if (s->shm_pci_addr > 0) { ! 494: /* map memory into BAR2 */ ! 495: cpu_register_physical_memory(s->shm_pci_addr, s->ivshmem_size, ! 496: s->ivshmem_offset); ! 497: } ! 498: ! 499: /* only store the fd if it is successfully mapped */ ! 500: s->shm_fd = incoming_fd; ! 501: ! 502: return; ! 503: } ! 504: ! 505: /* each guest has an array of eventfds, and we keep track of how many ! 506: * guests for each VM */ ! 507: guest_max_eventfd = s->peers[incoming_posn].nb_eventfds; ! 508: ! 509: if (guest_max_eventfd == 0) { ! 510: /* one eventfd per MSI vector */ ! 511: s->peers[incoming_posn].eventfds = (int *) qemu_malloc(s->vectors * ! 512: sizeof(int)); ! 513: } ! 514: ! 515: /* this is an eventfd for a particular guest VM */ ! 516: IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn, ! 517: guest_max_eventfd, incoming_fd); ! 518: s->peers[incoming_posn].eventfds[guest_max_eventfd] = incoming_fd; ! 519: ! 520: /* increment count for particular guest */ ! 521: s->peers[incoming_posn].nb_eventfds++; ! 522: ! 523: /* keep track of the maximum VM ID */ ! 524: if (incoming_posn > s->max_peer) { ! 525: s->max_peer = incoming_posn; ! 526: } ! 527: ! 528: if (incoming_posn == s->vm_id) { ! 529: s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s, ! 530: s->peers[s->vm_id].eventfds[guest_max_eventfd], ! 531: guest_max_eventfd); ! 532: } ! 533: ! 534: if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { ! 535: if (kvm_set_ioeventfd_mmio_long(incoming_fd, s->mmio_addr + DOORBELL, ! 536: (incoming_posn << 16) | guest_max_eventfd, 1) < 0) { ! 537: fprintf(stderr, "ivshmem: ioeventfd not available\n"); ! 538: } ! 539: } ! 540: ! 541: return; ! 542: } ! 543: ! 544: static void ivshmem_reset(DeviceState *d) ! 545: { ! 546: IVShmemState *s = DO_UPCAST(IVShmemState, dev.qdev, d); ! 547: ! 548: s->intrstatus = 0; ! 549: return; ! 550: } ! 551: ! 552: static void ivshmem_mmio_map(PCIDevice *pci_dev, int region_num, ! 553: pcibus_t addr, pcibus_t size, int type) ! 554: { ! 555: IVShmemState *s = DO_UPCAST(IVShmemState, dev, pci_dev); ! 556: ! 557: s->mmio_addr = addr; ! 558: cpu_register_physical_memory(addr + 0, IVSHMEM_REG_BAR_SIZE, ! 559: s->ivshmem_mmio_io_addr); ! 560: ! 561: if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) { ! 562: setup_ioeventfds(s); ! 563: } ! 564: } ! 565: ! 566: static uint64_t ivshmem_get_size(IVShmemState * s) { ! 567: ! 568: uint64_t value; ! 569: char *ptr; ! 570: ! 571: value = strtoull(s->sizearg, &ptr, 10); ! 572: switch (*ptr) { ! 573: case 0: case 'M': case 'm': ! 574: value <<= 20; ! 575: break; ! 576: case 'G': case 'g': ! 577: value <<= 30; ! 578: break; ! 579: default: ! 580: fprintf(stderr, "qemu: invalid ram size: %s\n", s->sizearg); ! 581: exit(1); ! 582: } ! 583: ! 584: /* BARs must be a power of 2 */ ! 585: if (!is_power_of_two(value)) { ! 586: fprintf(stderr, "ivshmem: size must be power of 2\n"); ! 587: exit(1); ! 588: } ! 589: ! 590: return value; ! 591: } ! 592: ! 593: static void ivshmem_setup_msi(IVShmemState * s) { ! 594: ! 595: int i; ! 596: ! 597: /* allocate the MSI-X vectors */ ! 598: ! 599: if (!msix_init(&s->dev, s->vectors, 1, 0)) { ! 600: pci_register_bar(&s->dev, 1, ! 601: msix_bar_size(&s->dev), ! 602: PCI_BASE_ADDRESS_SPACE_MEMORY, ! 603: msix_mmio_map); ! 604: IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors); ! 605: } else { ! 606: IVSHMEM_DPRINTF("msix initialization failed\n"); ! 607: exit(1); ! 608: } ! 609: ! 610: /* 'activate' the vectors */ ! 611: for (i = 0; i < s->vectors; i++) { ! 612: msix_vector_use(&s->dev, i); ! 613: } ! 614: ! 615: /* allocate Qemu char devices for receiving interrupts */ ! 616: s->eventfd_table = qemu_mallocz(s->vectors * sizeof(EventfdEntry)); ! 617: } ! 618: ! 619: static void ivshmem_save(QEMUFile* f, void *opaque) ! 620: { ! 621: IVShmemState *proxy = opaque; ! 622: ! 623: IVSHMEM_DPRINTF("ivshmem_save\n"); ! 624: pci_device_save(&proxy->dev, f); ! 625: ! 626: if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) { ! 627: msix_save(&proxy->dev, f); ! 628: } else { ! 629: qemu_put_be32(f, proxy->intrstatus); ! 630: qemu_put_be32(f, proxy->intrmask); ! 631: } ! 632: ! 633: } ! 634: ! 635: static int ivshmem_load(QEMUFile* f, void *opaque, int version_id) ! 636: { ! 637: IVSHMEM_DPRINTF("ivshmem_load\n"); ! 638: ! 639: IVShmemState *proxy = opaque; ! 640: int ret, i; ! 641: ! 642: if (version_id > 0) { ! 643: return -EINVAL; ! 644: } ! 645: ! 646: if (proxy->role_val == IVSHMEM_PEER) { ! 647: fprintf(stderr, "ivshmem: 'peer' devices are not migratable\n"); ! 648: return -EINVAL; ! 649: } ! 650: ! 651: ret = pci_device_load(&proxy->dev, f); ! 652: if (ret) { ! 653: return ret; ! 654: } ! 655: ! 656: if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) { ! 657: msix_load(&proxy->dev, f); ! 658: for (i = 0; i < proxy->vectors; i++) { ! 659: msix_vector_use(&proxy->dev, i); ! 660: } ! 661: } else { ! 662: proxy->intrstatus = qemu_get_be32(f); ! 663: proxy->intrmask = qemu_get_be32(f); ! 664: } ! 665: ! 666: return 0; ! 667: } ! 668: ! 669: static int pci_ivshmem_init(PCIDevice *dev) ! 670: { ! 671: IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev); ! 672: uint8_t *pci_conf; ! 673: ! 674: if (s->sizearg == NULL) ! 675: s->ivshmem_size = 4 << 20; /* 4 MB default */ ! 676: else { ! 677: s->ivshmem_size = ivshmem_get_size(s); ! 678: } ! 679: ! 680: register_savevm(&s->dev.qdev, "ivshmem", 0, 0, ivshmem_save, ivshmem_load, ! 681: dev); ! 682: ! 683: /* IRQFD requires MSI */ ! 684: if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) && ! 685: !ivshmem_has_feature(s, IVSHMEM_MSI)) { ! 686: fprintf(stderr, "ivshmem: ioeventfd/irqfd requires MSI\n"); ! 687: exit(1); ! 688: } ! 689: ! 690: /* check that role is reasonable */ ! 691: if (s->role) { ! 692: if (strncmp(s->role, "peer", 5) == 0) { ! 693: s->role_val = IVSHMEM_PEER; ! 694: } else if (strncmp(s->role, "master", 7) == 0) { ! 695: s->role_val = IVSHMEM_MASTER; ! 696: } else { ! 697: fprintf(stderr, "ivshmem: 'role' must be 'peer' or 'master'\n"); ! 698: exit(1); ! 699: } ! 700: } else { ! 701: s->role_val = IVSHMEM_MASTER; /* default */ ! 702: } ! 703: ! 704: if (s->role_val == IVSHMEM_PEER) { ! 705: register_device_unmigratable(&s->dev.qdev, "ivshmem", s); ! 706: } ! 707: ! 708: pci_conf = s->dev.config; ! 709: pci_config_set_vendor_id(pci_conf, PCI_VENDOR_ID_REDHAT_QUMRANET); ! 710: pci_conf[0x02] = 0x10; ! 711: pci_conf[0x03] = 0x11; ! 712: pci_conf[PCI_COMMAND] = PCI_COMMAND_IO | PCI_COMMAND_MEMORY; ! 713: pci_config_set_class(pci_conf, PCI_CLASS_MEMORY_RAM); ! 714: pci_conf[PCI_HEADER_TYPE] = PCI_HEADER_TYPE_NORMAL; ! 715: ! 716: pci_config_set_interrupt_pin(pci_conf, 1); ! 717: ! 718: s->shm_pci_addr = 0; ! 719: s->ivshmem_offset = 0; ! 720: s->shm_fd = 0; ! 721: ! 722: s->ivshmem_mmio_io_addr = cpu_register_io_memory(ivshmem_mmio_read, ! 723: ivshmem_mmio_write, s); ! 724: /* region for registers*/ ! 725: pci_register_bar(&s->dev, 0, IVSHMEM_REG_BAR_SIZE, ! 726: PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_mmio_map); ! 727: ! 728: if ((s->server_chr != NULL) && ! 729: (strncmp(s->server_chr->filename, "unix:", 5) == 0)) { ! 730: /* if we get a UNIX socket as the parameter we will talk ! 731: * to the ivshmem server to receive the memory region */ ! 732: ! 733: if (s->shmobj != NULL) { ! 734: fprintf(stderr, "WARNING: do not specify both 'chardev' " ! 735: "and 'shm' with ivshmem\n"); ! 736: } ! 737: ! 738: IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n", ! 739: s->server_chr->filename); ! 740: ! 741: if (ivshmem_has_feature(s, IVSHMEM_MSI)) { ! 742: ivshmem_setup_msi(s); ! 743: } ! 744: ! 745: /* we allocate enough space for 16 guests and grow as needed */ ! 746: s->nb_peers = 16; ! 747: s->vm_id = -1; ! 748: ! 749: /* allocate/initialize space for interrupt handling */ ! 750: s->peers = qemu_mallocz(s->nb_peers * sizeof(Peer)); ! 751: ! 752: pci_register_bar(&s->dev, 2, s->ivshmem_size, ! 753: PCI_BASE_ADDRESS_SPACE_MEMORY, ivshmem_map); ! 754: ! 755: s->eventfd_chr = qemu_mallocz(s->vectors * sizeof(CharDriverState *)); ! 756: ! 757: qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read, ! 758: ivshmem_event, s); ! 759: } else { ! 760: /* just map the file immediately, we're not using a server */ ! 761: int fd; ! 762: ! 763: if (s->shmobj == NULL) { ! 764: fprintf(stderr, "Must specify 'chardev' or 'shm' to ivshmem\n"); ! 765: } ! 766: ! 767: IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj); ! 768: ! 769: /* try opening with O_EXCL and if it succeeds zero the memory ! 770: * by truncating to 0 */ ! 771: if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL, ! 772: S_IRWXU|S_IRWXG|S_IRWXO)) > 0) { ! 773: /* truncate file to length PCI device's memory */ ! 774: if (ftruncate(fd, s->ivshmem_size) != 0) { ! 775: fprintf(stderr, "ivshmem: could not truncate shared file\n"); ! 776: } ! 777: ! 778: } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR, ! 779: S_IRWXU|S_IRWXG|S_IRWXO)) < 0) { ! 780: fprintf(stderr, "ivshmem: could not open shared file\n"); ! 781: exit(-1); ! 782: ! 783: } ! 784: ! 785: if (check_shm_size(s, fd) == -1) { ! 786: exit(-1); ! 787: } ! 788: ! 789: create_shared_memory_BAR(s, fd); ! 790: ! 791: } ! 792: ! 793: return 0; ! 794: } ! 795: ! 796: static int pci_ivshmem_uninit(PCIDevice *dev) ! 797: { ! 798: IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev); ! 799: ! 800: cpu_unregister_io_memory(s->ivshmem_mmio_io_addr); ! 801: unregister_savevm(&dev->qdev, "ivshmem", s); ! 802: ! 803: return 0; ! 804: } ! 805: ! 806: static PCIDeviceInfo ivshmem_info = { ! 807: .qdev.name = "ivshmem", ! 808: .qdev.size = sizeof(IVShmemState), ! 809: .qdev.reset = ivshmem_reset, ! 810: .init = pci_ivshmem_init, ! 811: .exit = pci_ivshmem_uninit, ! 812: .qdev.props = (Property[]) { ! 813: DEFINE_PROP_CHR("chardev", IVShmemState, server_chr), ! 814: DEFINE_PROP_STRING("size", IVShmemState, sizearg), ! 815: DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1), ! 816: DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false), ! 817: DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true), ! 818: DEFINE_PROP_STRING("shm", IVShmemState, shmobj), ! 819: DEFINE_PROP_STRING("role", IVShmemState, role), ! 820: DEFINE_PROP_END_OF_LIST(), ! 821: } ! 822: }; ! 823: ! 824: static void ivshmem_register_devices(void) ! 825: { ! 826: pci_qdev_register(&ivshmem_info); ! 827: } ! 828: ! 829: device_init(ivshmem_register_devices)
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.