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