]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Bus/Pci/EhciPei/UsbHcMem.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Bus / Pci / EhciPei / UsbHcMem.c
CommitLineData
4b1bf81c 1/** @file\r
2PEIM to produce gPeiUsb2HostControllerPpiGuid based on gPeiUsbControllerPpiGuid\r
3which is used to enable recovery function from USB Drivers.\r
4\r
d1102dba
LG
5Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>\r
6\r
9d510e61 7SPDX-License-Identifier: BSD-2-Clause-Patent\r
4b1bf81c 8\r
9**/\r
10\r
11#include "EhcPeim.h"\r
12\r
13/**\r
14 Allocate a block of memory to be used by the buffer pool.\r
15\r
16 @param Ehc The EHCI device.\r
17 @param Pool The buffer pool to allocate memory for.\r
18 @param Pages How many pages to allocate.\r
19\r
20 @return The allocated memory block or NULL if failed.\r
21\r
22**/\r
23USBHC_MEM_BLOCK *\r
24UsbHcAllocMemBlock (\r
25 IN PEI_USB2_HC_DEV *Ehc,\r
26 IN USBHC_MEM_POOL *Pool,\r
27 IN UINTN Pages\r
28 )\r
29{\r
30 USBHC_MEM_BLOCK *Block;\r
31 VOID *BufHost;\r
32 VOID *Mapping;\r
33 EFI_PHYSICAL_ADDRESS MappedAddr;\r
34 EFI_STATUS Status;\r
35 UINTN PageNumber;\r
36 EFI_PHYSICAL_ADDRESS TempPtr;\r
37\r
38 Mapping = NULL;\r
39 PageNumber = sizeof(USBHC_MEM_BLOCK)/PAGESIZE +1;\r
40 Status = PeiServicesAllocatePages (\r
41 EfiBootServicesCode,\r
42 PageNumber,\r
43 &TempPtr\r
44 );\r
45\r
46 if (EFI_ERROR (Status)) {\r
47 return NULL;\r
48 }\r
49 ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);\r
d1102dba 50\r
4b1bf81c 51 //\r
52 // each bit in the bit array represents USBHC_MEM_UNIT\r
53 // bytes of memory in the memory block.\r
54 //\r
55 ASSERT (USBHC_MEM_UNIT * 8 <= EFI_PAGE_SIZE);\r
d1102dba 56\r
4b1bf81c 57 Block = (USBHC_MEM_BLOCK*)(UINTN)TempPtr;\r
58 Block->BufLen = EFI_PAGES_TO_SIZE (Pages);\r
59 Block->BitsLen = Block->BufLen / (USBHC_MEM_UNIT * 8);\r
d1102dba 60\r
4b1bf81c 61 PageNumber = (Block->BitsLen)/PAGESIZE +1;\r
62 Status = PeiServicesAllocatePages (\r
63 EfiBootServicesCode,\r
64 PageNumber,\r
65 &TempPtr\r
66 );\r
d1102dba 67\r
4b1bf81c 68 if (EFI_ERROR (Status)) {\r
69 return NULL;\r
70 }\r
d1102dba 71 ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);\r
4b1bf81c 72\r
73 Block->Bits = (UINT8 *)(UINTN)TempPtr;\r
74\r
2c656af0
SZ
75 Status = IoMmuAllocateBuffer (\r
76 Ehc->IoMmu,\r
4b1bf81c 77 Pages,\r
2c656af0
SZ
78 (VOID **) &BufHost,\r
79 &MappedAddr,\r
80 &Mapping\r
4b1bf81c 81 );\r
2c656af0
SZ
82 if (EFI_ERROR (Status)) {\r
83 return NULL;\r
84 }\r
85 ZeroMem (BufHost, Pages*EFI_PAGE_SIZE);\r
4b1bf81c 86\r
4b1bf81c 87 //\r
88 // Check whether the data structure used by the host controller\r
89 // should be restricted into the same 4G\r
90 //\r
91 if (Pool->Check4G && (Pool->Which4G != USB_HC_HIGH_32BIT (MappedAddr))) {\r
92 return NULL;\r
93 }\r
94\r
95 Block->BufHost = BufHost;\r
96 Block->Buf = (UINT8 *) ((UINTN) MappedAddr);\r
97 Block->Mapping = Mapping;\r
98 Block->Next = NULL;\r
99\r
100 return Block;\r
101\r
102}\r
103\r
104/**\r
105 Free the memory block from the memory pool.\r
106\r
2c656af0 107 @param Ehc The EHCI device.\r
4b1bf81c 108 @param Pool The memory pool to free the block from.\r
109 @param Block The memory block to free.\r
110\r
111**/\r
112VOID\r
113UsbHcFreeMemBlock (\r
2c656af0 114 IN PEI_USB2_HC_DEV *Ehc,\r
4b1bf81c 115 IN USBHC_MEM_POOL *Pool,\r
116 IN USBHC_MEM_BLOCK *Block\r
117 )\r
118{\r
119 ASSERT ((Pool != NULL) && (Block != NULL));\r
2c656af0
SZ
120\r
121 IoMmuFreeBuffer (Ehc->IoMmu, EFI_SIZE_TO_PAGES (Block->BufLen), Block->BufHost, Block->Mapping);\r
4b1bf81c 122}\r
123\r
124/**\r
125 Alloc some memory from the block.\r
126\r
127 @param Block The memory block to allocate memory from.\r
128 @param Units Number of memory units to allocate.\r
129\r
130 @return The pointer to the allocated memory. If couldn't allocate the needed memory,\r
131 the return value is NULL.\r
132\r
133**/\r
134VOID *\r
135UsbHcAllocMemFromBlock (\r
136 IN USBHC_MEM_BLOCK *Block,\r
137 IN UINTN Units\r
138 )\r
139{\r
140 UINTN Byte;\r
141 UINT8 Bit;\r
142 UINTN StartByte;\r
143 UINT8 StartBit;\r
144 UINTN Available;\r
145 UINTN Count;\r
146\r
147 ASSERT ((Block != 0) && (Units != 0));\r
148\r
149 StartByte = 0;\r
150 StartBit = 0;\r
151 Available = 0;\r
152\r
153 for (Byte = 0, Bit = 0; Byte < Block->BitsLen;) {\r
154 //\r
155 // If current bit is zero, the corresponding memory unit is\r
156 // available, otherwise we need to restart our searching.\r
157 // Available counts the consective number of zero bit.\r
158 //\r
159 if (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit)) {\r
160 Available++;\r
161\r
162 if (Available >= Units) {\r
163 break;\r
164 }\r
165\r
166 NEXT_BIT (Byte, Bit);\r
167\r
168 } else {\r
169 NEXT_BIT (Byte, Bit);\r
170\r
171 Available = 0;\r
172 StartByte = Byte;\r
173 StartBit = Bit;\r
174 }\r
175 }\r
176\r
177 if (Available < Units) {\r
178 return NULL;\r
179 }\r
d1102dba 180\r
4b1bf81c 181 //\r
182 // Mark the memory as allocated\r
183 //\r
184 Byte = StartByte;\r
185 Bit = StartBit;\r
186\r
187 for (Count = 0; Count < Units; Count++) {\r
188 ASSERT (!USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));\r
189\r
190 Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] | (UINT8) USB_HC_BIT (Bit));\r
191 NEXT_BIT (Byte, Bit);\r
192 }\r
193\r
194 return Block->Buf + (StartByte * 8 + StartBit) * USBHC_MEM_UNIT;\r
195}\r
196\r
2c656af0
SZ
197/**\r
198 Calculate the corresponding pci bus address according to the Mem parameter.\r
199\r
200 @param Pool The memory pool of the host controller.\r
201 @param Mem The pointer to host memory.\r
202 @param Size The size of the memory region.\r
203\r
204 @return the pci memory address\r
205**/\r
206EFI_PHYSICAL_ADDRESS\r
207UsbHcGetPciAddressForHostMem (\r
208 IN USBHC_MEM_POOL *Pool,\r
209 IN VOID *Mem,\r
210 IN UINTN Size\r
211 )\r
212{\r
213 USBHC_MEM_BLOCK *Head;\r
214 USBHC_MEM_BLOCK *Block;\r
215 UINTN AllocSize;\r
216 EFI_PHYSICAL_ADDRESS PhyAddr;\r
217 UINTN Offset;\r
218\r
219 Head = Pool->Head;\r
220 AllocSize = USBHC_MEM_ROUND (Size);\r
221\r
222 if (Mem == NULL) {\r
223 return 0;\r
224 }\r
225\r
226 for (Block = Head; Block != NULL; Block = Block->Next) {\r
227 //\r
228 // scan the memory block list for the memory block that\r
229 // completely contains the allocated memory.\r
230 //\r
231 if ((Block->BufHost <= (UINT8 *) Mem) && (((UINT8 *) Mem + AllocSize) <= (Block->BufHost + Block->BufLen))) {\r
232 break;\r
233 }\r
234 }\r
235\r
236 ASSERT ((Block != NULL));\r
237 //\r
238 // calculate the pci memory address for host memory address.\r
239 //\r
240 Offset = (UINT8 *)Mem - Block->BufHost;\r
241 PhyAddr = (EFI_PHYSICAL_ADDRESS)(UINTN) (Block->Buf + Offset);\r
242 return PhyAddr;\r
243}\r
244\r
4b1bf81c 245/**\r
246 Insert the memory block to the pool's list of the blocks.\r
247\r
248 @param Head The head of the memory pool's block list.\r
249 @param Block The memory block to insert.\r
250\r
251**/\r
252VOID\r
253UsbHcInsertMemBlockToPool (\r
254 IN USBHC_MEM_BLOCK *Head,\r
255 IN USBHC_MEM_BLOCK *Block\r
256 )\r
257{\r
258 ASSERT ((Head != NULL) && (Block != NULL));\r
259 Block->Next = Head->Next;\r
260 Head->Next = Block;\r
261}\r
262\r
263/**\r
264 Is the memory block empty?\r
265\r
266 @param Block The memory block to check.\r
267\r
268 @retval TRUE The memory block is empty.\r
269 @retval FALSE The memory block isn't empty.\r
270\r
271**/\r
272BOOLEAN\r
273UsbHcIsMemBlockEmpty (\r
274 IN USBHC_MEM_BLOCK *Block\r
275 )\r
276{\r
277 UINTN Index;\r
278\r
d1102dba 279\r
4b1bf81c 280 for (Index = 0; Index < Block->BitsLen; Index++) {\r
281 if (Block->Bits[Index] != 0) {\r
282 return FALSE;\r
283 }\r
284 }\r
285\r
286 return TRUE;\r
287}\r
288\r
4b1bf81c 289\r
290/**\r
291 Initialize the memory management pool for the host controller.\r
d1102dba 292\r
4b1bf81c 293 @param Ehc The EHCI device.\r
294 @param Check4G Whether the host controller requires allocated memory.\r
295 from one 4G address space.\r
296 @param Which4G The 4G memory area each memory allocated should be from.\r
297\r
298 @retval EFI_SUCCESS The memory pool is initialized.\r
299 @retval EFI_OUT_OF_RESOURCE Fail to init the memory pool.\r
300\r
301**/\r
302USBHC_MEM_POOL *\r
303UsbHcInitMemPool (\r
304 IN PEI_USB2_HC_DEV *Ehc,\r
305 IN BOOLEAN Check4G,\r
306 IN UINT32 Which4G\r
307 )\r
308{\r
309 USBHC_MEM_POOL *Pool;\r
310 UINTN PageNumber;\r
311 EFI_STATUS Status;\r
312 EFI_PHYSICAL_ADDRESS TempPtr;\r
d1102dba 313\r
4b1bf81c 314 PageNumber = sizeof(USBHC_MEM_POOL)/PAGESIZE +1;\r
315 Status = PeiServicesAllocatePages (\r
316 EfiBootServicesCode,\r
317 PageNumber,\r
318 &TempPtr\r
319 );\r
320\r
321 if (EFI_ERROR (Status)) {\r
322 return NULL;\r
323 }\r
d1102dba 324 ZeroMem ((VOID *)(UINTN)TempPtr, PageNumber*EFI_PAGE_SIZE);\r
4b1bf81c 325\r
d1102dba 326 Pool = (USBHC_MEM_POOL *) ((UINTN) TempPtr);\r
4b1bf81c 327\r
328 Pool->Check4G = Check4G;\r
329 Pool->Which4G = Which4G;\r
330 Pool->Head = UsbHcAllocMemBlock (Ehc, Pool, USBHC_MEM_DEFAULT_PAGES);\r
331\r
332 if (Pool->Head == NULL) {\r
333 Pool = NULL;\r
334 }\r
335\r
336 return Pool;\r
337}\r
338\r
339/**\r
340 Release the memory management pool.\r
2c656af0
SZ
341\r
342 @param Ehc The EHCI device.\r
4b1bf81c 343 @param Pool The USB memory pool to free.\r
344\r
345 @retval EFI_DEVICE_ERROR Fail to free the memory pool.\r
346 @retval EFI_SUCCESS The memory pool is freed.\r
347\r
348**/\r
349EFI_STATUS\r
350UsbHcFreeMemPool (\r
2c656af0 351 IN PEI_USB2_HC_DEV *Ehc,\r
4b1bf81c 352 IN USBHC_MEM_POOL *Pool\r
353 )\r
354{\r
355 USBHC_MEM_BLOCK *Block;\r
356\r
357 ASSERT (Pool->Head != NULL);\r
358\r
359 //\r
360 // Unlink all the memory blocks from the pool, then free them.\r
4b1bf81c 361 //\r
2c656af0
SZ
362 for (Block = Pool->Head->Next; Block != NULL; Block = Block->Next) {\r
363 UsbHcFreeMemBlock (Ehc, Pool, Block);\r
4b1bf81c 364 }\r
365\r
2c656af0 366 UsbHcFreeMemBlock (Ehc, Pool, Pool->Head);\r
4b1bf81c 367\r
368 return EFI_SUCCESS;\r
369}\r
370\r
371/**\r
372 Allocate some memory from the host controller's memory pool\r
373 which can be used to communicate with host controller.\r
d1102dba 374\r
4b1bf81c 375 @param Ehc The EHCI device.\r
376 @param Pool The host controller's memory pool.\r
377 @param Size Size of the memory to allocate.\r
378\r
379 @return The allocated memory or NULL.\r
380\r
381**/\r
382VOID *\r
383UsbHcAllocateMem (\r
384 IN PEI_USB2_HC_DEV *Ehc,\r
385 IN USBHC_MEM_POOL *Pool,\r
386 IN UINTN Size\r
387 )\r
388{\r
389 USBHC_MEM_BLOCK *Head;\r
390 USBHC_MEM_BLOCK *Block;\r
391 USBHC_MEM_BLOCK *NewBlock;\r
392 VOID *Mem;\r
393 UINTN AllocSize;\r
394 UINTN Pages;\r
395\r
396 Mem = NULL;\r
397 AllocSize = USBHC_MEM_ROUND (Size);\r
398 Head = Pool->Head;\r
399 ASSERT (Head != NULL);\r
400\r
401 //\r
402 // First check whether current memory blocks can satisfy the allocation.\r
403 //\r
404 for (Block = Head; Block != NULL; Block = Block->Next) {\r
405 Mem = UsbHcAllocMemFromBlock (Block, AllocSize / USBHC_MEM_UNIT);\r
406\r
407 if (Mem != NULL) {\r
408 ZeroMem (Mem, Size);\r
409 break;\r
410 }\r
411 }\r
412\r
413 if (Mem != NULL) {\r
414 return Mem;\r
415 }\r
416\r
417 //\r
418 // Create a new memory block if there is not enough memory\r
419 // in the pool. If the allocation size is larger than the\r
420 // default page number, just allocate a large enough memory\r
421 // block. Otherwise allocate default pages.\r
422 //\r
423 if (AllocSize > EFI_PAGES_TO_SIZE (USBHC_MEM_DEFAULT_PAGES)) {\r
424 Pages = EFI_SIZE_TO_PAGES (AllocSize) + 1;\r
425 } else {\r
426 Pages = USBHC_MEM_DEFAULT_PAGES;\r
427 }\r
428 NewBlock = UsbHcAllocMemBlock (Ehc,Pool, Pages);\r
429\r
430 if (NewBlock == NULL) {\r
431 return NULL;\r
432 }\r
d1102dba 433\r
4b1bf81c 434 //\r
435 // Add the new memory block to the pool, then allocate memory from it\r
436 //\r
437 UsbHcInsertMemBlockToPool (Head, NewBlock);\r
438 Mem = UsbHcAllocMemFromBlock (NewBlock, AllocSize / USBHC_MEM_UNIT);\r
439\r
440 if (Mem != NULL) {\r
441 ZeroMem (Mem, Size);\r
442 }\r
443\r
444 return Mem;\r
445}\r
446\r
447/**\r
448 Free the allocated memory back to the memory pool.\r
449\r
2c656af0 450 @param Ehc The EHCI device.\r
4b1bf81c 451 @param Pool The memory pool of the host controller.\r
452 @param Mem The memory to free.\r
453 @param Size The size of the memory to free.\r
454\r
455**/\r
456VOID\r
457UsbHcFreeMem (\r
2c656af0 458 IN PEI_USB2_HC_DEV *Ehc,\r
4b1bf81c 459 IN USBHC_MEM_POOL *Pool,\r
460 IN VOID *Mem,\r
461 IN UINTN Size\r
462 )\r
463{\r
464 USBHC_MEM_BLOCK *Head;\r
465 USBHC_MEM_BLOCK *Block;\r
466 UINT8 *ToFree;\r
467 UINTN AllocSize;\r
468 UINTN Byte;\r
469 UINTN Bit;\r
470 UINTN Count;\r
471\r
472 Head = Pool->Head;\r
473 AllocSize = USBHC_MEM_ROUND (Size);\r
474 ToFree = (UINT8 *) Mem;\r
475\r
476 for (Block = Head; Block != NULL; Block = Block->Next) {\r
477 //\r
478 // scan the memory block list for the memory block that\r
479 // completely contains the memory to free.\r
480 //\r
481 if ((Block->Buf <= ToFree) && ((ToFree + AllocSize) <= (Block->Buf + Block->BufLen))) {\r
482 //\r
483 // compute the start byte and bit in the bit array\r
484 //\r
485 Byte = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) / 8;\r
486 Bit = ((ToFree - Block->Buf) / USBHC_MEM_UNIT) % 8;\r
487\r
488 //\r
2048c585 489 // reset associated bits in bit array\r
4b1bf81c 490 //\r
491 for (Count = 0; Count < (AllocSize / USBHC_MEM_UNIT); Count++) {\r
492 ASSERT (USB_HC_BIT_IS_SET (Block->Bits[Byte], Bit));\r
493\r
494 Block->Bits[Byte] = (UINT8) (Block->Bits[Byte] ^ USB_HC_BIT (Bit));\r
495 NEXT_BIT (Byte, Bit);\r
496 }\r
497\r
498 break;\r
499 }\r
500 }\r
501\r
502 //\r
503 // If Block == NULL, it means that the current memory isn't\r
504 // in the host controller's pool. This is critical because\r
505 // the caller has passed in a wrong memory point\r
506 //\r
507 ASSERT (Block != NULL);\r
508\r
509 //\r
510 // Release the current memory block if it is empty and not the head\r
511 //\r
512 if ((Block != Head) && UsbHcIsMemBlockEmpty (Block)) {\r
2c656af0 513 UsbHcFreeMemBlock (Ehc, Pool, Block);\r
4b1bf81c 514 }\r
515\r
516 return ;\r
517}\r