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