]> git.proxmox.com Git - mirror_edk2.git/blob - ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe/ArmJunoDxe.c
ArmPlatformPkg/ArmJunoPkg/Drivers/ArmJunoDxe: Fixed crash on Juno R0
[mirror_edk2.git] / ArmPlatformPkg / ArmJunoPkg / Drivers / ArmJunoDxe / ArmJunoDxe.c
1 /** @file
2 *
3 * Copyright (c) 2013-2015, ARM Limited. All rights reserved.
4 *
5 * This program and the accompanying materials
6 * are licensed and made available under the terms and conditions of the BSD License
7 * which accompanies this distribution. The full text of the license may be found at
8 * http://opensource.org/licenses/bsd-license.php
9 *
10 * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12 *
13 **/
14
15 #include "ArmJunoDxeInternal.h"
16 #include <ArmPlatform.h>
17
18 #include <IndustryStandard/Pci.h>
19 #include <Protocol/DevicePathFromText.h>
20 #include <Protocol/PciIo.h>
21 #include <Protocol/PciRootBridgeIo.h>
22
23 #include <Guid/EventGroup.h>
24 #include <Guid/GlobalVariable.h>
25
26 #include <Library/ArmShellCmdLib.h>
27 #include <Library/AcpiLib.h>
28 #include <Library/BaseMemoryLib.h>
29 #include <Library/DevicePathLib.h>
30 #include <Library/MemoryAllocationLib.h>
31 #include <Library/UefiRuntimeServicesTableLib.h>
32 #include <Library/IoLib.h>
33 #include <Library/PrintLib.h>
34
35
36 // This GUID must match the FILE_GUID in ArmPlatformPkg/ArmJunoPkg/AcpiTables/AcpiTables.inf
37 STATIC CONST EFI_GUID mJunoAcpiTableFile = { 0xa1dd808e, 0x1e95, 0x4399, { 0xab, 0xc0, 0x65, 0x3c, 0x82, 0xe8, 0x53, 0x0c } };
38
39 typedef struct {
40 ACPI_HID_DEVICE_PATH AcpiDevicePath;
41 PCI_DEVICE_PATH PciDevicePath;
42 EFI_DEVICE_PATH_PROTOCOL EndDevicePath;
43 } EFI_PCI_ROOT_BRIDGE_DEVICE_PATH;
44
45 STATIC CONST EFI_PCI_ROOT_BRIDGE_DEVICE_PATH mPciRootComplexDevicePath = {
46 {
47 { ACPI_DEVICE_PATH,
48 ACPI_DP,
49 { (UINT8) (sizeof (ACPI_HID_DEVICE_PATH)),
50 (UINT8) ((sizeof (ACPI_HID_DEVICE_PATH)) >> 8) }
51 },
52 EISA_PNP_ID (0x0A03),
53 0
54 },
55 {
56 { HARDWARE_DEVICE_PATH,
57 HW_PCI_DP,
58 { (UINT8) (sizeof (PCI_DEVICE_PATH)),
59 (UINT8) ((sizeof (PCI_DEVICE_PATH)) >> 8) }
60 },
61 0,
62 0
63 },
64 {
65 END_DEVICE_PATH_TYPE,
66 END_ENTIRE_DEVICE_PATH_SUBTYPE,
67 { END_DEVICE_PATH_LENGTH, 0 }
68 }
69 };
70
71 EFI_EVENT mAcpiRegistration = NULL;
72
73 /**
74 This function reads PCI ID of the controller.
75
76 @param[in] PciIo PCI IO protocol handle
77 @param[in] PciId Looking for specified PCI ID Vendor/Device
78 **/
79 STATIC
80 EFI_STATUS
81 ReadMarvellYoukonPciId (
82 IN EFI_PCI_IO_PROTOCOL *PciIo,
83 IN UINT32 PciId
84 )
85 {
86 UINT32 DevicePciId;
87 EFI_STATUS Status;
88
89 Status = PciIo->Pci.Read (
90 PciIo,
91 EfiPciIoWidthUint32,
92 PCI_VENDOR_ID_OFFSET,
93 1,
94 &DevicePciId);
95 if (EFI_ERROR (Status)) {
96 return Status;
97 }
98
99 if (DevicePciId != PciId) {
100 return EFI_NOT_FOUND;
101 }
102
103 return EFI_SUCCESS;
104 }
105
106 /**
107 This function searches for Marvell Yukon NIC on the Juno
108 platform and returns PCI IO protocol handle for the controller.
109
110 @param[out] PciIo PCI IO protocol handle
111 **/
112 STATIC
113 EFI_STATUS
114 GetMarvellYukonPciIoProtocol (
115 OUT EFI_PCI_IO_PROTOCOL **PciIo
116 )
117 {
118 UINTN HandleCount;
119 EFI_HANDLE *HandleBuffer;
120 UINTN HIndex;
121 EFI_STATUS Status;
122
123 Status = gBS->LocateHandleBuffer (
124 ByProtocol,
125 &gEfiPciIoProtocolGuid,
126 NULL,
127 &HandleCount,
128 &HandleBuffer);
129 if (EFI_ERROR (Status)) {
130 return (Status);
131 }
132
133 for (HIndex = 0; HIndex < HandleCount; ++HIndex) {
134 // If PciIo opened with EFI_OPEN_PROTOCOL_GET_PROTOCOL, the CloseProtocol() is not required
135 Status = gBS->OpenProtocol (
136 HandleBuffer[HIndex],
137 &gEfiPciIoProtocolGuid,
138 (VOID **) PciIo,
139 NULL,
140 NULL,
141 EFI_OPEN_PROTOCOL_GET_PROTOCOL);
142 if (EFI_ERROR (Status)) {
143 continue;
144 }
145
146 Status = ReadMarvellYoukonPciId (*PciIo, JUNO_MARVELL_YUKON_ID);
147 if (EFI_ERROR (Status)) {
148 continue;
149 } else {
150 break;
151 }
152 }
153
154 gBS->FreePool (HandleBuffer);
155
156 return Status;
157 }
158
159 /**
160 This function restore the original controller attributes
161
162 @param[in] PciIo PCI IO protocol handle
163 @param[in] PciAttr PCI controller attributes.
164 @param[in] AcpiResDescriptor ACPI 2.0 resource descriptors for the BAR
165 **/
166 STATIC
167 VOID
168 RestorePciDev (
169 IN EFI_PCI_IO_PROTOCOL *PciIo,
170 IN UINT64 PciAttr
171 )
172 {
173 PciIo->Attributes (
174 PciIo,
175 EfiPciIoAttributeOperationSet,
176 PciAttr,
177 NULL
178 );
179 }
180
181 /**
182 This function returns PCI MMIO base address for a controller
183
184 @param[in] PciIo PCI IO protocol handle
185 @param[out] PciRegBase PCI base MMIO address
186 **/
187 STATIC
188 EFI_STATUS
189 BarIsDeviceMemory (
190 IN EFI_PCI_IO_PROTOCOL *PciIo,
191 OUT UINT32 *PciRegBase
192 )
193 {
194 EFI_STATUS Status;
195 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AcpiResDescriptor;
196 EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AcpiCurrentDescriptor;
197
198 // Marvell Yukon's Bar0 provides base memory address for control registers
199 Status = PciIo->GetBarAttributes (PciIo, PCI_BAR_IDX0, NULL, (VOID**)&AcpiResDescriptor);
200 if (EFI_ERROR (Status)) {
201 return Status;
202 }
203
204 AcpiCurrentDescriptor = AcpiResDescriptor;
205
206 // Search for a memory type descriptor
207 while (AcpiCurrentDescriptor->Desc != ACPI_END_TAG_DESCRIPTOR) {
208
209 // Check if Bar is memory type one and fetch a base address
210 if (AcpiCurrentDescriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR &&
211 AcpiCurrentDescriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM &&
212 !(AcpiCurrentDescriptor->SpecificFlag & ACPI_SPECFLAG_PREFETCHABLE)) {
213 *PciRegBase = AcpiCurrentDescriptor->AddrRangeMin;
214 break;
215 } else {
216 Status = EFI_UNSUPPORTED;
217 }
218
219 AcpiCurrentDescriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) (AcpiCurrentDescriptor + 1);
220 }
221
222 gBS->FreePool (AcpiResDescriptor);
223
224 return Status;
225 }
226
227 /**
228 This function provides PCI MMIO base address, old PCI controller attributes.
229
230 @param[in] PciIo PCI IO protocol handle
231 @param[out] PciRegBase PCI base MMIO address
232 @param[out] OldPciAttr Old PCI controller attributes.
233 **/
234 STATIC
235 EFI_STATUS
236 InitPciDev (
237 IN EFI_PCI_IO_PROTOCOL *PciIo,
238 OUT UINT32 *PciRegBase,
239 OUT UINT64 *OldPciAttr
240 )
241 {
242 UINT64 AttrSupports;
243 EFI_STATUS Status;
244
245 // Get controller's current attributes
246 Status = PciIo->Attributes (
247 PciIo,
248 EfiPciIoAttributeOperationGet,
249 0,
250 OldPciAttr);
251 if (EFI_ERROR (Status)) {
252 return Status;
253 }
254
255 // Fetch supported attributes
256 Status = PciIo->Attributes (
257 PciIo,
258 EfiPciIoAttributeOperationSupported,
259 0,
260 &AttrSupports);
261 if (EFI_ERROR (Status)) {
262 return Status;
263 }
264
265 // Enable EFI_PCI_IO_ATTRIBUTE_IO, EFI_PCI_IO_ATTRIBUTE_MEMORY and
266 // EFI_PCI_IO_ATTRIBUTE_BUS_MASTER bits in the PCI Config Header
267 AttrSupports &= EFI_PCI_DEVICE_ENABLE;
268 Status = PciIo->Attributes (
269 PciIo,
270 EfiPciIoAttributeOperationEnable,
271 AttrSupports,
272 NULL);
273 if (EFI_ERROR (Status)) {
274 return Status;
275 }
276
277 Status = BarIsDeviceMemory (PciIo, PciRegBase);
278 if (EFI_ERROR (Status)) {
279 RestorePciDev (PciIo, *OldPciAttr);
280 }
281
282 return Status;
283 }
284
285 /**
286 This function reads MAC address from IOFPGA and writes it to Marvell Yukon NIC
287
288 @param[in] PciRegBase PCI base MMIO address
289 **/
290 STATIC
291 EFI_STATUS
292 WriteMacAddress (
293 IN UINT32 PciRegBase
294 )
295 {
296 UINT32 MacHigh;
297 UINT32 MacLow;
298
299 // Read MAC address from IOFPGA
300 MacHigh= MmioRead32 (ARM_JUNO_SYS_PCIGBE_H);
301 MacLow = MmioRead32 (ARM_JUNO_SYS_PCIGBE_L);
302
303 // Set software reset control register to protect from deactivation
304 // the config write state
305 MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_CLR);
306
307 // Convert to Marvell MAC Address register format
308 MacHigh = SwapBytes32 ((MacHigh & 0xFFFF) << 16 |
309 (MacLow & 0xFFFF0000) >> 16);
310 MacLow = SwapBytes32 (MacLow) >> 16;
311
312 // Set MAC Address
313 MmioWrite8 (PciRegBase + R_TST_CTRL_1, TST_CFG_WRITE_ENABLE);
314 MmioWrite32 (PciRegBase + R_MAC, MacHigh);
315 MmioWrite32 (PciRegBase + R_MAC_MAINT, MacHigh);
316 MmioWrite32 (PciRegBase + R_MAC + R_MAC_LOW, MacLow);
317 MmioWrite32 (PciRegBase + R_MAC_MAINT + R_MAC_LOW, MacLow);
318 MmioWrite8 (PciRegBase + R_TST_CTRL_1, TST_CFG_WRITE_DISABLE);
319
320 // Initiate device reset
321 MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_SET);
322 MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_CLR);
323
324 return EFI_SUCCESS;
325 }
326
327 /**
328 The function reads MAC address from Juno IOFPGA registers and writes it
329 into Marvell Yukon NIC.
330 **/
331 STATIC
332 EFI_STATUS
333 ArmJunoSetNicMacAddress ()
334 {
335 UINT64 OldPciAttr;
336 EFI_PCI_IO_PROTOCOL* PciIo;
337 UINT32 PciRegBase;
338 EFI_STATUS Status;
339
340 Status = GetMarvellYukonPciIoProtocol (&PciIo);
341 if (EFI_ERROR (Status)) {
342 return Status;
343 }
344
345 Status = InitPciDev (PciIo, &PciRegBase, &OldPciAttr);
346 if (EFI_ERROR (Status)) {
347 return Status;
348 }
349
350 Status = WriteMacAddress (PciRegBase);
351
352 RestorePciDev (PciIo, OldPciAttr);
353
354 return EFI_SUCCESS;
355 }
356
357 /**
358 Notification function of the event defined as belonging to the
359 EFI_END_OF_DXE_EVENT_GROUP_GUID event group that was created in
360 the entry point of the driver.
361
362 This function is called when an event belonging to the
363 EFI_END_OF_DXE_EVENT_GROUP_GUID event group is signalled. Such an
364 event is signalled once at the end of the dispatching of all
365 drivers (end of the so called DXE phase).
366
367 @param[in] Event Event declared in the entry point of the driver whose
368 notification function is being invoked.
369 @param[in] Context NULL
370 **/
371 STATIC
372 VOID
373 OnEndOfDxe (
374 IN EFI_EVENT Event,
375 IN VOID *Context
376 )
377 {
378 EFI_DEVICE_PATH_PROTOCOL* PciRootComplexDevicePath;
379 EFI_HANDLE Handle;
380 EFI_STATUS Status;
381 UINT32 JunoRevision;
382
383 //
384 // PCI Root Complex initialization
385 // At the end of the DXE phase, we should get all the driver dispatched.
386 // Force the PCI Root Complex to be initialized. It allows the OS to skip
387 // this step.
388 //
389 PciRootComplexDevicePath = (EFI_DEVICE_PATH_PROTOCOL*) &mPciRootComplexDevicePath;
390 Status = gBS->LocateDevicePath (&gEfiPciRootBridgeIoProtocolGuid,
391 &PciRootComplexDevicePath,
392 &Handle);
393
394 Status = gBS->ConnectController (Handle, NULL, PciRootComplexDevicePath, FALSE);
395 ASSERT_EFI_ERROR (Status);
396
397 GetJunoRevision (JunoRevision);
398
399 if (JunoRevision != JUNO_REVISION_R0) {
400 Status = ArmJunoSetNicMacAddress ();
401 if (EFI_ERROR (Status)) {
402 DEBUG ((DEBUG_ERROR, "ArmJunoDxe: Failed to set Marvell Yukon NIC MAC address\n"));
403 }
404 }
405 }
406
407 STATIC
408 BOOLEAN
409 AcpiTableJunoR0Check (
410 IN EFI_ACPI_DESCRIPTION_HEADER *AcpiHeader
411 )
412 {
413 return TRUE;
414 }
415
416 STATIC
417 BOOLEAN
418 AcpiTableJunoR1Check (
419 IN EFI_ACPI_DESCRIPTION_HEADER *AcpiHeader
420 )
421 {
422 return TRUE;
423 }
424
425 STATIC
426 BOOLEAN
427 AcpiTableJunoR2Check (
428 IN EFI_ACPI_DESCRIPTION_HEADER *AcpiHeader
429 )
430 {
431 return TRUE;
432 }
433
434
435 EFI_STATUS
436 EFIAPI
437 ArmJunoEntryPoint (
438 IN EFI_HANDLE ImageHandle,
439 IN EFI_SYSTEM_TABLE *SystemTable
440 )
441 {
442 EFI_STATUS Status;
443 EFI_PHYSICAL_ADDRESS HypBase;
444 CHAR16 *TextDevicePath;
445 UINTN TextDevicePathSize;
446 VOID *Buffer;
447 UINT32 JunoRevision;
448 EFI_EVENT EndOfDxeEvent;
449
450 Status = PciEmulationEntryPoint ();
451 if (EFI_ERROR (Status)) {
452 return Status;
453 }
454
455 //
456 // If a hypervisor has been declared then we need to make sure its region is protected at runtime
457 //
458 // Note: This code is only a workaround for our dummy hypervisor (ArmPkg/Extra/AArch64ToAArch32Shim/)
459 // that does not set up (yet) the stage 2 translation table to hide its own memory to EL1.
460 //
461 if (FixedPcdGet32 (PcdHypFvSize) != 0) {
462 // Ensure the hypervisor region is strictly contained into a EFI_PAGE_SIZE-aligned region.
463 // The memory must be a multiple of EFI_PAGE_SIZE to ensure we do not reserve more memory than the hypervisor itself.
464 // A UEFI Runtime region size granularity cannot be smaller than EFI_PAGE_SIZE. If the hypervisor size is not rounded
465 // to this size then there is a risk some non-runtime memory could be visible to the OS view.
466 if (((FixedPcdGet32 (PcdHypFvSize) & EFI_PAGE_MASK) == 0) && ((FixedPcdGet32 (PcdHypFvBaseAddress) & EFI_PAGE_MASK) == 0)) {
467 // The memory needs to be declared because the DXE core marked it as reserved and removed it from the memory space
468 // as it contains the Firmware.
469 Status = gDS->AddMemorySpace (
470 EfiGcdMemoryTypeSystemMemory,
471 FixedPcdGet32 (PcdHypFvBaseAddress), FixedPcdGet32 (PcdHypFvSize),
472 EFI_MEMORY_WB | EFI_MEMORY_RUNTIME
473 );
474 if (!EFI_ERROR (Status)) {
475 // We allocate the memory to ensure it is marked as runtime memory
476 HypBase = FixedPcdGet32 (PcdHypFvBaseAddress);
477 Status = gBS->AllocatePages (AllocateAddress, EfiRuntimeServicesCode,
478 EFI_SIZE_TO_PAGES (FixedPcdGet32 (PcdHypFvSize)), &HypBase);
479 }
480 } else {
481 // The hypervisor must be contained into a EFI_PAGE_SIZE-aligned region and its size must also be aligned
482 // on a EFI_PAGE_SIZE boundary (ie: 4KB).
483 Status = EFI_UNSUPPORTED;
484 ASSERT_EFI_ERROR (Status);
485 }
486
487 if (EFI_ERROR (Status)) {
488 return Status;
489 }
490 }
491
492 //
493 // Create an event belonging to the "gEfiEndOfDxeEventGroupGuid" group.
494 // The "OnEndOfDxe()" function is declared as the call back function.
495 // It will be called at the end of the DXE phase when an event of the
496 // same group is signalled to inform about the end of the DXE phase.
497 // Install the INSTALL_FDT_PROTOCOL protocol.
498 //
499 Status = gBS->CreateEventEx (
500 EVT_NOTIFY_SIGNAL,
501 TPL_CALLBACK,
502 OnEndOfDxe,
503 NULL,
504 &gEfiEndOfDxeEventGroupGuid,
505 &EndOfDxeEvent
506 );
507
508 // Install dynamic Shell command to run baremetal binaries.
509 Status = ShellDynCmdRunAxfInstall (ImageHandle);
510 if (EFI_ERROR (Status)) {
511 DEBUG ((EFI_D_ERROR, "ArmJunoDxe: Failed to install ShellDynCmdRunAxf\n"));
512 }
513
514 GetJunoRevision(JunoRevision);
515
516 //
517 // Try to install the ACPI Tables
518 //
519 if (JunoRevision == JUNO_REVISION_R0) {
520 Status = LocateAndInstallAcpiFromFvConditional (&mJunoAcpiTableFile, AcpiTableJunoR0Check);
521 } else if (JunoRevision == JUNO_REVISION_R1) {
522 Status = LocateAndInstallAcpiFromFvConditional (&mJunoAcpiTableFile, AcpiTableJunoR1Check);
523 } else if (JunoRevision == JUNO_REVISION_R2) {
524 Status = LocateAndInstallAcpiFromFvConditional (&mJunoAcpiTableFile, AcpiTableJunoR2Check);
525 }
526
527 ASSERT_EFI_ERROR (Status);
528
529 //
530 // Setup R1/R2 options if not already done.
531 //
532 if (JunoRevision != JUNO_REVISION_R0) {
533 // Enable PCI enumeration
534 PcdSetBool (PcdPciDisableBusEnumeration, FALSE);
535
536 // Declare the related ACPI Tables
537 EfiCreateProtocolNotifyEvent (
538 &gEfiAcpiTableProtocolGuid,
539 TPL_CALLBACK,
540 AcpiPciNotificationEvent,
541 NULL,
542 &mAcpiRegistration
543 );
544 }
545
546 //
547 // Set up the device path to the FDT.
548 //
549 TextDevicePath = (CHAR16*)FixedPcdGetPtr (PcdJunoFdtDevicePath);
550 if (TextDevicePath != NULL) {
551 TextDevicePathSize = StrSize (TextDevicePath);
552 Buffer = PcdSetPtr (PcdFdtDevicePaths, &TextDevicePathSize, TextDevicePath);
553 Status = (Buffer != NULL) ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL;
554 } else {
555 Status = EFI_NOT_FOUND;
556 }
557
558 if (EFI_ERROR (Status)) {
559 DEBUG (
560 (EFI_D_ERROR,
561 "ArmJunoDxe: Setting of FDT device path in PcdFdtDevicePaths failed - %r\n", Status)
562 );
563 return Status;
564 }
565
566 return Status;
567 }