/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 | } |