1 /** @file
2 PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid
3 which is used to enable recovery function from USB Drivers.
4 
5 Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
6 Copyright (c) Microsoft Corporation.<BR>
7 
8 SPDX-License-Identifier: BSD-2-Clause-Patent
9 
10 **/
11 
12 #include "EhcPeim.h"
13 
14 /**
15   Delete a single asynchronous interrupt transfer for
16   the device and endpoint.
17 
18   @param  Ehc         The EHCI device.
19   @param  Data        Current data not associated with a QTD.
20   @param  DataLen     The length of the data.
21   @param  PktId       Packet ID to use in the QTD.
22   @param  Toggle      Data toggle to use in the QTD.
23   @param  MaxPacket   Maximu packet length of the endpoint.
24 
25   @retval the pointer to the created QTD or NULL if failed to create one.
26 
27 **/
28 PEI_EHC_QTD *
EhcCreateQtd(IN PEI_USB2_HC_DEV * Ehc,IN UINT8 * Data,IN UINTN DataLen,IN UINT8 PktId,IN UINT8 Toggle,IN UINTN MaxPacket)29 EhcCreateQtd (
30   IN PEI_USB2_HC_DEV      *Ehc,
31   IN UINT8                *Data,
32   IN UINTN                DataLen,
33   IN UINT8                PktId,
34   IN UINT8                Toggle,
35   IN UINTN                MaxPacket
36   )
37 {
38   PEI_EHC_QTD             *Qtd;
39   QTD_HW                  *QtdHw;
40   UINTN                   Index;
41   UINTN                   Len;
42   UINTN                   ThisBufLen;
43 
44   ASSERT (Ehc != NULL);
45 
46   Qtd = UsbHcAllocateMem (Ehc, Ehc->MemPool, sizeof (PEI_EHC_QTD));
47 
48   if (Qtd == NULL) {
49     return NULL;
50   }
51 
52   Qtd->Signature    = EHC_QTD_SIG;
53   Qtd->Data         = Data;
54   Qtd->DataLen      = 0;
55 
56   InitializeListHead (&Qtd->QtdList);
57 
58   QtdHw             = &Qtd->QtdHw;
59   QtdHw->NextQtd    = QTD_LINK (NULL, TRUE);
60   QtdHw->AltNext    = QTD_LINK (NULL, TRUE);
61   QtdHw->Status     = QTD_STAT_ACTIVE;
62   QtdHw->Pid        = PktId;
63   QtdHw->ErrCnt     = QTD_MAX_ERR;
64   QtdHw->Ioc        = 0;
65   QtdHw->TotalBytes = 0;
66   QtdHw->DataToggle = Toggle;
67 
68   //
69   // Fill in the buffer points
70   //
71   if (Data != NULL) {
72     Len = 0;
73 
74     for (Index = 0; Index <= QTD_MAX_BUFFER; Index++) {
75       //
76       // Set the buffer point (Check page 41 EHCI Spec 1.0). No need to
77       // compute the offset and clear Reserved fields. This is already
78       // done in the data point.
79       //
80       QtdHw->Page[Index]      = EHC_LOW_32BIT (Data);
81       QtdHw->PageHigh[Index]  = EHC_HIGH_32BIT (Data);
82 
83       ThisBufLen              = QTD_BUF_LEN - (EHC_LOW_32BIT (Data) & QTD_BUF_MASK);
84 
85       if (Len + ThisBufLen >= DataLen) {
86         Len = DataLen;
87         break;
88       }
89 
90       Len += ThisBufLen;
91       Data += ThisBufLen;
92     }
93 
94     //
95     // Need to fix the last pointer if the Qtd can't hold all the
96     // user's data to make sure that the length is in the unit of
97     // max packets. If it can hold all the data, there is no such
98     // need.
99     //
100     if (Len < DataLen) {
101       Len = Len - Len % MaxPacket;
102     }
103 
104     QtdHw->TotalBytes = (UINT32) Len;
105     Qtd->DataLen      = Len;
106   }
107 
108   return Qtd;
109 }
110 
111 /**
112   Initialize the queue head for interrupt transfer,
113   that is, initialize the following three fields:
114     1. SplitXState in the Status field.
115     2. Microframe S-mask.
116     3. Microframe C-mask.
117 
118   @param  Ep    The queue head's related endpoint.
119   @param  QhHw  The queue head to initialize.
120 
121 **/
122 VOID
EhcInitIntQh(IN USB_ENDPOINT * Ep,IN QH_HW * QhHw)123 EhcInitIntQh (
124   IN USB_ENDPOINT         *Ep,
125   IN QH_HW                *QhHw
126   )
127 {
128   //
129   // Because UEFI interface can't utilitize an endpoint with
130   // poll rate faster than 1ms, only need to set one bit in
131   // the queue head. simple. But it may be changed later. If
132   // sub-1ms interrupt is supported, need to update the S-Mask
133   // here
134   //
135   if (Ep->DevSpeed == EFI_USB_SPEED_HIGH) {
136     QhHw->SMask = QH_MICROFRAME_0;
137     return ;
138   }
139 
140   //
141   // For low/full speed device, the transfer must go through
142   // the split transaction. Need to update three fields
143   // 1. SplitXState in the status
144   // 2. Microframe S-Mask
145   // 3. Microframe C-Mask
146   // UEFI USB doesn't exercise admission control. It simplely
147   // schedule the high speed transactions in microframe 0, and
148   // full/low speed transactions at microframe 1. This also
149   // avoid the use of FSTN.
150   //
151   QhHw->SMask = QH_MICROFRAME_1;
152   QhHw->CMask = QH_MICROFRAME_3 | QH_MICROFRAME_4 | QH_MICROFRAME_5;
153 }
154 
155 /**
156   Allocate and initialize a EHCI queue head.
157 
158   @param  Ehci      The EHCI device.
159   @param  Ep        The endpoint to create queue head for.
160 
161   @retval the pointer to the created queue head or NULL if failed to create one.
162 
163 **/
164 PEI_EHC_QH *
EhcCreateQh(IN PEI_USB2_HC_DEV * Ehci,IN USB_ENDPOINT * Ep)165 EhcCreateQh (
166   IN PEI_USB2_HC_DEV      *Ehci,
167   IN USB_ENDPOINT         *Ep
168   )
169 {
170   PEI_EHC_QH              *Qh;
171   QH_HW                   *QhHw;
172 
173   Qh = UsbHcAllocateMem (Ehci, Ehci->MemPool, sizeof (PEI_EHC_QH));
174 
175   if (Qh == NULL) {
176     return NULL;
177   }
178 
179   Qh->Signature       = EHC_QH_SIG;
180   Qh->NextQh          = NULL;
181   Qh->Interval        = Ep->PollRate;
182 
183   InitializeListHead (&Qh->Qtds);
184 
185   QhHw                = &Qh->QhHw;
186   QhHw->HorizonLink   = QH_LINK (NULL, 0, TRUE);
187   QhHw->DeviceAddr    = Ep->DevAddr;
188   QhHw->Inactive      = 0;
189   QhHw->EpNum         = Ep->EpAddr;
190   QhHw->EpSpeed       = Ep->DevSpeed;
191   QhHw->DtCtrl        = 0;
192   QhHw->ReclaimHead   = 0;
193   QhHw->MaxPacketLen  = (UINT32) Ep->MaxPacket;
194   QhHw->CtrlEp        = 0;
195   QhHw->NakReload     = QH_NAK_RELOAD;
196   QhHw->HubAddr       = Ep->HubAddr;
197   QhHw->PortNum       = Ep->HubPort;
198   QhHw->Multiplier    = 1;
199   QhHw->DataToggle    = Ep->Toggle;
200 
201   if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) {
202     QhHw->Status |= QTD_STAT_DO_SS;
203   }
204 
205   switch (Ep->Type) {
206   case EHC_CTRL_TRANSFER:
207     //
208     // Special initialization for the control transfer:
209     // 1. Control transfer initialize data toggle from each QTD
210     // 2. Set the Control Endpoint Flag (C) for low/full speed endpoint.
211     //
212     QhHw->DtCtrl = 1;
213 
214     if (Ep->DevSpeed != EFI_USB_SPEED_HIGH) {
215       QhHw->CtrlEp = 1;
216     }
217     break;
218 
219   case EHC_INT_TRANSFER_ASYNC:
220   case EHC_INT_TRANSFER_SYNC:
221     //
222     // Special initialization for the interrupt transfer
223     // to set the S-Mask and C-Mask
224     //
225     QhHw->NakReload = 0;
226     EhcInitIntQh (Ep, QhHw);
227     break;
228 
229   case EHC_BULK_TRANSFER:
230     if ((Ep->DevSpeed == EFI_USB_SPEED_HIGH) && (Ep->Direction == EfiUsbDataOut)) {
231       QhHw->Status |= QTD_STAT_DO_PING;
232     }
233 
234     break;
235   }
236 
237   return Qh;
238 }
239 
240 /**
241   Convert the poll interval from application to that
242   be used by EHCI interface data structure. Only need
243   to get the max 2^n that is less than interval. UEFI
244   can't support high speed endpoint with a interval less
245   than 8 microframe because interval is specified in
246   the unit of ms (millisecond).
247 
248   @param Interval       The interval to convert.
249 
250   @retval The converted interval.
251 
252 **/
253 UINTN
EhcConvertPollRate(IN UINTN Interval)254 EhcConvertPollRate (
255   IN  UINTN               Interval
256   )
257 {
258   UINTN                   BitCount;
259 
260   if (Interval == 0) {
261     return 1;
262   }
263 
264   //
265   // Find the index (1 based) of the highest non-zero bit
266   //
267   BitCount = 0;
268 
269   while (Interval != 0) {
270     Interval >>= 1;
271     BitCount++;
272   }
273 
274   return (UINTN)1 << (BitCount - 1);
275 }
276 
277 /**
278   Free a list of QTDs.
279 
280   @param  Ehc         The EHCI device.
281   @param  Qtds        The list head of the QTD.
282 
283 **/
284 VOID
EhcFreeQtds(IN PEI_USB2_HC_DEV * Ehc,IN EFI_LIST_ENTRY * Qtds)285 EhcFreeQtds (
286   IN PEI_USB2_HC_DEV      *Ehc,
287   IN EFI_LIST_ENTRY       *Qtds
288   )
289 {
290   EFI_LIST_ENTRY          *Entry;
291   EFI_LIST_ENTRY          *Next;
292   PEI_EHC_QTD             *Qtd;
293 
294   BASE_LIST_FOR_EACH_SAFE (Entry, Next, Qtds) {
295     Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
296 
297     RemoveEntryList (&Qtd->QtdList);
298     UsbHcFreeMem (Ehc, Ehc->MemPool, Qtd, sizeof (PEI_EHC_QTD));
299   }
300 }
301 
302 /**
303   Free an allocated URB. It is possible for it to be partially inited.
304 
305   @param  Ehc         The EHCI device.
306   @param  Urb         The URB to free.
307 
308 **/
309 VOID
EhcFreeUrb(IN PEI_USB2_HC_DEV * Ehc,IN PEI_URB * Urb)310 EhcFreeUrb (
311   IN PEI_USB2_HC_DEV      *Ehc,
312   IN PEI_URB              *Urb
313   )
314 {
315   if (Urb->RequestPhy != NULL) {
316     IoMmuUnmap (Ehc->IoMmu, Urb->RequestMap);
317   }
318 
319   if (Urb->DataMap != NULL) {
320     IoMmuUnmap (Ehc->IoMmu, Urb->DataMap);
321   }
322 
323   if (Urb->Qh != NULL) {
324     //
325     // Ensure that this queue head has been unlinked from the
326     // schedule data structures. Free all the associated QTDs
327     //
328     EhcFreeQtds (Ehc, &Urb->Qh->Qtds);
329     UsbHcFreeMem (Ehc, Ehc->MemPool, Urb->Qh, sizeof (PEI_EHC_QH));
330   }
331 }
332 
333 /**
334   Create a list of QTDs for the URB.
335 
336   @param  Ehc         The EHCI device.
337   @param  Urb         The URB to create QTDs for.
338 
339   @retval EFI_OUT_OF_RESOURCES    Failed to allocate resource for QTD.
340   @retval EFI_SUCCESS             The QTDs are allocated for the URB.
341 
342 **/
343 EFI_STATUS
EhcCreateQtds(IN PEI_USB2_HC_DEV * Ehc,IN PEI_URB * Urb)344 EhcCreateQtds (
345   IN PEI_USB2_HC_DEV      *Ehc,
346   IN PEI_URB              *Urb
347   )
348 {
349   USB_ENDPOINT            *Ep;
350   PEI_EHC_QH              *Qh;
351   PEI_EHC_QTD             *Qtd;
352   PEI_EHC_QTD             *StatusQtd;
353   PEI_EHC_QTD             *NextQtd;
354   EFI_LIST_ENTRY          *Entry;
355   UINT32                  AlterNext;
356   UINT8                   Toggle;
357   UINTN                   Len;
358   UINT8                   Pid;
359 
360   ASSERT ((Urb != NULL) && (Urb->Qh != NULL));
361 
362   //
363   // EHCI follows the alternet next QTD pointer if it meets
364   // a short read and the AlterNext pointer is valid. UEFI
365   // EHCI driver should terminate the transfer except the
366   // control transfer.
367   //
368   Toggle    = 0;
369   Qh        = Urb->Qh;
370   Ep        = &Urb->Ep;
371   StatusQtd = NULL;
372   AlterNext = QTD_LINK (NULL, TRUE);
373 
374   if (Ep->Direction == EfiUsbDataIn) {
375     AlterNext = QTD_LINK (Ehc->ShortReadStop, FALSE);
376   }
377 
378   //
379   // Build the Setup and status packets for control transfer
380   //
381   if (Urb->Ep.Type == EHC_CTRL_TRANSFER) {
382     Len = sizeof (EFI_USB_DEVICE_REQUEST);
383     Qtd = EhcCreateQtd (Ehc, Urb->RequestPhy, Len, QTD_PID_SETUP, 0, Ep->MaxPacket);
384 
385     if (Qtd == NULL) {
386       return EFI_OUT_OF_RESOURCES;
387     }
388 
389     InsertTailList (&Qh->Qtds, &Qtd->QtdList);
390 
391     //
392     // Create the status packet now. Set the AlterNext to it. So, when
393     // EHCI meets a short control read, it can resume at the status stage.
394     // Use the opposite direction of the data stage, or IN if there is
395     // no data stage.
396     //
397     if (Ep->Direction == EfiUsbDataIn) {
398       Pid = QTD_PID_OUTPUT;
399     } else {
400       Pid = QTD_PID_INPUT;
401     }
402 
403     StatusQtd = EhcCreateQtd (Ehc, NULL, 0, Pid, 1, Ep->MaxPacket);
404 
405     if (StatusQtd == NULL) {
406       goto ON_ERROR;
407     }
408 
409     if (Ep->Direction == EfiUsbDataIn) {
410       AlterNext = QTD_LINK (StatusQtd, FALSE);
411     }
412 
413     Toggle = 1;
414   }
415 
416   //
417   // Build the data packets for all the transfers
418   //
419   if (Ep->Direction == EfiUsbDataIn) {
420     Pid = QTD_PID_INPUT;
421   } else {
422     Pid = QTD_PID_OUTPUT;
423   }
424 
425   Qtd = NULL;
426   Len = 0;
427 
428   while (Len < Urb->DataLen) {
429     Qtd = EhcCreateQtd (
430             Ehc,
431             (UINT8 *) Urb->DataPhy + Len,
432             Urb->DataLen - Len,
433             Pid,
434             Toggle,
435             Ep->MaxPacket
436             );
437 
438     if (Qtd == NULL) {
439       goto ON_ERROR;
440     }
441 
442     Qtd->QtdHw.AltNext = AlterNext;
443     InsertTailList (&Qh->Qtds, &Qtd->QtdList);
444 
445     //
446     // Switch the Toggle bit if odd number of packets are included in the QTD.
447     //
448     if (((Qtd->DataLen + Ep->MaxPacket - 1) / Ep->MaxPacket) % 2) {
449       Toggle = (UINT8) (1 - Toggle);
450     }
451 
452     Len += Qtd->DataLen;
453   }
454 
455   //
456   // Insert the status packet for control transfer
457   //
458   if (Ep->Type == EHC_CTRL_TRANSFER) {
459     InsertTailList (&Qh->Qtds, &StatusQtd->QtdList);
460   }
461 
462   //
463   // OK, all the QTDs needed are created. Now, fix the NextQtd point
464   //
465   BASE_LIST_FOR_EACH (Entry, &Qh->Qtds) {
466     Qtd = EFI_LIST_CONTAINER (Entry, PEI_EHC_QTD, QtdList);
467 
468     //
469     // break if it is the last entry on the list
470     //
471     if (Entry->ForwardLink == &Qh->Qtds) {
472       break;
473     }
474 
475     NextQtd             = EFI_LIST_CONTAINER (Entry->ForwardLink, PEI_EHC_QTD, QtdList);
476     Qtd->QtdHw.NextQtd  = QTD_LINK (NextQtd, FALSE);
477   }
478 
479   //
480   // Link the QTDs to the queue head
481   //
482   NextQtd           = EFI_LIST_CONTAINER (Qh->Qtds.ForwardLink, PEI_EHC_QTD, QtdList);
483   Qh->QhHw.NextQtd  = QTD_LINK (NextQtd, FALSE);
484   return EFI_SUCCESS;
485 
486 ON_ERROR:
487   EhcFreeQtds (Ehc, &Qh->Qtds);
488   return EFI_OUT_OF_RESOURCES;
489 }
490 
491 /**
492   Create a new URB and its associated QTD.
493 
494   @param  Ehc               The EHCI device.
495   @param  DevAddr           The device address.
496   @param  EpAddr            Endpoint addrress & its direction.
497   @param  DevSpeed          The device speed.
498   @param  Toggle            Initial data toggle to use.
499   @param  MaxPacket         The max packet length of the endpoint.
500   @param  Hub               The transaction translator to use.
501   @param  Type              The transaction type.
502   @param  Request           The standard USB request for control transfer.
503   @param  Data              The user data to transfer.
504   @param  DataLen           The length of data buffer.
505   @param  Callback          The function to call when data is transferred.
506   @param  Context           The context to the callback.
507   @param  Interval          The interval for interrupt transfer.
508 
509   @retval the pointer to the created URB or NULL.
510 
511 **/
512 PEI_URB *
EhcCreateUrb(IN PEI_USB2_HC_DEV * Ehc,IN UINT8 DevAddr,IN UINT8 EpAddr,IN UINT8 DevSpeed,IN UINT8 Toggle,IN UINTN MaxPacket,IN EFI_USB2_HC_TRANSACTION_TRANSLATOR * Hub,IN UINTN Type,IN EFI_USB_DEVICE_REQUEST * Request,IN VOID * Data,IN UINTN DataLen,IN EFI_ASYNC_USB_TRANSFER_CALLBACK Callback,IN VOID * Context,IN UINTN Interval)513 EhcCreateUrb (
514   IN PEI_USB2_HC_DEV                    *Ehc,
515   IN UINT8                              DevAddr,
516   IN UINT8                              EpAddr,
517   IN UINT8                              DevSpeed,
518   IN UINT8                              Toggle,
519   IN UINTN                              MaxPacket,
520   IN EFI_USB2_HC_TRANSACTION_TRANSLATOR *Hub,
521   IN UINTN                              Type,
522   IN EFI_USB_DEVICE_REQUEST             *Request,
523   IN VOID                               *Data,
524   IN UINTN                              DataLen,
525   IN EFI_ASYNC_USB_TRANSFER_CALLBACK    Callback,
526   IN VOID                               *Context,
527   IN UINTN                              Interval
528   )
529 {
530   USB_ENDPOINT                  *Ep;
531   EFI_PHYSICAL_ADDRESS          PhyAddr;
532   EDKII_IOMMU_OPERATION         MapOp;
533   EFI_STATUS                    Status;
534   UINTN                         Len;
535   PEI_URB                       *Urb;
536   VOID                          *Map;
537 
538   Map = NULL;
539 
540   Urb = Ehc->Urb;
541   Urb->Signature  = EHC_URB_SIG;
542   InitializeListHead (&Urb->UrbList);
543 
544   Ep              = &Urb->Ep;
545   Ep->DevAddr     = DevAddr;
546   Ep->EpAddr      = (UINT8) (EpAddr & 0x0F);
547   Ep->Direction   = (((EpAddr & 0x80) != 0) ? EfiUsbDataIn : EfiUsbDataOut);
548   Ep->DevSpeed    = DevSpeed;
549   Ep->MaxPacket   = MaxPacket;
550 
551   Ep->HubAddr     = 0;
552   Ep->HubPort     = 0;
553 
554   if (DevSpeed != EFI_USB_SPEED_HIGH) {
555     ASSERT (Hub != NULL);
556 
557     Ep->HubAddr   = Hub->TranslatorHubAddress;
558     Ep->HubPort   = Hub->TranslatorPortNumber;
559   }
560 
561   Ep->Toggle      = Toggle;
562   Ep->Type        = Type;
563   Ep->PollRate    = EhcConvertPollRate (Interval);
564 
565   Urb->Request    = Request;
566   Urb->Data       = Data;
567   Urb->DataLen    = DataLen;
568   Urb->Callback   = Callback;
569   Urb->Context    = Context;
570   Urb->Qh         = EhcCreateQh (Ehc, &Urb->Ep);
571 
572   if (Urb->Qh == NULL) {
573     goto ON_ERROR;
574   }
575 
576   Urb->RequestPhy = NULL;
577   Urb->RequestMap = NULL;
578   Urb->DataPhy  = NULL;
579   Urb->DataMap  = NULL;
580 
581   //
582   // Map the request and user data
583   //
584   if (Request != NULL) {
585     Len     = sizeof (EFI_USB_DEVICE_REQUEST);
586     MapOp   = EdkiiIoMmuOperationBusMasterRead;
587     Status  = IoMmuMap (Ehc->IoMmu, MapOp, Request, &Len, &PhyAddr, &Map);
588 
589     if (EFI_ERROR (Status) || (Len != sizeof (EFI_USB_DEVICE_REQUEST))) {
590       goto ON_ERROR;
591     }
592 
593     Urb->RequestPhy = (VOID *) ((UINTN) PhyAddr);
594     Urb->RequestMap = Map;
595   }
596 
597   if (Data != NULL) {
598     Len      = DataLen;
599 
600     if (Ep->Direction == EfiUsbDataIn) {
601       MapOp = EdkiiIoMmuOperationBusMasterWrite;
602     } else {
603       MapOp = EdkiiIoMmuOperationBusMasterRead;
604     }
605 
606     Status  = IoMmuMap (Ehc->IoMmu, MapOp, Data, &Len, &PhyAddr, &Map);
607 
608     if (EFI_ERROR (Status) || (Len != DataLen)) {
609       goto ON_ERROR;
610     }
611 
612     Urb->DataPhy  = (VOID *) ((UINTN) PhyAddr);
613     Urb->DataMap  = Map;
614   }
615 
616   Status = EhcCreateQtds (Ehc, Urb);
617 
618   if (EFI_ERROR (Status)) {
619     goto ON_ERROR;
620   }
621 
622   return Urb;
623 
624 ON_ERROR:
625   EhcFreeUrb (Ehc, Urb);
626   return NULL;
627 }
628