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