]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/XhciPei/UsbHcMem.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / XhciPei / UsbHcMem.c
CommitLineData
d987459f
SZ
1/** @file\r
2PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid\r
3which is used to enable recovery function from USB Drivers.\r
4\r
2048c585 5Copyright (c) 2014 - 2016, Intel Corporation. All rights reserved.<BR>\r
d987459f 6\r
9d510e61 7SPDX-License-Identifier: BSD-2-Clause-Patent\r
d987459f
SZ
8\r
9**/\r
10\r
11#include "XhcPeim.h"\r
12\r
13/**\r
14 Allocate a block of memory to be used by the buffer pool.\r
15\r
16 @param Pages How many pages to allocate.\r
17\r
18 @return Pointer to the allocated memory block or NULL if failed.\r
19\r
20**/\r
21USBHC_MEM_BLOCK *\r
22UsbHcAllocMemBlock (\r
23 IN UINTN Pages\r
24 )\r
25{\r
26 USBHC_MEM_BLOCK *Block;\r
b575ca32
JY
27 VOID *BufHost;\r
28 VOID *Mapping;\r
29 EFI_PHYSICAL_ADDRESS MappedAddr;\r
d987459f
SZ
30 EFI_STATUS Status;\r
31 UINTN PageNumber;\r
32 EFI_PHYSICAL_ADDRESS TempPtr;\r
33\r
34 PageNumber = EFI_SIZE_TO_PAGES (sizeof (USBHC_MEM_BLOCK));\r
35 Status = PeiServicesAllocatePages (\r
36 EfiBootServicesData,\r
37 PageNumber,\r
38 &TempPtr\r
39 );\r
40\r
41 if (EFI_ERROR (Status)) {\r
42 return NULL;\r
43 }\r
44 ZeroMem ((VOID *) (UINTN) TempPtr, EFI_PAGES_TO_SIZE (PageNumber));\r
45\r
46 //\r
47 // each bit in the bit array represents USBHC_MEM_UNIT\r
48 // bytes of memory in the memory block.\r
49 //\r
50 ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE);\r
51\r
52 Block = (USBHC_MEM_BLOCK *) (UINTN) TempPtr;\r
53 Block->BufLen = EFI_PAGES_TO_SIZE (Pages);\r
54 Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8);\r
55\r
56 PageNumber = EFI_SIZE_TO_PAGES (Block->BitsLen);\r
57 Status = PeiServicesAllocatePages (\r
58 EfiBootServicesData,\r
59 PageNumber,\r
60 &TempPtr\r
61 );\r
62\r
63 if (EFI_ERROR (Status)) {\r
64 return NULL;\r
65 }\r
66 ZeroMem ((VOID *) (UINTN) TempPtr, EFI_PAGES_TO_SIZE (PageNumber));\r
67\r
68 Block->Bits = (UINT8 *) (UINTN) TempPtr;\r
69\r
b575ca32 70 Status = IoMmuAllocateBuffer (\r
d987459f 71 Pages,\r
b575ca32
JY
72 &BufHost,\r
73 &MappedAddr,\r
74 &Mapping\r
d987459f
SZ
75 );\r
76 if (EFI_ERROR (Status)) {\r
77 return NULL;\r
78 }\r
b575ca32 79 ZeroMem ((VOID *) (UINTN) BufHost, EFI_PAGES_TO_SIZE (Pages));\r
d987459f 80\r
b575ca32
JY
81 Block->BufHost = (UINT8 *) (UINTN) BufHost;\r
82 Block->Buf = (UINT8 *) (UINTN) MappedAddr;\r
83 Block->Mapping = Mapping;\r
d987459f
SZ
84 Block->Next = NULL;\r
85\r
86 return Block;\r
87}\r
88\r
89/**\r
90 Free the memory block from the memory pool.\r
91\r
92 @param Pool The memory pool to free the block from.\r
93 @param Block The memory block to free.\r
94\r
95**/\r
96VOID\r
97UsbHcFreeMemBlock (\r
98 IN USBHC_MEM_POOL *Pool,\r
99 IN USBHC_MEM_BLOCK *Block\r
100 )\r
101{\r
102 ASSERT ((Pool != NULL) && (Block != NULL));\r
b575ca32
JY
103\r
104 IoMmuFreeBuffer (EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost, Block->Mapping);\r
105\r
d987459f
SZ
106 //\r
107 // No free memory in PEI.\r
108 //\r
109}\r
110\r
111/**\r
112 Alloc some memory from the block.\r
113\r
114 @param Block The memory block to allocate memory from.\r
115 @param Units Number of memory units to allocate.\r
116\r
117 @return The pointer to the allocated memory.\r
118 If couldn't allocate the needed memory, the return value is NULL.\r
119\r
120**/\r
121VOID *\r
122UsbHcAllocMemFromBlock (\r
123 IN USBHC_MEM_BLOCK *Block,\r
124 IN UINTN Units\r
125 )\r
126{\r
127 UINTN Byte;\r
128 UINT8 Bit;\r
129 UINTN StartByte;\r
130 UINT8 StartBit;\r
131 UINTN Available;\r
132 UINTN Count;\r
133\r
134 ASSERT ((Block != 0) && (Units != 0));\r
135\r
136 StartByte = 0;\r
137 StartBit = 0;\r
138 Available = 0;\r
139\r
140 for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {\r
141 //\r
142 // If current bit is zero, the corresponding memory unit is\r
143 // available, otherwise we need to restart our searching.\r
144 // Available counts the consective number of zero bit.\r
145 //\r
146 if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {\r
147 Available++;\r
148\r
149 if (Available >= Units) {\r
150 break;\r
151 }\r
152\r
153 NEXT_BIT (Byte, Bit);\r
154 } else {\r
155 NEXT_BIT (Byte, Bit);\r
156\r
157 Available = 0;\r
158 StartByte = Byte;\r
159 StartBit = Bit;\r
160 }\r
161 }\r
162\r
163 if (Available < Units) {\r
164 return NULL;\r
165 }\r
166\r
167 //\r
168 // Mark the memory as allocated\r
169 //\r
170 Byte = StartByte;\r
171 Bit = StartBit;\r
172\r
173 for (Count = 0; Count < Units; Count++) {\r
174 ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));\r
175\r
176 Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) USB_HC_BIT (Bit));\r
177 NEXT_BIT (Byte, Bit);\r
178 }\r
179\r
180 return Block->BufHost + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;\r
181}\r
182\r
183/**\r
184 Calculate the corresponding pci bus address according to the Mem parameter.\r
185\r
186 @param Pool The memory pool of the host controller.\r
187 @param Mem The pointer to host memory.\r
188 @param Size The size of the memory region.\r
189\r
190 @return The pci memory address\r
191\r
192**/\r
193EFI_PHYSICAL_ADDRESS\r
194UsbHcGetPciAddrForHostAddr (\r
195 IN USBHC_MEM_POOL *Pool,\r
196 IN VOID *Mem,\r
197 IN UINTN Size\r
198 )\r
199{\r
200 USBHC_MEM_BLOCK *Head;\r
201 USBHC_MEM_BLOCK *Block;\r
202 UINTN AllocSize;\r
203 EFI_PHYSICAL_ADDRESS PhyAddr;\r
204 UINTN Offset;\r
205\r
206 Head = Pool->Head;\r
207 AllocSize = USBHC_MEM_ROUND (Size);\r
208\r
209 if (Mem == NULL) {\r
210 return 0;\r
211 }\r
212\r
213 for (Block = Head; Block != NULL; Block = Block->Next) {\r
214 //\r
215 // scan the memory block list for the memory block that\r
216 // completely contains the allocated memory.\r
217 //\r
218 if ((Block->BufHost <= (UINT8 *) Mem) && (((UINT8 *) Mem + AllocSize) <= (Block->BufHost + Block->BufLen))) {\r
219 break;\r
220 }\r
221 }\r
222\r
223 ASSERT ((Block != NULL));\r
224 //\r
225 // calculate the pci memory address for host memory address.\r
226 //\r
227 Offset = (UINT8 *) Mem - Block->BufHost;\r
228 PhyAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) (Block->Buf + Offset);\r
229 return PhyAddr;\r
230}\r
231\r
232/**\r
233 Calculate the corresponding host address according to the pci address.\r
234\r
235 @param Pool The memory pool of the host controller.\r
236 @param Mem The pointer to pci memory.\r
237 @param Size The size of the memory region.\r
238\r
239 @return The host memory address\r
240\r
241**/\r
242EFI_PHYSICAL_ADDRESS\r
243UsbHcGetHostAddrForPciAddr (\r
244 IN USBHC_MEM_POOL *Pool,\r
245 IN VOID *Mem,\r
246 IN UINTN Size\r
247 )\r
248{\r
249 USBHC_MEM_BLOCK *Head;\r
250 USBHC_MEM_BLOCK *Block;\r
251 UINTN AllocSize;\r
252 EFI_PHYSICAL_ADDRESS HostAddr;\r
253 UINTN Offset;\r
254\r
255 Head = Pool->Head;\r
256 AllocSize = USBHC_MEM_ROUND (Size);\r
257\r
258 if (Mem == NULL) {\r
259 return 0;\r
260 }\r
261\r
262 for (Block = Head; Block != NULL; Block = Block->Next) {\r
263 //\r
264 // scan the memory block list for the memory block that\r
265 // completely contains the allocated memory.\r
266 //\r
267 if ((Block->Buf <= (UINT8 *) Mem) && (((UINT8 *) Mem + AllocSize) <= (Block->Buf + Block->BufLen))) {\r
268 break;\r
269 }\r
270 }\r
271\r
272 ASSERT ((Block != NULL));\r
273 //\r
274 // calculate the host memory address for pci memory address.\r
275 //\r
276 Offset = (UINT8 *) Mem - Block->Buf;\r
277 HostAddr = (EFI_PHYSICAL_ADDRESS) (UINTN) (Block->BufHost + Offset);\r
278 return HostAddr;\r
279}\r
280\r
281/**\r
282 Insert the memory block to the pool's list of the blocks.\r
283\r
284 @param Head The head of the memory pool's block list.\r
285 @param Block The memory block to insert.\r
286\r
287**/\r
288VOID\r
289UsbHcInsertMemBlockToPool (\r
290 IN USBHC_MEM_BLOCK *Head,\r
291 IN USBHC_MEM_BLOCK *Block\r
292 )\r
293{\r
294 ASSERT ((Head != NULL) && (Block != NULL));\r
295 Block->Next = Head->Next;\r
296 Head->Next = Block;\r
297}\r
298\r
299/**\r
300 Is the memory block empty?\r
301\r
302 @param Block The memory block to check.\r
303\r
304 @retval TRUE The memory block is empty.\r
305 @retval FALSE The memory block isn't empty.\r
306\r
307**/\r
308BOOLEAN\r
309UsbHcIsMemBlockEmpty (\r
310 IN USBHC_MEM_BLOCK *Block\r
311 )\r
312{\r
313 UINTN Index;\r
314\r
315 for (Index = 0; Index < Block->BitsLen; Index++) {\r
316 if (Block->Bits[Index] != 0) {\r
317 return FALSE;\r
318 }\r
319 }\r
320\r
321 return TRUE;\r
322}\r
323\r
d987459f 324\r
d987459f
SZ
325\r
326/**\r
327 Initialize the memory management pool for the host controller.\r
328\r
329 @return Pointer to the allocated memory pool or NULL if failed.\r
330\r
331**/\r
332USBHC_MEM_POOL *\r
333UsbHcInitMemPool (\r
334 VOID\r
335 )\r
336{\r
337 USBHC_MEM_POOL *Pool;\r
338 UINTN PageNumber;\r
339 EFI_STATUS Status;\r
340 EFI_PHYSICAL_ADDRESS TempPtr;\r
341\r
342 PageNumber = EFI_SIZE_TO_PAGES (sizeof (USBHC_MEM_POOL));\r
343 Status = PeiServicesAllocatePages (\r
344 EfiBootServicesData,\r
345 PageNumber,\r
346 &TempPtr\r
347 );\r
348 if (EFI_ERROR (Status)) {\r
349 return NULL;\r
350 }\r
351 ZeroMem ((VOID *) (UINTN) TempPtr, EFI_PAGES_TO_SIZE (PageNumber));\r
352\r
353 Pool = (USBHC_MEM_POOL *) ((UINTN) TempPtr);\r
354 Pool->Head = UsbHcAllocMemBlock (USBHC_MEM_DEFAULT_PAGES);\r
355\r
356 if (Pool->Head == NULL) {\r
357 //\r
358 // No free memory in PEI.\r
359 //\r
360 Pool = NULL;\r
361 }\r
362\r
363 return Pool;\r
364}\r
365\r
366/**\r
367 Release the memory management pool.\r
368\r
369 @param Pool The USB memory pool to free.\r
370\r
371**/\r
372VOID\r
373UsbHcFreeMemPool (\r
374 IN USBHC_MEM_POOL *Pool\r
375 )\r
376{\r
377 USBHC_MEM_BLOCK *Block;\r
378\r
379 ASSERT (Pool->Head != NULL);\r
380\r
381 //\r
382 // Unlink all the memory blocks from the pool, then free them.\r
383 // UsbHcUnlinkMemBlock can't be used to unlink and free the\r
384 // first block.\r
385 //\r
386 for (Block = Pool->Head->Next; Block != NULL; Block = Pool->Head->Next) {\r
387 //UsbHcUnlinkMemBlock (Pool->Head, Block);\r
388 UsbHcFreeMemBlock (Pool, Block);\r
389 }\r
390\r
391 UsbHcFreeMemBlock (Pool, Pool->Head);\r
392}\r
393\r
394/**\r
395 Allocate some memory from the host controller's memory pool\r
396 which can be used to communicate with host controller.\r
397\r
398 @param Pool The host controller's memory pool.\r
399 @param Size Size of the memory to allocate.\r
400\r
401 @return The allocated memory or NULL.\r
402\r
403**/\r
404VOID *\r
405UsbHcAllocateMem (\r
406 IN USBHC_MEM_POOL *Pool,\r
407 IN UINTN Size\r
408 )\r
409{\r
410 USBHC_MEM_BLOCK *Head;\r
411 USBHC_MEM_BLOCK *Block;\r
412 USBHC_MEM_BLOCK *NewBlock;\r
413 VOID *Mem;\r
414 UINTN AllocSize;\r
415 UINTN Pages;\r
416\r
417 Mem = NULL;\r
418 AllocSize = USBHC_MEM_ROUND (Size);\r
419 Head = Pool->Head;\r
420 ASSERT (Head != NULL);\r
421\r
422 //\r
423 // First check whether current memory blocks can satisfy the allocation.\r
424 //\r
425 for (Block = Head; Block != NULL; Block = Block->Next) {\r
426 Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);\r
427\r
428 if (Mem != NULL) {\r
429 ZeroMem (Mem, Size);\r
430 break;\r
431 }\r
432 }\r
433\r
434 if (Mem != NULL) {\r
435 return Mem;\r
436 }\r
437\r
438 //\r
439 // Create a new memory block if there is not enough memory\r
440 // in the pool. If the allocation size is larger than the\r
441 // default page number, just allocate a large enough memory\r
442 // block. Otherwise allocate default pages.\r
443 //\r
444 if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {\r
445 Pages = EFI_SIZE_TO_PAGES (AllocSize);\r
446 } else {\r
447 Pages = USBHC_MEM_DEFAULT_PAGES;\r
448 }\r
449 NewBlock = UsbHcAllocMemBlock (Pages);\r
450\r
451 if (NewBlock == NULL) {\r
452 return NULL;\r
453 }\r
454\r
455 //\r
456 // Add the new memory block to the pool, then allocate memory from it\r
457 //\r
458 UsbHcInsertMemBlockToPool (Head, NewBlock);\r
459 Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);\r
460\r
461 if (Mem != NULL) {\r
462 ZeroMem (Mem, Size);\r
463 }\r
464\r
465 return Mem;\r
466}\r
467\r
468/**\r
469 Free the allocated memory back to the memory pool.\r
470\r
471 @param Pool The memory pool of the host controller.\r
472 @param Mem The memory to free.\r
473 @param Size The size of the memory to free.\r
474\r
475**/\r
476VOID\r
477UsbHcFreeMem (\r
478 IN USBHC_MEM_POOL *Pool,\r
479 IN VOID *Mem,\r
480 IN UINTN Size\r
481 )\r
482{\r
483 USBHC_MEM_BLOCK *Head;\r
484 USBHC_MEM_BLOCK *Block;\r
485 UINT8 *ToFree;\r
486 UINTN AllocSize;\r
487 UINTN Byte;\r
488 UINTN Bit;\r
489 UINTN Count;\r
490\r
491 Head = Pool->Head;\r
492 AllocSize = USBHC_MEM_ROUND (Size);\r
493 ToFree = (UINT8 *) Mem;\r
494\r
495 for (Block = Head; Block != NULL; Block = Block->Next) {\r
496 //\r
497 // scan the memory block list for the memory block that\r
498 // completely contains the memory to free.\r
499 //\r
500 if ((Block->BufHost <= ToFree) && ((ToFree + AllocSize) <= (Block->BufHost + Block->BufLen))) {\r
501 //\r
502 // compute the start byte and bit in the bit array\r
503 //\r
504 Byte = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) / 8;\r
505 Bit = ((ToFree - Block->BufHost) / USBHC_MEM_UNIT) % 8;\r
506\r
507 //\r
2048c585 508 // reset associated bits in bit array\r
d987459f
SZ
509 //\r
510 for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {\r
511 ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));\r
512\r
513 Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit));\r
514 NEXT_BIT (Byte, Bit);\r
515 }\r
516\r
517 break;\r
518 }\r
519 }\r
520\r
521 //\r
522 // If Block == NULL, it means that the current memory isn't\r
523 // in the host controller's pool. This is critical because\r
524 // the caller has passed in a wrong memory pointer\r
525 //\r
526 ASSERT (Block != NULL);\r
527\r
528 //\r
529 // Release the current memory block if it is empty and not the head\r
530 //\r
531 if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {\r
532 //UsbHcUnlinkMemBlock (Head, Block);\r
533 UsbHcFreeMemBlock (Pool, Block);\r
534 }\r
535}\r
536\r
537/**\r
538 Allocates pages at a specified alignment.\r
539\r
540 If Alignment is not a power of two and Alignment is not zero, then ASSERT().\r
541\r
542 @param Pages The number of pages to allocate.\r
543 @param Alignment The requested alignment of the allocation. Must be a power of two.\r
544 @param HostAddress The system memory address to map to the PCI controller.\r
545 @param DeviceAddress The resulting map address for the bus master PCI controller to\r
546 use to access the hosts HostAddress.\r
b575ca32 547 @param Mapping A resulting value to pass to Unmap().\r
d987459f
SZ
548\r
549 @retval EFI_SUCCESS Success to allocate aligned pages.\r
550 @retval EFI_INVALID_PARAMETER Pages or Alignment is not valid.\r
551 @retval EFI_OUT_OF_RESOURCES Do not have enough resources to allocate memory.\r
552\r
553**/\r
554EFI_STATUS\r
555UsbHcAllocateAlignedPages (\r
556 IN UINTN Pages,\r
557 IN UINTN Alignment,\r
558 OUT VOID **HostAddress,\r
b575ca32
JY
559 OUT EFI_PHYSICAL_ADDRESS *DeviceAddress,\r
560 OUT VOID **Mapping\r
d987459f
SZ
561 )\r
562{\r
563 EFI_STATUS Status;\r
b575ca32 564 VOID *Memory;\r
d987459f
SZ
565 UINTN AlignedMemory;\r
566 UINTN AlignmentMask;\r
b575ca32
JY
567 EFI_PHYSICAL_ADDRESS DeviceMemory;\r
568 UINTN AlignedDeviceMemory;\r
d987459f
SZ
569 UINTN RealPages;\r
570\r
571 //\r
572 // Alignment must be a power of two or zero.\r
573 //\r
574 ASSERT ((Alignment & (Alignment - 1)) == 0);\r
575\r
576 if ((Alignment & (Alignment - 1)) != 0) {\r
577 return EFI_INVALID_PARAMETER;\r
578 }\r
579\r
580 if (Pages == 0) {\r
581 return EFI_INVALID_PARAMETER;\r
582 }\r
583\r
584 if (Alignment > EFI_PAGE_SIZE) {\r
585 //\r
e50a226b 586 // Calculate the total number of pages since alignment is larger than page size.\r
d987459f
SZ
587 //\r
588 AlignmentMask = Alignment - 1;\r
589 RealPages = Pages + EFI_SIZE_TO_PAGES (Alignment);\r
590 //\r
591 // Make sure that Pages plus EFI_SIZE_TO_PAGES (Alignment) does not overflow.\r
592 //\r
593 ASSERT (RealPages > Pages);\r
594\r
b575ca32 595 Status = IoMmuAllocateBuffer (\r
d987459f 596 Pages,\r
b575ca32
JY
597 &Memory,\r
598 &DeviceMemory,\r
599 Mapping\r
d987459f
SZ
600 );\r
601 if (EFI_ERROR (Status)) {\r
602 return EFI_OUT_OF_RESOURCES;\r
603 }\r
604 AlignedMemory = ((UINTN) Memory + AlignmentMask) & ~AlignmentMask;\r
b575ca32 605 AlignedDeviceMemory = ((UINTN) DeviceMemory + AlignmentMask) & ~AlignmentMask;\r
d987459f
SZ
606 } else {\r
607 //\r
608 // Do not over-allocate pages in this case.\r
609 //\r
b575ca32 610 Status = IoMmuAllocateBuffer (\r
d987459f 611 Pages,\r
b575ca32
JY
612 &Memory,\r
613 &DeviceMemory,\r
614 Mapping\r
d987459f
SZ
615 );\r
616 if (EFI_ERROR (Status)) {\r
617 return EFI_OUT_OF_RESOURCES;\r
618 }\r
619 AlignedMemory = (UINTN) Memory;\r
b575ca32 620 AlignedDeviceMemory = (UINTN) DeviceMemory;\r
d987459f
SZ
621 }\r
622\r
623 *HostAddress = (VOID *) AlignedMemory;\r
b575ca32 624 *DeviceAddress = (EFI_PHYSICAL_ADDRESS) AlignedDeviceMemory;\r
d987459f
SZ
625\r
626 return EFI_SUCCESS;\r
627}\r
628\r
629/**\r
630 Frees memory that was allocated with UsbHcAllocateAlignedPages().\r
631\r
632 @param HostAddress The system memory address to map to the PCI controller.\r
633 @param Pages The number of pages to free.\r
b575ca32 634 @param Mapping The mapping value returned from Map().\r
d987459f
SZ
635\r
636**/\r
637VOID\r
638UsbHcFreeAlignedPages (\r
639 IN VOID *HostAddress,\r
b575ca32
JY
640 IN UINTN Pages,\r
641 IN VOID *Mapping\r
d987459f
SZ
642 )\r
643{\r
644 ASSERT (Pages != 0);\r
b575ca32
JY
645\r
646 IoMmuFreeBuffer (Pages, HostAddress, Mapping);\r
d987459f
SZ
647}\r
648\r