]> git.proxmox.com Git - mirror_edk2.git/blame - OvmfPkg/Library/HardwareInfoLib/HardwareInfoPciHostBridgeLib.c
OvmfPkg/Library: Create base HardwareInfoLib for PCI Host Bridges
[mirror_edk2.git] / OvmfPkg / Library / HardwareInfoLib / HardwareInfoPciHostBridgeLib.c
CommitLineData
6c9f218b
NOL
1/**@file\r
2 Hardware info library with types and accessors to parse information about\r
3 PCI host bridges.\r
4\r
5 Copyright 2021 - 2022 Amazon.com, Inc. or its affiliates. All Rights Reserved.\r
6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
7\r
8**/\r
9\r
10#include <Library/DebugLib.h>\r
11\r
12#include "HardwareInfoPciHostBridgeLib.h"\r
13\r
14#define IS_RANGE_INVALID(Start, Size, MaxValue) (Start >= MaxValue || Size == 0)\r
15\r
16/**\r
17 Calculate the last (inclusive) address of a range.\r
18\r
19 @param[in] Start First address of the range\r
20 @param[in] Size Size of the range\r
21 @return Last address of the range\r
22**/\r
23STATIC\r
24UINT64\r
25GetRangeEnd (\r
26 IN UINT64 Start,\r
27 IN UINT64 Size,\r
28 IN UINT64 MaxValue\r
29 )\r
30{\r
31 if (IS_RANGE_INVALID (Start, Size, MaxValue)) {\r
32 return 0;\r
33 }\r
34\r
35 return Start + Size - 1;\r
36}\r
37\r
38/**\r
39 Internal helper to update LastAddress if the Limit address\r
40 of the Mem aperture is higher than the provided value.\r
41\r
42 @param[in] Mem Pointer to aperture whose limit is\r
43 to be compared against accumulative\r
44 last address.\r
45 @param[out] LastAddress Pointer to accumulative last address\r
46 to be updated if Limit is higher\r
47**/\r
48STATIC\r
49VOID\r
50UpdateLastAddressIfHigher (\r
51 IN PCI_ROOT_BRIDGE_APERTURE *Mem,\r
52 OUT UINT64 *LastAddress\r
53 )\r
54{\r
55 if (Mem->Limit > *LastAddress) {\r
56 *LastAddress = Mem->Limit;\r
57 }\r
58}\r
59\r
60EFI_STATUS\r
61HardwareInfoPciHostBridgeLastMmioAddress (\r
62 IN CONST HOST_BRIDGE_INFO *HostBridge,\r
63 IN UINTN DataSize,\r
64 IN BOOLEAN HighMem,\r
65 OUT UINT64 *LastMmioAddress\r
66 )\r
67{\r
68 EFI_STATUS Status;\r
69 PCI_ROOT_BRIDGE_APERTURE Mem;\r
70 PCI_ROOT_BRIDGE_APERTURE MemAbove4G;\r
71 PCI_ROOT_BRIDGE_APERTURE PMem;\r
72 PCI_ROOT_BRIDGE_APERTURE PMemAbove4G;\r
73\r
74 if (LastMmioAddress == NULL) {\r
75 return EFI_INVALID_PARAMETER;\r
76 }\r
77\r
78 //\r
79 // Set the output to the lowest possible value so that if some step fails\r
80 // the overall outcome reflects no information found\r
81 //\r
82 *LastMmioAddress = 0;\r
83\r
84 Status = HardwareInfoPciHostBridgeGetApertures (\r
85 HostBridge,\r
86 DataSize,\r
87 NULL,\r
88 &Mem,\r
89 &MemAbove4G,\r
90 &PMem,\r
91 &PMemAbove4G,\r
92 NULL\r
93 );\r
94\r
95 //\r
96 // Forward error to caller but ignore warnings given that, very likely,\r
97 // the host bridge will have a PIO aperture we are explicitly\r
98 // ignoring here since we care only about MMIO resources.\r
99 //\r
100 if (EFI_ERROR (Status)) {\r
101 return Status;\r
102 }\r
103\r
104 if (HighMem) {\r
105 UpdateLastAddressIfHigher (&MemAbove4G, LastMmioAddress);\r
106 UpdateLastAddressIfHigher (&PMemAbove4G, LastMmioAddress);\r
107 } else {\r
108 UpdateLastAddressIfHigher (&Mem, LastMmioAddress);\r
109 UpdateLastAddressIfHigher (&PMem, LastMmioAddress);\r
110 }\r
111\r
112 return EFI_SUCCESS;\r
113}\r
114\r
115/**\r
116 Set the boundaries of an aperture to invalid values having\r
117 size zero and start MaxValue (yields Start > Limit which\r
118 depicts an invalid range)\r
119\r
120 @param[in] MaxValue Max value of the aperture's range (depends\r
121 on the data type)\r
122 @param[out] Aperture Aperture object to invalidate\r
123**/\r
124STATIC\r
125VOID\r
126InvalidateRootBridgeAperture (\r
127 OUT PCI_ROOT_BRIDGE_APERTURE *Aperture\r
128 )\r
129{\r
130 if (Aperture == NULL) {\r
131 return;\r
132 }\r
133\r
134 Aperture->Base = MAX_UINT64;\r
135 Aperture->Limit = 0;\r
136}\r
137\r
138/**\r
139 Fill a PCI ROOT BRIDGE APERTURE with the proper values calculated\r
140 from the provided start and size.\r
141\r
142 @param[in] Start Start address of the aperture\r
143 @param[in] Size Size, in bytes, of the aperture\r
144 @param[in] MaxValue Max value a valid address could take and which\r
145 represents an invalid start address.\r
146 @param[out] Aperture Pointer to the aperture to be filled\r
147\r
148 @retval EFI_SUCCESS Aperture was filled successfully\r
149 @retval EFI_INVALID_PARAMETER Range depicted by Start and Size is\r
150 valid but ignored because aperture\r
151 pointer is NULL\r
152 @retval EFI_WARN_BUFFER_TOO_SMALL Aperture pointer is invalid but the\r
153 range also is so no harm.\r
154**/\r
155STATIC\r
156EFI_STATUS\r
157FillHostBridgeAperture (\r
158 IN UINT64 Start,\r
159 IN UINT64 Size,\r
160 IN UINT64 MaxValue,\r
161 OUT PCI_ROOT_BRIDGE_APERTURE *Aperture\r
162 )\r
163{\r
164 UINT64 End;\r
165\r
166 End = GetRangeEnd (Start, Size, MaxValue);\r
167\r
168 if (Aperture == NULL) {\r
169 if (!IS_RANGE_INVALID (Start, Size, MaxValue)) {\r
170 //\r
171 // Report an error to the caller since the range specified in\r
172 // the host bridge's resources is non-empty but the provided\r
173 // aperture pointer is null, thus the valid range is ignored.\r
174 //\r
175 return EFI_INVALID_PARAMETER;\r
176 }\r
177\r
178 return EFI_WARN_BUFFER_TOO_SMALL;\r
179 }\r
180\r
181 if (IS_RANGE_INVALID (Start, Size, MaxValue)) {\r
182 //\r
183 // Fill Aperture with invalid range values to signal the\r
184 // absence of an address space (empty range)\r
185 //\r
186 InvalidateRootBridgeAperture (Aperture);\r
187 } else {\r
188 Aperture->Base = Start;\r
189 Aperture->Limit = End;\r
190 }\r
191\r
192 return EFI_SUCCESS;\r
193}\r
194\r
195/**\r
196 Merge 2 ranges (normal and prefetchable) into a single aperture\r
197 comprehending the addresses encompassed by both of them. If both\r
198 ranges are not empty they must be contiguous for correctness.\r
199\r
200 @param[in] Start Range start address\r
201 @param[in] Size Range size in bytes\r
202 @param[in] PStart Prefetchable range start address\r
203 @param[in] PSize Prefetchable range size in bytes\r
204 @param[in] MaxValue Max value a valid address could take and which\r
205 represents an invalid start address.\r
206 @param[out] Aperture Pointer to the aperture to be filled\r
207\r
208 @retval EFI_SUCCESS Aperture was filled successfully\r
209 @retval EFI_INVALID_PARAMETER Either range depicted by Start, Size\r
210 or PStart, PSize or both are valid\r
211 but ignored because aperture pointer\r
212 is NULL\r
213 @retval EFI_WARN_BUFFER_TOO_SMALL Aperture pointer is invalid but both\r
214 ranges are too so no harm.\r
215**/\r
216STATIC\r
217EFI_STATUS\r
218MergeHostBridgeApertures (\r
219 IN UINT64 Start,\r
220 IN UINT64 Size,\r
221 IN UINT64 PStart,\r
222 IN UINT64 PSize,\r
223 IN UINT64 MaxValue,\r
224 OUT PCI_ROOT_BRIDGE_APERTURE *Aperture\r
225 )\r
226{\r
227 UINT64 PEnd;\r
228\r
229 if (Aperture == NULL) {\r
230 if (!IS_RANGE_INVALID (Start, Size, MaxValue) ||\r
231 !IS_RANGE_INVALID (PStart, PSize, MaxValue))\r
232 {\r
233 //\r
234 // Report an error to the caller since the range specified in\r
235 // the host bridge's resources is non-empty but the provided\r
236 // aperture pointer is null, thus the valid range is ignored.\r
237 //\r
238 return EFI_INVALID_PARAMETER;\r
239 }\r
240\r
241 return EFI_WARN_BUFFER_TOO_SMALL;\r
242 }\r
243\r
244 //\r
245 // Start from an empty range (Limit < Base)\r
246 //\r
247 InvalidateRootBridgeAperture (Aperture);\r
248\r
249 if (!IS_RANGE_INVALID (Start, Size, MaxValue)) {\r
250 Aperture->Base = Start;\r
251 Aperture->Limit = Start + Size - 1;\r
252 }\r
253\r
254 if (!IS_RANGE_INVALID (PStart, PSize, MaxValue)) {\r
255 PEnd = PStart + PSize - 1;\r
256\r
257 if (PStart < Aperture->Base) {\r
258 Aperture->Base = PStart;\r
259 }\r
260\r
261 if (PEnd > Aperture->Limit) {\r
262 Aperture->Limit = PEnd;\r
263 }\r
264 }\r
265\r
266 return EFI_SUCCESS;\r
267}\r
268\r
269EFI_STATUS\r
270HardwareInfoPciHostBridgeGetBusNrRange (\r
271 IN CONST HOST_BRIDGE_INFO *HostBridge,\r
272 IN UINTN DataSize,\r
273 OUT UINTN *BusNrStart,\r
274 OUT UINTN *BusNrLast\r
275 )\r
276{\r
277 if ((HostBridge == NULL) || (DataSize == 0) ||\r
278 (BusNrStart == NULL) || (BusNrLast == NULL))\r
279 {\r
280 return EFI_INVALID_PARAMETER;\r
281 }\r
282\r
283 //\r
284 // For now only version 0 is supported\r
285 //\r
286 if (HostBridge->Version != 0) {\r
287 return EFI_INCOMPATIBLE_VERSION;\r
288 }\r
289\r
290 *BusNrStart = HostBridge->BusNrStart;\r
291 *BusNrLast = HostBridge->BusNrLast;\r
292\r
293 return EFI_SUCCESS;\r
294}\r
295\r
296EFI_STATUS\r
297HardwareInfoPciHostBridgeGetApertures (\r
298 IN CONST HOST_BRIDGE_INFO *HostBridge,\r
299 IN UINTN DataSize,\r
300 OUT PCI_ROOT_BRIDGE_APERTURE *Io,\r
301 OUT PCI_ROOT_BRIDGE_APERTURE *Mem,\r
302 OUT PCI_ROOT_BRIDGE_APERTURE *MemAbove4G,\r
303 OUT PCI_ROOT_BRIDGE_APERTURE *PMem,\r
304 OUT PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G,\r
305 OUT PCI_ROOT_BRIDGE_APERTURE *PcieConfig\r
306 )\r
307{\r
308 EFI_STATUS Status;\r
309 BOOLEAN StickyError;\r
310\r
311 StickyError = FALSE;\r
312 if ((HostBridge == NULL) || (DataSize == 0)) {\r
313 return EFI_INVALID_PARAMETER;\r
314 }\r
315\r
316 //\r
317 // For now only version 0 is supported\r
318 //\r
319 if (HostBridge->Version != 0) {\r
320 return EFI_INCOMPATIBLE_VERSION;\r
321 }\r
322\r
323 Status = FillHostBridgeAperture (\r
324 HostBridge->IoStart,\r
325 HostBridge->IoSize,\r
326 MAX_UINT32,\r
327 Io\r
328 );\r
329 StickyError |= EFI_ERROR (Status);\r
330\r
331 Status = FillHostBridgeAperture (\r
332 HostBridge->PcieConfigStart,\r
333 HostBridge->PcieConfigSize,\r
334 MAX_UINT64,\r
335 PcieConfig\r
336 );\r
337 StickyError |= EFI_ERROR (Status);\r
338\r
339 if (HostBridge->Flags.Bits.CombineMemPMem) {\r
340 Status = MergeHostBridgeApertures (\r
341 HostBridge->MemStart,\r
342 HostBridge->MemSize,\r
343 HostBridge->PMemStart,\r
344 HostBridge->PMemSize,\r
345 MAX_UINT32,\r
346 Mem\r
347 );\r
348 StickyError |= EFI_ERROR (Status);\r
349\r
350 Status = MergeHostBridgeApertures (\r
351 HostBridge->MemAbove4GStart,\r
352 HostBridge->MemAbove4GSize,\r
353 HostBridge->PMemAbove4GStart,\r
354 HostBridge->PMemAbove4GSize,\r
355 MAX_UINT64,\r
356 MemAbove4G\r
357 );\r
358 StickyError |= EFI_ERROR (Status);\r
359\r
360 //\r
361 // Invalidate unused apertures\r
362 //\r
363 InvalidateRootBridgeAperture (PMem);\r
364 InvalidateRootBridgeAperture (PMemAbove4G);\r
365 } else {\r
366 Status = FillHostBridgeAperture (\r
367 HostBridge->MemStart,\r
368 HostBridge->MemSize,\r
369 MAX_UINT32,\r
370 Mem\r
371 );\r
372 StickyError |= EFI_ERROR (Status);\r
373\r
374 Status = FillHostBridgeAperture (\r
375 HostBridge->PMemStart,\r
376 HostBridge->PMemSize,\r
377 MAX_UINT32,\r
378 PMem\r
379 );\r
380 StickyError |= EFI_ERROR (Status);\r
381\r
382 Status = FillHostBridgeAperture (\r
383 HostBridge->MemAbove4GStart,\r
384 HostBridge->MemAbove4GSize,\r
385 MAX_UINT64,\r
386 MemAbove4G\r
387 );\r
388 StickyError |= EFI_ERROR (Status);\r
389\r
390 Status = FillHostBridgeAperture (\r
391 HostBridge->PMemAbove4GStart,\r
392 HostBridge->PMemAbove4GSize,\r
393 MAX_UINT64,\r
394 PMem\r
395 );\r
396 StickyError |= EFI_ERROR (Status);\r
397 }\r
398\r
399 if (StickyError) {\r
400 //\r
401 // If any function returned an error it is due to a valid range\r
402 // specified in the host bridge that was ignored due to a NULL\r
403 // pointer. Translate it to a warning to allow for calling with\r
404 // only a subset of the apertures.\r
405 //\r
406 return EFI_WARN_STALE_DATA;\r
407 }\r
408\r
409 return EFI_SUCCESS;\r
410}\r
411\r
412EFI_STATUS\r
413HardwareInfoPciHostBridgeGetFlags (\r
414 IN CONST HOST_BRIDGE_INFO *HostBridge,\r
415 IN UINTN DataSize,\r
416 OUT UINT64 *Attributes OPTIONAL,\r
417 OUT BOOLEAN *DmaAbove4G OPTIONAL,\r
418 OUT BOOLEAN *NoExtendedConfigSpace OPTIONAL,\r
419 OUT BOOLEAN *CombineMemPMem OPTIONAL\r
420 )\r
421{\r
422 if ((HostBridge == NULL) || (DataSize == 0)) {\r
423 return EFI_INVALID_PARAMETER;\r
424 }\r
425\r
426 //\r
427 // For now only version 0 is supported\r
428 //\r
429 if (HostBridge->Version != 0) {\r
430 return EFI_INCOMPATIBLE_VERSION;\r
431 }\r
432\r
433 if (Attributes) {\r
434 *Attributes = HostBridge->Attributes;\r
435 }\r
436\r
437 if (DmaAbove4G) {\r
438 *DmaAbove4G = !!HostBridge->Flags.Bits.DmaAbove4G;\r
439 }\r
440\r
441 if (NoExtendedConfigSpace) {\r
442 *NoExtendedConfigSpace = !!HostBridge->Flags.Bits.NoExtendedConfigSpace;\r
443 }\r
444\r
445 if (CombineMemPMem) {\r
446 *CombineMemPMem = !!HostBridge->Flags.Bits.CombineMemPMem;\r
447 }\r
448\r
449 return EFI_SUCCESS;\r
450}\r
451\r
452EFI_STATUS\r
453HardwareInfoPciHostBridgeGet (\r
454 IN CONST HOST_BRIDGE_INFO *HostBridge,\r
455 IN UINTN DataSize,\r
456 OUT UINTN *BusNrStart,\r
457 OUT UINTN *BusNrLast,\r
458 OUT UINT64 *Attributes OPTIONAL,\r
459 OUT BOOLEAN *DmaAbove4G OPTIONAL,\r
460 OUT BOOLEAN *NoExtendedConfigSpace OPTIONAL,\r
461 OUT BOOLEAN *CombineMemPMem OPTIONAL,\r
462 OUT PCI_ROOT_BRIDGE_APERTURE *Io OPTIONAL,\r
463 OUT PCI_ROOT_BRIDGE_APERTURE *Mem OPTIONAL,\r
464 OUT PCI_ROOT_BRIDGE_APERTURE *MemAbove4G OPTIONAL,\r
465 OUT PCI_ROOT_BRIDGE_APERTURE *PMem OPTIONAL,\r
466 OUT PCI_ROOT_BRIDGE_APERTURE *PMemAbove4G OPTIONAL,\r
467 OUT PCI_ROOT_BRIDGE_APERTURE *PcieConfig OPTIONAL\r
468 )\r
469{\r
470 EFI_STATUS Status;\r
471\r
472 Status = HardwareInfoPciHostBridgeGetBusNrRange (\r
473 HostBridge,\r
474 DataSize,\r
475 BusNrStart,\r
476 BusNrLast\r
477 );\r
478\r
479 if (EFI_ERROR (Status)) {\r
480 return Status;\r
481 }\r
482\r
483 Status = HardwareInfoPciHostBridgeGetFlags (\r
484 HostBridge,\r
485 DataSize,\r
486 Attributes,\r
487 DmaAbove4G,\r
488 NoExtendedConfigSpace,\r
489 CombineMemPMem\r
490 );\r
491\r
492 if (EFI_ERROR (Status)) {\r
493 return Status;\r
494 }\r
495\r
496 Status = HardwareInfoPciHostBridgeGetApertures (\r
497 HostBridge,\r
498 DataSize,\r
499 Io,\r
500 Mem,\r
501 MemAbove4G,\r
502 PMem,\r
503 PMemAbove4G,\r
504 PcieConfig\r
505 );\r
506\r
507 return Status;\r
508}\r