Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/virtio_pci_common_config.rs
5394 views
1
// Copyright 2018 The ChromiumOS Authors
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
use std::convert::TryInto;
6
7
use base::warn;
8
use serde::Deserialize;
9
use serde::Serialize;
10
use vm_memory::GuestAddress;
11
12
use super::*;
13
14
/// Contains the data for reading and writing the common configuration structure of a virtio PCI
15
/// device.
16
///
17
/// * Registers:
18
/// * About the whole device.
19
/// * le32 device_feature_select; // read-write
20
/// * le32 device_feature; // read-only for driver
21
/// * le32 driver_feature_select; // read-write
22
/// * le32 driver_feature; // read-write
23
/// * le16 msix_config; // read-write
24
/// * le16 num_queues; // read-only for driver
25
/// * u8 device_status; // read-write (driver_status)
26
/// * u8 config_generation; // read-only for driver
27
/// * About a specific virtqueue.
28
/// * le16 queue_select; // read-write
29
/// * le16 queue_size; // read-write, power of 2, or 0.
30
/// * le16 queue_msix_vector; // read-write
31
/// * le16 queue_enable; // read-write (Ready)
32
/// * le16 queue_notify_off; // read-only for driver
33
/// * le64 queue_desc; // read-write
34
/// * le64 queue_avail; // read-write
35
/// * le64 queue_used; // read-write
36
#[derive(Copy, Clone, Serialize, Deserialize)]
37
pub struct VirtioPciCommonConfig {
38
pub driver_status: u8,
39
pub config_generation: u8,
40
pub device_feature_select: u32,
41
pub driver_feature_select: u32,
42
pub queue_select: u16,
43
pub msix_config: u16,
44
}
45
46
impl VirtioPciCommonConfig {
47
pub fn read(
48
&mut self,
49
offset: u64,
50
data: &mut [u8],
51
queues: &mut [QueueConfig],
52
device: &mut dyn VirtioDevice,
53
) {
54
match data.len() {
55
1 => {
56
let v = self.read_common_config_byte(offset);
57
data[0] = v;
58
}
59
2 => {
60
let v = self.read_common_config_word(offset, queues);
61
data.copy_from_slice(&v.to_le_bytes());
62
}
63
4 => {
64
let v = self.read_common_config_dword(offset, device);
65
data.copy_from_slice(&v.to_le_bytes());
66
}
67
8 => {
68
let v = self.read_common_config_qword(offset);
69
data.copy_from_slice(&v.to_le_bytes());
70
}
71
_ => (),
72
}
73
}
74
75
pub fn write(
76
&mut self,
77
offset: u64,
78
data: &[u8],
79
queues: &mut [QueueConfig],
80
device: &mut dyn VirtioDevice,
81
) {
82
match data.len() {
83
1 => self.write_common_config_byte(offset, data[0]),
84
2 => self.write_common_config_word(
85
offset,
86
// This unwrap (and those below) cannot fail since data.len() is checked.
87
u16::from_le_bytes(data.try_into().unwrap()),
88
queues,
89
),
90
4 => self.write_common_config_dword(
91
offset,
92
u32::from_le_bytes(data.try_into().unwrap()),
93
queues,
94
device,
95
),
96
8 => self.write_common_config_qword(
97
offset,
98
u64::from_le_bytes(data.try_into().unwrap()),
99
queues,
100
),
101
_ => (),
102
}
103
}
104
105
fn read_common_config_byte(&self, offset: u64) -> u8 {
106
// The driver is only allowed to do aligned, properly sized access.
107
match offset {
108
0x14 => self.driver_status,
109
0x15 => self.config_generation,
110
_ => 0,
111
}
112
}
113
114
fn write_common_config_byte(&mut self, offset: u64, value: u8) {
115
match offset {
116
0x14 => self.driver_status = value,
117
_ => {
118
warn!("invalid virtio config byt access: 0x{:x}", offset);
119
}
120
}
121
}
122
123
fn read_common_config_word(&self, offset: u64, queues: &[QueueConfig]) -> u16 {
124
match offset {
125
0x10 => self.msix_config,
126
0x12 => queues.len() as u16, // num_queues
127
0x16 => self.queue_select,
128
0x18 => self.with_queue(queues, |q| q.size()).unwrap_or(0),
129
0x1a => self.with_queue(queues, |q| q.vector()).unwrap_or(0),
130
0x1c => self
131
.with_queue(queues, |q| q.ready())
132
.unwrap_or(false)
133
.into(),
134
0x1e => self.queue_select, // notify_off
135
_ => 0,
136
}
137
}
138
139
fn write_common_config_word(&mut self, offset: u64, value: u16, queues: &mut [QueueConfig]) {
140
match offset {
141
0x10 => self.msix_config = value,
142
0x16 => self.queue_select = value,
143
0x18 => self.with_queue_mut(queues, |q| q.set_size(value)),
144
0x1a => self.with_queue_mut(queues, |q| q.set_vector(value)),
145
0x1c => self.with_queue_mut(queues, |q| q.set_ready(value == 1)),
146
_ => {
147
warn!("invalid virtio register word write: 0x{:x}", offset);
148
}
149
}
150
}
151
152
fn read_common_config_dword(&self, offset: u64, device: &dyn VirtioDevice) -> u32 {
153
match offset {
154
0x00 => self.device_feature_select,
155
0x04 => {
156
// Only 64 bits of features (2 pages) are defined for now, so limit
157
// device_feature_select to avoid shifting by 64 or more bits.
158
if self.device_feature_select < 2 {
159
(device.features() >> (self.device_feature_select * 32)) as u32
160
} else {
161
0
162
}
163
}
164
0x08 => self.driver_feature_select,
165
_ => 0,
166
}
167
}
168
169
fn write_common_config_dword(
170
&mut self,
171
offset: u64,
172
value: u32,
173
queues: &mut [QueueConfig],
174
device: &mut dyn VirtioDevice,
175
) {
176
macro_rules! hi {
177
($q:expr, $get:ident, $set:ident, $x:expr) => {
178
$q.$set(($q.$get() & 0xffffffff) | (($x as u64) << 32))
179
};
180
}
181
macro_rules! lo {
182
($q:expr, $get:ident, $set:ident, $x:expr) => {
183
$q.$set(($q.$get() & !0xffffffff) | ($x as u64))
184
};
185
}
186
187
match offset {
188
0x00 => self.device_feature_select = value,
189
0x08 => self.driver_feature_select = value,
190
0x0c => {
191
if self.driver_feature_select < 2 {
192
let features: u64 = (value as u64) << (self.driver_feature_select * 32);
193
device.ack_features(features);
194
for queue in queues.iter_mut() {
195
queue.ack_features(features);
196
}
197
} else {
198
warn!(
199
"invalid ack_features (page {}, value 0x{:x})",
200
self.driver_feature_select, value
201
);
202
}
203
}
204
0x20 => self.with_queue_mut(queues, |q| lo!(q, desc_table, set_desc_table, value)),
205
0x24 => self.with_queue_mut(queues, |q| hi!(q, desc_table, set_desc_table, value)),
206
0x28 => self.with_queue_mut(queues, |q| lo!(q, avail_ring, set_avail_ring, value)),
207
0x2c => self.with_queue_mut(queues, |q| hi!(q, avail_ring, set_avail_ring, value)),
208
0x30 => self.with_queue_mut(queues, |q| lo!(q, used_ring, set_used_ring, value)),
209
0x34 => self.with_queue_mut(queues, |q| hi!(q, used_ring, set_used_ring, value)),
210
_ => {
211
warn!("invalid virtio register dword write: 0x{:x}", offset);
212
}
213
}
214
}
215
216
fn read_common_config_qword(&self, _offset: u64) -> u64 {
217
0 // Assume the guest has no reason to read write-only registers.
218
}
219
220
fn write_common_config_qword(&mut self, offset: u64, value: u64, queues: &mut [QueueConfig]) {
221
match offset {
222
0x20 => self.with_queue_mut(queues, |q| q.set_desc_table(GuestAddress(value))),
223
0x28 => self.with_queue_mut(queues, |q| q.set_avail_ring(GuestAddress(value))),
224
0x30 => self.with_queue_mut(queues, |q| q.set_used_ring(GuestAddress(value))),
225
_ => {
226
warn!("invalid virtio register qword write: 0x{:x}", offset);
227
}
228
}
229
}
230
231
fn with_queue<U, F>(&self, queues: &[QueueConfig], f: F) -> Option<U>
232
where
233
F: FnOnce(&QueueConfig) -> U,
234
{
235
queues.get(self.queue_select as usize).map(f)
236
}
237
238
fn with_queue_mut<F: FnOnce(&mut QueueConfig)>(&self, queues: &mut [QueueConfig], f: F) {
239
if let Some(queue) = queues.get_mut(self.queue_select as usize) {
240
f(queue);
241
}
242
}
243
}
244
245
#[cfg(test)]
246
mod tests {
247
use std::collections::BTreeMap;
248
249
use base::RawDescriptor;
250
use vm_memory::GuestMemory;
251
252
use super::*;
253
254
struct DummyDevice(DeviceType);
255
const QUEUE_SIZE: u16 = 256;
256
const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE];
257
const DUMMY_FEATURES: u64 = 0x5555_aaaa;
258
impl VirtioDevice for DummyDevice {
259
fn keep_rds(&self) -> Vec<RawDescriptor> {
260
Vec::new()
261
}
262
fn device_type(&self) -> DeviceType {
263
self.0
264
}
265
fn queue_max_sizes(&self) -> &[u16] {
266
QUEUE_SIZES
267
}
268
fn activate(
269
&mut self,
270
_mem: GuestMemory,
271
_interrupt: Interrupt,
272
_queues: BTreeMap<usize, Queue>,
273
) -> anyhow::Result<()> {
274
Ok(())
275
}
276
fn features(&self) -> u64 {
277
DUMMY_FEATURES
278
}
279
}
280
281
#[test]
282
fn write_base_regs() {
283
let mut regs = VirtioPciCommonConfig {
284
driver_status: 0xaa,
285
config_generation: 0x55,
286
device_feature_select: 0x0,
287
driver_feature_select: 0x0,
288
queue_select: 0xff,
289
msix_config: 0x00,
290
};
291
292
let dev = &mut DummyDevice(DeviceType::Rng) as &mut dyn VirtioDevice;
293
let mut queues = Vec::new();
294
295
// Can set all bits of driver_status.
296
regs.write(0x14, &[0x55], &mut queues, dev);
297
let mut read_back = vec![0x00];
298
regs.read(0x14, &mut read_back, &mut queues, dev);
299
assert_eq!(read_back[0], 0x55);
300
301
// The config generation register is read only.
302
regs.write(0x15, &[0xaa], &mut queues, dev);
303
let mut read_back = vec![0x00];
304
regs.read(0x15, &mut read_back, &mut queues, dev);
305
assert_eq!(read_back[0], 0x55);
306
307
// Device features is read-only and passed through from the device.
308
regs.write(0x04, &[0, 0, 0, 0], &mut queues, dev);
309
let mut read_back = [0u8; 4];
310
regs.read(0x04, &mut read_back, &mut queues, dev);
311
assert_eq!(u32::from_le_bytes(read_back), DUMMY_FEATURES as u32);
312
313
// Feature select registers are read/write.
314
regs.write(0x00, &[1, 2, 3, 4], &mut queues, dev);
315
let mut read_back = [0u8; 4];
316
regs.read(0x00, &mut read_back, &mut queues, dev);
317
assert_eq!(u32::from_le_bytes(read_back), 0x0403_0201);
318
regs.write(0x08, &[1, 2, 3, 4], &mut queues, dev);
319
let mut read_back = [0u8; 4];
320
regs.read(0x08, &mut read_back, &mut queues, dev);
321
assert_eq!(u32::from_le_bytes(read_back), 0x0403_0201);
322
323
// 'queue_select' can be read and written.
324
regs.write(0x16, &[0xaa, 0x55], &mut queues, dev);
325
let mut read_back = vec![0x00, 0x00];
326
regs.read(0x16, &mut read_back, &mut queues, dev);
327
assert_eq!(read_back[0], 0xaa);
328
assert_eq!(read_back[1], 0x55);
329
}
330
}
331
332