devices/usb/xhci/
event_ring.rs

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
5use std::mem::size_of;
6use std::sync::atomic::fence;
7use std::sync::atomic::Ordering;
8
9use remain::sorted;
10use thiserror::Error;
11use vm_memory::GuestAddress;
12use vm_memory::GuestMemory;
13use vm_memory::GuestMemoryError;
14use zerocopy::IntoBytes;
15
16use super::xhci_abi::EventRingSegmentTableEntry;
17use super::xhci_abi::Trb;
18
19#[sorted]
20#[derive(Error, Debug)]
21pub enum Error {
22    #[error("event ring has a bad enqueue pointer: {0}")]
23    BadEnqueuePointer(GuestAddress),
24    #[error("event ring has a bad seg table addr: {0}")]
25    BadSegTableAddress(GuestAddress),
26    #[error("event ring has a bad seg table index: {0}")]
27    BadSegTableIndex(u16),
28    #[error("event ring is full")]
29    EventRingFull,
30    #[error("event ring cannot read from guest memory: {0}")]
31    MemoryRead(GuestMemoryError),
32    #[error("event ring cannot write to guest memory: {0}")]
33    MemoryWrite(GuestMemoryError),
34    #[error("event ring is uninitialized")]
35    Uninitialized,
36}
37
38type Result<T> = std::result::Result<T, Error>;
39
40/// Event rings are segmented circular buffers used to pass event TRBs from the xHCI device back to
41/// the guest.  Each event ring is associated with a single interrupter.  See section 4.9.4 of the
42/// xHCI specification for more details.
43/// This implementation is only for primary interrupter. Please review xhci spec before using it
44/// for secondary.
45pub struct EventRing {
46    mem: GuestMemory,
47    segment_table_size: u16,
48    segment_table_base_address: GuestAddress,
49    current_segment_index: u16,
50    trb_count: u16,
51    enqueue_pointer: GuestAddress,
52    dequeue_pointer: GuestAddress,
53    producer_cycle_state: bool,
54}
55
56impl EventRing {
57    /// Create an empty, uninitialized event ring.
58    pub fn new(mem: GuestMemory) -> Self {
59        EventRing {
60            mem,
61            segment_table_size: 0,
62            segment_table_base_address: GuestAddress(0),
63            current_segment_index: 0,
64            enqueue_pointer: GuestAddress(0),
65            dequeue_pointer: GuestAddress(0),
66            trb_count: 0,
67            // As specified in xHCI spec 4.9.4, cycle state should be initialized to 1.
68            producer_cycle_state: true,
69        }
70    }
71
72    /// This function implements left side of xHCI spec, Figure 4-12.
73    pub fn add_event(&mut self, mut trb: Trb) -> Result<()> {
74        self.check_inited()?;
75        if self.is_full()? {
76            return Err(Error::EventRingFull);
77        }
78        // Event is write twice to avoid race condition.
79        // Guest kernel use cycle bit to check ownership, thus we should write cycle last.
80        trb.set_cycle(!self.producer_cycle_state);
81        self.mem
82            .write_obj_at_addr(trb, self.enqueue_pointer)
83            .map_err(Error::MemoryWrite)?;
84
85        // Updating the cycle state bit should always happen after updating other parts.
86        fence(Ordering::SeqCst);
87
88        trb.set_cycle(self.producer_cycle_state);
89
90        // Offset of cycle state byte.
91        const CYCLE_STATE_OFFSET: usize = 12usize;
92        let data = trb.as_bytes();
93        // Trb contains 4 dwords, the last one contains cycle bit.
94        let cycle_bit_dword = &data[CYCLE_STATE_OFFSET..];
95        let address = self.enqueue_pointer;
96        let address = address
97            .checked_add(CYCLE_STATE_OFFSET as u64)
98            .ok_or(Error::BadEnqueuePointer(self.enqueue_pointer))?;
99        self.mem
100            .write_all_at_addr(cycle_bit_dword, address)
101            .map_err(Error::MemoryWrite)?;
102
103        xhci_trace!(
104            "event write to pointer {:#x}, trb_count {}, {}",
105            self.enqueue_pointer.0,
106            self.trb_count,
107            trb
108        );
109        self.enqueue_pointer = match self.enqueue_pointer.checked_add(size_of::<Trb>() as u64) {
110            Some(addr) => addr,
111            None => return Err(Error::BadEnqueuePointer(self.enqueue_pointer)),
112        };
113        self.trb_count -= 1;
114        if self.trb_count == 0 {
115            self.current_segment_index += 1;
116            if self.current_segment_index == self.segment_table_size {
117                self.producer_cycle_state ^= true;
118                self.current_segment_index = 0;
119            }
120            self.load_current_seg_table_entry()?;
121        }
122        Ok(())
123    }
124
125    /// Set segment table size.
126    pub fn set_seg_table_size(&mut self, size: u16) -> Result<()> {
127        xhci_trace!("set_seg_table_size({:#x})", size);
128        self.segment_table_size = size;
129        self.try_reconfigure_event_ring()
130    }
131
132    /// Set segment table base addr.
133    pub fn set_seg_table_base_addr(&mut self, addr: GuestAddress) -> Result<()> {
134        xhci_trace!("set_seg_table_base_addr({:#x})", addr.0);
135        self.segment_table_base_address = addr;
136        self.try_reconfigure_event_ring()
137    }
138
139    /// Set dequeue pointer.
140    pub fn set_dequeue_pointer(&mut self, addr: GuestAddress) {
141        xhci_trace!("set_dequeue_pointer({:#x})", addr.0);
142        self.dequeue_pointer = addr;
143    }
144
145    /// Check if event ring is empty.
146    pub fn is_empty(&self) -> bool {
147        self.enqueue_pointer == self.dequeue_pointer
148    }
149
150    /// Event ring is considered full when there is only space for one last TRB. In this case, xHC
151    /// should write an error Trb and do a bunch of handlings. See spec, figure 4-12 for more
152    /// details.
153    /// For now, we just check event ring full and fail (as it's unlikely to happen).
154    pub fn is_full(&self) -> Result<bool> {
155        if self.trb_count == 1 {
156            let next_erst_idx =
157                Self::next_seg_table_index(self.current_segment_index, self.segment_table_size)?;
158            let erst_entry = self.read_seg_table_entry(next_erst_idx)?;
159            Ok(self.dequeue_pointer.0 == erst_entry.get_ring_segment_base_address())
160        } else {
161            Self::is_non_boundary_full(self.enqueue_pointer, self.dequeue_pointer)
162        }
163    }
164
165    fn next_seg_table_index(current_segment_index: u16, segment_table_size: u16) -> Result<u16> {
166        if segment_table_size == 0 {
167            return Err(Error::Uninitialized);
168        }
169        Ok(((current_segment_index as u32 + 1) % (segment_table_size as u32)) as u16)
170    }
171
172    fn is_non_boundary_full(
173        enqueue_pointer: GuestAddress,
174        dequeue_pointer: GuestAddress,
175    ) -> Result<bool> {
176        let next_enq = enqueue_pointer
177            .checked_add(size_of::<Trb>() as u64)
178            .ok_or(Error::BadEnqueuePointer(enqueue_pointer))?;
179        Ok(dequeue_pointer == next_enq)
180    }
181
182    /// Try to init event ring. Will fail if seg table size/address are invalid.
183    fn try_reconfigure_event_ring(&mut self) -> Result<()> {
184        if self.segment_table_size == 0 || self.segment_table_base_address.0 == 0 {
185            return Ok(());
186        }
187        if self.current_segment_index >= self.segment_table_size {
188            self.current_segment_index = 0;
189        }
190        self.load_current_seg_table_entry()
191    }
192
193    // Check if this event ring is inited.
194    fn check_inited(&self) -> Result<()> {
195        Self::check_inited_params(
196            self.segment_table_size,
197            self.segment_table_base_address,
198            self.enqueue_pointer,
199            self.trb_count,
200        )
201    }
202
203    fn check_inited_params(
204        segment_table_size: u16,
205        segment_table_base_address: GuestAddress,
206        enqueue_pointer: GuestAddress,
207        trb_count: u16,
208    ) -> Result<()> {
209        if segment_table_size == 0
210            || segment_table_base_address == GuestAddress(0)
211            || enqueue_pointer == GuestAddress(0)
212            || trb_count == 0
213        {
214            return Err(Error::Uninitialized);
215        }
216        Ok(())
217    }
218
219    // Load entry of current seg table.
220    fn load_current_seg_table_entry(&mut self) -> Result<()> {
221        let entry = self.read_seg_table_entry(self.current_segment_index)?;
222        self.enqueue_pointer = GuestAddress(entry.get_ring_segment_base_address());
223        self.trb_count = entry.get_ring_segment_size();
224        Ok(())
225    }
226
227    // Get seg table entry at index.
228    fn read_seg_table_entry(&self, index: u16) -> Result<EventRingSegmentTableEntry> {
229        let seg_table_addr = self.get_seg_table_addr(index)?;
230        // TODO(jkwang) We can refactor GuestMemory to allow in-place memory operation.
231        self.mem
232            .read_obj_from_addr(seg_table_addr)
233            .map_err(Error::MemoryRead)
234    }
235
236    // Get seg table addr at index.
237    fn get_seg_table_addr(&self, index: u16) -> Result<GuestAddress> {
238        Self::calc_seg_table_addr(
239            self.segment_table_base_address,
240            self.segment_table_size,
241            index,
242        )
243    }
244
245    fn calc_seg_table_addr(
246        base_address: GuestAddress,
247        segment_table_size: u16,
248        index: u16,
249    ) -> Result<GuestAddress> {
250        if index >= segment_table_size {
251            return Err(Error::BadSegTableIndex(index));
252        }
253        base_address
254            .checked_add((size_of::<EventRingSegmentTableEntry>() as u64) * (index as u64))
255            .ok_or(Error::BadSegTableAddress(base_address))
256    }
257}
258
259#[cfg(test)]
260mod test {
261    use std::mem::size_of;
262
263    use base::pagesize;
264
265    use super::*;
266
267    #[test]
268    fn test_uninited() {
269        let gm = GuestMemory::new(&[(GuestAddress(0), pagesize() as u64)]).unwrap();
270        let mut er = EventRing::new(gm);
271        let trb = Trb::new();
272        match er.add_event(trb).err().unwrap() {
273            Error::Uninitialized => {}
274            _ => panic!("unexpected error"),
275        }
276        assert_eq!(er.is_empty(), true);
277        assert_eq!(er.is_full().unwrap(), false);
278    }
279
280    #[test]
281    fn test_event_ring() {
282        let trb_size = size_of::<Trb>() as u64;
283        let gm = GuestMemory::new(&[(GuestAddress(0), pagesize() as u64)]).unwrap();
284        let mut er = EventRing::new(gm.clone());
285        let mut st_entries = [EventRingSegmentTableEntry::new(); 3];
286        st_entries[0].set_ring_segment_base_address(0x100);
287        st_entries[0].set_ring_segment_size(3);
288        st_entries[1].set_ring_segment_base_address(0x200);
289        st_entries[1].set_ring_segment_size(3);
290        st_entries[2].set_ring_segment_base_address(0x300);
291        st_entries[2].set_ring_segment_size(3);
292        gm.write_obj_at_addr(st_entries[0], GuestAddress(0x8))
293            .unwrap();
294        gm.write_obj_at_addr(
295            st_entries[1],
296            GuestAddress(0x8 + size_of::<EventRingSegmentTableEntry>() as u64),
297        )
298        .unwrap();
299        gm.write_obj_at_addr(
300            st_entries[2],
301            GuestAddress(0x8 + 2 * size_of::<EventRingSegmentTableEntry>() as u64),
302        )
303        .unwrap();
304        // Init event ring. Must init after segment tables writting.
305        er.set_seg_table_size(3).unwrap();
306        er.set_seg_table_base_addr(GuestAddress(0x8)).unwrap();
307        er.set_dequeue_pointer(GuestAddress(0x100));
308
309        let mut trb = Trb::new();
310
311        // Fill first table.
312        trb.set_control(1);
313        assert_eq!(er.is_empty(), true);
314        assert_eq!(er.is_full().unwrap(), false);
315        assert!(er.add_event(trb).is_ok());
316        assert_eq!(er.is_full().unwrap(), false);
317        assert_eq!(er.is_empty(), false);
318        let t: Trb = gm.read_obj_from_addr(GuestAddress(0x100)).unwrap();
319        assert_eq!(t.get_control(), 1);
320        assert_eq!(t.get_cycle(), true);
321
322        trb.set_control(2);
323        assert!(er.add_event(trb).is_ok());
324        assert_eq!(er.is_full().unwrap(), false);
325        assert_eq!(er.is_empty(), false);
326        let t: Trb = gm
327            .read_obj_from_addr(GuestAddress(0x100 + trb_size))
328            .unwrap();
329        assert_eq!(t.get_control(), 2);
330        assert_eq!(t.get_cycle(), true);
331
332        trb.set_control(3);
333        assert!(er.add_event(trb).is_ok());
334        assert_eq!(er.is_full().unwrap(), false);
335        assert_eq!(er.is_empty(), false);
336        let t: Trb = gm
337            .read_obj_from_addr(GuestAddress(0x100 + 2 * trb_size))
338            .unwrap();
339        assert_eq!(t.get_control(), 3);
340        assert_eq!(t.get_cycle(), true);
341
342        // Fill second table.
343        trb.set_control(4);
344        assert!(er.add_event(trb).is_ok());
345        assert_eq!(er.is_full().unwrap(), false);
346        assert_eq!(er.is_empty(), false);
347        let t: Trb = gm.read_obj_from_addr(GuestAddress(0x200)).unwrap();
348        assert_eq!(t.get_control(), 4);
349        assert_eq!(t.get_cycle(), true);
350
351        trb.set_control(5);
352        assert!(er.add_event(trb).is_ok());
353        assert_eq!(er.is_full().unwrap(), false);
354        assert_eq!(er.is_empty(), false);
355        let t: Trb = gm
356            .read_obj_from_addr(GuestAddress(0x200 + trb_size))
357            .unwrap();
358        assert_eq!(t.get_control(), 5);
359        assert_eq!(t.get_cycle(), true);
360
361        trb.set_control(6);
362        assert!(er.add_event(trb).is_ok());
363        assert_eq!(er.is_full().unwrap(), false);
364        assert_eq!(er.is_empty(), false);
365        let t: Trb = gm
366            .read_obj_from_addr(GuestAddress(0x200 + 2 * trb_size))
367            .unwrap();
368        assert_eq!(t.get_control(), 6);
369        assert_eq!(t.get_cycle(), true);
370
371        // Fill third table.
372        trb.set_control(7);
373        assert!(er.add_event(trb).is_ok());
374        assert_eq!(er.is_full().unwrap(), false);
375        assert_eq!(er.is_empty(), false);
376        let t: Trb = gm.read_obj_from_addr(GuestAddress(0x300)).unwrap();
377        assert_eq!(t.get_control(), 7);
378        assert_eq!(t.get_cycle(), true);
379
380        trb.set_control(8);
381        assert!(er.add_event(trb).is_ok());
382        // There is only one last trb. Considered full.
383        assert_eq!(er.is_full().unwrap(), true);
384        assert_eq!(er.is_empty(), false);
385        let t: Trb = gm
386            .read_obj_from_addr(GuestAddress(0x300 + trb_size))
387            .unwrap();
388        assert_eq!(t.get_control(), 8);
389        assert_eq!(t.get_cycle(), true);
390
391        // Add the last trb will result in error.
392        match er.add_event(trb) {
393            Err(Error::EventRingFull) => {}
394            _ => panic!("er should be full"),
395        };
396
397        // Dequeue one trb.
398        er.set_dequeue_pointer(GuestAddress(0x100 + trb_size));
399        assert_eq!(er.is_full().unwrap(), false);
400        assert_eq!(er.is_empty(), false);
401
402        // Fill the last trb of the third table.
403        trb.set_control(9);
404        assert!(er.add_event(trb).is_ok());
405        // There is only one last trb. Considered full.
406        assert_eq!(er.is_full().unwrap(), true);
407        assert_eq!(er.is_empty(), false);
408        let t: Trb = gm
409            .read_obj_from_addr(GuestAddress(0x300 + trb_size))
410            .unwrap();
411        assert_eq!(t.get_control(), 8);
412        assert_eq!(t.get_cycle(), true);
413
414        // Add the last trb will result in error.
415        match er.add_event(trb) {
416            Err(Error::EventRingFull) => {}
417            _ => panic!("er should be full"),
418        };
419
420        // Dequeue until empty.
421        er.set_dequeue_pointer(GuestAddress(0x100));
422        assert_eq!(er.is_full().unwrap(), false);
423        assert_eq!(er.is_empty(), true);
424
425        // Fill first table again.
426        trb.set_control(10);
427        assert!(er.add_event(trb).is_ok());
428        assert_eq!(er.is_full().unwrap(), false);
429        assert_eq!(er.is_empty(), false);
430        let t: Trb = gm.read_obj_from_addr(GuestAddress(0x100)).unwrap();
431        assert_eq!(t.get_control(), 10);
432        // cycle bit should be reversed.
433        assert_eq!(t.get_cycle(), false);
434
435        trb.set_control(11);
436        assert!(er.add_event(trb).is_ok());
437        assert_eq!(er.is_full().unwrap(), false);
438        assert_eq!(er.is_empty(), false);
439        let t: Trb = gm
440            .read_obj_from_addr(GuestAddress(0x100 + trb_size))
441            .unwrap();
442        assert_eq!(t.get_control(), 11);
443        assert_eq!(t.get_cycle(), false);
444
445        trb.set_control(12);
446        assert!(er.add_event(trb).is_ok());
447        assert_eq!(er.is_full().unwrap(), false);
448        assert_eq!(er.is_empty(), false);
449        let t: Trb = gm
450            .read_obj_from_addr(GuestAddress(0x100 + 2 * trb_size))
451            .unwrap();
452        assert_eq!(t.get_control(), 12);
453        assert_eq!(t.get_cycle(), false);
454    }
455}
456
457#[cfg(kani)]
458mod kani_proofs {
459    use super::*;
460
461    #[kani::proof]
462    fn proof_event_ring_seg_table_and_state() {
463        let seg_table_size: u16 = kani::any();
464        let base_addr: u64 = kani::any();
465        let cur_idx: u16 = kani::any();
466        let trb_count: u16 = kani::any();
467        let enq_addr: u64 = kani::any();
468        let deq_addr: u64 = kani::any();
469        let lookup_idx: u16 = kani::any();
470
471        // 1. Verify calc_seg_table_addr bounds check and 64-bit multiplication without u16
472        //    overflow.
473        let addr_res =
474            EventRing::calc_seg_table_addr(GuestAddress(base_addr), seg_table_size, lookup_idx);
475        if lookup_idx >= seg_table_size {
476            assert!(matches!(addr_res, Err(Error::BadSegTableIndex(_))));
477        } else {
478            let expected_offset =
479                (size_of::<EventRingSegmentTableEntry>() as u64) * (lookup_idx as u64);
480            match base_addr.checked_add(expected_offset) {
481                Some(expected_addr) => {
482                    assert!(matches!(addr_res, Ok(addr) if addr.0 == expected_addr));
483                }
484                None => {
485                    assert!(matches!(addr_res, Err(Error::BadSegTableAddress(_))));
486                }
487            }
488        }
489
490        // 2. Verify check_inited_params rejects uninitialized state including trb_count == 0.
491        let inited = EventRing::check_inited_params(
492            seg_table_size,
493            GuestAddress(base_addr),
494            GuestAddress(enq_addr),
495            trb_count,
496        );
497        if seg_table_size == 0 || base_addr == 0 || enq_addr == 0 || trb_count == 0 {
498            assert!(matches!(inited, Err(Error::Uninitialized)));
499        } else {
500            assert!(inited.is_ok());
501        }
502
503        // 3. Verify next_seg_table_index and is_non_boundary_full never panic or overflow.
504        let next_idx_res = EventRing::next_seg_table_index(cur_idx, seg_table_size);
505        if seg_table_size == 0 {
506            assert!(matches!(next_idx_res, Err(Error::Uninitialized)));
507        } else {
508            let Ok(next_idx) = next_idx_res else {
509                unreachable!();
510            };
511            assert!(next_idx < seg_table_size);
512            if cur_idx < seg_table_size {
513                let expected = if cur_idx + 1 == seg_table_size {
514                    0
515                } else {
516                    cur_idx + 1
517                };
518                assert!(next_idx == expected);
519            }
520        }
521
522        let full_res =
523            EventRing::is_non_boundary_full(GuestAddress(enq_addr), GuestAddress(deq_addr));
524        match enq_addr.checked_add(size_of::<Trb>() as u64) {
525            Some(next_enq) => {
526                assert!(matches!(full_res, Ok(full) if full == (deq_addr == next_enq)));
527            }
528            None => {
529                assert!(matches!(full_res, Err(Error::BadEnqueuePointer(_))));
530            }
531        }
532    }
533}