Statistics
| Branch: | Revision:

root / hw / ivshmem.c @ 7e7de876

History | View | Annotate | Download (22.3 kB)

1
/*
2
 * Inter-VM Shared Memory PCI device.
3
 *
4
 * Author:
5
 *      Cam Macdonell <cam@cs.ualberta.ca>
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
 * Contributions after 2012-01-13 are licensed under the terms of the
17
 * GNU GPL, version 2 or (at your option) any later version.
18
 */
19
#include "hw.h"
20
#include "pc.h"
21
#include "pci.h"
22
#include "msix.h"
23
#include "kvm.h"
24
#include "migration.h"
25
#include "qerror.h"
26
#include "event_notifier.h"
27

    
28
#include <sys/mman.h>
29
#include <sys/types.h>
30

    
31
#define IVSHMEM_IOEVENTFD   0
32
#define IVSHMEM_MSI     1
33

    
34
#define IVSHMEM_PEER    0
35
#define IVSHMEM_MASTER  1
36

    
37
#define IVSHMEM_REG_BAR_SIZE 0x100
38

    
39
//#define DEBUG_IVSHMEM
40
#ifdef DEBUG_IVSHMEM
41
#define IVSHMEM_DPRINTF(fmt, ...)        \
42
    do {printf("IVSHMEM: " fmt, ## __VA_ARGS__); } while (0)
43
#else
44
#define IVSHMEM_DPRINTF(fmt, ...)
45
#endif
46

    
47
typedef struct Peer {
48
    int nb_eventfds;
49
    EventNotifier *eventfds;
50
} Peer;
51

    
52
typedef struct EventfdEntry {
53
    PCIDevice *pdev;
54
    int vector;
55
} EventfdEntry;
56

    
57
typedef struct IVShmemState {
58
    PCIDevice dev;
59
    uint32_t intrmask;
60
    uint32_t intrstatus;
61
    uint32_t doorbell;
62

    
63
    CharDriverState **eventfd_chr;
64
    CharDriverState *server_chr;
65
    MemoryRegion ivshmem_mmio;
66

    
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
    uint64_t ivshmem_size; /* size of shared memory region */
74
    int shm_fd; /* shared memory file descriptor */
75

    
76
    Peer *peers;
77
    int nb_peers; /* how many guests we have space for */
78
    int max_peer; /* maximum numbered peer */
79

    
80
    int vm_id;
81
    uint32_t vectors;
82
    uint32_t features;
83
    EventfdEntry *eventfd_table;
84

    
85
    Error *migration_blocker;
86

    
87
    char * shmobj;
88
    char * sizearg;
89
    char * role;
90
    int role_val;   /* scalar to avoid multiple string comparisons */
91
} IVShmemState;
92

    
93
/* registers for the Inter-VM shared memory device */
94
enum ivshmem_registers {
95
    INTRMASK = 0,
96
    INTRSTATUS = 4,
97
    IVPOSITION = 8,
98
    DOORBELL = 12,
99
};
100

    
101
static inline uint32_t ivshmem_has_feature(IVShmemState *ivs,
102
                                                    unsigned int feature) {
103
    return (ivs->features & (1 << feature));
104
}
105

    
106
static inline bool is_power_of_two(uint64_t x) {
107
    return (x & (x - 1)) == 0;
108
}
109

    
110
/* accessing registers - based on rtl8139 */
111
static void ivshmem_update_irq(IVShmemState *s, int val)
112
{
113
    int isr;
114
    isr = (s->intrstatus & s->intrmask) & 0xffffffff;
115

    
116
    /* don't print ISR resets */
117
    if (isr) {
118
        IVSHMEM_DPRINTF("Set IRQ to %d (%04x %04x)\n",
119
           isr ? 1 : 0, s->intrstatus, s->intrmask);
120
    }
121

    
122
    qemu_set_irq(s->dev.irq[0], (isr != 0));
123
}
124

    
125
static void ivshmem_IntrMask_write(IVShmemState *s, uint32_t val)
126
{
127
    IVSHMEM_DPRINTF("IntrMask write(w) val = 0x%04x\n", val);
128

    
129
    s->intrmask = val;
130

    
131
    ivshmem_update_irq(s, val);
132
}
133

    
134
static uint32_t ivshmem_IntrMask_read(IVShmemState *s)
135
{
136
    uint32_t ret = s->intrmask;
137

    
138
    IVSHMEM_DPRINTF("intrmask read(w) val = 0x%04x\n", ret);
139

    
140
    return ret;
141
}
142

    
143
static void ivshmem_IntrStatus_write(IVShmemState *s, uint32_t val)
144
{
145
    IVSHMEM_DPRINTF("IntrStatus write(w) val = 0x%04x\n", val);
146

    
147
    s->intrstatus = val;
148

    
149
    ivshmem_update_irq(s, val);
150
    return;
151
}
152

    
153
static uint32_t ivshmem_IntrStatus_read(IVShmemState *s)
154
{
155
    uint32_t ret = s->intrstatus;
156

    
157
    /* reading ISR clears all interrupts */
158
    s->intrstatus = 0;
159

    
160
    ivshmem_update_irq(s, 0);
161

    
162
    return ret;
163
}
164

    
165
static void ivshmem_io_write(void *opaque, target_phys_addr_t addr,
166
                             uint64_t val, unsigned size)
167
{
168
    IVShmemState *s = opaque;
169

    
170
    uint16_t dest = val >> 16;
171
    uint16_t vector = val & 0xff;
172

    
173
    addr &= 0xfc;
174

    
175
    IVSHMEM_DPRINTF("writing to addr " TARGET_FMT_plx "\n", addr);
176
    switch (addr)
177
    {
178
        case INTRMASK:
179
            ivshmem_IntrMask_write(s, val);
180
            break;
181

    
182
        case INTRSTATUS:
183
            ivshmem_IntrStatus_write(s, val);
184
            break;
185

    
186
        case DOORBELL:
187
            /* check that dest VM ID is reasonable */
188
            if (dest > s->max_peer) {
189
                IVSHMEM_DPRINTF("Invalid destination VM ID (%d)\n", dest);
190
                break;
191
            }
192

    
193
            /* check doorbell range */
194
            if (vector < s->peers[dest].nb_eventfds) {
195
                IVSHMEM_DPRINTF("Notifying VM %d on vector %d\n", dest, vector);
196
                event_notifier_set(&s->peers[dest].eventfds[vector]);
197
            }
198
            break;
199
        default:
200
            IVSHMEM_DPRINTF("Invalid VM Doorbell VM %d\n", dest);
201
    }
202
}
203

    
204
static uint64_t ivshmem_io_read(void *opaque, target_phys_addr_t addr,
205
                                unsigned size)
206
{
207

    
208
    IVShmemState *s = opaque;
209
    uint32_t ret;
210

    
211
    switch (addr)
212
    {
213
        case INTRMASK:
214
            ret = ivshmem_IntrMask_read(s);
215
            break;
216

    
217
        case INTRSTATUS:
218
            ret = ivshmem_IntrStatus_read(s);
219
            break;
220

    
221
        case IVPOSITION:
222
            /* return my VM ID if the memory is mapped */
223
            if (s->shm_fd > 0) {
224
                ret = s->vm_id;
225
            } else {
226
                ret = -1;
227
            }
228
            break;
229

    
230
        default:
231
            IVSHMEM_DPRINTF("why are we reading " TARGET_FMT_plx "\n", addr);
232
            ret = 0;
233
    }
234

    
235
    return ret;
236
}
237

    
238
static const MemoryRegionOps ivshmem_mmio_ops = {
239
    .read = ivshmem_io_read,
240
    .write = ivshmem_io_write,
241
    .endianness = DEVICE_NATIVE_ENDIAN,
242
    .impl = {
243
        .min_access_size = 4,
244
        .max_access_size = 4,
245
    },
246
};
247

    
248
static void ivshmem_receive(void *opaque, const uint8_t *buf, int size)
249
{
250
    IVShmemState *s = opaque;
251

    
252
    ivshmem_IntrStatus_write(s, *buf);
253

    
254
    IVSHMEM_DPRINTF("ivshmem_receive 0x%02x\n", *buf);
255
}
256

    
257
static int ivshmem_can_receive(void * opaque)
258
{
259
    return 8;
260
}
261

    
262
static void ivshmem_event(void *opaque, int event)
263
{
264
    IVSHMEM_DPRINTF("ivshmem_event %d\n", event);
265
}
266

    
267
static void fake_irqfd(void *opaque, const uint8_t *buf, int size) {
268

    
269
    EventfdEntry *entry = opaque;
270
    PCIDevice *pdev = entry->pdev;
271

    
272
    IVSHMEM_DPRINTF("interrupt on vector %p %d\n", pdev, entry->vector);
273
    msix_notify(pdev, entry->vector);
274
}
275

    
276
static CharDriverState* create_eventfd_chr_device(void * opaque, EventNotifier *n,
277
                                                  int vector)
278
{
279
    /* create a event character device based on the passed eventfd */
280
    IVShmemState *s = opaque;
281
    CharDriverState * chr;
282
    int eventfd = event_notifier_get_fd(n);
283

    
284
    chr = qemu_chr_open_eventfd(eventfd);
285

    
286
    if (chr == NULL) {
287
        fprintf(stderr, "creating eventfd for eventfd %d failed\n", eventfd);
288
        exit(-1);
289
    }
290

    
291
    /* if MSI is supported we need multiple interrupts */
292
    if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
293
        s->eventfd_table[vector].pdev = &s->dev;
294
        s->eventfd_table[vector].vector = vector;
295

    
296
        qemu_chr_add_handlers(chr, ivshmem_can_receive, fake_irqfd,
297
                      ivshmem_event, &s->eventfd_table[vector]);
298
    } else {
299
        qemu_chr_add_handlers(chr, ivshmem_can_receive, ivshmem_receive,
300
                      ivshmem_event, s);
301
    }
302

    
303
    return chr;
304

    
305
}
306

    
307
static int check_shm_size(IVShmemState *s, int fd) {
308
    /* check that the guest isn't going to try and map more memory than the
309
     * the object has allocated return -1 to indicate error */
310

    
311
    struct stat buf;
312

    
313
    fstat(fd, &buf);
314

    
315
    if (s->ivshmem_size > buf.st_size) {
316
        fprintf(stderr,
317
                "IVSHMEM ERROR: Requested memory size greater"
318
                " than shared object size (%" PRIu64 " > %" PRIu64")\n",
319
                s->ivshmem_size, (uint64_t)buf.st_size);
320
        return -1;
321
    } else {
322
        return 0;
323
    }
324
}
325

    
326
/* create the shared memory BAR when we are not using the server, so we can
327
 * create the BAR and map the memory immediately */
328
static void create_shared_memory_BAR(IVShmemState *s, int fd) {
329

    
330
    void * ptr;
331

    
332
    s->shm_fd = fd;
333

    
334
    ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
335

    
336
    memory_region_init_ram_ptr(&s->ivshmem, "ivshmem.bar2",
337
                               s->ivshmem_size, ptr);
338
    vmstate_register_ram(&s->ivshmem, &s->dev.qdev);
339
    memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
340

    
341
    /* region for shared memory */
342
    pci_register_bar(&s->dev, 2, PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
343
}
344

    
345
static void ivshmem_add_eventfd(IVShmemState *s, int posn, int i)
346
{
347
    memory_region_add_eventfd(&s->ivshmem_mmio,
348
                              DOORBELL,
349
                              4,
350
                              true,
351
                              (posn << 16) | i,
352
                              &s->peers[posn].eventfds[i]);
353
}
354

    
355
static void ivshmem_del_eventfd(IVShmemState *s, int posn, int i)
356
{
357
    memory_region_del_eventfd(&s->ivshmem_mmio,
358
                              DOORBELL,
359
                              4,
360
                              true,
361
                              (posn << 16) | i,
362
                              &s->peers[posn].eventfds[i]);
363
}
364

    
365
static void close_guest_eventfds(IVShmemState *s, int posn)
366
{
367
    int i, guest_curr_max;
368

    
369
    if (!ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
370
        return;
371
    }
372

    
373
    guest_curr_max = s->peers[posn].nb_eventfds;
374

    
375
    memory_region_transaction_begin();
376
    for (i = 0; i < guest_curr_max; i++) {
377
        ivshmem_del_eventfd(s, posn, i);
378
    }
379
    memory_region_transaction_commit();
380
    for (i = 0; i < guest_curr_max; i++) {
381
        event_notifier_cleanup(&s->peers[posn].eventfds[i]);
382
    }
383

    
384
    g_free(s->peers[posn].eventfds);
385
    s->peers[posn].nb_eventfds = 0;
386
}
387

    
388
/* this function increase the dynamic storage need to store data about other
389
 * guests */
390
static void increase_dynamic_storage(IVShmemState *s, int new_min_size) {
391

    
392
    int j, old_nb_alloc;
393

    
394
    old_nb_alloc = s->nb_peers;
395

    
396
    while (new_min_size >= s->nb_peers)
397
        s->nb_peers = s->nb_peers * 2;
398

    
399
    IVSHMEM_DPRINTF("bumping storage to %d guests\n", s->nb_peers);
400
    s->peers = g_realloc(s->peers, s->nb_peers * sizeof(Peer));
401

    
402
    /* zero out new pointers */
403
    for (j = old_nb_alloc; j < s->nb_peers; j++) {
404
        s->peers[j].eventfds = NULL;
405
        s->peers[j].nb_eventfds = 0;
406
    }
407
}
408

    
409
static void ivshmem_read(void *opaque, const uint8_t * buf, int flags)
410
{
411
    IVShmemState *s = opaque;
412
    int incoming_fd, tmp_fd;
413
    int guest_max_eventfd;
414
    long incoming_posn;
415

    
416
    memcpy(&incoming_posn, buf, sizeof(long));
417
    /* pick off s->server_chr->msgfd and store it, posn should accompany msg */
418
    tmp_fd = qemu_chr_fe_get_msgfd(s->server_chr);
419
    IVSHMEM_DPRINTF("posn is %ld, fd is %d\n", incoming_posn, tmp_fd);
420

    
421
    /* make sure we have enough space for this guest */
422
    if (incoming_posn >= s->nb_peers) {
423
        increase_dynamic_storage(s, incoming_posn);
424
    }
425

    
426
    if (tmp_fd == -1) {
427
        /* if posn is positive and unseen before then this is our posn*/
428
        if ((incoming_posn >= 0) &&
429
                            (s->peers[incoming_posn].eventfds == NULL)) {
430
            /* receive our posn */
431
            s->vm_id = incoming_posn;
432
            return;
433
        } else {
434
            /* otherwise an fd == -1 means an existing guest has gone away */
435
            IVSHMEM_DPRINTF("posn %ld has gone away\n", incoming_posn);
436
            close_guest_eventfds(s, incoming_posn);
437
            return;
438
        }
439
    }
440

    
441
    /* because of the implementation of get_msgfd, we need a dup */
442
    incoming_fd = dup(tmp_fd);
443

    
444
    if (incoming_fd == -1) {
445
        fprintf(stderr, "could not allocate file descriptor %s\n",
446
                                                            strerror(errno));
447
        return;
448
    }
449

    
450
    /* if the position is -1, then it's shared memory region fd */
451
    if (incoming_posn == -1) {
452

    
453
        void * map_ptr;
454

    
455
        s->max_peer = 0;
456

    
457
        if (check_shm_size(s, incoming_fd) == -1) {
458
            exit(-1);
459
        }
460

    
461
        /* mmap the region and map into the BAR2 */
462
        map_ptr = mmap(0, s->ivshmem_size, PROT_READ|PROT_WRITE, MAP_SHARED,
463
                                                            incoming_fd, 0);
464
        memory_region_init_ram_ptr(&s->ivshmem,
465
                                   "ivshmem.bar2", s->ivshmem_size, map_ptr);
466
        vmstate_register_ram(&s->ivshmem, &s->dev.qdev);
467

    
468
        IVSHMEM_DPRINTF("guest h/w addr = %" PRIu64 ", size = %" PRIu64 "\n",
469
                         s->ivshmem_offset, s->ivshmem_size);
470

    
471
        memory_region_add_subregion(&s->bar, 0, &s->ivshmem);
472

    
473
        /* only store the fd if it is successfully mapped */
474
        s->shm_fd = incoming_fd;
475

    
476
        return;
477
    }
478

    
479
    /* each guest has an array of eventfds, and we keep track of how many
480
     * guests for each VM */
481
    guest_max_eventfd = s->peers[incoming_posn].nb_eventfds;
482

    
483
    if (guest_max_eventfd == 0) {
484
        /* one eventfd per MSI vector */
485
        s->peers[incoming_posn].eventfds = g_new(EventNotifier, s->vectors);
486
    }
487

    
488
    /* this is an eventfd for a particular guest VM */
489
    IVSHMEM_DPRINTF("eventfds[%ld][%d] = %d\n", incoming_posn,
490
                                            guest_max_eventfd, incoming_fd);
491
    event_notifier_init_fd(&s->peers[incoming_posn].eventfds[guest_max_eventfd],
492
                           incoming_fd);
493

    
494
    /* increment count for particular guest */
495
    s->peers[incoming_posn].nb_eventfds++;
496

    
497
    /* keep track of the maximum VM ID */
498
    if (incoming_posn > s->max_peer) {
499
        s->max_peer = incoming_posn;
500
    }
501

    
502
    if (incoming_posn == s->vm_id) {
503
        s->eventfd_chr[guest_max_eventfd] = create_eventfd_chr_device(s,
504
                   &s->peers[s->vm_id].eventfds[guest_max_eventfd],
505
                   guest_max_eventfd);
506
    }
507

    
508
    if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD)) {
509
        ivshmem_add_eventfd(s, incoming_posn, guest_max_eventfd);
510
    }
511

    
512
    return;
513
}
514

    
515
/* Select the MSI-X vectors used by device.
516
 * ivshmem maps events to vectors statically, so
517
 * we just enable all vectors on init and after reset. */
