Skip to content

Commit a7c4a46

Browse files
foxengwkozaczuk
authored andcommitted
virtio-pci: discover shared memory regions
The latest virtio spec adds shared memory regions: "Shared memory regions are an additional facility available to devices that need a region of memory that’s continuously shared between the device and the driver, rather than passed between them in the way virtqueue elements are." In virtio over PCI, these are enumerated as a sequence of VIRTIO_PCI_CAP_SHARED_MEMORY_CFG capabilities, one per region. This patch extends the virtio over PCI implementation to discover all such regions provided by a device. Signed-off-by: Fotis Xenakis <[email protected]> Message-Id: <VI1PR03MB438308A0EFF0221BAB6CC9F7A6F90@VI1PR03MB4383.eurprd03.prod.outlook.com>
1 parent 753ac13 commit a7c4a46

File tree

2 files changed

+78
-18
lines changed

2 files changed

+78
-18
lines changed

drivers/virtio-pci-device.cc

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -269,19 +269,24 @@ bool virtio_modern_pci_device::parse_pci_config()
269269
return false;
270270
}
271271

272+
// TODO: Consider consolidating these (they duplicate work)
272273
parse_virtio_capability(_common_cfg, VIRTIO_PCI_CAP_COMMON_CFG);
273274
parse_virtio_capability(_isr_cfg, VIRTIO_PCI_CAP_ISR_CFG);
274275
parse_virtio_capability(_notify_cfg, VIRTIO_PCI_CAP_NOTIFY_CFG);
275276
parse_virtio_capability(_device_cfg, VIRTIO_PCI_CAP_DEVICE_CFG);
277+
parse_virtio_capabilities(_shm_cfgs, VIRTIO_PCI_CAP_SHARED_MEMORY_CFG);
276278

277279
if (_notify_cfg) {
278280
_notify_offset_multiplier =_dev->pci_readl(_notify_cfg->get_cfg_offset() +
279281
offsetof(virtio_pci_notify_cap, notify_offset_multiplier));
280282
}
281283

282-
return _common_cfg && _isr_cfg && _notify_cfg && _device_cfg;
284+
// The common, isr and notifications configurations are mandatory
285+
return _common_cfg && _isr_cfg && _notify_cfg;
283286
}
284287

