/** @file * * Copyright (c) 2013-2015, ARM Limited. All rights reserved. * * This program and the accompanying materials * are licensed and made available under the terms and conditions of the BSD License * which accompanies this distribution. The full text of the license may be found at * http://opensource.org/licenses/bsd-license.php * * THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, * WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. * **/ #include "ArmJunoDxeInternal.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // This GUID must match the FILE_GUID in ArmPlatformPkg/ArmJunoPkg/AcpiTables/AcpiTables.inf STATIC CONST EFI_GUID mJunoAcpiTableFile = { 0xa1dd808e, 0x1e95, 0x4399, { 0xab, 0xc0, 0x65, 0x3c, 0x82, 0xe8, 0x53, 0x0c } }; typedef struct { ACPI_HID_DEVICE_PATH AcpiDevicePath; PCI_DEVICE_PATH PciDevicePath; EFI_DEVICE_PATH_PROTOCOL EndDevicePath; } EFI_PCI_ROOT_BRIDGE_DEVICE_PATH; STATIC CONST EFI_PCI_ROOT_BRIDGE_DEVICE_PATH mPciRootComplexDevicePath = { { { ACPI_DEVICE_PATH, ACPI_DP, { (UINT8) (sizeof (ACPI_HID_DEVICE_PATH)), (UINT8) ((sizeof (ACPI_HID_DEVICE_PATH)) >> 8) } }, EISA_PNP_ID (0x0A03), 0 }, { { HARDWARE_DEVICE_PATH, HW_PCI_DP, { (UINT8) (sizeof (PCI_DEVICE_PATH)), (UINT8) ((sizeof (PCI_DEVICE_PATH)) >> 8) } }, 0, 0 }, { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { END_DEVICE_PATH_LENGTH, 0 } } }; EFI_EVENT mAcpiRegistration = NULL; /** This function reads PCI ID of the controller. @param[in] PciIo PCI IO protocol handle @param[in] PciId Looking for specified PCI ID Vendor/Device **/ STATIC EFI_STATUS ReadMarvellYoukonPciId ( IN EFI_PCI_IO_PROTOCOL *PciIo, IN UINT32 PciId ) { UINT32 DevicePciId; EFI_STATUS Status; Status = PciIo->Pci.Read ( PciIo, EfiPciIoWidthUint32, PCI_VENDOR_ID_OFFSET, 1, &DevicePciId); if (EFI_ERROR (Status)) { return Status; } if (DevicePciId != PciId) { return EFI_NOT_FOUND; } return EFI_SUCCESS; } /** This function searches for Marvell Yukon NIC on the Juno platform and returns PCI IO protocol handle for the controller. @param[out] PciIo PCI IO protocol handle **/ STATIC EFI_STATUS GetMarvellYukonPciIoProtocol ( OUT EFI_PCI_IO_PROTOCOL **PciIo ) { UINTN HandleCount; EFI_HANDLE *HandleBuffer; UINTN HIndex; EFI_STATUS Status; Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiPciIoProtocolGuid, NULL, &HandleCount, &HandleBuffer); if (EFI_ERROR (Status)) { return (Status); } for (HIndex = 0; HIndex < HandleCount; ++HIndex) { // If PciIo opened with EFI_OPEN_PROTOCOL_GET_PROTOCOL, the CloseProtocol() is not required Status = gBS->OpenProtocol ( HandleBuffer[HIndex], &gEfiPciIoProtocolGuid, (VOID **) PciIo, NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL); if (EFI_ERROR (Status)) { continue; } Status = ReadMarvellYoukonPciId (*PciIo, JUNO_MARVELL_YUKON_ID); if (EFI_ERROR (Status)) { continue; } else { break; } } gBS->FreePool (HandleBuffer); return Status; } /** This function restore the original controller attributes @param[in] PciIo PCI IO protocol handle @param[in] PciAttr PCI controller attributes. @param[in] AcpiResDescriptor ACPI 2.0 resource descriptors for the BAR **/ STATIC VOID RestorePciDev ( IN EFI_PCI_IO_PROTOCOL *PciIo, IN UINT64 PciAttr ) { PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSet, PciAttr, NULL ); } /** This function returns PCI MMIO base address for a controller @param[in] PciIo PCI IO protocol handle @param[out] PciRegBase PCI base MMIO address **/ STATIC EFI_STATUS BarIsDeviceMemory ( IN EFI_PCI_IO_PROTOCOL *PciIo, OUT UINT32 *PciRegBase ) { EFI_STATUS Status; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AcpiResDescriptor; EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *AcpiCurrentDescriptor; // Marvell Yukon's Bar0 provides base memory address for control registers Status = PciIo->GetBarAttributes (PciIo, PCI_BAR_IDX0, NULL, (VOID**)&AcpiResDescriptor); if (EFI_ERROR (Status)) { return Status; } AcpiCurrentDescriptor = AcpiResDescriptor; // Search for a memory type descriptor while (AcpiCurrentDescriptor->Desc != ACPI_END_TAG_DESCRIPTOR) { // Check if Bar is memory type one and fetch a base address if (AcpiCurrentDescriptor->Desc == ACPI_ADDRESS_SPACE_DESCRIPTOR && AcpiCurrentDescriptor->ResType == ACPI_ADDRESS_SPACE_TYPE_MEM && !(AcpiCurrentDescriptor->SpecificFlag & ACPI_SPECFLAG_PREFETCHABLE)) { *PciRegBase = AcpiCurrentDescriptor->AddrRangeMin; break; } else { Status = EFI_UNSUPPORTED; } AcpiCurrentDescriptor = (EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *) (AcpiCurrentDescriptor + 1); } gBS->FreePool (AcpiResDescriptor); return Status; } /** This function provides PCI MMIO base address, old PCI controller attributes. @param[in] PciIo PCI IO protocol handle @param[out] PciRegBase PCI base MMIO address @param[out] OldPciAttr Old PCI controller attributes. **/ STATIC EFI_STATUS InitPciDev ( IN EFI_PCI_IO_PROTOCOL *PciIo, OUT UINT32 *PciRegBase, OUT UINT64 *OldPciAttr ) { UINT64 AttrSupports; EFI_STATUS Status; // Get controller's current attributes Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationGet, 0, OldPciAttr); if (EFI_ERROR (Status)) { return Status; } // Fetch supported attributes Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationSupported, 0, &AttrSupports); if (EFI_ERROR (Status)) { return Status; } // Enable EFI_PCI_IO_ATTRIBUTE_IO, EFI_PCI_IO_ATTRIBUTE_MEMORY and // EFI_PCI_IO_ATTRIBUTE_BUS_MASTER bits in the PCI Config Header AttrSupports &= EFI_PCI_DEVICE_ENABLE; Status = PciIo->Attributes ( PciIo, EfiPciIoAttributeOperationEnable, AttrSupports, NULL); if (EFI_ERROR (Status)) { return Status; } Status = BarIsDeviceMemory (PciIo, PciRegBase); if (EFI_ERROR (Status)) { RestorePciDev (PciIo, *OldPciAttr); } return Status; } /** This function reads MAC address from IOFPGA and writes it to Marvell Yukon NIC @param[in] PciRegBase PCI base MMIO address **/ STATIC EFI_STATUS WriteMacAddress ( IN UINT32 PciRegBase ) { UINT32 MacHigh; UINT32 MacLow; // Read MAC address from IOFPGA MacHigh= MmioRead32 (ARM_JUNO_SYS_PCIGBE_H); MacLow = MmioRead32 (ARM_JUNO_SYS_PCIGBE_L); // Set software reset control register to protect from deactivation // the config write state MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_CLR); // Convert to Marvell MAC Address register format MacHigh = SwapBytes32 ((MacHigh & 0xFFFF) << 16 | (MacLow & 0xFFFF0000) >> 16); MacLow = SwapBytes32 (MacLow) >> 16; // Set MAC Address MmioWrite8 (PciRegBase + R_TST_CTRL_1, TST_CFG_WRITE_ENABLE); MmioWrite32 (PciRegBase + R_MAC, MacHigh); MmioWrite32 (PciRegBase + R_MAC_MAINT, MacHigh); MmioWrite32 (PciRegBase + R_MAC + R_MAC_LOW, MacLow); MmioWrite32 (PciRegBase + R_MAC_MAINT + R_MAC_LOW, MacLow); MmioWrite8 (PciRegBase + R_TST_CTRL_1, TST_CFG_WRITE_DISABLE); // Initiate device reset MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_SET); MmioWrite16 (PciRegBase + R_CONTROL_STATUS, CS_RESET_CLR); return EFI_SUCCESS; } /** The function reads MAC address from Juno IOFPGA registers and writes it into Marvell Yukon NIC. **/ STATIC EFI_STATUS ArmJunoSetNicMacAddress () { UINT64 OldPciAttr; EFI_PCI_IO_PROTOCOL* PciIo; UINT32 PciRegBase; EFI_STATUS Status; Status = GetMarvellYukonPciIoProtocol (&PciIo); if (EFI_ERROR (Status)) { return Status; } PciRegBase = 0; Status = InitPciDev (PciIo, &PciRegBase, &OldPciAttr); if (EFI_ERROR (Status)) { return Status; } Status = WriteMacAddress (PciRegBase); RestorePciDev (PciIo, OldPciAttr); return EFI_SUCCESS; } /** Notification function of the event defined as belonging to the EFI_END_OF_DXE_EVENT_GROUP_GUID event group that was created in the entry point of the driver. This function is called when an event belonging to the EFI_END_OF_DXE_EVENT_GROUP_GUID event group is signalled. Such an event is signalled once at the end of the dispatching of all drivers (end of the so called DXE phase). @param[in] Event Event declared in the entry point of the driver whose notification function is being invoked. @param[in] Context NULL **/ STATIC VOID OnEndOfDxe ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_DEVICE_PATH_PROTOCOL* PciRootComplexDevicePath; EFI_HANDLE Handle; EFI_STATUS Status; // // PCI Root Complex initialization // At the end of the DXE phase, we should get all the driver dispatched. // Force the PCI Root Complex to be initialized. It allows the OS to skip // this step. // PciRootComplexDevicePath = (EFI_DEVICE_PATH_PROTOCOL*) &mPciRootComplexDevicePath; Status = gBS->LocateDevicePath (&gEfiPciRootBridgeIoProtocolGuid, &PciRootComplexDevicePath, &Handle); Status = gBS->ConnectController (Handle, NULL, PciRootComplexDevicePath, FALSE); ASSERT_EFI_ERROR (Status); Status = ArmJunoSetNicMacAddress (); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_ERROR, "ArmJunoDxe: Failed to set Marvell Yukon NIC MAC address\n")); } } EFI_STATUS EFIAPI ArmJunoEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; EFI_PHYSICAL_ADDRESS HypBase; CHAR16 *TextDevicePath; UINTN TextDevicePathSize; VOID *Buffer; UINT32 JunoRevision; EFI_EVENT EndOfDxeEvent; // // Register the OHCI and EHCI controllers as non-coherent // non-discoverable devices. // Status = RegisterNonDiscoverableMmioDevice ( NonDiscoverableDeviceTypeOhci, NonDiscoverableDeviceDmaTypeNonCoherent, NULL, NULL, 1, FixedPcdGet32 (PcdSynopsysUsbOhciBaseAddress), SIZE_64KB ); ASSERT_EFI_ERROR (Status); Status = RegisterNonDiscoverableMmioDevice ( NonDiscoverableDeviceTypeEhci, NonDiscoverableDeviceDmaTypeNonCoherent, NULL, NULL, 1, FixedPcdGet32 (PcdSynopsysUsbEhciBaseAddress), SIZE_64KB ); ASSERT_EFI_ERROR (Status); // // If a hypervisor has been declared then we need to make sure its region is protected at runtime // // Note: This code is only a workaround for our dummy hypervisor (ArmPkg/Extra/AArch64ToAArch32Shim/) // that does not set up (yet) the stage 2 translation table to hide its own memory to EL1. // if (FixedPcdGet32 (PcdHypFvSize) != 0) { // Ensure the hypervisor region is strictly contained into a EFI_PAGE_SIZE-aligned region. // The memory must be a multiple of EFI_PAGE_SIZE to ensure we do not reserve more memory than the hypervisor itself. // A UEFI Runtime region size granularity cannot be smaller than EFI_PAGE_SIZE. If the hypervisor size is not rounded // to this size then there is a risk some non-runtime memory could be visible to the OS view. if (((FixedPcdGet32 (PcdHypFvSize) & EFI_PAGE_MASK) == 0) && ((FixedPcdGet32 (PcdHypFvBaseAddress) & EFI_PAGE_MASK) == 0)) { // The memory needs to be declared because the DXE core marked it as reserved and removed it from the memory space // as it contains the Firmware. Status = gDS->AddMemorySpace ( EfiGcdMemoryTypeSystemMemory, FixedPcdGet32 (PcdHypFvBaseAddress), FixedPcdGet32 (PcdHypFvSize), EFI_MEMORY_WB | EFI_MEMORY_RUNTIME ); if (!EFI_ERROR (Status)) { // We allocate the memory to ensure it is marked as runtime memory HypBase = FixedPcdGet32 (PcdHypFvBaseAddress); Status = gBS->AllocatePages (AllocateAddress, EfiRuntimeServicesCode, EFI_SIZE_TO_PAGES (FixedPcdGet32 (PcdHypFvSize)), &HypBase); } } else { // The hypervisor must be contained into a EFI_PAGE_SIZE-aligned region and its size must also be aligned // on a EFI_PAGE_SIZE boundary (ie: 4KB). Status = EFI_UNSUPPORTED; ASSERT_EFI_ERROR (Status); } if (EFI_ERROR (Status)) { return Status; } } // Install dynamic Shell command to run baremetal binaries. Status = ShellDynCmdRunAxfInstall (ImageHandle); if (EFI_ERROR (Status)) { DEBUG ((EFI_D_ERROR, "ArmJunoDxe: Failed to install ShellDynCmdRunAxf\n")); } GetJunoRevision(JunoRevision); // // Try to install the ACPI Tables // Status = LocateAndInstallAcpiFromFv (&mJunoAcpiTableFile); ASSERT_EFI_ERROR (Status); // // Setup R1/R2 options if not already done. // if (JunoRevision != JUNO_REVISION_R0) { // Enable PCI enumeration PcdSetBool (PcdPciDisableBusEnumeration, FALSE); // // Create an event belonging to the "gEfiEndOfDxeEventGroupGuid" group. // The "OnEndOfDxe()" function is declared as the call back function. // It will be called at the end of the DXE phase when an event of the // same group is signalled to inform about the end of the DXE phase. // Install the INSTALL_FDT_PROTOCOL protocol. // Status = gBS->CreateEventEx ( EVT_NOTIFY_SIGNAL, TPL_CALLBACK, OnEndOfDxe, NULL, &gEfiEndOfDxeEventGroupGuid, &EndOfDxeEvent ); // Declare the related ACPI Tables EfiCreateProtocolNotifyEvent ( &gEfiAcpiTableProtocolGuid, TPL_CALLBACK, AcpiPciNotificationEvent, NULL, &mAcpiRegistration ); } // // Set up the device path to the FDT. // TextDevicePath = (CHAR16*)FixedPcdGetPtr (PcdJunoFdtDevicePath); if (TextDevicePath != NULL) { TextDevicePathSize = StrSize (TextDevicePath); Buffer = PcdSetPtr (PcdFdtDevicePaths, &TextDevicePathSize, TextDevicePath); Status = (Buffer != NULL) ? EFI_SUCCESS : EFI_BUFFER_TOO_SMALL; } else { Status = EFI_NOT_FOUND; } if (EFI_ERROR (Status)) { DEBUG ( (EFI_D_ERROR, "ArmJunoDxe: Setting of FDT device path in PcdFdtDevicePaths failed - %r\n", Status) ); return Status; } return Status; }