Annotation of qemu/hw/ivshmem.c, revision 1.1.1.4

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.