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

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

unix.superglobalmegacorp.com

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