]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/BasePciCapLib/BasePciCapLib.c
OvmfPkg/BasePciCapLib: suppress invalid "nullptr deref" warning
[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
c2f64347
LE
306 //\r
307 // Suppress invalid "nullptr dereference" compiler/analyzer warnings: the\r
308 // only way for "PciCap->Key.Instance" to be positive here is for it to\r
309 // have been assigned *from* dereferencing "InstanceZero" above.\r
310 //\r
311 ASSERT (InstanceZero != NULL);\r
312\r
392a3146
LE
313 InstanceZero->NumInstancesUnion.NumInstances++;\r
314 }\r
315 return RETURN_SUCCESS;\r
316\r
317DeletePciCapFromCapList:\r
318 OrderedCollectionDelete (CapList->Capabilities, PciCapEntry, NULL);\r
319\r
320FreePciCap:\r
321 FreePool (PciCap);\r
322\r
323 return Status;\r
324}\r
325\r
326\r
327/**\r
328 Calculate the MaxSizeHint member for a PCI_CAP object.\r
329\r
330 CalculatePciCapMaxSizeHint() may only be called once all capability instances\r
331 have been successfully processed by InsertPciCap().\r
332\r
333 @param[in,out] PciCap The PCI_CAP object for which to calculate the\r
334 MaxSizeHint member. The caller is responsible for\r
335 passing a PCI_CAP object that has been created by a\r
336 successful invocation of InsertPciCap().\r
337\r
338 @param[in] NextPciCap If NextPciCap is NULL, then the caller is responsible\r
339 for PciCap to represent the capability instance with\r
340 the highest header offset in all config space. If\r
341 NextPciCap is not NULL, then the caller is responsible\r
342 for (a) having created NextPciCap with a successful\r
343 invocation of InsertPciCap(), and (b) NextPciCap being\r
344 the direct successor of PciCap in config space offset\r
345 order, as ordered by ComparePciCapOffset().\r
346**/\r
347STATIC\r
348VOID\r
349CalculatePciCapMaxSizeHint (\r
350 IN OUT PCI_CAP *PciCap,\r
351 IN PCI_CAP *NextPciCap OPTIONAL\r
352 )\r
353{\r
354 UINT16 ConfigSpaceSize;\r
355\r
356 ConfigSpaceSize = (PciCap->Key.Domain == PciCapNormal ?\r
357 PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);\r
358 //\r
359 // The following is guaranteed by the interface contract on\r
360 // CalculatePciCapMaxSizeHint().\r
361 //\r
362 ASSERT (NextPciCap == NULL || PciCap->Offset < NextPciCap->Offset);\r
363 //\r
364 // The following is guaranteed by the interface contract on InsertPciCap().\r
365 //\r
366 ASSERT (PciCap->Offset < ConfigSpaceSize);\r
367 //\r
368 // Thus we can safely subtract PciCap->Offset from either of\r
369 // - ConfigSpaceSize\r
370 // - and NextPciCap->Offset (if NextPciCap is not NULL).\r
371 //\r
372 // PciCap extends from PciCap->Offset to NextPciCap->Offset (if any), except\r
373 // it cannot cross config space boundary.\r
374 //\r
375 if (NextPciCap == NULL || NextPciCap->Offset >= ConfigSpaceSize) {\r
376 PciCap->MaxSizeHint = ConfigSpaceSize - PciCap->Offset;\r
377 return;\r
378 }\r
379 PciCap->MaxSizeHint = NextPciCap->Offset - PciCap->Offset;\r
380}\r
381\r
382\r
383/**\r
384 Debug dump a PCI_CAP_LIST object at the DEBUG_VERBOSE level.\r
385\r
386 @param[in] CapList The PCI_CAP_LIST object to dump.\r
387**/\r
388STATIC\r
389VOID\r
390EFIAPI\r
391DebugDumpPciCapList (\r
392 IN PCI_CAP_LIST *CapList\r
393 )\r
394{\r
395 DEBUG_CODE_BEGIN ();\r
396 ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
397\r
398 for (PciCapEntry = OrderedCollectionMin (CapList->Capabilities);\r
399 PciCapEntry != NULL;\r
400 PciCapEntry = OrderedCollectionNext (PciCapEntry)) {\r
401 PCI_CAP *PciCap;\r
402 RETURN_STATUS Status;\r
403 PCI_CAP_INFO Info;\r
404\r
405 PciCap = OrderedCollectionUserStruct (PciCapEntry);\r
406 Status = PciCapGetInfo (PciCap, &Info);\r
407 //\r
408 // PciCapGetInfo() cannot fail in this library instance.\r
409 //\r
410 ASSERT_RETURN_ERROR (Status);\r
411\r
412 DEBUG ((DEBUG_VERBOSE,\r
413 "%a:%a: %a 0x%04x %03u/%03u v0x%x @0x%03x+0x%03x\n", gEfiCallerBaseName,\r
414 __FUNCTION__, (Info.Domain == PciCapNormal ? "Norm" : "Extd"),\r
415 Info.CapId, Info.Instance, Info.NumInstances, Info.Version, Info.Offset,\r
416 Info.MaxSizeHint));\r
417 }\r
418 DEBUG_CODE_END ();\r
419}\r
420\r
421\r
422/**\r
423 Empty a collection of PCI_CAP structures, optionally releasing the referenced\r
424 PCI_CAP structures themselves. Release the collection at last.\r
425\r
426 @param[in,out] PciCapCollection The collection to empty and release.\r
427\r
428 @param[in] FreePciCap TRUE if the PCI_CAP structures linked by\r
429 PciCapCollection should be released. When\r
430 FALSE, the caller is responsible for\r
431 retaining at least one reference to each\r
432 PCI_CAP structure originally linked by\r
433 PciCapCollection.\r
434**/\r
435STATIC\r
436VOID\r
437EmptyAndUninitPciCapCollection (\r
438 IN OUT ORDERED_COLLECTION *PciCapCollection,\r
439 IN BOOLEAN FreePciCap\r
440 )\r
441{\r
442 ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
443 ORDERED_COLLECTION_ENTRY *NextEntry;\r
444\r
445 for (PciCapEntry = OrderedCollectionMin (PciCapCollection);\r
446 PciCapEntry != NULL;\r
447 PciCapEntry = NextEntry) {\r
448 PCI_CAP *PciCap;\r
449\r
450 NextEntry = OrderedCollectionNext (PciCapEntry);\r
451 OrderedCollectionDelete (PciCapCollection, PciCapEntry, (VOID **)&PciCap);\r
452 if (FreePciCap) {\r
453 FreePool (PciCap);\r
454 }\r
455 }\r
456 OrderedCollectionUninit (PciCapCollection);\r
457}\r
458\r
459\r
460/**\r
461 Parse the capabilities lists (both normal and extended, as applicable) of a\r
462 PCI device.\r
463\r
464 If the PCI device has no capabilities, that per se will not fail\r
465 PciCapListInit(); an empty capabilities list will be represented.\r
466\r
467 If the PCI device is found to be PCI Express, then an attempt will be made to\r
468 parse the extended capabilities list as well. If the first extended config\r
469 space access -- via PciDevice->ReadConfig() with SourceOffset=0x100 and\r
470 Size=4 -- fails, that per se will not fail PciCapListInit(); the device will\r
471 be assumed to have no extended capabilities.\r
472\r
473 @param[in] PciDevice Implementation-specific unique representation of the\r
474 PCI device in the PCI hierarchy.\r
475\r
476 @param[out] CapList Opaque data structure that holds an in-memory\r
477 representation of the parsed capabilities lists of\r
478 PciDevice.\r
479\r
480 @retval RETURN_SUCCESS The capabilities lists have been parsed from\r
481 config space.\r
482\r
483 @retval RETURN_OUT_OF_RESOURCES Memory allocation failed.\r
484\r
485 @retval RETURN_DEVICE_ERROR A loop or some other kind of invalid pointer\r
486 was detected in the capabilities lists of\r
487 PciDevice.\r
488\r
489 @return Error codes propagated from\r
490 PciDevice->ReadConfig().\r
491**/\r
492RETURN_STATUS\r
493EFIAPI\r
494PciCapListInit (\r
495 IN PCI_CAP_DEV *PciDevice,\r
496 OUT PCI_CAP_LIST **CapList\r
497 )\r
498{\r
499 PCI_CAP_LIST *OutCapList;\r
500 RETURN_STATUS Status;\r
501 ORDERED_COLLECTION *CapHdrOffsets;\r
502 UINT16 PciStatusReg;\r
503 BOOLEAN DeviceIsExpress;\r
504 ORDERED_COLLECTION_ENTRY *OffsetEntry;\r
505\r
506 //\r
507 // Allocate the output structure.\r
508 //\r
509 OutCapList = AllocatePool (sizeof *OutCapList);\r
510 if (OutCapList == NULL) {\r
511 return RETURN_OUT_OF_RESOURCES;\r
512 }\r
513 //\r
514 // The OutCapList->Capabilities collection owns the PCI_CAP structures and\r
515 // orders them based on PCI_CAP.Key.\r
516 //\r
517 OutCapList->Capabilities = OrderedCollectionInit (ComparePciCap,\r
518 ComparePciCapKey);\r
519 if (OutCapList->Capabilities == NULL) {\r
520 Status = RETURN_OUT_OF_RESOURCES;\r
521 goto FreeOutCapList;\r
522 }\r
523\r
524 //\r
525 // The (temporary) CapHdrOffsets collection only references PCI_CAP\r
526 // structures, and orders them based on PCI_CAP.Offset.\r
527 //\r
528 CapHdrOffsets = OrderedCollectionInit (ComparePciCapOffset,\r
529 ComparePciCapOffsetKey);\r
530 if (CapHdrOffsets == NULL) {\r
531 Status = RETURN_OUT_OF_RESOURCES;\r
532 goto FreeCapabilities;\r
533 }\r
534\r
535 //\r
536 // Whether the device is PCI Express depends on the normal capability with\r
537 // identifier EFI_PCI_CAPABILITY_ID_PCIEXP.\r
538 //\r
539 DeviceIsExpress = FALSE;\r
540\r
541 //\r
542 // Check whether a normal capabilities list is present. If there's none,\r
543 // that's not an error; we'll just return OutCapList->Capabilities empty.\r
544 //\r
545 Status = PciDevice->ReadConfig (PciDevice, PCI_PRIMARY_STATUS_OFFSET,\r
546 &PciStatusReg, sizeof PciStatusReg);\r
547 if (RETURN_ERROR (Status)) {\r
548 goto FreeCapHdrOffsets;\r
549 }\r
550 if ((PciStatusReg & EFI_PCI_STATUS_CAPABILITY) != 0) {\r
551 UINT8 NormalCapHdrOffset;\r
552\r
553 //\r
554 // Fetch the start offset of the normal capabilities list.\r
555 //\r
556 Status = PciDevice->ReadConfig (PciDevice, PCI_CAPBILITY_POINTER_OFFSET,\r
557 &NormalCapHdrOffset, sizeof NormalCapHdrOffset);\r
558 if (RETURN_ERROR (Status)) {\r
559 goto FreeCapHdrOffsets;\r
560 }\r
561\r
562 //\r
563 // Traverse the normal capabilities list.\r
564 //\r
565 NormalCapHdrOffset &= 0xFC;\r
566 while (NormalCapHdrOffset > 0) {\r
567 EFI_PCI_CAPABILITY_HDR NormalCapHdr;\r
568\r
569 Status = PciDevice->ReadConfig (PciDevice, NormalCapHdrOffset,\r
570 &NormalCapHdr, sizeof NormalCapHdr);\r
571 if (RETURN_ERROR (Status)) {\r
572 goto FreeCapHdrOffsets;\r
573 }\r
574\r
575 Status = InsertPciCap (OutCapList, CapHdrOffsets, PciCapNormal,\r
576 NormalCapHdr.CapabilityID, NormalCapHdrOffset, 0);\r
577 if (RETURN_ERROR (Status)) {\r
578 goto FreeCapHdrOffsets;\r
579 }\r
580\r
581 if (NormalCapHdr.CapabilityID == EFI_PCI_CAPABILITY_ID_PCIEXP) {\r
582 DeviceIsExpress = TRUE;\r
583 }\r
584 NormalCapHdrOffset = NormalCapHdr.NextItemPtr & 0xFC;\r
585 }\r
586 }\r
587\r
588 //\r
589 // If the device has been found PCI Express, attempt to traverse the extended\r
590 // capabilities list. It starts right after the normal config space.\r
591 //\r
592 if (DeviceIsExpress) {\r
593 UINT16 ExtendedCapHdrOffset;\r
594\r
595 ExtendedCapHdrOffset = PCI_MAX_CONFIG_OFFSET;\r
596 while (ExtendedCapHdrOffset > 0) {\r
597 PCI_EXPRESS_EXTENDED_CAPABILITIES_HEADER ExtendedCapHdr;\r
598\r
599 Status = PciDevice->ReadConfig (PciDevice, ExtendedCapHdrOffset,\r
600 &ExtendedCapHdr, sizeof ExtendedCapHdr);\r
601 //\r
602 // If the first extended config space access fails, assume the device has\r
603 // no extended capabilities. If the first extended config space access\r
604 // succeeds but we read an "all bits zero" extended capability header,\r
605 // that means (by spec) the device has no extended capabilities.\r
606 //\r
607 if (ExtendedCapHdrOffset == PCI_MAX_CONFIG_OFFSET &&\r
608 (RETURN_ERROR (Status) ||\r
609 IsZeroBuffer (&ExtendedCapHdr, sizeof ExtendedCapHdr))) {\r
610 break;\r
611 }\r
612 if (RETURN_ERROR (Status)) {\r
613 goto FreeCapHdrOffsets;\r
614 }\r
615\r
616 Status = InsertPciCap (OutCapList, CapHdrOffsets, PciCapExtended,\r
2d0c6692
LG
617 (UINT16)ExtendedCapHdr.CapabilityId, ExtendedCapHdrOffset,\r
618 (UINT8)ExtendedCapHdr.CapabilityVersion);\r
392a3146
LE
619 if (RETURN_ERROR (Status)) {\r
620 goto FreeCapHdrOffsets;\r
621 }\r
622\r
623 ExtendedCapHdrOffset = ExtendedCapHdr.NextCapabilityOffset & 0xFFC;\r
624 if (ExtendedCapHdrOffset > 0 &&\r
625 ExtendedCapHdrOffset < PCI_MAX_CONFIG_OFFSET) {\r
626 //\r
627 // Invalid capability pointer.\r
628 //\r
629 Status = RETURN_DEVICE_ERROR;\r
630 goto FreeCapHdrOffsets;\r
631 }\r
632 }\r
633 }\r
634\r
635 //\r
636 // Both capabilities lists have been parsed; compute the PCI_CAP.MaxSizeHint\r
637 // members if at least one capability has been found. In parallel, evacuate\r
638 // the CapHdrOffsets collection.\r
639 //\r
640 // At first, set OffsetEntry to the iterator of the PCI_CAP object with the\r
641 // lowest Offset (if such exists).\r
642 //\r
643 OffsetEntry = OrderedCollectionMin (CapHdrOffsets);\r
644 if (OffsetEntry != NULL) {\r
645 ORDERED_COLLECTION_ENTRY *NextOffsetEntry;\r
646 PCI_CAP *PciCap;\r
647\r
648 //\r
649 // Initialize NextOffsetEntry to the iterator of the PCI_CAP object with\r
650 // the second lowest Offset (if such exists).\r
651 //\r
652 NextOffsetEntry = OrderedCollectionNext (OffsetEntry);\r
653 //\r
654 // Calculate MaxSizeHint for all PCI_CAP objects except the one with the\r
655 // highest Offset.\r
656 //\r
657 while (NextOffsetEntry != NULL) {\r
658 PCI_CAP *NextPciCap;\r
659\r
660 OrderedCollectionDelete (CapHdrOffsets, OffsetEntry, (VOID **)&PciCap);\r
661 NextPciCap = OrderedCollectionUserStruct (NextOffsetEntry);\r
662 CalculatePciCapMaxSizeHint (PciCap, NextPciCap);\r
663\r
664 OffsetEntry = NextOffsetEntry;\r
665 NextOffsetEntry = OrderedCollectionNext (OffsetEntry);\r
666 }\r
667 //\r
668 // Calculate MaxSizeHint for the PCI_CAP object with the highest Offset.\r
669 //\r
670 OrderedCollectionDelete (CapHdrOffsets, OffsetEntry, (VOID **)&PciCap);\r
671 CalculatePciCapMaxSizeHint (PciCap, NULL);\r
672 }\r
673 ASSERT (OrderedCollectionIsEmpty (CapHdrOffsets));\r
674 OrderedCollectionUninit (CapHdrOffsets);\r
675\r
676 DebugDumpPciCapList (OutCapList);\r
677 *CapList = OutCapList;\r
678 return RETURN_SUCCESS;\r
679\r
680FreeCapHdrOffsets:\r
681 EmptyAndUninitPciCapCollection (CapHdrOffsets, FALSE);\r
682\r
683FreeCapabilities:\r
684 EmptyAndUninitPciCapCollection (OutCapList->Capabilities, TRUE);\r
685\r
686FreeOutCapList:\r
687 FreePool (OutCapList);\r
688\r
689 ASSERT (RETURN_ERROR (Status));\r
690 DEBUG ((DEBUG_ERROR, "%a:%a: %r\n", gEfiCallerBaseName, __FUNCTION__,\r
691 Status));\r
692 return Status;\r
693}\r
694\r
695\r
696/**\r
697 Free the resources used by CapList.\r
698\r
699 @param[in] CapList The PCI_CAP_LIST object to free, originally produced by\r
700 PciCapListInit().\r
701**/\r
702VOID\r
703EFIAPI\r
704PciCapListUninit (\r
705 IN PCI_CAP_LIST *CapList\r
706 )\r
707{\r
708 EmptyAndUninitPciCapCollection (CapList->Capabilities, TRUE);\r
709 FreePool (CapList);\r
710}\r
711\r
712\r
713/**\r
714 Locate a capability instance in the parsed capabilities lists.\r
715\r
716 @param[in] CapList The PCI_CAP_LIST object produced by PciCapListInit().\r
717\r
718 @param[in] Domain Distinguishes whether CapId is 8-bit wide and\r
719 interpreted in normal config space, or 16-bit wide and\r
720 interpreted in extended config space. Capability ID\r
721 definitions are relative to domain.\r
722\r
723 @param[in] CapId Capability identifier to look up.\r
724\r
725 @param[in] Instance Domain and CapId may identify a multi-instance\r
726 capability. When Instance is zero, the first instance of\r
727 the capability is located (in list traversal order --\r
728 which may not mean increasing config space offset\r
729 order). Higher Instance values locate subsequent\r
730 instances of the same capability (in list traversal\r
731 order).\r
732\r
733 @param[out] Cap The capability instance that matches the search\r
734 criteria. Cap is owned by CapList and becomes invalid\r
735 when CapList is freed with PciCapListUninit().\r
736 PciCapListFindCap() may be called with Cap set to NULL,\r
737 in order to test the existence of a specific capability\r
738 instance.\r
739\r
740 @retval RETURN_SUCCESS The capability instance identified by (Domain,\r
741 CapId, Instance) has been found.\r
742\r
743 @retval RETURN_NOT_FOUND The requested (Domain, CapId, Instance) capability\r
744 instance does not exist.\r
745**/\r
746RETURN_STATUS\r
747EFIAPI\r
748PciCapListFindCap (\r
749 IN PCI_CAP_LIST *CapList,\r
750 IN PCI_CAP_DOMAIN Domain,\r
751 IN UINT16 CapId,\r
752 IN UINT16 Instance,\r
753 OUT PCI_CAP **Cap OPTIONAL\r
754 )\r
755{\r
756 PCI_CAP_KEY Key;\r
757 ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
758\r
759 Key.Domain = Domain;\r
760 Key.CapId = CapId;\r
761 Key.Instance = Instance;\r
762\r
763 PciCapEntry = OrderedCollectionFind (CapList->Capabilities, &Key);\r
764 if (PciCapEntry == NULL) {\r
765 return RETURN_NOT_FOUND;\r
766 }\r
767 if (Cap != NULL) {\r
768 *Cap = OrderedCollectionUserStruct (PciCapEntry);\r
769 }\r
770 return RETURN_SUCCESS;\r
771}\r
772\r
773\r
774/**\r
775 Locate the first instance of the capability given by (Domain, CapId) such\r
776 that the instance's Version is greater than or equal to MinVersion.\r
777\r
778 This is a convenience function that may save client code calls to\r
779 PciCapListFindCap() and PciCapGetInfo().\r
780\r
781 @param[in] CapList The PCI_CAP_LIST object produced by PciCapListInit().\r
782\r
783 @param[in] Domain Distinguishes whether CapId is 8-bit wide and\r
784 interpreted in normal config space, or 16-bit wide and\r
785 interpreted in extended config space. Capability ID\r
786 definitions are relative to domain.\r
787\r
788 @param[in] CapId Capability identifier to look up.\r
789\r
790 @param[in] MinVersion The minimum version that the capability instance is\r
791 required to have. Note that all capability instances\r
792 in Domain=PciCapNormal have Version=0.\r
793\r
794 @param[out] Cap The first capability instance that matches the search\r
795 criteria. Cap is owned by CapList and becomes invalid\r
796 when CapList is freed with PciCapListUninit().\r
797 PciCapListFindCapVersion() may be called with Cap set\r
798 to NULL, in order just to test whether the search\r
799 criteria are satisfiable.\r
800\r
801 @retval RETURN_SUCCESS The first capability instance matching (Domain,\r
802 CapId, MinVersion) has been located.\r
803\r
804 @retval RETURN_NOT_FOUND No capability instance matches (Domain, CapId,\r
805 MinVersion).\r
806**/\r
807RETURN_STATUS\r
808EFIAPI\r
809PciCapListFindCapVersion (\r
810 IN PCI_CAP_LIST *CapList,\r
811 IN PCI_CAP_DOMAIN Domain,\r
812 IN UINT16 CapId,\r
813 IN UINT8 MinVersion,\r
814 OUT PCI_CAP **Cap OPTIONAL\r
815 )\r
816{\r
817 PCI_CAP_KEY Key;\r
818 ORDERED_COLLECTION_ENTRY *PciCapEntry;\r
819\r
820 //\r
821 // Start the version checks at Instance#0 of (Domain, CapId).\r
822 //\r
823 Key.Domain = Domain;\r
824 Key.CapId = CapId;\r
825 Key.Instance = 0;\r
826\r
827 for (PciCapEntry = OrderedCollectionFind (CapList->Capabilities, &Key);\r
828 PciCapEntry != NULL;\r
829 PciCapEntry = OrderedCollectionNext (PciCapEntry)) {\r
830 PCI_CAP *PciCap;\r
831\r
832 PciCap = OrderedCollectionUserStruct (PciCapEntry);\r
833 //\r
834 // PCI_CAP.Key ordering keeps instances of the same (Domain, CapId)\r
835 // adjacent to each other, so stop searching if either Domain or CapId\r
836 // changes.\r
837 //\r
838 if (PciCap->Key.Domain != Domain || PciCap->Key.CapId != CapId) {\r
839 break;\r
840 }\r
841 if (PciCap->Version >= MinVersion) {\r
842 //\r
843 // Match found.\r
844 //\r
845 if (Cap != NULL) {\r
846 *Cap = PciCap;\r
847 }\r
848 return RETURN_SUCCESS;\r
849 }\r
850 }\r
851 return RETURN_NOT_FOUND;\r
852}\r
853\r
854\r
855/**\r
856 Get information about a PCI Capability instance.\r
857\r
858 @param[in] Cap The capability instance to get info about, located with\r
859 PciCapListFindCap*().\r
860\r
861 @param[out] Info A PCI_CAP_INFO structure that describes the properties of\r
862 Cap.\r
863\r
864 @retval RETURN_SUCCESS Fields of Info have been set.\r
865\r
866 @return Unspecified error codes, if filling in Info failed\r
867 for some reason.\r
868**/\r
869RETURN_STATUS\r
870EFIAPI\r
871PciCapGetInfo (\r
872 IN PCI_CAP *Cap,\r
873 OUT PCI_CAP_INFO *Info\r
874 )\r
875{\r
876 PCI_CAP *InstanceZero;\r
877\r
878 ASSERT (Info != NULL);\r
879\r
880 InstanceZero = (Cap->Key.Instance == 0 ? Cap :\r
881 Cap->NumInstancesUnion.InstanceZero);\r
882\r
883 Info->Domain = Cap->Key.Domain;\r
884 Info->CapId = Cap->Key.CapId;\r
885 Info->NumInstances = InstanceZero->NumInstancesUnion.NumInstances;\r
886 Info->Instance = Cap->Key.Instance;\r
887 Info->Offset = Cap->Offset;\r
888 Info->MaxSizeHint = Cap->MaxSizeHint;\r
889 Info->Version = Cap->Version;\r
890\r
891 return RETURN_SUCCESS;\r
892}\r
893\r
894\r
895/**\r
896 Read a slice of a capability instance.\r
897\r
898 The function performs as few config space accesses as possible (without\r
899 attempting 64-bit wide accesses). PciCapRead() performs bounds checking on\r
900 SourceOffsetInCap and Size, and only invokes PciDevice->ReadConfig() if the\r
901 requested transfer falls within Cap.\r
902\r
903 @param[in] PciDevice Implementation-specific unique representation\r
904 of the PCI device in the PCI hierarchy.\r
905\r
906 @param[in] Cap The capability instance to read, located with\r
907 PciCapListFindCap*().\r
908\r
909 @param[in] SourceOffsetInCap Source offset relative to the capability\r
910 header to start reading from. A zero value\r
911 refers to the first byte of the capability\r
912 header.\r
913\r
914 @param[out] DestinationBuffer Buffer to store the read data to.\r
915\r
916 @param[in] Size The number of bytes to transfer.\r
917\r
918 @retval RETURN_SUCCESS Size bytes have been transferred from Cap to\r
919 DestinationBuffer.\r
920\r
921 @retval RETURN_BAD_BUFFER_SIZE Reading Size bytes starting from\r
922 SourceOffsetInCap would not (entirely) be\r
923 contained within Cap, as suggested by\r
924 PCI_CAP_INFO.MaxSizeHint. No bytes have been\r
925 read.\r
926\r
927 @return Error codes propagated from\r
928 PciDevice->ReadConfig(). Fewer than Size\r
929 bytes may have been read.\r
930**/\r
931RETURN_STATUS\r
932EFIAPI\r
933PciCapRead (\r
934 IN PCI_CAP_DEV *PciDevice,\r
935 IN PCI_CAP *Cap,\r
936 IN UINT16 SourceOffsetInCap,\r
937 OUT VOID *DestinationBuffer,\r
938 IN UINT16 Size\r
939 )\r
940{\r
941 //\r
942 // Note: all UINT16 values are promoted to INT32 below, and addition and\r
943 // comparison take place between INT32 values.\r
944 //\r
945 if (SourceOffsetInCap + Size > Cap->MaxSizeHint) {\r
946 return RETURN_BAD_BUFFER_SIZE;\r
947 }\r
948 return PciDevice->ReadConfig (PciDevice, Cap->Offset + SourceOffsetInCap,\r
949 DestinationBuffer, Size);\r
950}\r
951\r
952\r
953/**\r
954 Write a slice of a capability instance.\r
955\r
956 The function performs as few config space accesses as possible (without\r
957 attempting 64-bit wide accesses). PciCapWrite() performs bounds checking on\r
958 DestinationOffsetInCap and Size, and only invokes PciDevice->WriteConfig() if\r
959 the requested transfer falls within Cap.\r
960\r
961 @param[in] PciDevice Implementation-specific unique\r
962 representation of the PCI device in the\r
963 PCI hierarchy.\r
964\r
965 @param[in] Cap The capability instance to write, located\r
966 with PciCapListFindCap*().\r
967\r
968 @param[in] DestinationOffsetInCap Destination offset relative to the\r
969 capability header to start writing at. A\r
970 zero value refers to the first byte of the\r
971 capability header.\r
972\r
973 @param[in] SourceBuffer Buffer to read the data to be stored from.\r
974\r
975 @param[in] Size The number of bytes to transfer.\r
976\r
977 @retval RETURN_SUCCESS Size bytes have been transferred from\r
978 SourceBuffer to Cap.\r
979\r
980 @retval RETURN_BAD_BUFFER_SIZE Writing Size bytes starting at\r
981 DestinationOffsetInCap would not (entirely)\r
982 be contained within Cap, as suggested by\r
983 PCI_CAP_INFO.MaxSizeHint. No bytes have been\r
984 written.\r
985\r
986 @return Error codes propagated from\r
987 PciDevice->WriteConfig(). Fewer than Size\r
988 bytes may have been written.\r
989**/\r
990RETURN_STATUS\r
991EFIAPI\r
992PciCapWrite (\r
993 IN PCI_CAP_DEV *PciDevice,\r
994 IN PCI_CAP *Cap,\r
995 IN UINT16 DestinationOffsetInCap,\r
996 IN VOID *SourceBuffer,\r
997 IN UINT16 Size\r
998 )\r
999{\r
1000 //\r
1001 // Note: all UINT16 values are promoted to INT32 below, and addition and\r
1002 // comparison take place between INT32 values.\r
1003 //\r
1004 if (DestinationOffsetInCap + Size > Cap->MaxSizeHint) {\r
1005 return RETURN_BAD_BUFFER_SIZE;\r
1006 }\r
1007 return PciDevice->WriteConfig (PciDevice,\r
1008 Cap->Offset + DestinationOffsetInCap, SourceBuffer,\r
1009 Size);\r
1010}\r