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