devices/usb/xhci/
command_ring_controller.rs

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
7use std::sync::Arc;
8
9use anyhow::Context;
10use base::error;
11use base::warn;
12use base::Error as SysError;
13use base::Event;
14use remain::sorted;
15use sync::Mutex;
16use thiserror::Error;
17use vm_memory::GuestAddress;
18use vm_memory::GuestMemory;
19
20use super::device_slot::DeviceSlot;
21use super::device_slot::DeviceSlots;
22use super::device_slot::Error as DeviceSlotError;
23use super::interrupter::Error as InterrupterError;
24use super::interrupter::Interrupter;
25use super::ring_buffer_controller::Error as RingBufferControllerError;
26use super::ring_buffer_controller::RingBufferController;
27use super::ring_buffer_controller::TransferDescriptorHandler;
28use super::xhci_abi::AddressDeviceCommandTrb;
29use super::xhci_abi::AddressedTrb;
30use super::xhci_abi::ConfigureEndpointCommandTrb;
31use super::xhci_abi::DisableSlotCommandTrb;
32use super::xhci_abi::Error as TrbError;
33use super::xhci_abi::EvaluateContextCommandTrb;
34use super::xhci_abi::ResetDeviceCommandTrb;
35use super::xhci_abi::ResetEndpointCommandTrb;
36use super::xhci_abi::SetTRDequeuePointerCommandTrb;
37use super::xhci_abi::StopEndpointCommandTrb;
38use super::xhci_abi::TransferDescriptor;
39use super::xhci_abi::TrbCast;
40use super::xhci_abi::TrbCompletionCode;
41use super::xhci_abi::TrbType;
42use super::xhci_regs::valid_slot_id;
43use super::xhci_regs::MAX_SLOTS;
44use crate::utils::EventLoop;
45
46#[sorted]
47#[derive(Error, Debug)]
48pub 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
73type Result<T> = std::result::Result<T, Error>;
74
75pub type CommandRingController = RingBufferController<CommandRingTrbHandler>;
76pub type CommandRingControllerError = RingBufferControllerError;
77
78impl 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
94pub struct CommandRingTrbHandler {
95    slots: DeviceSlots,
96    interrupter: Arc<Mutex<Interrupter>>,
97}
98
99impl 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
388impl 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}