518
static void ivshmem_use_msix(IVShmemState * s)
519
{
520
    int i;
521

    
522
    if (!msix_present(&s->dev)) {
523
        return;
524
    }
525

    
526
    for (i = 0; i < s->vectors; i++) {
527
        msix_vector_use(&s->dev, i);
528
    }
529
}
530

    
531
static void ivshmem_reset(DeviceState *d)
532
{
533
    IVShmemState *s = DO_UPCAST(IVShmemState, dev.qdev, d);
534

    
535
    s->intrstatus = 0;
536
    ivshmem_use_msix(s);
537
    return;
538
}
539

    
540
static uint64_t ivshmem_get_size(IVShmemState * s) {
541

    
542
    uint64_t value;
543
    char *ptr;
544

    
545
    value = strtoull(s->sizearg, &ptr, 10);
546
    switch (*ptr) {
547
        case 0: case 'M': case 'm':
548
            value <<= 20;
549
            break;
550
        case 'G': case 'g':
551
            value <<= 30;
552
            break;
553
        default:
554
            fprintf(stderr, "qemu: invalid ram size: %s\n", s->sizearg);
555
            exit(1);
556
    }
557

    
558
    /* BARs must be a power of 2 */
559
    if (!is_power_of_two(value)) {
560
        fprintf(stderr, "ivshmem: size must be power of 2\n");
561
        exit(1);
562
    }
563

    
564
    return value;
565
}
566

    
567
static void ivshmem_setup_msi(IVShmemState * s)
568
{
569
    if (msix_init_exclusive_bar(&s->dev, s->vectors, 1)) {
570
        IVSHMEM_DPRINTF("msix initialization failed\n");
571
        exit(1);
572
    }
573

    
574
    IVSHMEM_DPRINTF("msix initialized (%d vectors)\n", s->vectors);
575

    
576
    /* allocate QEMU char devices for receiving interrupts */
577
    s->eventfd_table = g_malloc0(s->vectors * sizeof(EventfdEntry));
578

    
579
    ivshmem_use_msix(s);
580
}
581

    
582
static void ivshmem_save(QEMUFile* f, void *opaque)
583
{
584
    IVShmemState *proxy = opaque;
585

    
586
    IVSHMEM_DPRINTF("ivshmem_save\n");
587
    pci_device_save(&proxy->dev, f);
588

    
589
    if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
590
        msix_save(&proxy->dev, f);
591
    } else {
592
        qemu_put_be32(f, proxy->intrstatus);
593
        qemu_put_be32(f, proxy->intrmask);
594
    }
