]> git.proxmox.com Git - mirror_edk2.git/blob - SourceLevelDebugPkg/Library/DebugCommunicationLibUsb3/DebugCommunicationLibUsb3Dxe.c
SourceLevelDebugPkg DebugUsb3: Re-Support IOMMU
[mirror_edk2.git] / SourceLevelDebugPkg / Library / DebugCommunicationLibUsb3 / DebugCommunicationLibUsb3Dxe.c
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 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php.
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include <Base.h>
16 #include <PiDxe.h>
17 #include <Library/UefiBootServicesTableLib.h>
18 #include <Library/HobLib.h>
19 #include <Protocol/PciIo.h>
20 #include <Protocol/IoMmu.h>
21 #include <Protocol/DxeSmmReadyToLock.h>
22 #include "DebugCommunicationLibUsb3Internal.h"
23
24 GUID gUsb3DbgGuid = USB3_DBG_GUID;
25
26 USB3_DEBUG_PORT_HANDLE mUsb3Instance = {USB3DBG_UNINITIALIZED};
27 EFI_PHYSICAL_ADDRESS mUsb3InstanceAddr = 0;
28 EFI_PHYSICAL_ADDRESS *mUsb3InstanceAddrPtr = NULL;
29 EFI_PCI_IO_PROTOCOL *mUsb3PciIo = NULL;
30
31 /**
32 Creates a named event that can be signaled.
33
34 This function creates an event using NotifyTpl, NoifyFunction.
35 If Name is NULL, then ASSERT().
36 If NotifyTpl is not a legal TPL value, then ASSERT().
37 If NotifyFunction is NULL, then ASSERT().
38
39 @param Name Supplies the GUID name of the event.
40 @param NotifyTpl Supplies the task priority level of the event notifications.
41 @param NotifyFunction Supplies the function to notify when the event is signaled.
42 @param Event A pointer to the event created.
43
44 @retval EFI_SUCCESS A named event was created.
45 @retval EFI_OUT_OF_RESOURCES There are not enough resource to create the named event.
46
47 **/
48 EFI_STATUS
49 EFIAPI
50 Usb3NamedEventListen (
51 IN CONST EFI_GUID *Name,
52 IN EFI_TPL NotifyTpl,
53 IN EFI_EVENT_NOTIFY NotifyFunction,
54 IN EFI_EVENT *Event
55 )
56 {
57 EFI_STATUS Status;
58 VOID *RegistrationLocal;
59
60 ASSERT (Name != NULL);
61 ASSERT (NotifyFunction != NULL);
62 ASSERT (NotifyTpl <= TPL_HIGH_LEVEL);
63
64 //
65 // Create event
66 //
67 Status = gBS->CreateEvent (
68 EVT_NOTIFY_SIGNAL,
69 NotifyTpl,
70 NotifyFunction,
71 NULL,
72 Event
73 );
74 ASSERT_EFI_ERROR (Status);
75
76 //
77 // Register for an installation of protocol interface
78 //
79 Status = gBS->RegisterProtocolNotify (
80 (EFI_GUID *) Name,
81 *Event,
82 &RegistrationLocal
83 );
84 ASSERT_EFI_ERROR (Status);
85
86 return Status;
87 }
88
89 /**
90 USB3 map one DMA buffer.
91
92 @param PciIo Pointer to PciIo for USB3 debug port.
93 @param Address DMA buffer address to be mapped.
94 @param NumberOfBytes Number of bytes to be mapped.
95
96 **/
97 VOID
98 Usb3MapOneDmaBuffer (
99 IN EFI_PCI_IO_PROTOCOL *PciIo,
100 IN EFI_PHYSICAL_ADDRESS Address,
101 IN UINTN NumberOfBytes
102 )
103 {
104 EFI_STATUS Status;
105 VOID *HostAddress;
106 EFI_PHYSICAL_ADDRESS DeviceAddress;
107 VOID *Mapping;
108
109 HostAddress = (VOID *) (UINTN) Address;
110 Status = PciIo->Map (
111 PciIo,
112 EfiPciIoOperationBusMasterCommonBuffer,
113 HostAddress,
114 &NumberOfBytes,
115 &DeviceAddress,
116 &Mapping
117 );
118 ASSERT_EFI_ERROR (Status);
119 ASSERT (DeviceAddress == ((EFI_PHYSICAL_ADDRESS) (UINTN) HostAddress));
120 }
121
122 /**
123 USB3 map DMA buffers.
124
125 @param Instance Pointer to USB3 debug port instance.
126 @param PciIo Pointer to PciIo for USB3 debug port.
127
128 **/
129 VOID
130 Usb3MapDmaBuffers (
131 IN USB3_DEBUG_PORT_HANDLE *Instance,
132 IN EFI_PCI_IO_PROTOCOL *PciIo
133 )
134 {
135 Usb3MapOneDmaBuffer (
136 PciIo,
137 Instance->UrbIn.Data,
138 XHCI_DEBUG_DEVICE_MAX_PACKET_SIZE * 2 + USB3_DEBUG_PORT_WRITE_MAX_PACKET_SIZE
139 );
140
141 Usb3MapOneDmaBuffer (
142 PciIo,
143 Instance->TransferRingIn.RingSeg0,
144 sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER
145 );
146
147 Usb3MapOneDmaBuffer (
148 PciIo,
149 Instance->TransferRingOut.RingSeg0,
150 sizeof (TRB_TEMPLATE) * TR_RING_TRB_NUMBER
151 );
152
153 Usb3MapOneDmaBuffer (
154 PciIo,
155 Instance->EventRing.EventRingSeg0,
156 sizeof (TRB_TEMPLATE) * EVENT_RING_TRB_NUMBER
157 );
158
159 Usb3MapOneDmaBuffer (
160 PciIo,
161 Instance->EventRing.ERSTBase,
162 sizeof (EVENT_RING_SEG_TABLE_ENTRY) * ERST_NUMBER
163 );
164
165 Usb3MapOneDmaBuffer (
166 PciIo,
167 Instance->DebugCapabilityContext,
168 sizeof (XHC_DC_CONTEXT)
169 );
170
171 Usb3MapOneDmaBuffer (
172 PciIo,
173 ((XHC_DC_CONTEXT *) (UINTN) Instance->DebugCapabilityContext)->DbcInfoContext.String0DescAddress,
174 STRING0_DESC_LEN + MANU_DESC_LEN + PRODUCT_DESC_LEN + SERIAL_DESC_LEN
175 );
176 }
177
178 /**
179 Invoke a notification event
180
181 @param[in] Event Event whose notification function is being invoked.
182 @param[in] Context The pointer to the notification function's context,
183 which is implementation-dependent.
184
185 **/
186 VOID
187 EFIAPI
188 Usb3DxeSmmReadyToLockNotify (
189 IN EFI_EVENT Event,
190 IN VOID *Context
191 )
192 {
193 USB3_DEBUG_PORT_HANDLE *Instance;
194
195 DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__));
196
197 Instance = GetUsb3DebugPortInstance ();
198 ASSERT (Instance != NULL);
199
200 Instance->InNotify = TRUE;
201
202 //
203 // For the case that the USB3 debug port instance and DMA buffers are
204 // from PEI HOB with IOMMU enabled.
205 // Reinitialize USB3 debug port with granted DXE DMA buffer accessible
206 // by SMM environment.
207 //
208 InitializeUsbDebugHardware (Instance);
209
210 //
211 // Wait some time for host to be ready after re-initialization.
212 //
213 MicroSecondDelay (1000000);
214
215 Instance->InNotify = FALSE;
216 gBS->CloseEvent (Event);
217 }
218
219 /**
220 USB3 get IOMMU protocol.
221
222 @return Pointer to IOMMU protocol.
223
224 **/
225 EDKII_IOMMU_PROTOCOL *
226 Usb3GetIoMmu (
227 VOID
228 )
229 {
230 EFI_STATUS Status;
231 EDKII_IOMMU_PROTOCOL *IoMmu;
232
233 IoMmu = NULL;
234 Status = gBS->LocateProtocol (
235 &gEdkiiIoMmuProtocolGuid,
236 NULL,
237 (VOID **) &IoMmu
238 );
239 if (!EFI_ERROR (Status) && (IoMmu != NULL)) {
240 return IoMmu;
241 }
242
243 return NULL;
244 }
245
246 /**
247 Invoke a notification event
248
249 @param[in] Event Event whose notification function is being invoked.
250 @param[in] Context The pointer to the notification function's context,
251 which is implementation-dependent.
252
253 **/
254 VOID
255 EFIAPI
256 Usb3PciIoNotify (
257 IN EFI_EVENT Event,
258 IN VOID *Context
259 )
260 {
261 EFI_STATUS Status;
262 UINTN PciIoHandleCount;
263 EFI_HANDLE *PciIoHandleBuffer;
264 UINTN Index;
265 EFI_PCI_IO_PROTOCOL *PciIo;
266 UINTN PciSegment;
267 UINTN PciBusNumber;
268 UINTN PciDeviceNumber;
269 UINTN PciFunctionNumber;
270 UINT32 PciAddress;
271 USB3_DEBUG_PORT_HANDLE *Instance;
272 EFI_EVENT SmmReadyToLockEvent;
273
274 Status = gBS->LocateHandleBuffer (
275 ByProtocol,
276 &gEfiPciIoProtocolGuid,
277 NULL,
278 &PciIoHandleCount,
279 &PciIoHandleBuffer
280 );
281 if (!EFI_ERROR (Status) &&
282 (PciIoHandleBuffer != NULL) &&
283 (PciIoHandleCount != 0)) {
284 for (Index = 0; Index < PciIoHandleCount; Index++) {
285 Status = gBS->HandleProtocol (
286 PciIoHandleBuffer[Index],
287 &gEfiPciIoProtocolGuid,
288 (VOID **) &PciIo
289 );
290 ASSERT_EFI_ERROR (Status);
291 Status = PciIo->GetLocation (PciIo, &PciSegment, &PciBusNumber, &PciDeviceNumber, &PciFunctionNumber);
292 ASSERT_EFI_ERROR (Status);
293 PciAddress = (UINT32) ((PciBusNumber << 20) | (PciDeviceNumber << 15) | (PciFunctionNumber << 12));
294 if (PciAddress == PcdGet32(PcdUsbXhciPciAddress)) {
295 //
296 // Found the PciIo for USB3 debug port.
297 //
298 DEBUG ((DEBUG_INFO, "%a()\n", __FUNCTION__));
299 if (Usb3GetIoMmu () != NULL) {
300 Instance = GetUsb3DebugPortInstance ();
301 ASSERT (Instance != NULL);
302 if (Instance->Ready) {
303 Instance->InNotify = TRUE;
304 Usb3MapDmaBuffers (Instance, PciIo);
305 Instance->InNotify = FALSE;
306
307 if (Instance->FromHob) {
308 mUsb3PciIo = PciIo;
309 Usb3NamedEventListen (
310 &gEfiDxeSmmReadyToLockProtocolGuid,
311 TPL_NOTIFY,
312 Usb3DxeSmmReadyToLockNotify,
313 &SmmReadyToLockEvent
314 );
315 }
316 }
317 }
318 gBS->CloseEvent (Event);
319 break;
320 }
321 }
322
323 gBS->FreePool (PciIoHandleBuffer);
324 }
325 }
326
327 /**
328 Return USB3 debug instance address pointer.
329
330 **/
331 EFI_PHYSICAL_ADDRESS *
332 GetUsb3DebugPortInstanceAddrPtr (
333 VOID
334 )
335 {
336 if (mUsb3InstanceAddrPtr == NULL) {
337 //
338 // Use the local variables temporarily.
339 //
340 mUsb3InstanceAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) &mUsb3Instance;
341 mUsb3InstanceAddrPtr = &mUsb3InstanceAddr;
342 }
343 return mUsb3InstanceAddrPtr;
344 }
345
346 /**
347 Allocates pages that are suitable for an OperationBusMasterCommonBuffer or
348 OperationBusMasterCommonBuffer64 mapping.
349
350 @param PciIo Pointer to PciIo for USB3 debug port.
351 @param Pages The number of pages to allocate.
352 @param Address A pointer to store the base system memory address of the
353 allocated range.
354
355 @retval EFI_SUCCESS The requested memory pages were allocated.
356 @retval EFI_UNSUPPORTED Attributes is unsupported. The only legal attribute bits are
357 MEMORY_WRITE_COMBINE and MEMORY_CACHED.
358 @retval EFI_INVALID_PARAMETER One or more parameters are invalid.
359 @retval EFI_OUT_OF_RESOURCES The memory pages could not be allocated.
360
361 **/
362 EFI_STATUS
363 Usb3AllocateDmaBuffer (
364 IN EFI_PCI_IO_PROTOCOL *PciIo,
365 IN UINTN Pages,
366 OUT VOID **Address
367 )
368 {
369 EFI_STATUS Status;
370
371 *Address = NULL;
372 Status = PciIo->AllocateBuffer (
373 PciIo,
374 AllocateAnyPages,
375 EfiRuntimeServicesData,
376 Pages,
377 Address,
378 0
379 );
380 if (!EFI_ERROR (Status)) {
381 Usb3MapOneDmaBuffer (
382 PciIo,
383 (EFI_PHYSICAL_ADDRESS) (UINTN) *Address,
384 EFI_PAGES_TO_SIZE (Pages)
385 );
386 }
387 return Status;
388 }
389
390 /**
391 Allocate aligned memory for XHC's usage.
392
393 @param BufferSize The size, in bytes, of the Buffer.
394
395 @return A pointer to the allocated buffer or NULL if allocation fails.
396
397 **/
398 VOID*
399 AllocateAlignBuffer (
400 IN UINTN BufferSize
401 )
402 {
403 EFI_PHYSICAL_ADDRESS TmpAddr;
404 EFI_STATUS Status;
405 VOID *Buf;
406
407 Buf = NULL;
408
409 if (gBS != NULL) {
410 if (mUsb3PciIo != NULL) {
411 Usb3AllocateDmaBuffer (
412 mUsb3PciIo,
413 EFI_SIZE_TO_PAGES (BufferSize),
414 &Buf
415 );
416 } else {
417 TmpAddr = 0xFFFFFFFF;
418 Status = gBS->AllocatePages (
419 AllocateMaxAddress,
420 EfiACPIMemoryNVS,
421 EFI_SIZE_TO_PAGES (BufferSize),
422 &TmpAddr
423 );
424 if (!EFI_ERROR (Status)) {
425 Buf = (VOID *) (UINTN) TmpAddr;
426 }
427 }
428 }
429
430 return Buf;
431 }
432
433 /**
434 The constructor function initialize USB3 debug port.
435
436 @param ImageHandle The firmware allocated handle for the EFI image.
437 @param SystemTable A pointer to the EFI System Table.
438
439 @retval EFI_SUCCESS The constructor always returns EFI_SUCCESS.
440
441 **/
442 EFI_STATUS
443 EFIAPI
444 DebugCommunicationUsb3DxeConstructor (
445 IN EFI_HANDLE ImageHandle,
446 IN EFI_SYSTEM_TABLE *SystemTable
447 )
448 {
449 EFI_PHYSICAL_ADDRESS *AddrPtr;
450 USB3_DEBUG_PORT_HANDLE *Instance;
451 EFI_PHYSICAL_ADDRESS Address;
452 EFI_STATUS Status;
453 EFI_EVENT Event;
454
455 Status = EfiGetSystemConfigurationTable (&gUsb3DbgGuid, (VOID **) &AddrPtr);
456 if (EFI_ERROR (Status)) {
457 //
458 // Instead of using local variables, install system configuration table for
459 // the local instance and the buffer to save instance address pointer.
460 //
461 Address = SIZE_4GB;
462 Status = gBS->AllocatePages (
463 AllocateMaxAddress,
464 EfiACPIMemoryNVS,
465 EFI_SIZE_TO_PAGES (sizeof (EFI_PHYSICAL_ADDRESS) + sizeof (USB3_DEBUG_PORT_HANDLE)),
466 &Address
467 );
468 if (EFI_ERROR (Status)) {
469 return Status;
470 }
471
472 AddrPtr = (EFI_PHYSICAL_ADDRESS *) (UINTN) Address;
473 ZeroMem (AddrPtr, sizeof (EFI_PHYSICAL_ADDRESS) + sizeof (USB3_DEBUG_PORT_HANDLE));
474 Instance = (USB3_DEBUG_PORT_HANDLE *) (AddrPtr + 1);
475 CopyMem (Instance, &mUsb3Instance, sizeof (USB3_DEBUG_PORT_HANDLE));
476 *AddrPtr = (EFI_PHYSICAL_ADDRESS) (UINTN) Instance;
477
478 Status = gBS->InstallConfigurationTable (&gUsb3DbgGuid, AddrPtr);
479 if (EFI_ERROR (Status)) {
480 return Status;
481 }
482 }
483
484 if (mUsb3InstanceAddrPtr != NULL) {
485 *AddrPtr = *mUsb3InstanceAddrPtr;
486 }
487 mUsb3InstanceAddrPtr = AddrPtr;
488
489 Instance = GetUsb3DebugPortInstance ();
490 ASSERT (Instance != NULL);
491
492 if (Instance->PciIoEvent == 0) {
493 Status = Usb3NamedEventListen (
494 &gEfiPciIoProtocolGuid,
495 TPL_NOTIFY,
496 Usb3PciIoNotify,
497 &Event
498 );
499 if (!EFI_ERROR (Status)) {
500 Instance->PciIoEvent = (EFI_PHYSICAL_ADDRESS) (UINTN) Event;
501 }
502 }
503
504 return EFI_SUCCESS;
505 }
506
507 /**
508 The destructor function.
509
510 @param ImageHandle The firmware allocated handle for the EFI image.
511 @param SystemTable A pointer to the EFI System Table.
512
513 @retval EFI_SUCCESS The destructor always returns EFI_SUCCESS.
514
515 **/
516 EFI_STATUS
517 EFIAPI
518 DebugCommunicationUsb3DxeDestructor (
519 IN EFI_HANDLE ImageHandle,
520 IN EFI_SYSTEM_TABLE *SystemTable
521 )
522 {
523 USB3_DEBUG_PORT_HANDLE *Instance;
524
525 Instance = GetUsb3DebugPortInstance ();
526 ASSERT (Instance != NULL);
527
528 if (Instance->PciIoEvent != 0) {
529 //
530 // Close the event created.
531 //
532 gBS->CloseEvent ((EFI_EVENT) (UINTN) Instance->PciIoEvent);
533 Instance->PciIoEvent = 0;
534 }
535 return EFI_SUCCESS;
536 }
537