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