595

    
596
}
597

    
598
static int ivshmem_load(QEMUFile* f, void *opaque, int version_id)
599
{
600
    IVSHMEM_DPRINTF("ivshmem_load\n");
601

    
602
    IVShmemState *proxy = opaque;
603
    int ret;
604

    
605
    if (version_id > 0) {
606
        return -EINVAL;
607
    }
608

    
609
    if (proxy->role_val == IVSHMEM_PEER) {
610
        fprintf(stderr, "ivshmem: 'peer' devices are not migratable\n");
611
        return -EINVAL;
612
    }
613

    
614
    ret = pci_device_load(&proxy->dev, f);
615
    if (ret) {
616
        return ret;
617
    }
618

    
619
    if (ivshmem_has_feature(proxy, IVSHMEM_MSI)) {
620
        msix_load(&proxy->dev, f);
621
        ivshmem_use_msix(proxy);
622
    } else {
623
        proxy->intrstatus = qemu_get_be32(f);
624
        proxy->intrmask = qemu_get_be32(f);
625
    }
626

    
627
    return 0;
628
}
629

    
630
static void ivshmem_write_config(PCIDevice *pci_dev, uint32_t address,
631
                                 uint32_t val, int len)
632
{
633
    pci_default_write_config(pci_dev, address, val, len);
634
    msix_write_config(pci_dev, address, val, len);
635
}
636

    
637
static int pci_ivshmem_init(PCIDevice *dev)
638
{
639
    IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev);
