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