1 /** @file
2   Debug Port Library implementation based on usb3 debug port.
3 
4   Copyright (c) 2014 - 2018, Intel Corporation. All rights reserved.<BR>
5   SPDX-License-Identifier: BSD-2-Clause-Patent
6 
7 **/
8 #include "DebugCommunicationLibUsb3Internal.h"
9 
10 /**
11   Synchronize the specified transfer ring to update the enqueue and dequeue pointer.
12 
13   @param  Handle      Debug port handle.
14   @param  TrsRing     The transfer ring to sync.
15 
16   @retval EFI_SUCCESS The transfer ring is synchronized successfully.
17 
18 **/
19 EFI_STATUS
20 EFIAPI
XhcSyncTrsRing(IN USB3_DEBUG_PORT_HANDLE * Handle,IN TRANSFER_RING * TrsRing)21 XhcSyncTrsRing (
22   IN USB3_DEBUG_PORT_HANDLE    *Handle,
23   IN TRANSFER_RING             *TrsRing
24   )
25 {
26   UINTN               Index;
27   TRB_TEMPLATE        *TrsTrb;
28   UINT32              CycleBit;
29 
30   ASSERT (TrsRing != NULL);
31 
32   //
33   // Calculate the latest RingEnqueue and RingPCS
34   //
35   TrsTrb = (TRB_TEMPLATE *)(UINTN) TrsRing->RingEnqueue;
36 
37   ASSERT (TrsTrb != NULL);
38 
39   for (Index = 0; Index < TrsRing->TrbNumber; Index++) {
40     if (TrsTrb->CycleBit != (TrsRing->RingPCS & BIT0)) {
41       break;
42     }
43     TrsTrb++;
44     if ((UINT8) TrsTrb->Type == TRB_TYPE_LINK) {
45       ASSERT (((LINK_TRB*)TrsTrb)->TC != 0);
46       //
47       // set cycle bit in Link TRB as normal
48       //
49       ((LINK_TRB*)TrsTrb)->CycleBit = TrsRing->RingPCS & BIT0;
50       //
51       // Toggle PCS maintained by software
52       //
53       TrsRing->RingPCS = (TrsRing->RingPCS & BIT0) ? 0 : 1;
54       TrsTrb           = (TRB_TEMPLATE *)(UINTN)((TrsTrb->Parameter1 | LShiftU64 ((UINT64)TrsTrb->Parameter2, 32)) & ~0x0F);
55     }
56   }
57   ASSERT (Index != TrsRing->TrbNumber);
58 
59   if ((EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb != TrsRing->RingEnqueue) {
60     TrsRing->RingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN) TrsTrb;
61   }
62 
63   //
64   // Clear the Trb context for enqueue, but reserve the PCS bit which indicates free Trb.
65   //
66   CycleBit = TrsTrb->CycleBit;
67   ZeroMem (TrsTrb, sizeof (TRB_TEMPLATE));
68   TrsTrb->CycleBit = CycleBit;
69 
70   return EFI_SUCCESS;
71 }
72 
73 /**
74   Synchronize the specified event ring to update the enqueue and dequeue pointer.
75 
76   @param  Handle      Debug port handle.
77   @param  EvtRing     The event ring to sync.
78 
79   @retval EFI_SUCCESS The event ring is synchronized successfully.
80 
81 **/
82 EFI_STATUS
83 EFIAPI
XhcSyncEventRing(IN USB3_DEBUG_PORT_HANDLE * Handle,IN EVENT_RING * EvtRing)84 XhcSyncEventRing (
85   IN USB3_DEBUG_PORT_HANDLE  *Handle,
86   IN EVENT_RING                *EvtRing
87   )
88 {
89   UINTN               Index;
90   TRB_TEMPLATE        *EvtTrb1;
91 
92   ASSERT (EvtRing != NULL);
93 
94   //
95   // Calculate the EventRingEnqueue and EventRingCCS.
96   // Note: only support single Segment
97   //
98   EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;
99 
100   for (Index = 0; Index < EvtRing->TrbNumber; Index++) {
101     if (EvtTrb1->CycleBit != EvtRing->EventRingCCS) {
102       break;
103     }
104 
105     EvtTrb1++;
106 
107     if ((UINTN)EvtTrb1 >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {
108       EvtTrb1 = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingSeg0;
109       EvtRing->EventRingCCS = (EvtRing->EventRingCCS) ? 0 : 1;
110     }
111   }
112 
113   if (Index < EvtRing->TrbNumber) {
114     EvtRing->EventRingEnqueue = (EFI_PHYSICAL_ADDRESS)(UINTN)EvtTrb1;
115   } else {
116     ASSERT (FALSE);
117   }
118 
119   return EFI_SUCCESS;
120 }
121 
122 /**
123   Check if there is a new generated event.
124 
125   @param  Handle        Debug port handle.
126   @param  EvtRing       The event ring to check.
127   @param  NewEvtTrb     The new event TRB found.
128 
129   @retval EFI_SUCCESS   Found a new event TRB at the event ring.
130   @retval EFI_NOT_READY The event ring has no new event.
131 
132 **/
133 EFI_STATUS
134 EFIAPI
XhcCheckNewEvent(IN USB3_DEBUG_PORT_HANDLE * Handle,IN EVENT_RING * EvtRing,OUT TRB_TEMPLATE ** NewEvtTrb)135 XhcCheckNewEvent (
136   IN  USB3_DEBUG_PORT_HANDLE   *Handle,
137   IN  EVENT_RING               *EvtRing,
138   OUT TRB_TEMPLATE             **NewEvtTrb
139   )
140 {
141   EFI_STATUS          Status;
142 
143   ASSERT (EvtRing != NULL);
144 
145   *NewEvtTrb = (TRB_TEMPLATE *)(UINTN) EvtRing->EventRingDequeue;
146 
147   if (EvtRing->EventRingDequeue == EvtRing->EventRingEnqueue) {
148     return EFI_NOT_READY;
149   }
150 
151   Status = EFI_SUCCESS;
152 
153   EvtRing->EventRingDequeue += sizeof (TRB_TEMPLATE);
154   //
155   // If the dequeue pointer is beyond the ring, then roll-back it to the begining of the ring.
156   //
157   if ((UINTN)EvtRing->EventRingDequeue >= ((UINTN) EvtRing->EventRingSeg0 + sizeof (TRB_TEMPLATE) * EvtRing->TrbNumber)) {
158     EvtRing->EventRingDequeue = EvtRing->EventRingSeg0;
159   }
160 
161   return Status;
162 }
163 
164 /**
165   Check if the Trb is a transaction of the URB.
166 
167   @param Ring   The transfer ring to be checked.
168   @param Trb    The TRB to be checked.
169 
170   @retval TRUE  It is a transaction of the URB.
171   @retval FALSE It is not any transaction of the URB.
172 
173 **/
174 BOOLEAN
IsTrbInTrsRing(IN TRANSFER_RING * Ring,IN TRB_TEMPLATE * Trb)175 IsTrbInTrsRing (
176   IN  TRANSFER_RING       *Ring,
177   IN  TRB_TEMPLATE        *Trb
178   )
179 {
180   TRB_TEMPLATE  *CheckedTrb;
181   UINTN         Index;
182 
183   CheckedTrb = (TRB_TEMPLATE *)(UINTN) Ring->RingSeg0;
184 
185   ASSERT (Ring->TrbNumber == TR_RING_TRB_NUMBER);
186 
187   for (Index = 0; Index < Ring->TrbNumber; Index++) {
188     if (Trb == CheckedTrb) {
189       return TRUE;
190     }
191     CheckedTrb++;
192   }
193 
194   return FALSE;
195 }
196 
197 /**
198   Check the URB's execution result and update the URB's
199   result accordingly.
200 
201   @param  Handle          Debug port handle.
202   @param  Urb             The URB to check result.
203 
204 **/
205 VOID
XhcCheckUrbResult(IN USB3_DEBUG_PORT_HANDLE * Handle,IN URB * Urb)206 XhcCheckUrbResult (
207   IN  USB3_DEBUG_PORT_HANDLE *Handle,
208   IN  URB                      *Urb
209   )
210 {
211   EVT_TRB_TRANSFER        *EvtTrb;
212   TRB_TEMPLATE            *TRBPtr;
213   UINTN                   Index;
214   EFI_STATUS              Status;
215   URB                     *CheckedUrb;
216   UINT64                  XhcDequeue;
217   UINT32                  High;
218   UINT32                  Low;
219 
220   ASSERT ((Handle != NULL) && (Urb != NULL));
221 
222   if (Urb->Finished) {
223     goto EXIT;
224   }
225 
226   EvtTrb = NULL;
227 
228   //
229   // Traverse the event ring to find out all new events from the previous check.
230   //
231   XhcSyncEventRing (Handle, &Handle->EventRing);
232 
233   for (Index = 0; Index < Handle->EventRing.TrbNumber; Index++) {
234 
235     Status = XhcCheckNewEvent (Handle, &Handle->EventRing, ((TRB_TEMPLATE **)&EvtTrb));
236     if (Status == EFI_NOT_READY) {
237       //
238       // All new events are handled, return directly.
239       //
240       goto EXIT;
241     }
242 
243     if ((EvtTrb->Type != TRB_TYPE_COMMAND_COMPLT_EVENT) && (EvtTrb->Type != TRB_TYPE_TRANS_EVENT)) {
244       continue;
245     }
246 
247     TRBPtr = (TRB_TEMPLATE *)(UINTN)(EvtTrb->TRBPtrLo | LShiftU64 ((UINT64) EvtTrb->TRBPtrHi, 32));
248 
249     if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Urb->Ring), TRBPtr)) {
250       CheckedUrb = Urb;
251     } else if (IsTrbInTrsRing ((TRANSFER_RING *)(UINTN)(Handle->UrbIn.Ring), TRBPtr)) {
252       //
253       // If it is read event and it should be generated by poll, and current operation is write, we need save data into internal buffer.
254       // Internal buffer is used by next read.
255       //
256       Handle->DataCount = (UINT8) (Handle->UrbIn.DataLen - EvtTrb->Length);
257       CopyMem ((VOID *)(UINTN)Handle->Data, (VOID *)(UINTN)Handle->UrbIn.Data, Handle->DataCount);
258       //
259       // Fill this TRB complete with CycleBit, otherwise next read will fail with old TRB.
260       //
261       TRBPtr->CycleBit = (TRBPtr->CycleBit & BIT0) ? 0 : 1;
262       continue;
263     } else {
264       continue;
265     }
266 
267     if ((EvtTrb->Completecode == TRB_COMPLETION_SHORT_PACKET) ||
268         (EvtTrb->Completecode == TRB_COMPLETION_SUCCESS)) {
269       //
270       // The length of data which were transferred.
271       //
272       CheckedUrb->Completed += (((TRANSFER_TRB_NORMAL*)TRBPtr)->Length - EvtTrb->Length);
273     } else {
274       CheckedUrb->Result  |= EFI_USB_ERR_TIMEOUT;
275     }
276     //
277     // This Urb has been processed
278     //
279     CheckedUrb->Finished = TRUE;
280   }
281 
282 EXIT:
283   //
284   // Advance event ring to last available entry
285   //
286   // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
287   // So divide it to two 32-bytes width register access.
288   //
289   Low  = XhcReadDebugReg (Handle, XHC_DC_DCERDP);
290   High = XhcReadDebugReg (Handle, XHC_DC_DCERDP + 4);
291   XhcDequeue = (UINT64)(LShiftU64((UINT64)High, 32) | Low);
292 
293   if ((XhcDequeue & (~0x0F)) != ((UINT64)(UINTN)Handle->EventRing.EventRingDequeue & (~0x0F))) {
294     //
295     // Some 3rd party XHCI external cards don't support single 64-bytes width register access,
296     // So divide it to two 32-bytes width register access.
297     //
298     XhcWriteDebugReg (Handle, XHC_DC_DCERDP, XHC_LOW_32BIT (Handle->EventRing.EventRingDequeue));
299     XhcWriteDebugReg (Handle, XHC_DC_DCERDP + 4, XHC_HIGH_32BIT (Handle->EventRing.EventRingDequeue));
300   }
301 }
302 
303 /**
304   Ring the door bell to notify XHCI there is a transaction to be executed.
305 
306   @param  Handle        Debug port handle.
307   @param  Urb           The pointer to URB.
308 
309   @retval EFI_SUCCESS   Successfully ring the door bell.
310 
311 **/
312 EFI_STATUS
313 EFIAPI
XhcRingDoorBell(IN USB3_DEBUG_PORT_HANDLE * Handle,IN URB * Urb)314 XhcRingDoorBell (
315   IN USB3_DEBUG_PORT_HANDLE    *Handle,
316   IN URB                       *Urb
317   )
318 {
319   UINT32      Dcdb;
320 
321   //
322   // 7.6.8.2 DCDB Register
323   //
324   Dcdb = (Urb->Direction == EfiUsbDataIn) ? 0x100 : 0x0;
325 
326   XhcWriteDebugReg (
327     Handle,
328     XHC_DC_DCDB,
329     Dcdb
330     );
331 
332   return EFI_SUCCESS;
333 }
334 
335 /**
336   Execute the transfer by polling the URB. This is a synchronous operation.
337 
338   @param  Handle            Debug port handle.
339   @param  Urb               The URB to execute.
340   @param  Timeout           The time to wait before abort, in microsecond.
341 
342 **/
343 VOID
XhcExecTransfer(IN USB3_DEBUG_PORT_HANDLE * Handle,IN URB * Urb,IN UINTN Timeout)344 XhcExecTransfer (
345   IN  USB3_DEBUG_PORT_HANDLE   *Handle,
346   IN  URB                      *Urb,
347   IN  UINTN                    Timeout
348   )
349 {
350   TRANSFER_RING           *Ring;
351   TRB_TEMPLATE            *Trb;
352   UINTN                   Loop;
353   UINTN                   Index;
354 
355   Loop = Timeout / XHC_DEBUG_PORT_1_MILLISECOND;
356   if (Timeout == 0) {
357     Loop = 0xFFFFFFFF;
358   }
359   XhcRingDoorBell (Handle, Urb);
360   //
361   // Event Ring Not Empty bit can only be set to 1 by XHC after ringing door bell with some delay.
362   //
363   for (Index = 0; Index < Loop; Index++) {
364     XhcCheckUrbResult (Handle, Urb);
365     if (Urb->Finished) {
366       break;
367     }
368     MicroSecondDelay (XHC_DEBUG_PORT_1_MILLISECOND);
369   }
370   if (Index == Loop) {
371     //
372     // If time out occurs.
373     //
374     Urb->Result |= EFI_USB_ERR_TIMEOUT;
375   }
376   //
377   // If URB transfer is error, restore transfer ring to original value before URB transfer
378   // This will make the current transfer TRB is always at the latest unused one in transfer ring.
379   //
380   Ring = (TRANSFER_RING *)(UINTN) Urb->Ring;
381   if ((Urb->Result != EFI_USB_NOERROR) && (Urb->Direction == EfiUsbDataIn)) {
382     //
383     // Adjust Enqueue pointer
384     //
385     Ring->RingEnqueue = Urb->Trb;
386     //
387     // Clear CCS flag for next use
388     //
389     Trb = (TRB_TEMPLATE *)(UINTN) Urb->Trb;
390     Trb->CycleBit = ((~Ring->RingPCS) & BIT0);
391   } else {
392     //
393     // Update transfer ring for next transfer.
394     //
395     XhcSyncTrsRing (Handle, Ring);
396   }
397 }
398 
399 /**
400   Create a transfer TRB.
401 
402   @param  Handle  Debug port handle.
403   @param  Urb     The urb used to construct the transfer TRB.
404 
405   @return Created TRB or NULL
406 
407 **/
408 EFI_STATUS
XhcCreateTransferTrb(IN USB3_DEBUG_PORT_HANDLE * Handle,IN URB * Urb)409 XhcCreateTransferTrb (
410   IN USB3_DEBUG_PORT_HANDLE   *Handle,
411   IN URB                        *Urb
412   )
413 {
414   TRANSFER_RING                 *EPRing;
415   TRB                           *Trb;
416 
417   if (Urb->Direction == EfiUsbDataIn) {
418     EPRing = &Handle->TransferRingIn;
419   } else {
420     EPRing = &Handle->TransferRingOut;
421   }
422 
423   Urb->Ring = (EFI_PHYSICAL_ADDRESS)(UINTN) EPRing;
424   XhcSyncTrsRing (Handle, EPRing);
425 
426   Urb->Trb = EPRing->RingEnqueue;
427   Trb = (TRB *)(UINTN)EPRing->RingEnqueue;
428   Trb->TrbNormal.TRBPtrLo  = XHC_LOW_32BIT (Urb->Data);
429   Trb->TrbNormal.TRBPtrHi  = XHC_HIGH_32BIT (Urb->Data);
430   Trb->TrbNormal.Length    = Urb->DataLen;
431   Trb->TrbNormal.TDSize    = 0;
432   Trb->TrbNormal.IntTarget = 0;
433   Trb->TrbNormal.ISP       = 1;
434   Trb->TrbNormal.IOC       = 1;
435   Trb->TrbNormal.Type      = TRB_TYPE_NORMAL;
436 
437   //
438   // Update the cycle bit to indicate this TRB has been consumed.
439   //
440   Trb->TrbNormal.CycleBit = EPRing->RingPCS & BIT0;
441 
442   return EFI_SUCCESS;
443 }
444 
445 /**
446   Create a new URB for a new transaction.
447 
448   @param  Handle     Debug port handle.
449   @param  Direction  The direction of data flow.
450   @param  Data       The user data to transfer
451   @param  DataLen    The length of data buffer
452 
453   @return Created URB or NULL
454 
455 **/
456 URB*
XhcCreateUrb(IN USB3_DEBUG_PORT_HANDLE * Handle,IN EFI_USB_DATA_DIRECTION Direction,IN VOID * Data,IN UINTN DataLen)457 XhcCreateUrb (
458   IN USB3_DEBUG_PORT_HANDLE             *Handle,
459   IN EFI_USB_DATA_DIRECTION             Direction,
460   IN VOID                               *Data,
461   IN UINTN                              DataLen
462   )
463 {
464   EFI_STATUS                    Status;
465   URB                           *Urb;
466   EFI_PHYSICAL_ADDRESS          UrbData;
467 
468   if (Direction == EfiUsbDataIn) {
469     Urb = &Handle->UrbIn;
470   } else {
471     Urb = &Handle->UrbOut;
472   }
473 
474   UrbData  = Urb->Data;
475 
476   ZeroMem (Urb, sizeof (URB));
477   Urb->Direction = Direction;
478 
479   //
480   // Allocate memory to move data from CAR or SMRAM to normal memory
481   // to make XHCI DMA successfully
482   // re-use the pre-allocate buffer in PEI to avoid DXE memory service or gBS are not ready
483   //
484   Urb->Data  = UrbData;
485 
486   if (Direction == EfiUsbDataIn) {
487     //
488     // Do not break URB data in buffer as it may contain the data which were just put in via DMA by XHC
489     //
490     Urb->DataLen  = (UINT32) DataLen;
491   } else {
492     //
493     // Put data into URB data out buffer which will create TRBs
494     //
495     ZeroMem ((VOID*)(UINTN) Urb->Data, DataLen);
496     CopyMem ((VOID*)(UINTN) Urb->Data, Data, DataLen);
497     Urb->DataLen  = (UINT32) DataLen;
498   }
499 
500   Status = XhcCreateTransferTrb (Handle, Urb);
501   ASSERT_EFI_ERROR (Status);
502 
503   return Urb;
504 }
505 
506 /**
507   Submits bulk transfer to a bulk endpoint of a USB device.
508 
509   @param  Handle                Debug port handle.
510   @param  Direction             The direction of data transfer.
511   @param  Data                  Array of pointers to the buffers of data to transmit
512                                 from or receive into.
513   @param  DataLength            The lenght of the data buffer.
514   @param  Timeout               Indicates the maximum time, in microsecond, which
515                                 the transfer is allowed to complete.
516 
517   @retval EFI_SUCCESS           The transfer was completed successfully.
518   @retval EFI_OUT_OF_RESOURCES  The transfer failed due to lack of resource.
519   @retval EFI_INVALID_PARAMETER Some parameters are invalid.
520   @retval EFI_TIMEOUT           The transfer failed due to timeout.
521   @retval EFI_DEVICE_ERROR      The transfer failed due to host controller error.
522 
523 **/
524 EFI_STATUS
525 EFIAPI
XhcDataTransfer(IN USB3_DEBUG_PORT_HANDLE * Handle,IN EFI_USB_DATA_DIRECTION Direction,IN OUT VOID * Data,IN OUT UINTN * DataLength,IN UINTN Timeout)526 XhcDataTransfer (
527   IN     USB3_DEBUG_PORT_HANDLE              *Handle,
528   IN     EFI_USB_DATA_DIRECTION              Direction,
529   IN OUT VOID                                *Data,
530   IN OUT UINTN                               *DataLength,
531   IN     UINTN                               Timeout
532   )
533 {
534   URB                     *Urb;
535   EFI_STATUS              Status;
536 
537   //
538   // Validate the parameters
539   //
540   if ((DataLength == NULL) || (*DataLength == 0) || (Data == NULL)) {
541     return EFI_INVALID_PARAMETER;
542   }
543 
544   //
545   // Create a new URB, insert it into the asynchronous
546   // schedule list, then poll the execution status.
547   //
548   Urb = XhcCreateUrb (Handle, Direction, Data, *DataLength);
549   ASSERT (Urb != NULL);
550 
551   XhcExecTransfer (Handle, Urb, Timeout);
552 
553   //
554   // Make sure the data received from HW can fit in the received buffer.
555   //
556   if (Urb->Completed > *DataLength) {
557     return EFI_DEVICE_ERROR;
558   }
559 
560   *DataLength     = Urb->Completed;
561 
562   Status = EFI_TIMEOUT;
563   if (Urb->Result == EFI_USB_NOERROR) {
564     Status = EFI_SUCCESS;
565   }
566 
567   if (Direction == EfiUsbDataIn) {
568     //
569     // Move data from internal buffer to outside buffer (outside buffer may be in SMRAM...)
570     // SMRAM does not allow to do DMA, so we create an internal buffer.
571     //
572     CopyMem (Data, (VOID *)(UINTN)Urb->Data, *DataLength);
573   }
574 
575   return Status;
576 }
577 
578