|
| 1 | +/* |
| 2 | + * VMApple specific VirtIO Block implementation |
| 3 | + * |
| 4 | + * Copyright © 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved. |
| 5 | + * |
| 6 | + * This work is licensed under the terms of the GNU GPL, version 2 or later. |
| 7 | + * See the COPYING file in the top-level directory. |
| 8 | + * |
| 9 | + * SPDX-License-Identifier: GPL-2.0-or-later |
| 10 | + * |
| 11 | + * VMApple uses almost standard VirtIO Block, but with a few key differences: |
| 12 | + * |
| 13 | + * - Different PCI device/vendor ID |
| 14 | + * - An additional "type" identifier to differentiate AUX and Root volumes |
| 15 | + * - An additional BARRIER command |
| 16 | + */ |
| 17 | + |
| 18 | +#include "qemu/osdep.h" |
| 19 | +#include "hw/vmapple/vmapple.h" |
| 20 | +#include "hw/virtio/virtio-blk.h" |
| 21 | +#include "hw/virtio/virtio-pci.h" |
| 22 | +#include "qemu/bswap.h" |
| 23 | +#include "qemu/log.h" |
| 24 | +#include "qemu/module.h" |
| 25 | +#include "qapi/error.h" |
| 26 | + |
| 27 | +#define TYPE_VMAPPLE_VIRTIO_BLK "vmapple-virtio-blk" |
| 28 | +OBJECT_DECLARE_TYPE(VMAppleVirtIOBlk, VMAppleVirtIOBlkClass, VMAPPLE_VIRTIO_BLK) |
| 29 | + |
| 30 | +typedef struct VMAppleVirtIOBlkClass { |
| 31 | + VirtIOBlkClass parent; |
| 32 | + |
| 33 | + void (*get_config)(VirtIODevice *vdev, uint8_t *config); |
| 34 | +} VMAppleVirtIOBlkClass; |
| 35 | + |
| 36 | +typedef struct VMAppleVirtIOBlk { |
| 37 | + VirtIOBlock parent_obj; |
| 38 | + |
| 39 | + uint32_t apple_type; |
| 40 | +} VMAppleVirtIOBlk; |
| 41 | + |
| 42 | +/* |
| 43 | + * vmapple-virtio-blk-pci: This extends VirtioPCIProxy. |
| 44 | + */ |
| 45 | +OBJECT_DECLARE_SIMPLE_TYPE(VMAppleVirtIOBlkPCI, VMAPPLE_VIRTIO_BLK_PCI) |
| 46 | + |
| 47 | +#define VIRTIO_BLK_T_APPLE_BARRIER 0x10000 |
| 48 | + |
| 49 | +static bool vmapple_virtio_blk_handle_unknown_request(VirtIOBlockReq *req, |
| 50 | + MultiReqBuffer *mrb, |
| 51 | + uint32_t type) |
| 52 | +{ |
| 53 | + switch (type) { |
| 54 | + case VIRTIO_BLK_T_APPLE_BARRIER: |
| 55 | + qemu_log_mask(LOG_UNIMP, "%s: Barrier requests are currently no-ops\n", |
| 56 | + __func__); |
| 57 | + virtio_blk_req_complete(req, VIRTIO_BLK_S_OK); |
| 58 | + g_free(req); |
| 59 | + return true; |
| 60 | + default: |
| 61 | + return false; |
| 62 | + } |
| 63 | +} |
| 64 | + |
| 65 | +/* |
| 66 | + * VMApple virtio-blk uses the same config format as normal virtio, with one |
| 67 | + * exception: It adds an "apple type" specififer at the same location that |
| 68 | + * the spec reserves for max_secure_erase_sectors. Let's hook into the |
| 69 | + * get_config code path here, run it as usual and then patch in the apple type. |
| 70 | + */ |
| 71 | +static void vmapple_virtio_blk_get_config(VirtIODevice *vdev, uint8_t *config) |
| 72 | +{ |
| 73 | + VMAppleVirtIOBlk *dev = VMAPPLE_VIRTIO_BLK(vdev); |
| 74 | + VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_GET_CLASS(dev); |
| 75 | + struct virtio_blk_config *blkcfg = (struct virtio_blk_config *)config; |
| 76 | + |
| 77 | + vvbk->get_config(vdev, config); |
| 78 | + |
| 79 | + g_assert(dev->parent_obj.config_size >= endof(struct virtio_blk_config, zoned)); |
| 80 | + |
| 81 | + /* Apple abuses the field for max_secure_erase_sectors as type id */ |
| 82 | + stl_he_p(&blkcfg->max_secure_erase_sectors, dev->apple_type); |
| 83 | +} |
| 84 | + |
| 85 | +static void vmapple_virtio_blk_class_init(ObjectClass *klass, void *data) |
| 86 | +{ |
| 87 | + VirtIOBlkClass *vbk = VIRTIO_BLK_CLASS(klass); |
| 88 | + VirtioDeviceClass *vdc = VIRTIO_DEVICE_CLASS(klass); |
| 89 | + VMAppleVirtIOBlkClass *vvbk = VMAPPLE_VIRTIO_BLK_CLASS(klass); |
| 90 | + |
| 91 | + vbk->handle_unknown_request = vmapple_virtio_blk_handle_unknown_request; |
| 92 | + vvbk->get_config = vdc->get_config; |
| 93 | + vdc->get_config = vmapple_virtio_blk_get_config; |
| 94 | +} |
| 95 | + |
| 96 | +static const TypeInfo vmapple_virtio_blk_info = { |
| 97 | + .name = TYPE_VMAPPLE_VIRTIO_BLK, |
| 98 | + .parent = TYPE_VIRTIO_BLK, |
| 99 | + .instance_size = sizeof(VMAppleVirtIOBlk), |
| 100 | + .class_size = sizeof(VMAppleVirtIOBlkClass), |
| 101 | + .class_init = vmapple_virtio_blk_class_init, |
| 102 | +}; |
| 103 | + |
| 104 | +/* PCI Devices */ |
| 105 | + |
| 106 | +struct VMAppleVirtIOBlkPCI { |
| 107 | + VirtIOPCIProxy parent_obj; |
| 108 | + |
| 109 | + VMAppleVirtIOBlk vdev; |
| 110 | + VMAppleVirtioBlkVariant variant; |
| 111 | +}; |
| 112 | + |
| 113 | +static const Property vmapple_virtio_blk_pci_properties[] = { |
| 114 | + DEFINE_PROP_UINT32("class", VirtIOPCIProxy, class_code, 0), |
| 115 | + DEFINE_PROP_BIT("ioeventfd", VirtIOPCIProxy, flags, |
| 116 | + VIRTIO_PCI_FLAG_USE_IOEVENTFD_BIT, true), |
| 117 | + DEFINE_PROP_UINT32("vectors", VirtIOPCIProxy, nvectors, |
| 118 | + DEV_NVECTORS_UNSPECIFIED), |
| 119 | + DEFINE_PROP_VMAPPLE_VIRTIO_BLK_VARIANT("variant", VMAppleVirtIOBlkPCI, variant, |
| 120 | + VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED), |
| 121 | +}; |
| 122 | + |
| 123 | +static void vmapple_virtio_blk_pci_realize(VirtIOPCIProxy *vpci_dev, Error **errp) |
| 124 | +{ |
| 125 | + ERRP_GUARD(); |
| 126 | + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(vpci_dev); |
| 127 | + DeviceState *vdev = DEVICE(&dev->vdev); |
| 128 | + VirtIOBlkConf *conf = &dev->vdev.parent_obj.conf; |
| 129 | + |
| 130 | + if (dev->variant == VM_APPLE_VIRTIO_BLK_VARIANT_UNSPECIFIED) { |
| 131 | + error_setg(errp, "vmapple virtio block device variant unspecified"); |
| 132 | + error_append_hint(errp, |
| 133 | + "Variant property must be set to 'aux' or 'root'.\n" |
| 134 | + "Use a regular virtio-blk-pci device instead when " |
| 135 | + "neither is applicaple.\n"); |
| 136 | + return; |
| 137 | + } |
| 138 | + |
| 139 | + if (conf->num_queues == VIRTIO_BLK_AUTO_NUM_QUEUES) { |
| 140 | + conf->num_queues = virtio_pci_optimal_num_queues(0); |
| 141 | + } |
| 142 | + |
| 143 | + if (vpci_dev->nvectors == DEV_NVECTORS_UNSPECIFIED) { |
| 144 | + vpci_dev->nvectors = conf->num_queues + 1; |
| 145 | + } |
| 146 | + |
| 147 | + /* |
| 148 | + * We don't support zones, but we need the additional config space size. |
| 149 | + * Let's just expose the feature so the rest of the virtio-blk logic |
| 150 | + * allocates enough space for us. The guest will ignore zones anyway. |
| 151 | + */ |
| 152 | + virtio_add_feature(&dev->vdev.parent_obj.host_features, VIRTIO_BLK_F_ZONED); |
| 153 | + /* Propagate the apple type down to the virtio-blk device */ |
| 154 | + dev->vdev.apple_type = dev->variant; |
| 155 | + /* and spawn the virtio-blk device */ |
| 156 | + qdev_realize(vdev, BUS(&vpci_dev->bus), errp); |
| 157 | + |
| 158 | + /* |
| 159 | + * The virtio-pci machinery adjusts its vendor/device ID based on whether |
| 160 | + * we support modern or legacy virtio. Let's patch it back to the Apple |
| 161 | + * identifiers here. |
| 162 | + */ |
| 163 | + pci_config_set_vendor_id(vpci_dev->pci_dev.config, PCI_VENDOR_ID_APPLE); |
| 164 | + pci_config_set_device_id(vpci_dev->pci_dev.config, |
| 165 | + PCI_DEVICE_ID_APPLE_VIRTIO_BLK); |
| 166 | +} |
| 167 | + |
| 168 | +static void vmapple_virtio_blk_pci_class_init(ObjectClass *klass, void *data) |
| 169 | +{ |
| 170 | + DeviceClass *dc = DEVICE_CLASS(klass); |
| 171 | + VirtioPCIClass *k = VIRTIO_PCI_CLASS(klass); |
| 172 | + PCIDeviceClass *pcidev_k = PCI_DEVICE_CLASS(klass); |
| 173 | + |
| 174 | + set_bit(DEVICE_CATEGORY_STORAGE, dc->categories); |
| 175 | + device_class_set_props(dc, vmapple_virtio_blk_pci_properties); |
| 176 | + k->realize = vmapple_virtio_blk_pci_realize; |
| 177 | + pcidev_k->vendor_id = PCI_VENDOR_ID_APPLE; |
| 178 | + pcidev_k->device_id = PCI_DEVICE_ID_APPLE_VIRTIO_BLK; |
| 179 | + pcidev_k->revision = VIRTIO_PCI_ABI_VERSION; |
| 180 | + pcidev_k->class_id = PCI_CLASS_STORAGE_SCSI; |
| 181 | +} |
| 182 | + |
| 183 | +static void vmapple_virtio_blk_pci_instance_init(Object *obj) |
| 184 | +{ |
| 185 | + VMAppleVirtIOBlkPCI *dev = VMAPPLE_VIRTIO_BLK_PCI(obj); |
| 186 | + |
| 187 | + virtio_instance_init_common(obj, &dev->vdev, sizeof(dev->vdev), |
| 188 | + TYPE_VMAPPLE_VIRTIO_BLK); |
| 189 | +} |
| 190 | + |
| 191 | +static const VirtioPCIDeviceTypeInfo vmapple_virtio_blk_pci_info = { |
| 192 | + .generic_name = TYPE_VMAPPLE_VIRTIO_BLK_PCI, |
| 193 | + .instance_size = sizeof(VMAppleVirtIOBlkPCI), |
| 194 | + .instance_init = vmapple_virtio_blk_pci_instance_init, |
| 195 | + .class_init = vmapple_virtio_blk_pci_class_init, |
| 196 | +}; |
| 197 | + |
| 198 | +static void vmapple_virtio_blk_register_types(void) |
| 199 | +{ |
| 200 | + type_register_static(&vmapple_virtio_blk_info); |
| 201 | + virtio_pci_types_register(&vmapple_virtio_blk_pci_info); |
| 202 | +} |
| 203 | + |
| 204 | +type_init(vmapple_virtio_blk_register_types) |
0 commit comments