Coverage Report

Created: 2026-03-21 06:29

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
107k
{
42
107k
  CONST PCI_CAP_KEY  *Key1;
43
107k
  CONST PCI_CAP_KEY  *Key2;
44
45
107k
  Key1 = PciCapKey;
46
107k
  Key2 = &((CONST PCI_CAP *)PciCap)->Key;
47
48
107k
  if (Key1->Domain < Key2->Domain) {
49
118
    return -1;
50
118
  }
51
52
107k
  if (Key1->Domain > Key2->Domain) {
53
12.0k
    return 1;
54
12.0k
  }
55
56
95.0k
  if (Key1->CapId < Key2->CapId) {
57
39.1k
    return -1;
58
39.1k
  }
59
60
55.8k
  if (Key1->CapId > Key2->CapId) {
61
38.4k
    return 1;
62
38.4k
  }
63
64
17.4k
  if (Key1->Instance < Key2->Instance) {
65
3.11k
    return -1;
66
3.11k
  }
67
68
14.3k
  if (Key1->Instance > Key2->Instance) {
69
9.52k
    return 1;
70
9.52k
  }
71
72
4.84k
  return 0;
73
14.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
102k
{
96
102k
  CONST PCI_CAP_KEY  *PciCap1Key;
97
98
102k
  PciCap1Key = &((CONST PCI_CAP *)PciCap1)->Key;
99
102k
  return ComparePciCapKey (PciCap1Key, PciCap2);
100
102k
}
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
84.9k
{
157
84.9k
  UINT16  Offset1;
158
84.9k
  UINT16  Offset2;
159
160
84.9k
  Offset1 = ((CONST PCI_CAP *)PciCap1)->Offset;
161
84.9k
  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
84.9k
  return Offset1 - Offset2;
167
84.9k
}
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
17.5k
{
217
17.5k
  PCI_CAP                   *PciCap;
218
17.5k
  RETURN_STATUS             Status;
219
17.5k
  ORDERED_COLLECTION_ENTRY  *PciCapEntry;
220
17.5k
  PCI_CAP                   *InstanceZero;
221
222
17.5k
  ASSERT ((Offset & 0x3) == 0);
223
17.5k
  ASSERT (
224
17.5k
    Offset < (Domain == PciCapNormal ?
225
17.5k
              PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET)
226
17.5k
    );
227
17.5k
  ASSERT (Domain == PciCapExtended || Version == 0);
228
229
  //
230
  // Set InstanceZero to suppress incorrect compiler/analyzer warnings.
231
  //
232
17.5k
  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
17.5k
  PciCap = AllocatePool (sizeof *PciCap);
240
17.5k
  if (PciCap == NULL) {
241
0
    return RETURN_OUT_OF_RESOURCES;
242
0
  }
243
244
17.5k
  PciCap->Key.Domain                     = Domain;
245
17.5k
  PciCap->Key.CapId                      = CapId;
246
17.5k
  PciCap->Key.Instance                   = 0;
247
17.5k
  PciCap->NumInstancesUnion.NumInstances = 1;
248
17.5k
  PciCap->Offset                         = Offset;
249
17.5k
  PciCap->MaxSizeHint                    = 0;
250
17.5k
  PciCap->Version                        = Version;
251
252
  //
253
  // Add PciCap to CapList.
254
  //
255
17.5k
  Status = OrderedCollectionInsert (
256
17.5k
             CapList->Capabilities,
257
17.5k
             &PciCapEntry,
258
17.5k
             PciCap
259
17.5k
             );
260
17.5k
  if (RETURN_ERROR (Status)) {
261
4.00k
    if (Status == RETURN_OUT_OF_RESOURCES) {
262
0
      goto FreePciCap;
263
0
    }
264
265
4.00k
    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.00k
    InstanceZero                           = OrderedCollectionUserStruct (PciCapEntry);
273
4.00k
    PciCap->Key.Instance                   = InstanceZero->NumInstancesUnion.NumInstances;
274
4.00k
    PciCap->NumInstancesUnion.InstanceZero = InstanceZero;
275
276
4.00k
    ASSERT (PciCap->Key.Instance > 0);
277
4.00k
    Status = OrderedCollectionInsert (
278
4.00k
               CapList->Capabilities,
279
4.00k
               &PciCapEntry,
280
4.00k
               PciCap
281
4.00k
               );
282
4.00k
    if (Status == RETURN_OUT_OF_RESOURCES) {
283
0
      goto FreePciCap;
284
0
    }
285
4.00k
  }
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
17.5k
  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
17.5k
  Status = OrderedCollectionInsert (CapHdrOffsets, NULL, PciCap);
303
17.5k
  if (RETURN_ERROR (Status)) {
304
100
    if (Status == RETURN_ALREADY_STARTED) {
305
      //
306
      // Loop found; map return status accordingly.
307
      //
308
100
      Status = RETURN_DEVICE_ERROR;
309
100
    }
310
311
100
    goto DeletePciCapFromCapList;
312
100
  }
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
17.4k
  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
3.90k
    ASSERT (InstanceZero != NULL);
325
326
3.90k
    InstanceZero->NumInstancesUnion.NumInstances++;
327
3.90k
  }
328
329
17.4k
  return RETURN_SUCCESS;
330
331
100
DeletePciCapFromCapList:
332
100
  OrderedCollectionDelete (CapList->Capabilities, PciCapEntry, NULL);
333
334
100
FreePciCap:
335
100
  FreePool (PciCap);
336
337
100
  return Status;
338
100
}
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
12.7k
{
367
12.7k
  UINT16  ConfigSpaceSize;
368
369
12.7k
  ConfigSpaceSize = (PciCap->Key.Domain == PciCapNormal ?
370
8.50k
                     PCI_MAX_CONFIG_OFFSET : PCI_EXP_MAX_CONFIG_OFFSET);
371
  //
372
  // The following is guaranteed by the interface contract on
373
  // CalculatePciCapMaxSizeHint().
374
  //
375
12.7k
  ASSERT (NextPciCap == NULL || PciCap->Offset < NextPciCap->Offset);
376
  //
377
  // The following is guaranteed by the interface contract on InsertPciCap().
378
  //
379
12.7k
  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
12.7k
  if ((NextPciCap == NULL) || (NextPciCap->Offset >= ConfigSpaceSize)) {
389
786
    PciCap->MaxSizeHint = ConfigSpaceSize - PciCap->Offset;
390
786
    return;
391
786
  }
392
393
12.0k
  PciCap->MaxSizeHint = NextPciCap->Offset - PciCap->Offset;
394
12.0k
}
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
556
{
408
1.11k
  DEBUG_CODE_BEGIN ();
409
1.11k
  ORDERED_COLLECTION_ENTRY  *PciCapEntry;
410
411
1.11k
  for (PciCapEntry = OrderedCollectionMin (CapList->Capabilities);
412
13.3k
       PciCapEntry != NULL;
413
12.7k
       PciCapEntry = OrderedCollectionNext (PciCapEntry))
414
12.7k
  {
415
12.7k
    PCI_CAP        *PciCap;
416
12.7k
    RETURN_STATUS  Status;
417
12.7k
    PCI_CAP_INFO   Info;
418
419
12.7k
    PciCap = OrderedCollectionUserStruct (PciCapEntry);
420
12.7k
    Status = PciCapGetInfo (PciCap, &Info);
421
    //
422
    // PciCapGetInfo() cannot fail in this library instance.
423
    //
424
12.7k
    ASSERT_RETURN_ERROR (Status);
425
426
12.7k
    DEBUG ((
427
12.7k
      DEBUG_VERBOSE,
428
12.7k
      "%a:%a: %a 0x%04x %03u/%03u v0x%x @0x%03x+0x%03x\n",
429
12.7k
      gEfiCallerBaseName,
430
12.7k
      __func__,
431
12.7k
      (Info.Domain == PciCapNormal ? "Norm" : "Extd"),
432
12.7k
      Info.CapId,
433
12.7k
      Info.Instance,
434
12.7k
      Info.NumInstances,
435
12.7k
      Info.Version,
436
12.7k
      Info.Offset,
437
12.7k
      Info.MaxSizeHint
438
12.7k
      ));
439
12.7k
  }
440
441
1.11k
  DEBUG_CODE_END ();
442
556
}
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
992
{
464
992
  ORDERED_COLLECTION_ENTRY  *PciCapEntry;
465
992
  ORDERED_COLLECTION_ENTRY  *NextEntry;
466
467
992
  for (PciCapEntry = OrderedCollectionMin (PciCapCollection);
468
23.0k
       PciCapEntry != NULL;
469
22.0k
       PciCapEntry = NextEntry)
470
22.0k
  {
471
22.0k
    PCI_CAP  *PciCap;
472
473
22.0k
    NextEntry = OrderedCollectionNext (PciCapEntry);
474
22.0k
    OrderedCollectionDelete (PciCapCollection, PciCapEntry, (VOID **)&PciCap);
475
22.0k
    if (FreePciCap) {
476
17.4k
      FreePool (PciCap);
477
17.4k
    }
478
22.0k
  }
479
480
992
  OrderedCollectionUninit (PciCapCollection);
481
992
}
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
774
{
522
774
  PCI_CAP_LIST              *OutCapList;
523
774
  RETURN_STATUS             Status;
524
774
  ORDERED_COLLECTION        *CapHdrOffsets;
525
774
  UINT16                    PciStatusReg;
526
774
  BOOLEAN                   DeviceIsExpress;
527
774
  ORDERED_COLLECTION_ENTRY  *OffsetEntry;
528
529
  //
530
  // Allocate the output structure.
531
  //
532
774
  OutCapList = AllocatePool (sizeof *OutCapList);
533
774
  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
774
  OutCapList->Capabilities = OrderedCollectionInit (
542
774
                               ComparePciCap,
543
774
                               ComparePciCapKey
544
774
                               );
545
774
  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
774
  CapHdrOffsets = OrderedCollectionInit (
555
774
                    ComparePciCapOffset,
556
774
                    ComparePciCapOffsetKey
557
774
                    );
558
774
  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
774
  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
774
  Status = PciDevice->ReadConfig (
574
774
                        PciDevice,
575
774
                        PCI_PRIMARY_STATUS_OFFSET,
576
774
                        &PciStatusReg,
577
774
                        sizeof PciStatusReg
578
774
                        );
579
774
  if (RETURN_ERROR (Status)) {
580
0
    goto FreeCapHdrOffsets;
581
0
  }
582
583
774
  if ((PciStatusReg & EFI_PCI_STATUS_CAPABILITY) != 0) {
584
774
    UINT8  NormalCapHdrOffset;
585
586
    //
587
    // Fetch the start offset of the normal capabilities list.
588
    //
589
774
    Status = PciDevice->ReadConfig (
590
774
                          PciDevice,
591
774
                          PCI_CAPBILITY_POINTER_OFFSET,
592
774
                          &NormalCapHdrOffset,
593
774
                          sizeof NormalCapHdrOffset
594
774
                          );
595
774
    if (RETURN_ERROR (Status)) {
596
0
      goto FreeCapHdrOffsets;
597
0
    }
598
599
    //
600
    // Traverse the normal capabilities list.
601
    //
602
774
    NormalCapHdrOffset &= 0xFC;
603
12.1k
    while (NormalCapHdrOffset > 0) {
604
11.4k
      EFI_PCI_CAPABILITY_HDR  NormalCapHdr;
605
606
11.4k
      Status = PciDevice->ReadConfig (
607
11.4k
                            PciDevice,
608
11.4k
                            NormalCapHdrOffset,
609
11.4k
                            &NormalCapHdr,
610
11.4k
                            sizeof NormalCapHdr
611
11.4k
                            );
612
11.4k
      if (RETURN_ERROR (Status)) {
613
0
        goto FreeCapHdrOffsets;
614
0
      }
615
616
11.4k
      Status = InsertPciCap (
617
11.4k
                 OutCapList,
618
11.4k
                 CapHdrOffsets,
619
11.4k
                 PciCapNormal,
620
11.4k
                 NormalCapHdr.CapabilityID,
621
11.4k
                 NormalCapHdrOffset,
622
11.4k
                 0
623
11.4k
                 );
624
11.4k
      if (RETURN_ERROR (Status)) {
625
84
        goto FreeCapHdrOffsets;
626
84
      }
627
628
11.4k
      if (NormalCapHdr.CapabilityID == EFI_PCI_CAPABILITY_ID_PCIEXP) {
629
606
        DeviceIsExpress = TRUE;
630
606
      }
631
632
11.4k
      NormalCapHdrOffset = NormalCapHdr.NextItemPtr & 0xFC;
633
11.4k
    }
634
774
  }
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
690
  if (DeviceIsExpress) {
641
444
    UINT16  ExtendedCapHdrOffset;
642
643
444
    ExtendedCapHdrOffset = PCI_MAX_CONFIG_OFFSET;
644
6.31k
    while (ExtendedCapHdrOffset > 0) {
645
6.06k
      PCI_EXPRESS_EXTENDED_CAPABILITIES_HEADER  ExtendedCapHdr;
646
647
6.06k
      Status = PciDevice->ReadConfig (
648
6.06k
                            PciDevice,
649
6.06k
                            ExtendedCapHdrOffset,
650
6.06k
                            &ExtendedCapHdr,
651
6.06k
                            sizeof ExtendedCapHdr
652
6.06k
                            );
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
6.06k
      if ((ExtendedCapHdrOffset == PCI_MAX_CONFIG_OFFSET) &&
660
452
          (RETURN_ERROR (Status) ||
661
452
           IsZeroBuffer (&ExtendedCapHdr, sizeof ExtendedCapHdr)))
662
60
      {
663
60
        break;
664
60
      }
665
666
6.00k
      if (RETURN_ERROR (Status)) {
667
0
        goto FreeCapHdrOffsets;
668
0
      }
669
670
6.00k
      Status = InsertPciCap (
671
6.00k
                 OutCapList,
672
6.00k
                 CapHdrOffsets,
673
6.00k
                 PciCapExtended,
674
6.00k
                 (UINT16)ExtendedCapHdr.CapabilityId,
675
6.00k
                 ExtendedCapHdrOffset,
676
6.00k
                 (UINT8)ExtendedCapHdr.CapabilityVersion
677
6.00k
                 );
678
6.00k
      if (RETURN_ERROR (Status)) {
679
16
        goto FreeCapHdrOffsets;
680
16
      }
681
682
5.99k
      ExtendedCapHdrOffset = ExtendedCapHdr.NextCapabilityOffset & 0xFFC;
683
5.99k
      if ((ExtendedCapHdrOffset > 0) &&
684
5.74k
          (ExtendedCapHdrOffset < PCI_MAX_CONFIG_OFFSET))
685
118
      {
686
        //
687
        // Invalid capability pointer.
688
        //
689
118
        Status = RETURN_DEVICE_ERROR;
690
118
        goto FreeCapHdrOffsets;
691
118
      }
692
5.99k
    }
693
444
  }
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
556
  OffsetEntry = OrderedCollectionMin (CapHdrOffsets);
704
556
  if (OffsetEntry != NULL) {
705
536
    ORDERED_COLLECTION_ENTRY  *NextOffsetEntry;
706
536
    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
536
    NextOffsetEntry = OrderedCollectionNext (OffsetEntry);
713
    //
714
    // Calculate MaxSizeHint for all PCI_CAP objects except the one with the
715
    // highest Offset.
716
    //
717
12.7k
    while (NextOffsetEntry != NULL) {
718
12.2k
      PCI_CAP  *NextPciCap;
719
720
12.2k
      OrderedCollectionDelete (CapHdrOffsets, OffsetEntry, (VOID **)&PciCap);
721
12.2k
      NextPciCap = OrderedCollectionUserStruct (NextOffsetEntry);
722
12.2k
      CalculatePciCapMaxSizeHint (PciCap, NextPciCap);
723
724
12.2k
      OffsetEntry     = NextOffsetEntry;
725
12.2k
      NextOffsetEntry = OrderedCollectionNext (OffsetEntry);
726
12.2k
    }
727
728
    //
729
    // Calculate MaxSizeHint for the PCI_CAP object with the highest Offset.
730
    //
731
536
    OrderedCollectionDelete (CapHdrOffsets, OffsetEntry, (VOID **)&PciCap);
732
536
    CalculatePciCapMaxSizeHint (PciCap, NULL);
733
536
  }
734
735
556
  ASSERT (OrderedCollectionIsEmpty (CapHdrOffsets));
736
556
  OrderedCollectionUninit (CapHdrOffsets);
737
738
556
  DebugDumpPciCapList (OutCapList);
739
556
  *CapList = OutCapList;
740
556
  return RETURN_SUCCESS;
741
742
218
FreeCapHdrOffsets:
743
218
  EmptyAndUninitPciCapCollection (CapHdrOffsets, FALSE);
744
745
218
FreeCapabilities:
746
218
  EmptyAndUninitPciCapCollection (OutCapList->Capabilities, TRUE);
747
748
218
FreeOutCapList:
749
218
  FreePool (OutCapList);
750
751
218
  ASSERT (RETURN_ERROR (Status));
752
218
  DEBUG ((
753
218
    DEBUG_ERROR,
754
218
    "%a:%a: %r\n",
755
218
    gEfiCallerBaseName,
756
218
    __func__,
757
218
    Status
758
218
    ));
759
218
  return Status;
760
218
}
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
556
{
774
556
  EmptyAndUninitPciCapCollection (CapList->Capabilities, TRUE);
775
556
  FreePool (CapList);
776
556
}
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.30k
{
821
1.30k
  PCI_CAP_KEY               Key;
822
1.30k
  ORDERED_COLLECTION_ENTRY  *PciCapEntry;
823
824
1.30k
  Key.Domain   = Domain;
825
1.30k
  Key.CapId    = CapId;
826
1.30k
  Key.Instance = Instance;
827
828
1.30k
  PciCapEntry = OrderedCollectionFind (CapList->Capabilities, &Key);
829
1.30k
  if (PciCapEntry == NULL) {
830
474
    return RETURN_NOT_FOUND;
831
474
  }
832
833
834
  if (Cap != NULL) {
834
834
    *Cap = OrderedCollectionUserStruct (PciCapEntry);
835
834
  }
836
837
834
  return RETURN_SUCCESS;
838
1.30k
}
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
12.7k
{
945
12.7k
  PCI_CAP  *InstanceZero;
946
947
12.7k
  ASSERT (Info != NULL);
948
949
12.7k
  InstanceZero = (Cap->Key.Instance == 0 ? Cap :
950
12.7k
                  Cap->NumInstancesUnion.InstanceZero);
951
952
12.7k
  Info->Domain       = Cap->Key.Domain;
953
12.7k
  Info->CapId        = Cap->Key.CapId;
954
12.7k
  Info->NumInstances = InstanceZero->NumInstancesUnion.NumInstances;
955
12.7k
  Info->Instance     = Cap->Key.Instance;
956
12.7k
  Info->Offset       = Cap->Offset;
957
12.7k
  Info->MaxSizeHint  = Cap->MaxSizeHint;
958
12.7k
  Info->Version      = Cap->Version;
959
960
12.7k
  return RETURN_SUCCESS;
961
12.7k
}
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.25k
{
1009
  //
1010
  // Note: all UINT16 values are promoted to INT32 below, and addition and
1011
  // comparison take place between INT32 values.
1012
  //
1013
1.25k
  if (SourceOffsetInCap + Size > Cap->MaxSizeHint) {
1014
62
    return RETURN_BAD_BUFFER_SIZE;
1015
62
  }
1016
1017
1.19k
  return PciDevice->ReadConfig (
1018
1.19k
                      PciDevice,
1019
1.19k
                      Cap->Offset + SourceOffsetInCap,
1020
1.19k
                      DestinationBuffer,
1021
1.19k
                      Size
1022
1.19k
                      );
1023
1.25k
}
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
}