Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
google
GitHub Repository: google/crosvm
Path: blob/main/devices/src/usb/xhci/command_ring_controller.rs
5394 views
1
// Copyright 2019 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
#![allow(clippy::result_large_err)]
6
7
use std::sync::Arc;
8
9
use anyhow::Context;
10
use base::error;
11
use base::warn;
12
use base::Error as SysError;
13
use base::Event;
14
use remain::sorted;
15
use sync::Mutex;
16
use thiserror::Error;
17
use vm_memory::GuestAddress;
18
use vm_memory::GuestMemory;
19
20
use super::device_slot::DeviceSlot;
21
use super::device_slot::DeviceSlots;
22
use super::device_slot::Error as DeviceSlotError;
23
use super::interrupter::Error as InterrupterError;
24
use super::interrupter::Interrupter;
25
use super::ring_buffer_controller::Error as RingBufferControllerError;
26
use super::ring_buffer_controller::RingBufferController;
27
use super::ring_buffer_controller::TransferDescriptorHandler;
28
use super::xhci_abi::AddressDeviceCommandTrb;
29
use super::xhci_abi::AddressedTrb;
30
use super::xhci_abi::ConfigureEndpointCommandTrb;
31
use super::xhci_abi::DisableSlotCommandTrb;
32
use super::xhci_abi::Error as TrbError;
33
use super::xhci_abi::EvaluateContextCommandTrb;
34
use super::xhci_abi::ResetDeviceCommandTrb;
35
use super::xhci_abi::ResetEndpointCommandTrb;
36
use super::xhci_abi::SetTRDequeuePointerCommandTrb;
37
use super::xhci_abi::StopEndpointCommandTrb;
38
use super::xhci_abi::TransferDescriptor;
39
use super::xhci_abi::TrbCast;
40
use super::xhci_abi::TrbCompletionCode;
41
use super::xhci_abi::TrbType;
42
use super::xhci_regs::valid_slot_id;
43
use super::xhci_regs::MAX_SLOTS;
44
use crate::utils::EventLoop;
45
46
#[sorted]
47
#[derive(Error, Debug)]
48
pub enum Error {
49
#[error("bad slot id: {0}")]
50
BadSlotId(u8),
51
#[error("failed to cast trb: {0}")]
52
CastTrb(TrbError),
53
#[error("failed to config endpoint: {0}")]
54
ConfigEndpoint(DeviceSlotError),
55
#[error("failed to disable slot: {0}")]
56
DisableSlot(DeviceSlotError),
57
#[error("failed to evaluate context: {0}")]
58
EvaluateContext(DeviceSlotError),
59
#[error("failed to reset slot: {0}")]
60
ResetSlot(DeviceSlotError),
61
#[error("failed to send interrupt: {0}")]
62
SendInterrupt(InterrupterError),
63
#[error("failed to set address: {0}")]
64
SetAddress(DeviceSlotError),
65
#[error("failed to set dequeue pointer: {0}")]
66
SetDequeuePointer(DeviceSlotError),
67
#[error("failed to stop endpoint: {0}")]
68
StopEndpoint(DeviceSlotError),
69
#[error("failed to write event: {0}")]
70
WriteEvent(SysError),
71
}
72
73
type Result<T> = std::result::Result<T, Error>;
74
75
pub type CommandRingController = RingBufferController<CommandRingTrbHandler>;
76
pub type CommandRingControllerError = RingBufferControllerError;
77
78
impl CommandRingController {
79
pub fn new(
80
mem: GuestMemory,
81
event_loop: Arc<EventLoop>,
82
slots: DeviceSlots,
83
interrupter: Arc<Mutex<Interrupter>>,
84
) -> std::result::Result<Arc<CommandRingController>, RingBufferControllerError> {
85
RingBufferController::new_with_handler(
86
String::from("command ring"),
87
mem,
88
event_loop,
89
CommandRingTrbHandler::new(slots, interrupter),
90
)
91
}
92
}
93
94
pub struct CommandRingTrbHandler {
95
slots: DeviceSlots,
96
interrupter: Arc<Mutex<Interrupter>>,
97
}
98
99
impl CommandRingTrbHandler {
100
fn new(slots: DeviceSlots, interrupter: Arc<Mutex<Interrupter>>) -> Self {
101
CommandRingTrbHandler { slots, interrupter }
102
}
103
104
fn slot(&self, slot_id: u8) -> Result<Arc<DeviceSlot>> {
105
self.slots.slot(slot_id).ok_or(Error::BadSlotId(slot_id))
106
}
107
108
fn command_completion_callback(
109
interrupter: &Arc<Mutex<Interrupter>>,
110
completion_code: TrbCompletionCode,
111
slot_id: u8,
112
trb_addr: u64,
113
event: &Event,
114
) -> Result<()> {
115
interrupter
116
.lock()
117
.send_command_completion_trb(completion_code, slot_id, GuestAddress(trb_addr))
118
.map_err(Error::SendInterrupt)?;
119
event.signal().map_err(Error::WriteEvent)
120
}
121
122
fn enable_slot(&self, atrb: &AddressedTrb, event: Event) -> Result<()> {
123
for slot_id in 1..=MAX_SLOTS {
124
if self.slot(slot_id)?.enable() {
125
return CommandRingTrbHandler::command_completion_callback(
126
&self.interrupter,
127
TrbCompletionCode::Success,
128
slot_id,
129
atrb.gpa,
130
&event,
131
);
132
}
133
}
134
135
CommandRingTrbHandler::command_completion_callback(
136
&self.interrupter,
137
TrbCompletionCode::NoSlotsAvailableError,
138
0,
139
atrb.gpa,
140
&event,
141
)
142
}
143
144
fn disable_slot(&self, atrb: &AddressedTrb, event: Event) -> Result<()> {
145
let trb = atrb
146
.trb
147
.cast::<DisableSlotCommandTrb>()
148
.map_err(Error::CastTrb)?;
149
let slot_id = trb.get_slot_id();
150
if valid_slot_id(slot_id) {
151
let gpa = atrb.gpa;
152
let interrupter = self.interrupter.clone();
153
self.slots
154
.disable_slot(slot_id, move |completion_code| {
155
CommandRingTrbHandler::command_completion_callback(
156
&interrupter,
157
completion_code,
158
slot_id,
159
gpa,
160
&event,
161
)
162
.map_err(|e| {
163
error!("failed to run command completion callback: {}", e);
164
})
165
})
166
.map_err(Error::DisableSlot)
167
} else {
168
CommandRingTrbHandler::command_completion_callback(
169
&self.interrupter,
170
TrbCompletionCode::TrbError,
171
slot_id,
172
atrb.gpa,
173
&event,
174
)
175
}
176
}
177
178
fn address_device(&self, atrb: &AddressedTrb, event: Event) -> Result<()> {
179
let trb = atrb
180
.trb
181
.cast::<AddressDeviceCommandTrb>()
182
.map_err(Error::CastTrb)?;
183
let slot_id = trb.get_slot_id();
184
let completion_code = {
185
if valid_slot_id(slot_id) {
186
self.slot(slot_id)?
187
.set_address(trb)
188
.map_err(Error::SetAddress)?
189
} else {
190
TrbCompletionCode::TrbError
191
}
192
};
193
CommandRingTrbHandler::command_completion_callback(
194
&self.interrupter,
195
completion_code,
196
slot_id,
197
atrb.gpa,
198
&event,
199
)
200
}
201
202
fn configure_endpoint(&self, atrb: &AddressedTrb, event: Event) -> Result<()> {
203
let trb = atrb
204
.trb
205
.cast::<ConfigureEndpointCommandTrb>()
206
.map_err(Error::CastTrb)?;
207
let slot_id = trb.get_slot_id();
208
let completion_code = {
209
if valid_slot_id(slot_id) {
210
self.slot(slot_id)?
211
.configure_endpoint(trb)
212
.map_err(Error::ConfigEndpoint)?
213
} else {
214
TrbCompletionCode::TrbError
215
}
216
};
217
CommandRingTrbHandler::command_completion_callback(
218
&self.interrupter,
219
completion_code,
220
slot_id,
221
atrb.gpa,
222
&event,
223
)
224
}
225
226
fn evaluate_context(&self, atrb: &AddressedTrb, event: Event) -> Result<()> {
227
let trb = atrb
228
.trb
229
.cast::<EvaluateContextCommandTrb>()
230
.map_err(Error::CastTrb)?;
231
let slot_id = trb.get_slot_id();
232
let completion_code = {
233
if valid_slot_id(slot_id) {
234
self.slot(slot_id)?
235
.evaluate_context(trb)
236
.map_err(Error::EvaluateContext)?
237
} else {
238
TrbCompletionCode::TrbError
239
}
240
};
241
CommandRingTrbHandler::command_completion_callback(
242
&self.interrupter,
243
completion_code,
244
slot_id,
245
atrb.gpa,
246
&event,
247
)
248
}
249
250
fn reset_device(&self, atrb: &AddressedTrb, event: Event) -> Result<()> {
251
let trb = atrb
252
.trb
253
.cast::<ResetDeviceCommandTrb>()
254
.map_err(Error::CastTrb)?;
255
let slot_id = trb.get_slot_id();
256
if valid_slot_id(slot_id) {
257
let gpa = atrb.gpa;
258
let interrupter = self.interrupter.clone();
259
self.slots
260
.reset_slot(slot_id, move |completion_code| {
261
CommandRingTrbHandler::command_completion_callback(
262
&interrupter,
263
completion_code,
264
slot_id,
265
gpa,
266
&event,
267
)
268
.map_err(|e| {
269
error!("command completion callback failed: {}", e);
270
})
271
})
272
.map_err(Error::ResetSlot)
273
} else {
274
CommandRingTrbHandler::command_completion_callback(
275
&self.interrupter,
276
TrbCompletionCode::TrbError,
277
slot_id,
278
atrb.gpa,
279
&event,
280
)
281
}
282
}
283
284
fn stop_endpoint(&self, atrb: &AddressedTrb, event: Event) -> Result<()> {
285
let trb = atrb
286
.trb
287
.cast::<StopEndpointCommandTrb>()
288
.map_err(Error::CastTrb)?;
289
let slot_id = trb.get_slot_id();
290
let endpoint_id = trb.get_endpoint_id();
291
if valid_slot_id(slot_id) {
292
let gpa = atrb.gpa;
293
let interrupter = self.interrupter.clone();
294
self.slots
295
.stop_endpoint(slot_id, endpoint_id, move |completion_code| {
296
CommandRingTrbHandler::command_completion_callback(
297
&interrupter,
298
completion_code,
299
slot_id,
300
gpa,
301
&event,
302
)
303
.map_err(|e| {
304
error!("command completion callback failed: {}", e);
305
})
306
})
307
.map_err(Error::StopEndpoint)?;
308
Ok(())
309
} else {
310
error!("stop endpoint trb has invalid slot id {}", slot_id);
311
CommandRingTrbHandler::command_completion_callback(
312
&self.interrupter,
313
TrbCompletionCode::TrbError,
314
slot_id,
315
atrb.gpa,
316
&event,
317
)
318
}
319
}
320
321
fn reset_endpoint(&self, atrb: &AddressedTrb, event: Event) -> Result<()> {
322
let trb = atrb
323
.trb
324
.cast::<ResetEndpointCommandTrb>()
325
.map_err(Error::CastTrb)?;
326
let slot_id = trb.get_slot_id();
327
let endpoint_id = trb.get_endpoint_id();
328
if valid_slot_id(slot_id) {
329
let gpa = atrb.gpa;
330
let interrupter = self.interrupter.clone();
331
self.slots
332
.reset_endpoint(slot_id, endpoint_id, move |completion_code| {
333
CommandRingTrbHandler::command_completion_callback(
334
&interrupter,
335
completion_code,
336
slot_id,
337
gpa,
338
&event,
339
)
340
.map_err(|e| {
341
error!("command completion callback failed: {}", e);
342
})
343
})
344
.map_err(Error::StopEndpoint)?;
345
Ok(())
346
} else {
347
error!("reset endpoint trb has invalid slot id {}", slot_id);
348
CommandRingTrbHandler::command_completion_callback(
349
&self.interrupter,
350
TrbCompletionCode::TrbError,
351
slot_id,
352
atrb.gpa,
353
&event,
354
)
355
}
356
}
357
358
fn set_tr_dequeue_ptr(&self, atrb: &AddressedTrb, event: Event) -> Result<()> {
359
let trb = atrb
360
.trb
361
.cast::<SetTRDequeuePointerCommandTrb>()
362
.map_err(Error::CastTrb)?;
363
let slot_id = trb.get_slot_id();
364
let endpoint_id = trb.get_endpoint_id();
365
let stream_id = trb.get_stream_id();
366
// See Set TR Dequeue Pointer Trb in spec.
367
let dequeue_ptr = trb.get_dequeue_ptr().get_gpa().offset();
368
let completion_code = {
369
if valid_slot_id(slot_id) {
370
self.slot(slot_id)?
371
.set_tr_dequeue_ptr(endpoint_id, stream_id, dequeue_ptr)
372
.map_err(Error::SetDequeuePointer)?
373
} else {
374
error!("stop endpoint trb has invalid slot id {}", slot_id);
375
TrbCompletionCode::TrbError
376
}
377
};
378
CommandRingTrbHandler::command_completion_callback(
379
&self.interrupter,
380
completion_code,
381
slot_id,
382
atrb.gpa,
383
&event,
384
)
385
}
386
}
387
388
impl TransferDescriptorHandler for CommandRingTrbHandler {
389
fn handle_transfer_descriptor(
390
&self,
391
descriptor: TransferDescriptor,
392
complete_event: Event,
393
) -> anyhow::Result<()> {
394
// Command descriptor always consist of a single TRB.
395
assert_eq!(descriptor.len(), 1);
396
let atrb = &descriptor[0];
397
let command_result = match atrb.trb.get_trb_type() {
398
Ok(TrbType::EnableSlotCommand) => self.enable_slot(atrb, complete_event),
399
Ok(TrbType::DisableSlotCommand) => self.disable_slot(atrb, complete_event),
400
Ok(TrbType::AddressDeviceCommand) => self.address_device(atrb, complete_event),
401
Ok(TrbType::ConfigureEndpointCommand) => self.configure_endpoint(atrb, complete_event),
402
Ok(TrbType::EvaluateContextCommand) => self.evaluate_context(atrb, complete_event),
403
Ok(TrbType::ResetDeviceCommand) => self.reset_device(atrb, complete_event),
404
Ok(TrbType::NoopCommand) => CommandRingTrbHandler::command_completion_callback(
405
&self.interrupter,
406
TrbCompletionCode::Success,
407
0,
408
atrb.gpa,
409
&complete_event,
410
),
411
Ok(TrbType::ResetEndpointCommand) => self.reset_endpoint(atrb, complete_event),
412
Ok(TrbType::StopEndpointCommand) => self.stop_endpoint(atrb, complete_event),
413
Ok(TrbType::SetTRDequeuePointerCommand) => {
414
self.set_tr_dequeue_ptr(atrb, complete_event)
415
}
416
_ => {
417
warn!(
418
// We are not handling type 14,15,16. See table 6.4.6.
419
"Unexpected command ring trb type: {}",
420
atrb.trb
421
);
422
match self.interrupter.lock().send_command_completion_trb(
423
TrbCompletionCode::TrbError,
424
0,
425
GuestAddress(atrb.gpa),
426
) {
427
Err(e) => Err(Error::SendInterrupt(e)),
428
Ok(_) => complete_event.signal().map_err(Error::WriteEvent),
429
}
430
}
431
};
432
command_result.context("command ring TRB failed")
433
}
434
}
435
436