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