640
    uint8_t *pci_conf;
641

    
642
    if (s->sizearg == NULL)
643
        s->ivshmem_size = 4 << 20; /* 4 MB default */
644
    else {
645
        s->ivshmem_size = ivshmem_get_size(s);
646
    }
647

    
648
    register_savevm(&s->dev.qdev, "ivshmem", 0, 0, ivshmem_save, ivshmem_load,
649
                                                                        dev);
650

    
651
    /* IRQFD requires MSI */
652
    if (ivshmem_has_feature(s, IVSHMEM_IOEVENTFD) &&
653
        !ivshmem_has_feature(s, IVSHMEM_MSI)) {
654
        fprintf(stderr, "ivshmem: ioeventfd/irqfd requires MSI\n");
655
        exit(1);
656
    }
657

    
658
    /* check that role is reasonable */
659
    if (s->role) {
660
        if (strncmp(s->role, "peer", 5) == 0) {
661
            s->role_val = IVSHMEM_PEER;
662
        } else if (strncmp(s->role, "master", 7) == 0) {
663
            s->role_val = IVSHMEM_MASTER;
664
        } else {
665
            fprintf(stderr, "ivshmem: 'role' must be 'peer' or 'master'\n");
666
            exit(1);
667
        }
668
    } else {
669
        s->role_val = IVSHMEM_MASTER; /* default */
670
    }
