]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c
OvmfPkg BasePciCapLib: Fix VS build failure
[mirror_edk2.git] / OvmfPkg / Library / BasePciCapLib / BasePciCapLib.c
CommitLineData
392a3146
LE
1/** @file\r
2 Work with PCI capabilities in PCI config space.\r
3\r
4 Provides functions to parse capabilities lists, and to locate, describe, read\r
5 and write capabilities. PCI config space access is abstracted away.\r
6\r
7 Copyright (C) 2018, Red Hat, Inc.\r
8\r
9 This program and the accompanying materials are licensed and made available\r
10 under the terms and conditions of the BSD License which accompanies this\r
11 distribution. The full text of the license may be found at\r
12 http://opensource.org/licenses/bsd-license.php\r
13\r
14 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT\r
15 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
16**/\r
17\r
18#include <IndustryStandard/PciExpress21.h>\r
19\r
20#include <Library/BaseMemoryLib.h>\r
21#include <Library/DebugLib.h>\r
22#include <Library/MemoryAllocationLib.h>\r
23\r
24#include "BasePciCapLib.h"\r
25\r
26\r
27/**\r
28 Compare a standalone PCI_CAP_KEY against a PCI_CAP containing an embedded\r
29 PCI_CAP_KEY.\r
30\r
31 @param[in] PciCapKey Pointer to the bare PCI_CAP_KEY.\r
32\r
33 @param[in] PciCap Pointer to the PCI_CAP with the embedded PCI_CAP_KEY.\r
34\r
35 @retval <0 If PciCapKey compares less than PciCap->Key.\r
36\r
37 @retval 0 If PciCapKey compares equal to PciCap->Key.\r
38\r
39 @retval >0 If PciCapKey compares greater than PciCap->Key.\r
40**/\r
41STATIC\r
42INTN\r
43EFIAPI\r
44ComparePciCapKey (\r
45 IN CONST VOID *PciCapKey,\r
46 IN CONST VOID *PciCap\r
47 )\r
48{\r
49 CONST PCI_CAP_KEY *Key1;\r
50 CONST PCI_CAP_KEY *Key2;\r
51\r
52 Key1 = PciCapKey;\r
53 Key2 = &((CONST PCI_CAP *)PciCap)->Key;\r
54\r
55 if (Key1->Domain < Key2->Domain) {\r
56 return -1;\r
57 }\r
58 if (Key1->Domain > Key2->Domain) {\r
59 return 1;\r
60 }\r
61 if (Key1->CapId < Key2->CapId) {\r
62 return -1;\r
63 }\r
64 if (Key1->CapId > Key2->CapId) {\r
65 return 1;\r
66 }\r
67 if (Key1->Instance < Key2->Instance) {\r
68 return -1;\r
69 }\r
70 if (Key1->Instance > Key2->Instance) {\r
71 return 1;\r
72 }\r
73 return 0;\r
74}\r
75\r
76\r
77/**\r
78 Compare two PCI_CAP objects based on PCI_CAP.Key.\r
79\r
80 @param[in] PciCap1 Pointer to the first PCI_CAP.\r
81\r
82 @param[in] PciCap2 Pointer to the second PCI_CAP.\r
83\r
84 @retval <0 If PciCap1 compares less than PciCap2.\r
85\r
86 @retval 0 If PciCap1 compares equal to PciCap2.\r
87\r
88 @retval >0 If PciCap1 compares greater than PciCap2.\r
89**/\r
90STATIC\r
91INTN\r
92EFIAPI\r
93ComparePciCap (\r
94 IN CONST VOID *PciCap1,\r
95 IN CONST VOID *PciCap2\r
96 )\r
97{\r
98 CONST PCI_CAP_KEY *PciCap1Key;\r
99\r
100 PciCap1Key = &((CONST PCI_CAP *)PciCap1)->Key;\r
101 return ComparePciCapKey (PciCap1Key, PciCap2);\r
102}\r
103\r
104\r
105/**\r
106 Compare the standalone UINT16 config space offset of a capability header\r
107 against a PCI_CAP containing an embedded Offset.\r
108\r
109 @param[in] CapHdrOffset Pointer to the bare UINT16 config space offset.\r
110\r
111 @param[in] PciCap Pointer to the PCI_CAP with the embedded Offset.\r
112\r
113 @retval <0 If CapHdrOffset compares less than PciCap->Offset.\r
114\r
115 @retval 0 If CapHdrOffset compares equal to PciCap->Offset.\r
116\r
117 @retval >0 If CapHdrOffset compares greater than PciCap->Offset.\r
118**/\r
119STATIC\r
120INTN\r
121EFIAPI\r
122ComparePciCapOffsetKey (\r
123 IN CONST VOID *CapHdrOffset,\r
124 IN CONST VOID *PciCap\r
125 )\r
126{\r
127 UINT16 Offset1;\r
128 UINT16 Offset2;\r
129\r
130 Offset1 = *(CONST UINT16 *)CapHdrOffset;\r
131 Offset2 = ((CONST PCI_CAP *)PciCap)->Offset;\r
132 //\r
133 // Note: both Offset1 and Offset2 are promoted to INT32 below, and the\r
134 // subtraction takes place between INT32 values.\r
135 //\r
136 return Offset1 - Offset2;\r
137}\r
138\r
139\r
140/**\r
141 Compare two PCI_CAP objects based on PCI_CAP.Offset.\r
142\r
143 @param[in] PciCap1 Pointer to the first PCI_CAP.\r
144\r
145 @param[in] PciCap2 Pointer to the second PCI_CAP.\r
146\r
147 @retval <0 If PciCap1 compares less than PciCap2.\r
148\r
149 @retval 0 If PciCap1 compares equal to PciCap2.\r
150\r
151 @retval >0 If PciCap1 compares greater than PciCap2.\r
152**/\r
153STATIC\r
154INTN\r
155EFIAPI\r
156ComparePciCapOffset (\r
157 IN CONST VOID *PciCap1,\r
158 IN CONST VOID *PciCap2\r
159 )\r
160{\r
161 UINT16 Offset1;\r
162 UINT16 Offset2;\r
163\r
164 Offset1 = ((CONST PCI_CAP *)PciCap1)->Offset;\r
165 Offset2 = ((CONST PCI_CAP *)PciCap2)->Offset;\r
166 //\r
167 // Note: both Offset1 and Offset2 are promoted to INT32 below, and the\r
168 // subtraction takes place between INT32 values.\r
169 //\r
170 return Offset1 - Offset2;\r
171}\r
172\r
173\r
174/**\r
175 Insert a new instance of the PCI capability given by (Domain, CapId) in\r
176 CapList.\r
177\r
178 @param[in,out] CapList The PCI_CAP_LIST into which the new PCI_CAP\r
179 should be inserted. CapList will own the new\r
180 PCI_CAP structure.\r
181\r
182 @param[in,out] CapHdrOffsets Link the new PCI_CAP structure into the\r
183 (non-owning) CapHdrOffsets collection as well.\r
184 CapHdrOffsets orders the PCI_CAP structures\r
185 based on the PCI_CAP.Offset member, and enables\r
186 the calculation of PCI_CAP.MaxSizeHint.\r
187\r
188 @param[in] Domain Whether the capability is normal or extended.\r
189\r
190 @param[in] CapId Capability ID (specific to Domain).\r
191\r
192 @param[in] Offset Config space offset at which the standard\r
193 header of the capability starts. The caller is\r
194 responsible for ensuring that Offset be DWORD\r
195 aligned. The caller is also responsible for\r
196 ensuring that Offset be within the config space\r
197 identified by Domain.\r
198\r
199 @param[in] Version The version number of the capability. The\r
200 caller is responsible for passing 0 as Version\r
201 if Domain is PciCapNormal.\r
202\r
203 @retval RETURN_SUCCESS Insertion successful.\r
204\r
205 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
206\r
207 @retval RETURN_DEVICE_ERROR A PCI_CAP with Offset is already linked by\r
208 CapHdrOffsets. This indicates a loop in the\r
209 capabilities list being parsed.\r
210**/\r
211STATIC\r
212RETURN_STATUS\r
213InsertPciCap (\r
214 IN OUT PCI_CAP_LIST *CapList,\r
215 IN OUT ORDERED_COLLECTION *CapHdrOffsets,\r
216 IN PCI_CAP_DOMAIN Domain,\r
217 IN UINT16 CapId,\r
218 IN UINT16 Offset,\r
219 IN UINT8 Version\r
220 )\r
221{\r
222 PCI_CAP *PciCap;\r
223 RETURN_STATUS Status;\r
224 ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
225 PCI_CAP *InstanceZero;\r
226\r
227 ASSERT ((Offset & 0x3) == 0);\r
228 ASSERT (Offset < (Domain == PciCapNormal ?\r
229 PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET));\r
230 ASSERT (Domain == PciCapExtended || Version == 0);\r
231\r
232 //\r
233 // Set InstanceZero to suppress incorrect compiler/analyzer warnings.\r
234 //\r
235 InstanceZero = NULL;\r
236\r
237 //\r
238 // Allocate PciCap, and populate it assuming it is the first occurrence of\r
239 // (Domain, CapId). Note that PciCap->MaxSizeHint is not assigned the final\r
240 // value just yet.\r
241 //\r
242 PciCap = AllocatePool (sizeof *PciCap);\r
243 if (PciCap == NULL) {\r
244 return RETURN_OUT_OF_RESOURCES;\r
245 }\r
246 PciCap->Key.Domain = Domain;\r
247 PciCap->Key.CapId = CapId;\r
248 PciCap->Key.Instance = 0;\r
249 PciCap->NumInstancesUnion.NumInstances = 1;\r
250 PciCap->Offset = Offset;\r
251 PciCap->MaxSizeHint = 0;\r
252 PciCap->Version = Version;\r
253\r
254 //\r
255 // Add PciCap to CapList.\r
256 //\r
257 Status = OrderedCollectionInsert (CapList->Capabilities, &PciCapEntry,\r
258 PciCap);\r
259 if (RETURN_ERROR (Status)) {\r
260 if (Status == RETURN_OUT_OF_RESOURCES) {\r
261 goto FreePciCap;\r
262 }\r
263 ASSERT (Status == RETURN_ALREADY_STARTED);\r
264 //\r
265 // PciCap is not the first instance of (Domain, CapId). Add it as a new\r
266 // instance, taking the current instance count from Instance#0. Note that\r
267 // we don't bump the instance count maintained in Instance#0 just yet, to\r
268 // keep rollback on errors simple.\r
269 //\r
270 InstanceZero = OrderedCollectionUserStruct (PciCapEntry);\r
271 PciCap->Key.Instance = InstanceZero->NumInstancesUnion.NumInstances;\r
272 PciCap->NumInstancesUnion.InstanceZero = InstanceZero;\r
273\r
274 ASSERT (PciCap->Key.Instance > 0);\r
275 Status = OrderedCollectionInsert (CapList->Capabilities, &PciCapEntry,\r
276 PciCap);\r
277 if (Status == RETURN_OUT_OF_RESOURCES) {\r
278 goto FreePciCap;\r
279 }\r
280 }\r
281 //\r
282 // At this point, PciCap has been inserted in CapList->Capabilities, either\r
283 // with Instance==0 or with Instance>0. PciCapEntry is the iterator that\r
284 // links PciCap.\r
285 //\r
286 ASSERT_RETURN_ERROR (Status);\r
287\r
288 //\r
289 // Link PciCap into CapHdrOffsets too, to order it globally based on config\r
290 // space offset. Note that partial overlaps between capability headers is not\r
291 // possible: Offset is DWORD aligned, normal capability headers are 16-bit\r
292 // wide, and extended capability headers are 32-bit wide. Therefore any two\r
293 // capability headers either are distinct or start at the same offset\r
294 // (implying a loop in the respective capabilities list).\r
295 //\r
296 Status = OrderedCollectionInsert (CapHdrOffsets, NULL, PciCap);\r
297 if (RETURN_ERROR (Status)) {\r
298 if (Status == RETURN_ALREADY_STARTED) {\r
299 //\r
300 // Loop found; map return status accordingly.\r
301 //\r
302 Status = RETURN_DEVICE_ERROR;\r
303 }\r
304 goto DeletePciCapFromCapList;\r
305 }\r
306\r
307 //\r
308 // Now we can bump the instance count maintained in Instance#0, if PciCap is\r
309 // not the first instance of (Domain, CapId).\r
310 //\r
311 if (PciCap->Key.Instance > 0) {\r
312 InstanceZero->NumInstancesUnion.NumInstances++;\r
313 }\r
314 return RETURN_SUCCESS;\r
315\r
316DeletePciCapFromCapList:\r
317 OrderedCollectionDelete (CapList->Capabilities, PciCapEntry, NULL);\r
318\r
319FreePciCap:\r
320 FreePool (PciCap);\r
321\r
322 return Status;\r
323}\r
324\r
325\r
326/**\r
327 Calculate the MaxSizeHint member for a PCI_CAP object.\r
328\r
329 CalculatePciCapMaxSizeHint() may only be called once all capability instances\r
330 have been successfully processed by InsertPciCap().\r
331\r
332 @param[in,out] PciCap The PCI_CAP object for which to calculate the\r
333 MaxSizeHint member. The caller is responsible for\r
334 passing a PCI_CAP object that has been created by a\r
335 successful invocation of InsertPciCap().\r
336\r
337 @param[in] NextPciCap If NextPciCap is NULL, then the caller is responsible\r
338 for PciCap to represent the capability instance with\r
339 the highest header offset in all config space. If\r
340 NextPciCap is not NULL, then the caller is responsible\r
341 for (a) having created NextPciCap with a successful\r
342 invocation of InsertPciCap(), and (b) NextPciCap being\r
343 the direct successor of PciCap in config space offset\r
344 order, as ordered by ComparePciCapOffset().\r
345**/\r
346STATIC\r
347VOID\r
348CalculatePciCapMaxSizeHint (\r
349 IN OUT PCI_CAP *PciCap,\r
350 IN PCI_CAP *NextPciCap OPTIONAL\r
351 )\r
352{\r
353 UINT16 ConfigSpaceSize;\r
354\r
355 ConfigSpaceSize = (PciCap->Key.Domain == PciCapNormal ?\r
356 PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);\r
357 //\r
358 // The following is guaranteed by the interface contract on\r
359 // CalculatePciCapMaxSizeHint().\r
360 //\r
361 ASSERT (NextPciCap == NULL || PciCap->Offset < NextPciCap->Offset);\r
362 //\r
363 // The following is guaranteed by the interface contract on InsertPciCap().\r
364 //\r
365 ASSERT (PciCap->Offset < ConfigSpaceSize);\r
366 //\r
367 // Thus we can safely subtract PciCap->Offset from either of\r
368 // - ConfigSpaceSize\r
369 // - and NextPciCap->Offset (if NextPciCap is not NULL).\r
370 //\r
371 // PciCap extends from PciCap->Offset to NextPciCap->Offset (if any), except\r
372 // it cannot cross config space boundary.\r
373 //\r
374 if (NextPciCap == NULL || NextPciCap->Offset >= ConfigSpaceSize) {\r
375 PciCap->MaxSizeHint = ConfigSpaceSize - PciCap->Offset;\r
376 return;\r
377 }\r
378 PciCap->MaxSizeHint = NextPciCap->Offset - PciCap->Offset;\r
379}\r
380\r
381\r
382/**\r
383 Debug dump a PCI_CAP_LIST object at the DEBUG_VERBOSE level.\r
384\r
385 @param[in] CapList The PCI_CAP_LIST object to dump.\r
386**/\r
387STATIC\r
388VOID\r
389EFIAPI\r
390DebugDumpPciCapList (\r
391 IN PCI_CAP_LIST *CapList\r
392 )\r
393{\r
394 DEBUG_CODE_BEGIN ();\r
395 ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
396\r
397 for (PciCapEntry = OrderedCollectionMin (CapList->Capabilities);\r
398 PciCapEntry != NULL;\r
399 PciCapEntry = OrderedCollectionNext (PciCapEntry)) {\r
400 PCI_CAP *PciCap;\r
401 RETURN_STATUS Status;\r
402 PCI_CAP_INFO Info;\r
403\r
404 PciCap = OrderedCollectionUserStruct (PciCapEntry);\r
405 Status = PciCapGetInfo (PciCap, &Info);\r
406 //\r
407 // PciCapGetInfo() cannot fail in this library instance.\r
408 //\r
409 ASSERT_RETURN_ERROR (Status);\r
410\r
411 DEBUG ((DEBUG_VERBOSE,\r
412 "%a:%a: %a 0x%04x %03u/%03u v0x%x @0x%03x+0x%03x\n", gEfiCallerBaseName,\r
413 __FUNCTION__, (Info.Domain == PciCapNormal ? "Norm" : "Extd"),\r
414 Info.CapId, Info.Instance, Info.NumInstances, Info.Version, Info.Offset,\r
415 Info.MaxSizeHint));\r
416 }\r
417 DEBUG_CODE_END ();\r
418}\r
419\r
420\r
421/**\r
422 Empty a collection of PCI_CAP structures, optionally releasing the referenced\r
423 PCI_CAP structures themselves. Release the collection at last.\r
424\r
425 @param[in,out] PciCapCollection The collection to empty and release.\r
426\r
427 @param[in] FreePciCap TRUE if the PCI_CAP structures linked by\r
428 PciCapCollection should be released. When\r
429 FALSE, the caller is responsible for\r
430 retaining at least one reference to each\r
431 PCI_CAP structure originally linked by\r
432 PciCapCollection.\r
433**/\r
434STATIC\r
435VOID\r
436EmptyAndUninitPciCapCollection (\r
437 IN OUT ORDERED_COLLECTION *PciCapCollection,\r
438 IN BOOLEAN FreePciCap\r
439 )\r
440{\r
441 ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
442 ORDERED_COLLECTION_ENTRY *NextEntry;\r
443\r
444 for (PciCapEntry = OrderedCollectionMin (PciCapCollection);\r
445 PciCapEntry != NULL;\r
446 PciCapEntry = NextEntry) {\r
447 PCI_CAP *PciCap;\r
448\r
449 NextEntry = OrderedCollectionNext (PciCapEntry);\r
450 OrderedCollectionDelete (PciCapCollection, PciCapEntry, (VOID **)&PciCap);\r
451 if (FreePciCap) {\r
452 FreePool (PciCap);\r
453 }\r
454 }\r
455 OrderedCollectionUninit (PciCapCollection);\r
456}\r
457\r
458\r
459/**\r
460 Parse the capabilities lists (both normal and extended, as applicable) of a\r
461 PCI device.\r
462\r
463 If the PCI device has no capabilities, that per se will not fail\r
464 PciCapListInit(); an empty capabilities list will be represented.\r
465\r
466 If the PCI device is found to be PCI Express, then an attempt will be made to\r
467 parse the extended capabilities list as well. If the first extended config\r
468 space access -- via PciDevice->ReadConfig() with SourceOffset=0x100 and\r
469 Size=4 -- fails, that per se will not fail PciCapListInit(); the device will\r
470 be assumed to have no extended capabilities.\r
471\r
472 @param[in] PciDevice Implementation-specific unique representation of the\r
473 PCI device in the PCI hierarchy.\r
474\r
475 @param[out] CapList Opaque data structure that holds an in-memory\r
476 representation of the parsed capabilities lists of\r
477 PciDevice.\r
478\r
479 @retval RETURN_SUCCESS The capabilities lists have been parsed from\r
480 config space.\r
481\r
482 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
483\r
484 @retval RETURN_DEVICE_ERROR A loop or some other kind of invalid pointer\r
485 was detected in the capabilities lists of\r
486 PciDevice.\r
487\r
488 @return Error codes propagated from\r
489 PciDevice->ReadConfig().\r
490**/\r
491RETURN_STATUS\r
492EFIAPI\r
493PciCapListInit (\r
494 IN PCI_CAP_DEV *PciDevice,\r
495 OUT PCI_CAP_LIST **CapList\r
496 )\r
497{\r
498 PCI_CAP_LIST *OutCapList;\r
499 RETURN_STATUS Status;\r
500 ORDERED_COLLECTION *CapHdrOffsets;\r
501 UINT16 PciStatusReg;\r
502 BOOLEAN DeviceIsExpress;\r
503 ORDERED_COLLECTION_ENTRY *OffsetEntry;\r
504\r
505 //\r
506 // Allocate the output structure.\r
507 //\r
508 OutCapList = AllocatePool (sizeof *OutCapList);\r
509 if (OutCapList == NULL) {\r
510 return RETURN_OUT_OF_RESOURCES;\r
511 }\r
512 //\r
513 // The OutCapList->Capabilities collection owns the PCI_CAP structures and\r
514 // orders them based on PCI_CAP.Key.\r
515 //\r
516 OutCapList->Capabilities = OrderedCollectionInit (ComparePciCap,\r
517 ComparePciCapKey);\r
518 if (OutCapList->Capabilities == NULL) {\r
519 Status = RETURN_OUT_OF_RESOURCES;\r
520 goto FreeOutCapList;\r
521 }\r
522\r
523 //\r
524 // The (temporary) CapHdrOffsets collection only references PCI_CAP\r
525 // structures, and orders them based on PCI_CAP.Offset.\r
526 //\r
527 CapHdrOffsets = OrderedCollectionInit (ComparePciCapOffset,\r
528 ComparePciCapOffsetKey);\r
529 if (CapHdrOffsets == NULL) {\r
530 Status = RETURN_OUT_OF_RESOURCES;\r
531 goto FreeCapabilities;\r
532 }\r
533\r
534 //\r
535 // Whether the device is PCI Express depends on the normal capability with\r
536 // identifier EFI_PCI_CAPABILITY_ID_PCIEXP.\r
537 //\r
538 DeviceIsExpress = FALSE;\r
539\r
540 //\r
541 // Check whether a normal capabilities list is present. If there's none,\r
542 // that's not an error; we'll just return OutCapList->Capabilities empty.\r
543 //\r
544 Status = PciDevice->ReadConfig (PciDevice, PCI_PRIMARY_STATUS_OFFSET,\r
545 &PciStatusReg, sizeof PciStatusReg);\r
546 if (RETURN_ERROR (Status)) {\r
547 goto FreeCapHdrOffsets;\r
548 }\r
549 if ((PciStatusReg & EFI_PCI_STATUS_CAPABILITY) != 0) {\r
550 UINT8 NormalCapHdrOffset;\r
551\r
552 //\r
553 // Fetch the start offset of the normal capabilities list.\r
554 //\r
555 Status = PciDevice->ReadConfig (PciDevice, PCI_CAPBILITY_POINTER_OFFSET,\r
556 &NormalCapHdrOffset, sizeof NormalCapHdrOffset);\r
557 if (RETURN_ERROR (Status)) {\r
558 goto FreeCapHdrOffsets;\r
559 }\r
560\r
561 //\r
562 // Traverse the normal capabilities list.\r
563 //\r
564 NormalCapHdrOffset &= 0xFC;\r
565 while (NormalCapHdrOffset > 0) {\r
566 EFI_PCI_CAPABILITY_HDR NormalCapHdr;\r
567\r
568 Status = PciDevice->ReadConfig (PciDevice, NormalCapHdrOffset,\r
569 &NormalCapHdr, sizeof NormalCapHdr);\r
570 if (RETURN_ERROR (Status)) {\r
571 goto FreeCapHdrOffsets;\r
572 }\r
573\r
574 Status = InsertPciCap (OutCapList, CapHdrOffsets, PciCapNormal,\r
575 NormalCapHdr.CapabilityID, NormalCapHdrOffset, 0);\r
576 if (RETURN_ERROR (Status)) {\r
577 goto FreeCapHdrOffsets;\r
578 }\r
579\r
580 if (NormalCapHdr.CapabilityID == EFI_PCI_CAPABILITY_ID_PCIEXP) {\r
581 DeviceIsExpress = TRUE;\r
582 }\r
583 NormalCapHdrOffset = NormalCapHdr.NextItemPtr & 0xFC;\r
584 }\r
585 }\r
586\r
587 //\r
588 // If the device has been found PCI Express, attempt to traverse the extended\r
589 // capabilities list. It starts right after the normal config space.\r
590 //\r
591 if (DeviceIsExpress) {\r
592 UINT16 ExtendedCapHdrOffset;\r
593\r
594 ExtendedCapHdrOffset = PCI_MAX_CONFIG_OFFSET;\r
595 while (ExtendedCapHdrOffset > 0) {\r
596 PCI_EXPRESS_EXTENDED_CAPABILITIES_HEADER ExtendedCapHdr;\r
597\r
598 Status = PciDevice->ReadConfig (PciDevice, ExtendedCapHdrOffset,\r
599 &ExtendedCapHdr, sizeof ExtendedCapHdr);\r
600 //\r
601 // If the first extended config space access fails, assume the device has\r
602 // no extended capabilities. If the first extended config space access\r
603 // succeeds but we read an "all bits zero" extended capability header,\r
604 // that means (by spec) the device has no extended capabilities.\r
605 //\r
606 if (ExtendedCapHdrOffset == PCI_MAX_CONFIG_OFFSET &&\r
607 (RETURN_ERROR (Status) ||\r
608 IsZeroBuffer (&ExtendedCapHdr, sizeof ExtendedCapHdr))) {\r
609 break;\r
610 }\r
611 if (RETURN_ERROR (Status)) {\r
612 goto FreeCapHdrOffsets;\r
613 }\r
614\r
615 Status = InsertPciCap (OutCapList, CapHdrOffsets, PciCapExtended,\r
2d0c6692
LG
616 (UINT16)ExtendedCapHdr.CapabilityId, ExtendedCapHdrOffset,\r
617 (UINT8)ExtendedCapHdr.CapabilityVersion);\r
392a3146
LE
618 if (RETURN_ERROR (Status)) {\r
619 goto FreeCapHdrOffsets;\r
620 }\r
621\r
622 ExtendedCapHdrOffset = ExtendedCapHdr.NextCapabilityOffset & 0xFFC;\r
623 if (ExtendedCapHdrOffset > 0 &&\r
624 ExtendedCapHdrOffset < PCI_MAX_CONFIG_OFFSET) {\r
625 //\r
626 // Invalid capability pointer.\r
627 //\r
628 Status = RETURN_DEVICE_ERROR;\r
629 goto FreeCapHdrOffsets;\r
630 }\r
631 }\r
632 }\r
633\r
634 //\r
635 // Both capabilities lists have been parsed; compute the PCI_CAP.MaxSizeHint\r
636 // members if at least one capability has been found. In parallel, evacuate\r
637 // the CapHdrOffsets collection.\r
638 //\r
639 // At first, set OffsetEntry to the iterator of the PCI_CAP object with the\r
640 // lowest Offset (if such exists).\r
641 //\r
642 OffsetEntry = OrderedCollectionMin (CapHdrOffsets);\r
643 if (OffsetEntry != NULL) {\r
644 ORDERED_COLLECTION_ENTRY *NextOffsetEntry;\r
645 PCI_CAP *PciCap;\r
646\r
647 //\r
648 // Initialize NextOffsetEntry to the iterator of the PCI_CAP object with\r
649 // the second lowest Offset (if such exists).\r
650 //\r
651 NextOffsetEntry = OrderedCollectionNext (OffsetEntry);\r
652 //\r
653 // Calculate MaxSizeHint for all PCI_CAP objects except the one with the\r
654 // highest Offset.\r
655 //\r
656 while (NextOffsetEntry != NULL) {\r
657 PCI_CAP *NextPciCap;\r
658\r
659 OrderedCollectionDelete (CapHdrOffsets, OffsetEntry, (VOID **)&PciCap);\r
660 NextPciCap = OrderedCollectionUserStruct (NextOffsetEntry);\r
661 CalculatePciCapMaxSizeHint (PciCap, NextPciCap);\r
662\r
663 OffsetEntry = NextOffsetEntry;\r
664 NextOffsetEntry = OrderedCollectionNext (OffsetEntry);\r
665 }\r
666 //\r
667 // Calculate MaxSizeHint for the PCI_CAP object with the highest Offset.\r
668 //\r
669 OrderedCollectionDelete (CapHdrOffsets, OffsetEntry, (VOID **)&PciCap);\r
670 CalculatePciCapMaxSizeHint (PciCap, NULL);\r
671 }\r
672 ASSERT (OrderedCollectionIsEmpty (CapHdrOffsets));\r
673 OrderedCollectionUninit (CapHdrOffsets);\r
674\r
675 DebugDumpPciCapList (OutCapList);\r
676 *CapList = OutCapList;\r
677 return RETURN_SUCCESS;\r
678\r
679FreeCapHdrOffsets:\r
680 EmptyAndUninitPciCapCollection (CapHdrOffsets, FALSE);\r
681\r
682FreeCapabilities:\r
683 EmptyAndUninitPciCapCollection (OutCapList->Capabilities, TRUE);\r
684\r
685FreeOutCapList:\r
686 FreePool (OutCapList);\r
687\r
688 ASSERT (RETURN_ERROR (Status));\r
689 DEBUG ((DEBUG_ERROR, "%a:%a: %r\n", gEfiCallerBaseName, __FUNCTION__,\r
690 Status));\r
691 return Status;\r
692}\r
693\r
694\r
695/**\r
696 Free the resources used by CapList.\r
697\r
698 @param[in] CapList The PCI_CAP_LIST object to free, originally produced by\r
699 PciCapListInit().\r
700**/\r
701VOID\r
702EFIAPI\r
703PciCapListUninit (\r
704 IN PCI_CAP_LIST *CapList\r
705 )\r
706{\r
707 EmptyAndUninitPciCapCollection (CapList->Capabilities, TRUE);\r
708 FreePool (CapList);\r
709}\r
710\r
711\r
712/**\r
713 Locate a capability instance in the parsed capabilities lists.\r
714\r
715 @param[in] CapList The PCI_CAP_LIST object produced by PciCapListInit().\r
716\r
717 @param[in] Domain Distinguishes whether CapId is 8-bit wide and\r
718 interpreted in normal config space, or 16-bit wide and\r
719 interpreted in extended config space. Capability ID\r
720 definitions are relative to domain.\r
721\r
722 @param[in] CapId Capability identifier to look up.\r
723\r
724 @param[in] Instance Domain and CapId may identify a multi-instance\r
725 capability. When Instance is zero, the first instance of\r
726 the capability is located (in list traversal order --\r
727 which may not mean increasing config space offset\r
728 order). Higher Instance values locate subsequent\r
729 instances of the same capability (in list traversal\r
730 order).\r
731\r
732 @param[out] Cap The capability instance that matches the search\r
733 criteria. Cap is owned by CapList and becomes invalid\r
734 when CapList is freed with PciCapListUninit().\r
735 PciCapListFindCap() may be called with Cap set to NULL,\r
736 in order to test the existence of a specific capability\r
737 instance.\r
738\r
739 @retval RETURN_SUCCESS The capability instance identified by (Domain,\r
740 CapId, Instance) has been found.\r
741\r
742 @retval RETURN_NOT_FOUND The requested (Domain, CapId, Instance) capability\r
743 instance does not exist.\r
744**/\r
745RETURN_STATUS\r
746EFIAPI\r
747PciCapListFindCap (\r
748 IN PCI_CAP_LIST *CapList,\r
749 IN PCI_CAP_DOMAIN Domain,\r
750 IN UINT16 CapId,\r
751 IN UINT16 Instance,\r
752 OUT PCI_CAP **Cap OPTIONAL\r
753 )\r
754{\r
755 PCI_CAP_KEY Key;\r
756 ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
757\r
758 Key.Domain = Domain;\r
759 Key.CapId = CapId;\r
760 Key.Instance = Instance;\r
761\r
762 PciCapEntry = OrderedCollectionFind (CapList->Capabilities, &Key);\r
763 if (PciCapEntry == NULL) {\r
764 return RETURN_NOT_FOUND;\r
765 }\r
766 if (Cap != NULL) {\r
767 *Cap = OrderedCollectionUserStruct (PciCapEntry);\r
768 }\r
769 return RETURN_SUCCESS;\r
770}\r
771\r
772\r
773/**\r
774 Locate the first instance of the capability given by (Domain, CapId) such\r
775 that the instance's Version is greater than or equal to MinVersion.\r
776\r
777 This is a convenience function that may save client code calls to\r
778 PciCapListFindCap() and PciCapGetInfo().\r
779\r
780 @param[in] CapList The PCI_CAP_LIST object produced by PciCapListInit().\r
781\r
782 @param[in] Domain Distinguishes whether CapId is 8-bit wide and\r
783 interpreted in normal config space, or 16-bit wide and\r
784 interpreted in extended config space. Capability ID\r
785 definitions are relative to domain.\r
786\r
787 @param[in] CapId Capability identifier to look up.\r
788\r
789 @param[in] MinVersion The minimum version that the capability instance is\r
790 required to have. Note that all capability instances\r
791 in Domain=PciCapNormal have Version=0.\r
792\r
793 @param[out] Cap The first capability instance that matches the search\r
794 criteria. Cap is owned by CapList and becomes invalid\r
795 when CapList is freed with PciCapListUninit().\r
796 PciCapListFindCapVersion() may be called with Cap set\r
797 to NULL, in order just to test whether the search\r
798 criteria are satisfiable.\r
799\r
800 @retval RETURN_SUCCESS The first capability instance matching (Domain,\r
801 CapId, MinVersion) has been located.\r
802\r
803 @retval RETURN_NOT_FOUND No capability instance matches (Domain, CapId,\r
804 MinVersion).\r
805**/\r
806RETURN_STATUS\r
807EFIAPI\r
808PciCapListFindCapVersion (\r
809 IN PCI_CAP_LIST *CapList,\r
810 IN PCI_CAP_DOMAIN Domain,\r
811 IN UINT16 CapId,\r
812 IN UINT8 MinVersion,\r
813 OUT PCI_CAP **Cap OPTIONAL\r
814 )\r
815{\r
816 PCI_CAP_KEY Key;\r
817 ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
818\r
819 //\r
820 // Start the version checks at Instance#0 of (Domain, CapId).\r
821 //\r
822 Key.Domain = Domain;\r
823 Key.CapId = CapId;\r
824 Key.Instance = 0;\r
825\r
826 for (PciCapEntry = OrderedCollectionFind (CapList->Capabilities, &Key);\r
827 PciCapEntry != NULL;\r
828 PciCapEntry = OrderedCollectionNext (PciCapEntry)) {\r
829 PCI_CAP *PciCap;\r
830\r
831 PciCap = OrderedCollectionUserStruct (PciCapEntry);\r
832 //\r
833 // PCI_CAP.Key ordering keeps instances of the same (Domain, CapId)\r
834 // adjacent to each other, so stop searching if either Domain or CapId\r
835 // changes.\r
836 //\r
837 if (PciCap->Key.Domain != Domain || PciCap->Key.CapId != CapId) {\r
838 break;\r
839 }\r
840 if (PciCap->Version >= MinVersion) {\r
841 //\r
842 // Match found.\r
843 //\r
844 if (Cap != NULL) {\r
845 *Cap = PciCap;\r
846 }\r
847 return RETURN_SUCCESS;\r
848 }\r
849 }\r
850 return RETURN_NOT_FOUND;\r
851}\r
852\r
853\r
854/**\r
855 Get information about a PCI Capability instance.\r
856\r
857 @param[in] Cap The capability instance to get info about, located with\r
858 PciCapListFindCap*().\r
859\r
860 @param[out] Info A PCI_CAP_INFO structure that describes the properties of\r
861 Cap.\r
862\r
863 @retval RETURN_SUCCESS Fields of Info have been set.\r
864\r
865 @return Unspecified error codes, if filling in Info failed\r
866 for some reason.\r
867**/\r
868RETURN_STATUS\r
869EFIAPI\r
870PciCapGetInfo (\r
871 IN PCI_CAP *Cap,\r
872 OUT PCI_CAP_INFO *Info\r
873 )\r
874{\r
875 PCI_CAP *InstanceZero;\r
876\r
877 ASSERT (Info != NULL);\r
878\r
879 InstanceZero = (Cap->Key.Instance == 0 ? Cap :\r
880 Cap->NumInstancesUnion.InstanceZero);\r
881\r
882 Info->Domain = Cap->Key.Domain;\r
883 Info->CapId = Cap->Key.CapId;\r
884 Info->NumInstances = InstanceZero->NumInstancesUnion.NumInstances;\r
885 Info->Instance = Cap->Key.Instance;\r
886 Info->Offset = Cap->Offset;\r
887 Info->MaxSizeHint = Cap->MaxSizeHint;\r
888 Info->Version = Cap->Version;\r
889\r
890 return RETURN_SUCCESS;\r
891}\r
892\r
893\r
894/**\r
895 Read a slice of a capability instance.\r
896\r
897 The function performs as few config space accesses as possible (without\r
898 attempting 64-bit wide accesses). PciCapRead() performs bounds checking on\r
899 SourceOffsetInCap and Size, and only invokes PciDevice->ReadConfig() if the\r
900 requested transfer falls within Cap.\r
901\r
902 @param[in] PciDevice Implementation-specific unique representation\r
903 of the PCI device in the PCI hierarchy.\r
904\r
905 @param[in] Cap The capability instance to read, located with\r
906 PciCapListFindCap*().\r
907\r
908 @param[in] SourceOffsetInCap Source offset relative to the capability\r
909 header to start reading from. A zero value\r
910 refers to the first byte of the capability\r
911 header.\r
912\r
913 @param[out] DestinationBuffer Buffer to store the read data to.\r
914\r
915 @param[in] Size The number of bytes to transfer.\r
916\r
917 @retval RETURN_SUCCESS Size bytes have been transferred from Cap to\r
918 DestinationBuffer.\r
919\r
920 @retval RETURN_BAD_BUFFER_SIZE Reading Size bytes starting from\r
921 SourceOffsetInCap would not (entirely) be\r
922 contained within Cap, as suggested by\r
923 PCI_CAP_INFO.MaxSizeHint. No bytes have been\r
924 read.\r
925\r
926 @return Error codes propagated from\r
927 PciDevice->ReadConfig(). Fewer than Size\r
928 bytes may have been read.\r
929**/\r
930RETURN_STATUS\r
931EFIAPI\r
932PciCapRead (\r
933 IN PCI_CAP_DEV *PciDevice,\r
934 IN PCI_CAP *Cap,\r
935 IN UINT16 SourceOffsetInCap,\r
936 OUT VOID *DestinationBuffer,\r
937 IN UINT16 Size\r
938 )\r
939{\r
940 //\r
941 // Note: all UINT16 values are promoted to INT32 below, and addition and\r
942 // comparison take place between INT32 values.\r
943 //\r
944 if (SourceOffsetInCap + Size > Cap->MaxSizeHint) {\r
945 return RETURN_BAD_BUFFER_SIZE;\r
946 }\r
947 return PciDevice->ReadConfig (PciDevice, Cap->Offset + SourceOffsetInCap,\r
948 DestinationBuffer, Size);\r
949}\r
950\r
951\r
952/**\r
953 Write a slice of a capability instance.\r
954\r
955 The function performs as few config space accesses as possible (without\r
956 attempting 64-bit wide accesses). PciCapWrite() performs bounds checking on\r
957 DestinationOffsetInCap and Size, and only invokes PciDevice->WriteConfig() if\r
958 the requested transfer falls within Cap.\r
959\r
960 @param[in] PciDevice Implementation-specific unique\r
961 representation of the PCI device in the\r
962 PCI hierarchy.\r
963\r
964 @param[in] Cap The capability instance to write, located\r
965 with PciCapListFindCap*().\r
966\r
967 @param[in] DestinationOffsetInCap Destination offset relative to the\r
968 capability header to start writing at. A\r
969 zero value refers to the first byte of the\r
970 capability header.\r
971\r
972 @param[in] SourceBuffer Buffer to read the data to be stored from.\r
973\r
974 @param[in] Size The number of bytes to transfer.\r
975\r
976 @retval RETURN_SUCCESS Size bytes have been transferred from\r
977 SourceBuffer to Cap.\r
978\r
979 @retval RETURN_BAD_BUFFER_SIZE Writing Size bytes starting at\r
980 DestinationOffsetInCap would not (entirely)\r
981 be contained within Cap, as suggested by\r
982 PCI_CAP_INFO.MaxSizeHint. No bytes have been\r
983 written.\r
984\r
985 @return Error codes propagated from\r
986 PciDevice->WriteConfig(). Fewer than Size\r
987 bytes may have been written.\r
988**/\r
989RETURN_STATUS\r
990EFIAPI\r
991PciCapWrite (\r
992 IN PCI_CAP_DEV *PciDevice,\r
993 IN PCI_CAP *Cap,\r
994 IN UINT16 DestinationOffsetInCap,\r
995 IN VOID *SourceBuffer,\r
996 IN UINT16 Size\r
997 )\r
998{\r
999 //\r
1000 // Note: all UINT16 values are promoted to INT32 below, and addition and\r
1001 // comparison take place between INT32 values.\r
1002 //\r
1003 if (DestinationOffsetInCap + Size > Cap->MaxSizeHint) {\r
1004 return RETURN_BAD_BUFFER_SIZE;\r
1005 }\r
1006 return PciDevice->WriteConfig (PciDevice,\r
1007 Cap->Offset + DestinationOffsetInCap, SourceBuffer,\r
1008 Size);\r
1009}\r