Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/virtio/vhost/scmi.rs
5394 views
1
// Copyright 2023 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::collections::BTreeMap;
6
use std::path::Path;
7
8
use anyhow::anyhow;
9
use anyhow::Context;
10
use base::error;
11
use base::warn;
12
use base::AsRawDescriptor;
13
use base::RawDescriptor;
14
use base::Tube;
15
use base::WorkerThread;
16
use vhost::Scmi as VhostScmiHandle;
17
use vhost::Vhost;
18
use vm_memory::GuestMemory;
19
20
use super::control_socket::VhostDevRequest;
21
use super::control_socket::VhostDevResponse;
22
use super::worker::Worker;
23
use super::Error;
24
use super::Result;
25
use crate::pci::MsixStatus;
26
use crate::virtio::DeviceType;
27
use crate::virtio::Interrupt;
28
use crate::virtio::Queue;
29
use crate::virtio::VirtioDevice;
30
use crate::Suspendable;
31
32
const QUEUE_SIZE: u16 = 128;
33
const NUM_QUEUES: usize = 2;
34
const QUEUE_SIZES: &[u16] = &[QUEUE_SIZE; NUM_QUEUES];
35
const VIRTIO_SCMI_F_P2A_CHANNELS: u32 = 0;
36
37
pub struct Scmi {
38
worker_thread: Option<WorkerThread<()>>,
39
worker_client_tube: Tube,
40
worker_server_tube: Option<Tube>,
41
vhost_handle: Option<VhostScmiHandle>,
42
avail_features: u64,
43
acked_features: u64,
44
}
45
46
impl Scmi {
47
/// Create a new virtio-scmi device.
48
pub fn new(vhost_scmi_device_path: &Path, base_features: u64) -> Result<Scmi> {
49
let handle = VhostScmiHandle::new(vhost_scmi_device_path).map_err(Error::VhostOpen)?;
50
51
let avail_features = base_features | 1 << VIRTIO_SCMI_F_P2A_CHANNELS;
52
53
let (worker_client_tube, worker_server_tube) = Tube::pair().map_err(Error::CreateTube)?;
54
55
Ok(Scmi {
56
worker_thread: None,
57
worker_client_tube,
58
worker_server_tube: Some(worker_server_tube),
59
vhost_handle: Some(handle),
60
avail_features,
61
acked_features: 0,
62
})
63
}
64
65
pub fn acked_features(&self) -> u64 {
66
self.acked_features
67
}
68
}
69
70
impl VirtioDevice for Scmi {
71
fn keep_rds(&self) -> Vec<RawDescriptor> {
72
let mut keep_rds = Vec::new();
73
74
if let Some(handle) = &self.vhost_handle {
75
keep_rds.push(handle.as_raw_descriptor());
76
}
77
keep_rds.push(self.worker_client_tube.as_raw_descriptor());
78
if let Some(worker_server_tube) = &self.worker_server_tube {
79
keep_rds.push(worker_server_tube.as_raw_descriptor());
80
}
81
82
keep_rds
83
}
84
85
fn device_type(&self) -> DeviceType {
86
DeviceType::Scmi
87
}
88
89
fn queue_max_sizes(&self) -> &[u16] {
90
QUEUE_SIZES
91
}
92
93
fn features(&self) -> u64 {
94
self.avail_features
95
}
96
97
fn ack_features(&mut self, value: u64) {
98
let mut v = value;
99
100
// Check if the guest is ACK'ing a feature that we didn't claim to have.
101
let unrequested_features = v & !self.avail_features;
102
if unrequested_features != 0 {
103
warn!("scmi: virtio-scmi got unknown feature ack: {:x}", v);
104
105
// Don't count these features as acked.
106
v &= !unrequested_features;
107
}
108
self.acked_features |= v;
109
}
110
111
fn activate(
112
&mut self,
113
mem: GuestMemory,
114
interrupt: Interrupt,
115
queues: BTreeMap<usize, Queue>,
116
) -> anyhow::Result<()> {
117
if queues.len() != NUM_QUEUES {
118
return Err(anyhow!(
119
"net: expected {} queues, got {}",
120
NUM_QUEUES,
121
queues.len()
122
));
123
}
124
let vhost_handle = self.vhost_handle.take().context("missing vhost_handle")?;
125
let acked_features = self.acked_features;
126
let mut worker = Worker::new(
127
"vhost-scmi",
128
queues,
129
vhost_handle,
130
interrupt,
131
acked_features,
132
self.worker_server_tube
133
.take()
134
.expect("worker control tube missing"),
135
mem,
136
None,
137
)
138
.context("vhost worker init exited with error")?;
139
140
self.worker_thread = Some(WorkerThread::start("vhost_scmi", move |kill_evt| {
141
let result = worker.run(kill_evt);
142
if let Err(e) = result {
143
error!("vhost_scmi worker thread exited with error: {:?}", e);
144
}
145
}));
146
Ok(())
147
}
148
149
fn on_device_sandboxed(&mut self) {
150
// ignore the error but to log the error. We don't need to do
151
// anything here because when activate, the other vhost set up
152
// will be failed to stop the activate thread.
153
if let Some(vhost_handle) = &self.vhost_handle {
154
match vhost_handle.set_owner() {
155
Ok(_) => {}
156
Err(e) => error!("{}: failed to set owner: {:?}", self.debug_label(), e),
157
}
158
}
159
}
160
161
fn control_notify(&self, behavior: MsixStatus) {
162
if self.worker_thread.is_none() {
163
return;
164
}
165
match behavior {
166
MsixStatus::EntryChanged(index) => {
167
if let Err(e) = self
168
.worker_client_tube
169
.send(&VhostDevRequest::MsixEntryChanged(index))
170
{
171
error!(
172
"{} failed to send VhostMsixEntryChanged request for entry {}: {:?}",
173
self.debug_label(),
174
index,
175
e
176
);
177
return;
178
}
179
if let Err(e) = self.worker_client_tube.recv::<VhostDevResponse>() {
180
error!(
181
"{} failed to receive VhostMsixEntryChanged response for entry {}: {:?}",
182
self.debug_label(),
183
index,
184
e
185
);
186
}
187
}
188
MsixStatus::Changed => {
189
if let Err(e) = self.worker_client_tube.send(&VhostDevRequest::MsixChanged) {
190
error!(
191
"{} failed to send VhostMsixChanged request: {:?}",
192
self.debug_label(),
193
e
194
);
195
return;
196
}
197
if let Err(e) = self.worker_client_tube.recv::<VhostDevResponse>() {
198
error!(
199
"{} failed to receive VhostMsixChanged response {:?}",
200
self.debug_label(),
201
e
202
);
203
}
204
}
205
_ => {}
206
}
207
}
208
}
209
210
impl Suspendable for Scmi {}
211
212