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