671

    
672
    if (s->role_val == IVSHMEM_PEER) {
673
        error_set(&s->migration_blocker, QERR_DEVICE_FEATURE_BLOCKS_MIGRATION,
674
                  "peer mode", "ivshmem");
675
        migrate_add_blocker(s->migration_blocker);
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

    
685
    memory_region_init_io(&s->ivshmem_mmio, &ivshmem_mmio_ops, s,
686
                          "ivshmem-mmio", IVSHMEM_REG_BAR_SIZE);
687

    
688
    /* region for registers*/
689
    pci_register_bar(&s->dev, 0, PCI_BASE_ADDRESS_SPACE_MEMORY,
690
                     &s->ivshmem_mmio);
691

    
692
    memory_region_init(&s->bar, "ivshmem-bar2-container", s->ivshmem_size);
693

    
694
    if ((s->server_chr != NULL) &&
695
                        (strncmp(s->server_chr->filename, "unix:", 5) == 0)) {
696
        /* if we get a UNIX socket as the parameter we will talk
697
         * to the ivshmem server to receive the memory region */
698

    
699
        if (s->shmobj != NULL) {
700
            fprintf(stderr, "WARNING: do not specify both 'chardev' "
701
                                                "and 'shm' with ivshmem\n");
702
        }
703

    
704
        IVSHMEM_DPRINTF("using shared memory server (socket = %s)\n",
705
                                                    s->server_chr->filename);
706

    
707
        if (ivshmem_has_feature(s, IVSHMEM_MSI)) {
708
            ivshmem_setup_msi(s);
709
        }
710

    
711
        /* we allocate enough space for 16 guests and grow as needed */
712
        s->nb_peers = 16;
713
        s->vm_id = -1;
714

    
715
        /* allocate/initialize space for interrupt handling */
716
        s->peers = g_malloc0(s->nb_peers * sizeof(Peer));
717

    
718
        pci_register_bar(&s->dev, 2,
719
                         PCI_BASE_ADDRESS_SPACE_MEMORY, &s->bar);
720

    
721
        s->eventfd_chr = g_malloc0(s->vectors * sizeof(CharDriverState *));
722

    
723
        qemu_chr_add_handlers(s->server_chr, ivshmem_can_receive, ivshmem_read,
724
                     ivshmem_event, s);
725
    } else {
726
        /* just map the file immediately, we're not using a server */
727
        int fd;
728

    
729
        if (s->shmobj == NULL) {
730
            fprintf(stderr, "Must specify 'chardev' or 'shm' to ivshmem\n");
731
        }
732

    
733
        IVSHMEM_DPRINTF("using shm_open (shm object = %s)\n", s->shmobj);
734

    
735
        /* try opening with O_EXCL and if it succeeds zero the memory
736
         * by truncating to 0 */
737
        if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR|O_EXCL,
738
                        S_IRWXU|S_IRWXG|S_IRWXO)) > 0) {
739
           /* truncate file to length PCI device's memory */
740
            if (ftruncate(fd, s->ivshmem_size) != 0) {
741
                fprintf(stderr, "ivshmem: could not truncate shared file\n");
742
            }
743

    
744
        } else if ((fd = shm_open(s->shmobj, O_CREAT|O_RDWR,
745
                        S_IRWXU|S_IRWXG|S_IRWXO)) < 0) {
746
            fprintf(stderr, "ivshmem: could not open shared file\n");
747
            exit(-1);
748

    
749
        }
