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