xref: /qemu/hw/virtio/virtio-mmio.c (revision e3d08143)
14b52530bSPeter Maydell /*
24b52530bSPeter Maydell  * Virtio MMIO bindings
34b52530bSPeter Maydell  *
44b52530bSPeter Maydell  * Copyright (c) 2011 Linaro Limited
54b52530bSPeter Maydell  *
64b52530bSPeter Maydell  * Author:
74b52530bSPeter Maydell  *  Peter Maydell <peter.maydell@linaro.org>
84b52530bSPeter Maydell  *
94b52530bSPeter Maydell  * This program is free software; you can redistribute it and/or modify
104b52530bSPeter Maydell  * it under the terms of the GNU General Public License; either version 2
114b52530bSPeter Maydell  * of the License, or (at your option) any later version.
124b52530bSPeter Maydell  *
134b52530bSPeter Maydell  * This program is distributed in the hope that it will be useful,
144b52530bSPeter Maydell  * but WITHOUT ANY WARRANTY; without even the implied warranty of
154b52530bSPeter Maydell  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
164b52530bSPeter Maydell  * GNU General Public License for more details.
174b52530bSPeter Maydell  *
184b52530bSPeter Maydell  * You should have received a copy of the GNU General Public License along
194b52530bSPeter Maydell  * with this program; if not, see <http://www.gnu.org/licenses/>.
204b52530bSPeter Maydell  */
214b52530bSPeter Maydell 
229b8bfe21SPeter Maydell #include "qemu/osdep.h"
237e71da7fSMichael S. Tsirkin #include "standard-headers/linux/virtio_mmio.h"
2464552b6bSMarkus Armbruster #include "hw/irq.h"
25a27bd6c7SMarkus Armbruster #include "hw/qdev-properties.h"
264b52530bSPeter Maydell #include "hw/sysbus.h"
274b52530bSPeter Maydell #include "hw/virtio/virtio.h"
28ca77ee28SMarkus Armbruster #include "migration/qemu-file-types.h"
294b52530bSPeter Maydell #include "qemu/host-utils.h"
300b8fa32fSMarkus Armbruster #include "qemu/module.h"
31434027baSYing-Shiuan Pan #include "sysemu/kvm.h"
323909c079SPavel Dovgalyuk #include "sysemu/replay.h"
33bca964bcSSergio Lopez #include "hw/virtio/virtio-mmio.h"
34434027baSYing-Shiuan Pan #include "qemu/error-report.h"
35da1804d1SBoxuan Li #include "qemu/log.h"
36da1804d1SBoxuan Li #include "trace.h"
374b52530bSPeter Maydell 
virtio_mmio_ioeventfd_enabled(DeviceState * d)388e93cef1SPaolo Bonzini static bool virtio_mmio_ioeventfd_enabled(DeviceState *d)
39c0971bcbSCornelia Huck {
40b8893a3cSPavel Dovgalyuk     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
41b8893a3cSPavel Dovgalyuk 
42b8893a3cSPavel Dovgalyuk     return (proxy->flags & VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD) != 0;
43c0971bcbSCornelia Huck }
44c0971bcbSCornelia Huck 
virtio_mmio_ioeventfd_assign(DeviceState * d,EventNotifier * notifier,int n,bool assign)45c0971bcbSCornelia Huck static int virtio_mmio_ioeventfd_assign(DeviceState *d,
46c0971bcbSCornelia Huck                                         EventNotifier *notifier,
47c0971bcbSCornelia Huck                                         int n, bool assign)
48c0971bcbSCornelia Huck {
49c0971bcbSCornelia Huck     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
50434027baSYing-Shiuan Pan 
51434027baSYing-Shiuan Pan     if (assign) {
527e71da7fSMichael S. Tsirkin         memory_region_add_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUE_NOTIFY, 4,
53434027baSYing-Shiuan Pan                                   true, n, notifier);
54434027baSYing-Shiuan Pan     } else {
557e71da7fSMichael S. Tsirkin         memory_region_del_eventfd(&proxy->iomem, VIRTIO_MMIO_QUEUE_NOTIFY, 4,
56434027baSYing-Shiuan Pan                                   true, n, notifier);
57434027baSYing-Shiuan Pan     }
58c0971bcbSCornelia Huck     return 0;
59434027baSYing-Shiuan Pan }
60434027baSYing-Shiuan Pan 
virtio_mmio_start_ioeventfd(VirtIOMMIOProxy * proxy)61434027baSYing-Shiuan Pan static void virtio_mmio_start_ioeventfd(VirtIOMMIOProxy *proxy)
62434027baSYing-Shiuan Pan {
63c0971bcbSCornelia Huck     virtio_bus_start_ioeventfd(&proxy->bus);
64434027baSYing-Shiuan Pan }
65434027baSYing-Shiuan Pan 
virtio_mmio_stop_ioeventfd(VirtIOMMIOProxy * proxy)66434027baSYing-Shiuan Pan static void virtio_mmio_stop_ioeventfd(VirtIOMMIOProxy *proxy)
67434027baSYing-Shiuan Pan {
68c0971bcbSCornelia Huck     virtio_bus_stop_ioeventfd(&proxy->bus);
69434027baSYing-Shiuan Pan }
70434027baSYing-Shiuan Pan 
virtio_mmio_soft_reset(VirtIOMMIOProxy * proxy)71351da832SJean-Philippe Brucker static void virtio_mmio_soft_reset(VirtIOMMIOProxy *proxy)
72351da832SJean-Philippe Brucker {
73351da832SJean-Philippe Brucker     int i;
74351da832SJean-Philippe Brucker 
7526cfd679SPaolo Bonzini     virtio_bus_reset(&proxy->bus);
76351da832SJean-Philippe Brucker 
7726cfd679SPaolo Bonzini     if (!proxy->legacy) {
78351da832SJean-Philippe Brucker         for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
79351da832SJean-Philippe Brucker             proxy->vqs[i].enabled = 0;
80351da832SJean-Philippe Brucker         }
81351da832SJean-Philippe Brucker     }
8226cfd679SPaolo Bonzini }
83351da832SJean-Philippe Brucker 
virtio_mmio_read(void * opaque,hwaddr offset,unsigned size)844b52530bSPeter Maydell static uint64_t virtio_mmio_read(void *opaque, hwaddr offset, unsigned size)
854b52530bSPeter Maydell {
864b52530bSPeter Maydell     VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
8706d3dff0SPaolo Bonzini     VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
884b52530bSPeter Maydell 
89da1804d1SBoxuan Li     trace_virtio_mmio_read(offset);
904b52530bSPeter Maydell 
914b52530bSPeter Maydell     if (!vdev) {
924b52530bSPeter Maydell         /* If no backend is present, we treat most registers as
934b52530bSPeter Maydell          * read-as-zero, except for the magic number, version and
944b52530bSPeter Maydell          * vendor ID. This is not strictly sanctioned by the virtio
954b52530bSPeter Maydell          * spec, but it allows us to provide transports with no backend
964b52530bSPeter Maydell          * plugged in which don't confuse Linux's virtio code: the
974b52530bSPeter Maydell          * probe won't complain about the bad magic number, but the
984b52530bSPeter Maydell          * device ID of zero means no backend will claim it.
994b52530bSPeter Maydell          */
1004b52530bSPeter Maydell         switch (offset) {
1017e71da7fSMichael S. Tsirkin         case VIRTIO_MMIO_MAGIC_VALUE:
1024b52530bSPeter Maydell             return VIRT_MAGIC;
1034b52530bSPeter Maydell         case VIRTIO_MMIO_VERSION:
10444e687a4SSergio Lopez             if (proxy->legacy) {
10544e687a4SSergio Lopez                 return VIRT_VERSION_LEGACY;
10644e687a4SSergio Lopez             } else {
1074b52530bSPeter Maydell                 return VIRT_VERSION;
10844e687a4SSergio Lopez             }
1097e71da7fSMichael S. Tsirkin         case VIRTIO_MMIO_VENDOR_ID:
1104b52530bSPeter Maydell             return VIRT_VENDOR;
1114b52530bSPeter Maydell         default:
1124b52530bSPeter Maydell             return 0;
1134b52530bSPeter Maydell         }
1144b52530bSPeter Maydell     }
1154b52530bSPeter Maydell 
1164b52530bSPeter Maydell     if (offset >= VIRTIO_MMIO_CONFIG) {
1174b52530bSPeter Maydell         offset -= VIRTIO_MMIO_CONFIG;
1180ab8c021SLaurent Vivier         if (proxy->legacy) {
1194b52530bSPeter Maydell             switch (size) {
1204b52530bSPeter Maydell             case 1:
1214b52530bSPeter Maydell                 return virtio_config_readb(vdev, offset);
1224b52530bSPeter Maydell             case 2:
1234b52530bSPeter Maydell                 return virtio_config_readw(vdev, offset);
1244b52530bSPeter Maydell             case 4:
1254b52530bSPeter Maydell                 return virtio_config_readl(vdev, offset);
1264b52530bSPeter Maydell             default:
1274b52530bSPeter Maydell                 abort();
1284b52530bSPeter Maydell             }
1290ab8c021SLaurent Vivier         } else {
1300ab8c021SLaurent Vivier             switch (size) {
1310ab8c021SLaurent Vivier             case 1:
1320ab8c021SLaurent Vivier                 return virtio_config_modern_readb(vdev, offset);
1330ab8c021SLaurent Vivier             case 2:
1340ab8c021SLaurent Vivier                 return virtio_config_modern_readw(vdev, offset);
1350ab8c021SLaurent Vivier             case 4:
1360ab8c021SLaurent Vivier                 return virtio_config_modern_readl(vdev, offset);
1370ab8c021SLaurent Vivier             default:
1380ab8c021SLaurent Vivier                 abort();
1390ab8c021SLaurent Vivier             }
1400ab8c021SLaurent Vivier         }
1414b52530bSPeter Maydell     }
1424b52530bSPeter Maydell     if (size != 4) {
143da1804d1SBoxuan Li         qemu_log_mask(LOG_GUEST_ERROR,
144da1804d1SBoxuan Li                       "%s: wrong size access to register!\n",
145da1804d1SBoxuan Li                       __func__);
1464b52530bSPeter Maydell         return 0;
1474b52530bSPeter Maydell     }
1484b52530bSPeter Maydell     switch (offset) {
1497e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_MAGIC_VALUE:
1504b52530bSPeter Maydell         return VIRT_MAGIC;
1514b52530bSPeter Maydell     case VIRTIO_MMIO_VERSION:
15244e687a4SSergio Lopez         if (proxy->legacy) {
15344e687a4SSergio Lopez             return VIRT_VERSION_LEGACY;
15444e687a4SSergio Lopez         } else {
1554b52530bSPeter Maydell             return VIRT_VERSION;
15644e687a4SSergio Lopez         }
1577e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_DEVICE_ID:
1584b52530bSPeter Maydell         return vdev->device_id;
1597e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_VENDOR_ID:
1604b52530bSPeter Maydell         return VIRT_VENDOR;
1617e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_DEVICE_FEATURES:
16244e687a4SSergio Lopez         if (proxy->legacy) {
1634b52530bSPeter Maydell             if (proxy->host_features_sel) {
1644b52530bSPeter Maydell                 return 0;
16544e687a4SSergio Lopez             } else {
1666b8f1020SCornelia Huck                 return vdev->host_features;
16744e687a4SSergio Lopez             }
16844e687a4SSergio Lopez         } else {
16944e687a4SSergio Lopez             VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
17044e687a4SSergio Lopez             return (vdev->host_features & ~vdc->legacy_features)
17144e687a4SSergio Lopez                 >> (32 * proxy->host_features_sel);
17244e687a4SSergio Lopez         }
1737e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_NUM_MAX:
174f7b803b3SPeter Maydell         if (!virtio_queue_get_num(vdev, vdev->queue_sel)) {
175f7b803b3SPeter Maydell             return 0;
176f7b803b3SPeter Maydell         }
1774b52530bSPeter Maydell         return VIRTQUEUE_MAX_SIZE;
1787e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_PFN:
17944e687a4SSergio Lopez         if (!proxy->legacy) {
18044e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
18144e687a4SSergio Lopez                           "%s: read from legacy register (0x%"
18244e687a4SSergio Lopez                           HWADDR_PRIx ") in non-legacy mode\n",
18344e687a4SSergio Lopez                           __func__, offset);
18444e687a4SSergio Lopez             return 0;
18544e687a4SSergio Lopez         }
1864b52530bSPeter Maydell         return virtio_queue_get_addr(vdev, vdev->queue_sel)
1874b52530bSPeter Maydell             >> proxy->guest_page_shift;
18844e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_READY:
18944e687a4SSergio Lopez         if (proxy->legacy) {
19044e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
19144e687a4SSergio Lopez                           "%s: read from non-legacy register (0x%"
19244e687a4SSergio Lopez                           HWADDR_PRIx ") in legacy mode\n",
19344e687a4SSergio Lopez                           __func__, offset);
19444e687a4SSergio Lopez             return 0;
19544e687a4SSergio Lopez         }
19644e687a4SSergio Lopez         return proxy->vqs[vdev->queue_sel].enabled;
1977e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_INTERRUPT_STATUS:
198d73415a3SStefan Hajnoczi         return qatomic_read(&vdev->isr);
1994b52530bSPeter Maydell     case VIRTIO_MMIO_STATUS:
2004b52530bSPeter Maydell         return vdev->status;
20144e687a4SSergio Lopez     case VIRTIO_MMIO_CONFIG_GENERATION:
20244e687a4SSergio Lopez         if (proxy->legacy) {
20344e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
20444e687a4SSergio Lopez                           "%s: read from non-legacy register (0x%"
20544e687a4SSergio Lopez                           HWADDR_PRIx ") in legacy mode\n",
20644e687a4SSergio Lopez                           __func__, offset);
20744e687a4SSergio Lopez             return 0;
20844e687a4SSergio Lopez         }
20944e687a4SSergio Lopez         return vdev->generation;
2102d9e7d4cSLaurent Vivier    case VIRTIO_MMIO_SHM_LEN_LOW:
2112d9e7d4cSLaurent Vivier    case VIRTIO_MMIO_SHM_LEN_HIGH:
2122d9e7d4cSLaurent Vivier         /*
2132d9e7d4cSLaurent Vivier          * VIRTIO_MMIO_SHM_SEL is unimplemented
2142d9e7d4cSLaurent Vivier          * according to the linux driver, if region length is -1
2152d9e7d4cSLaurent Vivier          * the shared memory doesn't exist
2162d9e7d4cSLaurent Vivier          */
2172d9e7d4cSLaurent Vivier         return -1;
2187e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
2197e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_DRIVER_FEATURES:
2207e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
2217e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_GUEST_PAGE_SIZE:
2227e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_SEL:
2237e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_NUM:
2247e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_ALIGN:
2257e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_NOTIFY:
2267e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_INTERRUPT_ACK:
22744e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_DESC_LOW:
22844e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_DESC_HIGH:
22944e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
23044e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
23144e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_USED_LOW:
23244e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_USED_HIGH:
233da1804d1SBoxuan Li         qemu_log_mask(LOG_GUEST_ERROR,
23444e687a4SSergio Lopez                       "%s: read of write-only register (0x%" HWADDR_PRIx ")\n",
23544e687a4SSergio Lopez                       __func__, offset);
2364b52530bSPeter Maydell         return 0;
2374b52530bSPeter Maydell     default:
23844e687a4SSergio Lopez         qemu_log_mask(LOG_GUEST_ERROR,
23944e687a4SSergio Lopez                       "%s: bad register offset (0x%" HWADDR_PRIx ")\n",
24044e687a4SSergio Lopez                       __func__, offset);
2414b52530bSPeter Maydell         return 0;
2424b52530bSPeter Maydell     }
2434b52530bSPeter Maydell     return 0;
2444b52530bSPeter Maydell }
2454b52530bSPeter Maydell 
virtio_mmio_write(void * opaque,hwaddr offset,uint64_t value,unsigned size)2464b52530bSPeter Maydell static void virtio_mmio_write(void *opaque, hwaddr offset, uint64_t value,
2474b52530bSPeter Maydell                               unsigned size)
2484b52530bSPeter Maydell {
2494b52530bSPeter Maydell     VirtIOMMIOProxy *proxy = (VirtIOMMIOProxy *)opaque;
25006d3dff0SPaolo Bonzini     VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
25154869366SJonah Palmer     uint16_t vq_idx;
2524b52530bSPeter Maydell 
253da1804d1SBoxuan Li     trace_virtio_mmio_write_offset(offset, value);
2544b52530bSPeter Maydell 
2554b52530bSPeter Maydell     if (!vdev) {
2564b52530bSPeter Maydell         /* If no backend is present, we just make all registers
2574b52530bSPeter Maydell          * write-ignored. This allows us to provide transports with
2584b52530bSPeter Maydell          * no backend plugged in.
2594b52530bSPeter Maydell          */
2604b52530bSPeter Maydell         return;
2614b52530bSPeter Maydell     }
2624b52530bSPeter Maydell 
2634b52530bSPeter Maydell     if (offset >= VIRTIO_MMIO_CONFIG) {
2644b52530bSPeter Maydell         offset -= VIRTIO_MMIO_CONFIG;
2650ab8c021SLaurent Vivier         if (proxy->legacy) {
2664b52530bSPeter Maydell             switch (size) {
2674b52530bSPeter Maydell             case 1:
2684b52530bSPeter Maydell                 virtio_config_writeb(vdev, offset, value);
2694b52530bSPeter Maydell                 break;
2704b52530bSPeter Maydell             case 2:
2714b52530bSPeter Maydell                 virtio_config_writew(vdev, offset, value);
2724b52530bSPeter Maydell                 break;
2734b52530bSPeter Maydell             case 4:
2744b52530bSPeter Maydell                 virtio_config_writel(vdev, offset, value);
2754b52530bSPeter Maydell                 break;
2764b52530bSPeter Maydell             default:
2774b52530bSPeter Maydell                 abort();
2784b52530bSPeter Maydell             }
2794b52530bSPeter Maydell             return;
2800ab8c021SLaurent Vivier         } else {
2810ab8c021SLaurent Vivier             switch (size) {
2820ab8c021SLaurent Vivier             case 1:
2830ab8c021SLaurent Vivier                 virtio_config_modern_writeb(vdev, offset, value);
2840ab8c021SLaurent Vivier                 break;
2850ab8c021SLaurent Vivier             case 2:
2860ab8c021SLaurent Vivier                 virtio_config_modern_writew(vdev, offset, value);
2870ab8c021SLaurent Vivier                 break;
2880ab8c021SLaurent Vivier             case 4:
2890ab8c021SLaurent Vivier                 virtio_config_modern_writel(vdev, offset, value);
2900ab8c021SLaurent Vivier                 break;
2910ab8c021SLaurent Vivier             default:
2920ab8c021SLaurent Vivier                 abort();
2930ab8c021SLaurent Vivier             }
2940ab8c021SLaurent Vivier             return;
2950ab8c021SLaurent Vivier         }
2964b52530bSPeter Maydell     }
2974b52530bSPeter Maydell     if (size != 4) {
298da1804d1SBoxuan Li         qemu_log_mask(LOG_GUEST_ERROR,
299da1804d1SBoxuan Li                       "%s: wrong size access to register!\n",
300da1804d1SBoxuan Li                       __func__);
3014b52530bSPeter Maydell         return;
3024b52530bSPeter Maydell     }
3034b52530bSPeter Maydell     switch (offset) {
3047e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_DEVICE_FEATURES_SEL:
30544e687a4SSergio Lopez         if (value) {
30644e687a4SSergio Lopez             proxy->host_features_sel = 1;
30744e687a4SSergio Lopez         } else {
30844e687a4SSergio Lopez             proxy->host_features_sel = 0;
30944e687a4SSergio Lopez         }
3104b52530bSPeter Maydell         break;
3117e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_DRIVER_FEATURES:
31244e687a4SSergio Lopez         if (proxy->legacy) {
31344e687a4SSergio Lopez             if (proxy->guest_features_sel) {
31444e687a4SSergio Lopez                 qemu_log_mask(LOG_GUEST_ERROR,
31544e687a4SSergio Lopez                               "%s: attempt to write guest features with "
31644e687a4SSergio Lopez                               "guest_features_sel > 0 in legacy mode\n",
31744e687a4SSergio Lopez                               __func__);
31844e687a4SSergio Lopez             } else {
3194b52530bSPeter Maydell                 virtio_set_features(vdev, value);
3204b52530bSPeter Maydell             }
32144e687a4SSergio Lopez         } else {
32244e687a4SSergio Lopez             proxy->guest_features[proxy->guest_features_sel] = value;
32344e687a4SSergio Lopez         }
3244b52530bSPeter Maydell         break;
3257e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_DRIVER_FEATURES_SEL:
32644e687a4SSergio Lopez         if (value) {
32744e687a4SSergio Lopez             proxy->guest_features_sel = 1;
32844e687a4SSergio Lopez         } else {
32944e687a4SSergio Lopez             proxy->guest_features_sel = 0;
33044e687a4SSergio Lopez         }
3314b52530bSPeter Maydell         break;
3327e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_GUEST_PAGE_SIZE:
33344e687a4SSergio Lopez         if (!proxy->legacy) {
33444e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
33544e687a4SSergio Lopez                           "%s: write to legacy register (0x%"
33644e687a4SSergio Lopez                           HWADDR_PRIx ") in non-legacy mode\n",
33744e687a4SSergio Lopez                           __func__, offset);
33844e687a4SSergio Lopez             return;
33944e687a4SSergio Lopez         }
3404b52530bSPeter Maydell         proxy->guest_page_shift = ctz32(value);
3414b52530bSPeter Maydell         if (proxy->guest_page_shift > 31) {
3424b52530bSPeter Maydell             proxy->guest_page_shift = 0;
3434b52530bSPeter Maydell         }
344da1804d1SBoxuan Li         trace_virtio_mmio_guest_page(value, proxy->guest_page_shift);
3454b52530bSPeter Maydell         break;
3467e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_SEL:
34787b3bd1cSJason Wang         if (value < VIRTIO_QUEUE_MAX) {
3484b52530bSPeter Maydell             vdev->queue_sel = value;
3494b52530bSPeter Maydell         }
3504b52530bSPeter Maydell         break;
3517e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_NUM:
352da1804d1SBoxuan Li         trace_virtio_mmio_queue_write(value, VIRTQUEUE_MAX_SIZE);
3534b52530bSPeter Maydell         virtio_queue_set_num(vdev, vdev->queue_sel, value);
3541049f4c6SDenis Plotnikov 
3551049f4c6SDenis Plotnikov         if (proxy->legacy) {
356ab223c95SCornelia Huck             virtio_queue_update_rings(vdev, vdev->queue_sel);
35744e687a4SSergio Lopez         } else {
358f0d634eaSCarlos López             virtio_init_region_cache(vdev, vdev->queue_sel);
35944e687a4SSergio Lopez             proxy->vqs[vdev->queue_sel].num = value;
36044e687a4SSergio Lopez         }
3614b52530bSPeter Maydell         break;
3627e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_ALIGN:
36344e687a4SSergio Lopez         if (!proxy->legacy) {
36444e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
36544e687a4SSergio Lopez                           "%s: write to legacy register (0x%"
36644e687a4SSergio Lopez                           HWADDR_PRIx ") in non-legacy mode\n",
36744e687a4SSergio Lopez                           __func__, offset);
36844e687a4SSergio Lopez             return;
36944e687a4SSergio Lopez         }
3704b52530bSPeter Maydell         virtio_queue_set_align(vdev, vdev->queue_sel, value);
3714b52530bSPeter Maydell         break;
3727e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_PFN:
37344e687a4SSergio Lopez         if (!proxy->legacy) {
37444e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
37544e687a4SSergio Lopez                           "%s: write to legacy register (0x%"
37644e687a4SSergio Lopez                           HWADDR_PRIx ") in non-legacy mode\n",
37744e687a4SSergio Lopez                           __func__, offset);
37844e687a4SSergio Lopez             return;
37944e687a4SSergio Lopez         }
3804b52530bSPeter Maydell         if (value == 0) {
38126cfd679SPaolo Bonzini             virtio_mmio_soft_reset(proxy);
3824b52530bSPeter Maydell         } else {
3834b52530bSPeter Maydell             virtio_queue_set_addr(vdev, vdev->queue_sel,
3844b52530bSPeter Maydell                                   value << proxy->guest_page_shift);
3854b52530bSPeter Maydell         }
3864b52530bSPeter Maydell         break;
38744e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_READY:
38844e687a4SSergio Lopez         if (proxy->legacy) {
38944e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
39044e687a4SSergio Lopez                           "%s: write to non-legacy register (0x%"
39144e687a4SSergio Lopez                           HWADDR_PRIx ") in legacy mode\n",
39244e687a4SSergio Lopez                           __func__, offset);
39344e687a4SSergio Lopez             return;
39444e687a4SSergio Lopez         }
39544e687a4SSergio Lopez         if (value) {
39644e687a4SSergio Lopez             virtio_queue_set_num(vdev, vdev->queue_sel,
39744e687a4SSergio Lopez                                  proxy->vqs[vdev->queue_sel].num);
39844e687a4SSergio Lopez             virtio_queue_set_rings(vdev, vdev->queue_sel,
39944e687a4SSergio Lopez                 ((uint64_t)proxy->vqs[vdev->queue_sel].desc[1]) << 32 |
40044e687a4SSergio Lopez                 proxy->vqs[vdev->queue_sel].desc[0],
40144e687a4SSergio Lopez                 ((uint64_t)proxy->vqs[vdev->queue_sel].avail[1]) << 32 |
40244e687a4SSergio Lopez                 proxy->vqs[vdev->queue_sel].avail[0],
40344e687a4SSergio Lopez                 ((uint64_t)proxy->vqs[vdev->queue_sel].used[1]) << 32 |
40444e687a4SSergio Lopez                 proxy->vqs[vdev->queue_sel].used[0]);
40544e687a4SSergio Lopez             proxy->vqs[vdev->queue_sel].enabled = 1;
40644e687a4SSergio Lopez         } else {
40744e687a4SSergio Lopez             proxy->vqs[vdev->queue_sel].enabled = 0;
40844e687a4SSergio Lopez         }
40944e687a4SSergio Lopez         break;
4107e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_NOTIFY:
41154869366SJonah Palmer         vq_idx = value;
41254869366SJonah Palmer         if (vq_idx < VIRTIO_QUEUE_MAX && virtio_queue_get_num(vdev, vq_idx)) {
41354869366SJonah Palmer             if (virtio_vdev_has_feature(vdev, VIRTIO_F_NOTIFICATION_DATA)) {
41454869366SJonah Palmer                 VirtQueue *vq = virtio_get_queue(vdev, vq_idx);
41554869366SJonah Palmer 
41654869366SJonah Palmer                 virtio_queue_set_shadow_avail_idx(vq, (value >> 16) & 0xFFFF);
41754869366SJonah Palmer             }
41854869366SJonah Palmer             virtio_queue_notify(vdev, vq_idx);
4194b52530bSPeter Maydell         }
4204b52530bSPeter Maydell         break;
4217e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_INTERRUPT_ACK:
422d73415a3SStefan Hajnoczi         qatomic_and(&vdev->isr, ~value);
4234b52530bSPeter Maydell         virtio_update_irq(vdev);
4244b52530bSPeter Maydell         break;
4254b52530bSPeter Maydell     case VIRTIO_MMIO_STATUS:
426434027baSYing-Shiuan Pan         if (!(value & VIRTIO_CONFIG_S_DRIVER_OK)) {
427434027baSYing-Shiuan Pan             virtio_mmio_stop_ioeventfd(proxy);
428434027baSYing-Shiuan Pan         }
429434027baSYing-Shiuan Pan 
43044e687a4SSergio Lopez         if (!proxy->legacy && (value & VIRTIO_CONFIG_S_FEATURES_OK)) {
43144e687a4SSergio Lopez             virtio_set_features(vdev,
43244e687a4SSergio Lopez                                 ((uint64_t)proxy->guest_features[1]) << 32 |
43344e687a4SSergio Lopez                                 proxy->guest_features[0]);
43444e687a4SSergio Lopez         }
43544e687a4SSergio Lopez 
4364b52530bSPeter Maydell         virtio_set_status(vdev, value & 0xff);
437434027baSYing-Shiuan Pan 
438434027baSYing-Shiuan Pan         if (value & VIRTIO_CONFIG_S_DRIVER_OK) {
439434027baSYing-Shiuan Pan             virtio_mmio_start_ioeventfd(proxy);
440434027baSYing-Shiuan Pan         }
441434027baSYing-Shiuan Pan 
4424b52530bSPeter Maydell         if (vdev->status == 0) {
443351da832SJean-Philippe Brucker             virtio_mmio_soft_reset(proxy);
4444b52530bSPeter Maydell         }
4454b52530bSPeter Maydell         break;
44644e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_DESC_LOW:
44744e687a4SSergio Lopez         if (proxy->legacy) {
44844e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
44944e687a4SSergio Lopez                           "%s: write to non-legacy register (0x%"
45044e687a4SSergio Lopez                           HWADDR_PRIx ") in legacy mode\n",
45144e687a4SSergio Lopez                           __func__, offset);
45244e687a4SSergio Lopez             return;
45344e687a4SSergio Lopez         }
45444e687a4SSergio Lopez         proxy->vqs[vdev->queue_sel].desc[0] = value;
45544e687a4SSergio Lopez         break;
45644e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_DESC_HIGH:
45744e687a4SSergio Lopez         if (proxy->legacy) {
45844e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
45944e687a4SSergio Lopez                           "%s: write to non-legacy register (0x%"
46044e687a4SSergio Lopez                           HWADDR_PRIx ") in legacy mode\n",
46144e687a4SSergio Lopez                           __func__, offset);
46244e687a4SSergio Lopez             return;
46344e687a4SSergio Lopez         }
46444e687a4SSergio Lopez         proxy->vqs[vdev->queue_sel].desc[1] = value;
46544e687a4SSergio Lopez         break;
46644e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_AVAIL_LOW:
46744e687a4SSergio Lopez         if (proxy->legacy) {
46844e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
46944e687a4SSergio Lopez                           "%s: write to non-legacy register (0x%"
47044e687a4SSergio Lopez                           HWADDR_PRIx ") in legacy mode\n",
47144e687a4SSergio Lopez                           __func__, offset);
47244e687a4SSergio Lopez             return;
47344e687a4SSergio Lopez         }
47444e687a4SSergio Lopez         proxy->vqs[vdev->queue_sel].avail[0] = value;
47544e687a4SSergio Lopez         break;
47644e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_AVAIL_HIGH:
47744e687a4SSergio Lopez         if (proxy->legacy) {
47844e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
47944e687a4SSergio Lopez                           "%s: write to non-legacy register (0x%"
48044e687a4SSergio Lopez                           HWADDR_PRIx ") in legacy mode\n",
48144e687a4SSergio Lopez                           __func__, offset);
48244e687a4SSergio Lopez             return;
48344e687a4SSergio Lopez         }
48444e687a4SSergio Lopez         proxy->vqs[vdev->queue_sel].avail[1] = value;
48544e687a4SSergio Lopez         break;
48644e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_USED_LOW:
48744e687a4SSergio Lopez         if (proxy->legacy) {
48844e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
48944e687a4SSergio Lopez                           "%s: write to non-legacy register (0x%"
49044e687a4SSergio Lopez                           HWADDR_PRIx ") in legacy mode\n",
49144e687a4SSergio Lopez                           __func__, offset);
49244e687a4SSergio Lopez             return;
49344e687a4SSergio Lopez         }
49444e687a4SSergio Lopez         proxy->vqs[vdev->queue_sel].used[0] = value;
49544e687a4SSergio Lopez         break;
49644e687a4SSergio Lopez     case VIRTIO_MMIO_QUEUE_USED_HIGH:
49744e687a4SSergio Lopez         if (proxy->legacy) {
49844e687a4SSergio Lopez             qemu_log_mask(LOG_GUEST_ERROR,
49944e687a4SSergio Lopez                           "%s: write to non-legacy register (0x%"
50044e687a4SSergio Lopez                           HWADDR_PRIx ") in legacy mode\n",
50144e687a4SSergio Lopez                           __func__, offset);
50244e687a4SSergio Lopez             return;
50344e687a4SSergio Lopez         }
50444e687a4SSergio Lopez         proxy->vqs[vdev->queue_sel].used[1] = value;
50544e687a4SSergio Lopez         break;
5067e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_MAGIC_VALUE:
5074b52530bSPeter Maydell     case VIRTIO_MMIO_VERSION:
5087e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_DEVICE_ID:
5097e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_VENDOR_ID:
5107e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_DEVICE_FEATURES:
5117e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_QUEUE_NUM_MAX:
5127e71da7fSMichael S. Tsirkin     case VIRTIO_MMIO_INTERRUPT_STATUS:
51344e687a4SSergio Lopez     case VIRTIO_MMIO_CONFIG_GENERATION:
514da1804d1SBoxuan Li         qemu_log_mask(LOG_GUEST_ERROR,
51544e687a4SSergio Lopez                       "%s: write to read-only register (0x%" HWADDR_PRIx ")\n",
51644e687a4SSergio Lopez                       __func__, offset);
5174b52530bSPeter Maydell         break;
5184b52530bSPeter Maydell 
5194b52530bSPeter Maydell     default:
52044e687a4SSergio Lopez         qemu_log_mask(LOG_GUEST_ERROR,
52144e687a4SSergio Lopez                       "%s: bad register offset (0x%" HWADDR_PRIx ")\n",
52244e687a4SSergio Lopez                       __func__, offset);
5234b52530bSPeter Maydell     }
5244b52530bSPeter Maydell }
5254b52530bSPeter Maydell 
52644e687a4SSergio Lopez static const MemoryRegionOps virtio_legacy_mem_ops = {
52744e687a4SSergio Lopez     .read = virtio_mmio_read,
52844e687a4SSergio Lopez     .write = virtio_mmio_write,
52944e687a4SSergio Lopez     .endianness = DEVICE_NATIVE_ENDIAN,
53044e687a4SSergio Lopez };
53144e687a4SSergio Lopez 
5324b52530bSPeter Maydell static const MemoryRegionOps virtio_mem_ops = {
5334b52530bSPeter Maydell     .read = virtio_mmio_read,
5344b52530bSPeter Maydell     .write = virtio_mmio_write,
53544e687a4SSergio Lopez     .endianness = DEVICE_LITTLE_ENDIAN,
5364b52530bSPeter Maydell };
5374b52530bSPeter Maydell 
virtio_mmio_update_irq(DeviceState * opaque,uint16_t vector)5384b52530bSPeter Maydell static void virtio_mmio_update_irq(DeviceState *opaque, uint16_t vector)
5394b52530bSPeter Maydell {
5404b52530bSPeter Maydell     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
54106d3dff0SPaolo Bonzini     VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
5424b52530bSPeter Maydell     int level;
5434b52530bSPeter Maydell 
54406d3dff0SPaolo Bonzini     if (!vdev) {
5454b52530bSPeter Maydell         return;
5464b52530bSPeter Maydell     }
547d73415a3SStefan Hajnoczi     level = (qatomic_read(&vdev->isr) != 0);
548da1804d1SBoxuan Li     trace_virtio_mmio_setting_irq(level);
5494b52530bSPeter Maydell     qemu_set_irq(proxy->irq, level);
5504b52530bSPeter Maydell }
5514b52530bSPeter Maydell 
virtio_mmio_load_config(DeviceState * opaque,QEMUFile * f)5524b52530bSPeter Maydell static int virtio_mmio_load_config(DeviceState *opaque, QEMUFile *f)
5534b52530bSPeter Maydell {
5544b52530bSPeter Maydell     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
5554b52530bSPeter Maydell 
5564b52530bSPeter Maydell     proxy->host_features_sel = qemu_get_be32(f);
5574b52530bSPeter Maydell     proxy->guest_features_sel = qemu_get_be32(f);
5584b52530bSPeter Maydell     proxy->guest_page_shift = qemu_get_be32(f);
5594b52530bSPeter Maydell     return 0;
5604b52530bSPeter Maydell }
5614b52530bSPeter Maydell 
virtio_mmio_save_config(DeviceState * opaque,QEMUFile * f)5624b52530bSPeter Maydell static void virtio_mmio_save_config(DeviceState *opaque, QEMUFile *f)
5634b52530bSPeter Maydell {
5644b52530bSPeter Maydell     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
5654b52530bSPeter Maydell 
5664b52530bSPeter Maydell     qemu_put_be32(f, proxy->host_features_sel);
5674b52530bSPeter Maydell     qemu_put_be32(f, proxy->guest_features_sel);
5684b52530bSPeter Maydell     qemu_put_be32(f, proxy->guest_page_shift);
5694b52530bSPeter Maydell }
5704b52530bSPeter Maydell 
57144e687a4SSergio Lopez static const VMStateDescription vmstate_virtio_mmio_queue_state = {
57244e687a4SSergio Lopez     .name = "virtio_mmio/queue_state",
57344e687a4SSergio Lopez     .version_id = 1,
57444e687a4SSergio Lopez     .minimum_version_id = 1,
575ca02a170SRichard Henderson     .fields = (const VMStateField[]) {
57644e687a4SSergio Lopez         VMSTATE_UINT16(num, VirtIOMMIOQueue),
57744e687a4SSergio Lopez         VMSTATE_BOOL(enabled, VirtIOMMIOQueue),
57844e687a4SSergio Lopez         VMSTATE_UINT32_ARRAY(desc, VirtIOMMIOQueue, 2),
57944e687a4SSergio Lopez         VMSTATE_UINT32_ARRAY(avail, VirtIOMMIOQueue, 2),
58044e687a4SSergio Lopez         VMSTATE_UINT32_ARRAY(used, VirtIOMMIOQueue, 2),
58144e687a4SSergio Lopez         VMSTATE_END_OF_LIST()
58244e687a4SSergio Lopez     }
58344e687a4SSergio Lopez };
58444e687a4SSergio Lopez 
58544e687a4SSergio Lopez static const VMStateDescription vmstate_virtio_mmio_state_sub = {
58644e687a4SSergio Lopez     .name = "virtio_mmio/state",
58744e687a4SSergio Lopez     .version_id = 1,
58844e687a4SSergio Lopez     .minimum_version_id = 1,
589ca02a170SRichard Henderson     .fields = (const VMStateField[]) {
59044e687a4SSergio Lopez         VMSTATE_UINT32_ARRAY(guest_features, VirtIOMMIOProxy, 2),
59144e687a4SSergio Lopez         VMSTATE_STRUCT_ARRAY(vqs, VirtIOMMIOProxy, VIRTIO_QUEUE_MAX, 0,
59244e687a4SSergio Lopez                              vmstate_virtio_mmio_queue_state,
59344e687a4SSergio Lopez                              VirtIOMMIOQueue),
59444e687a4SSergio Lopez         VMSTATE_END_OF_LIST()
59544e687a4SSergio Lopez     }
59644e687a4SSergio Lopez };
59744e687a4SSergio Lopez 
59844e687a4SSergio Lopez static const VMStateDescription vmstate_virtio_mmio = {
59944e687a4SSergio Lopez     .name = "virtio_mmio",
60044e687a4SSergio Lopez     .version_id = 1,
60144e687a4SSergio Lopez     .minimum_version_id = 1,
602ca02a170SRichard Henderson     .fields = (const VMStateField[]) {
60344e687a4SSergio Lopez         VMSTATE_END_OF_LIST()
60444e687a4SSergio Lopez     },
605ca02a170SRichard Henderson     .subsections = (const VMStateDescription * const []) {
60644e687a4SSergio Lopez         &vmstate_virtio_mmio_state_sub,
60744e687a4SSergio Lopez         NULL
60844e687a4SSergio Lopez     }
60944e687a4SSergio Lopez };
61044e687a4SSergio Lopez 
virtio_mmio_save_extra_state(DeviceState * opaque,QEMUFile * f)61144e687a4SSergio Lopez static void virtio_mmio_save_extra_state(DeviceState *opaque, QEMUFile *f)
61244e687a4SSergio Lopez {
61344e687a4SSergio Lopez     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
61444e687a4SSergio Lopez 
61544e687a4SSergio Lopez     vmstate_save_state(f, &vmstate_virtio_mmio, proxy, NULL);
61644e687a4SSergio Lopez }
61744e687a4SSergio Lopez 
virtio_mmio_load_extra_state(DeviceState * opaque,QEMUFile * f)61844e687a4SSergio Lopez static int virtio_mmio_load_extra_state(DeviceState *opaque, QEMUFile *f)
61944e687a4SSergio Lopez {
62044e687a4SSergio Lopez     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
62144e687a4SSergio Lopez 
62244e687a4SSergio Lopez     return vmstate_load_state(f, &vmstate_virtio_mmio, proxy, 1);
62344e687a4SSergio Lopez }
62444e687a4SSergio Lopez 
virtio_mmio_has_extra_state(DeviceState * opaque)62544e687a4SSergio Lopez static bool virtio_mmio_has_extra_state(DeviceState *opaque)
62644e687a4SSergio Lopez {
62744e687a4SSergio Lopez     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(opaque);
62844e687a4SSergio Lopez 
62944e687a4SSergio Lopez     return !proxy->legacy;
63044e687a4SSergio Lopez }
63144e687a4SSergio Lopez 
virtio_mmio_reset(DeviceState * d)6324b52530bSPeter Maydell static void virtio_mmio_reset(DeviceState *d)
6334b52530bSPeter Maydell {
6344b52530bSPeter Maydell     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
63544e687a4SSergio Lopez     int i;
6364b52530bSPeter Maydell 
63726cfd679SPaolo Bonzini     virtio_mmio_soft_reset(proxy);
63826cfd679SPaolo Bonzini 
6394b52530bSPeter Maydell     proxy->host_features_sel = 0;
6404b52530bSPeter Maydell     proxy->guest_features_sel = 0;
6414b52530bSPeter Maydell     proxy->guest_page_shift = 0;
64244e687a4SSergio Lopez 
64344e687a4SSergio Lopez     if (!proxy->legacy) {
64444e687a4SSergio Lopez         proxy->guest_features[0] = proxy->guest_features[1] = 0;
64544e687a4SSergio Lopez 
64644e687a4SSergio Lopez         for (i = 0; i < VIRTIO_QUEUE_MAX; i++) {
64744e687a4SSergio Lopez             proxy->vqs[i].num = 0;
64844e687a4SSergio Lopez             proxy->vqs[i].desc[0] = proxy->vqs[i].desc[1] = 0;
64944e687a4SSergio Lopez             proxy->vqs[i].avail[0] = proxy->vqs[i].avail[1] = 0;
65044e687a4SSergio Lopez             proxy->vqs[i].used[0] = proxy->vqs[i].used[1] = 0;
65144e687a4SSergio Lopez         }
65244e687a4SSergio Lopez     }
6534b52530bSPeter Maydell }
6544b52530bSPeter Maydell 
virtio_mmio_set_guest_notifier(DeviceState * d,int n,bool assign,bool with_irqfd)655434027baSYing-Shiuan Pan static int virtio_mmio_set_guest_notifier(DeviceState *d, int n, bool assign,
656434027baSYing-Shiuan Pan                                           bool with_irqfd)
657434027baSYing-Shiuan Pan {
658434027baSYing-Shiuan Pan     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
659434027baSYing-Shiuan Pan     VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
660434027baSYing-Shiuan Pan     VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
661434027baSYing-Shiuan Pan     VirtQueue *vq = virtio_get_queue(vdev, n);
662434027baSYing-Shiuan Pan     EventNotifier *notifier = virtio_queue_get_guest_notifier(vq);
663434027baSYing-Shiuan Pan 
664434027baSYing-Shiuan Pan     if (assign) {
665434027baSYing-Shiuan Pan         int r = event_notifier_init(notifier, 0);
666434027baSYing-Shiuan Pan         if (r < 0) {
667434027baSYing-Shiuan Pan             return r;
668434027baSYing-Shiuan Pan         }
669434027baSYing-Shiuan Pan         virtio_queue_set_guest_notifier_fd_handler(vq, true, with_irqfd);
670434027baSYing-Shiuan Pan     } else {
671434027baSYing-Shiuan Pan         virtio_queue_set_guest_notifier_fd_handler(vq, false, with_irqfd);
672434027baSYing-Shiuan Pan         event_notifier_cleanup(notifier);
673434027baSYing-Shiuan Pan     }
674434027baSYing-Shiuan Pan 
6752858bc68SWei Huang     if (vdc->guest_notifier_mask && vdev->use_guest_notifier_mask) {
676434027baSYing-Shiuan Pan         vdc->guest_notifier_mask(vdev, n, !assign);
677434027baSYing-Shiuan Pan     }
678434027baSYing-Shiuan Pan 
679434027baSYing-Shiuan Pan     return 0;
680434027baSYing-Shiuan Pan }
virtio_mmio_set_config_guest_notifier(DeviceState * d,bool assign,bool with_irqfd)681cd336e83SCindy Lu static int virtio_mmio_set_config_guest_notifier(DeviceState *d, bool assign,
682cd336e83SCindy Lu                                                  bool with_irqfd)
683cd336e83SCindy Lu {
684cd336e83SCindy Lu     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
685cd336e83SCindy Lu     VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
686cd336e83SCindy Lu     VirtioDeviceClass *vdc = VIRTIO_DEVICE_GET_CLASS(vdev);
687cd336e83SCindy Lu     EventNotifier *notifier = virtio_config_get_guest_notifier(vdev);
688cd336e83SCindy Lu     int r = 0;
689434027baSYing-Shiuan Pan 
690cd336e83SCindy Lu     if (assign) {
691cd336e83SCindy Lu         r = event_notifier_init(notifier, 0);
692cd336e83SCindy Lu         if (r < 0) {
693cd336e83SCindy Lu             return r;
694cd336e83SCindy Lu         }
695cd336e83SCindy Lu         virtio_config_set_guest_notifier_fd_handler(vdev, assign, with_irqfd);
696cd336e83SCindy Lu     } else {
697cd336e83SCindy Lu         virtio_config_set_guest_notifier_fd_handler(vdev, assign, with_irqfd);
698cd336e83SCindy Lu         event_notifier_cleanup(notifier);
699cd336e83SCindy Lu     }
700cd336e83SCindy Lu     if (vdc->guest_notifier_mask && vdev->use_guest_notifier_mask) {
701cd336e83SCindy Lu         vdc->guest_notifier_mask(vdev, VIRTIO_CONFIG_IRQ_IDX, !assign);
702cd336e83SCindy Lu     }
703cd336e83SCindy Lu     return r;
704cd336e83SCindy Lu }
virtio_mmio_set_guest_notifiers(DeviceState * d,int nvqs,bool assign)705434027baSYing-Shiuan Pan static int virtio_mmio_set_guest_notifiers(DeviceState *d, int nvqs,
706434027baSYing-Shiuan Pan                                            bool assign)
707434027baSYing-Shiuan Pan {
708434027baSYing-Shiuan Pan     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
709434027baSYing-Shiuan Pan     VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
710434027baSYing-Shiuan Pan     /* TODO: need to check if kvm-arm supports irqfd */
711434027baSYing-Shiuan Pan     bool with_irqfd = false;
712434027baSYing-Shiuan Pan     int r, n;
713434027baSYing-Shiuan Pan 
714434027baSYing-Shiuan Pan     nvqs = MIN(nvqs, VIRTIO_QUEUE_MAX);
715434027baSYing-Shiuan Pan 
716434027baSYing-Shiuan Pan     for (n = 0; n < nvqs; n++) {
717434027baSYing-Shiuan Pan         if (!virtio_queue_get_num(vdev, n)) {
718434027baSYing-Shiuan Pan             break;
719434027baSYing-Shiuan Pan         }
720434027baSYing-Shiuan Pan 
721434027baSYing-Shiuan Pan         r = virtio_mmio_set_guest_notifier(d, n, assign, with_irqfd);
722434027baSYing-Shiuan Pan         if (r < 0) {
723434027baSYing-Shiuan Pan             goto assign_error;
724434027baSYing-Shiuan Pan         }
725434027baSYing-Shiuan Pan     }
726cd336e83SCindy Lu     r = virtio_mmio_set_config_guest_notifier(d, assign, with_irqfd);
727cd336e83SCindy Lu     if (r < 0) {
728cd336e83SCindy Lu         goto assign_error;
729cd336e83SCindy Lu     }
730434027baSYing-Shiuan Pan 
731434027baSYing-Shiuan Pan     return 0;
732434027baSYing-Shiuan Pan 
733434027baSYing-Shiuan Pan assign_error:
734434027baSYing-Shiuan Pan     /* We get here on assignment failure. Recover by undoing for VQs 0 .. n. */
735434027baSYing-Shiuan Pan     assert(assign);
736434027baSYing-Shiuan Pan     while (--n >= 0) {
737434027baSYing-Shiuan Pan         virtio_mmio_set_guest_notifier(d, n, !assign, false);
738434027baSYing-Shiuan Pan     }
739434027baSYing-Shiuan Pan     return r;
740434027baSYing-Shiuan Pan }
741434027baSYing-Shiuan Pan 
virtio_mmio_pre_plugged(DeviceState * d,Error ** errp)74244e687a4SSergio Lopez static void virtio_mmio_pre_plugged(DeviceState *d, Error **errp)
74344e687a4SSergio Lopez {
74444e687a4SSergio Lopez     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
74544e687a4SSergio Lopez     VirtIODevice *vdev = virtio_bus_get_device(&proxy->bus);
74644e687a4SSergio Lopez 
74744e687a4SSergio Lopez     if (!proxy->legacy) {
74844e687a4SSergio Lopez         virtio_add_feature(&vdev->host_features, VIRTIO_F_VERSION_1);
74944e687a4SSergio Lopez     }
75044e687a4SSergio Lopez }
75144e687a4SSergio Lopez 
7524b52530bSPeter Maydell /* virtio-mmio device */
7534b52530bSPeter Maydell 
754f58b39d2SLaszlo Ersek static Property virtio_mmio_properties[] = {
755f58b39d2SLaszlo Ersek     DEFINE_PROP_BOOL("format_transport_address", VirtIOMMIOProxy,
756f58b39d2SLaszlo Ersek                      format_transport_address, true),
75744e687a4SSergio Lopez     DEFINE_PROP_BOOL("force-legacy", VirtIOMMIOProxy, legacy, true),
758b8893a3cSPavel Dovgalyuk     DEFINE_PROP_BIT("ioeventfd", VirtIOMMIOProxy, flags,
759b8893a3cSPavel Dovgalyuk                     VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD_BIT, true),
760f58b39d2SLaszlo Ersek     DEFINE_PROP_END_OF_LIST(),
761f58b39d2SLaszlo Ersek };
762f58b39d2SLaszlo Ersek 
virtio_mmio_realizefn(DeviceState * d,Error ** errp)7634b52530bSPeter Maydell static void virtio_mmio_realizefn(DeviceState *d, Error **errp)
7644b52530bSPeter Maydell {
7654b52530bSPeter Maydell     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
7664b52530bSPeter Maydell     SysBusDevice *sbd = SYS_BUS_DEVICE(d);
7674b52530bSPeter Maydell 
768d637e1dcSPeter Maydell     qbus_init(&proxy->bus, sizeof(proxy->bus), TYPE_VIRTIO_MMIO_BUS, d, NULL);
7694b52530bSPeter Maydell     sysbus_init_irq(sbd, &proxy->irq);
770b8893a3cSPavel Dovgalyuk 
7713909c079SPavel Dovgalyuk     /* fd-based ioevents can't be synchronized in record/replay */
7723909c079SPavel Dovgalyuk     if (replay_mode != REPLAY_MODE_NONE) {
7733909c079SPavel Dovgalyuk         proxy->flags &= ~VIRTIO_IOMMIO_FLAG_USE_IOEVENTFD;
7743909c079SPavel Dovgalyuk     }
7753909c079SPavel Dovgalyuk 
77644e687a4SSergio Lopez     if (proxy->legacy) {
77744e687a4SSergio Lopez         memory_region_init_io(&proxy->iomem, OBJECT(d),
77844e687a4SSergio Lopez                               &virtio_legacy_mem_ops, proxy,
7794b52530bSPeter Maydell                               TYPE_VIRTIO_MMIO, 0x200);
78044e687a4SSergio Lopez     } else {
78144e687a4SSergio Lopez         memory_region_init_io(&proxy->iomem, OBJECT(d),
78244e687a4SSergio Lopez                               &virtio_mem_ops, proxy,
78344e687a4SSergio Lopez                               TYPE_VIRTIO_MMIO, 0x200);
78444e687a4SSergio Lopez     }
7854b52530bSPeter Maydell     sysbus_init_mmio(sbd, &proxy->iomem);
7864b52530bSPeter Maydell }
7874b52530bSPeter Maydell 
virtio_mmio_class_init(ObjectClass * klass,void * data)7884b52530bSPeter Maydell static void virtio_mmio_class_init(ObjectClass *klass, void *data)
7894b52530bSPeter Maydell {
7904b52530bSPeter Maydell     DeviceClass *dc = DEVICE_CLASS(klass);
7914b52530bSPeter Maydell 
7924b52530bSPeter Maydell     dc->realize = virtio_mmio_realizefn;
793*e3d08143SPeter Maydell     device_class_set_legacy_reset(dc, virtio_mmio_reset);
794125ee0edSMarcel Apfelbaum     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
7954f67d30bSMarc-André Lureau     device_class_set_props(dc, virtio_mmio_properties);
7964b52530bSPeter Maydell }
7974b52530bSPeter Maydell 
7984b52530bSPeter Maydell static const TypeInfo virtio_mmio_info = {
7994b52530bSPeter Maydell     .name          = TYPE_VIRTIO_MMIO,
8004b52530bSPeter Maydell     .parent        = TYPE_SYS_BUS_DEVICE,
8014b52530bSPeter Maydell     .instance_size = sizeof(VirtIOMMIOProxy),
8024b52530bSPeter Maydell     .class_init    = virtio_mmio_class_init,
8034b52530bSPeter Maydell };
8044b52530bSPeter Maydell 
8054b52530bSPeter Maydell /* virtio-mmio-bus. */
8064b52530bSPeter Maydell 
virtio_mmio_bus_get_dev_path(DeviceState * dev)807f58b39d2SLaszlo Ersek static char *virtio_mmio_bus_get_dev_path(DeviceState *dev)
808f58b39d2SLaszlo Ersek {
809f58b39d2SLaszlo Ersek     BusState *virtio_mmio_bus;
810f58b39d2SLaszlo Ersek     VirtIOMMIOProxy *virtio_mmio_proxy;
811f58b39d2SLaszlo Ersek     char *proxy_path;
812f58b39d2SLaszlo Ersek     char *path;
813819b3496Sschspa     MemoryRegionSection section;
814f58b39d2SLaszlo Ersek 
815f58b39d2SLaszlo Ersek     virtio_mmio_bus = qdev_get_parent_bus(dev);
816f58b39d2SLaszlo Ersek     virtio_mmio_proxy = VIRTIO_MMIO(virtio_mmio_bus->parent);
817f58b39d2SLaszlo Ersek     proxy_path = qdev_get_dev_path(DEVICE(virtio_mmio_proxy));
818f58b39d2SLaszlo Ersek 
819f58b39d2SLaszlo Ersek     /*
820f58b39d2SLaszlo Ersek      * If @format_transport_address is false, then we just perform the same as
821f58b39d2SLaszlo Ersek      * virtio_bus_get_dev_path(): we delegate the address formatting for the
822f58b39d2SLaszlo Ersek      * device on the virtio-mmio bus to the bus that the virtio-mmio proxy
823f58b39d2SLaszlo Ersek      * (i.e., the device that implements the virtio-mmio bus) resides on. In
824f58b39d2SLaszlo Ersek      * this case the base address of the virtio-mmio transport will be
825f58b39d2SLaszlo Ersek      * invisible.
826f58b39d2SLaszlo Ersek      */
827f58b39d2SLaszlo Ersek     if (!virtio_mmio_proxy->format_transport_address) {
828f58b39d2SLaszlo Ersek         return proxy_path;
829f58b39d2SLaszlo Ersek     }
830f58b39d2SLaszlo Ersek 
831f58b39d2SLaszlo Ersek     /* Otherwise, we append the base address of the transport. */
832819b3496Sschspa     section = memory_region_find(&virtio_mmio_proxy->iomem, 0, 0x200);
833819b3496Sschspa     assert(section.mr);
834f58b39d2SLaszlo Ersek 
835f58b39d2SLaszlo Ersek     if (proxy_path) {
836883f2c59SPhilippe Mathieu-Daudé         path = g_strdup_printf("%s/virtio-mmio@" HWADDR_FMT_plx, proxy_path,
837819b3496Sschspa                                section.offset_within_address_space);
838f58b39d2SLaszlo Ersek     } else {
839883f2c59SPhilippe Mathieu-Daudé         path = g_strdup_printf("virtio-mmio@" HWADDR_FMT_plx,
840819b3496Sschspa                                section.offset_within_address_space);
841f58b39d2SLaszlo Ersek     }
842819b3496Sschspa     memory_region_unref(section.mr);
843819b3496Sschspa 
844f58b39d2SLaszlo Ersek     g_free(proxy_path);
845f58b39d2SLaszlo Ersek     return path;
846f58b39d2SLaszlo Ersek }
847f58b39d2SLaszlo Ersek 
virtio_mmio_vmstate_change(DeviceState * d,bool running)8487abba7c6SCindy Lu static void virtio_mmio_vmstate_change(DeviceState *d, bool running)
8497abba7c6SCindy Lu {
8507abba7c6SCindy Lu     VirtIOMMIOProxy *proxy = VIRTIO_MMIO(d);
8517abba7c6SCindy Lu 
8527abba7c6SCindy Lu     if (running) {
8537abba7c6SCindy Lu         virtio_mmio_start_ioeventfd(proxy);
8547abba7c6SCindy Lu     } else {
8557abba7c6SCindy Lu         virtio_mmio_stop_ioeventfd(proxy);
8567abba7c6SCindy Lu     }
8577abba7c6SCindy Lu }
8587abba7c6SCindy Lu 
virtio_mmio_bus_class_init(ObjectClass * klass,void * data)8594b52530bSPeter Maydell static void virtio_mmio_bus_class_init(ObjectClass *klass, void *data)
8604b52530bSPeter Maydell {
8614b52530bSPeter Maydell     BusClass *bus_class = BUS_CLASS(klass);
8624b52530bSPeter Maydell     VirtioBusClass *k = VIRTIO_BUS_CLASS(klass);
8634b52530bSPeter Maydell 
8644b52530bSPeter Maydell     k->notify = virtio_mmio_update_irq;
8654b52530bSPeter Maydell     k->save_config = virtio_mmio_save_config;
8664b52530bSPeter Maydell     k->load_config = virtio_mmio_load_config;
86744e687a4SSergio Lopez     k->save_extra_state = virtio_mmio_save_extra_state;
86844e687a4SSergio Lopez     k->load_extra_state = virtio_mmio_load_extra_state;
86944e687a4SSergio Lopez     k->has_extra_state = virtio_mmio_has_extra_state;
870434027baSYing-Shiuan Pan     k->set_guest_notifiers = virtio_mmio_set_guest_notifiers;
8718e93cef1SPaolo Bonzini     k->ioeventfd_enabled = virtio_mmio_ioeventfd_enabled;
872c0971bcbSCornelia Huck     k->ioeventfd_assign = virtio_mmio_ioeventfd_assign;
87344e687a4SSergio Lopez     k->pre_plugged = virtio_mmio_pre_plugged;
8747abba7c6SCindy Lu     k->vmstate_change = virtio_mmio_vmstate_change;
8754b52530bSPeter Maydell     k->has_variable_vring_alignment = true;
8764b52530bSPeter Maydell     bus_class->max_dev = 1;
877f58b39d2SLaszlo Ersek     bus_class->get_dev_path = virtio_mmio_bus_get_dev_path;
8784b52530bSPeter Maydell }
8794b52530bSPeter Maydell 
8804b52530bSPeter Maydell static const TypeInfo virtio_mmio_bus_info = {
8814b52530bSPeter Maydell     .name          = TYPE_VIRTIO_MMIO_BUS,
8824b52530bSPeter Maydell     .parent        = TYPE_VIRTIO_BUS,
8834b52530bSPeter Maydell     .instance_size = sizeof(VirtioBusState),
8844b52530bSPeter Maydell     .class_init    = virtio_mmio_bus_class_init,
8854b52530bSPeter Maydell };
8864b52530bSPeter Maydell 
virtio_mmio_register_types(void)8874b52530bSPeter Maydell static void virtio_mmio_register_types(void)
8884b52530bSPeter Maydell {
8894b52530bSPeter Maydell     type_register_static(&virtio_mmio_bus_info);
8904b52530bSPeter Maydell     type_register_static(&virtio_mmio_info);
8914b52530bSPeter Maydell }
8924b52530bSPeter Maydell 
8934b52530bSPeter Maydell type_init(virtio_mmio_register_types)
894