750

    
751
        if (check_shm_size(s, fd) == -1) {
752
            exit(-1);
753
        }
754

    
755
        create_shared_memory_BAR(s, fd);
756

    
757
    }
758

    
759
    s->dev.config_write = ivshmem_write_config;
760

    
761
    return 0;
762
}
763

    
764
static void pci_ivshmem_uninit(PCIDevice *dev)
765
{
766
    IVShmemState *s = DO_UPCAST(IVShmemState, dev, dev);
767

    
768
    if (s->migration_blocker) {
769
        migrate_del_blocker(s->migration_blocker);
770
        error_free(s->migration_blocker);
771
    }
772

    
773
    memory_region_destroy(&s->ivshmem_mmio);
774
    memory_region_del_subregion(&s->bar, &s->ivshmem);
775
    vmstate_unregister_ram(&s->ivshmem, &s->dev.qdev);
776
    memory_region_destroy(&s->ivshmem);
777
    memory_region_destroy(&s->bar);
778
    unregister_savevm(&dev->qdev, "ivshmem", s);
779
}
780

    
781
static Property ivshmem_properties[] = {
782
    DEFINE_PROP_CHR("chardev", IVShmemState, server_chr),
783
    DEFINE_PROP_STRING("size", IVShmemState, sizearg),
784
    DEFINE_PROP_UINT32("vectors", IVShmemState, vectors, 1),
785
    DEFINE_PROP_BIT("ioeventfd", IVShmemState, features, IVSHMEM_IOEVENTFD, false),
786
    DEFINE_PROP_BIT("msi", IVShmemState, features, IVSHMEM_MSI, true),
787
    DEFINE_PROP_STRING("shm", IVShmemState, shmobj),
788
    DEFINE_PROP_STRING("role", IVShmemState, role),
789
    DEFINE_PROP_END_OF_LIST(),
790
};
791

    
792
static void ivshmem_class_init(ObjectClass *klass, void *data)
793
{
794
    DeviceClass *dc = DEVICE_CLASS(klass);
795
    PCIDeviceClass *k = PCI_DEVICE_CLASS(klass);
796

    
797
    k->init = pci_ivshmem_init;
798
    k->exit = pci_ivshmem_uninit;
799
    k->vendor_id = PCI_VENDOR_ID_REDHAT_QUMRANET;
800
    k->device_id = 0x1110;
801
    k->class_id = PCI_CLASS_MEMORY_RAM;
802
    dc->reset = ivshmem_reset;
803
    dc->props = ivshmem_properties;
804
}
805

    
806
static TypeInfo ivshmem_info = {
807
    .name          = "ivshmem",
808
    .parent        = TYPE_PCI_DEVICE,
809
    .instance_size = sizeof(IVShmemState),
810
    .class_init    = ivshmem_class_init,
811
};
812

    
813
static void ivshmem_register_types(void)
814
{
815
    type_register_static(&ivshmem_info);
816
}
817

    
818
type_init(ivshmem_register_types)