]> git.proxmox.com Git - mirror_edk2.git/blob - OvmfPkg/PciHotPlugInitDxe/PciHotPlugInit.c
OvmfPkg/PciHotPlugInitDxe: generalize RESOURCE_PADDING composition
[mirror_edk2.git] / OvmfPkg / PciHotPlugInitDxe / PciHotPlugInit.c
1 /** @file
2 This driver implements EFI_PCI_HOT_PLUG_INIT_PROTOCOL, providing the PCI bus
3 driver with resource padding information, for PCIe hotplug purposes.
4
5 Copyright (C) 2016, Red Hat, Inc.
6
7 This program and the accompanying materials are licensed and made available
8 under the terms and conditions of the BSD License which accompanies this
9 distribution. The full text of the license may be found at
10 http://opensource.org/licenses/bsd-license.php
11
12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT
13 WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
14 **/
15
16 #include <IndustryStandard/Acpi10.h>
17
18 #include <Library/BaseMemoryLib.h>
19 #include <Library/DebugLib.h>
20 #include <Library/DevicePathLib.h>
21 #include <Library/MemoryAllocationLib.h>
22 #include <Library/UefiBootServicesTableLib.h>
23
24 #include <Protocol/PciHotPlugInit.h>
25 #include <Protocol/PciRootBridgeIo.h>
26
27 //
28 // The protocol interface this driver produces.
29 //
30 // Refer to 12.6 "PCI Hot Plug PCI Initialization Protocol" in the Platform
31 // Init 1.4a Spec, Volume 5.
32 //
33 STATIC EFI_PCI_HOT_PLUG_INIT_PROTOCOL mPciHotPlugInit;
34
35
36 //
37 // Resource padding template for the GetResourcePadding() protocol member
38 // function.
39 //
40 // Refer to Table 8 "ACPI 2.0 & 3.0 QWORD Address Space Descriptor Usage" in
41 // the Platform Init 1.4a Spec, Volume 5.
42 //
43 // This structure is interpreted by the ApplyResourcePadding() function in the
44 // edk2 PCI Bus UEFI_DRIVER.
45 //
46 // We can request padding for at most four resource types, each of which is
47 // optional, independently of the others:
48 // (a) bus numbers,
49 // (b) IO space,
50 // (c) non-prefetchable MMIO space (32-bit only),
51 // (d) prefetchable MMIO space (either 32-bit or 64-bit, never both).
52 //
53 #pragma pack (1)
54 typedef struct {
55 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR Padding[4];
56 EFI_ACPI_END_TAG_DESCRIPTOR EndDesc;
57 } RESOURCE_PADDING;
58 #pragma pack ()
59
60
61 /**
62 Initialize a RESOURCE_PADDING object.
63
64 @param[out] ResourcePadding The caller-allocated RESOURCE_PADDING object to
65 initialize.
66 **/
67 STATIC
68 VOID
69 InitializeResourcePadding (
70 OUT RESOURCE_PADDING *ResourcePadding
71 )
72 {
73 UINTN Index;
74
75 ZeroMem (ResourcePadding, sizeof *ResourcePadding);
76
77 //
78 // Fill in the Padding fields that don't vary across resource types.
79 //
80 for (Index = 0; Index < ARRAY_SIZE (ResourcePadding->Padding); ++Index) {
81 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Descriptor;
82
83 Descriptor = ResourcePadding->Padding + Index;
84 Descriptor->Desc = ACPI_ADDRESS_SPACE_DESCRIPTOR;
85 Descriptor->Len = (UINT16)(
86 sizeof (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR) -
87 OFFSET_OF (
88 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR,
89 ResType
90 )
91 );
92 }
93
94 //
95 // Fill in the End Tag.
96 //
97 ResourcePadding->EndDesc.Desc = ACPI_END_TAG_DESCRIPTOR;
98 }
99
100
101 /**
102 Returns a list of root Hot Plug Controllers (HPCs) that require
103 initialization during the boot process.
104
105 This procedure returns a list of root HPCs. The PCI bus driver must
106 initialize these controllers during the boot process. The PCI bus driver may
107 or may not be able to detect these HPCs. If the platform includes a
108 PCI-to-CardBus bridge, it can be included in this list if it requires
109 initialization. The HpcList must be self consistent. An HPC cannot control
110 any of its parent buses. Only one HPC can control a PCI bus. Because this
111 list includes only root HPCs, no HPC in the list can be a child of another
112 HPC. This policy must be enforced by the EFI_PCI_HOT_PLUG_INIT_PROTOCOL.
113 The PCI bus driver may not check for such invalid conditions. The callee
114 allocates the buffer HpcList
115
116 @param[in] This Pointer to the EFI_PCI_HOT_PLUG_INIT_PROTOCOL
117 instance.
118 @param[out] HpcCount The number of root HPCs that were returned.
119 @param[out] HpcList The list of root HPCs. HpcCount defines the number of
120 elements in this list.
121
122 @retval EFI_SUCCESS HpcList was returned.
123 @retval EFI_OUT_OF_RESOURCES HpcList was not returned due to insufficient
124 resources.
125 @retval EFI_INVALID_PARAMETER HpcCount is NULL or HpcList is NULL.
126 **/
127 STATIC
128 EFI_STATUS
129 EFIAPI
130 GetRootHpcList (
131 IN EFI_PCI_HOT_PLUG_INIT_PROTOCOL *This,
132 OUT UINTN *HpcCount,
133 OUT EFI_HPC_LOCATION **HpcList
134 )
135 {
136 if (HpcCount == NULL || HpcList == NULL) {
137 return EFI_INVALID_PARAMETER;
138 }
139
140 //
141 // There are no top-level (i.e., un-enumerable) hot-plug controllers in QEMU
142 // that would require special initialization.
143 //
144 *HpcCount = 0;
145 *HpcList = NULL;
146 return EFI_SUCCESS;
147 }
148
149
150 /**
151 Initializes one root Hot Plug Controller (HPC). This process may causes
152 initialization of its subordinate buses.
153
154 This function initializes the specified HPC. At the end of initialization,
155 the hot-plug slots or sockets (controlled by this HPC) are powered and are
156 connected to the bus. All the necessary registers in the HPC are set up. For
157 a Standard (PCI) Hot Plug Controller (SHPC), the registers that must be set
158 up are defined in the PCI Standard Hot Plug Controller and Subsystem
159 Specification.
160
161 @param[in] This Pointer to the EFI_PCI_HOT_PLUG_INIT_PROTOCOL
162 instance.
163 @param[in] HpcDevicePath The device path to the HPC that is being
164 initialized.
165 @param[in] HpcPciAddress The address of the HPC function on the PCI bus.
166 @param[in] Event The event that should be signaled when the HPC
167 initialization is complete. Set to NULL if the
168 caller wants to wait until the entire
169 initialization process is complete.
170 @param[out] HpcState The state of the HPC hardware. The state is
171 EFI_HPC_STATE_INITIALIZED or
172 EFI_HPC_STATE_ENABLED.
173
174 @retval EFI_SUCCESS If Event is NULL, the specific HPC was
175 successfully initialized. If Event is not
176 NULL, Event will be signaled at a later time
177 when initialization is complete.
178 @retval EFI_UNSUPPORTED This instance of
179 EFI_PCI_HOT_PLUG_INIT_PROTOCOL does not
180 support the specified HPC.
181 @retval EFI_OUT_OF_RESOURCES Initialization failed due to insufficient
182 resources.
183 @retval EFI_INVALID_PARAMETER HpcState is NULL.
184 **/
185 STATIC
186 EFI_STATUS
187 EFIAPI
188 InitializeRootHpc (
189 IN EFI_PCI_HOT_PLUG_INIT_PROTOCOL *This,
190 IN EFI_DEVICE_PATH_PROTOCOL *HpcDevicePath,
191 IN UINT64 HpcPciAddress,
192 IN EFI_EVENT Event, OPTIONAL
193 OUT EFI_HPC_STATE *HpcState
194 )
195 {
196 //
197 // This function should never be called, due to the information returned by
198 // GetRootHpcList().
199 //
200 ASSERT (FALSE);
201
202 if (HpcState == NULL) {
203 return EFI_INVALID_PARAMETER;
204 }
205 return EFI_UNSUPPORTED;
206 }
207
208
209 /**
210 Returns the resource padding that is required by the PCI bus that is
211 controlled by the specified Hot Plug Controller (HPC).
212
213 This function returns the resource padding that is required by the PCI bus
214 that is controlled by the specified HPC. This member function is called for
215 all the root HPCs and nonroot HPCs that are detected by the PCI bus
216 enumerator. This function will be called before PCI resource allocation is
217 completed. This function must be called after all the root HPCs, with the
218 possible exception of a PCI-to-CardBus bridge, have completed
219 initialization.
220
221 @param[in] This Pointer to the EFI_PCI_HOT_PLUG_INIT_PROTOCOL
222 instance.
223 @param[in] HpcDevicePath The device path to the HPC.
224 @param[in] HpcPciAddress The address of the HPC function on the PCI bus.
225 @param[in] HpcState The state of the HPC hardware.
226 @param[out] Padding The amount of resource padding that is required
227 by the PCI bus under the control of the specified
228 HPC.
229 @param[out] Attributes Describes how padding is accounted for. The
230 padding is returned in the form of ACPI 2.0
231 resource descriptors.
232
233 @retval EFI_SUCCESS The resource padding was successfully
234 returned.
235 @retval EFI_UNSUPPORTED This instance of the
236 EFI_PCI_HOT_PLUG_INIT_PROTOCOL does not
237 support the specified HPC.
238 @retval EFI_NOT_READY This function was called before HPC
239 initialization is complete.
240 @retval EFI_INVALID_PARAMETER HpcState or Padding or Attributes is NULL.
241 @retval EFI_OUT_OF_RESOURCES ACPI 2.0 resource descriptors for Padding
242 cannot be allocated due to insufficient
243 resources.
244 **/
245 STATIC
246 EFI_STATUS
247 EFIAPI
248 GetResourcePadding (
249 IN EFI_PCI_HOT_PLUG_INIT_PROTOCOL *This,
250 IN EFI_DEVICE_PATH_PROTOCOL *HpcDevicePath,
251 IN UINT64 HpcPciAddress,
252 OUT EFI_HPC_STATE *HpcState,
253 OUT VOID **Padding,
254 OUT EFI_HPC_PADDING_ATTRIBUTES *Attributes
255 )
256 {
257 BOOLEAN DefaultIo;
258 BOOLEAN DefaultMmio;
259 RESOURCE_PADDING ReservationRequest;
260 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *FirstResource;
261
262 DEBUG_CODE (
263 EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *Address;
264 CHAR16 *DevicePathString;
265
266 Address = (EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL_PCI_ADDRESS *)&HpcPciAddress;
267 DevicePathString = ConvertDevicePathToText (HpcDevicePath, FALSE, FALSE);
268
269 DEBUG ((EFI_D_VERBOSE, "%a: Address=%02x:%02x.%x DevicePath=%s\n",
270 __FUNCTION__, Address->Bus, Address->Device, Address->Function,
271 (DevicePathString == NULL) ? L"<unavailable>" : DevicePathString));
272
273 if (DevicePathString != NULL) {
274 FreePool (DevicePathString);
275 }
276 );
277
278 if (HpcState == NULL || Padding == NULL || Attributes == NULL) {
279 return EFI_INVALID_PARAMETER;
280 }
281
282 DefaultIo = TRUE;
283 DefaultMmio = TRUE;
284
285 //
286 // Init ReservationRequest, and point FirstResource one past the last
287 // descriptor entry. We're going to build the entries backwards from
288 // ReservationRequest.EndDesc.
289 //
290 InitializeResourcePadding (&ReservationRequest);
291 FirstResource = ReservationRequest.Padding +
292 ARRAY_SIZE (ReservationRequest.Padding);
293
294 //
295 // (b) Reserve IO space.
296 //
297 if (DefaultIo) {
298 //
299 // Request defaults.
300 //
301 --FirstResource;
302 FirstResource->ResType = ACPI_ADDRESS_SPACE_TYPE_IO;
303 FirstResource->AddrRangeMax = 512 - 1; // align at 512 IO ports
304 FirstResource->AddrLen = 512; // 512 IO ports
305 }
306
307 //
308 // (c) Reserve non-prefetchable MMIO space (32-bit only).
309 //
310 if (DefaultMmio) {
311 //
312 // Request defaults.
313 //
314 --FirstResource;
315 FirstResource->ResType = ACPI_ADDRESS_SPACE_TYPE_MEM;
316 FirstResource->SpecificFlag = 0; // non-prefetchable
317 FirstResource->AddrSpaceGranularity = 32; // 32-bit aperture
318 FirstResource->AddrRangeMax = SIZE_2MB - 1; // align at 2MB
319 FirstResource->AddrLen = SIZE_2MB; // 2MB padding
320 }
321
322 //
323 // Output a copy of ReservationRequest from the lowest-address populated
324 // entry until the end of the structure (including
325 // ReservationRequest.EndDesc). If no reservations are necessary, we'll only
326 // output the End Tag.
327 //
328 *Padding = AllocateCopyPool (
329 (UINT8 *)(&ReservationRequest + 1) - (UINT8 *)FirstResource,
330 FirstResource
331 );
332 if (*Padding == NULL) {
333 return EFI_OUT_OF_RESOURCES;
334 }
335
336 //
337 // Resource padding is required.
338 //
339 *HpcState = EFI_HPC_STATE_INITIALIZED | EFI_HPC_STATE_ENABLED;
340
341 //
342 // The padding should be applied at PCI bus level, and considered by upstream
343 // bridges, recursively.
344 //
345 *Attributes = EfiPaddingPciBus;
346 return EFI_SUCCESS;
347 }
348
349
350 /**
351 Entry point for this driver.
352
353 @param[in] ImageHandle Image handle of this driver.
354 @param[in] SystemTable Pointer to SystemTable.
355
356 @retval EFI_SUCESS Driver has loaded successfully.
357 @return Error codes from lower level functions.
358
359 **/
360 EFI_STATUS
361 EFIAPI
362 DriverInitialize (
363 IN EFI_HANDLE ImageHandle,
364 IN EFI_SYSTEM_TABLE *SystemTable
365 )
366 {
367 EFI_STATUS Status;
368
369 mPciHotPlugInit.GetRootHpcList = GetRootHpcList;
370 mPciHotPlugInit.InitializeRootHpc = InitializeRootHpc;
371 mPciHotPlugInit.GetResourcePadding = GetResourcePadding;
372 Status = gBS->InstallMultipleProtocolInterfaces (&ImageHandle,
373 &gEfiPciHotPlugInitProtocolGuid, &mPciHotPlugInit, NULL);
374 return Status;
375 }