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