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