Coverage Report

Created: 2026-06-17 06:13

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