288+
// Parse a single virtio PCI capability, whose type must match @type and store
289+
// it in @ptr.
285290
void virtio_modern_pci_device::parse_virtio_capability(std::unique_ptr<virtio_capability> &ptr, u8 type)
286291
{
287292
u8 cfg_offset = _dev->find_capability(pci::function::PCI_CAP_VENDOR, [type] (pci::function *fun, u8 offset) {
@@ -291,19 +296,61 @@ void virtio_modern_pci_device::parse_virtio_capability(std::unique_ptr<virtio_ca
291296

292297
if (cfg_offset != 0xFF) {
293298
u8 bar_index = _dev->pci_readb(cfg_offset + offsetof(struct virtio_pci_cap, bar));
294-
u32 offset = _dev->pci_readl(cfg_offset + offsetof(struct virtio_pci_cap, offset));
295-
u32 length = _dev->pci_readl(cfg_offset + offsetof(struct virtio_pci_cap, length));
296-
297299
auto bar_no = bar_index + 1;
298300
auto bar = _dev->get_bar(bar_no);
299301
if (bar && bar->is_mmio() && !bar->is_mapped()) {
300302
bar->map();
301303
}
302304

305+
u64 offset = _dev->pci_readl(cfg_offset + offsetof(struct virtio_pci_cap, offset));
306+
u64 length = _dev->pci_readl(cfg_offset + offsetof(struct virtio_pci_cap, length));
307+
303308
ptr.reset(new virtio_modern_pci_device::virtio_capability(cfg_offset, bar, bar_no, offset, length));
304309
}
305310
}
306311

312+
// Parse all virtio PCI capabilities whose types match @type and append them to
313+
// @caps.
314+
// From the spec: "The device MAY offer more than one structure of any type -
315+
// this makes it possible for the device to expose multiple interfaces to
316+
// drivers. The order of the capabilities in the capability list specifies the
317+
// order of preference suggested by the device. A device may specify that this
318+
// ordering mechanism be overridden by the use of the id field."
319+
void virtio_modern_pci_device::parse_virtio_capabilities(
320+
std::vector<std::unique_ptr<virtio_capability>>& caps, u8 type)
321+
{
322+
std::vector<u8> cap_offs;
323+
_dev->find_capabilities(cap_offs, pci::function::PCI_CAP_VENDOR);
324+
325+
for (auto cfg_offset: cap_offs) {
326+
u8 cfg_type = _dev->pci_readb(cfg_offset + offsetof(virtio_pci_cap, cfg_type));
327+
if (cfg_type != type) {
328+
continue;
329+
}
330+
331+
u8 bar_index = _dev->pci_readb(cfg_offset + offsetof(struct virtio_pci_cap, bar));
332+
auto bar_no = bar_index + 1;
333+
auto bar = _dev->get_bar(bar_no);
334+
if (bar && bar->is_mmio() && !bar->is_mapped()) {
335+
bar->map();
336+
}
337+
338+
u64 offset = _dev->pci_readl(cfg_offset + offsetof(struct virtio_pci_cap, offset));
339+
u64 length = _dev->pci_readl(cfg_offset + offsetof(struct virtio_pci_cap, length));
340+
if (type == VIRTIO_PCI_CAP_SHARED_MEMORY_CFG) {
341+
// The shared memory region capability is defined by a struct
342+
// virtio_pci_cap64
343+
u32 offset_hi = _dev->pci_readl(cfg_offset + offsetof(virtio_pci_cap64, offset_hi));
344+
u32 length_hi = _dev->pci_readl(cfg_offset + offsetof(virtio_pci_cap64, length_hi));
345+
offset |= ((u64)offset_hi << 32);
346+
length |= ((u64)length_hi << 32);
347+
}
348+
349+
caps.emplace_back(new virtio_modern_pci_device::virtio_capability(
350+
cfg_offset, bar, bar_no, offset, length));
351+
}
352+
}
353+
307354
virtio_device* create_virtio_pci_device(pci::device *dev) {
308355
if (dev->get_device_id() >= VIRTIO_PCI_MODERN_ID_MIN && dev->get_device_id() <= VIRTIO_PCI_MODERN_ID_MAX)
309356
return new virtio_modern_pci_device(dev);

drivers/virtio-pci-device.hh

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ public:
9898
~virtio_legacy_pci_device() {}
9999

100100
virtual const char *get_version() { return "legacy"; }
101-
virtual u16 get_type_id() { return _dev->get_subsystem_id(); };
101+
virtual u16 get_type_id() { return _dev->get_subsystem_id(); }
102102

103103
virtual void select_queue(int queue);
104104
virtual u16 get_queue_size();
@@ -115,7 +115,7 @@ public:
115115
virtual u8 read_config(u32 offset);
116116
virtual u8 read_and_ack_isr();
117117

118-
virtual bool is_modern() { return false; };
118+
virtual bool is_modern() { return false; }
119119
protected:
120120
virtual bool parse_pci_config();
121121

@@ -145,6 +145,8 @@ enum VIRTIO_MODERN_PCI_CONFIG {
145145
VIRTIO_PCI_CAP_DEVICE_CFG = 4,
146146
/* PCI configuration access */
147147
VIRTIO_PCI_CAP_PCI_CFG = 5,
148+
/* Shared memory region */
149+
VIRTIO_PCI_CAP_SHARED_MEMORY_CFG = 8,
148150
};
149151

150152
/* This is the PCI capability header: */
@@ -154,11 +156,20 @@ struct virtio_pci_cap {
154156
u8 cap_len; /* Generic PCI field: capability length */
155157
u8 cfg_type; /* Identifies the structure. */
156158
u8 bar; /* Where to find it. */
157-
u8 padding[3]; /* Pad to full dword. */
159+
u8 id; /* Multiple capabilities of the same type */
160+
u8 padding[2]; /* Pad to full dword. */
158161
u32 offset; /* Offset within bar. */
159162
u32 length; /* Length of the structure, in bytes. */
160163
};
161164

165+
/* A variant of virtio_pci_cap, for capabilities that require offsets or lengths
166+
* larger than 4GiB */
167+
struct virtio_pci_cap64 {
168+
struct virtio_pci_cap cap;
169+
u32 offset_hi;
170+
u32 length_hi;
171+
};
172+
162173
/* The notification location is found using the VIRTIO_PCI_CAP_NOTIFY_CFG capability.
163174
* This capability is immediately followed by an additional field, like so:*/
164175
struct virtio_pci_notify_cap {
@@ -198,7 +209,7 @@ struct virtio_pci_common_cfg {
198209
class virtio_modern_pci_device : public virtio_pci_device {
199210
public:
200211
struct virtio_capability {
201-
virtio_capability(u32 cfg_offset, pci::bar* bar, u32 bar_no, u32 bar_offset, u32 length) :
212+
virtio_capability(u32 cfg_offset, pci::bar* bar, u32 bar_no, u64 bar_offset, u64 length) :
202213
_cfg_offset(cfg_offset),
203214
_bar(bar),
204215
_bar_no(bar_no),
@@ -207,27 +218,27 @@ public:
207218
assert(_length > 0 && _bar_offset >= 0 && _bar_offset + _length <= _bar->get_size());
208219
}
209220

210-
u8 virtio_conf_readb(u32 offset) {
221+
u8 virtio_conf_readb(u64 offset) {
211222
verify_offset(offset, sizeof(u8));
212223
return _bar->readb(_bar_offset + offset);
213224
};
214-
u16 virtio_conf_readw(u32 offset) {
225+
u16 virtio_conf_readw(u64 offset) {
215226
verify_offset(offset, sizeof(u16));
216227
return _bar->readw(_bar_offset + offset);
217228
};
218-
u32 virtio_conf_readl(u32 offset) {
229+
u32 virtio_conf_readl(u64 offset) {
219230
verify_offset(offset, sizeof(u32));
220231
return _bar->readl(_bar_offset + offset);
221232
};
222-
void virtio_conf_writeb(u32 offset, u8 val) {
233+
void virtio_conf_writeb(u64 offset, u8 val) {
223234
verify_offset(offset, sizeof(u8));
224235
_bar->writeb(_bar_offset + offset, val);
225236
};
226-
void virtio_conf_writew(u32 offset, u16 val) {
237+
void virtio_conf_writew(u64 offset, u16 val) {
227238
verify_offset(offset, sizeof(u16));
228239
_bar->writew(_bar_offset + offset, val);
229240
};
230-
void virtio_conf_writel(u32 offset, u32 val) {
241+
void virtio_conf_writel(u64 offset, u32 val) {
231242
verify_offset(offset, sizeof(u32));
232243
_bar->writel(_bar_offset + offset, val);
233244
};
@@ -237,15 +248,15 @@ public:
237248
virtio_d("%s bar=%d, offset=%x, size=%x", prefix, _bar_no, _bar_offset, _length);
238249
}
239250
private:
240-
inline void verify_offset(u32 offset, u32 size) {
251+
inline void verify_offset(u64 offset, u32 size) {
241252
assert(offset >= 0 && offset + size <= _length);
242253
}
243254

244255
u32 _cfg_offset;
245256
pci::bar* _bar;
246257
u32 _bar_no;
247-
u32 _bar_offset;
248-
u32 _length;
258+
u64 _bar_offset;
259+
u64 _length;
249260
};
250261

251262
explicit virtio_modern_pci_device(pci::device *dev);
@@ -277,11 +288,13 @@ protected:
277288
virtual bool parse_pci_config();
278289
private:
279290
void parse_virtio_capability(std::unique_ptr<virtio_capability> &ptr, u8 type);
291+
void parse_virtio_capabilities(std::vector<std::unique_ptr<virtio_capability>>& caps, u8 type);
280292

281293
std::unique_ptr<virtio_capability> _common_cfg;
282294
std::unique_ptr<virtio_capability> _isr_cfg;
283295
std::unique_ptr<virtio_capability> _notify_cfg;
284296
std::unique_ptr<virtio_capability> _device_cfg;
297+
std::vector<std::unique_ptr<virtio_capability>> _shm_cfgs;
285298

286299
u32 _notify_offset_multiplier;
287300
u32 _queues_notify_offsets[64];
@@ -293,4 +306,4 @@ virtio_device* create_virtio_pci_device(pci::device *dev);
293306

294307
}
295308

296-
#endif //VIRTIO_PCI_DEVICE_HH
309+
#endif //VIRTIO_PCI_DEVICE_HH

0 commit comments

Comments
 (0)