diff --git a/ArmPkg/ArmPkg.ci.yaml b/ArmPkg/ArmPkg.ci.yaml new file mode 100644 index 000000000..24db74250 --- /dev/null +++ b/ArmPkg/ArmPkg.ci.yaml @@ -0,0 +1,243 @@ +## @file +# CI configuration for ArmPkg +# +# Copyright (c) 2021, Arm Limited. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +## +{ + ## options defined .pytool/Plugin/LicenseCheck + "LicenseCheck": { + "IgnoreFiles": [] + }, + + "EccCheck": { + ## Exception sample looks like below: + ## "ExceptionList": [ + ## "", "" + ## ] + "ExceptionList": [ + ], + ## Both file path and directory path are accepted. + "IgnoreFiles": [ + "Library/ArmSoftFloatLib/berkeley-softfloat-3", + "Library/ArmSoftFloatLib/ArmSoftFloatLib.c", + "Library/CompilerIntrinsicsLib", + "Universal/Smbios/SmbiosMiscDxe" + ] + }, + + ## options defined .pytool/Plugin/CompilerPlugin + "CompilerPlugin": { + "DscPath": "ArmPkg.dsc" + }, + + ## options defined .pytool/Plugin/HostUnitTestCompilerPlugin + "HostUnitTestCompilerPlugin": { + "DscPath": "" # Don't support this test + }, + + ## options defined .pytool/Plugin/CharEncodingCheck + "CharEncodingCheck": { + "IgnoreFiles": [] + }, + + ## options defined .pytool/Plugin/DependencyCheck + "DependencyCheck": { + "AcceptableDependencies": [ + "ArmPlatformPkg/ArmPlatformPkg.dec", + "ArmPkg/ArmPkg.dec", + "EmbeddedPkg/EmbeddedPkg.dec", + "MdeModulePkg/MdeModulePkg.dec", + "MdePkg/MdePkg.dec", + "ShellPkg/ShellPkg.dec" + ], + # For host based unit tests + "AcceptableDependencies-HOST_APPLICATION":[ + "UnitTestFrameworkPkg/UnitTestFrameworkPkg.dec" + ], + # For UEFI shell based apps + "AcceptableDependencies-UEFI_APPLICATION":[], + "IgnoreInf": [] + }, + + ## options defined .pytool/Plugin/DscCompleteCheck + "DscCompleteCheck": { + "IgnoreInf": [], + "DscPath": "ArmPkg.dsc" + }, + + ## options defined .pytool/Plugin/HostUnitTestDscCompleteCheck + "HostUnitTestDscCompleteCheck": { + "IgnoreInf": [""], + "DscPath": "" # Don't support this test + }, + + ## options defined .pytool/Plugin/GuidCheck + "GuidCheck": { + "IgnoreGuidName": [], + "IgnoreGuidValue": [], + "IgnoreFoldersAndFiles": [], + "IgnoreDuplicates": [], + }, + + ## options defined .pytool/Plugin/LibraryClassCheck + "LibraryClassCheck": { + "IgnoreHeaderFile": [] + }, + + ## options defined .pytool/Plugin/SpellCheck + "SpellCheck": { + "AuditOnly": True, + "IgnoreFiles": [ + "Library/ArmSoftFloatLib/berkeley-softfloat-3/**" + ], # use gitignore syntax to ignore errors + # in matching files + "ExtendWords": [ + "api's", + "ackintid", + "actlr", + "aeabi", + "asedis", + "ashldi", + "ashrdi", + "baddr", + "ccidx", + "ccsidr", + "clidr", + "clrex", + "clzsi", + "cnthctl", + "cortexa", + "cpacr", + "cpuactlr", + "csselr", + "ctzsi", + "cygdrive", + "cygpaths", + "datas", + "dcmpeq", + "dcmpge", + "dcmpgt", + "dcmple", + "dcmplt", + "ddisable", + "divdi", + "divsi", + "dmdepkg", + "dpref", + "drsub", + "fcmpeq", + "fcmpge", + "fcmpgt", + "fcmple", + "fcmplt", + "ffreestanding", + "frsub", + "hisilicon", + "iccabpr", + "iccbpr", + "icciar", + "iccicr", + "icciidr", + "iccpir", + "iccpmr", + "iccrpr", + "icdabr", + "icdicer", + "icdicfr", + "icdicpr", + "icdictr", + "icdiidr", + "icdiser", + "icdisr", + "icdppisr", + "icdsgir", + "icdspr", + "icenabler", + "intid", + "ipriority", + "irouter", + "isenabler", + "istatus", + "itargets", + "lable", + "ldivmod", + "ldmdb", + "ldmia", + "ldrbt", + "ldrex", + "ldrexb", + "ldrexd", + "ldrexh", + "ldrhbt", + "ldrht", + "ldrsb", + "ldrsbt", + "ldrsh", + "lshrdi", + "moddi", + "modsi", + "mpcore", + "mpidr", + "muldi", + "mullu", + "nonshareable", + "nsacr", + "nsasedis", + "nuvia", + "oldit", + "pcten", + "plpis", + "procno", + "readc", + "revsh", + "rfedb", + "sctlr", + "smccc", + "smlabb", + "smlabt", + "smlad", + "smladx", + "smlatb", + "smlatt", + "smlawb", + "smlawt", + "smlsd", + "smlsdx", + "smmla", + "smmlar", + "smmls", + "smmlsr", + "sourcery", + "srsdb", + "ssacr", + "stmdb", + "stmia", + "strbt", + "strexb", + "strexd", + "strexh", + "strht", + "switchu", + "tpidrurw", + "ttbcr", + "typer", + "ucmpdi", + "udivdi", + "udivmoddi", + "udivsi", + "uefi's", + "uldiv", + "umoddi", + "umodsi", + "usada", + "vlpis", + "writec" + ], # words to extend to the dictionary for this package + "IgnoreStandardPaths": [ # Standard Plugin defined paths that + "*.asm", "*.s" # should be ignore + ], + "AdditionalIncludePaths": [] # Additional paths to spell check + # (wildcards supported) + } +} diff --git a/ArmPkg/ArmPkg.dec b/ArmPkg/ArmPkg.dec new file mode 100644 index 000000000..f17ba913e --- /dev/null +++ b/ArmPkg/ArmPkg.dec @@ -0,0 +1,401 @@ +#/** @file +# ARM processor package. +# +# Copyright (c) 2009 - 2010, Apple Inc. All rights reserved.
+# Copyright (c) 2011 - 2022, ARM Limited. All rights reserved. +# Copyright (c) 2021, Ampere Computing LLC. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + DEC_SPECIFICATION = 0x00010005 + PACKAGE_NAME = ArmPkg + PACKAGE_GUID = 5CFBD99E-3C43-4E7F-8054-9CDEAFF7710F + PACKAGE_VERSION = 0.1 + +################################################################################ +# +# Include Section - list of Include Paths that are provided by this package. +# Comments are used for Keywords and Module Types. +# +# Supported Module Types: +# BASE SEC PEI_CORE PEIM DXE_CORE DXE_DRIVER DXE_RUNTIME_DRIVER DXE_SMM_DRIVER DXE_SAL_DRIVER UEFI_DRIVER UEFI_APPLICATION +# +################################################################################ +[Includes.common] + Include # Root include for the package + +[LibraryClasses.common] + ## @libraryclass Convert Arm instructions to a human readable format. + # + ArmDisassemblerLib|Include/Library/ArmDisassemblerLib.h + + ## @libraryclass Provides an interface to Arm generic counters. + # + ArmGenericTimerCounterLib|Include/Library/ArmGenericTimerCounterLib.h + + ## @libraryclass Provides an interface to initialize a + # Generic Interrupt Controller (GIC). + # + ArmGicArchLib|Include/Library/ArmGicArchLib.h + + ## @libraryclass Provides a Generic Interrupt Controller (GIC) + # configuration interface. + # + ArmGicLib|Include/Library/ArmGicLib.h + + ## @libraryclass Provides a HyperVisor Call (HVC) interface. + # + ArmHvcLib|Include/Library/ArmHvcLib.h + + ## @libraryclass Provides an interface to Arm registers. + # + ArmLib|Include/Library/ArmLib.h + + ## @libraryclass Provides a Mmu interface. + # + ArmMmuLib|Include/Library/ArmMmuLib.h + + ## @libraryclass Provides a Mailbox Transport Layer (MTL) interface + # for the System Control and Management Interface (SCMI). + # + ArmMtlLib|Include/Library/ArmMtlLib.h + + ## @libraryclass Provides a System Monitor Call (SMC) interface. + # + ArmSmcLib|Include/Library/ArmSmcLib.h + + ## @libraryclass Provides a SuperVisor Call (SVC) interface. + # + ArmSvcLib|Include/Library/ArmSvcLib.h + + ## @libraryclass Provides a Monitor Call interface that will use the + # default conduit (HVC or SMC). + # + ArmMonitorLib|Include/Library/ArmMonitorLib.h + + ## @libraryclass Provides a default exception handler. + # + DefaultExceptionHandlerLib|Include/Library/DefaultExceptionHandlerLib.h + + ## @libraryclass Provides an interface to query miscellaneous OEM + # information. + # + OemMiscLib|Include/Library/OemMiscLib.h + + ## @libraryclass Provides an OpTee interface. + # + OpteeLib|Include/Library/OpteeLib.h + + ## @libraryclass Provides a semihosting interface. + # + SemihostLib|Include/Library/SemihostLib.h + + ## @libraryclass Provides an interface for a StandaloneMm Mmu. + # + StandaloneMmMmuLib|Include/Library/StandaloneMmMmuLib.h + +[Guids.common] + gArmTokenSpaceGuid = { 0xBB11ECFE, 0x820F, 0x4968, { 0xBB, 0xA6, 0xF7, 0x6A, 0xFE, 0x30, 0x25, 0x96 } } + + ## ARM MPCore table + # Include/Guid/ArmMpCoreInfo.h + gArmMpCoreInfoGuid = { 0xa4ee0728, 0xe5d7, 0x4ac5, {0xb2, 0x1e, 0x65, 0x8e, 0xd8, 0x57, 0xe8, 0x34} } + + gArmMmuReplaceLiveTranslationEntryFuncGuid = { 0xa8b50ff3, 0x08ec, 0x4dd3, {0xbf, 0x04, 0x28, 0xbf, 0x71, 0x75, 0xc7, 0x4a} } + +[Protocols.common] + ## Arm System Control and Management Interface(SCMI) Base protocol + ## ArmPkg/Include/Protocol/ArmScmiBaseProtocol.h + gArmScmiBaseProtocolGuid = { 0xd7e5abe9, 0x33ab, 0x418e, { 0x9f, 0x91, 0x72, 0xda, 0xe2, 0xba, 0x8e, 0x2f } } + + ## Arm System Control and Management Interface(SCMI) Clock management protocol + ## ArmPkg/Include/Protocol/ArmScmiClockProtocol.h + gArmScmiClockProtocolGuid = { 0x91ce67a8, 0xe0aa, 0x4012, { 0xb9, 0x9f, 0xb6, 0xfc, 0xf3, 0x4, 0x8e, 0xaa } } + gArmScmiClock2ProtocolGuid = { 0xb8d8caf2, 0x9e94, 0x462c, { 0xa8, 0x34, 0x6c, 0x99, 0xfc, 0x05, 0xef, 0xcf } } + + ## Arm System Control and Management Interface(SCMI) Clock management protocol + ## ArmPkg/Include/Protocol/ArmScmiPerformanceProtocol.h + gArmScmiPerformanceProtocolGuid = { 0x9b8ba84, 0x3dd3, 0x49a6, { 0xa0, 0x5a, 0x31, 0x34, 0xa5, 0xf0, 0x7b, 0xad } } + +[Ppis] + ## Include/Ppi/ArmMpCoreInfo.h + gArmMpCoreInfoPpiGuid = { 0x6847cc74, 0xe9ec, 0x4f8f, {0xa2, 0x9d, 0xab, 0x44, 0xe7, 0x54, 0xa8, 0xfc} } + +[PcdsFeatureFlag.common] + gArmTokenSpaceGuid.PcdCpuDxeProduceDebugSupport|FALSE|BOOLEAN|0x00000001 + + # On ARM Architecture with the Security Extension, the address for the + # Vector Table can be mapped anywhere in the memory map. It means we can + # point the Exception Vector Table to its location in CpuDxe. + # By default we copy the Vector Table at PcdGet64(PcdCpuVectorBaseAddress) + gArmTokenSpaceGuid.PcdRelocateVectorTable|TRUE|BOOLEAN|0x00000022 + # Set this PCD to TRUE if the Exception Vector is changed to add debugger support before + # it has been configured by the CPU DXE + gArmTokenSpaceGuid.PcdDebuggerExceptionSupport|FALSE|BOOLEAN|0x00000032 + + # Define if the GICv3 controller should use the GICv2 legacy + gArmTokenSpaceGuid.PcdArmGicV3WithV2Legacy|FALSE|BOOLEAN|0x00000042 + + ## Define the conduit to use for monitor calls. + # Default PcdMonitorConduitHvc = FALSE, conduit = SMC + # If PcdMonitorConduitHvc = TRUE, conduit = HVC + gArmTokenSpaceGuid.PcdMonitorConduitHvc|FALSE|BOOLEAN|0x00000047 + +[PcdsFeatureFlag.ARM] + # Whether to map normal memory as non-shareable. FALSE is the safe choice, but + # TRUE may be appropriate to fix performance problems if you don't care about + # hardware coherency (i.e., no virtualization or cache coherent DMA) + gArmTokenSpaceGuid.PcdNormalMemoryNonshareableOverride|FALSE|BOOLEAN|0x00000043 + +[PcdsFeatureFlag.AARCH64, PcdsFeatureFlag.ARM] + ## Used to select method for requesting services from S-EL1.

+ # TRUE - Selects FF-A calls for communication between S-EL0 and SPMC.
+ # FALSE - Selects SVC calls for communication between S-EL0 and SPMC.
+ # @Prompt Enable FF-A support. + gArmTokenSpaceGuid.PcdFfaEnable|FALSE|BOOLEAN|0x0000005B + +[PcdsFixedAtBuild.common] + gArmTokenSpaceGuid.PcdTrustzoneSupport|FALSE|BOOLEAN|0x00000006 + + # This PCD should be a FeaturePcd. But we used this PCD as an '#if' in an ASM file. + # Using a FeaturePcd make a '(BOOLEAN) casting for its value which is not understood by the preprocessor. + gArmTokenSpaceGuid.PcdVFPEnabled|0|UINT32|0x00000024 + + gArmTokenSpaceGuid.PcdCpuVectorBaseAddress|0xffff0000|UINT64|0x00000004 + gArmTokenSpaceGuid.PcdCpuResetAddress|0x00000000|UINT32|0x00000005 + + # + # ARM Secure Firmware PCDs + # + gArmTokenSpaceGuid.PcdSecureFdBaseAddress|0|UINT64|0x00000015 + gArmTokenSpaceGuid.PcdSecureFdSize|0|UINT32|0x00000016 + gArmTokenSpaceGuid.PcdSecureFvBaseAddress|0x0|UINT64|0x0000002F + gArmTokenSpaceGuid.PcdSecureFvSize|0x0|UINT32|0x00000030 + + # + # ARM Hypervisor Firmware PCDs + # + gArmTokenSpaceGuid.PcdHypFdBaseAddress|0|UINT32|0x0000003A + gArmTokenSpaceGuid.PcdHypFdSize|0|UINT32|0x0000003B + gArmTokenSpaceGuid.PcdHypFvBaseAddress|0|UINT32|0x0000003C + gArmTokenSpaceGuid.PcdHypFvSize|0|UINT32|0x0000003D + + # Use ClusterId + CoreId to identify the PrimaryCore + gArmTokenSpaceGuid.PcdArmPrimaryCoreMask|0xF03|UINT32|0x00000031 + # The Primary Core is ClusterId[0] & CoreId[0] + gArmTokenSpaceGuid.PcdArmPrimaryCore|0|UINT32|0x00000037 + + # + # SMBIOS PCDs + # + gArmTokenSpaceGuid.PcdSystemProductName|L""|VOID*|0x30000053 + gArmTokenSpaceGuid.PcdSystemVersion|L""|VOID*|0x30000054 + gArmTokenSpaceGuid.PcdBaseBoardManufacturer|L""|VOID*|0x30000055 + gArmTokenSpaceGuid.PcdBaseBoardProductName|L""|VOID*|0x30000056 + gArmTokenSpaceGuid.PcdBaseBoardVersion|L""|VOID*|0x30000057 + gArmTokenSpaceGuid.PcdProcessorManufacturer|L""|VOID*|0x30000071 + gArmTokenSpaceGuid.PcdProcessorVersion|L""|VOID*|0x30000072 + gArmTokenSpaceGuid.PcdProcessorSerialNumber|L""|VOID*|0x30000073 + gArmTokenSpaceGuid.PcdProcessorAssetTag|L""|VOID*|0x30000074 + gArmTokenSpaceGuid.PcdProcessorPartNumber|L""|VOID*|0x30000075 + + # + # ARM L2x0 PCDs + # + gArmTokenSpaceGuid.PcdL2x0ControllerBase|0|UINT32|0x0000001B + + # + # ARM Normal (or Non Secure) Firmware PCDs + # + gArmTokenSpaceGuid.PcdFdSize|0|UINT32|0x0000002C + gArmTokenSpaceGuid.PcdFvSize|0|UINT32|0x0000002E + + # + # Value to add to a host address to obtain a device address, using + # unsigned 64-bit integer arithmetic on both ARM and AArch64. This + # means we can rely on truncation on overflow to specify negative + # offsets. + # + gArmTokenSpaceGuid.PcdArmDmaDeviceOffset|0x0|UINT64|0x0000044 + +[PcdsFixedAtBuild.common, PcdsPatchableInModule.common] + gArmTokenSpaceGuid.PcdFdBaseAddress|0|UINT64|0x0000002B + gArmTokenSpaceGuid.PcdFvBaseAddress|0|UINT64|0x0000002D + +[PcdsFixedAtBuild.ARM] + # + # ARM Security Extension + # + + # Secure Configuration Register + # - BIT0 : NS - Non Secure bit + # - BIT1 : IRQ Handler + # - BIT2 : FIQ Handler + # - BIT3 : EA - External Abort + # - BIT4 : FW - F bit writable + # - BIT5 : AW - A bit writable + # - BIT6 : nET - Not Early Termination + # - BIT7 : SCD - Secure Monitor Call Disable + # - BIT8 : HCE - Hyp Call enable + # - BIT9 : SIF - Secure Instruction Fetch + # 0x31 = NS | EA | FW + gArmTokenSpaceGuid.PcdArmScr|0x31|UINT32|0x00000038 + + # By default we do not do a transition to non-secure mode + gArmTokenSpaceGuid.PcdArmNonSecModeTransition|0x0|UINT32|0x0000003E + + # Non Secure Access Control Register + # - BIT15 : NSASEDIS - Disable Non-secure Advanced SIMD functionality + # - BIT14 : NSD32DIS - Disable Non-secure use of D16-D31 + # - BIT11 : cp11 - Non-secure access to coprocessor 11 enable + # - BIT10 : cp10 - Non-secure access to coprocessor 10 enable + # 0xC00 = cp10 | cp11 + gArmTokenSpaceGuid.PcdArmNsacr|0xC00|UINT32|0x00000039 + +[PcdsFixedAtBuild.AARCH64] + # + # AArch64 Security Extension + # + + # Secure Configuration Register + # - BIT0 : NS - Non Secure bit + # - BIT1 : IRQ Handler + # - BIT2 : FIQ Handler + # - BIT3 : EA - External Abort + # - BIT4 : FW - F bit writable + # - BIT5 : AW - A bit writable + # - BIT6 : nET - Not Early Termination + # - BIT7 : SCD - Secure Monitor Call Disable + # - BIT8 : HCE - Hyp Call enable + # - BIT9 : SIF - Secure Instruction Fetch + # - BIT10: RW - Register width control for lower exception levels + # - BIT11: SIF - Enables Secure EL1 access to EL1 Architectural Timer + # - BIT12: TWI - Trap WFI + # - BIT13: TWE - Trap WFE + # 0x501 = NS | HCE | RW + gArmTokenSpaceGuid.PcdArmScr|0x501|UINT32|0x00000038 + + # By default we do transition to EL2 non-secure mode with Stack for EL2. + # Mode Description Bits + # NS EL2 SP2 all interrupts disabled = 0x3c9 + # NS EL1 SP1 all interrupts disabled = 0x3c5 + # Other modes include using SP0 or switching to Aarch32, but these are + # not currently supported. + gArmTokenSpaceGuid.PcdArmNonSecModeTransition|0x3c9|UINT32|0x0000003E + + +# +# These PCDs are also defined as 'PcdsDynamic' or 'PcdsPatchableInModule' to be +# redefined when using UEFI in a context of virtual machine. +# +[PcdsFixedAtBuild.common, PcdsDynamic.common, PcdsPatchableInModule.common] + + # System Memory (DRAM): These PCDs define the region of in-built system memory + # Some platforms can get DRAM extensions, these additional regions may be + # declared to UEFI using separate resource descriptor HOBs + gArmTokenSpaceGuid.PcdSystemMemoryBase|0|UINT64|0x00000029 + gArmTokenSpaceGuid.PcdSystemMemorySize|0|UINT64|0x0000002A + + gArmTokenSpaceGuid.PcdMmBufferBase|0|UINT64|0x00000045 + gArmTokenSpaceGuid.PcdMmBufferSize|0|UINT64|0x00000046 + + gArmTokenSpaceGuid.PcdSystemBiosRelease|0xFFFF|UINT16|0x30000058 + gArmTokenSpaceGuid.PcdEmbeddedControllerFirmwareRelease|0xFFFF|UINT16|0x30000059 + +[PcdsFixedAtBuild.common, PcdsDynamic.common] + # + # ARM Architectural Timer + # + gArmTokenSpaceGuid.PcdArmArchTimerFreqInHz|0|UINT32|0x00000034 + + # ARM Architectural Timer Interrupt(GIC PPI) numbers + gArmTokenSpaceGuid.PcdArmArchTimerSecIntrNum|29|UINT32|0x00000035 + gArmTokenSpaceGuid.PcdArmArchTimerIntrNum|30|UINT32|0x00000036 + gArmTokenSpaceGuid.PcdArmArchTimerHypIntrNum|26|UINT32|0x00000040 + gArmTokenSpaceGuid.PcdArmArchTimerVirtIntrNum|27|UINT32|0x00000041 + + # + # ARM Generic Watchdog + # + + gArmTokenSpaceGuid.PcdGenericWatchdogControlBase|0x2A440000|UINT64|0x00000007 + gArmTokenSpaceGuid.PcdGenericWatchdogRefreshBase|0x2A450000|UINT64|0x00000008 + gArmTokenSpaceGuid.PcdGenericWatchdogEl2IntrNum|93|UINT32|0x00000009 + + # + # ARM Generic Interrupt Controller + # + gArmTokenSpaceGuid.PcdGicDistributorBase|0|UINT64|0x0000000C + # Base address for the GIC Redistributor region that contains the boot CPU + gArmTokenSpaceGuid.PcdGicRedistributorsBase|0|UINT64|0x0000000E + gArmTokenSpaceGuid.PcdGicInterruptInterfaceBase|0|UINT64|0x0000000D + gArmTokenSpaceGuid.PcdGicSgiIntId|0|UINT32|0x00000025 + + # + # Bases, sizes and translation offsets of IO and MMIO spaces, respectively. + # Note that "IO" is just another MMIO range that simulates IO space; there + # are no special instructions to access it. + # + # The base addresses PcdPciIoBase, PcdPciMmio32Base and PcdPciMmio64Base are + # specific to their containing address spaces. In order to get the physical + # address for the CPU, for a given access, the respective translation value + # has to be added. + # + # The translations always have to be initialized like this, using UINT64: + # + # UINT64 IoCpuBase; // mapping target in 64-bit cpu-physical space + # UINT64 Mmio32CpuBase; // mapping target in 64-bit cpu-physical space + # UINT64 Mmio64CpuBase; // mapping target in 64-bit cpu-physical space + # + # gEfiMdePkgTokenSpaceGuid.PcdPciIoTranslation = IoCpuBase - PcdPciIoBase; + # gEfiMdePkgTokenSpaceGuid.PcdPciMmio32Translation = Mmio32CpuBase - (UINT64)PcdPciMmio32Base; + # gEfiMdePkgTokenSpaceGuid.PcdPciMmio64Translation = Mmio64CpuBase - PcdPciMmio64Base; + # + # because (a) the target address space (ie. the cpu-physical space) is + # 64-bit, and (b) the translation values are meant as offsets for *modular* + # arithmetic. + # + # Accordingly, the translation itself needs to be implemented as: + # + # UINT64 UntranslatedIoAddress; // input parameter + # UINT32 UntranslatedMmio32Address; // input parameter + # UINT64 UntranslatedMmio64Address; // input parameter + # + # UINT64 TranslatedIoAddress; // output parameter + # UINT64 TranslatedMmio32Address; // output parameter + # UINT64 TranslatedMmio64Address; // output parameter + # + # TranslatedIoAddress = UntranslatedIoAddress + + # gEfiMdePkgTokenSpaceGuid.PcdPciIoTranslation; + # TranslatedMmio32Address = (UINT64)UntranslatedMmio32Address + + # gEfiMdePkgTokenSpaceGuid.PcdPciMmio32Translation; + # TranslatedMmio64Address = UntranslatedMmio64Address + + # gEfiMdePkgTokenSpaceGuid.PcdPciMmio64Translation; + # + # The modular arithmetic performed in UINT64 ensures that the translation + # works correctly regardless of the relation between IoCpuBase and + # PcdPciIoBase, Mmio32CpuBase and PcdPciMmio32Base, and Mmio64CpuBase and + # PcdPciMmio64Base. + # + gArmTokenSpaceGuid.PcdPciIoBase|0x0|UINT64|0x00000050 + gArmTokenSpaceGuid.PcdPciIoSize|0x0|UINT64|0x00000051 + gArmTokenSpaceGuid.PcdPciMmio32Base|0x0|UINT32|0x00000053 + gArmTokenSpaceGuid.PcdPciMmio32Size|0x0|UINT32|0x00000054 + gArmTokenSpaceGuid.PcdPciMmio64Base|0x0|UINT64|0x00000056 + gArmTokenSpaceGuid.PcdPciMmio64Size|0x0|UINT64|0x00000057 + + # + # Inclusive range of allowed PCI buses. + # + gArmTokenSpaceGuid.PcdPciBusMin|0x0|UINT32|0x00000059 + gArmTokenSpaceGuid.PcdPciBusMax|0x0|UINT32|0x0000005A + +[PcdsDynamicEx] + # + # This dynamic PCD hold the GUID of a firmware FFS which contains + # the LinuxBoot payload. + # + gArmTokenSpaceGuid.PcdLinuxBootFileGuid|{0x0}|VOID*|0x0000005C diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc new file mode 100644 index 000000000..ac24ebce4 --- /dev/null +++ b/ArmPkg/ArmPkg.dsc @@ -0,0 +1,171 @@ +#/** @file +# ARM processor package. +# +# Copyright (c) 2009 - 2010, Apple Inc. All rights reserved.
+# Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
+# Copyright (c) 2016, Linaro Ltd. All rights reserved.
+# Copyright (c) Microsoft Corporation.
+# Copyright (c) 2021, Ampere Computing LLC. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +################################################################################ +# +# Defines Section - statements that will be processed to create a Makefile. +# +################################################################################ +[Defines] + PLATFORM_NAME = ArmPkg + PLATFORM_GUID = 5CFBD99E-3C43-4E7F-8054-9CDEAFF7710F + PLATFORM_VERSION = 0.1 + DSC_SPECIFICATION = 0x00010005 + OUTPUT_DIRECTORY = Build/Arm + SUPPORTED_ARCHITECTURES = ARM|AARCH64 + BUILD_TARGETS = DEBUG|RELEASE|NOOPT + SKUID_IDENTIFIER = DEFAULT + +[BuildOptions] + RELEASE_*_*_CC_FLAGS = -DMDEPKG_NDEBUG + *_*_*_CC_FLAGS = -DDISABLE_NEW_DEPRECATED_INTERFACES + +[PcdsFixedAtBuild] + gEfiMdePkgTokenSpaceGuid.PcdDefaultTerminalType|4 + +!include MdePkg/MdeLibs.dsc.inc + +[LibraryClasses.common] + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + BootLogoLib|MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf + CacheMaintenanceLib|ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.inf + CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf + DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf + DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf + HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf + UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf + PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf + PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf + + UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf + HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf + + SemihostLib|ArmPkg/Library/SemihostLib/SemihostLib.inf + DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf + DefaultExceptionHandlerLib|ArmPkg/Library/DefaultExceptionHandlerLib/DefaultExceptionHandlerLib.inf + CpuExceptionHandlerLib|ArmPkg/Library/ArmExceptionLib/ArmExceptionLib.inf + + CpuLib|MdePkg/Library/BaseCpuLib/BaseCpuLib.inf + ArmGicLib|ArmPkg/Drivers/ArmGic/ArmGicLib.inf + ArmGicArchLib|ArmPkg/Library/ArmGicArchLib/ArmGicArchLib.inf + ArmGenericTimerCounterLib|ArmPkg/Library/ArmGenericTimerPhyCounterLib/ArmGenericTimerPhyCounterLib.inf + ArmSmcLib|ArmPkg/Library/ArmSmcLib/ArmSmcLib.inf + ArmDisassemblerLib|ArmPkg/Library/ArmDisassemblerLib/ArmDisassemblerLib.inf + OpteeLib|ArmPkg/Library/OpteeLib/OpteeLib.inf + + UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf + PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf + SerialPortLib|MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf + + FdtLib|EmbeddedPkg/Library/FdtLib/FdtLib.inf + + ShellLib|ShellPkg/Library/UefiShellLib/UefiShellLib.inf + FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf + SortLib|MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf + + IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf + + ArmLib|ArmPkg/Library/ArmLib/ArmBaseLib.inf + ArmMmuLib|ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf + + ArmMtlLib|ArmPkg/Library/ArmMtlNullLib/ArmMtlNullLib.inf + + OemMiscLib|ArmPkg/Universal/Smbios/OemMiscLibNull/OemMiscLibNull.inf + +[LibraryClasses.common.PEIM] + HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf + PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf + MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf + PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf + PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf + +[LibraryClasses.ARM, LibraryClasses.AARCH64] + NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf + + # Add support for GCC stack protector + NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf + +[Components.common] + ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.inf + ArmPkg/Library/ArmDisassemblerLib/ArmDisassemblerLib.inf + ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf + ArmPkg/Library/DebugAgentSymbolsBaseLib/DebugAgentSymbolsBaseLib.inf + ArmPkg/Library/DebugPeCoffExtraActionLib/DebugPeCoffExtraActionLib.inf + ArmPkg/Library/DefaultExceptionHandlerLib/DefaultExceptionHandlerLib.inf + ArmPkg/Library/RvdPeCoffExtraActionLib/RvdPeCoffExtraActionLib.inf + ArmPkg/Library/SemiHostingDebugLib/SemiHostingDebugLib.inf + ArmPkg/Library/SemiHostingSerialPortLib/SemiHostingSerialPortLib.inf + ArmPkg/Library/SemihostLib/SemihostLib.inf + ArmPkg/Library/ArmPsciResetSystemLib/ArmPsciResetSystemLib.inf + ArmPkg/Library/ArmExceptionLib/ArmExceptionLib.inf + ArmPkg/Library/ArmExceptionLib/ArmRelocateExceptionLib.inf + + ArmPkg/Drivers/CpuDxe/CpuDxe.inf + ArmPkg/Drivers/CpuPei/CpuPei.inf + ArmPkg/Drivers/ArmGic/ArmGicDxe.inf + ArmPkg/Drivers/ArmGic/ArmGicLib.inf + ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf + ArmPkg/Drivers/TimerDxe/TimerDxe.inf + + ArmPkg/Library/ArmGenericTimerPhyCounterLib/ArmGenericTimerPhyCounterLib.inf + ArmPkg/Library/ArmGenericTimerVirtCounterLib/ArmGenericTimerVirtCounterLib.inf + + ArmPkg/Library/ArmTrngLib/ArmTrngLib.inf + ArmPkg/Library/ArmHvcLib/ArmHvcLib.inf + ArmPkg/Library/ArmHvcLibNull/ArmHvcLibNull.inf + ArmPkg/Library/ArmMonitorLib/ArmMonitorLib.inf + ArmPkg/Library/ArmSmcLib/ArmSmcLib.inf + ArmPkg/Library/ArmSmcLibNull/ArmSmcLibNull.inf + ArmPkg/Library/ArmSvcLib/ArmSvcLib.inf + ArmPkg/Library/OpteeLib/OpteeLib.inf + + ArmPkg/Filesystem/SemihostFs/SemihostFs.inf + + ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf + + ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf + ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.inf + ArmPkg/Library/ArmGicArchLib/ArmGicArchLib.inf + ArmPkg/Library/ArmGicArchSecLib/ArmGicArchSecLib.inf + ArmPkg/Library/ArmLib/ArmBaseLib.inf + ArmPkg/Library/ArmMtlNullLib/ArmMtlNullLib.inf + ArmPkg/Library/ArmSoftFloatLib/ArmSoftFloatLib.inf + ArmPkg/Library/ArmSmcPsciResetSystemLib/ArmSmcPsciResetSystemLib.inf + ArmPkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf + ArmPkg/Library/PlatformBootManagerLib/PlatformBootManagerLib.inf + ArmPkg/Library/LinuxBootBootManagerLib/LinuxBootBootManagerLib.inf + + ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.inf + ArmPkg/Drivers/ArmScmiDxe/ArmScmiDxe.inf + + ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClassDxe.inf + ArmPkg/Universal/Smbios/SmbiosMiscDxe/SmbiosMiscDxe.inf + ArmPkg/Universal/Smbios/OemMiscLibNull/OemMiscLibNull.inf + +[Components.AARCH64] + ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.inf + ArmPkg/Library/ArmMmuLib/ArmMmuPeiLib.inf + +[Components.AARCH64, Components.ARM] + ArmPkg/Library/StandaloneMmMmuLib/ArmMmuStandaloneMmLib.inf diff --git a/ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.c b/ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.c new file mode 100644 index 000000000..8bb815559 --- /dev/null +++ b/ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.c @@ -0,0 +1,34 @@ +/** @file + + Copyright (c) 2017, Linaro, Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include + +STATIC EFI_CPU_ARCH_PROTOCOL *mCpu; + +EFI_STATUS +EFIAPI +ArmCrashDumpDxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&mCpu); + ASSERT_EFI_ERROR (Status); + + return mCpu->RegisterInterruptHandler ( + mCpu, + EXCEPT_AARCH64_SYNCHRONOUS_EXCEPTIONS, + &DefaultExceptionHandler + ); +} diff --git a/ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.dsc b/ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.dsc new file mode 100644 index 000000000..2818ce65d --- /dev/null +++ b/ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.dsc @@ -0,0 +1,50 @@ +#/** @file +# +# Copyright (c) 2017, Linaro Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +################################################################################ +# +# Defines Section - statements that will be processed to create a Makefile. +# +################################################################################ +[Defines] + PLATFORM_NAME = ArmCrashDumpDxe + PLATFORM_GUID = 8dc3c2f8-988e-4e32-8fb7-0df43f6d0d8a + PLATFORM_VERSION = 0.1 + DSC_SPECIFICATION = 0x00010019 + OUTPUT_DIRECTORY = Build/ArmCrashDumpDxe + SUPPORTED_ARCHITECTURES = AARCH64 + BUILD_TARGETS = DEBUG + SKUID_IDENTIFIER = DEFAULT + +[PcdsFixedAtBuild] + gEfiMdePkgTokenSpaceGuid.PcdDebugPrintErrorLevel|0x8000004F + gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x27 + +[LibraryClasses] + ArmDisassemblerLib|ArmPkg/Library/ArmDisassemblerLib/ArmDisassemblerLib.inf + BaseLib|MdePkg/Library/BaseLib/BaseLib.inf + BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf + DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf + DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf + DefaultExceptionHandlerLib|ArmPkg/Library/DefaultExceptionHandlerLib/DefaultExceptionHandlerLib.inf + DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf + MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf + PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf + PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf + PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf + SerialPortLib|MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf + UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf + UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf + UefiLib|MdePkg/Library/UefiLib/UefiLib.inf + UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf + + NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf + NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf + +[Components.common] + ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.inf diff --git a/ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.inf b/ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.inf new file mode 100644 index 000000000..4dc92a1d6 --- /dev/null +++ b/ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.inf @@ -0,0 +1,34 @@ +#/** @file +# +# Copyright (c) 2017, Linaro, Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 0x00010018 + BASE_NAME = ArmCrashDumpDxe + FILE_GUID = 0bda00b0-05d6-4bb8-bfc7-058ad13615cf + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = ArmCrashDumpDxeInitialize + +[Sources] + ArmCrashDumpDxe.c + +[Packages] + ArmPkg/ArmPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + DebugLib + DefaultExceptionHandlerLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gEfiCpuArchProtocolGuid + +[Depex] + gEfiCpuArchProtocolGuid diff --git a/ArmPkg/Drivers/ArmGic/ArmGicCommonDxe.c b/ArmPkg/Drivers/ArmGic/ArmGicCommonDxe.c new file mode 100644 index 000000000..cd12fcda2 --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/ArmGicCommonDxe.c @@ -0,0 +1,215 @@ +/*++ + +Copyright (c) 2013-2017, ARM Ltd. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +--*/ + +#include "ArmGicDxe.h" + +VOID +EFIAPI +IrqInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ); + +VOID +EFIAPI +ExitBootServicesEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ); + +// Making this global saves a few bytes in image size +EFI_HANDLE gHardwareInterruptHandle = NULL; + +// Notifications +EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL; + +// Maximum Number of Interrupts +UINTN mGicNumInterrupts = 0; + +HARDWARE_INTERRUPT_HANDLER *gRegisteredInterruptHandlers = NULL; + +/** + Calculate GICD_ICFGRn base address and corresponding bit + field Int_config[1] of the GIC distributor register. + + @param Source Hardware source of the interrupt. + @param RegAddress Corresponding GICD_ICFGRn base address. + @param Config1Bit Bit number of F Int_config[1] bit in the register. + + @retval EFI_SUCCESS Source interrupt supported. + @retval EFI_UNSUPPORTED Source interrupt is not supported. +**/ +EFI_STATUS +GicGetDistributorIcfgBaseAndBit ( + IN HARDWARE_INTERRUPT_SOURCE Source, + OUT UINTN *RegAddress, + OUT UINTN *Config1Bit + ) +{ + UINTN RegIndex; + UINTN Field; + + if (Source >= mGicNumInterrupts) { + ASSERT (Source < mGicNumInterrupts); + return EFI_UNSUPPORTED; + } + + RegIndex = Source / ARM_GIC_ICDICFR_F_STRIDE; // NOTE: truncation is significant + Field = Source % ARM_GIC_ICDICFR_F_STRIDE; + *RegAddress = PcdGet64 (PcdGicDistributorBase) + + ARM_GIC_ICDICFR + + (ARM_GIC_ICDICFR_BYTES * RegIndex); + *Config1Bit = ((Field * ARM_GIC_ICDICFR_F_WIDTH) + + ARM_GIC_ICDICFR_F_CONFIG1_BIT); + + return EFI_SUCCESS; +} + +/** + Register Handler for the specified interrupt source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + @param Handler Callback for interrupt. NULL to unregister + + @retval EFI_SUCCESS Source was updated to support Handler. + @retval EFI_DEVICE_ERROR Hardware could not be programmed. + +**/ +EFI_STATUS +EFIAPI +RegisterInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN HARDWARE_INTERRUPT_HANDLER Handler + ) +{ + if (Source >= mGicNumInterrupts) { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + if ((Handler == NULL) && (gRegisteredInterruptHandlers[Source] == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((Handler != NULL) && (gRegisteredInterruptHandlers[Source] != NULL)) { + return EFI_ALREADY_STARTED; + } + + gRegisteredInterruptHandlers[Source] = Handler; + + // If the interrupt handler is unregistered then disable the interrupt + if (NULL == Handler) { + return This->DisableInterruptSource (This, Source); + } else { + return This->EnableInterruptSource (This, Source); + } +} + +STATIC VOID *mCpuArchProtocolNotifyEventRegistration; + +STATIC +VOID +EFIAPI +CpuArchEventProtocolNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_CPU_ARCH_PROTOCOL *Cpu; + EFI_STATUS Status; + + // Get the CPU protocol that this driver requires. + Status = gBS->LocateProtocol (&gEfiCpuArchProtocolGuid, NULL, (VOID **)&Cpu); + if (EFI_ERROR (Status)) { + return; + } + + // Unregister the default exception handler. + Status = Cpu->RegisterInterruptHandler (Cpu, ARM_ARCH_EXCEPTION_IRQ, NULL); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: Cpu->RegisterInterruptHandler() - %r\n", + __FUNCTION__, + Status + )); + return; + } + + // Register to receive interrupts + Status = Cpu->RegisterInterruptHandler ( + Cpu, + ARM_ARCH_EXCEPTION_IRQ, + Context + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "%a: Cpu->RegisterInterruptHandler() - %r\n", + __FUNCTION__, + Status + )); + } + + gBS->CloseEvent (Event); +} + +EFI_STATUS +InstallAndRegisterInterruptService ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *InterruptProtocol, + IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *Interrupt2Protocol, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler, + IN EFI_EVENT_NOTIFY ExitBootServicesEvent + ) +{ + EFI_STATUS Status; + CONST UINTN RihArraySize = + (sizeof (HARDWARE_INTERRUPT_HANDLER) * mGicNumInterrupts); + + // Initialize the array for the Interrupt Handlers + gRegisteredInterruptHandlers = AllocateZeroPool (RihArraySize); + if (gRegisteredInterruptHandlers == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &gHardwareInterruptHandle, + &gHardwareInterruptProtocolGuid, + InterruptProtocol, + &gHardwareInterrupt2ProtocolGuid, + Interrupt2Protocol, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Install the interrupt handler as soon as the CPU arch protocol appears. + // + EfiCreateProtocolNotifyEvent ( + &gEfiCpuArchProtocolGuid, + TPL_CALLBACK, + CpuArchEventProtocolNotify, + InterruptHandler, + &mCpuArchProtocolNotifyEventRegistration + ); + + // Register for an ExitBootServicesEvent + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, + TPL_NOTIFY, + ExitBootServicesEvent, + NULL, + &EfiExitBootServicesEvent + ); + + return Status; +} diff --git a/ArmPkg/Drivers/ArmGic/ArmGicDxe.c b/ArmPkg/Drivers/ArmGic/ArmGicDxe.c new file mode 100644 index 000000000..1b40d8f94 --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/ArmGicDxe.c @@ -0,0 +1,53 @@ +/*++ + +Copyright (c) 2013-2014, ARM Ltd. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +Module Name: + + ArmGicDxe.c + +Abstract: + + Driver implementing the GIC interrupt controller protocol + +--*/ + +#include + +#include "ArmGicDxe.h" + +/** + Initialize the state information for the CPU Architectural Protocol + + @param ImageHandle of the loaded driver + @param SystemTable Pointer to the System Table + + @retval EFI_SUCCESS Protocol registered + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure + @retval EFI_DEVICE_ERROR Hardware problems + @retval EFI_UNSUPPORTED GIC version not supported + +**/ +EFI_STATUS +InterruptDxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + ARM_GIC_ARCH_REVISION Revision; + + Revision = ArmGicGetSupportedArchRevision (); + + if (Revision == ARM_GIC_ARCH_REVISION_2) { + Status = GicV2DxeInitialize (ImageHandle, SystemTable); + } else if (Revision == ARM_GIC_ARCH_REVISION_3) { + Status = GicV3DxeInitialize (ImageHandle, SystemTable); + } else { + Status = EFI_UNSUPPORTED; + } + + return Status; +} diff --git a/ArmPkg/Drivers/ArmGic/ArmGicDxe.h b/ArmPkg/Drivers/ArmGic/ArmGicDxe.h new file mode 100644 index 000000000..0f621682a --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/ArmGicDxe.h @@ -0,0 +1,78 @@ +/*++ + +Copyright (c) 2013-2017, ARM Ltd. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +--*/ + +#ifndef ARM_GIC_DXE_H_ +#define ARM_GIC_DXE_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +extern UINTN mGicNumInterrupts; +extern HARDWARE_INTERRUPT_HANDLER *gRegisteredInterruptHandlers; + +// Common API +EFI_STATUS +InstallAndRegisterInterruptService ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *InterruptProtocol, + IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *Interrupt2Protocol, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler, + IN EFI_EVENT_NOTIFY ExitBootServicesEvent + ); + +EFI_STATUS +EFIAPI +RegisterInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN HARDWARE_INTERRUPT_HANDLER Handler + ); + +// GicV2 API +EFI_STATUS +GicV2DxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// GicV3 API +EFI_STATUS +GicV3DxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ); + +// Shared code + +/** + Calculate GICD_ICFGRn base address and corresponding bit + field Int_config[1] of the GIC distributor register. + + @param Source Hardware source of the interrupt. + @param RegAddress Corresponding GICD_ICFGRn base address. + @param Config1Bit Bit number of F Int_config[1] bit in the register. + + @retval EFI_SUCCESS Source interrupt supported. + @retval EFI_UNSUPPORTED Source interrupt is not supported. +**/ +EFI_STATUS +GicGetDistributorIcfgBaseAndBit ( + IN HARDWARE_INTERRUPT_SOURCE Source, + OUT UINTN *RegAddress, + OUT UINTN *Config1Bit + ); + +#endif // ARM_GIC_DXE_H_ diff --git a/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf b/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf new file mode 100644 index 000000000..cd2cec248 --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/ArmGicDxe.inf @@ -0,0 +1,57 @@ +#/** @file +# +# Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.
+# Copyright (c) 2012 - 2017, ARM Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArmGicDxe + FILE_GUID = DE371F7C-DEC4-4D21-ADF1-593ABCC15882 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = InterruptDxeInitialize + +[Sources.common] + ArmGicDxe.h + ArmGicDxe.c + ArmGicCommonDxe.c + + GicV2/ArmGicV2Dxe.c + GicV3/ArmGicV3Dxe.c + +[Packages] + MdePkg/MdePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + ArmPkg/ArmPkg.dec + +[LibraryClasses] + ArmGicLib + BaseLib + UefiLib + UefiBootServicesTableLib + DebugLib + PrintLib + MemoryAllocationLib + UefiDriverEntryPoint + IoLib + PcdLib + UefiLib + +[Protocols] + gHardwareInterruptProtocolGuid ## PRODUCES + gHardwareInterrupt2ProtocolGuid ## PRODUCES + gEfiCpuArchProtocolGuid ## CONSUMES ## NOTIFY + +[Pcd.common] + gArmTokenSpaceGuid.PcdGicDistributorBase + gArmTokenSpaceGuid.PcdGicRedistributorsBase + gArmTokenSpaceGuid.PcdGicInterruptInterfaceBase + gArmTokenSpaceGuid.PcdArmGicV3WithV2Legacy + +[Depex] + TRUE diff --git a/ArmPkg/Drivers/ArmGic/ArmGicLib.c b/ArmPkg/Drivers/ArmGic/ArmGicLib.c new file mode 100644 index 000000000..dd3670c7c --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/ArmGicLib.c @@ -0,0 +1,434 @@ +/** @file +* +* Copyright (c) 2011-2021, Arm Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include +#include +#include +#include +#include +#include + +// In GICv3, there are 2 x 64KB frames: +// Redistributor control frame + SGI Control & Generation frame +#define GIC_V3_REDISTRIBUTOR_GRANULARITY (ARM_GICR_CTLR_FRAME_SIZE \ + + ARM_GICR_SGI_PPI_FRAME_SIZE) + +// In GICv4, there are 2 additional 64KB frames: +// VLPI frame + Reserved page frame +#define GIC_V4_REDISTRIBUTOR_GRANULARITY (GIC_V3_REDISTRIBUTOR_GRANULARITY \ + + ARM_GICR_SGI_VLPI_FRAME_SIZE \ + + ARM_GICR_SGI_RESERVED_FRAME_SIZE) + +#define ISENABLER_ADDRESS(base, offset) ((base) +\ + ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ISENABLER + 4 * (offset)) + +#define ICENABLER_ADDRESS(base, offset) ((base) +\ + ARM_GICR_CTLR_FRAME_SIZE + ARM_GICR_ICENABLER + 4 * (offset)) + +#define IPRIORITY_ADDRESS(base, offset) ((base) +\ + ARM_GICR_CTLR_FRAME_SIZE + ARM_GIC_ICDIPR + 4 * (offset)) + +/** + * + * Return whether the Source interrupt index refers to a shared interrupt (SPI) + */ +STATIC +BOOLEAN +SourceIsSpi ( + IN UINTN Source + ) +{ + return Source >= 32 && Source < 1020; +} + +/** + * Return the base address of the GIC redistributor for the current CPU + * + * @param Revision GIC Revision. The GIC redistributor might have a different + * granularity following the GIC revision. + * + * @retval Base address of the associated GIC Redistributor + */ +STATIC +UINTN +GicGetCpuRedistributorBase ( + IN UINTN GicRedistributorBase, + IN ARM_GIC_ARCH_REVISION Revision + ) +{ + UINTN MpId; + UINTN CpuAffinity; + UINTN Affinity; + UINTN GicCpuRedistributorBase; + UINT64 TypeRegister; + + MpId = ArmReadMpidr (); + // Define CPU affinity as: + // Affinity0[0:8], Affinity1[9:15], Affinity2[16:23], Affinity3[24:32] + // whereas Affinity3 is defined at [32:39] in MPIDR + CpuAffinity = (MpId & (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2)) | + ((MpId & ARM_CORE_AFF3) >> 8); + + if (Revision < ARM_GIC_ARCH_REVISION_3) { + ASSERT_EFI_ERROR (EFI_UNSUPPORTED); + return 0; + } + + GicCpuRedistributorBase = GicRedistributorBase; + + do { + TypeRegister = MmioRead64 (GicCpuRedistributorBase + ARM_GICR_TYPER); + Affinity = ARM_GICR_TYPER_GET_AFFINITY (TypeRegister); + if (Affinity == CpuAffinity) { + return GicCpuRedistributorBase; + } + + // Move to the next GIC Redistributor frame. + // The GIC specification does not forbid a mixture of redistributors + // with or without support for virtual LPIs, so we test Virtual LPIs + // Support (VLPIS) bit for each frame to decide the granularity. + // Note: The assumption here is that the redistributors are adjacent + // for all CPUs. However this may not be the case for NUMA systems. + GicCpuRedistributorBase += (((ARM_GICR_TYPER_VLPIS & TypeRegister) != 0) + ? GIC_V4_REDISTRIBUTOR_GRANULARITY + : GIC_V3_REDISTRIBUTOR_GRANULARITY); + } while ((TypeRegister & ARM_GICR_TYPER_LAST) == 0); + + // The Redistributor has not been found for the current CPU + ASSERT_EFI_ERROR (EFI_NOT_FOUND); + return 0; +} + +UINTN +EFIAPI +ArmGicGetInterfaceIdentification ( + IN INTN GicInterruptInterfaceBase + ) +{ + // Read the GIC Identification Register + return MmioRead32 (GicInterruptInterfaceBase + ARM_GIC_ICCIIDR); +} + +UINTN +EFIAPI +ArmGicGetMaxNumInterrupts ( + IN INTN GicDistributorBase + ) +{ + UINTN ItLines; + + ItLines = MmioRead32 (GicDistributorBase + ARM_GIC_ICDICTR) & 0x1F; + + // + // Interrupt ID 1020-1023 are reserved. + // + return (ItLines == 0x1f) ? 1020 : 32 * (ItLines + 1); +} + +VOID +EFIAPI +ArmGicSendSgiTo ( + IN INTN GicDistributorBase, + IN INTN TargetListFilter, + IN INTN CPUTargetList, + IN INTN SgiId + ) +{ + MmioWrite32 ( + GicDistributorBase + ARM_GIC_ICDSGIR, + ((TargetListFilter & 0x3) << 24) | ((CPUTargetList & 0xFF) << 16) | SgiId + ); +} + +/* + * Acknowledge and return the value of the Interrupt Acknowledge Register + * + * InterruptId is returned separately from the register value because in + * the GICv2 the register value contains the CpuId and InterruptId while + * in the GICv3 the register value is only the InterruptId. + * + * @param GicInterruptInterfaceBase Base Address of the GIC CPU Interface + * @param InterruptId InterruptId read from the Interrupt + * Acknowledge Register + * + * @retval value returned by the Interrupt Acknowledge Register + * + */ +UINTN +EFIAPI +ArmGicAcknowledgeInterrupt ( + IN UINTN GicInterruptInterfaceBase, + OUT UINTN *InterruptId + ) +{ + UINTN Value; + ARM_GIC_ARCH_REVISION Revision; + + Revision = ArmGicGetSupportedArchRevision (); + if (Revision == ARM_GIC_ARCH_REVISION_2) { + Value = ArmGicV2AcknowledgeInterrupt (GicInterruptInterfaceBase); + // InterruptId is required for the caller to know if a valid or spurious + // interrupt has been read + ASSERT (InterruptId != NULL); + if (InterruptId != NULL) { + *InterruptId = Value & ARM_GIC_ICCIAR_ACKINTID; + } + } else if (Revision == ARM_GIC_ARCH_REVISION_3) { + Value = ArmGicV3AcknowledgeInterrupt (); + } else { + ASSERT_EFI_ERROR (EFI_UNSUPPORTED); + // Report Spurious interrupt which is what the above controllers would + // return if no interrupt was available + Value = 1023; + } + + return Value; +} + +VOID +EFIAPI +ArmGicEndOfInterrupt ( + IN UINTN GicInterruptInterfaceBase, + IN UINTN Source + ) +{ + ARM_GIC_ARCH_REVISION Revision; + + Revision = ArmGicGetSupportedArchRevision (); + if (Revision == ARM_GIC_ARCH_REVISION_2) { + ArmGicV2EndOfInterrupt (GicInterruptInterfaceBase, Source); + } else if (Revision == ARM_GIC_ARCH_REVISION_3) { + ArmGicV3EndOfInterrupt (Source); + } else { + ASSERT_EFI_ERROR (EFI_UNSUPPORTED); + } +} + +VOID +EFIAPI +ArmGicSetInterruptPriority ( + IN UINTN GicDistributorBase, + IN UINTN GicRedistributorBase, + IN UINTN Source, + IN UINTN Priority + ) +{ + UINT32 RegOffset; + UINTN RegShift; + ARM_GIC_ARCH_REVISION Revision; + UINTN GicCpuRedistributorBase; + + // Calculate register offset and bit position + RegOffset = Source / 4; + RegShift = (Source % 4) * 8; + + Revision = ArmGicGetSupportedArchRevision (); + if ((Revision == ARM_GIC_ARCH_REVISION_2) || + FeaturePcdGet (PcdArmGicV3WithV2Legacy) || + SourceIsSpi (Source)) + { + MmioAndThenOr32 ( + GicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset), + ~(0xff << RegShift), + Priority << RegShift + ); + } else { + GicCpuRedistributorBase = GicGetCpuRedistributorBase ( + GicRedistributorBase, + Revision + ); + if (GicCpuRedistributorBase == 0) { + return; + } + + MmioAndThenOr32 ( + IPRIORITY_ADDRESS (GicCpuRedistributorBase, RegOffset), + ~(0xff << RegShift), + Priority << RegShift + ); + } +} + +VOID +EFIAPI +ArmGicEnableInterrupt ( + IN UINTN GicDistributorBase, + IN UINTN GicRedistributorBase, + IN UINTN Source + ) +{ + UINT32 RegOffset; + UINTN RegShift; + ARM_GIC_ARCH_REVISION Revision; + UINTN GicCpuRedistributorBase; + + // Calculate enable register offset and bit position + RegOffset = Source / 32; + RegShift = Source % 32; + + Revision = ArmGicGetSupportedArchRevision (); + if ((Revision == ARM_GIC_ARCH_REVISION_2) || + FeaturePcdGet (PcdArmGicV3WithV2Legacy) || + SourceIsSpi (Source)) + { + // Write set-enable register + MmioWrite32 ( + GicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset), + 1 << RegShift + ); + } else { + GicCpuRedistributorBase = GicGetCpuRedistributorBase ( + GicRedistributorBase, + Revision + ); + if (GicCpuRedistributorBase == 0) { + ASSERT_EFI_ERROR (EFI_NOT_FOUND); + return; + } + + // Write set-enable register + MmioWrite32 ( + ISENABLER_ADDRESS (GicCpuRedistributorBase, RegOffset), + 1 << RegShift + ); + } +} + +VOID +EFIAPI +ArmGicDisableInterrupt ( + IN UINTN GicDistributorBase, + IN UINTN GicRedistributorBase, + IN UINTN Source + ) +{ + UINT32 RegOffset; + UINTN RegShift; + ARM_GIC_ARCH_REVISION Revision; + UINTN GicCpuRedistributorBase; + + // Calculate enable register offset and bit position + RegOffset = Source / 32; + RegShift = Source % 32; + + Revision = ArmGicGetSupportedArchRevision (); + if ((Revision == ARM_GIC_ARCH_REVISION_2) || + FeaturePcdGet (PcdArmGicV3WithV2Legacy) || + SourceIsSpi (Source)) + { + // Write clear-enable register + MmioWrite32 ( + GicDistributorBase + ARM_GIC_ICDICER + (4 * RegOffset), + 1 << RegShift + ); + } else { + GicCpuRedistributorBase = GicGetCpuRedistributorBase ( + GicRedistributorBase, + Revision + ); + if (GicCpuRedistributorBase == 0) { + return; + } + + // Write clear-enable register + MmioWrite32 ( + ICENABLER_ADDRESS (GicCpuRedistributorBase, RegOffset), + 1 << RegShift + ); + } +} + +BOOLEAN +EFIAPI +ArmGicIsInterruptEnabled ( + IN UINTN GicDistributorBase, + IN UINTN GicRedistributorBase, + IN UINTN Source + ) +{ + UINT32 RegOffset; + UINTN RegShift; + ARM_GIC_ARCH_REVISION Revision; + UINTN GicCpuRedistributorBase; + UINT32 Interrupts; + + // Calculate enable register offset and bit position + RegOffset = Source / 32; + RegShift = Source % 32; + + Revision = ArmGicGetSupportedArchRevision (); + if ((Revision == ARM_GIC_ARCH_REVISION_2) || + FeaturePcdGet (PcdArmGicV3WithV2Legacy) || + SourceIsSpi (Source)) + { + Interrupts = MmioRead32 ( + GicDistributorBase + ARM_GIC_ICDISER + (4 * RegOffset) + ); + } else { + GicCpuRedistributorBase = GicGetCpuRedistributorBase ( + GicRedistributorBase, + Revision + ); + if (GicCpuRedistributorBase == 0) { + return 0; + } + + // Read set-enable register + Interrupts = MmioRead32 ( + ISENABLER_ADDRESS (GicCpuRedistributorBase, RegOffset) + ); + } + + return ((Interrupts & (1 << RegShift)) != 0); +} + +VOID +EFIAPI +ArmGicDisableDistributor ( + IN INTN GicDistributorBase + ) +{ + // Disable Gic Distributor + MmioWrite32 (GicDistributorBase + ARM_GIC_ICDDCR, 0x0); +} + +VOID +EFIAPI +ArmGicEnableInterruptInterface ( + IN INTN GicInterruptInterfaceBase + ) +{ + ARM_GIC_ARCH_REVISION Revision; + + Revision = ArmGicGetSupportedArchRevision (); + if (Revision == ARM_GIC_ARCH_REVISION_2) { + ArmGicV2EnableInterruptInterface (GicInterruptInterfaceBase); + } else if (Revision == ARM_GIC_ARCH_REVISION_3) { + ArmGicV3EnableInterruptInterface (); + } else { + ASSERT_EFI_ERROR (EFI_UNSUPPORTED); + } +} + +VOID +EFIAPI +ArmGicDisableInterruptInterface ( + IN INTN GicInterruptInterfaceBase + ) +{ + ARM_GIC_ARCH_REVISION Revision; + + Revision = ArmGicGetSupportedArchRevision (); + if (Revision == ARM_GIC_ARCH_REVISION_2) { + ArmGicV2DisableInterruptInterface (GicInterruptInterfaceBase); + } else if (Revision == ARM_GIC_ARCH_REVISION_3) { + ArmGicV3DisableInterruptInterface (); + } else { + ASSERT_EFI_ERROR (EFI_UNSUPPORTED); + } +} diff --git a/ArmPkg/Drivers/ArmGic/ArmGicLib.inf b/ArmPkg/Drivers/ArmGic/ArmGicLib.inf new file mode 100644 index 000000000..addb8d315 --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/ArmGicLib.inf @@ -0,0 +1,41 @@ +#/* @file +# Copyright (c) 2011-2018, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#*/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArmGicLib + FILE_GUID = 03d05ee4-cdeb-458c-9dfc-993f09bdf405 + MODULE_TYPE = SEC + VERSION_STRING = 1.0 + LIBRARY_CLASS = ArmGicLib + +[Sources] + ArmGicLib.c + ArmGicNonSecLib.c + + GicV2/ArmGicV2Lib.c + GicV2/ArmGicV2NonSecLib.c + +[Sources.ARM] + GicV3/Arm/ArmGicV3.S | GCC + +[Sources.AARCH64] + GicV3/AArch64/ArmGicV3.S + +[LibraryClasses] + ArmLib + DebugLib + IoLib + ArmGicArchLib + +[Packages] + ArmPkg/ArmPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + MdePkg/MdePkg.dec + +[FeaturePcd] + gArmTokenSpaceGuid.PcdArmGicV3WithV2Legacy diff --git a/ArmPkg/Drivers/ArmGic/ArmGicNonSecLib.c b/ArmPkg/Drivers/ArmGic/ArmGicNonSecLib.c new file mode 100644 index 000000000..aa4f0e212 --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/ArmGicNonSecLib.c @@ -0,0 +1,35 @@ +/** @file +* +* Copyright (c) 2011-2015, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include +#include +#include + +VOID +EFIAPI +ArmGicEnableDistributor ( + IN INTN GicDistributorBase + ) +{ + ARM_GIC_ARCH_REVISION Revision; + + /* + * Enable GIC distributor in Non-Secure world. + * Note: The ICDDCR register is banked when Security extensions are implemented + */ + Revision = ArmGicGetSupportedArchRevision (); + if (Revision == ARM_GIC_ARCH_REVISION_2) { + MmioWrite32 (GicDistributorBase + ARM_GIC_ICDDCR, 0x1); + } else { + if (MmioRead32 (GicDistributorBase + ARM_GIC_ICDDCR) & ARM_GIC_ICDDCR_ARE) { + MmioOr32 (GicDistributorBase + ARM_GIC_ICDDCR, 0x2); + } else { + MmioOr32 (GicDistributorBase + ARM_GIC_ICDDCR, 0x1); + } + } +} diff --git a/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Dxe.c b/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Dxe.c new file mode 100644 index 000000000..25290342b --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Dxe.c @@ -0,0 +1,464 @@ +/*++ + +Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.
+Portions copyright (c) 2010, Apple Inc. All rights reserved.
+Portions copyright (c) 2011-2017, ARM Ltd. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +Module Name: + + GicV2/ArmGicV2Dxe.c + +Abstract: + + Driver implementing the GicV2 interrupt controller protocol + +--*/ + +#include + +#include "ArmGicDxe.h" + +#define ARM_GIC_DEFAULT_PRIORITY 0x80 + +extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol; +extern EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V2Protocol; + +STATIC UINT32 mGicInterruptInterfaceBase; +STATIC UINT32 mGicDistributorBase; + +/** + Enable interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt enabled. + @retval EFI_UNSUPPORTED Source interrupt is not supported + +**/ +STATIC +EFI_STATUS +EFIAPI +GicV2EnableInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + if (Source >= mGicNumInterrupts) { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + ArmGicEnableInterrupt (mGicDistributorBase, 0, Source); + + return EFI_SUCCESS; +} + +/** + Disable interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt disabled. + @retval EFI_UNSUPPORTED Source interrupt is not supported + +**/ +STATIC +EFI_STATUS +EFIAPI +GicV2DisableInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + if (Source >= mGicNumInterrupts) { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + ArmGicDisableInterrupt (mGicDistributorBase, 0, Source); + + return EFI_SUCCESS; +} + +/** + Return current state of interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + @param InterruptState TRUE: source enabled, FALSE: source disabled. + + @retval EFI_SUCCESS InterruptState is valid + @retval EFI_UNSUPPORTED Source interrupt is not supported + +**/ +STATIC +EFI_STATUS +EFIAPI +GicV2GetInterruptSourceState ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN BOOLEAN *InterruptState + ) +{ + if (Source >= mGicNumInterrupts) { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + *InterruptState = ArmGicIsInterruptEnabled (mGicDistributorBase, 0, Source); + + return EFI_SUCCESS; +} + +/** + Signal to the hardware that the End Of Interrupt state + has been reached. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt ended successfully. + @retval EFI_UNSUPPORTED Source interrupt is not supported + +**/ +STATIC +EFI_STATUS +EFIAPI +GicV2EndOfInterrupt ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + if (Source >= mGicNumInterrupts) { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + ArmGicV2EndOfInterrupt (mGicInterruptInterfaceBase, Source); + return EFI_SUCCESS; +} + +/** + EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs. + + @param InterruptType Defines the type of interrupt or exception that + occurred on the processor.This parameter is + processor architecture specific. + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. + + @return None + +**/ +STATIC +VOID +EFIAPI +GicV2IrqInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINT32 GicInterrupt; + HARDWARE_INTERRUPT_HANDLER InterruptHandler; + + GicInterrupt = ArmGicV2AcknowledgeInterrupt (mGicInterruptInterfaceBase); + + // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the + // number of interrupt (ie: Spurious interrupt). + if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) { + // The special interrupts do not need to be acknowledged + return; + } + + InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt]; + if (InterruptHandler != NULL) { + // Call the registered interrupt handler. + InterruptHandler (GicInterrupt, SystemContext); + } else { + DEBUG ((DEBUG_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt)); + GicV2EndOfInterrupt (&gHardwareInterruptV2Protocol, GicInterrupt); + } +} + +// The protocol instance produced by this driver +EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV2Protocol = { + RegisterInterruptSource, + GicV2EnableInterruptSource, + GicV2DisableInterruptSource, + GicV2GetInterruptSourceState, + GicV2EndOfInterrupt +}; + +/** + Get interrupt trigger type of an interrupt + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt. + @param TriggerType Returns interrupt trigger type. + + @retval EFI_SUCCESS Source interrupt supported. + @retval EFI_UNSUPPORTED Source interrupt is not supported. +**/ +STATIC +EFI_STATUS +EFIAPI +GicV2GetTriggerType ( + IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + OUT EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE *TriggerType + ) +{ + UINTN RegAddress; + UINTN Config1Bit; + EFI_STATUS Status; + + Status = GicGetDistributorIcfgBaseAndBit ( + Source, + &RegAddress, + &Config1Bit + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((MmioRead32 (RegAddress) & (1 << Config1Bit)) == 0) { + *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH; + } else { + *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING; + } + + return EFI_SUCCESS; +} + +/** + Set interrupt trigger type of an interrupt + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt. + @param TriggerType Interrupt trigger type. + + @retval EFI_SUCCESS Source interrupt supported. + @retval EFI_UNSUPPORTED Source interrupt is not supported. +**/ +STATIC +EFI_STATUS +EFIAPI +GicV2SetTriggerType ( + IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE TriggerType + ) +{ + UINTN RegAddress; + UINTN Config1Bit; + UINT32 Value; + EFI_STATUS Status; + BOOLEAN SourceEnabled; + + if ( (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING) + && (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH)) + { + DEBUG (( + DEBUG_ERROR, + "Invalid interrupt trigger type: %d\n", \ + TriggerType + )); + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + Status = GicGetDistributorIcfgBaseAndBit ( + Source, + &RegAddress, + &Config1Bit + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = GicV2GetInterruptSourceState ( + (EFI_HARDWARE_INTERRUPT_PROTOCOL *)This, + Source, + &SourceEnabled + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Value = (TriggerType == EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING) + ? ARM_GIC_ICDICFR_EDGE_TRIGGERED + : ARM_GIC_ICDICFR_LEVEL_TRIGGERED; + + // Before changing the value, we must disable the interrupt, + // otherwise GIC behavior is UNPREDICTABLE. + if (SourceEnabled) { + GicV2DisableInterruptSource ( + (EFI_HARDWARE_INTERRUPT_PROTOCOL *)This, + Source + ); + } + + MmioAndThenOr32 ( + RegAddress, + ~(0x1 << Config1Bit), + Value << Config1Bit + ); + + // Restore interrupt state + if (SourceEnabled) { + GicV2EnableInterruptSource ( + (EFI_HARDWARE_INTERRUPT_PROTOCOL *)This, + Source + ); + } + + return EFI_SUCCESS; +} + +EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V2Protocol = { + (HARDWARE_INTERRUPT2_REGISTER)RegisterInterruptSource, + (HARDWARE_INTERRUPT2_ENABLE)GicV2EnableInterruptSource, + (HARDWARE_INTERRUPT2_DISABLE)GicV2DisableInterruptSource, + (HARDWARE_INTERRUPT2_INTERRUPT_STATE)GicV2GetInterruptSourceState, + (HARDWARE_INTERRUPT2_END_OF_INTERRUPT)GicV2EndOfInterrupt, + GicV2GetTriggerType, + GicV2SetTriggerType +}; + +/** + Shutdown our hardware + + DXE Core will disable interrupts and turn off the timer and disable + interrupts after all the event handlers have run. + + @param[in] Event The Event that is being processed + @param[in] Context Event Context +**/ +STATIC +VOID +EFIAPI +GicV2ExitBootServicesEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN Index; + UINT32 GicInterrupt; + + // Disable all the interrupts + for (Index = 0; Index < mGicNumInterrupts; Index++) { + GicV2DisableInterruptSource (&gHardwareInterruptV2Protocol, Index); + } + + // Acknowledge all pending interrupts + do { + GicInterrupt = ArmGicV2AcknowledgeInterrupt (mGicInterruptInterfaceBase); + + if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) < mGicNumInterrupts) { + GicV2EndOfInterrupt (&gHardwareInterruptV2Protocol, GicInterrupt); + } + } while (!ARM_GIC_IS_SPECIAL_INTERRUPTS (GicInterrupt)); + + // Disable Gic Interface + ArmGicV2DisableInterruptInterface (mGicInterruptInterfaceBase); + + // Disable Gic Distributor + ArmGicDisableDistributor (mGicDistributorBase); +} + +/** + Initialize the state information for the CPU Architectural Protocol + + @param ImageHandle of the loaded driver + @param SystemTable Pointer to the System Table + + @retval EFI_SUCCESS Protocol registered + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure + @retval EFI_DEVICE_ERROR Hardware problems + +**/ +EFI_STATUS +GicV2DxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINTN Index; + UINT32 RegOffset; + UINTN RegShift; + UINT32 CpuTarget; + + // Make sure the Interrupt Controller Protocol is not already installed in + // the system. + ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid); + + mGicInterruptInterfaceBase = PcdGet64 (PcdGicInterruptInterfaceBase); + mGicDistributorBase = PcdGet64 (PcdGicDistributorBase); + mGicNumInterrupts = ArmGicGetMaxNumInterrupts (mGicDistributorBase); + + for (Index = 0; Index < mGicNumInterrupts; Index++) { + GicV2DisableInterruptSource (&gHardwareInterruptV2Protocol, Index); + + // Set Priority + RegOffset = Index / 4; + RegShift = (Index % 4) * 8; + MmioAndThenOr32 ( + mGicDistributorBase + ARM_GIC_ICDIPR + (4 * RegOffset), + ~(0xff << RegShift), + ARM_GIC_DEFAULT_PRIORITY << RegShift + ); + } + + // Targets the interrupts to the Primary Cpu + + // Only Primary CPU will run this code. We can identify our GIC CPU ID by + // reading the GIC Distributor Target register. The 8 first GICD_ITARGETSRn + // are banked to each connected CPU. These 8 registers hold the CPU targets + // fields for interrupts 0-31. More Info in the GIC Specification about + // "Interrupt Processor Targets Registers" + + // Read the first Interrupt Processor Targets Register (that corresponds to + // the 4 first SGIs) + CpuTarget = MmioRead32 (mGicDistributorBase + ARM_GIC_ICDIPTR); + + // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. + // This value is 0 when we run on a uniprocessor platform. + if (CpuTarget != 0) { + // The 8 first Interrupt Processor Targets Registers are read-only + for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) { + MmioWrite32 ( + mGicDistributorBase + ARM_GIC_ICDIPTR + (Index * 4), + CpuTarget + ); + } + } + + // Set binary point reg to 0x7 (no preemption) + MmioWrite32 (mGicInterruptInterfaceBase + ARM_GIC_ICCBPR, 0x7); + + // Set priority mask reg to 0xff to allow all priorities through + MmioWrite32 (mGicInterruptInterfaceBase + ARM_GIC_ICCPMR, 0xff); + + // Enable gic cpu interface + ArmGicEnableInterruptInterface (mGicInterruptInterfaceBase); + + // Enable gic distributor + ArmGicEnableDistributor (mGicDistributorBase); + + Status = InstallAndRegisterInterruptService ( + &gHardwareInterruptV2Protocol, + &gHardwareInterrupt2V2Protocol, + GicV2IrqInterruptHandler, + GicV2ExitBootServicesEvent + ); + + return Status; +} diff --git a/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Lib.c b/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Lib.c new file mode 100644 index 000000000..f403bec36 --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2Lib.c @@ -0,0 +1,30 @@ +/** @file +* +* Copyright (c) 2013-2014, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include +#include + +UINTN +EFIAPI +ArmGicV2AcknowledgeInterrupt ( + IN UINTN GicInterruptInterfaceBase + ) +{ + // Read the Interrupt Acknowledge Register + return MmioRead32 (GicInterruptInterfaceBase + ARM_GIC_ICCIAR); +} + +VOID +EFIAPI +ArmGicV2EndOfInterrupt ( + IN UINTN GicInterruptInterfaceBase, + IN UINTN Source + ) +{ + MmioWrite32 (GicInterruptInterfaceBase + ARM_GIC_ICCEIOR, Source); +} diff --git a/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2NonSecLib.c b/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2NonSecLib.c new file mode 100644 index 000000000..85c2a920a --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/GicV2/ArmGicV2NonSecLib.c @@ -0,0 +1,35 @@ +/** @file +* +* Copyright (c) 2011-2014, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include +#include +#include + +VOID +EFIAPI +ArmGicV2EnableInterruptInterface ( + IN INTN GicInterruptInterfaceBase + ) +{ + /* + * Enable the CPU interface in Non-Secure world + * Note: The ICCICR register is banked when Security extensions are implemented + */ + MmioWrite32 (GicInterruptInterfaceBase + ARM_GIC_ICCICR, 0x1); +} + +VOID +EFIAPI +ArmGicV2DisableInterruptInterface ( + IN INTN GicInterruptInterfaceBase + ) +{ + // Disable Gic Interface + MmioWrite32 (GicInterruptInterfaceBase + ARM_GIC_ICCICR, 0x0); + MmioWrite32 (GicInterruptInterfaceBase + ARM_GIC_ICCPMR, 0x0); +} diff --git a/ArmPkg/Drivers/ArmGic/GicV3/AArch64/ArmGicV3.S b/ArmPkg/Drivers/ArmGic/GicV3/AArch64/ArmGicV3.S new file mode 100644 index 000000000..20f83aa85 --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/GicV3/AArch64/ArmGicV3.S @@ -0,0 +1,106 @@ +# +# Copyright (c) 2014, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# + +#include + +#if !defined(__clang__) + +// +// Clang versions before v3.6 do not support the GNU extension that allows +// system registers outside of the IMPLEMENTATION DEFINED range to be specified +// using the generic notation below. However, clang knows these registers by +// their architectural names, so it has no need for these aliases anyway. +// +#define ICC_SRE_EL1 S3_0_C12_C12_5 +#define ICC_SRE_EL2 S3_4_C12_C9_5 +#define ICC_SRE_EL3 S3_6_C12_C12_5 +#define ICC_IGRPEN1_EL1 S3_0_C12_C12_7 +#define ICC_EOIR1_EL1 S3_0_C12_C12_1 +#define ICC_IAR1_EL1 S3_0_C12_C12_0 +#define ICC_PMR_EL1 S3_0_C4_C6_0 +#define ICC_BPR1_EL1 S3_0_C12_C12_3 + +#endif + +//UINT32 +//EFIAPI +//ArmGicV3GetControlSystemRegisterEnable ( +// VOID +// ); +ASM_FUNC(ArmGicV3GetControlSystemRegisterEnable) + EL1_OR_EL2_OR_EL3(x1) +1: mrs x0, ICC_SRE_EL1 + b 4f +2: mrs x0, ICC_SRE_EL2 + b 4f +3: mrs x0, ICC_SRE_EL3 +4: ret + +//VOID +//EFIAPI +//ArmGicV3SetControlSystemRegisterEnable ( +// IN UINT32 ControlSystemRegisterEnable +// ); +ASM_FUNC(ArmGicV3SetControlSystemRegisterEnable) + EL1_OR_EL2_OR_EL3(x1) +1: msr ICC_SRE_EL1, x0 + b 4f +2: msr ICC_SRE_EL2, x0 + b 4f +3: msr ICC_SRE_EL3, x0 +4: isb + ret + +//VOID +//ArmGicV3EnableInterruptInterface ( +// VOID +// ); +ASM_FUNC(ArmGicV3EnableInterruptInterface) + mov x0, #1 + msr ICC_IGRPEN1_EL1, x0 + ret + +//VOID +//ArmGicV3DisableInterruptInterface ( +// VOID +// ); +ASM_FUNC(ArmGicV3DisableInterruptInterface) + mov x0, #0 + msr ICC_IGRPEN1_EL1, x0 + ret + +//VOID +//ArmGicV3EndOfInterrupt ( +// IN UINTN InterruptId +// ); +ASM_FUNC(ArmGicV3EndOfInterrupt) + msr ICC_EOIR1_EL1, x0 + ret + +//UINTN +//ArmGicV3AcknowledgeInterrupt ( +// VOID +// ); +ASM_FUNC(ArmGicV3AcknowledgeInterrupt) + mrs x0, ICC_IAR1_EL1 + ret + +//VOID +//ArmGicV3SetPriorityMask ( +// IN UINTN Priority +// ); +ASM_FUNC(ArmGicV3SetPriorityMask) + msr ICC_PMR_EL1, x0 + ret + +//VOID +//ArmGicV3SetBinaryPointer ( +// IN UINTN BinaryPoint +// ); +ASM_FUNC(ArmGicV3SetBinaryPointer) + msr ICC_BPR1_EL1, x0 + ret diff --git a/ArmPkg/Drivers/ArmGic/GicV3/Arm/ArmGicV3.S b/ArmPkg/Drivers/ArmGic/GicV3/Arm/ArmGicV3.S new file mode 100644 index 000000000..8c43a613d --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/GicV3/Arm/ArmGicV3.S @@ -0,0 +1,80 @@ +# +# Copyright (c) 2014, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# + +#include +#include + +// For the moment we assume this will run in SVC mode on ARMv7 + +//UINT32 +//EFIAPI +//ArmGicGetControlSystemRegisterEnable ( +// VOID +// ); +ASM_FUNC(ArmGicV3GetControlSystemRegisterEnable) + mrc p15, 0, r0, c12, c12, 5 // ICC_SRE + bx lr + +//VOID +//EFIAPI +//ArmGicSetControlSystemRegisterEnable ( +// IN UINT32 ControlSystemRegisterEnable +// ); +ASM_FUNC(ArmGicV3SetControlSystemRegisterEnable) + mcr p15, 0, r0, c12, c12, 5 // ICC_SRE + isb + bx lr + +//VOID +//ArmGicV3EnableInterruptInterface ( +// VOID +// ); +ASM_FUNC(ArmGicV3EnableInterruptInterface) + mov r0, #1 + mcr p15, 0, r0, c12, c12, 7 // ICC_IGRPEN1 + bx lr + +//VOID +//ArmGicV3DisableInterruptInterface ( +// VOID +// ); +ASM_FUNC(ArmGicV3DisableInterruptInterface) + mov r0, #0 + mcr p15, 0, r0, c12, c12, 7 // ICC_IGRPEN1 + bx lr + +//VOID +//ArmGicV3EndOfInterrupt ( +// IN UINTN InterruptId +// ); +ASM_FUNC(ArmGicV3EndOfInterrupt) + mcr p15, 0, r0, c12, c12, 1 //ICC_EOIR1 + bx lr + +//UINTN +//ArmGicV3AcknowledgeInterrupt ( +// VOID +// ); +ASM_FUNC(ArmGicV3AcknowledgeInterrupt) + mrc p15, 0, r0, c12, c12, 0 //ICC_IAR1 + bx lr + +//VOID +//ArmGicV3SetPriorityMask ( +// IN UINTN Priority +// ); +ASM_FUNC(ArmGicV3SetPriorityMask) + mcr p15, 0, r0, c4, c6, 0 //ICC_PMR + bx lr + +//VOID +//ArmGicV3SetBinaryPointer ( +// IN UINTN BinaryPoint +// ); +ASM_FUNC(ArmGicV3SetBinaryPointer) + mcr p15, 0, r0, c12, c12, 3 //ICC_BPR1 + bx lr diff --git a/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c b/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c new file mode 100644 index 000000000..b1f0cd48c --- /dev/null +++ b/ArmPkg/Drivers/ArmGic/GicV3/ArmGicV3Dxe.c @@ -0,0 +1,488 @@ +/** @file +* +* Copyright (c) 2011-2018, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include + +#include "ArmGicDxe.h" + +#define ARM_GIC_DEFAULT_PRIORITY 0x80 + +extern EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol; +extern EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V3Protocol; + +STATIC UINTN mGicDistributorBase; +STATIC UINTN mGicRedistributorsBase; + +/** + Enable interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt enabled. + @retval EFI_DEVICE_ERROR Hardware could not be programmed. + +**/ +STATIC +EFI_STATUS +EFIAPI +GicV3EnableInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + if (Source >= mGicNumInterrupts) { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + ArmGicEnableInterrupt (mGicDistributorBase, mGicRedistributorsBase, Source); + + return EFI_SUCCESS; +} + +/** + Disable interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt disabled. + @retval EFI_DEVICE_ERROR Hardware could not be programmed. + +**/ +STATIC +EFI_STATUS +EFIAPI +GicV3DisableInterruptSource ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + if (Source >= mGicNumInterrupts) { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + ArmGicDisableInterrupt (mGicDistributorBase, mGicRedistributorsBase, Source); + + return EFI_SUCCESS; +} + +/** + Return current state of interrupt source Source. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + @param InterruptState TRUE: source enabled, FALSE: source disabled. + + @retval EFI_SUCCESS InterruptState is valid + @retval EFI_DEVICE_ERROR InterruptState is not valid + +**/ +STATIC +EFI_STATUS +EFIAPI +GicV3GetInterruptSourceState ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN BOOLEAN *InterruptState + ) +{ + if (Source >= mGicNumInterrupts) { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + *InterruptState = ArmGicIsInterruptEnabled ( + mGicDistributorBase, + mGicRedistributorsBase, + Source + ); + + return EFI_SUCCESS; +} + +/** + Signal to the hardware that the End Of Interrupt state + has been reached. + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt + + @retval EFI_SUCCESS Source interrupt ended successfully. + @retval EFI_DEVICE_ERROR Hardware could not be programmed. + +**/ +STATIC +EFI_STATUS +EFIAPI +GicV3EndOfInterrupt ( + IN EFI_HARDWARE_INTERRUPT_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source + ) +{ + if (Source >= mGicNumInterrupts) { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + ArmGicV3EndOfInterrupt (Source); + return EFI_SUCCESS; +} + +/** + EFI_CPU_INTERRUPT_HANDLER that is called when a processor interrupt occurs. + + @param InterruptType Defines the type of interrupt or exception that + occurred on the processor. This parameter is + processor architecture specific. + @param SystemContext A pointer to the processor context when + the interrupt occurred on the processor. + + @return None + +**/ +STATIC +VOID +EFIAPI +GicV3IrqInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + UINT32 GicInterrupt; + HARDWARE_INTERRUPT_HANDLER InterruptHandler; + + GicInterrupt = ArmGicV3AcknowledgeInterrupt (); + + // Special Interrupts (ID1020-ID1023) have an Interrupt ID greater than the + // number of interrupt (ie: Spurious interrupt). + if ((GicInterrupt & ARM_GIC_ICCIAR_ACKINTID) >= mGicNumInterrupts) { + // The special interrupt do not need to be acknowledge + return; + } + + InterruptHandler = gRegisteredInterruptHandlers[GicInterrupt]; + if (InterruptHandler != NULL) { + // Call the registered interrupt handler. + InterruptHandler (GicInterrupt, SystemContext); + } else { + DEBUG ((DEBUG_ERROR, "Spurious GIC interrupt: 0x%x\n", GicInterrupt)); + GicV3EndOfInterrupt (&gHardwareInterruptV3Protocol, GicInterrupt); + } +} + +// The protocol instance produced by this driver +EFI_HARDWARE_INTERRUPT_PROTOCOL gHardwareInterruptV3Protocol = { + RegisterInterruptSource, + GicV3EnableInterruptSource, + GicV3DisableInterruptSource, + GicV3GetInterruptSourceState, + GicV3EndOfInterrupt +}; + +/** + Get interrupt trigger type of an interrupt + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt. + @param TriggerType Returns interrupt trigger type. + + @retval EFI_SUCCESS Source interrupt supported. + @retval EFI_UNSUPPORTED Source interrupt is not supported. +**/ +STATIC +EFI_STATUS +EFIAPI +GicV3GetTriggerType ( + IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + OUT EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE *TriggerType + ) +{ + UINTN RegAddress; + UINTN Config1Bit; + EFI_STATUS Status; + + Status = GicGetDistributorIcfgBaseAndBit ( + Source, + &RegAddress, + &Config1Bit + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + if ((MmioRead32 (RegAddress) & (1 << Config1Bit)) == 0) { + *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH; + } else { + *TriggerType = EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING; + } + + return EFI_SUCCESS; +} + +/** + Set interrupt trigger type of an interrupt + + @param This Instance pointer for this protocol + @param Source Hardware source of the interrupt. + @param TriggerType Interrupt trigger type. + + @retval EFI_SUCCESS Source interrupt supported. + @retval EFI_UNSUPPORTED Source interrupt is not supported. +**/ +STATIC +EFI_STATUS +EFIAPI +GicV3SetTriggerType ( + IN EFI_HARDWARE_INTERRUPT2_PROTOCOL *This, + IN HARDWARE_INTERRUPT_SOURCE Source, + IN EFI_HARDWARE_INTERRUPT2_TRIGGER_TYPE TriggerType + ) +{ + UINTN RegAddress; + UINTN Config1Bit; + UINT32 Value; + EFI_STATUS Status; + BOOLEAN SourceEnabled; + + if ( (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING) + && (TriggerType != EFI_HARDWARE_INTERRUPT2_TRIGGER_LEVEL_HIGH)) + { + DEBUG (( + DEBUG_ERROR, + "Invalid interrupt trigger type: %d\n", \ + TriggerType + )); + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + Status = GicGetDistributorIcfgBaseAndBit ( + Source, + &RegAddress, + &Config1Bit + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Status = GicV3GetInterruptSourceState ( + (EFI_HARDWARE_INTERRUPT_PROTOCOL *)This, + Source, + &SourceEnabled + ); + + if (EFI_ERROR (Status)) { + return Status; + } + + Value = (TriggerType == EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING) + ? ARM_GIC_ICDICFR_EDGE_TRIGGERED + : ARM_GIC_ICDICFR_LEVEL_TRIGGERED; + + // Before changing the value, we must disable the interrupt, + // otherwise GIC behavior is UNPREDICTABLE. + if (SourceEnabled) { + GicV3DisableInterruptSource ( + (EFI_HARDWARE_INTERRUPT_PROTOCOL *)This, + Source + ); + } + + MmioAndThenOr32 ( + RegAddress, + ~(0x1 << Config1Bit), + Value << Config1Bit + ); + // Restore interrupt state + if (SourceEnabled) { + GicV3EnableInterruptSource ( + (EFI_HARDWARE_INTERRUPT_PROTOCOL *)This, + Source + ); + } + + return EFI_SUCCESS; +} + +EFI_HARDWARE_INTERRUPT2_PROTOCOL gHardwareInterrupt2V3Protocol = { + (HARDWARE_INTERRUPT2_REGISTER)RegisterInterruptSource, + (HARDWARE_INTERRUPT2_ENABLE)GicV3EnableInterruptSource, + (HARDWARE_INTERRUPT2_DISABLE)GicV3DisableInterruptSource, + (HARDWARE_INTERRUPT2_INTERRUPT_STATE)GicV3GetInterruptSourceState, + (HARDWARE_INTERRUPT2_END_OF_INTERRUPT)GicV3EndOfInterrupt, + GicV3GetTriggerType, + GicV3SetTriggerType +}; + +/** + Shutdown our hardware + + DXE Core will disable interrupts and turn off the timer and disable interrupts + after all the event handlers have run. + + @param[in] Event The Event that is being processed + @param[in] Context Event Context +**/ +VOID +EFIAPI +GicV3ExitBootServicesEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + UINTN Index; + + // Acknowledge all pending interrupts + for (Index = 0; Index < mGicNumInterrupts; Index++) { + GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index); + } + + // Disable Gic Interface + ArmGicV3DisableInterruptInterface (); + + // Disable Gic Distributor + ArmGicDisableDistributor (mGicDistributorBase); +} + +/** + Initialize the state information for the CPU Architectural Protocol + + @param ImageHandle of the loaded driver + @param SystemTable Pointer to the System Table + + @retval EFI_SUCCESS Protocol registered + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure + @retval EFI_DEVICE_ERROR Hardware problems + +**/ +EFI_STATUS +GicV3DxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINTN Index; + UINT64 CpuTarget; + UINT64 MpId; + + // Make sure the Interrupt Controller Protocol is not already installed in + // the system. + ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gHardwareInterruptProtocolGuid); + + mGicDistributorBase = PcdGet64 (PcdGicDistributorBase); + mGicRedistributorsBase = PcdGet64 (PcdGicRedistributorsBase); + mGicNumInterrupts = ArmGicGetMaxNumInterrupts (mGicDistributorBase); + + // We will be driving this GIC in native v3 mode, i.e., with Affinity + // Routing enabled. So ensure that the ARE bit is set. + if (!FeaturePcdGet (PcdArmGicV3WithV2Legacy)) { + MmioOr32 (mGicDistributorBase + ARM_GIC_ICDDCR, ARM_GIC_ICDDCR_ARE); + } + + for (Index = 0; Index < mGicNumInterrupts; Index++) { + GicV3DisableInterruptSource (&gHardwareInterruptV3Protocol, Index); + + // Set Priority + ArmGicSetInterruptPriority ( + mGicDistributorBase, + mGicRedistributorsBase, + Index, + ARM_GIC_DEFAULT_PRIORITY + ); + } + + // Targets the interrupts to the Primary Cpu + + if (FeaturePcdGet (PcdArmGicV3WithV2Legacy)) { + // Only Primary CPU will run this code. We can identify our GIC CPU ID by + // reading the GIC Distributor Target register. The 8 first + // GICD_ITARGETSRn are banked to each connected CPU. These 8 registers + // hold the CPU targets fields for interrupts 0-31. More Info in the GIC + // Specification about "Interrupt Processor Targets Registers" + + // Read the first Interrupt Processor Targets Register (that corresponds + // to the 4 first SGIs) + CpuTarget = MmioRead32 (mGicDistributorBase + ARM_GIC_ICDIPTR); + + // The CPU target is a bit field mapping each CPU to a GIC CPU Interface. + // This value is 0 when we run on a uniprocessor platform. + if (CpuTarget != 0) { + // The 8 first Interrupt Processor Targets Registers are read-only + for (Index = 8; Index < (mGicNumInterrupts / 4); Index++) { + MmioWrite32 ( + mGicDistributorBase + ARM_GIC_ICDIPTR + (Index * 4), + CpuTarget + ); + } + } + } else { + MpId = ArmReadMpidr (); + CpuTarget = MpId & + (ARM_CORE_AFF0 | ARM_CORE_AFF1 | ARM_CORE_AFF2 | ARM_CORE_AFF3); + + if ((MmioRead32 ( + mGicDistributorBase + ARM_GIC_ICDDCR + ) & ARM_GIC_ICDDCR_DS) != 0) + { + // If the Disable Security (DS) control bit is set, we are dealing with a + // GIC that has only one security state. In this case, let's assume we are + // executing in non-secure state (which is appropriate for DXE modules) + // and that no other firmware has performed any configuration on the GIC. + // This means we need to reconfigure all interrupts to non-secure Group 1 + // first. + + MmioWrite32 ( + mGicRedistributorsBase + ARM_GICR_CTLR_FRAME_SIZE + ARM_GIC_ICDISR, + 0xffffffff + ); + + for (Index = 32; Index < mGicNumInterrupts; Index += 32) { + MmioWrite32 ( + mGicDistributorBase + ARM_GIC_ICDISR + Index / 8, + 0xffffffff + ); + } + } + + // Route the SPIs to the primary CPU. SPIs start at the INTID 32 + for (Index = 0; Index < (mGicNumInterrupts - 32); Index++) { + MmioWrite64 ( + mGicDistributorBase + ARM_GICD_IROUTER + (Index * 8), + CpuTarget + ); + } + } + + // Set binary point reg to 0x7 (no preemption) + ArmGicV3SetBinaryPointer (0x7); + + // Set priority mask reg to 0xff to allow all priorities through + ArmGicV3SetPriorityMask (0xff); + + // Enable gic cpu interface + ArmGicV3EnableInterruptInterface (); + + // Enable gic distributor + ArmGicEnableDistributor (mGicDistributorBase); + + Status = InstallAndRegisterInterruptService ( + &gHardwareInterruptV3Protocol, + &gHardwareInterrupt2V3Protocol, + GicV3IrqInterruptHandler, + GicV3ExitBootServicesEvent + ); + + return Status; +} diff --git a/ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.c b/ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.c new file mode 100644 index 000000000..5a2866ccd --- /dev/null +++ b/ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.c @@ -0,0 +1,556 @@ +/** @file + Produces the CPU I/O 2 Protocol. + +Copyright (c) 2009 - 2012, Intel Corporation. All rights reserved.
+Copyright (c) 2016, Linaro Ltd. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include + +#include +#include +#include +#include +#include + +#define MAX_IO_PORT_ADDRESS 0xFFFF + +// +// Handle for the CPU I/O 2 Protocol +// +STATIC EFI_HANDLE mHandle = NULL; + +// +// Lookup table for increment values based on transfer widths +// +STATIC CONST UINT8 mInStride[] = { + 1, // EfiCpuIoWidthUint8 + 2, // EfiCpuIoWidthUint16 + 4, // EfiCpuIoWidthUint32 + 8, // EfiCpuIoWidthUint64 + 0, // EfiCpuIoWidthFifoUint8 + 0, // EfiCpuIoWidthFifoUint16 + 0, // EfiCpuIoWidthFifoUint32 + 0, // EfiCpuIoWidthFifoUint64 + 1, // EfiCpuIoWidthFillUint8 + 2, // EfiCpuIoWidthFillUint16 + 4, // EfiCpuIoWidthFillUint32 + 8 // EfiCpuIoWidthFillUint64 +}; + +// +// Lookup table for increment values based on transfer widths +// +STATIC CONST UINT8 mOutStride[] = { + 1, // EfiCpuIoWidthUint8 + 2, // EfiCpuIoWidthUint16 + 4, // EfiCpuIoWidthUint32 + 8, // EfiCpuIoWidthUint64 + 1, // EfiCpuIoWidthFifoUint8 + 2, // EfiCpuIoWidthFifoUint16 + 4, // EfiCpuIoWidthFifoUint32 + 8, // EfiCpuIoWidthFifoUint64 + 0, // EfiCpuIoWidthFillUint8 + 0, // EfiCpuIoWidthFillUint16 + 0, // EfiCpuIoWidthFillUint32 + 0 // EfiCpuIoWidthFillUint64 +}; + +/** + Check parameters to a CPU I/O 2 Protocol service request. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + @param[in] MmioOperation TRUE for an MMIO operation, FALSE for I/O Port operation. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[in] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The parameters for this request pass the checks. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +STATIC +EFI_STATUS +CpuIoCheckParameter ( + IN BOOLEAN MmioOperation, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + UINT64 MaxCount; + UINT64 Limit; + + // + // Check to see if Buffer is NULL + // + if (Buffer == NULL) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if Width is in the valid range + // + if ((UINT32)Width >= EfiCpuIoWidthMaximum) { + return EFI_INVALID_PARAMETER; + } + + // + // For FIFO type, the target address won't increase during the access, + // so treat Count as 1 + // + if ((Width >= EfiCpuIoWidthFifoUint8) && (Width <= EfiCpuIoWidthFifoUint64)) { + Count = 1; + } + + // + // Check to see if Width is in the valid range for I/O Port operations + // + Width = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03); + if (!MmioOperation && (Width == EfiCpuIoWidthUint64)) { + return EFI_INVALID_PARAMETER; + } + + // + // Check to see if Address is aligned + // + if ((Address & (UINT64)(mInStride[Width] - 1)) != 0) { + return EFI_UNSUPPORTED; + } + + // + // Check to see if any address associated with this transfer exceeds the maximum + // allowed address. The maximum address implied by the parameters passed in is + // Address + Size * Count. If the following condition is met, then the transfer + // is not supported. + // + // Address + Size * Count > (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS) + 1 + // + // Since MAX_ADDRESS can be the maximum integer value supported by the CPU and Count + // can also be the maximum integer value supported by the CPU, this range + // check must be adjusted to avoid all overflow conditions. + // + // The following form of the range check is equivalent but assumes that + // MAX_ADDRESS and MAX_IO_PORT_ADDRESS are of the form (2^n - 1). + // + Limit = (MmioOperation ? MAX_ADDRESS : MAX_IO_PORT_ADDRESS); + if (Count == 0) { + if (Address > Limit) { + return EFI_UNSUPPORTED; + } + } else { + MaxCount = RShiftU64 (Limit, Width); + if (MaxCount < (Count - 1)) { + return EFI_UNSUPPORTED; + } + + if (Address > LShiftU64 (MaxCount - Count + 1, Width)) { + return EFI_UNSUPPORTED; + } + } + + // + // Check to see if Buffer is aligned + // + if (((UINTN)Buffer & ((MIN (sizeof (UINTN), mInStride[Width]) - 1))) != 0) { + return EFI_UNSUPPORTED; + } + + return EFI_SUCCESS; +} + +/** + Reads memory-mapped registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[out] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +STATIC +EFI_STATUS +EFIAPI +CpuMemoryServiceRead ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_CPU_IO_PROTOCOL_WIDTH OperationWidth; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03); + for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiCpuIoWidthUint8) { + *Uint8Buffer = MmioRead8 ((UINTN)Address); + } else if (OperationWidth == EfiCpuIoWidthUint16) { + *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address); + } else if (OperationWidth == EfiCpuIoWidthUint32) { + *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address); + } else if (OperationWidth == EfiCpuIoWidthUint64) { + *((UINT64 *)Uint8Buffer) = MmioRead64 ((UINTN)Address); + } + } + + return EFI_SUCCESS; +} + +/** + Writes memory-mapped registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[in] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +STATIC +EFI_STATUS +EFIAPI +CpuMemoryServiceWrite ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_CPU_IO_PROTOCOL_WIDTH OperationWidth; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (TRUE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03); + for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiCpuIoWidthUint8) { + MmioWrite8 ((UINTN)Address, *Uint8Buffer); + } else if (OperationWidth == EfiCpuIoWidthUint16) { + MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer)); + } else if (OperationWidth == EfiCpuIoWidthUint32) { + MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer)); + } else if (OperationWidth == EfiCpuIoWidthUint64) { + MmioWrite64 ((UINTN)Address, *((UINT64 *)Uint8Buffer)); + } + } + + return EFI_SUCCESS; +} + +/** + Reads I/O registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[out] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +STATIC +EFI_STATUS +EFIAPI +CpuIoServiceRead ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + OUT VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_CPU_IO_PROTOCOL_WIDTH OperationWidth; + UINT8 *Uint8Buffer; + + Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + Address += PcdGet64 (PcdPciIoTranslation); + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03); + + for (Uint8Buffer = Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiCpuIoWidthUint8) { + *Uint8Buffer = MmioRead8 ((UINTN)Address); + } else if (OperationWidth == EfiCpuIoWidthUint16) { + *((UINT16 *)Uint8Buffer) = MmioRead16 ((UINTN)Address); + } else if (OperationWidth == EfiCpuIoWidthUint32) { + *((UINT32 *)Uint8Buffer) = MmioRead32 ((UINTN)Address); + } + } + + return EFI_SUCCESS; +} + +/** + Write I/O registers. + + The I/O operations are carried out exactly as requested. The caller is responsible + for satisfying any alignment and I/O width restrictions that a PI System on a + platform might require. For example on some platforms, width requests of + EfiCpuIoWidthUint64 do not work. Misaligned buffers, on the other hand, will + be handled by the driver. + + If Width is EfiCpuIoWidthUint8, EfiCpuIoWidthUint16, EfiCpuIoWidthUint32, + or EfiCpuIoWidthUint64, then both Address and Buffer are incremented for + each of the Count operations that is performed. + + If Width is EfiCpuIoWidthFifoUint8, EfiCpuIoWidthFifoUint16, + EfiCpuIoWidthFifoUint32, or EfiCpuIoWidthFifoUint64, then only Buffer is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times on the same Address. + + If Width is EfiCpuIoWidthFillUint8, EfiCpuIoWidthFillUint16, + EfiCpuIoWidthFillUint32, or EfiCpuIoWidthFillUint64, then only Address is + incremented for each of the Count operations that is performed. The read or + write operation is performed Count times from the first element of Buffer. + + @param[in] This A pointer to the EFI_CPU_IO2_PROTOCOL instance. + @param[in] Width Signifies the width of the I/O or Memory operation. + @param[in] Address The base address of the I/O operation. + @param[in] Count The number of I/O operations to perform. The number of + bytes moved is Width size * Count, starting at Address. + @param[in] Buffer For read operations, the destination buffer to store the results. + For write operations, the source buffer from which to write data. + + @retval EFI_SUCCESS The data was read from or written to the PI system. + @retval EFI_INVALID_PARAMETER Width is invalid for this PI system. + @retval EFI_INVALID_PARAMETER Buffer is NULL. + @retval EFI_UNSUPPORTED The Buffer is not aligned for the given Width. + @retval EFI_UNSUPPORTED The address range specified by Address, Width, + and Count is not valid for this PI system. + +**/ +STATIC +EFI_STATUS +EFIAPI +CpuIoServiceWrite ( + IN EFI_CPU_IO2_PROTOCOL *This, + IN EFI_CPU_IO_PROTOCOL_WIDTH Width, + IN UINT64 Address, + IN UINTN Count, + IN VOID *Buffer + ) +{ + EFI_STATUS Status; + UINT8 InStride; + UINT8 OutStride; + EFI_CPU_IO_PROTOCOL_WIDTH OperationWidth; + UINT8 *Uint8Buffer; + + // + // Make sure the parameters are valid + // + Status = CpuIoCheckParameter (FALSE, Width, Address, Count, Buffer); + if (EFI_ERROR (Status)) { + return Status; + } + + Address += PcdGet64 (PcdPciIoTranslation); + + // + // Select loop based on the width of the transfer + // + InStride = mInStride[Width]; + OutStride = mOutStride[Width]; + OperationWidth = (EFI_CPU_IO_PROTOCOL_WIDTH)(Width & 0x03); + + for (Uint8Buffer = (UINT8 *)Buffer; Count > 0; Address += InStride, Uint8Buffer += OutStride, Count--) { + if (OperationWidth == EfiCpuIoWidthUint8) { + MmioWrite8 ((UINTN)Address, *Uint8Buffer); + } else if (OperationWidth == EfiCpuIoWidthUint16) { + MmioWrite16 ((UINTN)Address, *((UINT16 *)Uint8Buffer)); + } else if (OperationWidth == EfiCpuIoWidthUint32) { + MmioWrite32 ((UINTN)Address, *((UINT32 *)Uint8Buffer)); + } + } + + return EFI_SUCCESS; +} + +// +// CPU I/O 2 Protocol instance +// +STATIC EFI_CPU_IO2_PROTOCOL mCpuIo2 = { + { + CpuMemoryServiceRead, + CpuMemoryServiceWrite + }, + { + CpuIoServiceRead, + CpuIoServiceWrite + } +}; + +/** + The user Entry Point for module CpuIo2Dxe. The user code starts with this function. + + @param[in] ImageHandle The firmware allocated handle for the EFI image. + @param[in] SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval other Some error occurs when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +ArmPciCpuIo2Initialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiCpuIo2ProtocolGuid); + Status = gBS->InstallMultipleProtocolInterfaces ( + &mHandle, + &gEfiCpuIo2ProtocolGuid, + &mCpuIo2, + NULL + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf b/ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf new file mode 100644 index 000000000..9339c2b53 --- /dev/null +++ b/ArmPkg/Drivers/ArmPciCpuIo2Dxe/ArmPciCpuIo2Dxe.inf @@ -0,0 +1,47 @@ +## @file +# Produces the CPU I/O 2 Protocol by using the services of the I/O Library. +# +# Copyright (c) 2009 - 2014, Intel Corporation. All rights reserved.
+# Copyright (c) 2016, Linaro Ltd. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArmPciCpuIo2Dxe + FILE_GUID = 168D1A6E-F4A5-448A-9E95-795661BB3067 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = ArmPciCpuIo2Initialize + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = ARM AARCH64 +# + +[Sources] + ArmPciCpuIo2Dxe.c + +[Packages] + ArmPkg/ArmPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + UefiDriverEntryPoint + BaseLib + DebugLib + IoLib + PcdLib + UefiBootServicesTableLib + +[Pcd] + gEfiMdePkgTokenSpaceGuid.PcdPciIoTranslation + +[Protocols] + gEfiCpuIo2ProtocolGuid ## PRODUCES + +[Depex] + TRUE diff --git a/ArmPkg/Drivers/ArmScmiDxe/ArmScmiBaseProtocolPrivate.h b/ArmPkg/Drivers/ArmScmiDxe/ArmScmiBaseProtocolPrivate.h new file mode 100644 index 000000000..de443015b --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/ArmScmiBaseProtocolPrivate.h @@ -0,0 +1,40 @@ +/** @file + + Copyright (c) 2017-2018, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#ifndef ARM_SCMI_BASE_PROTOCOL_PRIVATE_H_ +#define ARM_SCMI_BASE_PROTOCOL_PRIVATE_H_ + +// Return values of BASE_DISCOVER_LIST_PROTOCOLS command. +typedef struct { + UINT32 NumProtocols; + + // Array of four protocols in each element + // Total elements = 1 + (NumProtocols-1)/4 + + // NOTE: Since EDK2 does not allow flexible array member [] we declare + // here array of 1 element length. However below is used as a variable + // length array. + UINT8 Protocols[1]; +} BASE_DISCOVER_LIST; + +/** Initialize Base protocol and install protocol on a given handle. + + @param[in] Handle Handle to install Base protocol. + + @retval EFI_SUCCESS Base protocol interface installed + successfully. +**/ +EFI_STATUS +ScmiBaseProtocolInit ( + IN OUT EFI_HANDLE *Handle + ); + +#endif /* ARM_SCMI_BASE_PROTOCOL_PRIVATE_H_ */ diff --git a/ArmPkg/Drivers/ArmScmiDxe/ArmScmiClockProtocolPrivate.h b/ArmPkg/Drivers/ArmScmiDxe/ArmScmiClockProtocolPrivate.h new file mode 100644 index 000000000..34dca852c --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/ArmScmiClockProtocolPrivate.h @@ -0,0 +1,84 @@ +/** @file + + Copyright (c) 2017-2018, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#ifndef ARM_SCMI_CLOCK_PROTOCOL_PRIVATE_H_ +#define ARM_SCMI_CLOCK_PROTOCOL_PRIVATE_H_ + +#pragma pack(1) + +// Clock rate in two 32bit words. +typedef struct { + UINT32 Low; + UINT32 High; +} CLOCK_RATE_DWORD; + +// Format of the returned rate array. Linear or Non-linear,.RatesFlag Bit[12] +#define RATE_FORMAT_SHIFT 12 +#define RATE_FORMAT_MASK 0x0001 +#define RATE_FORMAT(RatesFlags) ((RatesFlags >> RATE_FORMAT_SHIFT) \ + & RATE_FORMAT_MASK) + +// Number of remaining rates after a call to the SCP, RatesFlag Bits[31:16] +#define NUM_REMAIN_RATES_SHIFT 16 +#define NUM_REMAIN_RATES(RatesFlags) ((RatesFlags >> NUM_REMAIN_RATES_SHIFT)) + +// Number of rates that are returned by a call.to the SCP, RatesFlag Bits[11:0] +#define NUM_RATES_MASK 0x0FFF +#define NUM_RATES(RatesFlags) (RatesFlags & NUM_RATES_MASK) + +// Return values for the CLOCK_DESCRIBER_RATE command. +typedef struct { + UINT32 NumRatesFlags; + + // NOTE: Since EDK2 does not allow flexible array member [] we declare + // here array of 1 element length. However below is used as a variable + // length array. + CLOCK_RATE_DWORD Rates[1]; +} CLOCK_DESCRIBE_RATES; + +#define CLOCK_SET_DEFAULT_FLAGS 0 + +// Message parameters for CLOCK_RATE_SET command. +typedef struct { + UINT32 Flags; + UINT32 ClockId; + CLOCK_RATE_DWORD Rate; +} CLOCK_RATE_SET_ATTRIBUTES; + +// Message parameters for CLOCK_CONFIG_SET command. +typedef struct { + UINT32 ClockId; + UINT32 Attributes; +} CLOCK_CONFIG_SET_ATTRIBUTES; + +// if ClockAttr Bit[0] is set then clock device is enabled. +#define CLOCK_ENABLE_MASK 0x1 +#define CLOCK_ENABLED(ClockAttr) ((ClockAttr & CLOCK_ENABLE_MASK) == 1) + +typedef struct { + UINT32 Attributes; + UINT8 ClockName[SCMI_MAX_STR_LEN]; +} CLOCK_ATTRIBUTES; + +#pragma pack() + +/** Initialize clock management protocol and install protocol on a given handle. + + @param[in] Handle Handle to install clock management protocol. + + @retval EFI_SUCCESS Clock protocol interface installed successfully. +**/ +EFI_STATUS +ScmiClockProtocolInit ( + IN EFI_HANDLE *Handle + ); + +#endif /* ARM_SCMI_CLOCK_PROTOCOL_PRIVATE_H_ */ diff --git a/ArmPkg/Drivers/ArmScmiDxe/ArmScmiDxe.inf b/ArmPkg/Drivers/ArmScmiDxe/ArmScmiDxe.inf new file mode 100644 index 000000000..fc5f841c2 --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/ArmScmiDxe.inf @@ -0,0 +1,53 @@ +#/** @file +# +# Copyright (c) 2017-2021, Arm Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# System Control and Management Interface V1.0 +# http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ +# DEN0056A_System_Control_and_Management_Interface.pdf +#**/ + +[Defines] + INF_VERSION = 0x00010019 + BASE_NAME = ArmScmiDxe + FILE_GUID = 9585984C-F027-45E9-AFDF-ADAA6DFAAAC7 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = ArmScmiDxeEntryPoint + +[Sources.common] + ArmScmiBaseProtocolPrivate.h + ArmScmiClockProtocolPrivate.h + ArmScmiPerformanceProtocolPrivate.h + ScmiBaseProtocol.c + Scmi.c + ScmiClockProtocol.c + ScmiDxe.c + ScmiDxe.h + ScmiPerformanceProtocol.c + ScmiPrivate.h + +[Packages] + ArmPkg/ArmPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + ArmLib + ArmMtlLib + DebugLib + IoLib + UefiBootServicesTableLib + UefiDriverEntryPoint + +[Protocols] + gArmScmiBaseProtocolGuid + gArmScmiClockProtocolGuid + gArmScmiClock2ProtocolGuid + gArmScmiPerformanceProtocolGuid + +[Depex] + TRUE + diff --git a/ArmPkg/Drivers/ArmScmiDxe/ArmScmiPerformanceProtocolPrivate.h b/ArmPkg/Drivers/ArmScmiDxe/ArmScmiPerformanceProtocolPrivate.h new file mode 100644 index 000000000..3c038f183 --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/ArmScmiPerformanceProtocolPrivate.h @@ -0,0 +1,49 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#ifndef ARM_SCMI_PERFORMANCE_PROTOCOL_PRIVATE_H_ +#define ARM_SCMI_PERFORMANCE_PROTOCOL_PRIVATE_H_ + +#include + +// Number of performance levels returned by a call to the SCP, Lvls Bits[11:0] +#define NUM_PERF_LEVELS_MASK 0x0FFF +#define NUM_PERF_LEVELS(Lvls) (Lvls & NUM_PERF_LEVELS_MASK) + +// Number of performance levels remaining after a call to the SCP, Lvls Bits[31:16] +#define NUM_REMAIN_PERF_LEVELS_SHIFT 16 +#define NUM_REMAIN_PERF_LEVELS(Lvls) (Lvls >> NUM_REMAIN_PERF_LEVELS_SHIFT) + +/** Return values for ScmiMessageIdPerformanceDescribeLevels command. + SCMI Spec section 4.5.2.5 +**/ +typedef struct { + UINT32 NumLevels; + + // NOTE: Since EDK2 does not allow flexible array member [] we declare + // here array of 1 element length. However below is used as a variable + // length array. + SCMI_PERFORMANCE_LEVEL PerfLevel[1]; // Offset to array of performance levels +} PERF_DESCRIBE_LEVELS; + +/** Initialize performance management protocol and install on a given Handle. + + @param[in] Handle Handle to install performance management + protocol. + + @retval EFI_SUCCESS Performance protocol installed successfully. +**/ +EFI_STATUS +ScmiPerformanceProtocolInit ( + IN EFI_HANDLE *Handle + ); + +#endif /* ARM_SCMI_PERFORMANCE_PROTOCOL_PRIVATE_H_ */ diff --git a/ArmPkg/Drivers/ArmScmiDxe/Scmi.c b/ArmPkg/Drivers/ArmScmiDxe/Scmi.c new file mode 100644 index 000000000..fcf4ed496 --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/Scmi.c @@ -0,0 +1,254 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#include +#include +#include +#include + +#include "ScmiPrivate.h" + +// Arbitrary timeout value 20ms. +#define RESPONSE_TIMEOUT 20000 + +/** Return a pointer to the message payload. + + @param[out] Payload Holds pointer to the message payload. + + @retval EFI_SUCCESS Payload holds a valid message payload pointer. + @retval EFI_TIMEOUT Time out error if MTL channel is busy. + @retval EFI_UNSUPPORTED If MTL channel is unsupported. +**/ +EFI_STATUS +ScmiCommandGetPayload ( + OUT UINT32 **Payload + ) +{ + EFI_STATUS Status; + MTL_CHANNEL *Channel; + + // Get handle to the Channel. + Status = MtlGetChannel (MTL_CHANNEL_TYPE_LOW, &Channel); + if (EFI_ERROR (Status)) { + return Status; + } + + // Payload will not be populated until channel is free. + Status = MtlWaitUntilChannelFree (Channel, RESPONSE_TIMEOUT); + if (EFI_ERROR (Status)) { + return Status; + } + + // Get the address of the payload. + *Payload = MtlGetChannelPayload (Channel); + + return EFI_SUCCESS; +} + +/** Execute a SCMI command and receive a response. + + This function uses a MTL channel to transfer message to SCP + and waits for a response. + + @param[in] Command Pointer to the SCMI command (Protocol ID + and Message ID) + + @param[in,out] PayloadLength SCMI command message length. + + @param[out] OPTIONAL ReturnValues Pointer to SCMI response. + + @retval OUT EFI_SUCCESS Command sent and message received successfully. + @retval OUT EFI_UNSUPPORTED Channel not supported. + @retval OUT EFI_TIMEOUT Timeout on the channel. + @retval OUT EFI_DEVICE_ERROR Channel not ready. + @retval OUT EFI_DEVICE_ERROR Message Header corrupted. + @retval OUT EFI_DEVICE_ERROR SCMI error. +**/ +EFI_STATUS +ScmiCommandExecute ( + IN SCMI_COMMAND *Command, + IN OUT UINT32 *PayloadLength, + OUT UINT32 **ReturnValues OPTIONAL + ) +{ + EFI_STATUS Status; + SCMI_MESSAGE_RESPONSE *Response; + UINT32 MessageHeader; + UINT32 ResponseHeader; + MTL_CHANNEL *Channel; + + ASSERT (PayloadLength != NULL); + + Status = MtlGetChannel (MTL_CHANNEL_TYPE_LOW, &Channel); + if (EFI_ERROR (Status)) { + return Status; + } + + // Fill in message header. + MessageHeader = SCMI_MESSAGE_HEADER ( + Command->MessageId, + ScmiMessageTypeCommand, + Command->ProtocolId + ); + + // Send payload using MTL channel. + Status = MtlSendMessage ( + Channel, + MessageHeader, + *PayloadLength + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // Wait for the response on the channel. + Status = MtlReceiveMessage (Channel, &ResponseHeader, PayloadLength); + if (EFI_ERROR (Status)) { + return Status; + } + + // SCMI must return MessageHeader unmodified. + if (MessageHeader != ResponseHeader) { + ASSERT (FALSE); + return EFI_DEVICE_ERROR; + } + + Response = (SCMI_MESSAGE_RESPONSE *)MtlGetChannelPayload (Channel); + + if (Response->Status != ScmiSuccess) { + DEBUG (( + DEBUG_ERROR, + "SCMI error: ProtocolId = 0x%x, MessageId = 0x%x, error = %d\n", + Command->ProtocolId, + Command->MessageId, + Response->Status + )); + + ASSERT (FALSE); + return EFI_DEVICE_ERROR; + } + + if (ReturnValues != NULL) { + *ReturnValues = Response->ReturnValues; + } + + return EFI_SUCCESS; +} + +/** Internal common function useful for common protocol discovery messages. + + @param[in] ProtocolId Protocol Id of the protocol. + @param[in] MessageId Message Id of the message. + + @param[out] ReturnValues SCMI response return values. + + @retval EFI_SUCCESS Success with valid return values. + @retval EFI_DEVICE_ERROR SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +ScmiProtocolDiscoveryCommon ( + IN SCMI_PROTOCOL_ID ProtocolId, + IN SCMI_MESSAGE_ID MessageId, + OUT UINT32 **ReturnValues + ) +{ + SCMI_COMMAND Command; + UINT32 PayloadLength; + + PayloadLength = 0; + Command.ProtocolId = ProtocolId; + Command.MessageId = MessageId; + + return ScmiCommandExecute ( + &Command, + &PayloadLength, + ReturnValues + ); +} + +/** Return protocol version from SCP for a given protocol ID. + + @param[in] Protocol ID Protocol ID. + @param[out] Version Pointer to version of the protocol. + + @retval EFI_SUCCESS Version holds a valid version received + from the SCP. + @retval EFI_DEVICE_ERROR SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +ScmiGetProtocolVersion ( + IN SCMI_PROTOCOL_ID ProtocolId, + OUT UINT32 *Version + ) +{ + EFI_STATUS Status; + UINT32 *ProtocolVersion; + + Status = ScmiProtocolDiscoveryCommon ( + ProtocolId, + ScmiMessageIdProtocolVersion, + (UINT32 **)&ProtocolVersion + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *Version = *ProtocolVersion; + + return EFI_SUCCESS; +} + +/** Return protocol attributes from SCP for a given protocol ID. + + @param[in] Protocol ID Protocol ID. + @param[out] ReturnValues Pointer to attributes of the protocol. + + @retval EFI_SUCCESS ReturnValues points to protocol attributes. + @retval EFI_DEVICE_ERROR SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +ScmiGetProtocolAttributes ( + IN SCMI_PROTOCOL_ID ProtocolId, + OUT UINT32 **ReturnValues + ) +{ + return ScmiProtocolDiscoveryCommon ( + ProtocolId, + ScmiMessageIdProtocolAttributes, + ReturnValues + ); +} + +/** Return protocol message attributes from SCP for a given protocol ID. + + @param[in] Protocol ID Protocol ID. + @param[out] Attributes Pointer to attributes of the protocol. + + @retval EFI_SUCCESS ReturnValues points to protocol message attributes. + @retval EFI_DEVICE_ERROR SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +ScmiGetProtocolMessageAttributes ( + IN SCMI_PROTOCOL_ID ProtocolId, + OUT UINT32 **ReturnValues + ) +{ + return ScmiProtocolDiscoveryCommon ( + ProtocolId, + ScmiMessageIdProtocolMessageAttributes, + ReturnValues + ); +} diff --git a/ArmPkg/Drivers/ArmScmiDxe/ScmiBaseProtocol.c b/ArmPkg/Drivers/ArmScmiDxe/ScmiBaseProtocol.c new file mode 100644 index 000000000..6ab292206 --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/ScmiBaseProtocol.c @@ -0,0 +1,311 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#include +#include +#include +#include + +#include "ArmScmiBaseProtocolPrivate.h" +#include "ScmiPrivate.h" + +/** Return version of the Base protocol supported by SCP firmware. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] Version Version of the supported SCMI Base protocol. + + @retval EFI_SUCCESS The version of the protocol is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +BaseGetVersion ( + IN SCMI_BASE_PROTOCOL *This, + OUT UINT32 *Version + ) +{ + return ScmiGetProtocolVersion (ScmiProtocolIdBase, Version); +} + +/** Return total number of SCMI protocols supported by the SCP firmware. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] TotalProtocols Total number of SCMI protocols supported. + + @retval EFI_SUCCESS Total number of protocols supported are returned. + @retval EFI_DEVICE_ERROR SCP returns a SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +BaseGetTotalProtocols ( + IN SCMI_BASE_PROTOCOL *This, + OUT UINT32 *TotalProtocols + ) +{ + EFI_STATUS Status; + UINT32 *ReturnValues; + + Status = ScmiGetProtocolAttributes (ScmiProtocolIdBase, &ReturnValues); + if (EFI_ERROR (Status)) { + return Status; + } + + *TotalProtocols = SCMI_TOTAL_PROTOCOLS (ReturnValues[0]); + + return EFI_SUCCESS; +} + +/** Common function which returns vendor details. + + @param[in] MessageId ScmiMessageIdBaseDiscoverVendor + OR + ScmiMessageIdBaseDiscoverSubVendor + + @param[out] VendorIdentifier ASCII name of the vendor/subvendor. + + @retval EFI_SUCCESS VendorIdentifier is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +BaseDiscoverVendorDetails ( + IN SCMI_MESSAGE_ID_BASE MessageId, + OUT UINT8 VendorIdentifier[SCMI_MAX_STR_LEN] + ) +{ + EFI_STATUS Status; + UINT32 *ReturnValues; + SCMI_COMMAND Cmd; + UINT32 PayloadLength; + + Cmd.ProtocolId = ScmiProtocolIdBase; + Cmd.MessageId = MessageId; + + PayloadLength = 0; + + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + &ReturnValues + ); + if (EFI_ERROR (Status)) { + return Status; + } + + AsciiStrCpyS ( + (CHAR8 *)VendorIdentifier, + SCMI_MAX_STR_LEN, + (CONST CHAR8 *)ReturnValues + ); + + return EFI_SUCCESS; +} + +/** Return vendor name. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] VendorIdentifier Null terminated ASCII string of up to + 16 bytes with a vendor name. + + @retval EFI_SUCCESS VendorIdentifier is returned. + @retval EFI_DEVICE_ERROR SCP returns a SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +BaseDiscoverVendor ( + IN SCMI_BASE_PROTOCOL *This, + OUT UINT8 VendorIdentifier[SCMI_MAX_STR_LEN] + ) +{ + return BaseDiscoverVendorDetails ( + ScmiMessageIdBaseDiscoverVendor, + VendorIdentifier + ); +} + +/** Return sub vendor name. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] VendorIdentifier Null terminated ASCII string of up to + 16 bytes with a sub vendor name. + + @retval EFI_SUCCESS VendorIdentifier is returned. + @retval EFI_DEVICE_ERROR SCP returns a SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +BaseDiscoverSubVendor ( + IN SCMI_BASE_PROTOCOL *This, + OUT UINT8 VendorIdentifier[SCMI_MAX_STR_LEN] + ) +{ + return BaseDiscoverVendorDetails ( + ScmiMessageIdBaseDiscoverSubVendor, + VendorIdentifier + ); +} + +/** Return implementation version. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] ImplementationVersion Vendor specific implementation version. + + @retval EFI_SUCCESS Implementation version is returned. + @retval EFI_DEVICE_ERROR SCP returns a SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +BaseDiscoverImplVersion ( + IN SCMI_BASE_PROTOCOL *This, + OUT UINT32 *ImplementationVersion + ) +{ + EFI_STATUS Status; + UINT32 *ReturnValues; + SCMI_COMMAND Cmd; + UINT32 PayloadLength; + + Cmd.ProtocolId = ScmiProtocolIdBase; + Cmd.MessageId = ScmiMessageIdBaseDiscoverImplementationVersion; + + PayloadLength = 0; + + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + &ReturnValues + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *ImplementationVersion = ReturnValues[0]; + + return EFI_SUCCESS; +} + +/** Return list of protocols. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] ProtocolListSize Size of the ProtocolList. + + @param[out] ProtocolList Protocol list. + + @retval EFI_SUCCESS List of protocols is returned. + @retval EFI_BUFFER_TOO_SMALL ProtocolListSize is too small for the result. + It has been updated to the size needed. + @retval EFI_DEVICE_ERROR SCP returns a SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +BaseDiscoverListProtocols ( + IN SCMI_BASE_PROTOCOL *This, + IN OUT UINT32 *ProtocolListSize, + OUT UINT8 *ProtocolList + ) +{ + EFI_STATUS Status; + UINT32 TotalProtocols; + UINT32 *MessageParams; + BASE_DISCOVER_LIST *DiscoverList; + UINT32 Skip; + UINT32 Index; + SCMI_COMMAND Cmd; + UINT32 PayloadLength; + UINT32 RequiredSize; + + Status = BaseGetTotalProtocols (This, &TotalProtocols); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = ScmiCommandGetPayload (&MessageParams); + if (EFI_ERROR (Status)) { + return Status; + } + + RequiredSize = sizeof (UINT8) * TotalProtocols; + if (*ProtocolListSize < RequiredSize) { + *ProtocolListSize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + + Cmd.ProtocolId = ScmiProtocolIdBase; + Cmd.MessageId = ScmiMessageIdBaseDiscoverListProtocols; + + Skip = 0; + + while (Skip < TotalProtocols) { + *MessageParams = Skip; + + // Note PayloadLength is a IN/OUT parameter. + PayloadLength = sizeof (Skip); + + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + (UINT32 **)&DiscoverList + ); + if (EFI_ERROR (Status)) { + return Status; + } + + for (Index = 0; Index < DiscoverList->NumProtocols; Index++) { + ProtocolList[Skip++] = DiscoverList->Protocols[Index]; + } + } + + *ProtocolListSize = RequiredSize; + + return EFI_SUCCESS; +} + +// Instance of the SCMI Base protocol. +STATIC CONST SCMI_BASE_PROTOCOL BaseProtocol = { + BaseGetVersion, + BaseGetTotalProtocols, + BaseDiscoverVendor, + BaseDiscoverSubVendor, + BaseDiscoverImplVersion, + BaseDiscoverListProtocols +}; + +/** Initialize Base protocol and install protocol on a given handle. + + @param[in] Handle Handle to install Base protocol. + + @retval EFI_SUCCESS Base protocol interface installed + successfully. +**/ +EFI_STATUS +ScmiBaseProtocolInit ( + IN OUT EFI_HANDLE *Handle + ) +{ + return gBS->InstallMultipleProtocolInterfaces ( + Handle, + &gArmScmiBaseProtocolGuid, + &BaseProtocol, + NULL + ); +} diff --git a/ArmPkg/Drivers/ArmScmiDxe/ScmiClockProtocol.c b/ArmPkg/Drivers/ArmScmiDxe/ScmiClockProtocol.c new file mode 100644 index 000000000..12a7e6df5 --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/ScmiClockProtocol.c @@ -0,0 +1,480 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#include +#include +#include +#include +#include + +#include "ArmScmiClockProtocolPrivate.h" +#include "ScmiPrivate.h" + +/** Convert to 64 bit value from two 32 bit words. + + @param[in] Low Lower 32 bits. + @param[in] High Higher 32 bits. + + @retval UINT64 64 bit value. +**/ +STATIC +UINT64 +ConvertTo64Bit ( + IN UINT32 Low, + IN UINT32 High + ) +{ + return (Low | ((UINT64)High << 32)); +} + +/** Return version of the clock management protocol supported by SCP firmware. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + + @param[out] Version Version of the supported SCMI Clock management protocol. + + @retval EFI_SUCCESS The version is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +ClockGetVersion ( + IN SCMI_CLOCK_PROTOCOL *This, + OUT UINT32 *Version + ) +{ + return ScmiGetProtocolVersion (ScmiProtocolIdClock, Version); +} + +/** Return total number of clock devices supported by the clock management + protocol. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + + @param[out] TotalClocks Total number of clocks supported. + + @retval EFI_SUCCESS Total number of clocks supported is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +ClockGetTotalClocks ( + IN SCMI_CLOCK_PROTOCOL *This, + OUT UINT32 *TotalClocks + ) +{ + EFI_STATUS Status; + UINT32 *ReturnValues; + + Status = ScmiGetProtocolAttributes (ScmiProtocolIdClock, &ReturnValues); + if (EFI_ERROR (Status)) { + return Status; + } + + *TotalClocks = SCMI_CLOCK_PROTOCOL_TOTAL_CLKS (ReturnValues[0]); + + return EFI_SUCCESS; +} + +/** Return attributes of a clock device. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + + @param[out] Enabled If TRUE, the clock device is enabled. + @param[out] ClockAsciiName A NULL terminated ASCII string with the clock + name, of up to 16 bytes. + + @retval EFI_SUCCESS Clock device attributes are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +ClockGetClockAttributes ( + IN SCMI_CLOCK_PROTOCOL *This, + IN UINT32 ClockId, + OUT BOOLEAN *Enabled, + OUT CHAR8 *ClockAsciiName + ) +{ + EFI_STATUS Status; + + UINT32 *MessageParams; + CLOCK_ATTRIBUTES *ClockAttributes; + SCMI_COMMAND Cmd; + UINT32 PayloadLength; + + Status = ScmiCommandGetPayload (&MessageParams); + if (EFI_ERROR (Status)) { + return Status; + } + + *MessageParams = ClockId; + + Cmd.ProtocolId = ScmiProtocolIdClock; + Cmd.MessageId = ScmiMessageIdClockAttributes; + + PayloadLength = sizeof (ClockId); + + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + (UINT32 **)&ClockAttributes + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // TRUE if bit 0 of ClockAttributes->Attributes is set. + *Enabled = CLOCK_ENABLED (ClockAttributes->Attributes); + + AsciiStrCpyS ( + ClockAsciiName, + SCMI_MAX_STR_LEN, + (CONST CHAR8 *)ClockAttributes->ClockName + ); + + return EFI_SUCCESS; +} + +/** Return list of rates supported by a given clock device. + + @param[in] This A pointer to SCMI_CLOCK_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + + @param[out] Format ScmiClockRateFormatDiscrete: Clock device + supports range of clock rates which are non-linear. + + ScmiClockRateFormatLinear: Clock device supports + range of linear clock rates from Min to Max in steps. + + @param[out] TotalRates Total number of rates. + + @param[in,out] RateArraySize Size of the RateArray. + + @param[out] RateArray List of clock rates. + + @retval EFI_SUCCESS List of clock rates is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval EFI_BUFFER_TOO_SMALL RateArraySize is too small for the result. + It has been updated to the size needed. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +ClockDescribeRates ( + IN SCMI_CLOCK_PROTOCOL *This, + IN UINT32 ClockId, + OUT SCMI_CLOCK_RATE_FORMAT *Format, + OUT UINT32 *TotalRates, + IN OUT UINT32 *RateArraySize, + OUT SCMI_CLOCK_RATE *RateArray + ) +{ + EFI_STATUS Status; + + UINT32 PayloadLength; + SCMI_COMMAND Cmd; + UINT32 *MessageParams; + CLOCK_DESCRIBE_RATES *DescribeRates; + CLOCK_RATE_DWORD *Rate; + + UINT32 RequiredArraySize; + UINT32 RateIndex; + UINT32 RateNo; + UINT32 RateOffset; + + *TotalRates = 0; + RequiredArraySize = 0; + RateIndex = 0; + + Status = ScmiCommandGetPayload (&MessageParams); + if (EFI_ERROR (Status)) { + return Status; + } + + Cmd.ProtocolId = ScmiProtocolIdClock; + Cmd.MessageId = ScmiMessageIdClockDescribeRates; + + *MessageParams++ = ClockId; + + do { + *MessageParams = RateIndex; + + // Set Payload length, note PayloadLength is a IN/OUT parameter. + PayloadLength = sizeof (ClockId) + sizeof (RateIndex); + + // Execute and wait for response on a SCMI channel. + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + (UINT32 **)&DescribeRates + ); + if (EFI_ERROR (Status)) { + return Status; + } + + if (*TotalRates == 0) { + // In the first iteration we will get number of returned rates and number + // of remaining rates. With this information calculate required size + // for rate array. If provided RateArraySize is less, return an + // error. + + *Format = RATE_FORMAT (DescribeRates->NumRatesFlags); + + *TotalRates = NUM_RATES (DescribeRates->NumRatesFlags) + + NUM_REMAIN_RATES (DescribeRates->NumRatesFlags); + + if (*Format == ScmiClockRateFormatDiscrete) { + RequiredArraySize = (*TotalRates) * sizeof (UINT64); + } else { + // We need to return triplet of 64 bit value for each rate + RequiredArraySize = (*TotalRates) * 3 * sizeof (UINT64); + } + + if (RequiredArraySize > (*RateArraySize)) { + *RateArraySize = RequiredArraySize; + return EFI_BUFFER_TOO_SMALL; + } + } + + RateOffset = 0; + + if (*Format == ScmiClockRateFormatDiscrete) { + for (RateNo = 0; RateNo < NUM_RATES (DescribeRates->NumRatesFlags); RateNo++) { + Rate = &DescribeRates->Rates[RateOffset++]; + // Non-linear discrete rates. + RateArray[RateIndex++].DiscreteRate.Rate = + ConvertTo64Bit (Rate->Low, Rate->High); + } + } else { + for (RateNo = 0; RateNo < NUM_RATES (DescribeRates->NumRatesFlags); RateNo++) { + // Linear clock rates from minimum to maximum in steps + // Minimum clock rate. + Rate = &DescribeRates->Rates[RateOffset++]; + RateArray[RateIndex].ContinuousRate.Min = + ConvertTo64Bit (Rate->Low, Rate->High); + + Rate = &DescribeRates->Rates[RateOffset++]; + // Maximum clock rate. + RateArray[RateIndex].ContinuousRate.Max = + ConvertTo64Bit (Rate->Low, Rate->High); + + Rate = &DescribeRates->Rates[RateOffset++]; + // Step. + RateArray[RateIndex++].ContinuousRate.Step = + ConvertTo64Bit (Rate->Low, Rate->High); + } + } + } while (NUM_REMAIN_RATES (DescribeRates->NumRatesFlags) != 0); + + // Update RateArraySize with RequiredArraySize. + *RateArraySize = RequiredArraySize; + + return EFI_SUCCESS; +} + +/** Get clock rate. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + + @param[out] Rate Clock rate. + + @retval EFI_SUCCESS Clock rate is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +ClockRateGet ( + IN SCMI_CLOCK_PROTOCOL *This, + IN UINT32 ClockId, + OUT UINT64 *Rate + ) +{ + EFI_STATUS Status; + + UINT32 *MessageParams; + CLOCK_RATE_DWORD *ClockRate; + SCMI_COMMAND Cmd; + + UINT32 PayloadLength; + + Status = ScmiCommandGetPayload (&MessageParams); + if (EFI_ERROR (Status)) { + return Status; + } + + // Fill arguments for clock protocol command. + *MessageParams = ClockId; + + Cmd.ProtocolId = ScmiProtocolIdClock; + Cmd.MessageId = ScmiMessageIdClockRateGet; + + PayloadLength = sizeof (ClockId); + + // Execute and wait for response on a SCMI channel. + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + (UINT32 **)&ClockRate + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *Rate = ConvertTo64Bit (ClockRate->Low, ClockRate->High); + + return EFI_SUCCESS; +} + +/** Set clock rate. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + @param[in] Rate Clock rate. + + @retval EFI_SUCCESS Clock rate set success. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +ClockRateSet ( + IN SCMI_CLOCK_PROTOCOL *This, + IN UINT32 ClockId, + IN UINT64 Rate + ) +{ + EFI_STATUS Status; + CLOCK_RATE_SET_ATTRIBUTES *ClockRateSetAttributes; + SCMI_COMMAND Cmd; + UINT32 PayloadLength; + + Status = ScmiCommandGetPayload ((UINT32 **)&ClockRateSetAttributes); + if (EFI_ERROR (Status)) { + return Status; + } + + // Fill arguments for clock protocol command. + ClockRateSetAttributes->ClockId = ClockId; + ClockRateSetAttributes->Flags = CLOCK_SET_DEFAULT_FLAGS; + ClockRateSetAttributes->Rate.Low = (UINT32)Rate; + ClockRateSetAttributes->Rate.High = (UINT32)(Rate >> 32); + + Cmd.ProtocolId = ScmiProtocolIdClock; + Cmd.MessageId = ScmiMessageIdClockRateSet; + + PayloadLength = sizeof (CLOCK_RATE_SET_ATTRIBUTES); + + // Execute and wait for response on a SCMI channel. + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + NULL + ); + + return Status; +} + +/** Enable/Disable specified clock. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + @param[in] Enable TRUE to enable, FALSE to disable. + + @retval EFI_SUCCESS Clock enable/disable successful. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +ClockEnable ( + IN SCMI_CLOCK2_PROTOCOL *This, + IN UINT32 ClockId, + IN BOOLEAN Enable + ) +{ + EFI_STATUS Status; + CLOCK_CONFIG_SET_ATTRIBUTES *ClockConfigSetAttributes; + SCMI_COMMAND Cmd; + UINT32 PayloadLength; + + Status = ScmiCommandGetPayload ((UINT32 **)&ClockConfigSetAttributes); + if (EFI_ERROR (Status)) { + return Status; + } + + // Fill arguments for clock protocol command. + ClockConfigSetAttributes->ClockId = ClockId; + ClockConfigSetAttributes->Attributes = Enable ? BIT0 : 0; + + Cmd.ProtocolId = ScmiProtocolIdClock; + Cmd.MessageId = ScmiMessageIdClockConfigSet; + + PayloadLength = sizeof (CLOCK_CONFIG_SET_ATTRIBUTES); + + // Execute and wait for response on a SCMI channel. + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + NULL + ); + + return Status; +} + +// Instance of the SCMI clock management protocol. +STATIC CONST SCMI_CLOCK_PROTOCOL ScmiClockProtocol = { + ClockGetVersion, + ClockGetTotalClocks, + ClockGetClockAttributes, + ClockDescribeRates, + ClockRateGet, + ClockRateSet +}; + +// Instance of the SCMI clock management protocol. +STATIC CONST SCMI_CLOCK2_PROTOCOL ScmiClock2Protocol = { + (SCMI_CLOCK2_GET_VERSION)ClockGetVersion, + (SCMI_CLOCK2_GET_TOTAL_CLOCKS)ClockGetTotalClocks, + (SCMI_CLOCK2_GET_CLOCK_ATTRIBUTES)ClockGetClockAttributes, + (SCMI_CLOCK2_DESCRIBE_RATES)ClockDescribeRates, + (SCMI_CLOCK2_RATE_GET)ClockRateGet, + (SCMI_CLOCK2_RATE_SET)ClockRateSet, + SCMI_CLOCK2_PROTOCOL_VERSION, + ClockEnable +}; + +/** Initialize clock management protocol and install protocol on a given handle. + + @param[in] Handle Handle to install clock management protocol. + + @retval EFI_SUCCESS Clock protocol interface installed successfully. +**/ +EFI_STATUS +ScmiClockProtocolInit ( + IN EFI_HANDLE *Handle + ) +{ + return gBS->InstallMultipleProtocolInterfaces ( + Handle, + &gArmScmiClockProtocolGuid, + &ScmiClockProtocol, + &gArmScmiClock2ProtocolGuid, + &ScmiClock2Protocol, + NULL + ); +} diff --git a/ArmPkg/Drivers/ArmScmiDxe/ScmiDxe.c b/ArmPkg/Drivers/ArmScmiDxe/ScmiDxe.c new file mode 100644 index 000000000..1fc448b9b --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/ScmiDxe.c @@ -0,0 +1,152 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Specification Reference: + - Arm System Control and Management Interface - Platform Design Document + (https://developer.arm.com/documentation/den0056/) +**/ + +#include +#include +#include +#include +#include +#include +#include + +#include "ArmScmiBaseProtocolPrivate.h" +#include "ArmScmiClockProtocolPrivate.h" +#include "ArmScmiPerformanceProtocolPrivate.h" +#include "ScmiDxe.h" +#include "ScmiPrivate.h" + +STATIC CONST SCMI_PROTOCOL_ENTRY Protocols[] = { + { ScmiProtocolIdBase, ScmiBaseProtocolInit }, + { ScmiProtocolIdPerformance, ScmiPerformanceProtocolInit }, + { ScmiProtocolIdClock, ScmiClockProtocolInit } +}; + +/** ARM SCMI driver entry point function. + + This function installs the SCMI Base protocol and a list of other + protocols is queried using the Base protocol. If protocol is supported, + driver will call each protocol init function to install the protocol on + the ImageHandle. + + @param[in] ImageHandle Handle to this EFI Image which will be used to + install Base, Clock and Performance protocols. + @param[in] SystemTable A pointer to boot time system table. + + @retval EFI_SUCCESS Driver initalized successfully. + @retval EFI_UNSUPPORTED If SCMI base protocol version is not supported. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +EFIAPI +ArmScmiDxeEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + SCMI_BASE_PROTOCOL *BaseProtocol; + UINT32 Version; + UINT32 Index; + UINT32 NumProtocols; + UINT32 ProtocolIndex; + UINT8 *SupportedList; + UINT32 SupportedListSize; + + // Every SCMI implementation must implement the base protocol. + ASSERT (Protocols[0].Id == ScmiProtocolIdBase); + + Status = ScmiBaseProtocolInit (&ImageHandle); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return Status; + } + + Status = gBS->LocateProtocol ( + &gArmScmiBaseProtocolGuid, + NULL, + (VOID **)&BaseProtocol + ); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return Status; + } + + // Get SCMI Base protocol version. + Status = BaseProtocol->GetVersion (BaseProtocol, &Version); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return Status; + } + + // Accept any version between SCMI v1.0 and SCMI v2.0 + if ((Version < BASE_PROTOCOL_VERSION_V1) || + (Version > BASE_PROTOCOL_VERSION_V2)) + { + ASSERT (FALSE); + return EFI_UNSUPPORTED; + } + + // Apart from Base protocol, SCMI may implement various other protocols, + // query total protocols implemented by the SCP firmware. + NumProtocols = 0; + Status = BaseProtocol->GetTotalProtocols (BaseProtocol, &NumProtocols); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return Status; + } + + ASSERT (NumProtocols != 0); + + SupportedListSize = (NumProtocols * sizeof (*SupportedList)); + + Status = gBS->AllocatePool ( + EfiBootServicesData, + SupportedListSize, + (VOID **)&SupportedList + ); + if (EFI_ERROR (Status)) { + ASSERT (FALSE); + return Status; + } + + // Get the list of protocols supported by SCP firmware on the platform. + Status = BaseProtocol->DiscoverListProtocols ( + BaseProtocol, + &SupportedListSize, + SupportedList + ); + if (EFI_ERROR (Status)) { + gBS->FreePool (SupportedList); + ASSERT (FALSE); + return Status; + } + + // Install supported protocol on ImageHandle. + for (ProtocolIndex = 1; ProtocolIndex < ARRAY_SIZE (Protocols); + ProtocolIndex++) + { + for (Index = 0; Index < NumProtocols; Index++) { + if (Protocols[ProtocolIndex].Id == SupportedList[Index]) { + Status = Protocols[ProtocolIndex].InitFn (&ImageHandle); + if (EFI_ERROR (Status)) { + ASSERT_EFI_ERROR (Status); + return Status; + } + + break; + } + } + } + + gBS->FreePool (SupportedList); + + return EFI_SUCCESS; +} diff --git a/ArmPkg/Drivers/ArmScmiDxe/ScmiDxe.h b/ArmPkg/Drivers/ArmScmiDxe/ScmiDxe.h new file mode 100644 index 000000000..a6f87a34d --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/ScmiDxe.h @@ -0,0 +1,37 @@ +/** @file + + Copyright (c) 2017-2018, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#ifndef SCMI_DXE_H_ +#define SCMI_DXE_H_ + +#include "ScmiPrivate.h" + +#define MAX_VENDOR_LEN SCMI_MAX_STR_LEN + +/** Pointer to protocol initialization function. + + @param[in] Handle A pointer to the EFI_HANDLE on which the protocol + interface is to be installed. + + @retval EFI_SUCCESS Protocol interface installed successfully. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_PROTOCOL_INIT_FXN)( + IN EFI_HANDLE *Handle + ); + +typedef struct { + SCMI_PROTOCOL_ID Id; // Protocol Id. + SCMI_PROTOCOL_INIT_FXN InitFn; // Protocol init function. +} SCMI_PROTOCOL_ENTRY; + +#endif /* SCMI_DXE_H_ */ diff --git a/ArmPkg/Drivers/ArmScmiDxe/ScmiPerformanceProtocol.c b/ArmPkg/Drivers/ArmScmiDxe/ScmiPerformanceProtocol.c new file mode 100644 index 000000000..0f89808fb --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/ScmiPerformanceProtocol.c @@ -0,0 +1,449 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#include +#include +#include + +#include "ArmScmiPerformanceProtocolPrivate.h" +#include "ScmiPrivate.h" + +/** Return version of the performance management protocol supported by SCP. + firmware. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + + @param[out] Version Version of the supported SCMI performance management + protocol. + + @retval EFI_SUCCESS The version is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +PerformanceGetVersion ( + IN SCMI_PERFORMANCE_PROTOCOL *This, + OUT UINT32 *Version + ) +{ + return ScmiGetProtocolVersion (ScmiProtocolIdPerformance, Version); +} + +/** Return protocol attributes of the performance management protocol. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + + @param[out] Attributes Protocol attributes. + + @retval EFI_SUCCESS Protocol attributes are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +PerformanceGetAttributes ( + IN SCMI_PERFORMANCE_PROTOCOL *This, + OUT SCMI_PERFORMANCE_PROTOCOL_ATTRIBUTES *Attributes + ) +{ + EFI_STATUS Status; + UINT32 *ReturnValues; + + Status = ScmiGetProtocolAttributes ( + ScmiProtocolIdPerformance, + &ReturnValues + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem ( + Attributes, + ReturnValues, + sizeof (SCMI_PERFORMANCE_PROTOCOL_ATTRIBUTES) + ); + + return EFI_SUCCESS; +} + +/** Return performance domain attributes. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + + @param[out] Attributes Performance domain attributes. + + @retval EFI_SUCCESS Domain attributes are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +PerformanceDomainAttributes ( + IN SCMI_PERFORMANCE_PROTOCOL *This, + IN UINT32 DomainId, + OUT SCMI_PERFORMANCE_DOMAIN_ATTRIBUTES *DomainAttributes + ) +{ + EFI_STATUS Status; + UINT32 *MessageParams; + UINT32 *ReturnValues; + UINT32 PayloadLength; + SCMI_COMMAND Cmd; + + Status = ScmiCommandGetPayload (&MessageParams); + if (EFI_ERROR (Status)) { + return Status; + } + + *MessageParams = DomainId; + + Cmd.ProtocolId = ScmiProtocolIdPerformance; + Cmd.MessageId = ScmiMessageIdPerformanceDomainAttributes; + + PayloadLength = sizeof (DomainId); + + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + &ReturnValues + ); + if (EFI_ERROR (Status)) { + return Status; + } + + CopyMem ( + DomainAttributes, + ReturnValues, + sizeof (SCMI_PERFORMANCE_DOMAIN_ATTRIBUTES) + ); + + return EFI_SUCCESS; +} + +/** Return list of performance domain levels of a given domain. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + + @param[out] NumLevels Total number of levels a domain can support. + + @param[in,out] LevelArraySize Size of the performance level array. + + @param[out] LevelArray Array of the performance levels. + + @retval EFI_SUCCESS Domain levels are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval EFI_BUFFER_TOO_SMALL LevelArraySize is too small for the result. + It has been updated to the size needed. + @retval !(EFI_SUCCESS) Other errors. +**/ +STATIC +EFI_STATUS +PerformanceDescribeLevels ( + IN SCMI_PERFORMANCE_PROTOCOL *This, + IN UINT32 DomainId, + OUT UINT32 *NumLevels, + IN OUT UINT32 *LevelArraySize, + OUT SCMI_PERFORMANCE_LEVEL *LevelArray + ) +{ + EFI_STATUS Status; + UINT32 PayloadLength; + SCMI_COMMAND Cmd; + UINT32 *MessageParams; + UINT32 LevelIndex; + UINT32 RequiredSize; + UINT32 LevelNo; + UINT32 ReturnNumLevels; + UINT32 ReturnRemainNumLevels; + + PERF_DESCRIBE_LEVELS *Levels; + + Status = ScmiCommandGetPayload (&MessageParams); + if (EFI_ERROR (Status)) { + return Status; + } + + LevelIndex = 0; + RequiredSize = 0; + + *MessageParams++ = DomainId; + + Cmd.ProtocolId = ScmiProtocolIdPerformance; + Cmd.MessageId = ScmiMessageIdPerformanceDescribeLevels; + + do { + *MessageParams = LevelIndex; + + // Note, PayloadLength is an IN/OUT parameter. + PayloadLength = sizeof (DomainId) + sizeof (LevelIndex); + + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + (UINT32 **)&Levels + ); + if (EFI_ERROR (Status)) { + return Status; + } + + ReturnNumLevels = NUM_PERF_LEVELS (Levels->NumLevels); + ReturnRemainNumLevels = NUM_REMAIN_PERF_LEVELS (Levels->NumLevels); + + if (RequiredSize == 0) { + *NumLevels = ReturnNumLevels + ReturnRemainNumLevels; + + RequiredSize = (*NumLevels) * sizeof (SCMI_PERFORMANCE_LEVEL); + if (RequiredSize > (*LevelArraySize)) { + // Update LevelArraySize with required size. + *LevelArraySize = RequiredSize; + return EFI_BUFFER_TOO_SMALL; + } + } + + for (LevelNo = 0; LevelNo < ReturnNumLevels; LevelNo++) { + CopyMem ( + &LevelArray[LevelIndex++], + &Levels->PerfLevel[LevelNo], + sizeof (SCMI_PERFORMANCE_LEVEL) + ); + } + } while (ReturnRemainNumLevels != 0); + + *LevelArraySize = RequiredSize; + + return EFI_SUCCESS; +} + +/** Set performance limits of a domain. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + @param[in] Limit Performance limit to set. + + @retval EFI_SUCCESS Performance limits set successfully. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +PerformanceLimitsSet ( + IN SCMI_PERFORMANCE_PROTOCOL *This, + IN UINT32 DomainId, + IN SCMI_PERFORMANCE_LIMITS *Limits + ) +{ + EFI_STATUS Status; + UINT32 PayloadLength; + SCMI_COMMAND Cmd; + UINT32 *MessageParams; + + Status = ScmiCommandGetPayload (&MessageParams); + if (EFI_ERROR (Status)) { + return Status; + } + + *MessageParams++ = DomainId; + *MessageParams++ = Limits->RangeMax; + *MessageParams = Limits->RangeMin; + + Cmd.ProtocolId = ScmiProtocolIdPerformance; + Cmd.MessageId = ScmiMessageIdPerformanceLimitsSet; + + PayloadLength = sizeof (DomainId) + sizeof (SCMI_PERFORMANCE_LIMITS); + + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + NULL + ); + + return Status; +} + +/** Get performance limits of a domain. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + + @param[out] Limit Performance Limits of the domain. + + @retval EFI_SUCCESS Performance limits are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +PerformanceLimitsGet ( + SCMI_PERFORMANCE_PROTOCOL *This, + UINT32 DomainId, + SCMI_PERFORMANCE_LIMITS *Limits + ) +{ + EFI_STATUS Status; + UINT32 PayloadLength; + SCMI_COMMAND Cmd; + UINT32 *MessageParams; + + SCMI_PERFORMANCE_LIMITS *ReturnValues; + + Status = ScmiCommandGetPayload (&MessageParams); + if (EFI_ERROR (Status)) { + return Status; + } + + *MessageParams = DomainId; + + Cmd.ProtocolId = ScmiProtocolIdPerformance; + Cmd.MessageId = ScmiMessageIdPerformanceLimitsGet; + + PayloadLength = sizeof (DomainId); + + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + (UINT32 **)&ReturnValues + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Limits->RangeMax = ReturnValues->RangeMax; + Limits->RangeMin = ReturnValues->RangeMin; + + return EFI_SUCCESS; +} + +/** Set performance level of a domain. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + @param[in] Level Performance level of the domain. + + @retval EFI_SUCCESS Performance level set successfully. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +PerformanceLevelSet ( + IN SCMI_PERFORMANCE_PROTOCOL *This, + IN UINT32 DomainId, + IN UINT32 Level + ) +{ + EFI_STATUS Status; + UINT32 PayloadLength; + SCMI_COMMAND Cmd; + UINT32 *MessageParams; + + Status = ScmiCommandGetPayload (&MessageParams); + if (EFI_ERROR (Status)) { + return Status; + } + + *MessageParams++ = DomainId; + *MessageParams = Level; + + Cmd.ProtocolId = ScmiProtocolIdPerformance; + Cmd.MessageId = ScmiMessageIdPerformanceLevelSet; + + PayloadLength = sizeof (DomainId) + sizeof (Level); + + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + NULL + ); + + return Status; +} + +/** Get performance level of a domain. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + + @param[out] Level Performance level of the domain. + + @retval EFI_SUCCESS Performance level got successfully. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +PerformanceLevelGet ( + IN SCMI_PERFORMANCE_PROTOCOL *This, + IN UINT32 DomainId, + OUT UINT32 *Level + ) +{ + EFI_STATUS Status; + UINT32 PayloadLength; + SCMI_COMMAND Cmd; + UINT32 *ReturnValues; + UINT32 *MessageParams; + + Status = ScmiCommandGetPayload (&MessageParams); + if (EFI_ERROR (Status)) { + return Status; + } + + *MessageParams = DomainId; + + Cmd.ProtocolId = ScmiProtocolIdPerformance; + Cmd.MessageId = ScmiMessageIdPerformanceLevelGet; + + PayloadLength = sizeof (DomainId); + + Status = ScmiCommandExecute ( + &Cmd, + &PayloadLength, + &ReturnValues + ); + if (EFI_ERROR (Status)) { + return Status; + } + + *Level = *ReturnValues; + + return EFI_SUCCESS; +} + +// Instance of the SCMI performance management protocol. +STATIC CONST SCMI_PERFORMANCE_PROTOCOL PerformanceProtocol = { + PerformanceGetVersion, + PerformanceGetAttributes, + PerformanceDomainAttributes, + PerformanceDescribeLevels, + PerformanceLimitsSet, + PerformanceLimitsGet, + PerformanceLevelSet, + PerformanceLevelGet +}; + +/** Initialize performance management protocol and install on a given Handle. + + @param[in] Handle Handle to install performance management + protocol. + + @retval EFI_SUCCESS Performance protocol installed successfully. +**/ +EFI_STATUS +ScmiPerformanceProtocolInit ( + IN EFI_HANDLE *Handle + ) +{ + return gBS->InstallMultipleProtocolInterfaces ( + Handle, + &gArmScmiPerformanceProtocolGuid, + &PerformanceProtocol, + NULL + ); +} diff --git a/ArmPkg/Drivers/ArmScmiDxe/ScmiPrivate.h b/ArmPkg/Drivers/ArmScmiDxe/ScmiPrivate.h new file mode 100644 index 000000000..c041d1ed0 --- /dev/null +++ b/ArmPkg/Drivers/ArmScmiDxe/ScmiPrivate.h @@ -0,0 +1,169 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#ifndef SCMI_PRIVATE_H_ +#define SCMI_PRIVATE_H_ + +// SCMI protocol IDs. +typedef enum { + ScmiProtocolIdBase = 0x10, + ScmiProtocolIdPowerDomain = 0x11, + ScmiProtocolIdSystemPower = 0x12, + ScmiProtocolIdPerformance = 0x13, + ScmiProtocolIdClock = 0x14, + ScmiProtocolIdSensor = 0x15 +} SCMI_PROTOCOL_ID; + +// SCMI message types. +typedef enum { + ScmiMessageTypeCommand = 0, + ScmiMessageTypeDelayedResponse = 2, // Skipping 1 is deliberate. + ScmiMessageTypeNotification = 3 +} SCMI_MESSAGE_TYPE; + +// SCMI response error codes. +typedef enum { + ScmiSuccess = 0, + ScmiNotSupported = -1, + ScmiInvalidParameters = -2, + ScmiDenied = -3, + ScmiNotFound = -4, + ScmiOutOfRange = -5, + ScmiBusy = -6, + ScmiCommsError = -7, + ScmiGenericError = -8, + ScmiHardwareError = -9, + ScmiProtocolError = -10 +} SCMI_STATUS; + +// SCMI message IDs common to all protocols. +typedef enum { + ScmiMessageIdProtocolVersion = 0x0, + ScmiMessageIdProtocolAttributes = 0x1, + ScmiMessageIdProtocolMessageAttributes = 0x2 +} SCMI_MESSAGE_ID; + +// Not defined in SCMI specification but will help to identify a message. +typedef struct { + SCMI_PROTOCOL_ID ProtocolId; + UINT32 MessageId; +} SCMI_COMMAND; + +#pragma pack(1) + +// Response to a SCMI command. +typedef struct { + INT32 Status; + UINT32 ReturnValues[]; +} SCMI_MESSAGE_RESPONSE; + +// Message header. MsgId[7:0], MsgType[9:8], ProtocolId[17:10] +#define MESSAGE_TYPE_SHIFT 8 +#define PROTOCOL_ID_SHIFT 10 +#define SCMI_MESSAGE_HEADER(MsgId, MsgType, ProtocolId) ( \ + MsgType << MESSAGE_TYPE_SHIFT | \ + ProtocolId << PROTOCOL_ID_SHIFT | \ + MsgId \ + ) +// SCMI message header. +typedef struct { + UINT32 MessageHeader; +} SCMI_MESSAGE_HEADER; + +#pragma pack() + +/** Return a pointer to the message payload. + + @param[out] Payload Holds pointer to the message payload. + + @retval EFI_SUCCESS Payload holds a valid message payload pointer. + @retval EFI_TIMEOUT Time out error if MTL channel is busy. + @retval EFI_UNSUPPORTED If MTL channel is unsupported. +**/ +EFI_STATUS +ScmiCommandGetPayload ( + OUT UINT32 **Payload + ); + +/** Execute a SCMI command and receive a response. + + This function uses a MTL channel to transfer message to SCP + and waits for a response. + + @param[in] Command Pointer to the SCMI command (Protocol ID + and Message ID) + + @param[in,out] PayloadLength SCMI command message length. + + @param[out] OPTIONAL ReturnValues Pointer to SCMI response. + + @retval OUT EFI_SUCCESS Command sent and message received successfully. + @retval OUT EFI_UNSUPPORTED Channel not supported. + @retval OUT EFI_TIMEOUT Timeout on the channel. + @retval OUT EFI_DEVICE_ERROR Channel not ready. + @retval OUT EFI_DEVICE_ERROR Message Header corrupted. + @retval OUT EFI_DEVICE_ERROR SCMI error. +**/ +EFI_STATUS +ScmiCommandExecute ( + IN SCMI_COMMAND *Command, + IN OUT UINT32 *PayloadLength, + OUT UINT32 **ReturnValues OPTIONAL + ); + +/** Return protocol version from SCP for a given protocol ID. + + @param[in] Protocol ID Protocol ID. + @param[out] Version Pointer to version of the protocol. + + @retval EFI_SUCCESS Version holds a valid version received + from the SCP. + @retval EFI_DEVICE_ERROR SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +ScmiGetProtocolVersion ( + IN SCMI_PROTOCOL_ID ProtocolId, + OUT UINT32 *Version + ); + +/** Return protocol attributes from SCP for a given protocol ID. + + @param[in] Protocol ID Protocol ID. + @param[out] ReturnValues Pointer to attributes of the protocol. + + @retval EFI_SUCCESS ReturnValues points to protocol attributes. + @retval EFI_DEVICE_ERROR SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +ScmiGetProtocolAttributes ( + IN SCMI_PROTOCOL_ID ProtocolId, + OUT UINT32 **ReturnValues + ); + +/** Return protocol message attributes from SCP for a given protocol ID. + + @param[in] Protocol ID Protocol ID. + + @param[out] Attributes Pointer to attributes of the protocol. + + @retval EFI_SUCCESS ReturnValues points to protocol message attributes. + @retval EFI_DEVICE_ERROR SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +EFI_STATUS +ScmiGetProtocolMessageAttributes ( + IN SCMI_PROTOCOL_ID ProtocolId, + OUT UINT32 **ReturnValues + ); + +#endif /* SCMI_PRIVATE_H_ */ diff --git a/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c new file mode 100644 index 000000000..8bb33046e --- /dev/null +++ b/ArmPkg/Drivers/CpuDxe/AArch64/Mmu.c @@ -0,0 +1,433 @@ +/*++ + +Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.
+Portions copyright (c) 2010, Apple Inc. All rights reserved.
+Portions copyright (c) 2011-2021, Arm Limited. All rights reserved.
+Copyright (c) 2017, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + + +--*/ + +#include +#include "CpuDxe.h" + +#define INVALID_ENTRY ((UINT32)~0) + +#define MIN_T0SZ 16 +#define BITS_PER_LEVEL 9 + +STATIC +VOID +GetRootTranslationTableInfo ( + IN UINTN T0SZ, + OUT UINTN *RootTableLevel, + OUT UINTN *RootTableEntryCount + ) +{ + *RootTableLevel = (T0SZ - MIN_T0SZ) / BITS_PER_LEVEL; + *RootTableEntryCount = TT_ENTRY_COUNT >> (T0SZ - MIN_T0SZ) % BITS_PER_LEVEL; +} + +STATIC +UINT64 +PageAttributeToGcdAttribute ( + IN UINT64 PageAttributes + ) +{ + UINT64 GcdAttributes; + + switch (PageAttributes & TT_ATTR_INDX_MASK) { + case TT_ATTR_INDX_DEVICE_MEMORY: + GcdAttributes = EFI_MEMORY_UC; + break; + case TT_ATTR_INDX_MEMORY_NON_CACHEABLE: + GcdAttributes = EFI_MEMORY_WC; + break; + case TT_ATTR_INDX_MEMORY_WRITE_THROUGH: + GcdAttributes = EFI_MEMORY_WT; + break; + case TT_ATTR_INDX_MEMORY_WRITE_BACK: + GcdAttributes = EFI_MEMORY_WB; + break; + default: + DEBUG (( + DEBUG_ERROR, + "PageAttributeToGcdAttribute: PageAttributes:0x%lX not supported.\n", + PageAttributes + )); + ASSERT (0); + // The Global Coherency Domain (GCD) value is defined as a bit set. + // Returning 0 means no attribute has been set. + GcdAttributes = 0; + } + + // Determine protection attributes + if (((PageAttributes & TT_AP_MASK) == TT_AP_NO_RO) || + ((PageAttributes & TT_AP_MASK) == TT_AP_RO_RO)) + { + // Read only cases map to write-protect + GcdAttributes |= EFI_MEMORY_RO; + } + + // Process eXecute Never attribute + if ((PageAttributes & (TT_PXN_MASK | TT_UXN_MASK)) != 0) { + GcdAttributes |= EFI_MEMORY_XP; + } + + return GcdAttributes; +} + +STATIC +UINT64 +GetFirstPageAttribute ( + IN UINT64 *FirstLevelTableAddress, + IN UINTN TableLevel + ) +{ + UINT64 FirstEntry; + + // Get the first entry of the table + FirstEntry = *FirstLevelTableAddress; + + if ((TableLevel != 3) && ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_TABLE_ENTRY)) { + // Only valid for Levels 0, 1 and 2 + + // Get the attribute of the subsequent table + return GetFirstPageAttribute ((UINT64 *)(FirstEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), TableLevel + 1); + } else if (((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY) || + ((TableLevel == 3) && ((FirstEntry & TT_TYPE_MASK) == TT_TYPE_BLOCK_ENTRY_LEVEL3))) + { + return FirstEntry & TT_ATTR_INDX_MASK; + } else { + return INVALID_ENTRY; + } +} + +STATIC +UINT64 +GetNextEntryAttribute ( + IN UINT64 *TableAddress, + IN UINTN EntryCount, + IN UINTN TableLevel, + IN UINT64 BaseAddress, + IN OUT UINT32 *PrevEntryAttribute, + IN OUT UINT64 *StartGcdRegion + ) +{ + UINTN Index; + UINT64 Entry; + UINT32 EntryAttribute; + UINT32 EntryType; + EFI_STATUS Status; + UINTN NumberOfDescriptors; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; + + // Get the memory space map from GCD + MemorySpaceMap = NULL; + Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); + ASSERT_EFI_ERROR (Status); + + // We cannot get more than 3-level page table + ASSERT (TableLevel <= 3); + + // While the top level table might not contain TT_ENTRY_COUNT entries; + // the subsequent ones should be filled up + for (Index = 0; Index < EntryCount; Index++) { + Entry = TableAddress[Index]; + EntryType = Entry & TT_TYPE_MASK; + EntryAttribute = Entry & TT_ATTR_INDX_MASK; + + // If Entry is a Table Descriptor type entry then go through the sub-level table + if ((EntryType == TT_TYPE_BLOCK_ENTRY) || + ((TableLevel == 3) && (EntryType == TT_TYPE_BLOCK_ENTRY_LEVEL3))) + { + if ((*PrevEntryAttribute == INVALID_ENTRY) || (EntryAttribute != *PrevEntryAttribute)) { + if (*PrevEntryAttribute != INVALID_ENTRY) { + // Update GCD with the last region + SetGcdMemorySpaceAttributes ( + MemorySpaceMap, + NumberOfDescriptors, + *StartGcdRegion, + (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel))) - *StartGcdRegion, + PageAttributeToGcdAttribute (*PrevEntryAttribute) + ); + } + + // Start of the new region + *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel)); + *PrevEntryAttribute = EntryAttribute; + } else { + continue; + } + } else if (EntryType == TT_TYPE_TABLE_ENTRY) { + // Table Entry type is only valid for Level 0, 1, 2 + ASSERT (TableLevel < 3); + + // Increase the level number and scan the sub-level table + GetNextEntryAttribute ( + (UINT64 *)(Entry & TT_ADDRESS_MASK_DESCRIPTION_TABLE), + TT_ENTRY_COUNT, + TableLevel + 1, + (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel))), + PrevEntryAttribute, + StartGcdRegion + ); + } else { + if (*PrevEntryAttribute != INVALID_ENTRY) { + // Update GCD with the last region + SetGcdMemorySpaceAttributes ( + MemorySpaceMap, + NumberOfDescriptors, + *StartGcdRegion, + (BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel))) - *StartGcdRegion, + PageAttributeToGcdAttribute (*PrevEntryAttribute) + ); + + // Start of the new region + *StartGcdRegion = BaseAddress + (Index * TT_ADDRESS_AT_LEVEL (TableLevel)); + *PrevEntryAttribute = INVALID_ENTRY; + } + } + } + + FreePool (MemorySpaceMap); + + return BaseAddress + (EntryCount * TT_ADDRESS_AT_LEVEL (TableLevel)); +} + +EFI_STATUS +SyncCacheConfig ( + IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol + ) +{ + EFI_STATUS Status; + UINT32 PageAttribute; + UINT64 *FirstLevelTableAddress; + UINTN TableLevel; + UINTN TableCount; + UINTN NumberOfDescriptors; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; + UINTN Tcr; + UINTN T0SZ; + UINT64 BaseAddressGcdRegion; + UINT64 EndAddressGcdRegion; + + // This code assumes MMU is enabled and filed with section translations + ASSERT (ArmMmuEnabled ()); + + // + // Get the memory space map from GCD + // + MemorySpaceMap = NULL; + Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); + ASSERT_EFI_ERROR (Status); + + // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs + // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a + // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were + // a client) to update its copy of the attributes. This is bad architecture and should be replaced + // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead. + + // Obtain page table base + FirstLevelTableAddress = (UINT64 *)(ArmGetTTBR0BaseAddress ()); + + // Get Translation Control Register value + Tcr = ArmGetTCR (); + // Get Address Region Size + T0SZ = Tcr & TCR_T0SZ_MASK; + + // Get the level of the first table for the indicated Address Region Size + GetRootTranslationTableInfo (T0SZ, &TableLevel, &TableCount); + + // First Attribute of the Page Tables + PageAttribute = GetFirstPageAttribute (FirstLevelTableAddress, TableLevel); + + // We scan from the start of the memory map (ie: at the address 0x0) + BaseAddressGcdRegion = 0x0; + EndAddressGcdRegion = GetNextEntryAttribute ( + FirstLevelTableAddress, + TableCount, + TableLevel, + BaseAddressGcdRegion, + &PageAttribute, + &BaseAddressGcdRegion + ); + + // Update GCD with the last region if valid + if (PageAttribute != INVALID_ENTRY) { + SetGcdMemorySpaceAttributes ( + MemorySpaceMap, + NumberOfDescriptors, + BaseAddressGcdRegion, + EndAddressGcdRegion - BaseAddressGcdRegion, + PageAttributeToGcdAttribute (PageAttribute) + ); + } + + FreePool (MemorySpaceMap); + + return EFI_SUCCESS; +} + +UINT64 +EfiAttributeToArmAttribute ( + IN UINT64 EfiAttributes + ) +{ + UINT64 ArmAttributes; + + switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) { + case EFI_MEMORY_UC: + if (ArmReadCurrentEL () == AARCH64_EL2) { + ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_XN_MASK; + } else { + ArmAttributes = TT_ATTR_INDX_DEVICE_MEMORY | TT_UXN_MASK | TT_PXN_MASK; + } + + break; + case EFI_MEMORY_WC: + ArmAttributes = TT_ATTR_INDX_MEMORY_NON_CACHEABLE; + break; + case EFI_MEMORY_WT: + ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_THROUGH | TT_SH_INNER_SHAREABLE; + break; + case EFI_MEMORY_WB: + ArmAttributes = TT_ATTR_INDX_MEMORY_WRITE_BACK | TT_SH_INNER_SHAREABLE; + break; + default: + ArmAttributes = TT_ATTR_INDX_MASK; + } + + // Set the access flag to match the block attributes + ArmAttributes |= TT_AF; + + // Determine protection attributes + if ((EfiAttributes & EFI_MEMORY_RO) != 0) { + ArmAttributes |= TT_AP_NO_RO; + } + + // Process eXecute Never attribute + if ((EfiAttributes & EFI_MEMORY_XP) != 0) { + ArmAttributes |= TT_PXN_MASK; + } + + return ArmAttributes; +} + +// This function will recursively go down the page table to find the first block address linked to 'BaseAddress'. +// And then the function will identify the size of the region that has the same page table attribute. +EFI_STATUS +GetMemoryRegionRec ( + IN UINT64 *TranslationTable, + IN UINTN TableLevel, + IN UINT64 *LastBlockEntry, + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ) +{ + EFI_STATUS Status; + UINT64 *NextTranslationTable; + UINT64 *BlockEntry; + UINT64 BlockEntryType; + UINT64 EntryType; + + if (TableLevel != 3) { + BlockEntryType = TT_TYPE_BLOCK_ENTRY; + } else { + BlockEntryType = TT_TYPE_BLOCK_ENTRY_LEVEL3; + } + + // Find the block entry linked to the Base Address + BlockEntry = (UINT64 *)TT_GET_ENTRY_FOR_ADDRESS (TranslationTable, TableLevel, *BaseAddress); + EntryType = *BlockEntry & TT_TYPE_MASK; + + if ((TableLevel < 3) && (EntryType == TT_TYPE_TABLE_ENTRY)) { + NextTranslationTable = (UINT64 *)(*BlockEntry & TT_ADDRESS_MASK_DESCRIPTION_TABLE); + + // The entry is a page table, so we go to the next level + Status = GetMemoryRegionRec ( + NextTranslationTable, // Address of the next level page table + TableLevel + 1, // Next Page Table level + (UINTN *)TT_LAST_BLOCK_ADDRESS (NextTranslationTable, TT_ENTRY_COUNT), + BaseAddress, + RegionLength, + RegionAttributes + ); + + // In case of 'Success', it means the end of the block region has been found into the upper + // level translation table + if (!EFI_ERROR (Status)) { + return EFI_SUCCESS; + } + + // Now we processed the table move to the next entry + BlockEntry++; + } else if (EntryType == BlockEntryType) { + // We have found the BlockEntry attached to the address. We save its start address (the start + // address might be before the 'BaseAddress') and attributes + *BaseAddress = *BaseAddress & ~(TT_ADDRESS_AT_LEVEL (TableLevel) - 1); + *RegionLength = 0; + *RegionAttributes = *BlockEntry & TT_ATTRIBUTES_MASK; + } else { + // We have an 'Invalid' entry + return EFI_UNSUPPORTED; + } + + while (BlockEntry <= LastBlockEntry) { + if ((*BlockEntry & TT_ATTRIBUTES_MASK) == *RegionAttributes) { + *RegionLength = *RegionLength + TT_BLOCK_ENTRY_SIZE_AT_LEVEL (TableLevel); + } else { + // In case we have found the end of the region we return success + return EFI_SUCCESS; + } + + BlockEntry++; + } + + // If we have reached the end of the TranslationTable and we have not found the end of the region then + // we return EFI_NOT_FOUND. + // The caller will continue to look for the memory region at its level + return EFI_NOT_FOUND; +} + +EFI_STATUS +GetMemoryRegion ( + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ) +{ + EFI_STATUS Status; + UINT64 *TranslationTable; + UINTN TableLevel; + UINTN EntryCount; + UINTN T0SZ; + + ASSERT ((BaseAddress != NULL) && (RegionLength != NULL) && (RegionAttributes != NULL)); + + TranslationTable = ArmGetTTBR0BaseAddress (); + + T0SZ = ArmGetTCR () & TCR_T0SZ_MASK; + // Get the Table info from T0SZ + GetRootTranslationTableInfo (T0SZ, &TableLevel, &EntryCount); + + Status = GetMemoryRegionRec ( + TranslationTable, + TableLevel, + (UINTN *)TT_LAST_BLOCK_ADDRESS (TranslationTable, EntryCount), + BaseAddress, + RegionLength, + RegionAttributes + ); + + // If the region continues up to the end of the root table then GetMemoryRegionRec() + // will return EFI_NOT_FOUND + if (Status == EFI_NOT_FOUND) { + return EFI_SUCCESS; + } else { + return Status; + } +} diff --git a/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c new file mode 100644 index 000000000..2daf47ba6 --- /dev/null +++ b/ArmPkg/Drivers/CpuDxe/Arm/Mmu.c @@ -0,0 +1,518 @@ +/*++ + +Copyright (c) 2009, Hewlett-Packard Company. All rights reserved.
+Portions copyright (c) 2010, Apple Inc. All rights reserved.
+Portions copyright (c) 2013-2021, Arm Limited. All rights reserved.
+Copyright (c) 2017, Intel Corporation. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + + +--*/ + +#include +#include "CpuDxe.h" + +EFI_STATUS +SectionToGcdAttributes ( + IN UINT32 SectionAttributes, + OUT UINT64 *GcdAttributes + ) +{ + *GcdAttributes = 0; + + // determine cacheability attributes + switch (SectionAttributes & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) { + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED: + *GcdAttributes |= EFI_MEMORY_UC; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE: + *GcdAttributes |= EFI_MEMORY_UC; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC: + *GcdAttributes |= EFI_MEMORY_WT; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC: + *GcdAttributes |= EFI_MEMORY_WB; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE: + *GcdAttributes |= EFI_MEMORY_WC; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC: + *GcdAttributes |= EFI_MEMORY_WB; + break; + case TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE: + *GcdAttributes |= EFI_MEMORY_UC; + break; + default: + return EFI_UNSUPPORTED; + } + + // determine protection attributes + switch (SectionAttributes & TT_DESCRIPTOR_SECTION_AP_MASK) { + case TT_DESCRIPTOR_SECTION_AP_NO_NO: // no read, no write + // *GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP; + break; + + case TT_DESCRIPTOR_SECTION_AP_RW_NO: + case TT_DESCRIPTOR_SECTION_AP_RW_RW: + // normal read/write access, do not add additional attributes + break; + + // read only cases map to write-protect + case TT_DESCRIPTOR_SECTION_AP_RO_NO: + case TT_DESCRIPTOR_SECTION_AP_RO_RO: + *GcdAttributes |= EFI_MEMORY_RO; + break; + + default: + return EFI_UNSUPPORTED; + } + + // now process eXectue Never attribute + if ((SectionAttributes & TT_DESCRIPTOR_SECTION_XN_MASK) != 0 ) { + *GcdAttributes |= EFI_MEMORY_XP; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +PageToGcdAttributes ( + IN UINT32 PageAttributes, + OUT UINT64 *GcdAttributes + ) +{ + *GcdAttributes = 0; + + // determine cacheability attributes + switch (PageAttributes & TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) { + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED: + *GcdAttributes |= EFI_MEMORY_UC; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE: + *GcdAttributes |= EFI_MEMORY_UC; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC: + *GcdAttributes |= EFI_MEMORY_WT; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC: + *GcdAttributes |= EFI_MEMORY_WB; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE: + *GcdAttributes |= EFI_MEMORY_WC; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC: + *GcdAttributes |= EFI_MEMORY_WB; + break; + case TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE: + *GcdAttributes |= EFI_MEMORY_UC; + break; + default: + return EFI_UNSUPPORTED; + } + + // determine protection attributes + switch (PageAttributes & TT_DESCRIPTOR_PAGE_AP_MASK) { + case TT_DESCRIPTOR_PAGE_AP_NO_NO: // no read, no write + // *GcdAttributes |= EFI_MEMORY_RO | EFI_MEMORY_RP; + break; + + case TT_DESCRIPTOR_PAGE_AP_RW_NO: + case TT_DESCRIPTOR_PAGE_AP_RW_RW: + // normal read/write access, do not add additional attributes + break; + + // read only cases map to write-protect + case TT_DESCRIPTOR_PAGE_AP_RO_NO: + case TT_DESCRIPTOR_PAGE_AP_RO_RO: + *GcdAttributes |= EFI_MEMORY_RO; + break; + + default: + return EFI_UNSUPPORTED; + } + + // now process eXectue Never attribute + if ((PageAttributes & TT_DESCRIPTOR_PAGE_XN_MASK) != 0 ) { + *GcdAttributes |= EFI_MEMORY_XP; + } + + return EFI_SUCCESS; +} + +EFI_STATUS +SyncCacheConfigPage ( + IN UINT32 SectionIndex, + IN UINT32 FirstLevelDescriptor, + IN UINTN NumberOfDescriptors, + IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, + IN OUT EFI_PHYSICAL_ADDRESS *NextRegionBase, + IN OUT UINT64 *NextRegionLength, + IN OUT UINT32 *NextSectionAttributes + ) +{ + EFI_STATUS Status; + UINT32 i; + volatile ARM_PAGE_TABLE_ENTRY *SecondLevelTable; + UINT32 NextPageAttributes; + UINT32 PageAttributes; + UINT32 BaseAddress; + UINT64 GcdAttributes; + + // Get the Base Address from FirstLevelDescriptor; + BaseAddress = TT_DESCRIPTOR_PAGE_BASE_ADDRESS (SectionIndex << TT_DESCRIPTOR_SECTION_BASE_SHIFT); + + // Convert SectionAttributes into PageAttributes + NextPageAttributes = + TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY (*NextSectionAttributes, 0) | + TT_DESCRIPTOR_CONVERT_TO_PAGE_AP (*NextSectionAttributes); + + // obtain page table base + SecondLevelTable = (ARM_PAGE_TABLE_ENTRY *)(FirstLevelDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK); + + for (i = 0; i < TRANSLATION_TABLE_PAGE_COUNT; i++) { + if ((SecondLevelTable[i] & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) { + // extract attributes (cacheability and permissions) + PageAttributes = SecondLevelTable[i] & (TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK | TT_DESCRIPTOR_PAGE_AP_MASK); + + if (NextPageAttributes == 0) { + // start on a new region + *NextRegionLength = 0; + *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT); + NextPageAttributes = PageAttributes; + } else if (PageAttributes != NextPageAttributes) { + // Convert Section Attributes into GCD Attributes + Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes); + ASSERT_EFI_ERROR (Status); + + // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK) + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes); + + // start on a new region + *NextRegionLength = 0; + *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT); + NextPageAttributes = PageAttributes; + } + } else if (NextPageAttributes != 0) { + // Convert Page Attributes into GCD Attributes + Status = PageToGcdAttributes (NextPageAttributes, &GcdAttributes); + ASSERT_EFI_ERROR (Status); + + // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK) + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, *NextRegionBase, *NextRegionLength, GcdAttributes); + + *NextRegionLength = 0; + *NextRegionBase = BaseAddress | (i << TT_DESCRIPTOR_PAGE_BASE_SHIFT); + NextPageAttributes = 0; + } + + *NextRegionLength += TT_DESCRIPTOR_PAGE_SIZE; + } + + // Convert back PageAttributes into SectionAttributes + *NextSectionAttributes = + TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (NextPageAttributes, 0) | + TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (NextPageAttributes); + + return EFI_SUCCESS; +} + +EFI_STATUS +SyncCacheConfig ( + IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol + ) +{ + EFI_STATUS Status; + UINT32 i; + EFI_PHYSICAL_ADDRESS NextRegionBase; + UINT64 NextRegionLength; + UINT32 NextSectionAttributes; + UINT32 SectionAttributes; + UINT64 GcdAttributes; + volatile ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable; + UINTN NumberOfDescriptors; + EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap; + + DEBUG ((DEBUG_PAGE, "SyncCacheConfig()\n")); + + // This code assumes MMU is enabled and filed with section translations + ASSERT (ArmMmuEnabled ()); + + // + // Get the memory space map from GCD + // + MemorySpaceMap = NULL; + Status = gDS->GetMemorySpaceMap (&NumberOfDescriptors, &MemorySpaceMap); + ASSERT_EFI_ERROR (Status); + + // The GCD implementation maintains its own copy of the state of memory space attributes. GCD needs + // to know what the initial memory space attributes are. The CPU Arch. Protocol does not provide a + // GetMemoryAttributes function for GCD to get this so we must resort to calling GCD (as if we were + // a client) to update its copy of the attributes. This is bad architecture and should be replaced + // with a way for GCD to query the CPU Arch. driver of the existing memory space attributes instead. + + // obtain page table base + FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)(ArmGetTTBR0BaseAddress ()); + + // Get the first region + NextSectionAttributes = FirstLevelTable[0] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK); + + // iterate through each 1MB descriptor + NextRegionBase = NextRegionLength = 0; + for (i = 0; i < TRANSLATION_TABLE_SECTION_COUNT; i++) { + if ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) { + // extract attributes (cacheability and permissions) + SectionAttributes = FirstLevelTable[i] & (TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK | TT_DESCRIPTOR_SECTION_AP_MASK); + + if (NextSectionAttributes == 0) { + // start on a new region + NextRegionLength = 0; + NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (i << TT_DESCRIPTOR_SECTION_BASE_SHIFT); + NextSectionAttributes = SectionAttributes; + } else if (SectionAttributes != NextSectionAttributes) { + // Convert Section Attributes into GCD Attributes + Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes); + ASSERT_EFI_ERROR (Status); + + // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK) + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes); + + // start on a new region + NextRegionLength = 0; + NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (i << TT_DESCRIPTOR_SECTION_BASE_SHIFT); + NextSectionAttributes = SectionAttributes; + } + + NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE; + } else if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (FirstLevelTable[i])) { + // In this case any bits set in the 'NextSectionAttributes' are garbage and were set from + // bits that are actually part of the pagetable address. We clear it out to zero so that + // the SyncCacheConfigPage will use the page attributes instead of trying to convert the + // section attributes into page attributes + NextSectionAttributes = 0; + Status = SyncCacheConfigPage ( + i, + FirstLevelTable[i], + NumberOfDescriptors, + MemorySpaceMap, + &NextRegionBase, + &NextRegionLength, + &NextSectionAttributes + ); + ASSERT_EFI_ERROR (Status); + } else { + // We do not support yet 16MB sections + ASSERT ((FirstLevelTable[i] & TT_DESCRIPTOR_SECTION_TYPE_MASK) != TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION); + + // start on a new region + if (NextSectionAttributes != 0) { + // Convert Section Attributes into GCD Attributes + Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes); + ASSERT_EFI_ERROR (Status); + + // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK) + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes); + + NextRegionLength = 0; + NextRegionBase = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (i << TT_DESCRIPTOR_SECTION_BASE_SHIFT); + NextSectionAttributes = 0; + } + + NextRegionLength += TT_DESCRIPTOR_SECTION_SIZE; + } + } // section entry loop + + if (NextSectionAttributes != 0) { + // Convert Section Attributes into GCD Attributes + Status = SectionToGcdAttributes (NextSectionAttributes, &GcdAttributes); + ASSERT_EFI_ERROR (Status); + + // update GCD with these changes (this will recurse into our own CpuSetMemoryAttributes below which is OK) + SetGcdMemorySpaceAttributes (MemorySpaceMap, NumberOfDescriptors, NextRegionBase, NextRegionLength, GcdAttributes); + } + + FreePool (MemorySpaceMap); + + return EFI_SUCCESS; +} + +UINT64 +EfiAttributeToArmAttribute ( + IN UINT64 EfiAttributes + ) +{ + UINT64 ArmAttributes; + + switch (EfiAttributes & EFI_MEMORY_CACHETYPE_MASK) { + case EFI_MEMORY_UC: + // Map to strongly ordered + ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED; // TEX[2:0] = 0, C=0, B=0 + break; + + case EFI_MEMORY_WC: + // Map to normal non-cacheable + ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE; // TEX [2:0]= 001 = 0x2, B=0, C=0 + break; + + case EFI_MEMORY_WT: + // Write through with no-allocate + ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC; // TEX [2:0] = 0, C=1, B=0 + break; + + case EFI_MEMORY_WB: + // Write back (with allocate) + ArmAttributes = TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC; // TEX [2:0] = 001, C=1, B=1 + break; + + case EFI_MEMORY_UCE: + default: + ArmAttributes = TT_DESCRIPTOR_SECTION_TYPE_FAULT; + break; + } + + // Determine protection attributes + if ((EfiAttributes & EFI_MEMORY_RO) != 0) { + ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RO_RO; + } else { + ArmAttributes |= TT_DESCRIPTOR_SECTION_AP_RW_RW; + } + + // Determine eXecute Never attribute + if ((EfiAttributes & EFI_MEMORY_XP) != 0) { + ArmAttributes |= TT_DESCRIPTOR_SECTION_XN_MASK; + } + + return ArmAttributes; +} + +EFI_STATUS +GetMemoryRegionPage ( + IN UINT32 *PageTable, + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ) +{ + UINT32 PageAttributes; + UINT32 TableIndex; + UINT32 PageDescriptor; + + // Convert the section attributes into page attributes + PageAttributes = ConvertSectionAttributesToPageAttributes (*RegionAttributes, 0); + + // Calculate index into first level translation table for start of modification + TableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT; + ASSERT (TableIndex < TRANSLATION_TABLE_PAGE_COUNT); + + // Go through the page table to find the end of the section + for ( ; TableIndex < TRANSLATION_TABLE_PAGE_COUNT; TableIndex++) { + // Get the section at the given index + PageDescriptor = PageTable[TableIndex]; + + if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_MASK) == TT_DESCRIPTOR_PAGE_TYPE_FAULT) { + // Case: End of the boundary of the region + return EFI_SUCCESS; + } else if ((PageDescriptor & TT_DESCRIPTOR_PAGE_TYPE_PAGE) == TT_DESCRIPTOR_PAGE_TYPE_PAGE) { + if ((PageDescriptor & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK) == PageAttributes) { + *RegionLength = *RegionLength + TT_DESCRIPTOR_PAGE_SIZE; + } else { + // Case: End of the boundary of the region + return EFI_SUCCESS; + } + } else { + // We do not support Large Page yet. We return EFI_SUCCESS that means end of the region. + ASSERT (0); + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +EFI_STATUS +GetMemoryRegion ( + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ) +{ + EFI_STATUS Status; + UINT32 TableIndex; + UINT32 PageAttributes; + UINT32 PageTableIndex; + UINT32 SectionDescriptor; + ARM_FIRST_LEVEL_DESCRIPTOR *FirstLevelTable; + UINT32 *PageTable; + + // Initialize the arguments + *RegionLength = 0; + + // Obtain page table base + FirstLevelTable = (ARM_FIRST_LEVEL_DESCRIPTOR *)ArmGetTTBR0BaseAddress (); + + // Calculate index into first level translation table for start of modification + TableIndex = TT_DESCRIPTOR_SECTION_BASE_ADDRESS (*BaseAddress) >> TT_DESCRIPTOR_SECTION_BASE_SHIFT; + ASSERT (TableIndex < TRANSLATION_TABLE_SECTION_COUNT); + + // Get the section at the given index + SectionDescriptor = FirstLevelTable[TableIndex]; + if (!SectionDescriptor) { + return EFI_NOT_FOUND; + } + + // If 'BaseAddress' belongs to the section then round it to the section boundary + if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) || + ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) + { + *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK; + *RegionAttributes = SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK; + } else { + // Otherwise, we round it to the page boundary + *BaseAddress = (*BaseAddress) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK; + + // Get the attribute at the page table level (Level 2) + PageTable = (UINT32 *)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK); + + // Calculate index into first level translation table for start of modification + PageTableIndex = ((*BaseAddress) & TT_DESCRIPTOR_PAGE_INDEX_MASK) >> TT_DESCRIPTOR_PAGE_BASE_SHIFT; + ASSERT (PageTableIndex < TRANSLATION_TABLE_PAGE_COUNT); + + PageAttributes = PageTable[PageTableIndex] & TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK; + *RegionAttributes = TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY (PageAttributes, 0) | + TT_DESCRIPTOR_CONVERT_TO_SECTION_AP (PageAttributes); + } + + for ( ; TableIndex < TRANSLATION_TABLE_SECTION_COUNT; TableIndex++) { + // Get the section at the given index + SectionDescriptor = FirstLevelTable[TableIndex]; + + // If the entry is a level-2 page table then we scan it to find the end of the region + if (TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE (SectionDescriptor)) { + // Extract the page table location from the descriptor + PageTable = (UINT32 *)(SectionDescriptor & TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK); + + // Scan the page table to find the end of the region. + Status = GetMemoryRegionPage (PageTable, BaseAddress, RegionLength, RegionAttributes); + + // If we have found the end of the region (Status == EFI_SUCCESS) then we exit the for-loop + if (Status == EFI_SUCCESS) { + break; + } + } else if (((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SECTION) || + ((SectionDescriptor & TT_DESCRIPTOR_SECTION_TYPE_MASK) == TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION)) + { + if ((SectionDescriptor & TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK) != *RegionAttributes) { + // If the attributes of the section differ from the one targeted then we exit the loop + break; + } else { + *RegionLength = *RegionLength + TT_DESCRIPTOR_SECTION_SIZE; + } + } else { + // If we are on an invalid section then it means it is the end of our section. + break; + } + } + + return EFI_SUCCESS; +} diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.c b/ArmPkg/Drivers/CpuDxe/CpuDxe.c new file mode 100644 index 000000000..e6742f0a2 --- /dev/null +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.c @@ -0,0 +1,273 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Copyright (c) 2011, ARM Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuDxe.h" + +#include + +BOOLEAN mIsFlushingGCD; + +/** + This function flushes the range of addresses from Start to Start+Length + from the processor's data cache. If Start is not aligned to a cache line + boundary, then the bytes before Start to the preceding cache line boundary + are also flushed. If Start+Length is not aligned to a cache line boundary, + then the bytes past Start+Length to the end of the next cache line boundary + are also flushed. The FlushType of EfiCpuFlushTypeWriteBackInvalidate must be + supported. If the data cache is fully coherent with all DMA operations, then + this function can just return EFI_SUCCESS. If the processor does not support + flushing a range of the data cache, then the entire data cache can be flushed. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param Start The beginning physical address to flush from the processor's data + cache. + @param Length The number of bytes to flush from the processor's data cache. This + function may flush more bytes than Length specifies depending upon + the granularity of the flush operation that the processor supports. + @param FlushType Specifies the type of flush operation to perform. + + @retval EFI_SUCCESS The address range from Start to Start+Length was flushed from + the processor's data cache. + @retval EFI_UNSUPPORTED The processor does not support the cache flush type specified + by FlushType. + @retval EFI_DEVICE_ERROR The address range from Start to Start+Length could not be flushed + from the processor's data cache. + +**/ +EFI_STATUS +EFIAPI +CpuFlushCpuDataCache ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS Start, + IN UINT64 Length, + IN EFI_CPU_FLUSH_TYPE FlushType + ) +{ + switch (FlushType) { + case EfiCpuFlushTypeWriteBack: + WriteBackDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length); + break; + case EfiCpuFlushTypeInvalidate: + InvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length); + break; + case EfiCpuFlushTypeWriteBackInvalidate: + WriteBackInvalidateDataCacheRange ((VOID *)(UINTN)Start, (UINTN)Length); + break; + default: + return EFI_INVALID_PARAMETER; + } + + return EFI_SUCCESS; +} + +/** + This function enables interrupt processing by the processor. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS Interrupts are enabled on the processor. + @retval EFI_DEVICE_ERROR Interrupts could not be enabled on the processor. + +**/ +EFI_STATUS +EFIAPI +CpuEnableInterrupt ( + IN EFI_CPU_ARCH_PROTOCOL *This + ) +{ + ArmEnableInterrupts (); + + return EFI_SUCCESS; +} + +/** + This function disables interrupt processing by the processor. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS Interrupts are disabled on the processor. + @retval EFI_DEVICE_ERROR Interrupts could not be disabled on the processor. + +**/ +EFI_STATUS +EFIAPI +CpuDisableInterrupt ( + IN EFI_CPU_ARCH_PROTOCOL *This + ) +{ + ArmDisableInterrupts (); + + return EFI_SUCCESS; +} + +/** + This function retrieves the processor's current interrupt state a returns it in + State. If interrupts are currently enabled, then TRUE is returned. If interrupts + are currently disabled, then FALSE is returned. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param State A pointer to the processor's current interrupt state. Set to TRUE if + interrupts are enabled and FALSE if interrupts are disabled. + + @retval EFI_SUCCESS The processor's current interrupt state was returned in State. + @retval EFI_INVALID_PARAMETER State is NULL. + +**/ +EFI_STATUS +EFIAPI +CpuGetInterruptState ( + IN EFI_CPU_ARCH_PROTOCOL *This, + OUT BOOLEAN *State + ) +{ + if (State == NULL) { + return EFI_INVALID_PARAMETER; + } + + *State = ArmGetInterruptState (); + return EFI_SUCCESS; +} + +/** + This function generates an INIT on the processor. If this function succeeds, then the + processor will be reset, and control will not be returned to the caller. If InitType is + not supported by this processor, or the processor cannot programmatically generate an + INIT without help from external hardware, then EFI_UNSUPPORTED is returned. If an error + occurs attempting to generate an INIT, then EFI_DEVICE_ERROR is returned. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param InitType The type of processor INIT to perform. + + @retval EFI_SUCCESS The processor INIT was performed. This return code should never be seen. + @retval EFI_UNSUPPORTED The processor INIT operation specified by InitType is not supported + by this processor. + @retval EFI_DEVICE_ERROR The processor INIT failed. + +**/ +EFI_STATUS +EFIAPI +CpuInit ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_CPU_INIT_TYPE InitType + ) +{ + return EFI_UNSUPPORTED; +} + +EFI_STATUS +EFIAPI +CpuRegisterInterruptHandler ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) +{ + return RegisterInterruptHandler (InterruptType, InterruptHandler); +} + +EFI_STATUS +EFIAPI +CpuGetTimerValue ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN UINT32 TimerIndex, + OUT UINT64 *TimerValue, + OUT UINT64 *TimerPeriod OPTIONAL + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Callback function for idle events. + + @param Event Event whose notification function is being invoked. + @param Context The pointer to the notification function's context, + which is implementation-dependent. + +**/ +VOID +EFIAPI +IdleLoopEventCallback ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + CpuSleep (); +} + +// +// Globals used to initialize the protocol +// +EFI_HANDLE mCpuHandle = NULL; +EFI_CPU_ARCH_PROTOCOL mCpu = { + CpuFlushCpuDataCache, + CpuEnableInterrupt, + CpuDisableInterrupt, + CpuGetInterruptState, + CpuInit, + CpuRegisterInterruptHandler, + CpuGetTimerValue, + CpuSetMemoryAttributes, + 0, // NumberOfTimers + 2048, // DmaBufferAlignment +}; + +STATIC +VOID +InitializeDma ( + IN OUT EFI_CPU_ARCH_PROTOCOL *CpuArchProtocol + ) +{ + CpuArchProtocol->DmaBufferAlignment = ArmCacheWritebackGranule (); +} + +EFI_STATUS +CpuDxeInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_EVENT IdleLoopEvent; + + InitializeExceptions (&mCpu); + + InitializeDma (&mCpu); + + Status = gBS->InstallMultipleProtocolInterfaces ( + &mCpuHandle, + &gEfiCpuArchProtocolGuid, + &mCpu, + NULL + ); + + // + // Make sure GCD and MMU settings match. This API calls gDS->SetMemorySpaceAttributes () + // and that calls EFI_CPU_ARCH_PROTOCOL.SetMemoryAttributes, so this code needs to go + // after the protocol is installed + // + mIsFlushingGCD = TRUE; + SyncCacheConfig (&mCpu); + mIsFlushingGCD = FALSE; + + // + // Setup a callback for idle events + // + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_NOTIFY, + IdleLoopEventCallback, + NULL, + &gIdleLoopEventGuid, + &IdleLoopEvent + ); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.h b/ArmPkg/Drivers/CpuDxe/CpuDxe.h new file mode 100644 index 000000000..ff672390c --- /dev/null +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.h @@ -0,0 +1,129 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Copyright (c) 2011 - 2013, ARM Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef CPU_DXE_H_ +#define CPU_DXE_H_ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +extern BOOLEAN mIsFlushingGCD; + +/** + This function registers and enables the handler specified by InterruptHandler for a processor + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the + handler for the processor interrupt or exception type specified by InterruptType is uninstalled. + The installed handler is called once for each processor interrupt or exception. + + @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts + are enabled and FALSE if interrupts are disabled. + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. + +**/ +EFI_STATUS +RegisterInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ); + +/** + This function registers and enables the handler specified by InterruptHandler for a processor + interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the + handler for the processor interrupt or exception type specified by InterruptType is uninstalled. + The installed handler is called once for each processor interrupt or exception. + + @param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts + are enabled and FALSE if interrupts are disabled. + @param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called + when a processor interrupt occurs. If this parameter is NULL, then the handler + will be uninstalled. + + @retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. + @retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was + previously installed. + @retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not + previously installed. + @retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. + +**/ +EFI_STATUS +RegisterDebuggerInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ); + +EFI_STATUS +EFIAPI +CpuSetMemoryAttributes ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +EFI_STATUS +InitializeExceptions ( + IN EFI_CPU_ARCH_PROTOCOL *Cpu + ); + +EFI_STATUS +SyncCacheConfig ( + IN EFI_CPU_ARCH_PROTOCOL *CpuProtocol + ); + +// The ARM Attributes might be defined on 64-bit (case of the long format description table) +UINT64 +EfiAttributeToArmAttribute ( + IN UINT64 EfiAttributes + ); + +EFI_STATUS +GetMemoryRegion ( + IN OUT UINTN *BaseAddress, + OUT UINTN *RegionLength, + OUT UINTN *RegionAttributes + ); + +EFI_STATUS +SetGcdMemorySpaceAttributes ( + IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, + IN UINTN NumberOfDescriptors, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +#endif // CPU_DXE_H_ diff --git a/ArmPkg/Drivers/CpuDxe/CpuDxe.inf b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf new file mode 100644 index 000000000..10792b393 --- /dev/null +++ b/ArmPkg/Drivers/CpuDxe/CpuDxe.inf @@ -0,0 +1,70 @@ +#/** @file +# +# DXE CPU driver +# +# Copyright (c) 2009, Apple Inc. All rights reserved.
+# Copyright (c) 2011-2013, ARM Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArmCpuDxe + FILE_GUID = B8D9777E-D72A-451F-9BDB-BAFB52A68415 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = CpuDxeInitialize + +[Sources.Common] + CpuDxe.c + CpuDxe.h + CpuMmuCommon.c + Exception.c + +[Sources.ARM] + Arm/Mmu.c + +[Sources.AARCH64] + AArch64/Mmu.c + +[Packages] + ArmPkg/ArmPkg.dec + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + MdeModulePkg/MdeModulePkg.dec + +[LibraryClasses] + ArmLib + ArmMmuLib + BaseMemoryLib + CacheMaintenanceLib + CpuLib + CpuExceptionHandlerLib + DebugLib + DefaultExceptionHandlerLib + DxeServicesTableLib + HobLib + PeCoffGetEntryPointLib + UefiDriverEntryPoint + UefiLib + +[Protocols] + gEfiCpuArchProtocolGuid + +[Guids] + gEfiDebugImageInfoTableGuid + gArmMpCoreInfoGuid + gIdleLoopEventGuid + gEfiVectorHandoffTableGuid + +[Pcd.common] + gArmTokenSpaceGuid.PcdVFPEnabled + +[FeaturePcd.common] + gArmTokenSpaceGuid.PcdDebuggerExceptionSupport + +[Depex] + gHardwareInterruptProtocolGuid OR gHardwareInterrupt2ProtocolGuid diff --git a/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c b/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c new file mode 100644 index 000000000..2e73719dc --- /dev/null +++ b/ArmPkg/Drivers/CpuDxe/CpuMmuCommon.c @@ -0,0 +1,224 @@ +/** @file +* +* Copyright (c) 2013, ARM Limited. All rights reserved. +* Copyright (c) 2017, Intel Corporation. All rights reserved.
+* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include "CpuDxe.h" + +/** + Searches memory descriptors covered by given memory range. + + This function searches into the Gcd Memory Space for descriptors + (from StartIndex to EndIndex) that contains the memory range + specified by BaseAddress and Length. + + @param MemorySpaceMap Gcd Memory Space Map as array. + @param NumberOfDescriptors Number of descriptors in map. + @param BaseAddress BaseAddress for the requested range. + @param Length Length for the requested range. + @param StartIndex Start index into the Gcd Memory Space Map. + @param EndIndex End index into the Gcd Memory Space Map. + + @retval EFI_SUCCESS Search successfully. + @retval EFI_NOT_FOUND The requested descriptors does not exist. + +**/ +EFI_STATUS +SearchGcdMemorySpaces ( + IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, + IN UINTN NumberOfDescriptors, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + OUT UINTN *StartIndex, + OUT UINTN *EndIndex + ) +{ + UINTN Index; + + *StartIndex = 0; + *EndIndex = 0; + for (Index = 0; Index < NumberOfDescriptors; Index++) { + if ((BaseAddress >= MemorySpaceMap[Index].BaseAddress) && + (BaseAddress < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) + { + *StartIndex = Index; + } + + if (((BaseAddress + Length - 1) >= MemorySpaceMap[Index].BaseAddress) && + ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length))) + { + *EndIndex = Index; + return EFI_SUCCESS; + } + } + + return EFI_NOT_FOUND; +} + +/** + Sets the attributes for a specified range in Gcd Memory Space Map. + + This function sets the attributes for a specified range in + Gcd Memory Space Map. + + @param MemorySpaceMap Gcd Memory Space Map as array + @param NumberOfDescriptors Number of descriptors in map + @param BaseAddress BaseAddress for the range + @param Length Length for the range + @param Attributes Attributes to set + + @retval EFI_SUCCESS Memory attributes set successfully + @retval EFI_NOT_FOUND The specified range does not exist in Gcd Memory Space + +**/ +EFI_STATUS +SetGcdMemorySpaceAttributes ( + IN EFI_GCD_MEMORY_SPACE_DESCRIPTOR *MemorySpaceMap, + IN UINTN NumberOfDescriptors, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ) +{ + EFI_STATUS Status; + UINTN Index; + UINTN StartIndex; + UINTN EndIndex; + EFI_PHYSICAL_ADDRESS RegionStart; + UINT64 RegionLength; + + DEBUG (( + DEBUG_GCD, + "SetGcdMemorySpaceAttributes[0x%lX; 0x%lX] = 0x%lX\n", + BaseAddress, + BaseAddress + Length, + Attributes + )); + + // We do not support a smaller granularity than 4KB on ARM Architecture + if ((Length & EFI_PAGE_MASK) != 0) { + DEBUG (( + DEBUG_WARN, + "Warning: We do not support smaller granularity than 4KB on ARM Architecture (passed length: 0x%lX).\n", + Length + )); + } + + // + // Get all memory descriptors covered by the memory range + // + Status = SearchGcdMemorySpaces ( + MemorySpaceMap, + NumberOfDescriptors, + BaseAddress, + Length, + &StartIndex, + &EndIndex + ); + if (EFI_ERROR (Status)) { + return Status; + } + + // + // Go through all related descriptors and set attributes accordingly + // + for (Index = StartIndex; Index <= EndIndex; Index++) { + if (MemorySpaceMap[Index].GcdMemoryType == EfiGcdMemoryTypeNonExistent) { + continue; + } + + // + // Calculate the start and end address of the overlapping range + // + if (BaseAddress >= MemorySpaceMap[Index].BaseAddress) { + RegionStart = BaseAddress; + } else { + RegionStart = MemorySpaceMap[Index].BaseAddress; + } + + if ((BaseAddress + Length - 1) < (MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length)) { + RegionLength = BaseAddress + Length - RegionStart; + } else { + RegionLength = MemorySpaceMap[Index].BaseAddress + MemorySpaceMap[Index].Length - RegionStart; + } + + // + // Set memory attributes according to MTRR attribute and the original attribute of descriptor + // + gDS->SetMemorySpaceAttributes ( + RegionStart, + RegionLength, + (MemorySpaceMap[Index].Attributes & ~EFI_MEMORY_CACHETYPE_MASK) | (MemorySpaceMap[Index].Capabilities & Attributes) + ); + } + + return EFI_SUCCESS; +} + +/** + This function modifies the attributes for the memory region specified by BaseAddress and + Length from their current attributes to the attributes specified by Attributes. + + @param This The EFI_CPU_ARCH_PROTOCOL instance. + @param BaseAddress The physical address that is the start address of a memory region. + @param Length The size in bytes of the memory region. + @param Attributes The bit mask of attributes to set for the memory region. + + @retval EFI_SUCCESS The attributes were set for the memory region. + @retval EFI_ACCESS_DENIED The attributes for the memory resource range specified by + BaseAddress and Length cannot be modified. + @retval EFI_INVALID_PARAMETER Length is zero. + @retval EFI_OUT_OF_RESOURCES There are not enough system resources to modify the attributes of + the memory resource range. + @retval EFI_UNSUPPORTED The processor does not support one or more bytes of the memory + resource range specified by BaseAddress and Length. + The bit mask of attributes is not support for the memory resource + range specified by BaseAddress and Length. + +**/ +EFI_STATUS +EFIAPI +CpuSetMemoryAttributes ( + IN EFI_CPU_ARCH_PROTOCOL *This, + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 EfiAttributes + ) +{ + EFI_STATUS Status; + UINTN ArmAttributes; + UINTN RegionBaseAddress; + UINTN RegionLength; + UINTN RegionArmAttributes; + + if (mIsFlushingGCD) { + return EFI_SUCCESS; + } + + if ((BaseAddress & (SIZE_4KB - 1)) != 0) { + // Minimum granularity is SIZE_4KB (4KB on ARM) + DEBUG ((DEBUG_PAGE, "CpuSetMemoryAttributes(%lx, %lx, %lx): Minimum granularity is SIZE_4KB\n", BaseAddress, Length, EfiAttributes)); + return EFI_UNSUPPORTED; + } + + // Convert the 'Attribute' into ARM Attribute + ArmAttributes = EfiAttributeToArmAttribute (EfiAttributes); + + // Get the region starting from 'BaseAddress' and its 'Attribute' + RegionBaseAddress = BaseAddress; + Status = GetMemoryRegion (&RegionBaseAddress, &RegionLength, &RegionArmAttributes); + + // Data & Instruction Caches are flushed when we set new memory attributes. + // So, we only set the attributes if the new region is different. + if (EFI_ERROR (Status) || (RegionArmAttributes != ArmAttributes) || + ((BaseAddress + Length) > (RegionBaseAddress + RegionLength))) + { + return ArmSetMemoryAttributes (BaseAddress, Length, EfiAttributes); + } else { + return EFI_SUCCESS; + } +} diff --git a/ArmPkg/Drivers/CpuDxe/Exception.c b/ArmPkg/Drivers/CpuDxe/Exception.c new file mode 100644 index 000000000..441f92d50 --- /dev/null +++ b/ArmPkg/Drivers/CpuDxe/Exception.c @@ -0,0 +1,100 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Portions Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include "CpuDxe.h" +#include +#include + +EFI_STATUS +InitializeExceptions ( + IN EFI_CPU_ARCH_PROTOCOL *Cpu + ) +{ + EFI_STATUS Status; + EFI_VECTOR_HANDOFF_INFO *VectorInfoList; + EFI_VECTOR_HANDOFF_INFO *VectorInfo; + BOOLEAN IrqEnabled; + BOOLEAN FiqEnabled; + + VectorInfo = (EFI_VECTOR_HANDOFF_INFO *)NULL; + Status = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **)&VectorInfoList); + if ((Status == EFI_SUCCESS) && (VectorInfoList != NULL)) { + VectorInfo = VectorInfoList; + } + + // initialize the CpuExceptionHandlerLib so we take over the exception vector table from the DXE Core + InitializeCpuExceptionHandlers (VectorInfo); + + Status = EFI_SUCCESS; + + // + // Disable interrupts + // + Cpu->GetInterruptState (Cpu, &IrqEnabled); + Cpu->DisableInterrupt (Cpu); + + // + // EFI does not use the FIQ, but a debugger might so we must disable + // as we take over the exception vectors. + // + FiqEnabled = ArmGetFiqState (); + ArmDisableFiq (); + + if (FiqEnabled) { + ArmEnableFiq (); + } + + if (IrqEnabled) { + // + // Restore interrupt state + // + Status = Cpu->EnableInterrupt (Cpu); + } + + // + // On a DEBUG build, unmask SErrors so they are delivered right away rather + // than when the OS unmasks them. This gives us a better chance of figuring + // out the cause. + // + DEBUG_CODE ( + ArmEnableAsynchronousAbort (); + ); + + return Status; +} + +/** +This function registers and enables the handler specified by InterruptHandler for a processor +interrupt or exception type specified by InterruptType. If InterruptHandler is NULL, then the +handler for the processor interrupt or exception type specified by InterruptType is uninstalled. +The installed handler is called once for each processor interrupt or exception. + +@param InterruptType A pointer to the processor's current interrupt state. Set to TRUE if interrupts +are enabled and FALSE if interrupts are disabled. +@param InterruptHandler A pointer to a function of type EFI_CPU_INTERRUPT_HANDLER that is called +when a processor interrupt occurs. If this parameter is NULL, then the handler +will be uninstalled. + +@retval EFI_SUCCESS The handler for the processor interrupt was successfully installed or uninstalled. +@retval EFI_ALREADY_STARTED InterruptHandler is not NULL, and a handler for InterruptType was +previously installed. +@retval EFI_INVALID_PARAMETER InterruptHandler is NULL, and a handler for InterruptType was not +previously installed. +@retval EFI_UNSUPPORTED The interrupt specified by InterruptType is not supported. + +**/ +EFI_STATUS +RegisterInterruptHandler ( + IN EFI_EXCEPTION_TYPE InterruptType, + IN EFI_CPU_INTERRUPT_HANDLER InterruptHandler + ) +{ + // pass down to CpuExceptionHandlerLib + return (EFI_STATUS)RegisterCpuInterruptHandler (InterruptType, InterruptHandler); +} diff --git a/ArmPkg/Drivers/CpuPei/CpuPei.c b/ArmPkg/Drivers/CpuPei/CpuPei.c new file mode 100644 index 000000000..85ef5ec07 --- /dev/null +++ b/ArmPkg/Drivers/CpuPei/CpuPei.c @@ -0,0 +1,83 @@ +/**@file + +Copyright (c) 2006, Intel Corporation. All rights reserved.
+Copyright (c) 2011 Hewlett Packard Corporation. All rights reserved.
+Copyright (c) 2011-2013, ARM Limited. All rights reserved.
+ +SPDX-License-Identifier: BSD-2-Clause-Patent + +Module Name: + + MemoryInit.c + +Abstract: + + PEIM to provide fake memory init + +**/ + +// +// The package level header files this module uses +// +#include +// +// The protocols, PPI and GUID definitions for this module +// +#include + +// +// The Library classes this module consumes +// +#include +#include +#include +#include +#include +#include + +/*++ + +Routine Description: + +Arguments: + + FileHandle - Handle of the file being invoked. + PeiServices - Describes the list of possible PEI Services. + +Returns: + + Status - EFI_SUCCESS if the boot mode could be set + +--*/ +EFI_STATUS +EFIAPI +InitializeCpuPeim ( + IN EFI_PEI_FILE_HANDLE FileHandle, + IN CONST EFI_PEI_SERVICES **PeiServices + ) +{ + EFI_STATUS Status; + ARM_MP_CORE_INFO_PPI *ArmMpCoreInfoPpi; + UINTN ArmCoreCount; + ARM_CORE_INFO *ArmCoreInfoTable; + + // Enable program flow prediction, if supported. + ArmEnableBranchPrediction (); + + // Publish the CPU memory and io spaces sizes + BuildCpuHob (ArmGetPhysicalAddressBits (), PcdGet8 (PcdPrePiCpuIoSize)); + + // Only MP Core platform need to produce gArmMpCoreInfoPpiGuid + Status = PeiServicesLocatePpi (&gArmMpCoreInfoPpiGuid, 0, NULL, (VOID **)&ArmMpCoreInfoPpi); + if (!EFI_ERROR (Status)) { + // Build the MP Core Info Table + ArmCoreCount = 0; + Status = ArmMpCoreInfoPpi->GetMpCoreInfo (&ArmCoreCount, &ArmCoreInfoTable); + if (!EFI_ERROR (Status) && (ArmCoreCount > 0)) { + // Build MPCore Info HOB + BuildGuidDataHob (&gArmMpCoreInfoGuid, ArmCoreInfoTable, sizeof (ARM_CORE_INFO) * ArmCoreCount); + } + } + + return EFI_SUCCESS; +} diff --git a/ArmPkg/Drivers/CpuPei/CpuPei.inf b/ArmPkg/Drivers/CpuPei/CpuPei.inf new file mode 100644 index 000000000..a9f85cbc6 --- /dev/null +++ b/ArmPkg/Drivers/CpuPei/CpuPei.inf @@ -0,0 +1,52 @@ +## @file +# Component description file for BootMode module +# +# This module provides platform specific function to detect boot mode. +# Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +## + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = CpuPei + FILE_GUID = 2FD8B7AD-F8FA-4021-9FC0-0AA572147CDC + MODULE_TYPE = PEIM + VERSION_STRING = 1.0 + + ENTRY_POINT = InitializeCpuPeim + +# +# The following information is for reference only and not required by the build tools. +# +# VALID_ARCHITECTURES = ARM +# + +[Sources] + CpuPei.c + +[Packages] + MdePkg/MdePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + ArmPkg/ArmPkg.dec + +[LibraryClasses] + PeimEntryPoint + DebugLib + HobLib + ArmLib + +[Ppis] + gArmMpCoreInfoPpiGuid + +[Guids] + gArmMpCoreInfoGuid + +[FixedPcd] + gEmbeddedTokenSpaceGuid.PcdPrePiCpuIoSize + +[Depex] + gEfiPeiMemoryDiscoveredPpiGuid + diff --git a/ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdog.h b/ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdog.h new file mode 100644 index 000000000..9bc3bf470 --- /dev/null +++ b/ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdog.h @@ -0,0 +1,25 @@ +/** @file +* +* Copyright (c) 2013-2017, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef GENERIC_WATCHDOG_H_ +#define GENERIC_WATCHDOG_H_ + +// Refresh Frame: +#define GENERIC_WDOG_REFRESH_REG ((UINTN)FixedPcdGet64 (PcdGenericWatchdogRefreshBase) + 0x000) + +// Control Frame: +#define GENERIC_WDOG_CONTROL_STATUS_REG ((UINTN)FixedPcdGet64 (PcdGenericWatchdogControlBase) + 0x000) +#define GENERIC_WDOG_OFFSET_REG ((UINTN)FixedPcdGet64 (PcdGenericWatchdogControlBase) + 0x008) +#define GENERIC_WDOG_COMPARE_VALUE_REG_LOW ((UINTN)FixedPcdGet64 (PcdGenericWatchdogControlBase) + 0x010) +#define GENERIC_WDOG_COMPARE_VALUE_REG_HIGH ((UINTN)FixedPcdGet64 (PcdGenericWatchdogControlBase) + 0x014) + +// Values of bit 0 of the Control/Status Register +#define GENERIC_WDOG_ENABLED 1 +#define GENERIC_WDOG_DISABLED 0 + +#endif // GENERIC_WATCHDOG_H_ diff --git a/ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.c b/ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.c new file mode 100644 index 000000000..66c6c37c0 --- /dev/null +++ b/ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.c @@ -0,0 +1,383 @@ +/** @file +* +* Copyright (c) 2013-2018, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "GenericWatchdog.h" + +/* The number of 100ns periods (the unit of time passed to these functions) + in a second */ +#define TIME_UNITS_PER_SECOND 10000000 + +// Tick frequency of the generic timer basis of the generic watchdog. +STATIC UINTN mTimerFrequencyHz = 0; + +/* In cases where the compare register was set manually, information about + how long the watchdog was asked to wait cannot be retrieved from hardware. + It is therefore stored here. 0 means the timer is not running. */ +STATIC UINT64 mNumTimerTicks = 0; + +STATIC EFI_HARDWARE_INTERRUPT2_PROTOCOL *mInterruptProtocol; +STATIC EFI_WATCHDOG_TIMER_NOTIFY mWatchdogNotify; + +STATIC +VOID +WatchdogWriteOffsetRegister ( + UINT32 Value + ) +{ + MmioWrite32 (GENERIC_WDOG_OFFSET_REG, Value); +} + +STATIC +VOID +WatchdogWriteCompareRegister ( + UINT64 Value + ) +{ + MmioWrite32 (GENERIC_WDOG_COMPARE_VALUE_REG_LOW, Value & MAX_UINT32); + MmioWrite32 (GENERIC_WDOG_COMPARE_VALUE_REG_HIGH, (Value >> 32) & MAX_UINT32); +} + +STATIC +VOID +WatchdogEnable ( + VOID + ) +{ + MmioWrite32 (GENERIC_WDOG_CONTROL_STATUS_REG, GENERIC_WDOG_ENABLED); +} + +STATIC +VOID +WatchdogDisable ( + VOID + ) +{ + MmioWrite32 (GENERIC_WDOG_CONTROL_STATUS_REG, GENERIC_WDOG_DISABLED); +} + +/** On exiting boot services we must make sure the Watchdog Timer + is stopped. +**/ +STATIC +VOID +EFIAPI +WatchdogExitBootServicesEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + WatchdogDisable (); + mNumTimerTicks = 0; +} + +/* This function is called when the watchdog's first signal (WS0) goes high. + It uses the ResetSystem Runtime Service to reset the board. +*/ +STATIC +VOID +EFIAPI +WatchdogInterruptHandler ( + IN HARDWARE_INTERRUPT_SOURCE Source, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + STATIC CONST CHAR16 ResetString[] = L"The generic watchdog timer ran out."; + UINT64 TimerPeriod; + + WatchdogDisable (); + + mInterruptProtocol->EndOfInterrupt (mInterruptProtocol, Source); + + // + // The notify function should be called with the elapsed number of ticks + // since the watchdog was armed, which should exceed the timer period. + // We don't actually know the elapsed number of ticks, so let's return + // the timer period plus 1. + // + if (mWatchdogNotify != NULL) { + TimerPeriod = ((TIME_UNITS_PER_SECOND / mTimerFrequencyHz) * mNumTimerTicks); + mWatchdogNotify (TimerPeriod + 1); + } + + gRT->ResetSystem ( + EfiResetCold, + EFI_TIMEOUT, + StrSize (ResetString), + (CHAR16 *)ResetString + ); + + // If we got here then the reset didn't work + ASSERT (FALSE); +} + +/** + This function registers the handler NotifyFunction so it is called every time + the watchdog timer expires. It also passes the amount of time since the last + handler call to the NotifyFunction. + If NotifyFunction is not NULL and a handler is not already registered, + then the new handler is registered and EFI_SUCCESS is returned. + If NotifyFunction is NULL, and a handler is already registered, + then that handler is unregistered. + If an attempt is made to register a handler when a handler is already + registered, then EFI_ALREADY_STARTED is returned. + If an attempt is made to unregister a handler when a handler is not + registered, then EFI_INVALID_PARAMETER is returned. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param NotifyFunction The function to call when a timer interrupt fires. + This function executes at TPL_HIGH_LEVEL. The DXE + Core will register a handler for the timer interrupt, + so it can know how much time has passed. This + information is used to signal timer based events. + NULL will unregister the handler. + + @retval EFI_UNSUPPORTED The code does not support NotifyFunction. + +**/ +STATIC +EFI_STATUS +EFIAPI +WatchdogRegisterHandler ( + IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This, + IN EFI_WATCHDOG_TIMER_NOTIFY NotifyFunction + ) +{ + if ((mWatchdogNotify == NULL) && (NotifyFunction == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((mWatchdogNotify != NULL) && (NotifyFunction != NULL)) { + return EFI_ALREADY_STARTED; + } + + mWatchdogNotify = NotifyFunction; + return EFI_SUCCESS; +} + +/** + This function sets the amount of time to wait before firing the watchdog + timer to TimerPeriod 100ns units. If TimerPeriod is 0, then the watchdog + timer is disabled. + + @param This The EFI_WATCHDOG_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod The amount of time in 100ns units to wait before + the watchdog timer is fired. If TimerPeriod is zero, + then the watchdog timer is disabled. + + @retval EFI_SUCCESS The watchdog timer has been programmed to fire + in TimerPeriod 100ns units. + +**/ +STATIC +EFI_STATUS +EFIAPI +WatchdogSetTimerPeriod ( + IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This, + IN UINT64 TimerPeriod // In 100ns units + ) +{ + UINTN SystemCount; + + // if TimerPeriod is 0, this is a request to stop the watchdog. + if (TimerPeriod == 0) { + mNumTimerTicks = 0; + WatchdogDisable (); + return EFI_SUCCESS; + } + + // Work out how many timer ticks will equate to TimerPeriod + mNumTimerTicks = (mTimerFrequencyHz * TimerPeriod) / TIME_UNITS_PER_SECOND; + + /* If the number of required ticks is greater than the max the watchdog's + offset register (WOR) can hold, we need to manually compute and set + the compare register (WCV) */ + if (mNumTimerTicks > MAX_UINT32) { + /* We need to enable the watchdog *before* writing to the compare register, + because enabling the watchdog causes an "explicit refresh", which + clobbers the compare register (WCV). In order to make sure this doesn't + trigger an interrupt, set the offset to max. */ + WatchdogWriteOffsetRegister (MAX_UINT32); + WatchdogEnable (); + SystemCount = ArmGenericTimerGetSystemCount (); + WatchdogWriteCompareRegister (SystemCount + mNumTimerTicks); + } else { + WatchdogWriteOffsetRegister ((UINT32)mNumTimerTicks); + WatchdogEnable (); + } + + return EFI_SUCCESS; +} + +/** + This function retrieves the period of timer interrupts in 100ns units, + returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod + is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is + returned, then the timer is currently disabled. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod A pointer to the timer period to retrieve in + 100ns units. If 0 is returned, then the timer is + currently disabled. + + + @retval EFI_SUCCESS The timer period was returned in TimerPeriod. + @retval EFI_INVALID_PARAMETER TimerPeriod is NULL. + +**/ +STATIC +EFI_STATUS +EFIAPI +WatchdogGetTimerPeriod ( + IN EFI_WATCHDOG_TIMER_ARCH_PROTOCOL *This, + OUT UINT64 *TimerPeriod + ) +{ + if (TimerPeriod == NULL) { + return EFI_INVALID_PARAMETER; + } + + *TimerPeriod = ((TIME_UNITS_PER_SECOND / mTimerFrequencyHz) * mNumTimerTicks); + + return EFI_SUCCESS; +} + +/** + Interface structure for the Watchdog Architectural Protocol. + + @par Protocol Description: + This protocol provides a service to set the amount of time to wait + before firing the watchdog timer, and it also provides a service to + register a handler that is invoked when the watchdog timer fires. + + @par When the watchdog timer fires, control will be passed to a handler + if one has been registered. If no handler has been registered, + or the registered handler returns, then the system will be + reset by calling the Runtime Service ResetSystem(). + + @param RegisterHandler + Registers a handler that will be called each time the + watchdogtimer interrupt fires. TimerPeriod defines the minimum + time between timer interrupts, so TimerPeriod will also + be the minimum time between calls to the registered + handler. + NOTE: If the watchdog resets the system in hardware, then + this function will not have any chance of executing. + + @param SetTimerPeriod + Sets the period of the timer interrupt in 100ns units. + This function is optional, and may return EFI_UNSUPPORTED. + If this function is supported, then the timer period will + be rounded up to the nearest supported timer period. + + @param GetTimerPeriod + Retrieves the period of the timer interrupt in 100ns units. + +**/ +STATIC EFI_WATCHDOG_TIMER_ARCH_PROTOCOL mWatchdogTimer = { + WatchdogRegisterHandler, + WatchdogSetTimerPeriod, + WatchdogGetTimerPeriod +}; + +STATIC EFI_EVENT mEfiExitBootServicesEvent; + +EFI_STATUS +EFIAPI +GenericWatchdogEntry ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + EFI_HANDLE Handle; + + Status = gBS->LocateProtocol ( + &gHardwareInterrupt2ProtocolGuid, + NULL, + (VOID **)&mInterruptProtocol + ); + ASSERT_EFI_ERROR (Status); + + /* Make sure the Watchdog Timer Architectural Protocol has not been installed + in the system yet. + This will avoid conflicts with the universal watchdog */ + ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiWatchdogTimerArchProtocolGuid); + + mTimerFrequencyHz = ArmGenericTimerGetTimerFreq (); + ASSERT (mTimerFrequencyHz != 0); + + // Install interrupt handler + Status = mInterruptProtocol->RegisterInterruptSource ( + mInterruptProtocol, + FixedPcdGet32 (PcdGenericWatchdogEl2IntrNum), + WatchdogInterruptHandler + ); + if (EFI_ERROR (Status)) { + return Status; + } + + Status = mInterruptProtocol->SetTriggerType ( + mInterruptProtocol, + FixedPcdGet32 (PcdGenericWatchdogEl2IntrNum), + EFI_HARDWARE_INTERRUPT2_TRIGGER_EDGE_RISING + ); + if (EFI_ERROR (Status)) { + goto UnregisterHandler; + } + + // Install the Timer Architectural Protocol onto a new handle + Handle = NULL; + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiWatchdogTimerArchProtocolGuid, + &mWatchdogTimer, + NULL + ); + if (EFI_ERROR (Status)) { + goto UnregisterHandler; + } + + // Register for an ExitBootServicesEvent + Status = gBS->CreateEvent ( + EVT_SIGNAL_EXIT_BOOT_SERVICES, + TPL_NOTIFY, + WatchdogExitBootServicesEvent, + NULL, + &mEfiExitBootServicesEvent + ); + ASSERT_EFI_ERROR (Status); + + mNumTimerTicks = 0; + WatchdogDisable (); + + return EFI_SUCCESS; + +UnregisterHandler: + // Unregister the handler + mInterruptProtocol->RegisterInterruptSource ( + mInterruptProtocol, + FixedPcdGet32 (PcdGenericWatchdogEl2IntrNum), + NULL + ); + return Status; +} diff --git a/ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf b/ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf new file mode 100644 index 000000000..7dbd30783 --- /dev/null +++ b/ArmPkg/Drivers/GenericWatchdogDxe/GenericWatchdogDxe.inf @@ -0,0 +1,47 @@ +# +# Copyright (c) 2013-2021, Arm Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# + +[Defines] + INF_VERSION = 0x00010016 + BASE_NAME = GenericWatchdogDxe + FILE_GUID = 0619f5c2-4858-4caa-a86a-73a21a18df6b + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = GenericWatchdogEntry + +[Sources.common] + GenericWatchdog.h + GenericWatchdogDxe.c + +[Packages] + ArmPkg/ArmPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + EmbeddedPkg/EmbeddedPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + ArmGenericTimerCounterLib + BaseLib + BaseMemoryLib + DebugLib + IoLib + PcdLib + UefiLib + UefiBootServicesTableLib + UefiDriverEntryPoint + UefiRuntimeServicesTableLib + +[Pcd.common] + gArmTokenSpaceGuid.PcdGenericWatchdogControlBase + gArmTokenSpaceGuid.PcdGenericWatchdogRefreshBase + gArmTokenSpaceGuid.PcdGenericWatchdogEl2IntrNum + +[Protocols] + gEfiWatchdogTimerArchProtocolGuid ## ALWAYS_PRODUCES + gHardwareInterrupt2ProtocolGuid ## ALWAYS_CONSUMES + +[Depex] + gHardwareInterrupt2ProtocolGuid diff --git a/ArmPkg/Drivers/MmCommunicationDxe/MmCommunicate.h b/ArmPkg/Drivers/MmCommunicationDxe/MmCommunicate.h new file mode 100644 index 000000000..5c5fcb576 --- /dev/null +++ b/ArmPkg/Drivers/MmCommunicationDxe/MmCommunicate.h @@ -0,0 +1,22 @@ +/** @file + + Copyright (c) 2016-2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef MM_COMMUNICATE_H_ +#define MM_COMMUNICATE_H_ + +#define MM_MAJOR_VER_MASK 0xEFFF0000 +#define MM_MINOR_VER_MASK 0x0000FFFF +#define MM_MAJOR_VER_SHIFT 16 + +#define MM_MAJOR_VER(x) (((x) & MM_MAJOR_VER_MASK) >> MM_MAJOR_VER_SHIFT) +#define MM_MINOR_VER(x) ((x) & MM_MINOR_VER_MASK) + +#define MM_CALLER_MAJOR_VER 0x1UL +#define MM_CALLER_MINOR_VER 0x0 + +#endif /* MM_COMMUNICATE_H_ */ diff --git a/ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c b/ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c new file mode 100644 index 000000000..85d903455 --- /dev/null +++ b/ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.c @@ -0,0 +1,458 @@ +/** @file + + Copyright (c) 2016-2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include "MmCommunicate.h" + +// +// Address, Length of the pre-allocated buffer for communication with the secure +// world. +// +STATIC ARM_MEMORY_REGION_DESCRIPTOR mNsCommBuffMemRegion; + +// Notification event when virtual address map is set. +STATIC EFI_EVENT mSetVirtualAddressMapEvent; + +// +// Handle to install the MM Communication Protocol +// +STATIC EFI_HANDLE mMmCommunicateHandle; + +/** + Communicates with a registered handler. + + This function provides a service to send and receive messages from a registered UEFI service. + + @param[in] This The EFI_MM_COMMUNICATION_PROTOCOL instance. + @param[in, out] CommBufferPhysical Physical address of the MM communication buffer + @param[in, out] CommBufferVirtual Virtual address of the MM communication buffer + @param[in, out] CommSize The size of the data buffer being passed in. On input, + when not omitted, the buffer should cover EFI_MM_COMMUNICATE_HEADER + and the value of MessageLength field. On exit, the size + of data being returned. Zero if the handler does not + wish to reply with any data. This parameter is optional + and may be NULL. + + @retval EFI_SUCCESS The message was successfully posted. + @retval EFI_INVALID_PARAMETER CommBufferPhysical or CommBufferVirtual was NULL, or + integer value pointed by CommSize does not cover + EFI_MM_COMMUNICATE_HEADER and the value of MessageLength + field. + @retval EFI_BAD_BUFFER_SIZE The buffer is too large for the MM implementation. + If this error is returned, the MessageLength field + in the CommBuffer header or the integer pointed by + CommSize, are updated to reflect the maximum payload + size the implementation can accommodate. + @retval EFI_ACCESS_DENIED The CommunicateBuffer parameter or CommSize parameter, + if not omitted, are in address range that cannot be + accessed by the MM environment. + +**/ +EFI_STATUS +EFIAPI +MmCommunication2Communicate ( + IN CONST EFI_MM_COMMUNICATION2_PROTOCOL *This, + IN OUT VOID *CommBufferPhysical, + IN OUT VOID *CommBufferVirtual, + IN OUT UINTN *CommSize OPTIONAL + ) +{ + EFI_MM_COMMUNICATE_HEADER *CommunicateHeader; + ARM_SMC_ARGS CommunicateSmcArgs; + EFI_STATUS Status; + UINTN BufferSize; + + Status = EFI_ACCESS_DENIED; + BufferSize = 0; + + ZeroMem (&CommunicateSmcArgs, sizeof (ARM_SMC_ARGS)); + + // + // Check parameters + // + if ((CommBufferVirtual == NULL) || (CommBufferPhysical == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Status = EFI_SUCCESS; + CommunicateHeader = CommBufferVirtual; + // CommBuffer is a mandatory parameter. Hence, Rely on + // MessageLength + Header to ascertain the + // total size of the communication payload rather than + // rely on optional CommSize parameter + BufferSize = CommunicateHeader->MessageLength + + sizeof (CommunicateHeader->HeaderGuid) + + sizeof (CommunicateHeader->MessageLength); + + // If CommSize is not omitted, perform size inspection before proceeding. + if (CommSize != NULL) { + // This case can be used by the consumer of this driver to find out the + // max size that can be used for allocating CommBuffer. + if ((*CommSize == 0) || + (*CommSize > mNsCommBuffMemRegion.Length)) + { + *CommSize = mNsCommBuffMemRegion.Length; + Status = EFI_BAD_BUFFER_SIZE; + } + + // + // CommSize should cover at least MessageLength + sizeof (EFI_MM_COMMUNICATE_HEADER); + // + if (*CommSize < BufferSize) { + Status = EFI_INVALID_PARAMETER; + } + } + + // + // If the message length is 0 or greater than what can be tolerated by the MM + // environment then return the expected size. + // + if ((CommunicateHeader->MessageLength == 0) || + (BufferSize > mNsCommBuffMemRegion.Length)) + { + CommunicateHeader->MessageLength = mNsCommBuffMemRegion.Length - + sizeof (CommunicateHeader->HeaderGuid) - + sizeof (CommunicateHeader->MessageLength); + Status = EFI_BAD_BUFFER_SIZE; + } + + // MessageLength or CommSize check has failed, return here. + if (EFI_ERROR (Status)) { + return Status; + } + + // SMC Function ID + CommunicateSmcArgs.Arg0 = ARM_SMC_ID_MM_COMMUNICATE_AARCH64; + + // Cookie + CommunicateSmcArgs.Arg1 = 0; + + // Copy Communication Payload + CopyMem ((VOID *)mNsCommBuffMemRegion.VirtualBase, CommBufferVirtual, BufferSize); + + // comm_buffer_address (64-bit physical address) + CommunicateSmcArgs.Arg2 = (UINTN)mNsCommBuffMemRegion.PhysicalBase; + + // comm_size_address (not used, indicated by setting to zero) + CommunicateSmcArgs.Arg3 = 0; + + // Call the Standalone MM environment. + ArmCallSmc (&CommunicateSmcArgs); + + switch (CommunicateSmcArgs.Arg0) { + case ARM_SMC_MM_RET_SUCCESS: + ZeroMem (CommBufferVirtual, BufferSize); + // On successful return, the size of data being returned is inferred from + // MessageLength + Header. + CommunicateHeader = (EFI_MM_COMMUNICATE_HEADER *)mNsCommBuffMemRegion.VirtualBase; + BufferSize = CommunicateHeader->MessageLength + + sizeof (CommunicateHeader->HeaderGuid) + + sizeof (CommunicateHeader->MessageLength); + + CopyMem ( + CommBufferVirtual, + (VOID *)mNsCommBuffMemRegion.VirtualBase, + BufferSize + ); + Status = EFI_SUCCESS; + break; + + case ARM_SMC_MM_RET_INVALID_PARAMS: + Status = EFI_INVALID_PARAMETER; + break; + + case ARM_SMC_MM_RET_DENIED: + Status = EFI_ACCESS_DENIED; + break; + + case ARM_SMC_MM_RET_NO_MEMORY: + // Unexpected error since the CommSize was checked for zero length + // prior to issuing the SMC + Status = EFI_OUT_OF_RESOURCES; + ASSERT (0); + break; + + default: + Status = EFI_ACCESS_DENIED; + ASSERT (0); + } + + return Status; +} + +// +// MM Communication Protocol instance +// +STATIC EFI_MM_COMMUNICATION2_PROTOCOL mMmCommunication2 = { + MmCommunication2Communicate +}; + +/** + Notification callback on SetVirtualAddressMap event. + + This function notifies the MM communication protocol interface on + SetVirtualAddressMap event and converts pointers used in this driver + from physical to virtual address. + + @param Event SetVirtualAddressMap event. + @param Context A context when the SetVirtualAddressMap triggered. + + @retval EFI_SUCCESS The function executed successfully. + @retval Other Some error occurred when executing this function. + +**/ +STATIC +VOID +EFIAPI +NotifySetVirtualAddressMap ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_STATUS Status; + + Status = gRT->ConvertPointer ( + EFI_OPTIONAL_PTR, + (VOID **)&mNsCommBuffMemRegion.VirtualBase + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "NotifySetVirtualAddressMap():" + " Unable to convert MM runtime pointer. Status:0x%r\n", + Status + )); + } +} + +STATIC +EFI_STATUS +GetMmCompatibility ( + ) +{ + EFI_STATUS Status; + UINT32 MmVersion; + ARM_SMC_ARGS MmVersionArgs; + + // MM_VERSION uses SMC32 calling conventions + MmVersionArgs.Arg0 = ARM_SMC_ID_MM_VERSION_AARCH32; + + ArmCallSmc (&MmVersionArgs); + + MmVersion = MmVersionArgs.Arg0; + + if ((MM_MAJOR_VER (MmVersion) == MM_CALLER_MAJOR_VER) && + (MM_MINOR_VER (MmVersion) >= MM_CALLER_MINOR_VER)) + { + DEBUG (( + DEBUG_INFO, + "MM Version: Major=0x%x, Minor=0x%x\n", + MM_MAJOR_VER (MmVersion), + MM_MINOR_VER (MmVersion) + )); + Status = EFI_SUCCESS; + } else { + DEBUG (( + DEBUG_ERROR, + "Incompatible MM Versions.\n Current Version: Major=0x%x, Minor=0x%x.\n Expected: Major=0x%x, Minor>=0x%x.\n", + MM_MAJOR_VER (MmVersion), + MM_MINOR_VER (MmVersion), + MM_CALLER_MAJOR_VER, + MM_CALLER_MINOR_VER + )); + Status = EFI_UNSUPPORTED; + } + + return Status; +} + +STATIC EFI_GUID *CONST mGuidedEventGuid[] = { + &gEfiEndOfDxeEventGroupGuid, + &gEfiEventExitBootServicesGuid, + &gEfiEventReadyToBootGuid, +}; + +STATIC EFI_EVENT mGuidedEvent[ARRAY_SIZE (mGuidedEventGuid)]; + +/** + Event notification that is fired when GUIDed Event Group is signaled. + + @param Event The Event that is being processed, not used. + @param Context Event Context, not used. + +**/ +STATIC +VOID +EFIAPI +MmGuidedEventNotify ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + EFI_MM_COMMUNICATE_HEADER Header; + UINTN Size; + + // + // Use Guid to initialize EFI_SMM_COMMUNICATE_HEADER structure + // + CopyGuid (&Header.HeaderGuid, Context); + Header.MessageLength = 1; + Header.Data[0] = 0; + + Size = sizeof (Header); + MmCommunication2Communicate (&mMmCommunication2, &Header, &Header, &Size); +} + +/** + The Entry Point for MM Communication + + This function installs the MM communication protocol interface and finds out + what type of buffer management will be required prior to invoking the + communication SMC. + + @param ImageHandle The firmware allocated handle for the EFI image. + @param SystemTable A pointer to the EFI System Table. + + @retval EFI_SUCCESS The entry point is executed successfully. + @retval Other Some error occurred when executing this entry point. + +**/ +EFI_STATUS +EFIAPI +MmCommunication2Initialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + UINTN Index; + + // Check if we can make the MM call + Status = GetMmCompatibility (); + if (EFI_ERROR (Status)) { + goto ReturnErrorStatus; + } + + mNsCommBuffMemRegion.PhysicalBase = PcdGet64 (PcdMmBufferBase); + // During boot , Virtual and Physical are same + mNsCommBuffMemRegion.VirtualBase = mNsCommBuffMemRegion.PhysicalBase; + mNsCommBuffMemRegion.Length = PcdGet64 (PcdMmBufferSize); + + ASSERT (mNsCommBuffMemRegion.PhysicalBase != 0); + + ASSERT (mNsCommBuffMemRegion.Length != 0); + + Status = gDS->AddMemorySpace ( + EfiGcdMemoryTypeReserved, + mNsCommBuffMemRegion.PhysicalBase, + mNsCommBuffMemRegion.Length, + EFI_MEMORY_WB | + EFI_MEMORY_XP | + EFI_MEMORY_RUNTIME + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "MmCommunicateInitialize: " + "Failed to add MM-NS Buffer Memory Space\n" + )); + goto ReturnErrorStatus; + } + + Status = gDS->SetMemorySpaceAttributes ( + mNsCommBuffMemRegion.PhysicalBase, + mNsCommBuffMemRegion.Length, + EFI_MEMORY_WB | EFI_MEMORY_XP | EFI_MEMORY_RUNTIME + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "MmCommunicateInitialize: " + "Failed to set MM-NS Buffer Memory attributes\n" + )); + goto CleanAddedMemorySpace; + } + + // Install the communication protocol + Status = gBS->InstallProtocolInterface ( + &mMmCommunicateHandle, + &gEfiMmCommunication2ProtocolGuid, + EFI_NATIVE_INTERFACE, + &mMmCommunication2 + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_ERROR, + "MmCommunicationInitialize: " + "Failed to install MM communication protocol\n" + )); + goto CleanAddedMemorySpace; + } + + // Register notification callback when virtual address is associated + // with the physical address. + // Create a Set Virtual Address Map event. + Status = gBS->CreateEvent ( + EVT_SIGNAL_VIRTUAL_ADDRESS_CHANGE, + TPL_NOTIFY, + NotifySetVirtualAddressMap, + NULL, + &mSetVirtualAddressMapEvent + ); + ASSERT_EFI_ERROR (Status); + + for (Index = 0; Index < ARRAY_SIZE (mGuidedEventGuid); Index++) { + Status = gBS->CreateEventEx ( + EVT_NOTIFY_SIGNAL, + TPL_CALLBACK, + MmGuidedEventNotify, + mGuidedEventGuid[Index], + mGuidedEventGuid[Index], + &mGuidedEvent[Index] + ); + ASSERT_EFI_ERROR (Status); + if (EFI_ERROR (Status)) { + while (Index-- > 0) { + gBS->CloseEvent (mGuidedEvent[Index]); + } + + goto UninstallProtocol; + } + } + + return EFI_SUCCESS; + +UninstallProtocol: + gBS->UninstallProtocolInterface ( + mMmCommunicateHandle, + &gEfiMmCommunication2ProtocolGuid, + &mMmCommunication2 + ); + +CleanAddedMemorySpace: + gDS->RemoveMemorySpace ( + mNsCommBuffMemRegion.PhysicalBase, + mNsCommBuffMemRegion.Length + ); + +ReturnErrorStatus: + return EFI_INVALID_PARAMETER; +} diff --git a/ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.inf b/ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.inf new file mode 100644 index 000000000..05b6de73f --- /dev/null +++ b/ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.inf @@ -0,0 +1,56 @@ +#/** @file +# +# DXE MM Communicate driver +# +# Copyright (c) 2016 - 2021, Arm Limited. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 0x0001001A + BASE_NAME = ArmMmCommunication + FILE_GUID = 09EE81D3-F15E-43F4-85B4-CB9873DA5D6B + MODULE_TYPE = DXE_RUNTIME_DRIVER + VERSION_STRING = 1.0 + ENTRY_POINT = MmCommunication2Initialize + +# +# The following is for reference only and not required by +# build tools +# +# VALID_ARCHITECTURES = AARCH64 +# + +[Sources.AARCH64] + MmCommunicate.h + MmCommunication.c + +[Packages] + ArmPkg/ArmPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + ArmLib + ArmSmcLib + BaseMemoryLib + DebugLib + DxeServicesTableLib + HobLib + UefiDriverEntryPoint + +[Protocols] + gEfiMmCommunication2ProtocolGuid ## PRODUCES + +[Guids] + gEfiEndOfDxeEventGroupGuid + gEfiEventExitBootServicesGuid + gEfiEventReadyToBootGuid + +[Pcd.common] + gArmTokenSpaceGuid.PcdMmBufferBase + gArmTokenSpaceGuid.PcdMmBufferSize + +[Depex] + gEfiCpuArchProtocolGuid diff --git a/ArmPkg/Drivers/TimerDxe/TimerDxe.c b/ArmPkg/Drivers/TimerDxe/TimerDxe.c new file mode 100644 index 000000000..1559b323e --- /dev/null +++ b/ArmPkg/Drivers/TimerDxe/TimerDxe.c @@ -0,0 +1,427 @@ +/** @file + Timer Architecture Protocol driver of the ARM flavor + + Copyright (c) 2011-2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +// The notification function to call on every timer interrupt. +EFI_TIMER_NOTIFY mTimerNotifyFunction = (EFI_TIMER_NOTIFY)NULL; +EFI_EVENT EfiExitBootServicesEvent = (EFI_EVENT)NULL; + +// The current period of the timer interrupt +UINT64 mTimerPeriod = 0; +// The latest Timer Tick calculated for mTimerPeriod +UINT64 mTimerTicks = 0; +// Number of elapsed period since the last Timer interrupt +UINT64 mElapsedPeriod = 1; + +// Cached copy of the Hardware Interrupt protocol instance +EFI_HARDWARE_INTERRUPT_PROTOCOL *gInterrupt = NULL; + +/** + This function registers the handler NotifyFunction so it is called every time + the timer interrupt fires. It also passes the amount of time since the last + handler call to the NotifyFunction. If NotifyFunction is NULL, then the + handler is unregistered. If the handler is registered, then EFI_SUCCESS is + returned. If the CPU does not support registering a timer interrupt handler, + then EFI_UNSUPPORTED is returned. If an attempt is made to register a handler + when a handler is already registered, then EFI_ALREADY_STARTED is returned. + If an attempt is made to unregister a handler when a handler is not registered, + then EFI_INVALID_PARAMETER is returned. If an error occurs attempting to + register the NotifyFunction with the timer interrupt, then EFI_DEVICE_ERROR + is returned. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param NotifyFunction The function to call when a timer interrupt fires. This + function executes at TPL_HIGH_LEVEL. The DXE Core will + register a handler for the timer interrupt, so it can know + how much time has passed. This information is used to + signal timer based events. NULL will unregister the handler. + @retval EFI_SUCCESS The timer handler was registered. + @retval EFI_UNSUPPORTED The platform does not support timer interrupts. + @retval EFI_ALREADY_STARTED NotifyFunction is not NULL, and a handler is already + registered. + @retval EFI_INVALID_PARAMETER NotifyFunction is NULL, and a handler was not + previously registered. + @retval EFI_DEVICE_ERROR The timer handler could not be registered. + +**/ +EFI_STATUS +EFIAPI +TimerDriverRegisterHandler ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + IN EFI_TIMER_NOTIFY NotifyFunction + ) +{ + if ((NotifyFunction == NULL) && (mTimerNotifyFunction == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((NotifyFunction != NULL) && (mTimerNotifyFunction != NULL)) { + return EFI_ALREADY_STARTED; + } + + mTimerNotifyFunction = NotifyFunction; + + return EFI_SUCCESS; +} + +/** + Disable the timer +**/ +VOID +EFIAPI +ExitBootServicesEvent ( + IN EFI_EVENT Event, + IN VOID *Context + ) +{ + ArmGenericTimerDisableTimer (); +} + +/** + + This function adjusts the period of timer interrupts to the value specified + by TimerPeriod. If the timer period is updated, then the selected timer + period is stored in EFI_TIMER.TimerPeriod, and EFI_SUCCESS is returned. If + the timer hardware is not programmable, then EFI_UNSUPPORTED is returned. + If an error occurs while attempting to update the timer period, then the + timer hardware will be put back in its state prior to this call, and + EFI_DEVICE_ERROR is returned. If TimerPeriod is 0, then the timer interrupt + is disabled. This is not the same as disabling the CPU's interrupts. + Instead, it must either turn off the timer hardware, or it must adjust the + interrupt controller so that a CPU interrupt is not generated when the timer + interrupt fires. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod The rate to program the timer interrupt in 100 nS units. If + the timer hardware is not programmable, then EFI_UNSUPPORTED is + returned. If the timer is programmable, then the timer period + will be rounded up to the nearest timer period that is supported + by the timer hardware. If TimerPeriod is set to 0, then the + timer interrupts will be disabled. + + + @retval EFI_SUCCESS The timer period was changed. + @retval EFI_UNSUPPORTED The platform cannot change the period of the timer interrupt. + @retval EFI_DEVICE_ERROR The timer period could not be changed due to a device error. + +**/ +EFI_STATUS +EFIAPI +TimerDriverSetTimerPeriod ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + IN UINT64 TimerPeriod + ) +{ + UINT64 CounterValue; + UINT64 TimerTicks; + EFI_TPL OriginalTPL; + + // Always disable the timer + ArmGenericTimerDisableTimer (); + + if (TimerPeriod != 0) { + // mTimerTicks = TimerPeriod in 1ms unit x Frequency.10^-3 + // = TimerPeriod.10^-4 x Frequency.10^-3 + // = (TimerPeriod x Frequency) x 10^-7 + TimerTicks = MultU64x32 (TimerPeriod, ArmGenericTimerGetTimerFreq ()); + TimerTicks = DivU64x32 (TimerTicks, 10000000U); + + // Raise TPL to update the mTimerTicks and mTimerPeriod to ensure these values + // are coherent in the interrupt handler + OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + mTimerTicks = TimerTicks; + mTimerPeriod = TimerPeriod; + mElapsedPeriod = 1; + + gBS->RestoreTPL (OriginalTPL); + + // Get value of the current timer + CounterValue = ArmGenericTimerGetSystemCount (); + // Set the interrupt in Current Time + mTimerTick + ArmGenericTimerSetCompareVal (CounterValue + mTimerTicks); + + // Enable the timer + ArmGenericTimerEnableTimer (); + } else { + // Save the new timer period + mTimerPeriod = TimerPeriod; + // Reset the elapsed period + mElapsedPeriod = 1; + } + + return EFI_SUCCESS; +} + +/** + This function retrieves the period of timer interrupts in 100 ns units, + returns that value in TimerPeriod, and returns EFI_SUCCESS. If TimerPeriod + is NULL, then EFI_INVALID_PARAMETER is returned. If a TimerPeriod of 0 is + returned, then the timer is currently disabled. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + @param TimerPeriod A pointer to the timer period to retrieve in 100 ns units. If + 0 is returned, then the timer is currently disabled. + + + @retval EFI_SUCCESS The timer period was returned in TimerPeriod. + @retval EFI_INVALID_PARAMETER TimerPeriod is NULL. + +**/ +EFI_STATUS +EFIAPI +TimerDriverGetTimerPeriod ( + IN EFI_TIMER_ARCH_PROTOCOL *This, + OUT UINT64 *TimerPeriod + ) +{ + if (TimerPeriod == NULL) { + return EFI_INVALID_PARAMETER; + } + + *TimerPeriod = mTimerPeriod; + return EFI_SUCCESS; +} + +/** + This function generates a soft timer interrupt. If the platform does not support soft + timer interrupts, then EFI_UNSUPPORTED is returned. Otherwise, EFI_SUCCESS is returned. + If a handler has been registered through the EFI_TIMER_ARCH_PROTOCOL.RegisterHandler() + service, then a soft timer interrupt will be generated. If the timer interrupt is + enabled when this service is called, then the registered handler will be invoked. The + registered handler should not be able to distinguish a hardware-generated timer + interrupt from a software-generated timer interrupt. + + @param This The EFI_TIMER_ARCH_PROTOCOL instance. + + @retval EFI_SUCCESS The soft timer interrupt was generated. + @retval EFI_UNSUPPORTED The platform does not support the generation of soft timer interrupts. + +**/ +EFI_STATUS +EFIAPI +TimerDriverGenerateSoftInterrupt ( + IN EFI_TIMER_ARCH_PROTOCOL *This + ) +{ + return EFI_UNSUPPORTED; +} + +/** + Interface structure for the Timer Architectural Protocol. + + @par Protocol Description: + This protocol provides the services to initialize a periodic timer + interrupt, and to register a handler that is called each time the timer + interrupt fires. It may also provide a service to adjust the rate of the + periodic timer interrupt. When a timer interrupt occurs, the handler is + passed the amount of time that has passed since the previous timer + interrupt. + + @param RegisterHandler + Registers a handler that will be called each time the + timer interrupt fires. TimerPeriod defines the minimum + time between timer interrupts, so TimerPeriod will also + be the minimum time between calls to the registered + handler. + + @param SetTimerPeriod + Sets the period of the timer interrupt in 100 nS units. + This function is optional, and may return EFI_UNSUPPORTED. + If this function is supported, then the timer period will + be rounded up to the nearest supported timer period. + + + @param GetTimerPeriod + Retrieves the period of the timer interrupt in 100 nS units. + + @param GenerateSoftInterrupt + Generates a soft timer interrupt that simulates the firing of + the timer interrupt. This service can be used to invoke the registered handler if the timer interrupt has been masked for + a period of time. + +**/ +EFI_TIMER_ARCH_PROTOCOL gTimer = { + TimerDriverRegisterHandler, + TimerDriverSetTimerPeriod, + TimerDriverGetTimerPeriod, + TimerDriverGenerateSoftInterrupt +}; + +/** + + C Interrupt Handler called in the interrupt context when Source interrupt is active. + + + @param Source Source of the interrupt. Hardware routing off a specific platform defines + what source means. + + @param SystemContext Pointer to system register context. Mostly used by debuggers and will + update the system context after the return from the interrupt if + modified. Don't change these values unless you know what you are doing + +**/ +VOID +EFIAPI +TimerInterruptHandler ( + IN HARDWARE_INTERRUPT_SOURCE Source, + IN EFI_SYSTEM_CONTEXT SystemContext + ) +{ + EFI_TPL OriginalTPL; + UINT64 CurrentValue; + UINT64 CompareValue; + + // + // DXE core uses this callback for the EFI timer tick. The DXE core uses locks + // that raise to TPL_HIGH and then restore back to current level. Thus we need + // to make sure TPL level is set to TPL_HIGH while we are handling the timer tick. + // + OriginalTPL = gBS->RaiseTPL (TPL_HIGH_LEVEL); + + // Signal end of interrupt early to help avoid losing subsequent ticks + // from long duration handlers + gInterrupt->EndOfInterrupt (gInterrupt, Source); + + // Check if the timer interrupt is active + if ((ArmGenericTimerGetTimerCtrlReg ()) & ARM_ARCH_TIMER_ISTATUS) { + if (mTimerNotifyFunction != 0) { + mTimerNotifyFunction (mTimerPeriod * mElapsedPeriod); + } + + // + // Reload the Timer + // + + // Get current counter value + CurrentValue = ArmGenericTimerGetSystemCount (); + // Get the counter value to compare with + CompareValue = ArmGenericTimerGetCompareVal (); + + // This loop is needed in case we missed interrupts (eg: case when the interrupt handling + // has taken longer than mTickPeriod). + // Note: Physical Counter is counting up + mElapsedPeriod = 0; + do { + CompareValue += mTimerTicks; + mElapsedPeriod++; + } while (CompareValue < CurrentValue); + + // Set next compare value + ArmGenericTimerSetCompareVal (CompareValue); + ArmGenericTimerReenableTimer (); + ArmInstructionSynchronizationBarrier (); + } + + gBS->RestoreTPL (OriginalTPL); +} + +/** + Initialize the state information for the Timer Architectural Protocol and + the Timer Debug support protocol that allows the debugger to break into a + running program. + + @param ImageHandle of the loaded driver + @param SystemTable Pointer to the System Table + + @retval EFI_SUCCESS Protocol registered + @retval EFI_OUT_OF_RESOURCES Cannot allocate protocol data structure + @retval EFI_DEVICE_ERROR Hardware problems + +**/ +EFI_STATUS +EFIAPI +TimerInitialize ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_HANDLE Handle; + EFI_STATUS Status; + UINTN TimerCtrlReg; + UINT32 TimerHypIntrNum; + + if (ArmIsArchTimerImplemented () == 0) { + DEBUG ((DEBUG_ERROR, "ARM Architectural Timer is not available in the CPU, hence can't use this Driver \n")); + ASSERT (0); + } + + // Find the interrupt controller protocol. ASSERT if not found. + Status = gBS->LocateProtocol (&gHardwareInterruptProtocolGuid, NULL, (VOID **)&gInterrupt); + ASSERT_EFI_ERROR (Status); + + // Disable the timer + TimerCtrlReg = ArmGenericTimerGetTimerCtrlReg (); + TimerCtrlReg |= ARM_ARCH_TIMER_IMASK; + TimerCtrlReg &= ~ARM_ARCH_TIMER_ENABLE; + ArmGenericTimerSetTimerCtrlReg (TimerCtrlReg); + Status = TimerDriverSetTimerPeriod (&gTimer, 0); + ASSERT_EFI_ERROR (Status); + + // Install secure and Non-secure interrupt handlers + // Note: Because it is not possible to determine the security state of the + // CPU dynamically, we just install interrupt handler for both sec and non-sec + // timer PPI + Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerVirtIntrNum), TimerInterruptHandler); + ASSERT_EFI_ERROR (Status); + + // + // The hypervisor timer interrupt may be omitted by implementations that + // execute under virtualization. + // + TimerHypIntrNum = PcdGet32 (PcdArmArchTimerHypIntrNum); + if (TimerHypIntrNum != 0) { + Status = gInterrupt->RegisterInterruptSource (gInterrupt, TimerHypIntrNum, TimerInterruptHandler); + ASSERT_EFI_ERROR (Status); + } + + Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerSecIntrNum), TimerInterruptHandler); + ASSERT_EFI_ERROR (Status); + + Status = gInterrupt->RegisterInterruptSource (gInterrupt, PcdGet32 (PcdArmArchTimerIntrNum), TimerInterruptHandler); + ASSERT_EFI_ERROR (Status); + + // Set up default timer + Status = TimerDriverSetTimerPeriod (&gTimer, FixedPcdGet32 (PcdTimerPeriod)); // TIMER_DEFAULT_PERIOD + ASSERT_EFI_ERROR (Status); + + Handle = NULL; + // Install the Timer Architectural Protocol onto a new handle + Status = gBS->InstallMultipleProtocolInterfaces ( + &Handle, + &gEfiTimerArchProtocolGuid, + &gTimer, + NULL + ); + ASSERT_EFI_ERROR (Status); + + // Everything is ready, unmask and enable timer interrupts + TimerCtrlReg = ARM_ARCH_TIMER_ENABLE; + ArmGenericTimerSetTimerCtrlReg (TimerCtrlReg); + + // Register for an ExitBootServicesEvent + Status = gBS->CreateEvent (EVT_SIGNAL_EXIT_BOOT_SERVICES, TPL_NOTIFY, ExitBootServicesEvent, NULL, &EfiExitBootServicesEvent); + ASSERT_EFI_ERROR (Status); + + return Status; +} diff --git a/ArmPkg/Drivers/TimerDxe/TimerDxe.inf b/ArmPkg/Drivers/TimerDxe/TimerDxe.inf new file mode 100644 index 000000000..8b2d4b0fa --- /dev/null +++ b/ArmPkg/Drivers/TimerDxe/TimerDxe.inf @@ -0,0 +1,54 @@ +#/** @file +# +# Component description file for Timer DXE module +# +# Copyright (c) 2009 - 2010, Apple Inc. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArmTimerDxe + FILE_GUID = 49ea041e-6752-42ca-b0b1-7344fe2546b7 + MODULE_TYPE = DXE_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = TimerInitialize + +[Sources.common] + TimerDxe.c + +[Packages] + MdePkg/MdePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + ArmPkg/ArmPkg.dec + ArmPlatformPkg/ArmPlatformPkg.dec + +[LibraryClasses] + ArmLib + BaseLib + UefiRuntimeServicesTableLib + UefiLib + UefiBootServicesTableLib + BaseMemoryLib + DebugLib + UefiDriverEntryPoint + IoLib + ArmGenericTimerCounterLib + +[Guids] + +[Protocols] + gEfiTimerArchProtocolGuid + gHardwareInterruptProtocolGuid + +[Pcd.common] + gEmbeddedTokenSpaceGuid.PcdTimerPeriod + gArmTokenSpaceGuid.PcdArmArchTimerSecIntrNum + gArmTokenSpaceGuid.PcdArmArchTimerIntrNum + gArmTokenSpaceGuid.PcdArmArchTimerVirtIntrNum + gArmTokenSpaceGuid.PcdArmArchTimerHypIntrNum + +[Depex] + gHardwareInterruptProtocolGuid diff --git a/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c b/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c new file mode 100644 index 000000000..39a30533e --- /dev/null +++ b/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.c @@ -0,0 +1,1233 @@ +/** @file + Support a Semi Host file system over a debuggers JTAG + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Portions copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "SemihostFs.h" + +#define DEFAULT_SEMIHOST_FS_LABEL L"SemihostFs" + +STATIC CHAR16 *mSemihostFsLabel; + +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL gSemihostFs = { + EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_REVISION, + VolumeOpen +}; + +EFI_FILE gSemihostFsFile = { + EFI_FILE_PROTOCOL_REVISION, + FileOpen, + FileClose, + FileDelete, + FileRead, + FileWrite, + FileGetPosition, + FileSetPosition, + FileGetInfo, + FileSetInfo, + FileFlush +}; + +// +// Device path for semi-hosting. It contains our auto-generated Caller ID GUID. +// +typedef struct { + VENDOR_DEVICE_PATH Guid; + EFI_DEVICE_PATH_PROTOCOL End; +} SEMIHOST_DEVICE_PATH; + +SEMIHOST_DEVICE_PATH gDevicePath = { + { + { HARDWARE_DEVICE_PATH, HW_VENDOR_DP, { sizeof (VENDOR_DEVICE_PATH), 0 } + }, + EFI_CALLER_ID_GUID + }, + { END_DEVICE_PATH_TYPE, END_ENTIRE_DEVICE_PATH_SUBTYPE, { sizeof (EFI_DEVICE_PATH_PROTOCOL), 0 } + } +}; + +typedef struct { + LIST_ENTRY Link; + UINT64 Signature; + EFI_FILE File; + CHAR8 *FileName; + UINT64 OpenMode; + UINT32 Position; + UINTN SemihostHandle; + BOOLEAN IsRoot; + EFI_FILE_INFO Info; +} SEMIHOST_FCB; + +#define SEMIHOST_FCB_SIGNATURE SIGNATURE_32( 'S', 'H', 'F', 'C' ) +#define SEMIHOST_FCB_FROM_THIS(a) CR(a, SEMIHOST_FCB, File, SEMIHOST_FCB_SIGNATURE) +#define SEMIHOST_FCB_FROM_LINK(a) CR(a, SEMIHOST_FCB, Link, SEMIHOST_FCB_SIGNATURE); + +EFI_HANDLE gInstallHandle = NULL; +LIST_ENTRY gFileList = INITIALIZE_LIST_HEAD_VARIABLE (gFileList); + +SEMIHOST_FCB * +AllocateFCB ( + VOID + ) +{ + SEMIHOST_FCB *Fcb; + + Fcb = AllocateZeroPool (sizeof (SEMIHOST_FCB)); + if (Fcb != NULL) { + CopyMem (&Fcb->File, &gSemihostFsFile, sizeof (gSemihostFsFile)); + Fcb->Signature = SEMIHOST_FCB_SIGNATURE; + } + + return Fcb; +} + +VOID +FreeFCB ( + IN SEMIHOST_FCB *Fcb + ) +{ + // Remove Fcb from gFileList. + RemoveEntryList (&Fcb->Link); + + // To help debugging... + Fcb->Signature = 0; + + FreePool (Fcb); +} + +EFI_STATUS +VolumeOpen ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE **Root + ) +{ + SEMIHOST_FCB *RootFcb; + + if (Root == NULL) { + return EFI_INVALID_PARAMETER; + } + + RootFcb = AllocateFCB (); + if (RootFcb == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + RootFcb->IsRoot = TRUE; + RootFcb->Info.Attribute = EFI_FILE_READ_ONLY | EFI_FILE_DIRECTORY; + + InsertTailList (&gFileList, &RootFcb->Link); + + *Root = &RootFcb->File; + + return EFI_SUCCESS; +} + +/** + Open a file on the host system by means of the semihosting interface. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to source location. + @param[out] NewHandle A pointer to the location to return the opened + handle for the new file. + @param[in] FileName The Null-terminated string of the name of the file + to be opened. + @param[in] OpenMode The mode to open the file : Read or Read/Write or + Read/Write/Create + @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these + are the attribute bits for the newly created file. The + mnemonics of the attribute bits are : EFI_FILE_READ_ONLY, + EFI_FILE_HIDDEN, EFI_FILE_SYSTEM, EFI_FILE_RESERVED, + EFI_FILE_DIRECTORY and EFI_FILE_ARCHIVE. + + @retval EFI_SUCCESS The file was open. + @retval EFI_NOT_FOUND The specified file could not be found. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_WRITE_PROTECTED Attempt to create a directory. This is not possible + with the semi-hosting interface. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ +EFI_STATUS +FileOpen ( + IN EFI_FILE *This, + OUT EFI_FILE **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ) +{ + SEMIHOST_FCB *FileFcb; + RETURN_STATUS Return; + EFI_STATUS Status; + UINTN SemihostHandle; + CHAR8 *AsciiFileName; + UINT32 SemihostMode; + UINTN Length; + + if ((FileName == NULL) || (NewHandle == NULL)) { + return EFI_INVALID_PARAMETER; + } + + if ((OpenMode != EFI_FILE_MODE_READ) && + (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE)) && + (OpenMode != (EFI_FILE_MODE_READ | EFI_FILE_MODE_WRITE | EFI_FILE_MODE_CREATE))) + { + return EFI_INVALID_PARAMETER; + } + + if (((OpenMode & EFI_FILE_MODE_CREATE) != 0) && + ((Attributes & EFI_FILE_DIRECTORY) != 0)) + { + return EFI_WRITE_PROTECTED; + } + + Length = StrLen (FileName) + 1; + AsciiFileName = AllocatePool (Length); + if (AsciiFileName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStrS (FileName, AsciiFileName, Length); + + // Opening '/', '\', '.', or the NULL pathname is trying to open the root directory + if ((AsciiStrCmp (AsciiFileName, "\\") == 0) || + (AsciiStrCmp (AsciiFileName, "/") == 0) || + (AsciiStrCmp (AsciiFileName, "") == 0) || + (AsciiStrCmp (AsciiFileName, ".") == 0)) + { + FreePool (AsciiFileName); + return (VolumeOpen (&gSemihostFs, NewHandle)); + } + + // + // No control is done here concerning the file path. It is passed + // as it is to the host operating system through the semi-hosting + // interface. We first try to open the file in the read or update + // mode even if the file creation has been asked for. That way, if + // the file already exists, it is not truncated to zero length. In + // write mode (bit SEMIHOST_FILE_MODE_WRITE up), if the file already + // exists, it is reset to an empty file. + // + if (OpenMode == EFI_FILE_MODE_READ) { + SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY; + } else { + SemihostMode = SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY | SEMIHOST_FILE_MODE_UPDATE; + } + + Return = SemihostFileOpen (AsciiFileName, SemihostMode, &SemihostHandle); + + if (RETURN_ERROR (Return)) { + if ((OpenMode & EFI_FILE_MODE_CREATE) != 0) { + // + // In the create if does not exist case, if the opening in update + // mode failed, create it and open it in update mode. The update + // mode allows for both read and write from and to the file. + // + Return = SemihostFileOpen ( + AsciiFileName, + SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY | SEMIHOST_FILE_MODE_UPDATE, + &SemihostHandle + ); + if (RETURN_ERROR (Return)) { + Status = EFI_DEVICE_ERROR; + goto Error; + } + } else { + Status = EFI_NOT_FOUND; + goto Error; + } + } + + // Allocate a control block and fill it + FileFcb = AllocateFCB (); + if (FileFcb == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + FileFcb->FileName = AsciiFileName; + FileFcb->SemihostHandle = SemihostHandle; + FileFcb->Position = 0; + FileFcb->IsRoot = 0; + FileFcb->OpenMode = OpenMode; + + Return = SemihostFileLength (SemihostHandle, &Length); + if (RETURN_ERROR (Return)) { + Status = EFI_DEVICE_ERROR; + FreeFCB (FileFcb); + goto Error; + } + + FileFcb->Info.FileSize = Length; + FileFcb->Info.PhysicalSize = Length; + FileFcb->Info.Attribute = ((OpenMode & EFI_FILE_MODE_CREATE) != 0) ? + Attributes : 0; + + InsertTailList (&gFileList, &FileFcb->Link); + + *NewHandle = &FileFcb->File; + + return EFI_SUCCESS; + +Error: + + FreePool (AsciiFileName); + + return Status; +} + +/** + Worker function that truncate a file specified by its name to a given size. + + @param[in] FileName The Null-terminated string of the name of the file to be opened. + @param[in] Size The target size for the file. + + @retval EFI_SUCCESS The file was truncated. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + +**/ +STATIC +EFI_STATUS +TruncateFile ( + IN CHAR8 *FileName, + IN UINTN Size + ) +{ + EFI_STATUS Status; + RETURN_STATUS Return; + UINTN FileHandle; + UINT8 *Buffer; + UINTN Remaining; + UINTN Read; + UINTN ToRead; + + Status = EFI_DEVICE_ERROR; + FileHandle = 0; + Buffer = NULL; + + Return = SemihostFileOpen ( + FileName, + SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY, + &FileHandle + ); + if (RETURN_ERROR (Return)) { + goto Error; + } + + Buffer = AllocatePool (Size); + if (Buffer == NULL) { + Status = EFI_OUT_OF_RESOURCES; + goto Error; + } + + Read = 0; + Remaining = Size; + while (Remaining > 0) { + ToRead = Remaining; + Return = SemihostFileRead (FileHandle, &ToRead, Buffer + Read); + if (RETURN_ERROR (Return)) { + goto Error; + } + + Remaining -= ToRead; + Read += ToRead; + } + + Return = SemihostFileClose (FileHandle); + FileHandle = 0; + if (RETURN_ERROR (Return)) { + goto Error; + } + + Return = SemihostFileOpen ( + FileName, + SEMIHOST_FILE_MODE_WRITE | SEMIHOST_FILE_MODE_BINARY, + &FileHandle + ); + if (RETURN_ERROR (Return)) { + goto Error; + } + + if (Size > 0) { + Return = SemihostFileWrite (FileHandle, &Size, Buffer); + if (RETURN_ERROR (Return)) { + goto Error; + } + } + + Status = EFI_SUCCESS; + +Error: + + if (FileHandle != 0) { + SemihostFileClose (FileHandle); + } + + if (Buffer != NULL) { + FreePool (Buffer); + } + + return (Status); +} + +/** + Close a specified file handle. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to close. + + @retval EFI_SUCCESS The file was closed. + @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. + +**/ +EFI_STATUS +FileClose ( + IN EFI_FILE *This + ) +{ + SEMIHOST_FCB *Fcb; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + if (!Fcb->IsRoot) { + SemihostFileClose (Fcb->SemihostHandle); + // + // The file size might have been reduced from its actual + // size on the host file system with FileSetInfo(). In + // that case, the file has to be truncated. + // + if (Fcb->Info.FileSize < Fcb->Info.PhysicalSize) { + TruncateFile (Fcb->FileName, Fcb->Info.FileSize); + } + + FreePool (Fcb->FileName); + } + + FreeFCB (Fcb); + + return EFI_SUCCESS; +} + +/** + Close and delete a file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to delete. + + @retval EFI_SUCCESS The file was closed and deleted. + @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted. + @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. + +**/ +EFI_STATUS +FileDelete ( + IN EFI_FILE *This + ) +{ + SEMIHOST_FCB *Fcb; + RETURN_STATUS Return; + CHAR8 *FileName; + UINTN NameSize; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + if (!Fcb->IsRoot) { + // Get the filename from the Fcb + NameSize = AsciiStrLen (Fcb->FileName); + FileName = AllocatePool (NameSize + 1); + + AsciiStrCpyS (FileName, NameSize + 1, Fcb->FileName); + + // Close the file if it's open. Disregard return status, + // since it might give an error if the file isn't open. + This->Close (This); + + // Call the semihost interface to delete the file. + Return = SemihostFileRemove (FileName); + if (RETURN_ERROR (Return)) { + return EFI_WARN_DELETE_FAILURE; + } + + return EFI_SUCCESS; + } else { + return EFI_WARN_DELETE_FAILURE; + } +} + +/** + Read data from an open file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle to read data from. + @param[in out] BufferSize On input, the size of the Buffer. On output, the + amount of data returned in Buffer. In both cases, + the size is measured in bytes. + @param[out] Buffer The buffer into which the data is read. + + @retval EFI_SUCCESS The data was read. + @retval EFI_DEVICE_ERROR On entry, the current file position is + beyond the end of the file, or the semi-hosting + interface reported an error while performing the + read operation. + @retval EFI_INVALID_PARAMETER At least one of the three input pointers is NULL. + +**/ +EFI_STATUS +FileRead ( + IN EFI_FILE *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + SEMIHOST_FCB *Fcb; + EFI_STATUS Status; + RETURN_STATUS Return; + + if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + if (Fcb->IsRoot) { + // The semi-hosting interface does not allow to list files on the host machine. + Status = EFI_UNSUPPORTED; + } else { + Status = EFI_SUCCESS; + if (Fcb->Position >= Fcb->Info.FileSize) { + *BufferSize = 0; + if (Fcb->Position > Fcb->Info.FileSize) { + Status = EFI_DEVICE_ERROR; + } + } else { + Return = SemihostFileRead (Fcb->SemihostHandle, BufferSize, Buffer); + if (RETURN_ERROR (Return)) { + Status = EFI_DEVICE_ERROR; + } else { + Fcb->Position += *BufferSize; + } + } + } + + return Status; +} + +/** + Worker function that extends the size of an open file. + + The extension is filled with zeros. + + @param[in] Fcb Internal description of the opened file + @param[in] Size The number of bytes, the file has to be extended. + + @retval EFI_SUCCESS The file was extended. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + +**/ +STATIC +EFI_STATUS +ExtendFile ( + IN SEMIHOST_FCB *Fcb, + IN UINTN Size + ) +{ + RETURN_STATUS Return; + UINTN Remaining; + CHAR8 WriteBuffer[128]; + UINTN WriteNb; + UINTN WriteSize; + + Return = SemihostFileSeek (Fcb->SemihostHandle, Fcb->Info.FileSize); + if (RETURN_ERROR (Return)) { + return EFI_DEVICE_ERROR; + } + + Remaining = Size; + SetMem (WriteBuffer, 0, sizeof (WriteBuffer)); + while (Remaining > 0) { + WriteNb = MIN (Remaining, sizeof (WriteBuffer)); + WriteSize = WriteNb; + Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, WriteBuffer); + if (RETURN_ERROR (Return)) { + return EFI_DEVICE_ERROR; + } + + Remaining -= WriteNb; + } + + return EFI_SUCCESS; +} + +/** + Write data to an open file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle to write data to. + @param[in out] BufferSize On input, the size of the Buffer. On output, the + size of the data actually written. In both cases, + the size is measured in bytes. + @param[in] Buffer The buffer of data to write. + + @retval EFI_SUCCESS The data was written. + @retval EFI_ACCESS_DENIED Attempt to write into a read only file or + in a file opened in read only mode. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_INVALID_PARAMETER At least one of the three input pointers is NULL. + +**/ +EFI_STATUS +FileWrite ( + IN EFI_FILE *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ) +{ + SEMIHOST_FCB *Fcb; + EFI_STATUS Status; + UINTN WriteSize; + RETURN_STATUS Return; + UINTN Length; + + if ((This == NULL) || (BufferSize == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + // We cannot write a read-only file + if ( (Fcb->Info.Attribute & EFI_FILE_READ_ONLY) + || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) + { + return EFI_ACCESS_DENIED; + } + + // + // If the position has been set past the end of the file, first grow the + // file from its current size "Fcb->Info.FileSize" to "Fcb->Position" + // size, filling the gap with zeros. + // + if (Fcb->Position > Fcb->Info.FileSize) { + Status = ExtendFile (Fcb, Fcb->Position - Fcb->Info.FileSize); + if (EFI_ERROR (Status)) { + return Status; + } + + Fcb->Info.FileSize = Fcb->Position; + } + + WriteSize = *BufferSize; + Return = SemihostFileWrite (Fcb->SemihostHandle, &WriteSize, Buffer); + if (RETURN_ERROR (Return)) { + return EFI_DEVICE_ERROR; + } + + Fcb->Position += *BufferSize; + if (Fcb->Position > Fcb->Info.FileSize) { + Fcb->Info.FileSize = Fcb->Position; + } + + Return = SemihostFileLength (Fcb->SemihostHandle, &Length); + if (RETURN_ERROR (Return)) { + return EFI_DEVICE_ERROR; + } + + Fcb->Info.PhysicalSize = Length; + + return EFI_SUCCESS; +} + +/** + Return a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to get the current position on. + @param[out] Position The address to return the file's current position value. + + @retval EFI_SUCCESS The position was returned. + @retval EFI_INVALID_PARAMETER The parameter "This" or "Position" is NULL. + +**/ +EFI_STATUS +FileGetPosition ( + IN EFI_FILE *This, + OUT UINT64 *Position + ) +{ + SEMIHOST_FCB *Fcb; + + if ((This == NULL) || (Position == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + *Position = Fcb->Position; + + return EFI_SUCCESS; +} + +/** + Set a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to set the requested position on. + @param[in] Position The byte position from the start of the file to set. + + @retval EFI_SUCCESS The position was set. + @retval EFI_DEVICE_ERROR The semi-hosting positioning operation failed. + @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open + directories. + @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. + +**/ +EFI_STATUS +FileSetPosition ( + IN EFI_FILE *This, + IN UINT64 Position + ) +{ + SEMIHOST_FCB *Fcb; + RETURN_STATUS Return; + + if (This == NULL) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + if (Fcb->IsRoot) { + if (Position != 0) { + return EFI_UNSUPPORTED; + } + } else { + // + // UEFI Spec section 12.5: + // "Seeking to position 0xFFFFFFFFFFFFFFFF causes the current position to + // be set to the end of the file." + // + if (Position == 0xFFFFFFFFFFFFFFFF) { + Position = Fcb->Info.FileSize; + } + + Return = SemihostFileSeek (Fcb->SemihostHandle, MIN (Position, Fcb->Info.FileSize)); + if (RETURN_ERROR (Return)) { + return EFI_DEVICE_ERROR; + } + } + + Fcb->Position = Position; + + return EFI_SUCCESS; +} + +/** + Return information about a file. + + @param[in] Fcb A pointer to the description of an open file. + @param[in out] BufferSize The size, in bytes, of Buffer. + @param[out] Buffer A pointer to the data buffer to return. Not NULL if + "*BufferSize" is greater than 0. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to return the information. + BufferSize has been updated with the size needed to + complete the request. +**/ +STATIC +EFI_STATUS +GetFileInfo ( + IN SEMIHOST_FCB *Fcb, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_FILE_INFO *Info; + UINTN NameSize; + UINTN ResultSize; + UINTN Index; + + if (Fcb->IsRoot) { + NameSize = 0; + ResultSize = SIZE_OF_EFI_FILE_INFO + sizeof (CHAR16); + } else { + NameSize = AsciiStrLen (Fcb->FileName) + 1; + ResultSize = SIZE_OF_EFI_FILE_INFO + NameSize * sizeof (CHAR16); + } + + if (*BufferSize < ResultSize) { + *BufferSize = ResultSize; + return EFI_BUFFER_TOO_SMALL; + } + + Info = Buffer; + + // Copy the current file info + CopyMem (Info, &Fcb->Info, SIZE_OF_EFI_FILE_INFO); + + // Fill in the structure + Info->Size = ResultSize; + + if (Fcb->IsRoot) { + Info->FileName[0] = L'\0'; + } else { + for (Index = 0; Index < NameSize; Index++) { + Info->FileName[Index] = Fcb->FileName[Index]; + } + } + + *BufferSize = ResultSize; + + return EFI_SUCCESS; +} + +/** + Return information about a file system. + + @param[in] Fcb A pointer to the description of an open file + which belongs to the file system, the information + is requested for. + @param[in out] BufferSize The size, in bytes, of Buffer. + @param[out] Buffer A pointer to the data buffer to return. Not NULL if + "*BufferSize" is greater than 0. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to return the information. + BufferSize has been updated with the size needed to + complete the request. + +**/ +STATIC +EFI_STATUS +GetFilesystemInfo ( + IN SEMIHOST_FCB *Fcb, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + EFI_FILE_SYSTEM_INFO *Info; + EFI_STATUS Status; + UINTN ResultSize; + UINTN StringSize; + + StringSize = StrSize (mSemihostFsLabel); + ResultSize = SIZE_OF_EFI_FILE_SYSTEM_INFO + StringSize; + + if (*BufferSize >= ResultSize) { + ZeroMem (Buffer, ResultSize); + Status = EFI_SUCCESS; + + Info = Buffer; + + Info->Size = ResultSize; + Info->ReadOnly = FALSE; + Info->VolumeSize = 0; + Info->FreeSpace = 0; + Info->BlockSize = 0; + + CopyMem (Info->VolumeLabel, mSemihostFsLabel, StringSize); + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *BufferSize = ResultSize; + return Status; +} + +/** + Return information about a file or a file system. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle the requested information is for. + @param[in] InformationType The type identifier for the information being requested : + EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or + EFI_FILE_SYSTEM_VOLUME_LABEL_ID + @param[in out] BufferSize The size, in bytes, of Buffer. + @param[out] Buffer A pointer to the data buffer to return. The type of the + data inside the buffer is indicated by InformationType. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to return the information. + BufferSize has been updated with the size needed to + complete the request. + @retval EFI_INVALID_PARAMETER The parameter "This" or "InformationType" or "BufferSize" + is NULL or "Buffer" is NULL and "*Buffersize" is greater + than 0. + +**/ +EFI_STATUS +FileGetInfo ( + IN EFI_FILE *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ) +{ + SEMIHOST_FCB *Fcb; + EFI_STATUS Status; + UINTN ResultSize; + + if ((This == NULL) || + (InformationType == NULL) || + (BufferSize == NULL) || + ((Buffer == NULL) && (*BufferSize > 0))) + { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { + Status = GetFilesystemInfo (Fcb, BufferSize, Buffer); + } else if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { + Status = GetFileInfo (Fcb, BufferSize, Buffer); + } else if (CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + ResultSize = StrSize (mSemihostFsLabel); + + if (*BufferSize >= ResultSize) { + CopyMem (Buffer, mSemihostFsLabel, ResultSize); + Status = EFI_SUCCESS; + } else { + Status = EFI_BUFFER_TOO_SMALL; + } + + *BufferSize = ResultSize; + } else { + Status = EFI_UNSUPPORTED; + } + + return Status; +} + +/** + Set information about a file. + + @param[in] Fcb A pointer to the description of the open file. + @param[in] Info A pointer to the file information to write. + + @retval EFI_SUCCESS The information was set. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file + to a file that is already present. + @retval EFI_ACCESS_DENIED An attempt is being made to change the + EFI_FILE_DIRECTORY Attribute. + @retval EFI_ACCESS_DENIED The file is a read-only file or has been + opened in read-only mode and an attempt is + being made to modify a field other than + Attribute. + @retval EFI_WRITE_PROTECTED An attempt is being made to modify a + read-only attribute. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_OUT_OF_RESOURCES A allocation needed to process the request failed. + +**/ +STATIC +EFI_STATUS +SetFileInfo ( + IN SEMIHOST_FCB *Fcb, + IN EFI_FILE_INFO *Info + ) +{ + EFI_STATUS Status; + RETURN_STATUS Return; + BOOLEAN FileSizeIsDifferent; + BOOLEAN FileNameIsDifferent; + BOOLEAN ReadOnlyIsDifferent; + CHAR8 *AsciiFileName; + UINTN FileSize; + UINTN Length; + UINTN SemihostHandle; + + // + // A directory can not be changed to a file and a file can + // not be changed to a directory. + // + if (((Info->Attribute & EFI_FILE_DIRECTORY) != 0) != Fcb->IsRoot) { + return EFI_ACCESS_DENIED; + } + + Length = StrLen (Info->FileName) + 1; + AsciiFileName = AllocatePool (Length); + if (AsciiFileName == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + UnicodeStrToAsciiStrS (Info->FileName, AsciiFileName, Length); + + FileSizeIsDifferent = (Info->FileSize != Fcb->Info.FileSize); + FileNameIsDifferent = (AsciiStrCmp (AsciiFileName, Fcb->FileName) != 0); + ReadOnlyIsDifferent = CompareMem ( + &Info->CreateTime, + &Fcb->Info.CreateTime, + 3 * sizeof (EFI_TIME) + ) != 0; + + // + // For a read-only file or a file opened in read-only mode, only + // the Attribute field can be modified. As the root directory is + // read-only (i.e. VolumeOpen()), this protects the root directory + // description. + // + if ((Fcb->OpenMode == EFI_FILE_MODE_READ) || + (Fcb->Info.Attribute & EFI_FILE_READ_ONLY)) + { + if (FileSizeIsDifferent || FileNameIsDifferent || ReadOnlyIsDifferent) { + Status = EFI_ACCESS_DENIED; + goto Error; + } + } + + if (ReadOnlyIsDifferent) { + Status = EFI_WRITE_PROTECTED; + goto Error; + } + + Status = EFI_DEVICE_ERROR; + + if (FileSizeIsDifferent) { + FileSize = Info->FileSize; + if (Fcb->Info.FileSize < FileSize) { + Status = ExtendFile (Fcb, FileSize - Fcb->Info.FileSize); + if (EFI_ERROR (Status)) { + goto Error; + } + + // + // The read/write position from the host file system point of view + // is at the end of the file. If the position from this module + // point of view is smaller than the new file size, then + // ask the host file system to move to that position. + // + if (Fcb->Position < FileSize) { + FileSetPosition (&Fcb->File, Fcb->Position); + } + } + + Fcb->Info.FileSize = FileSize; + + Return = SemihostFileLength (Fcb->SemihostHandle, &Length); + if (RETURN_ERROR (Return)) { + goto Error; + } + + Fcb->Info.PhysicalSize = Length; + } + + // + // Note down in RAM the Attribute field but we can not ask + // for its modification to the host file system as the + // semi-host interface does not provide this feature. + // + Fcb->Info.Attribute = Info->Attribute; + + if (FileNameIsDifferent) { + Return = SemihostFileOpen ( + AsciiFileName, + SEMIHOST_FILE_MODE_READ | SEMIHOST_FILE_MODE_BINARY, + &SemihostHandle + ); + if (!RETURN_ERROR (Return)) { + SemihostFileClose (SemihostHandle); + Status = EFI_ACCESS_DENIED; + goto Error; + } + + Return = SemihostFileRename (Fcb->FileName, AsciiFileName); + if (RETURN_ERROR (Return)) { + goto Error; + } + + FreePool (Fcb->FileName); + Fcb->FileName = AsciiFileName; + AsciiFileName = NULL; + } + + Status = EFI_SUCCESS; + +Error: + if (AsciiFileName != NULL) { + FreePool (AsciiFileName); + } + + return Status; +} + +/** + Set information about a file or a file system. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle the information is for. + @param[in] InformationType The type identifier for the information being set : + EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or + EFI_FILE_SYSTEM_VOLUME_LABEL_ID + @param[in] BufferSize The size, in bytes, of Buffer. + @param[in] Buffer A pointer to the data buffer to write. The type of the + data inside the buffer is indicated by InformationType. + + @retval EFI_SUCCESS The information was set. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_ACCESS_DENIED An attempt is being made to change the + EFI_FILE_DIRECTORY Attribute. + @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and + the file is a read-only file or has been + opened in read-only mode and an attempt is + being made to modify a field other than + Attribute. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file + to a file that is already present. + @retval EFI_WRITE_PROTECTED An attempt is being made to modify a + read-only attribute. + @retval EFI_BAD_BUFFER_SIZE The size of the buffer is lower than that indicated by + the data inside the buffer. + @retval EFI_OUT_OF_RESOURCES An allocation needed to process the request failed. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ +EFI_STATUS +FileSetInfo ( + IN EFI_FILE *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ) +{ + SEMIHOST_FCB *Fcb; + EFI_FILE_INFO *Info; + EFI_FILE_SYSTEM_INFO *SystemInfo; + CHAR16 *VolumeLabel; + + if ((This == NULL) || (InformationType == NULL) || (Buffer == NULL)) { + return EFI_INVALID_PARAMETER; + } + + Fcb = SEMIHOST_FCB_FROM_THIS (This); + + if (CompareGuid (InformationType, &gEfiFileInfoGuid)) { + Info = Buffer; + if (Info->Size < (SIZE_OF_EFI_FILE_INFO + StrSize (Info->FileName))) { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize < Info->Size) { + return EFI_BAD_BUFFER_SIZE; + } + + return SetFileInfo (Fcb, Info); + } else if (CompareGuid (InformationType, &gEfiFileSystemInfoGuid)) { + SystemInfo = Buffer; + if (SystemInfo->Size < + (SIZE_OF_EFI_FILE_SYSTEM_INFO + StrSize (SystemInfo->VolumeLabel))) + { + return EFI_INVALID_PARAMETER; + } + + if (BufferSize < SystemInfo->Size) { + return EFI_BAD_BUFFER_SIZE; + } + + Buffer = SystemInfo->VolumeLabel; + + if (StrSize (Buffer) > 0) { + VolumeLabel = AllocateCopyPool (StrSize (Buffer), Buffer); + if (VolumeLabel != NULL) { + FreePool (mSemihostFsLabel); + mSemihostFsLabel = VolumeLabel; + return EFI_SUCCESS; + } else { + return EFI_OUT_OF_RESOURCES; + } + } else { + return EFI_INVALID_PARAMETER; + } + } else if (!CompareGuid (InformationType, &gEfiFileSystemVolumeLabelInfoIdGuid)) { + return EFI_UNSUPPORTED; + } else { + return EFI_UNSUPPORTED; + } +} + +EFI_STATUS +FileFlush ( + IN EFI_FILE *File + ) +{ + SEMIHOST_FCB *Fcb; + + Fcb = SEMIHOST_FCB_FROM_THIS (File); + + if (Fcb->IsRoot) { + return EFI_SUCCESS; + } else { + if ( (Fcb->Info.Attribute & EFI_FILE_READ_ONLY) + || !(Fcb->OpenMode & EFI_FILE_MODE_WRITE)) + { + return EFI_ACCESS_DENIED; + } else { + return EFI_SUCCESS; + } + } +} + +EFI_STATUS +SemihostFsEntryPoint ( + IN EFI_HANDLE ImageHandle, + IN EFI_SYSTEM_TABLE *SystemTable + ) +{ + EFI_STATUS Status; + + Status = EFI_NOT_FOUND; + + if (SemihostConnectionSupported ()) { + mSemihostFsLabel = AllocateCopyPool (StrSize (DEFAULT_SEMIHOST_FS_LABEL), DEFAULT_SEMIHOST_FS_LABEL); + if (mSemihostFsLabel == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + Status = gBS->InstallMultipleProtocolInterfaces ( + &gInstallHandle, + &gEfiSimpleFileSystemProtocolGuid, + &gSemihostFs, + &gEfiDevicePathProtocolGuid, + &gDevicePath, + NULL + ); + + if (EFI_ERROR (Status)) { + FreePool (mSemihostFsLabel); + } + } + + return Status; +} diff --git a/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.h b/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.h new file mode 100644 index 000000000..a065e5b33 --- /dev/null +++ b/ArmPkg/Filesystem/SemihostFs/Arm/SemihostFs.h @@ -0,0 +1,245 @@ +/** @file + Support a Semi Host file system over a debuggers JTAG + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef SEMIHOST_FS_H_ +#define SEMIHOST_FS_H_ + +EFI_STATUS +VolumeOpen ( + IN EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *This, + OUT EFI_FILE **Root + ); + +/** + Open a file on the host system by means of the semihosting interface. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to source location. + @param[out] NewHandle A pointer to the location to return the opened + handle for the new file. + @param[in] FileName The Null-terminated string of the name of the file + to be opened. + @param[in] OpenMode The mode to open the file : Read or Read/Write or + Read/Write/Create + @param[in] Attributes Only valid for EFI_FILE_MODE_CREATE, in which case these + are the attribute bits for the newly created file. The + mnemonics of the attribute bits are : EFI_FILE_READ_ONLY, + EFI_FILE_HIDDEN, EFI_FILE_SYSTEM, EFI_FILE_RESERVED, + EFI_FILE_DIRECTORY and EFI_FILE_ARCHIVE. + + @retval EFI_SUCCESS The file was open. + @retval EFI_NOT_FOUND The specified file could not be found. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_WRITE_PROTECTED Attempt to create a directory. This is not possible + with the semi-hosting interface. + @retval EFI_OUT_OF_RESOURCES Not enough resources were available to open the file. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ +EFI_STATUS +FileOpen ( + IN EFI_FILE *This, + OUT EFI_FILE **NewHandle, + IN CHAR16 *FileName, + IN UINT64 OpenMode, + IN UINT64 Attributes + ); + +/** + Close a specified file handle. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to close. + + @retval EFI_SUCCESS The file was closed. + @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. + +**/ +EFI_STATUS +FileClose ( + IN EFI_FILE *This + ); + +/** + Close and delete a file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is the file + handle to delete. + + @retval EFI_SUCCESS The file was closed and deleted. + @retval EFI_WARN_DELETE_FAILURE The handle was closed, but the file was not deleted. + @retval EFI_INVALID_PARAMETER The parameter "This" is NULL. + +**/ +EFI_STATUS +FileDelete ( + IN EFI_FILE *This + ); + +/** + Read data from an open file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle to read data from. + @param[in out] BufferSize On input, the size of the Buffer. On output, the + amount of data returned in Buffer. In both cases, + the size is measured in bytes. + @param[out] Buffer The buffer into which the data is read. + + @retval EFI_SUCCESS The data was read. + @retval EFI_DEVICE_ERROR On entry, the current file position is + beyond the end of the file, or the semi-hosting + interface reported an error while performing the + read operation. + @retval EFI_INVALID_PARAMETER The parameter "This" or the parameter "Buffer" + is NULL. +**/ +EFI_STATUS +FileRead ( + IN EFI_FILE *This, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + Write data to an open file. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle to write data to. + @param[in out] BufferSize On input, the size of the Buffer. On output, the + size of the data actually written. In both cases, + the size is measured in bytes. + @param[in] Buffer The buffer of data to write. + + @retval EFI_SUCCESS The data was written. + @retval EFI_ACCESS_DENIED Attempt to write into a read only file or + in a file opened in read only mode. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_INVALID_PARAMETER The parameter "This" or the parameter "Buffer" + is NULL. + +**/ +EFI_STATUS +FileWrite ( + IN EFI_FILE *This, + IN OUT UINTN *BufferSize, + IN VOID *Buffer + ); + +/** + Return a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to get the current position on. + @param[out] Position The address to return the file's current position value. + + @retval EFI_SUCCESS The position was returned. + @retval EFI_INVALID_PARAMETER Position is a NULL pointer. + +**/ +EFI_STATUS +FileGetPosition ( + IN EFI_FILE *File, + OUT UINT64 *Position + ); + +/** + Set a file's current position. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that is + the file handle to set the requested position on. + @param[in] Position The byte position from the start of the file to set. + + @retval EFI_SUCCESS The position was set. + @retval EFI_DEVICE_ERROR The semi-hosting positioning operation failed. + @retval EFI_UNSUPPORTED The seek request for nonzero is not valid on open + directories. + +**/ +EFI_STATUS +FileSetPosition ( + IN EFI_FILE *File, + IN UINT64 Position + ); + +/** + Return information about a file or a file system. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle the requested information is for. + @param[in] InformationType The type identifier for the information being requested : + EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or + EFI_FILE_SYSTEM_VOLUME_LABEL_ID + @param[in out] BufferSize The size, in bytes, of Buffer. + @param[out] Buffer A pointer to the data buffer to return. The type of the + data inside the buffer is indicated by InformationType. + + @retval EFI_SUCCESS The information was returned. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_BUFFER_TOO_SMALL The BufferSize is too small to return the information. + BufferSize has been updated with the size needed to + complete the request. + @retval EFI_INVALID_PARAMETER The parameter "This" or the parameter "Buffer" + is NULL. + +**/ +EFI_STATUS +FileGetInfo ( + IN EFI_FILE *This, + IN EFI_GUID *InformationType, + IN OUT UINTN *BufferSize, + OUT VOID *Buffer + ); + +/** + Set information about a file or a file system. + + @param[in] This A pointer to the EFI_FILE_PROTOCOL instance that + is the file handle the information is for. + @param[in] InformationType The type identifier for the information being set : + EFI_FILE_INFO_ID or EFI_FILE_SYSTEM_INFO_ID or + EFI_FILE_SYSTEM_VOLUME_LABEL_ID + @param[in] BufferSize The size, in bytes, of Buffer. + @param[in] Buffer A pointer to the data buffer to write. The type of the + data inside the buffer is indicated by InformationType. + + @retval EFI_SUCCESS The information was set. + @retval EFI_UNSUPPORTED The InformationType is not known. + @retval EFI_DEVICE_ERROR The last issued semi-hosting operation failed. + @retval EFI_ACCESS_DENIED An attempt is being made to change the + EFI_FILE_DIRECTORY Attribute. + @retval EFI_ACCESS_DENIED InformationType is EFI_FILE_INFO_ID and + the file is a read-only file or has been + opened in read-only mode and an attempt is + being made to modify a field other than + Attribute. + @retval EFI_ACCESS_DENIED An attempt is made to change the name of a file + to a file that is already present. + @retval EFI_WRITE_PROTECTED An attempt is being made to modify a + read-only attribute. + @retval EFI_BAD_BUFFER_SIZE The size of the buffer is lower than that indicated by + the data inside the buffer. + @retval EFI_OUT_OF_RESOURCES An allocation needed to process the request failed. + @retval EFI_INVALID_PARAMETER At least one of the parameters is invalid. + +**/ +EFI_STATUS +FileSetInfo ( + IN EFI_FILE *This, + IN EFI_GUID *InformationType, + IN UINTN BufferSize, + IN VOID *Buffer + ); + +EFI_STATUS +FileFlush ( + IN EFI_FILE *File + ); + +#endif // SEMIHOST_FS_H_ diff --git a/ArmPkg/Filesystem/SemihostFs/SemihostFs.inf b/ArmPkg/Filesystem/SemihostFs/SemihostFs.inf new file mode 100644 index 000000000..cd79e18b2 --- /dev/null +++ b/ArmPkg/Filesystem/SemihostFs/SemihostFs.inf @@ -0,0 +1,42 @@ +#/** @file +# Support a Semi Host file system over a debuggers JTAG +# +# Copyright (c) 2009, Apple Inc. All rights reserved.
+# Portions copyright (c) 2011 - 2013, ARM Ltd. All rights reserved. +# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = SemihostFs + FILE_GUID = C5B9C74A-6D72-4719-99AB-C59F199091EB + MODULE_TYPE = UEFI_DRIVER + VERSION_STRING = 1.0 + + ENTRY_POINT = SemihostFsEntryPoint + +[Sources.ARM, Sources.AARCH64] + Arm/SemihostFs.c + +[Packages] + MdePkg/MdePkg.dec + ArmPkg/ArmPkg.dec + +[LibraryClasses] + BaseLib + MemoryAllocationLib + SemihostLib + UefiDriverEntryPoint + UefiLib + +[Guids] + gEfiFileSystemInfoGuid + gEfiFileInfoGuid + gEfiFileSystemVolumeLabelInfoIdGuid + +[Protocols] + gEfiSimpleFileSystemProtocolGuid + gEfiDevicePathProtocolGuid + diff --git a/ArmPkg/Include/AsmMacroIoLib.h b/ArmPkg/Include/AsmMacroIoLib.h new file mode 100644 index 000000000..2493a15b7 --- /dev/null +++ b/ArmPkg/Include/AsmMacroIoLib.h @@ -0,0 +1,38 @@ +/** @file + Macros to work around lack of Apple support for LDR register, =expr + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Copyright (c) 2011-2012, ARM Ltd. All rights reserved.
+ Copyright (c) 2016, Linaro Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ASM_MACRO_IO_LIB_H_ +#define ASM_MACRO_IO_LIB_H_ + +#define _ASM_FUNC(Name, Section) \ + .global Name ; \ + .section #Section, "ax" ; \ + .type Name, %function ; \ + .p2align 2 ; \ + Name: + +#define ASM_FUNC(Name) _ASM_FUNC(ASM_PFX(Name), .text. ## Name) + +#define MOV32(Reg, Val) \ + movw Reg, #(Val) & 0xffff ; \ + movt Reg, #(Val) >> 16 + +#define ADRL(Reg, Sym) \ + movw Reg, #:lower16:(Sym) - (. + 16) ; \ + movt Reg, #:upper16:(Sym) - (. + 12) ; \ + add Reg, Reg, pc + +#define LDRL(Reg, Sym) \ + movw Reg, #:lower16:(Sym) - (. + 16) ; \ + movt Reg, #:upper16:(Sym) - (. + 12) ; \ + ldr Reg, [pc, Reg] + +#endif // ASM_MACRO_IO_LIB_H_ diff --git a/ArmPkg/Include/AsmMacroIoLib.inc b/ArmPkg/Include/AsmMacroIoLib.inc new file mode 100644 index 000000000..66b8d3d33 --- /dev/null +++ b/ArmPkg/Include/AsmMacroIoLib.inc @@ -0,0 +1,33 @@ +;%HEADER% +;/** @file +; Macros to work around lack of Apple support for LDR register, =expr +; +; Copyright (c) 2009, Apple Inc. All rights reserved.
+; Copyright (c) 2011-2012, ARM Ltd. All rights reserved.
+; +; SPDX-License-Identifier: BSD-2-Clause-Patent +; +;**/ + + + MACRO + adrll $Reg, $Symbol + add $Reg, pc, #-8 + RELOC R_ARM_ALU_PC_G0_NC, $Symbol + add $Reg, $Reg, #-4 + RELOC R_ARM_ALU_PC_G1_NC, $Symbol + add $Reg, $Reg, #0 + RELOC R_ARM_ALU_PC_G2, $Symbol + MEND + + MACRO + ldrl $Reg, $Symbol + add $Reg, pc, #-8 + RELOC R_ARM_ALU_PC_G0_NC, $Symbol + add $Reg, $Reg, #-4 + RELOC R_ARM_ALU_PC_G1_NC, $Symbol + ldr $Reg, [$Reg, #0] + RELOC R_ARM_LDR_PC_G2, $Symbol + MEND + + END diff --git a/ArmPkg/Include/AsmMacroIoLibV8.h b/ArmPkg/Include/AsmMacroIoLibV8.h new file mode 100644 index 000000000..2c2b1cabd --- /dev/null +++ b/ArmPkg/Include/AsmMacroIoLibV8.h @@ -0,0 +1,55 @@ +/** @file + Macros to work around lack of Clang support for LDR register, =expr + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Portions copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+ Copyright (c) 2016, Linaro Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ASM_MACRO_IO_LIBV8_H_ +#define ASM_MACRO_IO_LIBV8_H_ + +// CurrentEL : 0xC = EL3; 8 = EL2; 4 = EL1 +// This only selects between EL1 and EL2, else we die. +// Provide the Macro with a safe temp xreg to use. +#define EL1_OR_EL2(SAFE_XREG) \ + mrs SAFE_XREG, CurrentEL ;\ + cmp SAFE_XREG, #0x8 ;\ + b.gt . ;\ + b.eq 2f ;\ + cbnz SAFE_XREG, 1f ;\ + b . ;// We should never get here + +// CurrentEL : 0xC = EL3; 8 = EL2; 4 = EL1 +// This only selects between EL1 and EL2 and EL3, else we die. +// Provide the Macro with a safe temp xreg to use. +#define EL1_OR_EL2_OR_EL3(SAFE_XREG) \ + mrs SAFE_XREG, CurrentEL ;\ + cmp SAFE_XREG, #0x8 ;\ + b.gt 3f ;\ + b.eq 2f ;\ + cbnz SAFE_XREG, 1f ;\ + b . ;// We should never get here + +#define _ASM_FUNC(Name, Section) \ + .global Name ; \ + .section #Section, "ax" ; \ + .type Name, %function ; \ + Name: + +#define ASM_FUNC(Name) _ASM_FUNC(ASM_PFX(Name), .text. ## Name) + +#define MOV32(Reg, Val) \ + movz Reg, (Val) >> 16, lsl #16 ; \ + movk Reg, (Val) & 0xffff + +#define MOV64(Reg, Val) \ + movz Reg, (Val) >> 48, lsl #48 ; \ + movk Reg, ((Val) >> 32) & 0xffff, lsl #32 ; \ + movk Reg, ((Val) >> 16) & 0xffff, lsl #16 ; \ + movk Reg, (Val) & 0xffff + +#endif // ASM_MACRO_IO_LIBV8_H_ diff --git a/ArmPkg/Include/Chipset/AArch64.h b/ArmPkg/Include/Chipset/AArch64.h new file mode 100644 index 000000000..bfd2859f5 --- /dev/null +++ b/ArmPkg/Include/Chipset/AArch64.h @@ -0,0 +1,241 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef AARCH64_H_ +#define AARCH64_H_ + +#include + +// ARM Interrupt ID in Exception Table +#define ARM_ARCH_EXCEPTION_IRQ EXCEPT_AARCH64_IRQ + +// CPACR - Coprocessor Access Control Register definitions +#define CPACR_TTA_EN (1UL << 28) +#define CPACR_FPEN_EL1 (1UL << 20) +#define CPACR_FPEN_FULL (3UL << 20) +#define CPACR_CP_FULL_ACCESS 0x300000 + +// Coprocessor Trap Register (CPTR) +#define AARCH64_CPTR_TFP (1 << 10) + +// ID_AA64PFR0 - AArch64 Processor Feature Register 0 definitions +#define AARCH64_PFR0_FP (0xF << 16) +#define AARCH64_PFR0_GIC (0xF << 24) + +// SCR - Secure Configuration Register definitions +#define SCR_NS (1 << 0) +#define SCR_IRQ (1 << 1) +#define SCR_FIQ (1 << 2) +#define SCR_EA (1 << 3) +#define SCR_FW (1 << 4) +#define SCR_AW (1 << 5) + +// MIDR - Main ID Register definitions +#define ARM_CPU_TYPE_SHIFT 4 +#define ARM_CPU_TYPE_MASK 0xFFF +#define ARM_CPU_TYPE_AEMV8 0xD0F +#define ARM_CPU_TYPE_A53 0xD03 +#define ARM_CPU_TYPE_A57 0xD07 +#define ARM_CPU_TYPE_A72 0xD08 +#define ARM_CPU_TYPE_A15 0xC0F +#define ARM_CPU_TYPE_A9 0xC09 +#define ARM_CPU_TYPE_A7 0xC07 +#define ARM_CPU_TYPE_A5 0xC05 + +#define ARM_CPU_REV_MASK ((0xF << 20) | (0xF) ) +#define ARM_CPU_REV(rn, pn) ((((rn) & 0xF) << 20) | ((pn) & 0xF)) + +// Hypervisor Configuration Register +#define ARM_HCR_FMO BIT3 +#define ARM_HCR_IMO BIT4 +#define ARM_HCR_AMO BIT5 +#define ARM_HCR_TSC BIT19 +#define ARM_HCR_TGE BIT27 + +// Exception Syndrome Register +#define AARCH64_ESR_EC(Ecr) ((0x3F << 26) & (Ecr)) +#define AARCH64_ESR_ISS(Ecr) ((0x1FFFFFF) & (Ecr)) + +#define AARCH64_ESR_EC_SMC32 (0x13 << 26) +#define AARCH64_ESR_EC_SMC64 (0x17 << 26) + +// AArch64 Exception Level +#define AARCH64_EL3 0xC +#define AARCH64_EL2 0x8 +#define AARCH64_EL1 0x4 + +// Saved Program Status Register definitions +#define SPSR_A BIT8 +#define SPSR_I BIT7 +#define SPSR_F BIT6 + +#define SPSR_AARCH32 BIT4 + +#define SPSR_AARCH32_MODE_USER 0x0 +#define SPSR_AARCH32_MODE_FIQ 0x1 +#define SPSR_AARCH32_MODE_IRQ 0x2 +#define SPSR_AARCH32_MODE_SVC 0x3 +#define SPSR_AARCH32_MODE_ABORT 0x7 +#define SPSR_AARCH32_MODE_UNDEF 0xB +#define SPSR_AARCH32_MODE_SYS 0xF + +// Counter-timer Hypervisor Control register definitions +#define CNTHCTL_EL2_EL1PCTEN BIT0 +#define CNTHCTL_EL2_EL1PCEN BIT1 + +#define ARM_VECTOR_TABLE_ALIGNMENT ((1 << 11)-1) + +// Vector table offset definitions +#define ARM_VECTOR_CUR_SP0_SYNC 0x000 +#define ARM_VECTOR_CUR_SP0_IRQ 0x080 +#define ARM_VECTOR_CUR_SP0_FIQ 0x100 +#define ARM_VECTOR_CUR_SP0_SERR 0x180 + +#define ARM_VECTOR_CUR_SPX_SYNC 0x200 +#define ARM_VECTOR_CUR_SPX_IRQ 0x280 +#define ARM_VECTOR_CUR_SPX_FIQ 0x300 +#define ARM_VECTOR_CUR_SPX_SERR 0x380 + +#define ARM_VECTOR_LOW_A64_SYNC 0x400 +#define ARM_VECTOR_LOW_A64_IRQ 0x480 +#define ARM_VECTOR_LOW_A64_FIQ 0x500 +#define ARM_VECTOR_LOW_A64_SERR 0x580 + +#define ARM_VECTOR_LOW_A32_SYNC 0x600 +#define ARM_VECTOR_LOW_A32_IRQ 0x680 +#define ARM_VECTOR_LOW_A32_FIQ 0x700 +#define ARM_VECTOR_LOW_A32_SERR 0x780 + +// The ID_AA64MMFR2_EL1 register was added in ARMv8.2. Since we +// build for ARMv8.0, we need to define the register here. +#define ID_AA64MMFR2_EL1 S3_0_C0_C7_2 + +#define VECTOR_BASE(tbl) \ + .section .text.##tbl##,"ax"; \ + .align 11; \ + .org 0x0; \ + GCC_ASM_EXPORT(tbl); \ + ASM_PFX(tbl): \ + +#define VECTOR_ENTRY(tbl, off) \ + .org off + +#define VECTOR_END(tbl) \ + .org 0x800; \ + .previous + +VOID +EFIAPI +ArmEnableSWPInstruction ( + VOID + ); + +UINTN +EFIAPI +ArmReadCbar ( + VOID + ); + +UINTN +EFIAPI +ArmReadTpidrurw ( + VOID + ); + +VOID +EFIAPI +ArmWriteTpidrurw ( + UINTN Value + ); + +UINTN +EFIAPI +ArmGetTCR ( + VOID + ); + +VOID +EFIAPI +ArmSetTCR ( + UINTN Value + ); + +UINTN +EFIAPI +ArmGetMAIR ( + VOID + ); + +VOID +EFIAPI +ArmSetMAIR ( + UINTN Value + ); + +VOID +EFIAPI +ArmDisableAlignmentCheck ( + VOID + ); + +VOID +EFIAPI +ArmEnableAlignmentCheck ( + VOID + ); + +VOID +EFIAPI +ArmDisableStackAlignmentCheck ( + VOID + ); + +VOID +EFIAPI +ArmEnableStackAlignmentCheck ( + VOID + ); + +VOID +EFIAPI +ArmDisableAllExceptions ( + VOID + ); + +VOID +ArmWriteHcr ( + IN UINTN Hcr + ); + +UINTN +ArmReadHcr ( + VOID + ); + +UINTN +ArmReadCurrentEL ( + VOID + ); + +UINTN +ArmWriteCptr ( + IN UINT64 Cptr + ); + +UINT32 +ArmReadCntHctl ( + VOID + ); + +VOID +ArmWriteCntHctl ( + IN UINT32 CntHctl + ); + +#endif // AARCH64_H_ diff --git a/ArmPkg/Include/Chipset/AArch64Mmu.h b/ArmPkg/Include/Chipset/AArch64Mmu.h new file mode 100644 index 000000000..2ea2cc0a8 --- /dev/null +++ b/ArmPkg/Include/Chipset/AArch64Mmu.h @@ -0,0 +1,195 @@ +/** @file +* +* Copyright (c) 2011-2021, Arm Limited. All rights reserved.
+* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef AARCH64_MMU_H_ +#define AARCH64_MMU_H_ + +// +// Memory Attribute Indirection register Definitions +// +#define MAIR_ATTR_DEVICE_MEMORY 0x0ULL +#define MAIR_ATTR_NORMAL_MEMORY_NON_CACHEABLE 0x44ULL +#define MAIR_ATTR_NORMAL_MEMORY_WRITE_THROUGH 0xBBULL +#define MAIR_ATTR_NORMAL_MEMORY_WRITE_BACK 0xFFULL + +#define MAIR_ATTR(n, value) ((value) << (((n) >> 2)*8)) + +// +// Long-descriptor Translation Table format +// + +// Return the smallest offset from the table level. +// The first offset starts at 12bit. There are 4 levels of 9-bit address range from level 3 to level 0 +#define TT_ADDRESS_OFFSET_AT_LEVEL(TableLevel) (12 + ((3 - (TableLevel)) * 9)) + +#define TT_BLOCK_ENTRY_SIZE_AT_LEVEL(Level) (1ULL << TT_ADDRESS_OFFSET_AT_LEVEL(Level)) + +// Get the associated entry in the given Translation Table +#define TT_GET_ENTRY_FOR_ADDRESS(TranslationTable, Level, Address) \ + ((UINTN)(TranslationTable) + ((((UINTN)(Address) >> TT_ADDRESS_OFFSET_AT_LEVEL(Level)) & (BIT9-1)) * sizeof(UINT64))) + +// Return the smallest address granularity from the table level. +// The first offset starts at 12bit. There are 4 levels of 9-bit address range from level 3 to level 0 +#define TT_ADDRESS_AT_LEVEL(TableLevel) (1ULL << TT_ADDRESS_OFFSET_AT_LEVEL(TableLevel)) + +#define TT_LAST_BLOCK_ADDRESS(TranslationTable, EntryCount) \ + ((UINT64*)((EFI_PHYSICAL_ADDRESS)(TranslationTable) + (((EntryCount) - 1) * sizeof(UINT64)))) + +// There are 512 entries per table when 4K Granularity +#define TT_ENTRY_COUNT 512 +#define TT_ALIGNMENT_BLOCK_ENTRY BIT12 +#define TT_ALIGNMENT_DESCRIPTION_TABLE BIT12 + +#define TT_ADDRESS_MASK_BLOCK_ENTRY (0xFFFFFFFFFULL << 12) +#define TT_ADDRESS_MASK_DESCRIPTION_TABLE (0xFFFFFFFFFULL << 12) + +#define TT_TYPE_MASK 0x3 +#define TT_TYPE_TABLE_ENTRY 0x3 +#define TT_TYPE_BLOCK_ENTRY 0x1 +#define TT_TYPE_BLOCK_ENTRY_LEVEL3 0x3 + +#define TT_ATTR_INDX_MASK (0x7 << 2) +#define TT_ATTR_INDX_DEVICE_MEMORY (0x0 << 2) +#define TT_ATTR_INDX_MEMORY_NON_CACHEABLE (0x1 << 2) +#define TT_ATTR_INDX_MEMORY_WRITE_THROUGH (0x2 << 2) +#define TT_ATTR_INDX_MEMORY_WRITE_BACK (0x3 << 2) + +#define TT_AP_MASK (0x3UL << 6) +#define TT_AP_NO_RW (0x0UL << 6) +#define TT_AP_RW_RW (0x1UL << 6) +#define TT_AP_NO_RO (0x2UL << 6) +#define TT_AP_RO_RO (0x3UL << 6) + +#define TT_NS BIT5 +#define TT_AF BIT10 + +#define TT_SH_NON_SHAREABLE (0x0 << 8) +#define TT_SH_OUTER_SHAREABLE (0x2 << 8) +#define TT_SH_INNER_SHAREABLE (0x3 << 8) +#define TT_SH_MASK (0x3 << 8) + +#define TT_PXN_MASK BIT53 +#define TT_UXN_MASK BIT54 // EL1&0 +#define TT_XN_MASK BIT54 // EL2 / EL3 + +#define TT_ATTRIBUTES_MASK ((0xFFFULL << 52) | (0x3FFULL << 2)) + +#define TT_TABLE_PXN BIT59 +#define TT_TABLE_UXN BIT60 // EL1&0 +#define TT_TABLE_XN BIT60 // EL2 / EL3 +#define TT_TABLE_NS BIT63 + +#define TT_TABLE_AP_MASK (BIT62 | BIT61) +#define TT_TABLE_AP_NO_PERMISSION (0x0ULL << 61) +#define TT_TABLE_AP_EL0_NO_ACCESS (0x1ULL << 61) +#define TT_TABLE_AP_NO_WRITE_ACCESS (0x2ULL << 61) + +// +// Translation Control Register +// +#define TCR_T0SZ_MASK 0x3FUL + +#define TCR_PS_4GB (0UL << 16) +#define TCR_PS_64GB (1UL << 16) +#define TCR_PS_1TB (2UL << 16) +#define TCR_PS_4TB (3UL << 16) +#define TCR_PS_16TB (4UL << 16) +#define TCR_PS_256TB (5UL << 16) + +#define TCR_TG0_4KB (0UL << 14) +#define TCR_TG1_4KB (2UL << 30) + +#define TCR_IPS_4GB (0ULL << 32) +#define TCR_IPS_64GB (1ULL << 32) +#define TCR_IPS_1TB (2ULL << 32) +#define TCR_IPS_4TB (3ULL << 32) +#define TCR_IPS_16TB (4ULL << 32) +#define TCR_IPS_256TB (5ULL << 32) + +#define TCR_EPD1 (1UL << 23) + +#define TTBR_ASID_FIELD (48) +#define TTBR_ASID_MASK (0xFF << TTBR_ASID_FIELD) +#define TTBR_BADDR_MASK (0xFFFFFFFFFFFF ) // The width of this field depends on the values in TxSZ. Addr occupies bottom 48bits + +#define TCR_EL1_T0SZ_FIELD (0) +#define TCR_EL1_EPD0_FIELD (7) +#define TCR_EL1_IRGN0_FIELD (8) +#define TCR_EL1_ORGN0_FIELD (10) +#define TCR_EL1_SH0_FIELD (12) +#define TCR_EL1_TG0_FIELD (14) +#define TCR_EL1_T1SZ_FIELD (16) +#define TCR_EL1_A1_FIELD (22) +#define TCR_EL1_EPD1_FIELD (23) +#define TCR_EL1_IRGN1_FIELD (24) +#define TCR_EL1_ORGN1_FIELD (26) +#define TCR_EL1_SH1_FIELD (28) +#define TCR_EL1_TG1_FIELD (30) +#define TCR_EL1_IPS_FIELD (32) +#define TCR_EL1_AS_FIELD (36) +#define TCR_EL1_TBI0_FIELD (37) +#define TCR_EL1_TBI1_FIELD (38) +#define TCR_EL1_T0SZ_MASK (0x1FUL << TCR_EL1_T0SZ_FIELD) +#define TCR_EL1_EPD0_MASK (0x01UL << TCR_EL1_EPD0_FIELD) +#define TCR_EL1_IRGN0_MASK (0x03UL << TCR_EL1_IRGN0_FIELD) +#define TCR_EL1_ORGN0_MASK (0x03UL << TCR_EL1_ORGN0_FIELD) +#define TCR_EL1_SH0_MASK (0x03UL << TCR_EL1_SH0_FIELD) +#define TCR_EL1_TG0_MASK (0x01UL << TCR_EL1_TG0_FIELD) +#define TCR_EL1_T1SZ_MASK (0x1FUL << TCR_EL1_T1SZ_FIELD) +#define TCR_EL1_A1_MASK (0x01UL << TCR_EL1_A1_FIELD) +#define TCR_EL1_EPD1_MASK (0x01UL << TCR_EL1_EPD1_FIELD) +#define TCR_EL1_IRGN1_MASK (0x03UL << TCR_EL1_IRGN1_FIELD) +#define TCR_EL1_ORGN1_MASK (0x03UL << TCR_EL1_ORGN1_FIELD) +#define TCR_EL1_SH1_MASK (0x03UL << TCR_EL1_SH1_FIELD) +#define TCR_EL1_TG1_MASK (0x01UL << TCR_EL1_TG1_FIELD) +#define TCR_EL1_IPS_MASK (0x07UL << TCR_EL1_IPS_FIELD) +#define TCR_EL1_AS_MASK (0x01UL << TCR_EL1_AS_FIELD) +#define TCR_EL1_TBI0_MASK (0x01UL << TCR_EL1_TBI0_FIELD) +#define TCR_EL1_TBI1_MASK (0x01UL << TCR_EL1_TBI1_FIELD) + +#define TCR_EL23_T0SZ_FIELD (0) +#define TCR_EL23_IRGN0_FIELD (8) +#define TCR_EL23_ORGN0_FIELD (10) +#define TCR_EL23_SH0_FIELD (12) +#define TCR_EL23_TG0_FIELD (14) +#define TCR_EL23_PS_FIELD (16) +#define TCR_EL23_T0SZ_MASK (0x1FUL << TCR_EL23_T0SZ_FIELD) +#define TCR_EL23_IRGN0_MASK (0x03UL << TCR_EL23_IRGN0_FIELD) +#define TCR_EL23_ORGN0_MASK (0x03UL << TCR_EL23_ORGN0_FIELD) +#define TCR_EL23_SH0_MASK (0x03UL << TCR_EL23_SH0_FIELD) +#define TCR_EL23_TG0_MASK (0x01UL << TCR_EL23_TG0_FIELD) +#define TCR_EL23_PS_MASK (0x07UL << TCR_EL23_PS_FIELD) + +#define TCR_RGN_OUTER_NON_CACHEABLE (0x0UL << 10) +#define TCR_RGN_OUTER_WRITE_BACK_ALLOC (0x1UL << 10) +#define TCR_RGN_OUTER_WRITE_THROUGH (0x2UL << 10) +#define TCR_RGN_OUTER_WRITE_BACK_NO_ALLOC (0x3UL << 10) + +#define TCR_RGN_INNER_NON_CACHEABLE (0x0UL << 8) +#define TCR_RGN_INNER_WRITE_BACK_ALLOC (0x1UL << 8) +#define TCR_RGN_INNER_WRITE_THROUGH (0x2UL << 8) +#define TCR_RGN_INNER_WRITE_BACK_NO_ALLOC (0x3UL << 8) + +#define TCR_SH_NON_SHAREABLE (0x0UL << 12) +#define TCR_SH_OUTER_SHAREABLE (0x2UL << 12) +#define TCR_SH_INNER_SHAREABLE (0x3UL << 12) + +#define TCR_PASZ_32BITS_4GB (0x0UL) +#define TCR_PASZ_36BITS_64GB (0x1UL) +#define TCR_PASZ_40BITS_1TB (0x2UL) +#define TCR_PASZ_42BITS_4TB (0x3UL) +#define TCR_PASZ_44BITS_16TB (0x4UL) +#define TCR_PASZ_48BITS_256TB (0x5UL) + +// The value written to the T*SZ fields are defined as 2^(64-T*SZ). So a 39Bit +// Virtual address range for 512GB of virtual space sets T*SZ to 25 +#define INPUT_ADDRESS_SIZE_TO_TXSZ(a) (64 - a) + +// Uses LPAE Page Table format + +#endif // AARCH64_MMU_H_ diff --git a/ArmPkg/Include/Chipset/ArmCortexA5x.h b/ArmPkg/Include/Chipset/ArmCortexA5x.h new file mode 100644 index 000000000..cc8b23b96 --- /dev/null +++ b/ArmPkg/Include/Chipset/ArmCortexA5x.h @@ -0,0 +1,44 @@ +/** @file + + Copyright (c) 2012 - 2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ARM_CORTEX_A5X_H_ +#define ARM_CORTEX_A5X_H_ + +// +// Cortex A5x feature bit definitions +// +#define A5X_FEATURE_SMP (1 << 6) + +// +// Helper functions to access CPU Extended Control Register +// +UINT64 +EFIAPI +ArmReadCpuExCr ( + VOID + ); + +VOID +EFIAPI +ArmWriteCpuExCr ( + IN UINT64 Val + ); + +VOID +EFIAPI +ArmSetCpuExCrBit ( + IN UINT64 Bits + ); + +VOID +EFIAPI +ArmUnsetCpuExCrBit ( + IN UINT64 Bits + ); + +#endif // ARM_CORTEX_A5X_H_ diff --git a/ArmPkg/Include/Chipset/ArmCortexA9.h b/ArmPkg/Include/Chipset/ArmCortexA9.h new file mode 100644 index 000000000..a89aeebd4 --- /dev/null +++ b/ArmPkg/Include/Chipset/ArmCortexA9.h @@ -0,0 +1,57 @@ +/** @file + + Copyright (c) 2011, ARM Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ARM_CORTEX_A9_H_ +#define ARM_CORTEX_A9_H_ + +#include + +// +// Cortex A9 feature bit definitions +// +#define A9_FEATURE_PARITY (1<<9) +#define A9_FEATURE_AOW (1<<8) +#define A9_FEATURE_EXCL (1<<7) +#define A9_FEATURE_SMP (1<<6) +#define A9_FEATURE_FOZ (1<<3) +#define A9_FEATURE_DPREF (1<<2) +#define A9_FEATURE_HINT (1<<1) +#define A9_FEATURE_FWD (1<<0) + +// +// Cortex A9 Watchdog +// +#define ARM_A9_WATCHDOG_REGION 0x600 + +#define ARM_A9_WATCHDOG_LOAD_REGISTER 0x20 +#define ARM_A9_WATCHDOG_CONTROL_REGISTER 0x28 + +#define ARM_A9_WATCHDOG_WATCHDOG_MODE (1 << 3) +#define ARM_A9_WATCHDOG_TIMER_MODE (0 << 3) +#define ARM_A9_WATCHDOG_SINGLE_SHOT (0 << 1) +#define ARM_A9_WATCHDOG_AUTORELOAD (1 << 1) +#define ARM_A9_WATCHDOG_ENABLE 1 + +// +// SCU register offsets & masks +// +#define A9_SCU_CONTROL_OFFSET 0x0 +#define A9_SCU_CONFIG_OFFSET 0x4 +#define A9_SCU_INVALL_OFFSET 0xC +#define A9_SCU_FILT_START_OFFSET 0x40 +#define A9_SCU_FILT_END_OFFSET 0x44 +#define A9_SCU_SACR_OFFSET 0x50 +#define A9_SCU_SSACR_OFFSET 0x54 + +UINTN +EFIAPI +ArmGetScuBaseAddress ( + VOID + ); + +#endif // ARM_CORTEX_A9_H_ diff --git a/ArmPkg/Include/Chipset/ArmV7.h b/ArmPkg/Include/Chipset/ArmV7.h new file mode 100644 index 000000000..94620c087 --- /dev/null +++ b/ArmPkg/Include/Chipset/ArmV7.h @@ -0,0 +1,122 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Copyright (c) 2011-2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ARM_V7_H_ +#define ARM_V7_H_ + +#include + +// ARM Interrupt ID in Exception Table +#define ARM_ARCH_EXCEPTION_IRQ EXCEPT_ARM_IRQ + +// ID_PFR1 - ARM Processor Feature Register 1 definitions +#define ARM_PFR1_SEC (0xFUL << 4) +#define ARM_PFR1_TIMER (0xFUL << 16) +#define ARM_PFR1_GIC (0xFUL << 28) + +// Domain Access Control Register +#define DOMAIN_ACCESS_CONTROL_MASK(a) (3UL << (2 * (a))) +#define DOMAIN_ACCESS_CONTROL_NONE(a) (0UL << (2 * (a))) +#define DOMAIN_ACCESS_CONTROL_CLIENT(a) (1UL << (2 * (a))) +#define DOMAIN_ACCESS_CONTROL_RESERVED(a) (2UL << (2 * (a))) +#define DOMAIN_ACCESS_CONTROL_MANAGER(a) (3UL << (2 * (a))) + +// CPSR - Coprocessor Status Register definitions +#define CPSR_MODE_USER 0x10 +#define CPSR_MODE_FIQ 0x11 +#define CPSR_MODE_IRQ 0x12 +#define CPSR_MODE_SVC 0x13 +#define CPSR_MODE_ABORT 0x17 +#define CPSR_MODE_HYP 0x1A +#define CPSR_MODE_UNDEFINED 0x1B +#define CPSR_MODE_SYSTEM 0x1F +#define CPSR_MODE_MASK 0x1F +#define CPSR_ASYNC_ABORT (1 << 8) +#define CPSR_IRQ (1 << 7) +#define CPSR_FIQ (1 << 6) + +// CPACR - Coprocessor Access Control Register definitions +#define CPACR_CP_DENIED(cp) 0x00 +#define CPACR_CP_PRIV(cp) ((0x1 << ((cp) << 1)) & 0x0FFFFFFF) +#define CPACR_CP_FULL(cp) ((0x3 << ((cp) << 1)) & 0x0FFFFFFF) +#define CPACR_ASEDIS (1 << 31) +#define CPACR_D32DIS (1 << 30) +#define CPACR_CP_FULL_ACCESS 0x0FFFFFFF + +// NSACR - Non-Secure Access Control Register definitions +#define NSACR_CP(cp) ((1 << (cp)) & 0x3FFF) +#define NSACR_NSD32DIS (1 << 14) +#define NSACR_NSASEDIS (1 << 15) +#define NSACR_PLE (1 << 16) +#define NSACR_TL (1 << 17) +#define NSACR_NS_SMP (1 << 18) +#define NSACR_RFR (1 << 19) + +// SCR - Secure Configuration Register definitions +#define SCR_NS (1 << 0) +#define SCR_IRQ (1 << 1) +#define SCR_FIQ (1 << 2) +#define SCR_EA (1 << 3) +#define SCR_FW (1 << 4) +#define SCR_AW (1 << 5) + +// MIDR - Main ID Register definitions +#define ARM_CPU_TYPE_SHIFT 4 +#define ARM_CPU_TYPE_MASK 0xFFF +#define ARM_CPU_TYPE_AEMV8 0xD0F +#define ARM_CPU_TYPE_A53 0xD03 +#define ARM_CPU_TYPE_A57 0xD07 +#define ARM_CPU_TYPE_A15 0xC0F +#define ARM_CPU_TYPE_A12 0xC0D +#define ARM_CPU_TYPE_A9 0xC09 +#define ARM_CPU_TYPE_A7 0xC07 +#define ARM_CPU_TYPE_A5 0xC05 + +#define ARM_CPU_REV_MASK ((0xF << 20) | (0xF) ) +#define ARM_CPU_REV(rn, pn) ((((rn) & 0xF) << 20) | ((pn) & 0xF)) + +#define ARM_VECTOR_TABLE_ALIGNMENT ((1 << 5)-1) + +VOID +EFIAPI +ArmEnableSWPInstruction ( + VOID + ); + +UINTN +EFIAPI +ArmReadCbar ( + VOID + ); + +UINTN +EFIAPI +ArmReadTpidrurw ( + VOID + ); + +VOID +EFIAPI +ArmWriteTpidrurw ( + UINTN Value + ); + +UINT32 +EFIAPI +ArmReadNsacr ( + VOID + ); + +VOID +EFIAPI +ArmWriteNsacr ( + IN UINT32 Nsacr + ); + +#endif // ARM_V7_H_ diff --git a/ArmPkg/Include/Chipset/ArmV7Mmu.h b/ArmPkg/Include/Chipset/ArmV7Mmu.h new file mode 100644 index 000000000..db99527d6 --- /dev/null +++ b/ArmPkg/Include/Chipset/ArmV7Mmu.h @@ -0,0 +1,237 @@ +/** @file +* +* Copyright (c) 2011-2013, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef ARMV7_MMU_H_ +#define ARMV7_MMU_H_ + +#define TTBR_NOT_OUTER_SHAREABLE BIT5 +#define TTBR_RGN_OUTER_NON_CACHEABLE 0 +#define TTBR_RGN_OUTER_WRITE_BACK_ALLOC BIT3 +#define TTBR_RGN_OUTER_WRITE_THROUGH BIT4 +#define TTBR_RGN_OUTER_WRITE_BACK_NO_ALLOC (BIT3|BIT4) +#define TTBR_SHAREABLE BIT1 +#define TTBR_NON_SHAREABLE 0 +#define TTBR_INNER_CACHEABLE BIT0 +#define TTBR_INNER_NON_CACHEABLE 0 +#define TTBR_RGN_INNER_NON_CACHEABLE 0 +#define TTBR_RGN_INNER_WRITE_BACK_ALLOC BIT6 +#define TTBR_RGN_INNER_WRITE_THROUGH BIT0 +#define TTBR_RGN_INNER_WRITE_BACK_NO_ALLOC (BIT0|BIT6) + +#define TTBR_WRITE_THROUGH ( TTBR_RGN_OUTER_WRITE_THROUGH | TTBR_INNER_CACHEABLE | TTBR_SHAREABLE) +#define TTBR_WRITE_BACK_NO_ALLOC ( TTBR_RGN_OUTER_WRITE_BACK_NO_ALLOC | TTBR_INNER_CACHEABLE | TTBR_SHAREABLE) +#define TTBR_NON_CACHEABLE ( TTBR_RGN_OUTER_NON_CACHEABLE | TTBR_INNER_NON_CACHEABLE ) +#define TTBR_WRITE_BACK_ALLOC ( TTBR_RGN_OUTER_WRITE_BACK_ALLOC | TTBR_INNER_CACHEABLE | TTBR_SHAREABLE) + +#define TTBR_MP_WRITE_THROUGH ( TTBR_RGN_OUTER_WRITE_THROUGH | TTBR_RGN_INNER_WRITE_THROUGH | TTBR_SHAREABLE) +#define TTBR_MP_WRITE_BACK_NO_ALLOC ( TTBR_RGN_OUTER_WRITE_BACK_NO_ALLOC | TTBR_RGN_INNER_WRITE_BACK_NO_ALLOC | TTBR_SHAREABLE) +#define TTBR_MP_NON_CACHEABLE ( TTBR_RGN_OUTER_NON_CACHEABLE | TTBR_RGN_INNER_NON_CACHEABLE ) +#define TTBR_MP_WRITE_BACK_ALLOC ( TTBR_RGN_OUTER_WRITE_BACK_ALLOC | TTBR_RGN_INNER_WRITE_BACK_ALLOC | TTBR_SHAREABLE) + +#define TRANSLATION_TABLE_SECTION_COUNT 4096 +#define TRANSLATION_TABLE_SECTION_SIZE (sizeof(UINT32) * TRANSLATION_TABLE_SECTION_COUNT) +#define TRANSLATION_TABLE_SECTION_ALIGNMENT (sizeof(UINT32) * TRANSLATION_TABLE_SECTION_COUNT) +#define TRANSLATION_TABLE_SECTION_ALIGNMENT_MASK (TRANSLATION_TABLE_SECTION_ALIGNMENT - 1) + +#define TRANSLATION_TABLE_PAGE_COUNT 256 +#define TRANSLATION_TABLE_PAGE_SIZE (sizeof(UINT32) * TRANSLATION_TABLE_PAGE_COUNT) +#define TRANSLATION_TABLE_PAGE_ALIGNMENT (sizeof(UINT32) * TRANSLATION_TABLE_PAGE_COUNT) +#define TRANSLATION_TABLE_PAGE_ALIGNMENT_MASK (TRANSLATION_TABLE_PAGE_ALIGNMENT - 1) + +#define TRANSLATION_TABLE_ENTRY_FOR_VIRTUAL_ADDRESS(table, address) ((UINT32 *)(table) + (((UINTN)(address)) >> 20)) + +// Translation table descriptor types +#define TT_DESCRIPTOR_SECTION_TYPE_MASK ((1UL << 18) | (3UL << 0)) +#define TT_DESCRIPTOR_SECTION_TYPE_FAULT (0UL << 0) +#define TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE (1UL << 0) +#define TT_DESCRIPTOR_SECTION_TYPE_SECTION ((0UL << 18) | (2UL << 0)) +#define TT_DESCRIPTOR_SECTION_TYPE_SUPERSECTION ((1UL << 18) | (2UL << 0)) +#define TT_DESCRIPTOR_SECTION_TYPE_IS_PAGE_TABLE(Desc) (((Desc) & 3UL) == TT_DESCRIPTOR_SECTION_TYPE_PAGE_TABLE) + +// Translation table descriptor types +#define TT_DESCRIPTOR_PAGE_TYPE_MASK (3UL << 0) +#define TT_DESCRIPTOR_PAGE_TYPE_FAULT (0UL << 0) +#define TT_DESCRIPTOR_PAGE_TYPE_PAGE (2UL << 0) +#define TT_DESCRIPTOR_PAGE_TYPE_PAGE_XN (3UL << 0) +#define TT_DESCRIPTOR_PAGE_TYPE_LARGEPAGE (1UL << 0) + +// Section descriptor definitions +#define TT_DESCRIPTOR_SECTION_SIZE (0x00100000) + +#define TT_DESCRIPTOR_SECTION_NS_MASK (1UL << 19) +#define TT_DESCRIPTOR_SECTION_NS (1UL << 19) + +#define TT_DESCRIPTOR_SECTION_NG_MASK (1UL << 17) +#define TT_DESCRIPTOR_SECTION_NG_GLOBAL (0UL << 17) +#define TT_DESCRIPTOR_SECTION_NG_LOCAL (1UL << 17) + +#define TT_DESCRIPTOR_PAGE_NG_MASK (1UL << 11) +#define TT_DESCRIPTOR_PAGE_NG_GLOBAL (0UL << 11) +#define TT_DESCRIPTOR_PAGE_NG_LOCAL (1UL << 11) + +#define TT_DESCRIPTOR_SECTION_S_MASK (1UL << 16) +#define TT_DESCRIPTOR_SECTION_S_NOT_SHARED (0UL << 16) +#define TT_DESCRIPTOR_SECTION_S_SHARED (1UL << 16) + +#define TT_DESCRIPTOR_PAGE_S_MASK (1UL << 10) +#define TT_DESCRIPTOR_PAGE_S_NOT_SHARED (0UL << 10) +#define TT_DESCRIPTOR_PAGE_S_SHARED (1UL << 10) + +#define TT_DESCRIPTOR_SECTION_AP_MASK ((1UL << 15) | (3UL << 10)) +#define TT_DESCRIPTOR_SECTION_AP_NO_NO ((0UL << 15) | (0UL << 10)) +#define TT_DESCRIPTOR_SECTION_AP_RW_NO ((0UL << 15) | (1UL << 10)) +#define TT_DESCRIPTOR_SECTION_AP_RW_RO ((0UL << 15) | (2UL << 10)) +#define TT_DESCRIPTOR_SECTION_AP_RW_RW ((0UL << 15) | (3UL << 10)) +#define TT_DESCRIPTOR_SECTION_AP_RO_NO ((1UL << 15) | (1UL << 10)) +#define TT_DESCRIPTOR_SECTION_AP_RO_RO ((1UL << 15) | (3UL << 10)) + +#define TT_DESCRIPTOR_PAGE_AP_MASK ((1UL << 9) | (3UL << 4)) +#define TT_DESCRIPTOR_PAGE_AP_NO_NO ((0UL << 9) | (0UL << 4)) +#define TT_DESCRIPTOR_PAGE_AP_RW_NO ((0UL << 9) | (1UL << 4)) +#define TT_DESCRIPTOR_PAGE_AP_RW_RO ((0UL << 9) | (2UL << 4)) +#define TT_DESCRIPTOR_PAGE_AP_RW_RW ((0UL << 9) | (3UL << 4)) +#define TT_DESCRIPTOR_PAGE_AP_RO_NO ((1UL << 9) | (1UL << 4)) +#define TT_DESCRIPTOR_PAGE_AP_RO_RO ((1UL << 9) | (3UL << 4)) + +#define TT_DESCRIPTOR_SECTION_XN_MASK (0x1UL << 4) +#define TT_DESCRIPTOR_PAGE_XN_MASK (0x1UL << 0) +#define TT_DESCRIPTOR_LARGEPAGE_XN_MASK (0x1UL << 15) + +#define TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK ((3UL << 12) | (1UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_SECTION_CACHEABLE_MASK (1UL << 3) +#define TT_DESCRIPTOR_SECTION_CACHE_POLICY_STRONGLY_ORDERED ((0UL << 12) | (0UL << 3) | (0UL << 2)) +#define TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE ((0UL << 12) | (0UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC ((0UL << 12) | (1UL << 3) | (0UL << 2)) +#define TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_NO_ALLOC ((0UL << 12) | (1UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE ((1UL << 12) | (0UL << 3) | (0UL << 2)) +#define TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC ((1UL << 12) | (1UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_SHAREABLE_DEVICE ((2UL << 12) | (0UL << 3) | (0UL << 2)) + +#define TT_DESCRIPTOR_PAGE_SIZE (0x00001000) + +#define TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK ((3UL << 6) | (1UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_PAGE_CACHEABLE_MASK (1UL << 3) +#define TT_DESCRIPTOR_PAGE_CACHE_POLICY_STRONGLY_ORDERED ((0UL << 6) | (0UL << 3) | (0UL << 2)) +#define TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE ((0UL << 6) | (0UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC ((0UL << 6) | (1UL << 3) | (0UL << 2)) +#define TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC ((0UL << 6) | (1UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE ((1UL << 6) | (0UL << 3) | (0UL << 2)) +#define TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC ((1UL << 6) | (1UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE ((2UL << 6) | (0UL << 3) | (0UL << 2)) + +#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_MASK ((3UL << 12) | (1UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_STRONGLY_ORDERED ((0UL << 12) | (0UL << 3) | (0UL << 2)) +#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_SHAREABLE_DEVICE ((0UL << 12) | (0UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC ((0UL << 12) | (1UL << 3) | (0UL << 2)) +#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_WRITE_BACK_NO_ALLOC ((0UL << 12) | (1UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_NON_CACHEABLE ((1UL << 12) | (0UL << 3) | (0UL << 2)) +#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_WRITE_BACK_ALLOC ((1UL << 12) | (1UL << 3) | (1UL << 2)) +#define TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_NON_SHAREABLE_DEVICE ((2UL << 12) | (0UL << 3) | (0UL << 2)) + +#define TT_DESCRIPTOR_CONVERT_TO_PAGE_AP(Desc) ((((Desc) & TT_DESCRIPTOR_SECTION_AP_MASK) >> 6) & TT_DESCRIPTOR_PAGE_AP_MASK) +#define TT_DESCRIPTOR_CONVERT_TO_PAGE_NG(Desc) ((((Desc) & TT_DESCRIPTOR_SECTION_NG_MASK) >> 6) & TT_DESCRIPTOR_PAGE_NG_MASK) +#define TT_DESCRIPTOR_CONVERT_TO_PAGE_S(Desc) ((((Desc) & TT_DESCRIPTOR_SECTION_S_MASK) >> 6) & TT_DESCRIPTOR_PAGE_S_MASK) +#define TT_DESCRIPTOR_CONVERT_TO_PAGE_XN(Desc, IsLargePage) ((IsLargePage)?\ + ((((Desc) & TT_DESCRIPTOR_SECTION_XN_MASK) << 11) & TT_DESCRIPTOR_LARGEPAGE_XN_MASK): \ + ((((Desc) & TT_DESCRIPTOR_SECTION_XN_MASK) >> 4) & TT_DESCRIPTOR_PAGE_XN_MASK)) +#define TT_DESCRIPTOR_CONVERT_TO_PAGE_CACHE_POLICY(Desc, IsLargePage) (IsLargePage? \ + (((Desc) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) & TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_MASK): \ + (((((Desc) & (0x3 << 12)) >> 6) | (Desc & (0x3 << 2))))) + +#define TT_DESCRIPTOR_CONVERT_TO_SECTION_AP(Desc) ((((Desc) & TT_DESCRIPTOR_PAGE_AP_MASK) << 6) & TT_DESCRIPTOR_SECTION_AP_MASK) + +#define TT_DESCRIPTOR_CONVERT_TO_SECTION_CACHE_POLICY(Desc, IsLargePage) (IsLargePage? \ + (((Desc) & TT_DESCRIPTOR_LARGEPAGE_CACHE_POLICY_MASK) & TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK): \ + (((((Desc) & (0x3 << 6)) << 6) | (Desc & (0x3 << 2))))) + +#define TT_DESCRIPTOR_SECTION_ATTRIBUTE_MASK (TT_DESCRIPTOR_SECTION_NS_MASK | TT_DESCRIPTOR_SECTION_NG_MASK | \ + TT_DESCRIPTOR_SECTION_S_MASK | TT_DESCRIPTOR_SECTION_AP_MASK | \ + TT_DESCRIPTOR_SECTION_XN_MASK | TT_DESCRIPTOR_SECTION_CACHE_POLICY_MASK) + +#define TT_DESCRIPTOR_PAGE_ATTRIBUTE_MASK (TT_DESCRIPTOR_PAGE_NG_MASK | TT_DESCRIPTOR_PAGE_S_MASK | \ + TT_DESCRIPTOR_PAGE_AP_MASK | TT_DESCRIPTOR_PAGE_XN_MASK | \ + TT_DESCRIPTOR_PAGE_CACHE_POLICY_MASK) + +#define TT_DESCRIPTOR_SECTION_DOMAIN_MASK (0x0FUL << 5) +#define TT_DESCRIPTOR_SECTION_DOMAIN(a) (((a) & 0x0FUL) << 5) + +#define TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK (0xFFF00000) +#define TT_DESCRIPTOR_SECTION_PAGETABLE_ADDRESS_MASK (0xFFFFFC00) +#define TT_DESCRIPTOR_SECTION_BASE_ADDRESS(a) ((a) & TT_DESCRIPTOR_SECTION_BASE_ADDRESS_MASK) +#define TT_DESCRIPTOR_SECTION_BASE_SHIFT 20 + +#define TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK (0xFFFFF000) +#define TT_DESCRIPTOR_PAGE_INDEX_MASK (0x000FF000) +#define TT_DESCRIPTOR_PAGE_BASE_ADDRESS(a) ((a) & TT_DESCRIPTOR_PAGE_BASE_ADDRESS_MASK) +#define TT_DESCRIPTOR_PAGE_BASE_SHIFT 12 + +#define TT_DESCRIPTOR_SECTION_WRITE_BACK(NonSecure) (TT_DESCRIPTOR_SECTION_TYPE_SECTION | \ + ((NonSecure) ? TT_DESCRIPTOR_SECTION_NS : 0) | \ + TT_DESCRIPTOR_SECTION_NG_GLOBAL | \ + TT_DESCRIPTOR_SECTION_S_SHARED | \ + TT_DESCRIPTOR_SECTION_DOMAIN(0) | \ + TT_DESCRIPTOR_SECTION_AP_RW_RW | \ + TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_BACK_ALLOC) +#define TT_DESCRIPTOR_SECTION_WRITE_THROUGH(NonSecure) (TT_DESCRIPTOR_SECTION_TYPE_SECTION | \ + ((NonSecure) ? TT_DESCRIPTOR_SECTION_NS : 0) | \ + TT_DESCRIPTOR_SECTION_NG_GLOBAL | \ + TT_DESCRIPTOR_SECTION_S_SHARED | \ + TT_DESCRIPTOR_SECTION_DOMAIN(0) | \ + TT_DESCRIPTOR_SECTION_AP_RW_RW | \ + TT_DESCRIPTOR_SECTION_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC) +#define TT_DESCRIPTOR_SECTION_DEVICE(NonSecure) (TT_DESCRIPTOR_SECTION_TYPE_SECTION | \ + ((NonSecure) ? TT_DESCRIPTOR_SECTION_NS : 0) | \ + TT_DESCRIPTOR_SECTION_NG_GLOBAL | \ + TT_DESCRIPTOR_SECTION_S_NOT_SHARED | \ + TT_DESCRIPTOR_SECTION_DOMAIN(0) | \ + TT_DESCRIPTOR_SECTION_AP_RW_RW | \ + TT_DESCRIPTOR_SECTION_XN_MASK | \ + TT_DESCRIPTOR_SECTION_CACHE_POLICY_SHAREABLE_DEVICE) +#define TT_DESCRIPTOR_SECTION_UNCACHED(NonSecure) (TT_DESCRIPTOR_SECTION_TYPE_SECTION | \ + ((NonSecure) ? TT_DESCRIPTOR_SECTION_NS : 0) | \ + TT_DESCRIPTOR_SECTION_NG_GLOBAL | \ + TT_DESCRIPTOR_SECTION_S_NOT_SHARED | \ + TT_DESCRIPTOR_SECTION_DOMAIN(0) | \ + TT_DESCRIPTOR_SECTION_AP_RW_RW | \ + TT_DESCRIPTOR_SECTION_CACHE_POLICY_NON_CACHEABLE) + +#define TT_DESCRIPTOR_PAGE_WRITE_BACK (TT_DESCRIPTOR_PAGE_TYPE_PAGE | \ + TT_DESCRIPTOR_PAGE_NG_GLOBAL | \ + TT_DESCRIPTOR_PAGE_S_SHARED | \ + TT_DESCRIPTOR_PAGE_AP_RW_RW | \ + TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_BACK_ALLOC) +#define TT_DESCRIPTOR_PAGE_WRITE_THROUGH (TT_DESCRIPTOR_PAGE_TYPE_PAGE | \ + TT_DESCRIPTOR_PAGE_NG_GLOBAL | \ + TT_DESCRIPTOR_PAGE_S_SHARED | \ + TT_DESCRIPTOR_PAGE_AP_RW_RW | \ + TT_DESCRIPTOR_PAGE_CACHE_POLICY_WRITE_THROUGH_NO_ALLOC) +#define TT_DESCRIPTOR_PAGE_DEVICE (TT_DESCRIPTOR_PAGE_TYPE_PAGE | \ + TT_DESCRIPTOR_PAGE_NG_GLOBAL | \ + TT_DESCRIPTOR_PAGE_S_NOT_SHARED | \ + TT_DESCRIPTOR_PAGE_AP_RW_RW | \ + TT_DESCRIPTOR_PAGE_XN_MASK | \ + TT_DESCRIPTOR_PAGE_CACHE_POLICY_SHAREABLE_DEVICE) +#define TT_DESCRIPTOR_PAGE_UNCACHED (TT_DESCRIPTOR_PAGE_TYPE_PAGE | \ + TT_DESCRIPTOR_PAGE_NG_GLOBAL | \ + TT_DESCRIPTOR_PAGE_S_NOT_SHARED | \ + TT_DESCRIPTOR_PAGE_AP_RW_RW | \ + TT_DESCRIPTOR_PAGE_CACHE_POLICY_NON_CACHEABLE) + +// First Level Descriptors +typedef UINT32 ARM_FIRST_LEVEL_DESCRIPTOR; + +// Second Level Descriptors +typedef UINT32 ARM_PAGE_TABLE_ENTRY; + +UINT32 +ConvertSectionAttributesToPageAttributes ( + IN UINT32 SectionAttributes, + IN BOOLEAN IsLargePage + ); + +#endif // ARMV7_MMU_H_ diff --git a/ArmPkg/Include/Guid/ArmMpCoreInfo.h b/ArmPkg/Include/Guid/ArmMpCoreInfo.h new file mode 100644 index 000000000..3a10fffb6 --- /dev/null +++ b/ArmPkg/Include/Guid/ArmMpCoreInfo.h @@ -0,0 +1,31 @@ +/** @file +* +* Copyright (c) 2011, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef ARM_MP_CORE_INFO_GUID_H_ +#define ARM_MP_CORE_INFO_GUID_H_ + +#define MAX_CPUS_PER_MPCORE_SYSTEM 0x04 +#define SCU_CONFIG_REG_OFFSET 0x04 +#define MPIDR_U_BIT_MASK 0x40000000 + +typedef struct { + UINT64 Mpidr; + + // MP Core Mailbox + EFI_PHYSICAL_ADDRESS MailboxSetAddress; + EFI_PHYSICAL_ADDRESS MailboxGetAddress; + EFI_PHYSICAL_ADDRESS MailboxClearAddress; + UINT64 MailboxClearValue; +} ARM_CORE_INFO; + +#define ARM_MP_CORE_INFO_GUID \ + { 0xa4ee0728, 0xe5d7, 0x4ac5, {0xb2, 0x1e, 0x65, 0x8e, 0xd8, 0x57, 0xe8, 0x34} } + +extern EFI_GUID gArmMpCoreInfoGuid; + +#endif /* ARM_MP_CORE_INFO_GUID_H_ */ diff --git a/ArmPkg/Include/IndustryStandard/ArmCache.h b/ArmPkg/Include/IndustryStandard/ArmCache.h new file mode 100644 index 000000000..27a91fcda --- /dev/null +++ b/ArmPkg/Include/IndustryStandard/ArmCache.h @@ -0,0 +1,111 @@ +/** @file + + Copyright (c) 2020 - 2021, NUVIA Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ARM_CACHE_H_ +#define ARM_CACHE_H_ + +#include + +// The ARM Architecture Reference Manual for ARMv8-A defines up +// to 7 levels of cache, L1 through L7. +#define MAX_ARM_CACHE_LEVEL 7 + +/// Defines the structure of the CSSELR (Cache Size Selection) register +typedef union { + struct { + UINT32 InD : 1; ///< Instruction not Data bit + UINT32 Level : 3; ///< Cache level (zero based) + UINT32 TnD : 1; ///< Allocation not Data bit + UINT32 Reserved : 27; ///< Reserved, RES0 + } Bits; ///< Bitfield definition of the register + UINT32 Data; ///< The entire 32-bit value +} CSSELR_DATA; + +/// The cache type values for the InD field of the CSSELR register +typedef enum { + /// Select the data or unified cache + CsselrCacheTypeDataOrUnified = 0, + /// Select the instruction cache + CsselrCacheTypeInstruction, + CsselrCacheTypeMax +} CSSELR_CACHE_TYPE; + +/// Defines the structure of the CCSIDR (Current Cache Size ID) register +typedef union { + struct { + UINT64 LineSize : 3; ///< Line size (Log2(Num bytes in cache) - 4) + UINT64 Associativity : 10; ///< Associativity - 1 + UINT64 NumSets : 15; ///< Number of sets in the cache -1 + UINT64 Unknown : 4; ///< Reserved, UNKNOWN + UINT64 Reserved : 32; ///< Reserved, RES0 + } BitsNonCcidx; ///< Bitfield definition of the register when FEAT_CCIDX is not supported. + struct { + UINT64 LineSize : 3; ///< Line size (Log2(Num bytes in cache) - 4) + UINT64 Associativity : 21; ///< Associativity - 1 + UINT64 Reserved1 : 8; ///< Reserved, RES0 + UINT64 NumSets : 24; ///< Number of sets in the cache -1 + UINT64 Reserved2 : 8; ///< Reserved, RES0 + } BitsCcidxAA64; ///< Bitfield definition of the register when FEAT_IDX is supported. + struct { + UINT64 LineSize : 3; + UINT64 Associativity : 21; + UINT64 Reserved : 8; + UINT64 Unallocated : 32; + } BitsCcidxAA32; + UINT64 Data; ///< The entire 64-bit value +} CCSIDR_DATA; + +/// Defines the structure of the AARCH32 CCSIDR2 register. +typedef union { + struct { + UINT32 NumSets : 24; ///< Number of sets in the cache - 1 + UINT32 Reserved : 8; ///< Reserved, RES0 + } Bits; ///< Bitfield definition of the register + UINT32 Data; ///< The entire 32-bit value +} CCSIDR2_DATA; + +/** Defines the structure of the CLIDR (Cache Level ID) register. + * + * The lower 32 bits are the same for both AARCH32 and AARCH64 + * so we can use the same structure for both. +**/ +typedef union { + struct { + UINT32 Ctype1 : 3; ///< Level 1 cache type + UINT32 Ctype2 : 3; ///< Level 2 cache type + UINT32 Ctype3 : 3; ///< Level 3 cache type + UINT32 Ctype4 : 3; ///< Level 4 cache type + UINT32 Ctype5 : 3; ///< Level 5 cache type + UINT32 Ctype6 : 3; ///< Level 6 cache type + UINT32 Ctype7 : 3; ///< Level 7 cache type + UINT32 LoUIS : 3; ///< Level of Unification Inner Shareable + UINT32 LoC : 3; ///< Level of Coherency + UINT32 LoUU : 3; ///< Level of Unification Uniprocessor + UINT32 Icb : 3; ///< Inner Cache Boundary + } Bits; ///< Bitfield definition of the register + UINT32 Data; ///< The entire 32-bit value +} CLIDR_DATA; + +/// The cache types reported in the CLIDR register. +typedef enum { + /// No cache is present + ClidrCacheTypeNone = 0, + /// There is only an instruction cache + ClidrCacheTypeInstructionOnly, + /// There is only a data cache + ClidrCacheTypeDataOnly, + /// There are separate data and instruction caches + ClidrCacheTypeSeparate, + /// There is a unified cache + ClidrCacheTypeUnified, + ClidrCacheTypeMax +} CLIDR_CACHE_TYPE; + +#define CLIDR_GET_CACHE_TYPE(x, level) ((x >> (3 * (level))) & 0b111) + +#endif /* ARM_CACHE_H_ */ diff --git a/ArmPkg/Include/IndustryStandard/ArmFfaSvc.h b/ArmPkg/Include/IndustryStandard/ArmFfaSvc.h new file mode 100644 index 000000000..4126a4985 --- /dev/null +++ b/ArmPkg/Include/IndustryStandard/ArmFfaSvc.h @@ -0,0 +1,56 @@ +/** @file + Header file for FF-A ABI's that will be used for + communication between S-EL0 and the Secure Partition + Manager(SPM) + + Copyright (c) 2020, ARM Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Revision Reference: + - FF-A Version 1.0 + + +**/ + +#ifndef ARM_FFA_SVC_H_ +#define ARM_FFA_SVC_H_ + +#define ARM_SVC_ID_FFA_VERSION_AARCH32 0x84000063 +#define ARM_SVC_ID_FFA_MSG_SEND_DIRECT_REQ_AARCH32 0x8400006F +#define ARM_SVC_ID_FFA_MSG_SEND_DIRECT_RESP_AARCH32 0x84000070 +#define ARM_SVC_ID_FFA_MSG_SEND_DIRECT_REQ_AARCH64 0xC400006F +#define ARM_SVC_ID_FFA_MSG_SEND_DIRECT_RESP_AARCH64 0xC4000070 + +/* Generic IDs when using AArch32 or AArch64 execution state */ +#ifdef MDE_CPU_AARCH64 +#define ARM_SVC_ID_FFA_MSG_SEND_DIRECT_REQ ARM_SVC_ID_FFA_MSG_SEND_DIRECT_REQ_AARCH64 +#define ARM_SVC_ID_FFA_MSG_SEND_DIRECT_RESP ARM_SVC_ID_FFA_MSG_SEND_DIRECT_RESP_AARCH64 +#endif +#ifdef MDE_CPU_ARM +#define ARM_SVC_ID_FFA_MSG_SEND_DIRECT_REQ ARM_SVC_ID_FFA_MSG_SEND_DIRECT_REQ_AARCH32 +#define ARM_SVC_ID_FFA_MSG_SEND_DIRECT_RESP ARM_SVC_ID_FFA_MSG_SEND_DIRECT_RESP_AARCH32 +#endif + +#define SPM_MAJOR_VERSION_FFA 1 +#define SPM_MINOR_VERSION_FFA 0 + +#define ARM_FFA_SPM_RET_SUCCESS 0 +#define ARM_FFA_SPM_RET_NOT_SUPPORTED -1 +#define ARM_FFA_SPM_RET_INVALID_PARAMETERS -2 +#define ARM_FFA_SPM_RET_NO_MEMORY -3 +#define ARM_FFA_SPM_RET_BUSY -4 +#define ARM_FFA_SPM_RET_INTERRUPTED -5 +#define ARM_FFA_SPM_RET_DENIED -6 +#define ARM_FFA_SPM_RET_RETRY -7 +#define ARM_FFA_SPM_RET_ABORTED -8 + +// For now, the destination id to be used in the FF-A calls +// is being hard-coded. Subsequently, support will be added +// to get the endpoint id's dynamically +// This is the endpoint id used by the optee os's implementation +// of the spmc. +// https://github.com/OP-TEE/optee_os/blob/master/core/arch/arm/kernel/stmm_sp.c#L66 +#define ARM_FFA_DESTINATION_ENDPOINT_ID 3 + +#endif // ARM_FFA_SVC_H_ diff --git a/ArmPkg/Include/IndustryStandard/ArmMmSvc.h b/ArmPkg/Include/IndustryStandard/ArmMmSvc.h new file mode 100644 index 000000000..11aa50e3a --- /dev/null +++ b/ArmPkg/Include/IndustryStandard/ArmMmSvc.h @@ -0,0 +1,62 @@ +/** @file +* +* Copyright (c) 2012-2017, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef ARM_MM_SVC_H_ +#define ARM_MM_SVC_H_ + +/* + * SVC IDs to allow the MM secure partition to initialise itself, handle + * delegated events and request the Secure partition manager to perform + * privileged operations on its behalf. + */ +#define ARM_SVC_ID_SPM_VERSION_AARCH32 0x84000060 +#define ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH32 0x84000061 +#define ARM_SVC_ID_SP_GET_MEM_ATTRIBUTES_AARCH32 0x84000064 +#define ARM_SVC_ID_SP_SET_MEM_ATTRIBUTES_AARCH32 0x84000065 +#define ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 0xC4000061 +#define ARM_SVC_ID_SP_GET_MEM_ATTRIBUTES_AARCH64 0xC4000064 +#define ARM_SVC_ID_SP_SET_MEM_ATTRIBUTES_AARCH64 0xC4000065 + +/* Generic IDs when using AArch32 or AArch64 execution state */ +#ifdef MDE_CPU_AARCH64 +#define ARM_SVC_ID_SP_EVENT_COMPLETE ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH64 +#define ARM_SVC_ID_SP_GET_MEM_ATTRIBUTES ARM_SVC_ID_SP_GET_MEM_ATTRIBUTES_AARCH64 +#define ARM_SVC_ID_SP_SET_MEM_ATTRIBUTES ARM_SVC_ID_SP_SET_MEM_ATTRIBUTES_AARCH64 +#endif +#ifdef MDE_CPU_ARM +#define ARM_SVC_ID_SP_EVENT_COMPLETE ARM_SVC_ID_SP_EVENT_COMPLETE_AARCH32 +#define ARM_SVC_ID_SP_GET_MEM_ATTRIBUTES ARM_SVC_ID_SP_GET_MEM_ATTRIBUTES_AARCH32 +#define ARM_SVC_ID_SP_SET_MEM_ATTRIBUTES ARM_SVC_ID_SP_SET_MEM_ATTRIBUTES_AARCH32 +#endif + +#define SET_MEM_ATTR_DATA_PERM_MASK 0x3 +#define SET_MEM_ATTR_DATA_PERM_SHIFT 0 +#define SET_MEM_ATTR_DATA_PERM_NO_ACCESS 0 +#define SET_MEM_ATTR_DATA_PERM_RW 1 +#define SET_MEM_ATTR_DATA_PERM_RO 3 + +#define SET_MEM_ATTR_CODE_PERM_MASK 0x1 +#define SET_MEM_ATTR_CODE_PERM_SHIFT 2 +#define SET_MEM_ATTR_CODE_PERM_X 0 +#define SET_MEM_ATTR_CODE_PERM_XN 1 + +#define SET_MEM_ATTR_MAKE_PERM_REQUEST(d_perm, c_perm) \ + ((((c_perm) & SET_MEM_ATTR_CODE_PERM_MASK) << SET_MEM_ATTR_CODE_PERM_SHIFT) | \ + (( (d_perm) & SET_MEM_ATTR_DATA_PERM_MASK) << SET_MEM_ATTR_DATA_PERM_SHIFT)) + +/* MM SVC Return error codes */ +#define ARM_SVC_SPM_RET_SUCCESS 0 +#define ARM_SVC_SPM_RET_NOT_SUPPORTED -1 +#define ARM_SVC_SPM_RET_INVALID_PARAMS -2 +#define ARM_SVC_SPM_RET_DENIED -3 +#define ARM_SVC_SPM_RET_NO_MEMORY -5 + +#define SPM_MAJOR_VERSION 0 +#define SPM_MINOR_VERSION 1 + +#endif // ARM_MM_SVC_H_ diff --git a/ArmPkg/Include/IndustryStandard/ArmStdSmc.h b/ArmPkg/Include/IndustryStandard/ArmStdSmc.h new file mode 100644 index 000000000..374b9ded7 --- /dev/null +++ b/ArmPkg/Include/IndustryStandard/ArmStdSmc.h @@ -0,0 +1,247 @@ +/** @file +* +* Copyright (c) 2020, NUVIA Inc. All rights reserved.
+* Copyright (c) 2012 - 2022, Arm Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +* @par Revision Reference: +* - [1] SMC Calling Convention version 1.2 +* (https://developer.arm.com/documentation/den0028/c/?lang=en) +* - [2] Arm True Random Number Generator Firmware, Interface 1.0, +* Platform Design Document. +* (https://developer.arm.com/documentation/den0098/latest/) +* +* @par Glossary: +* - TRNG - True Random Number Generator +* +**/ + +#ifndef ARM_STD_SMC_H_ +#define ARM_STD_SMC_H_ + +/* + * SMC function IDs for Standard Service queries + */ + +#define ARM_SMC_ID_STD_CALL_COUNT 0x8400ff00 +#define ARM_SMC_ID_STD_UID 0x8400ff01 +/* 0x8400ff02 is reserved */ +#define ARM_SMC_ID_STD_REVISION 0x8400ff03 + +/* + * The 'Standard Service Call UID' is supposed to return the Standard + * Service UUID. This is a 128-bit value. + */ +#define ARM_SMC_STD_UUID0 0x108d905b +#define ARM_SMC_STD_UUID1 0x47e8f863 +#define ARM_SMC_STD_UUID2 0xfbc02dae +#define ARM_SMC_STD_UUID3 0xe2f64156 + +/* + * ARM Standard Service Calls revision numbers + * The current revision is: 0.1 + */ +#define ARM_SMC_STD_REVISION_MAJOR 0x0 +#define ARM_SMC_STD_REVISION_MINOR 0x1 + +/* + * Management Mode (MM) calls cover a subset of the Standard Service Call range. + * The list below is not exhaustive. + */ +#define ARM_SMC_ID_MM_VERSION_AARCH32 0x84000040 +#define ARM_SMC_ID_MM_VERSION_AARCH64 0xC4000040 + +// Request service from secure standalone MM environment +#define ARM_SMC_ID_MM_COMMUNICATE_AARCH32 0x84000041 +#define ARM_SMC_ID_MM_COMMUNICATE_AARCH64 0xC4000041 + +/* Generic ID when using AArch32 or AArch64 execution state */ +#ifdef MDE_CPU_AARCH64 +#define ARM_SMC_ID_MM_COMMUNICATE ARM_SMC_ID_MM_COMMUNICATE_AARCH64 +#endif +#ifdef MDE_CPU_ARM +#define ARM_SMC_ID_MM_COMMUNICATE ARM_SMC_ID_MM_COMMUNICATE_AARCH32 +#endif + +/* MM return error codes */ +#define ARM_SMC_MM_RET_SUCCESS 0 +#define ARM_SMC_MM_RET_NOT_SUPPORTED -1 +#define ARM_SMC_MM_RET_INVALID_PARAMS -2 +#define ARM_SMC_MM_RET_DENIED -3 +#define ARM_SMC_MM_RET_NO_MEMORY -4 + +// ARM Architecture Calls +#define SMCCC_VERSION 0x80000000 +#define SMCCC_ARCH_FEATURES 0x80000001 +#define SMCCC_ARCH_SOC_ID 0x80000002 +#define SMCCC_ARCH_WORKAROUND_1 0x80008000 +#define SMCCC_ARCH_WORKAROUND_2 0x80007FFF + +#define SMC_ARCH_CALL_SUCCESS 0 +#define SMC_ARCH_CALL_NOT_SUPPORTED -1 +#define SMC_ARCH_CALL_NOT_REQUIRED -2 +#define SMC_ARCH_CALL_INVALID_PARAMETER -3 + +/* + * Power State Coordination Interface (PSCI) calls cover a subset of the + * Standard Service Call range. + * The list below is not exhaustive. + */ +#define ARM_SMC_ID_PSCI_VERSION 0x84000000 +#define ARM_SMC_ID_PSCI_CPU_SUSPEND_AARCH64 0xc4000001 +#define ARM_SMC_ID_PSCI_CPU_SUSPEND_AARCH32 0x84000001 +#define ARM_SMC_ID_PSCI_CPU_OFF 0x84000002 +#define ARM_SMC_ID_PSCI_CPU_ON_AARCH64 0xc4000003 +#define ARM_SMC_ID_PSCI_CPU_ON_AARCH32 0x84000003 +#define ARM_SMC_ID_PSCI_AFFINITY_INFO_AARCH64 0xc4000004 +#define ARM_SMC_ID_PSCI_AFFINITY_INFO_AARCH32 0x84000004 +#define ARM_SMC_ID_PSCI_MIGRATE_AARCH64 0xc4000005 +#define ARM_SMC_ID_PSCI_MIGRATE_AARCH32 0x84000005 +#define ARM_SMC_ID_PSCI_SYSTEM_OFF 0x84000008 +#define ARM_SMC_ID_PSCI_SYSTEM_RESET 0x84000009 +#define ARM_SMC_ID_PSCI_FEATURES 0x8400000A +#define ARM_SMC_ID_PSCI_SYSTEM_RESET2_AARCH64 0xC4000012 + +/* The current PSCI version is: 0.2 */ +#define ARM_SMC_PSCI_VERSION_MAJOR 0 +#define ARM_SMC_PSCI_VERSION_MINOR 2 +#define ARM_SMC_PSCI_VERSION \ + ((ARM_SMC_PSCI_VERSION_MAJOR << 16) | ARM_SMC_PSCI_VERSION_MINOR) + +/* PSCI return error codes */ +#define ARM_SMC_PSCI_RET_SUCCESS 0 +#define ARM_SMC_PSCI_RET_NOT_SUPPORTED -1 +#define ARM_SMC_PSCI_RET_INVALID_PARAMS -2 +#define ARM_SMC_PSCI_RET_DENIED -3 +#define ARM_SMC_PSCI_RET_ALREADY_ON -4 +#define ARM_SMC_PSCI_RET_ON_PENDING -5 +#define ARM_SMC_PSCI_RET_INTERN_FAIL -6 +#define ARM_SMC_PSCI_RET_NOT_PRESENT -7 +#define ARM_SMC_PSCI_RET_DISABLED -8 + +#define ARM_SMC_PSCI_TARGET_CPU32(Aff2, Aff1, Aff0) \ + ((((Aff2) & 0xFF) << 16) | (((Aff1) & 0xFF) << 8) | ((Aff0) & 0xFF)) + +#define ARM_SMC_PSCI_TARGET_CPU64(Aff3, Aff2, Aff1, Aff0) \ + ((((Aff3) & 0xFFULL) << 32) | (((Aff2) & 0xFF) << 16) | (((Aff1) & 0xFF) << 8) | ((Aff0) & 0xFF)) + +#define ARM_SMC_PSCI_TARGET_GET_AFF0(TargetId) ((TargetId) & 0xFF) +#define ARM_SMC_PSCI_TARGET_GET_AFF1(TargetId) (((TargetId) >> 8) & 0xFF) + +#define ARM_SMC_ID_PSCI_AFFINITY_LEVEL_0 0 +#define ARM_SMC_ID_PSCI_AFFINITY_LEVEL_1 1 +#define ARM_SMC_ID_PSCI_AFFINITY_LEVEL_2 2 +#define ARM_SMC_ID_PSCI_AFFINITY_LEVEL_3 3 + +#define ARM_SMC_ID_PSCI_AFFINITY_INFO_ON 0 +#define ARM_SMC_ID_PSCI_AFFINITY_INFO_OFF 1 +#define ARM_SMC_ID_PSCI_AFFINITY_INFO_ON_PENDING 2 + +/* + * SMC function IDs for Trusted OS Service queries + */ +#define ARM_SMC_ID_TOS_CALL_COUNT 0xbf00ff00 +#define ARM_SMC_ID_TOS_UID 0xbf00ff01 +/* 0xbf00ff02 is reserved */ +#define ARM_SMC_ID_TOS_REVISION 0xbf00ff03 + +// Firmware TRNG interface Function IDs + +/* + SMC/HVC call to get the version of the TRNG backend, + Cf. [2], 2.1 TRNG_VERSION + Input values: + W0 0x8400_0050 + W1-W7 Reserved (MBZ) + Return values: + Success (W0 > 0) W0[31] MBZ + W0[30:16] Major revision + W0[15:0] Minor revision + W1 - W3 Reserved (MBZ) + Error (W0 < 0) + NOT_SUPPORTED Function not implemented +*/ +#define ARM_SMC_ID_TRNG_VERSION 0x84000050 + +/* + SMC/HVC call to check if a TRNG function ID is implemented by the backend, + Cf. [2], Section 2.2 TRNG_FEATURES + Input Values + W0 0x8400_0051 + W1 trng_func_id + W2-W7 Reserved (MBZ) + Return values: + Success (W0 >= 0): + SUCCESS Function is implemented. + > 0 Function is implemented and + has specific capabilities, + see function definition. + Error (W0 < 0) + NOT_SUPPORTED Function with FID=trng_func_id + is not implemented +*/ +#define ARM_SMC_ID_TRNG_FEATURES 0x84000051 + +/* + SMC/HVC call to get the UUID of the TRNG backend, + Cf. [2], Section 2.3 TRNG_GET_UUID + Input Values: + W0 0x8400_0052 + W1-W7 Reserved (MBZ) + Return Values: + Success (W0 != -1) + W0 UUID[31:0] + W1 UUID[63:32] + W2 UUID[95:64] + W3 UUID[127:96] + Error (W0 = -1) + W0 NOT_SUPPORTED +*/ +#define ARM_SMC_ID_TRNG_GET_UUID 0x84000052 + +/* + AARCH32 SMC/HVC call to get entropy bits, Cf. [2], Section 2.4 TRNG_RND. + Input values: + W0 0x8400_0053 + W2-W7 Reserved (MBZ) + Return values: + Success (W0 = 0): + W0 MBZ + W1 Entropy[95:64] + W2 Entropy[63:32] + W3 Entropy[31:0] + Error (W0 < 0) + W0 NOT_SUPPORTED + NO_ENTROPY + INVALID_PARAMETERS + W1 - W3 Reserved (MBZ) +*/ +#define ARM_SMC_ID_TRNG_RND_AARCH32 0x84000053 + +/* + AARCH64 SMC/HVC call to get entropy bits, Cf. [2], Section 2.4 TRNG_RND. + Input values: + X0 0xC400_0053 + X2-X7 Reserved (MBZ) + Return values: + Success (X0 = 0): + X0 MBZ + X1 Entropy[191:128] + X2 Entropy[127:64] + X3 Entropy[63:0] + Error (X0 < 0) + X0 NOT_SUPPORTED + NO_ENTROPY + INVALID_PARAMETERS + X1 - X3 Reserved (MBZ) +*/ +#define ARM_SMC_ID_TRNG_RND_AARCH64 0xC4000053 + +// Firmware TRNG status codes +#define TRNG_STATUS_SUCCESS (INT32)(0) +#define TRNG_STATUS_NOT_SUPPORTED (INT32)(-1) +#define TRNG_STATUS_INVALID_PARAMETER (INT32)(-2) +#define TRNG_STATUS_NO_ENTROPY (INT32)(-3) + +#endif // ARM_STD_SMC_H_ diff --git a/ArmPkg/Include/Library/ArmDisassemblerLib.h b/ArmPkg/Include/Library/ArmDisassemblerLib.h new file mode 100644 index 000000000..f065ded5f --- /dev/null +++ b/ArmPkg/Include/Library/ArmDisassemblerLib.h @@ -0,0 +1,37 @@ +/** @file + + Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ARM_DISASSEMBLER_LIB_H_ +#define ARM_DISASSEMBLER_LIB_H_ + +/** + Place a disassembly of **OpCodePtr into buffer, and update OpCodePtr to + point to next instruction. + + We cheat and only decode instructions that access + memory. If the instruction is not found we dump the instruction in hex. + + @param OpCodePtrPtr Pointer to pointer of ARM Thumb instruction to disassemble. + @param Thumb TRUE for Thumb(2), FALSE for ARM instruction stream + @param Extended TRUE dump hex for instruction too. + @param ItBlock Size of IT Block + @param Buf Buffer to sprintf disassembly into. + @param Size Size of Buf in bytes. + +**/ +VOID +DisassembleInstruction ( + IN UINT8 **OpCodePtr, + IN BOOLEAN Thumb, + IN BOOLEAN Extended, + IN OUT UINT32 *ItBlock, + OUT CHAR8 *Buf, + OUT UINTN Size + ); + +#endif // ARM_DISASSEMBLER_LIB_H_ diff --git a/ArmPkg/Include/Library/ArmGenericTimerCounterLib.h b/ArmPkg/Include/Library/ArmGenericTimerCounterLib.h new file mode 100644 index 000000000..45a32c6c2 --- /dev/null +++ b/ArmPkg/Include/Library/ArmGenericTimerCounterLib.h @@ -0,0 +1,85 @@ +/** @file + + Copyright (c) 2011 - 2014, ARM Ltd. All rights reserved.
+ Copyright (c) 2014, Linaro Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ARM_GENERIC_TIMER_COUNTER_LIB_H_ +#define ARM_GENERIC_TIMER_COUNTER_LIB_H_ + +VOID +EFIAPI +ArmGenericTimerEnableTimer ( + VOID + ); + +VOID +EFIAPI +ArmGenericTimerReenableTimer ( + VOID + ); + +VOID +EFIAPI +ArmGenericTimerDisableTimer ( + VOID + ); + +VOID +EFIAPI +ArmGenericTimerSetTimerFreq ( + IN UINTN FreqInHz + ); + +UINTN +EFIAPI +ArmGenericTimerGetTimerFreq ( + VOID + ); + +VOID +EFIAPI +ArmGenericTimerSetTimerVal ( + IN UINTN Value + ); + +UINTN +EFIAPI +ArmGenericTimerGetTimerVal ( + VOID + ); + +UINT64 +EFIAPI +ArmGenericTimerGetSystemCount ( + VOID + ); + +UINTN +EFIAPI +ArmGenericTimerGetTimerCtrlReg ( + VOID + ); + +VOID +EFIAPI +ArmGenericTimerSetTimerCtrlReg ( + UINTN Value + ); + +UINT64 +EFIAPI +ArmGenericTimerGetCompareVal ( + VOID + ); + +VOID +EFIAPI +ArmGenericTimerSetCompareVal ( + IN UINT64 Value + ); + +#endif // ARM_GENERIC_TIMER_COUNTER_LIB_H_ diff --git a/ArmPkg/Include/Library/ArmGicArchLib.h b/ArmPkg/Include/Library/ArmGicArchLib.h new file mode 100644 index 000000000..72ac17e13 --- /dev/null +++ b/ArmPkg/Include/Library/ArmGicArchLib.h @@ -0,0 +1,26 @@ +/** @file +* +* Copyright (c) 2015, Linaro Ltd. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef ARM_GIC_ARCH_LIB_H_ +#define ARM_GIC_ARCH_LIB_H_ + +// +// GIC definitions +// +typedef enum { + ARM_GIC_ARCH_REVISION_2, + ARM_GIC_ARCH_REVISION_3 +} ARM_GIC_ARCH_REVISION; + +ARM_GIC_ARCH_REVISION +EFIAPI +ArmGicGetSupportedArchRevision ( + VOID + ); + +#endif // ARM_GIC_ARCH_LIB_H_ diff --git a/ArmPkg/Include/Library/ArmGicLib.h b/ArmPkg/Include/Library/ArmGicLib.h new file mode 100644 index 000000000..4ab670967 --- /dev/null +++ b/ArmPkg/Include/Library/ArmGicLib.h @@ -0,0 +1,335 @@ +/** @file +* +* Copyright (c) 2011-2021, Arm Limited. All rights reserved.
+* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef ARMGIC_H_ +#define ARMGIC_H_ + +#include + +// GIC Distributor +#define ARM_GIC_ICDDCR 0x000 // Distributor Control Register +#define ARM_GIC_ICDICTR 0x004 // Interrupt Controller Type Register +#define ARM_GIC_ICDIIDR 0x008 // Implementer Identification Register + +// Each reg base below repeats for Number of interrupts / 4 (see GIC spec) +#define ARM_GIC_ICDISR 0x080 // Interrupt Security Registers +#define ARM_GIC_ICDISER 0x100 // Interrupt Set-Enable Registers +#define ARM_GIC_ICDICER 0x180 // Interrupt Clear-Enable Registers +#define ARM_GIC_ICDSPR 0x200 // Interrupt Set-Pending Registers +#define ARM_GIC_ICDICPR 0x280 // Interrupt Clear-Pending Registers +#define ARM_GIC_ICDABR 0x300 // Active Bit Registers + +// Each reg base below repeats for Number of interrupts / 4 +#define ARM_GIC_ICDIPR 0x400 // Interrupt Priority Registers + +// Each reg base below repeats for Number of interrupts +#define ARM_GIC_ICDIPTR 0x800 // Interrupt Processor Target Registers +#define ARM_GIC_ICDICFR 0xC00 // Interrupt Configuration Registers + +#define ARM_GIC_ICDPPISR 0xD00 // PPI Status register + +// just one of these +#define ARM_GIC_ICDSGIR 0xF00 // Software Generated Interrupt Register + +// GICv3 specific registers +#define ARM_GICD_IROUTER 0x6100 // Interrupt Routing Registers + +// GICD_CTLR bits +#define ARM_GIC_ICDDCR_ARE (1 << 4) // Affinity Routing Enable (ARE) +#define ARM_GIC_ICDDCR_DS (1 << 6) // Disable Security (DS) + +// GICD_ICDICFR bits +#define ARM_GIC_ICDICFR_WIDTH 32 // ICDICFR is a 32 bit register +#define ARM_GIC_ICDICFR_BYTES (ARM_GIC_ICDICFR_WIDTH / 8) +#define ARM_GIC_ICDICFR_F_WIDTH 2 // Each F field is 2 bits +#define ARM_GIC_ICDICFR_F_STRIDE 16 // (32/2) F fields per register +#define ARM_GIC_ICDICFR_F_CONFIG1_BIT 1 // Bit number within F field +#define ARM_GIC_ICDICFR_LEVEL_TRIGGERED 0x0 // Level triggered interrupt +#define ARM_GIC_ICDICFR_EDGE_TRIGGERED 0x1 // Edge triggered interrupt + +// GIC Redistributor +#define ARM_GICR_CTLR_FRAME_SIZE SIZE_64KB +#define ARM_GICR_SGI_PPI_FRAME_SIZE SIZE_64KB +#define ARM_GICR_SGI_VLPI_FRAME_SIZE SIZE_64KB +#define ARM_GICR_SGI_RESERVED_FRAME_SIZE SIZE_64KB + +// GIC Redistributor Control frame +#define ARM_GICR_TYPER 0x0008 // Redistributor Type Register + +// GIC Redistributor TYPER bit assignments +#define ARM_GICR_TYPER_PLPIS (1 << 0) // Physical LPIs +#define ARM_GICR_TYPER_VLPIS (1 << 1) // Virtual LPIs +#define ARM_GICR_TYPER_DIRECTLPI (1 << 3) // Direct LPIs +#define ARM_GICR_TYPER_LAST (1 << 4) // Last Redistributor in series +#define ARM_GICR_TYPER_DPGS (1 << 5) // Disable Processor Group + // Selection Support +#define ARM_GICR_TYPER_PROCNO (0xFFFF << 8) // Processor Number +#define ARM_GICR_TYPER_COMMONLPIAFF (0x3 << 24) // Common LPI Affinity +#define ARM_GICR_TYPER_AFFINITY (0xFFFFFFFFULL << 32) // Redistributor Affinity + +#define ARM_GICR_TYPER_GET_AFFINITY(TypeReg) (((TypeReg) & \ + ARM_GICR_TYPER_AFFINITY) >> 32) + +// GIC SGI & PPI Redistributor frame +#define ARM_GICR_ISENABLER 0x0100 // Interrupt Set-Enable Registers +#define ARM_GICR_ICENABLER 0x0180 // Interrupt Clear-Enable Registers + +// GIC Cpu interface +#define ARM_GIC_ICCICR 0x00 // CPU Interface Control Register +#define ARM_GIC_ICCPMR 0x04 // Interrupt Priority Mask Register +#define ARM_GIC_ICCBPR 0x08 // Binary Point Register +#define ARM_GIC_ICCIAR 0x0C // Interrupt Acknowledge Register +#define ARM_GIC_ICCEIOR 0x10 // End Of Interrupt Register +#define ARM_GIC_ICCRPR 0x14 // Running Priority Register +#define ARM_GIC_ICCPIR 0x18 // Highest Pending Interrupt Register +#define ARM_GIC_ICCABPR 0x1C // Aliased Binary Point Register +#define ARM_GIC_ICCIIDR 0xFC // Identification Register + +#define ARM_GIC_ICDSGIR_FILTER_TARGETLIST 0x0 +#define ARM_GIC_ICDSGIR_FILTER_EVERYONEELSE 0x1 +#define ARM_GIC_ICDSGIR_FILTER_ITSELF 0x2 + +// Bit-masks to configure the CPU Interface Control register +#define ARM_GIC_ICCICR_ENABLE_SECURE 0x01 +#define ARM_GIC_ICCICR_ENABLE_NS 0x02 +#define ARM_GIC_ICCICR_ACK_CTL 0x04 +#define ARM_GIC_ICCICR_SIGNAL_SECURE_TO_FIQ 0x08 +#define ARM_GIC_ICCICR_USE_SBPR 0x10 + +// Bit Mask for GICC_IIDR +#define ARM_GIC_ICCIIDR_GET_PRODUCT_ID(IccIidr) (((IccIidr) >> 20) & 0xFFF) +#define ARM_GIC_ICCIIDR_GET_ARCH_VERSION(IccIidr) (((IccIidr) >> 16) & 0xF) +#define ARM_GIC_ICCIIDR_GET_REVISION(IccIidr) (((IccIidr) >> 12) & 0xF) +#define ARM_GIC_ICCIIDR_GET_IMPLEMENTER(IccIidr) ((IccIidr) & 0xFFF) + +// Bit Mask for +#define ARM_GIC_ICCIAR_ACKINTID 0x3FF + +UINTN +EFIAPI +ArmGicGetInterfaceIdentification ( + IN INTN GicInterruptInterfaceBase + ); + +// GIC Secure interfaces +VOID +EFIAPI +ArmGicSetupNonSecure ( + IN UINTN MpId, + IN INTN GicDistributorBase, + IN INTN GicInterruptInterfaceBase + ); + +VOID +EFIAPI +ArmGicSetSecureInterrupts ( + IN UINTN GicDistributorBase, + IN UINTN *GicSecureInterruptMask, + IN UINTN GicSecureInterruptMaskSize + ); + +VOID +EFIAPI +ArmGicEnableInterruptInterface ( + IN INTN GicInterruptInterfaceBase + ); + +VOID +EFIAPI +ArmGicDisableInterruptInterface ( + IN INTN GicInterruptInterfaceBase + ); + +VOID +EFIAPI +ArmGicEnableDistributor ( + IN INTN GicDistributorBase + ); + +VOID +EFIAPI +ArmGicDisableDistributor ( + IN INTN GicDistributorBase + ); + +UINTN +EFIAPI +ArmGicGetMaxNumInterrupts ( + IN INTN GicDistributorBase + ); + +VOID +EFIAPI +ArmGicSendSgiTo ( + IN INTN GicDistributorBase, + IN INTN TargetListFilter, + IN INTN CPUTargetList, + IN INTN SgiId + ); + +/* + * Acknowledge and return the value of the Interrupt Acknowledge Register + * + * InterruptId is returned separately from the register value because in + * the GICv2 the register value contains the CpuId and InterruptId while + * in the GICv3 the register value is only the InterruptId. + * + * @param GicInterruptInterfaceBase Base Address of the GIC CPU Interface + * @param InterruptId InterruptId read from the Interrupt + * Acknowledge Register + * + * @retval value returned by the Interrupt Acknowledge Register + * + */ +UINTN +EFIAPI +ArmGicAcknowledgeInterrupt ( + IN UINTN GicInterruptInterfaceBase, + OUT UINTN *InterruptId + ); + +VOID +EFIAPI +ArmGicEndOfInterrupt ( + IN UINTN GicInterruptInterfaceBase, + IN UINTN Source + ); + +UINTN +EFIAPI +ArmGicSetPriorityMask ( + IN INTN GicInterruptInterfaceBase, + IN INTN PriorityMask + ); + +VOID +EFIAPI +ArmGicSetInterruptPriority ( + IN UINTN GicDistributorBase, + IN UINTN GicRedistributorBase, + IN UINTN Source, + IN UINTN Priority + ); + +VOID +EFIAPI +ArmGicEnableInterrupt ( + IN UINTN GicDistributorBase, + IN UINTN GicRedistributorBase, + IN UINTN Source + ); + +VOID +EFIAPI +ArmGicDisableInterrupt ( + IN UINTN GicDistributorBase, + IN UINTN GicRedistributorBase, + IN UINTN Source + ); + +BOOLEAN +EFIAPI +ArmGicIsInterruptEnabled ( + IN UINTN GicDistributorBase, + IN UINTN GicRedistributorBase, + IN UINTN Source + ); + +// GIC revision 2 specific declarations + +// Interrupts from 1020 to 1023 are considered as special interrupts +// (eg: spurious interrupts) +#define ARM_GIC_IS_SPECIAL_INTERRUPTS(Interrupt) \ + (((Interrupt) >= 1020) && ((Interrupt) <= 1023)) + +VOID +EFIAPI +ArmGicV2SetupNonSecure ( + IN UINTN MpId, + IN INTN GicDistributorBase, + IN INTN GicInterruptInterfaceBase + ); + +VOID +EFIAPI +ArmGicV2EnableInterruptInterface ( + IN INTN GicInterruptInterfaceBase + ); + +VOID +EFIAPI +ArmGicV2DisableInterruptInterface ( + IN INTN GicInterruptInterfaceBase + ); + +UINTN +EFIAPI +ArmGicV2AcknowledgeInterrupt ( + IN UINTN GicInterruptInterfaceBase + ); + +VOID +EFIAPI +ArmGicV2EndOfInterrupt ( + IN UINTN GicInterruptInterfaceBase, + IN UINTN Source + ); + +// GIC revision 3 specific declarations + +#define ICC_SRE_EL2_SRE (1 << 0) + +#define ARM_GICD_IROUTER_IRM BIT31 + +UINT32 +EFIAPI +ArmGicV3GetControlSystemRegisterEnable ( + VOID + ); + +VOID +EFIAPI +ArmGicV3SetControlSystemRegisterEnable ( + IN UINT32 ControlSystemRegisterEnable + ); + +VOID +EFIAPI +ArmGicV3EnableInterruptInterface ( + VOID + ); + +VOID +EFIAPI +ArmGicV3DisableInterruptInterface ( + VOID + ); + +UINTN +EFIAPI +ArmGicV3AcknowledgeInterrupt ( + VOID + ); + +VOID +EFIAPI +ArmGicV3EndOfInterrupt ( + IN UINTN Source + ); + +VOID +ArmGicV3SetBinaryPointer ( + IN UINTN BinaryPoint + ); + +VOID +ArmGicV3SetPriorityMask ( + IN UINTN Priority + ); + +#endif // ARMGIC_H_ diff --git a/ArmPkg/Include/Library/ArmHvcLib.h b/ArmPkg/Include/Library/ArmHvcLib.h new file mode 100644 index 000000000..663ceb8e1 --- /dev/null +++ b/ArmPkg/Include/Library/ArmHvcLib.h @@ -0,0 +1,40 @@ +/** @file +* +* Copyright (c) 2012-2014, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef ARM_HVC_LIB_H_ +#define ARM_HVC_LIB_H_ + +/** + * The size of the HVC arguments are different between AArch64 and AArch32. + * The native size is used for the arguments. + */ +typedef struct { + UINTN Arg0; + UINTN Arg1; + UINTN Arg2; + UINTN Arg3; + UINTN Arg4; + UINTN Arg5; + UINTN Arg6; + UINTN Arg7; +} ARM_HVC_ARGS; + +/** + Trigger an HVC call + + HVC calls can take up to 8 arguments and return up to 4 return values. + Therefore, the 4 first fields in the ARM_HVC_ARGS structure are used + for both input and output values. + +**/ +VOID +ArmCallHvc ( + IN OUT ARM_HVC_ARGS *Args + ); + +#endif // ARM_HVC_LIB_H_ diff --git a/ArmPkg/Include/Library/ArmLib.h b/ArmPkg/Include/Library/ArmLib.h new file mode 100644 index 000000000..6566deebd --- /dev/null +++ b/ArmPkg/Include/Library/ArmLib.h @@ -0,0 +1,791 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Copyright (c) 2011 - 2016, ARM Ltd. All rights reserved.
+ Copyright (c) 2020 - 2021, NUVIA Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ARM_LIB_H_ +#define ARM_LIB_H_ + +#include + +#ifdef MDE_CPU_ARM + #include +#elif defined (MDE_CPU_AARCH64) + #include +#else + #error "Unknown chipset." +#endif + +#define EFI_MEMORY_CACHETYPE_MASK (EFI_MEMORY_UC | EFI_MEMORY_WC | \ + EFI_MEMORY_WT | EFI_MEMORY_WB | \ + EFI_MEMORY_UCE) + +/** + * The UEFI firmware must not use the ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_* attributes. + * + * The Non Secure memory attribute (ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_*) should only + * be used in Secure World to distinguished Secure to Non-Secure memory. + */ +typedef enum { + ARM_MEMORY_REGION_ATTRIBUTE_UNCACHED_UNBUFFERED = 0, + ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_UNCACHED_UNBUFFERED, + ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK, + ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK, + + // On some platforms, memory mapped flash region is designed as not supporting + // shareable attribute, so WRITE_BACK_NONSHAREABLE is added for such special + // need. + // Do NOT use below two attributes if you are not sure. + ARM_MEMORY_REGION_ATTRIBUTE_WRITE_BACK_NONSHAREABLE, + ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_BACK_NONSHAREABLE, + + ARM_MEMORY_REGION_ATTRIBUTE_WRITE_THROUGH, + ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_WRITE_THROUGH, + ARM_MEMORY_REGION_ATTRIBUTE_DEVICE, + ARM_MEMORY_REGION_ATTRIBUTE_NONSECURE_DEVICE +} ARM_MEMORY_REGION_ATTRIBUTES; + +#define IS_ARM_MEMORY_REGION_ATTRIBUTES_SECURE(attr) ((UINT32)(attr) & 1) + +typedef struct { + EFI_PHYSICAL_ADDRESS PhysicalBase; + EFI_VIRTUAL_ADDRESS VirtualBase; + UINT64 Length; + ARM_MEMORY_REGION_ATTRIBUTES Attributes; +} ARM_MEMORY_REGION_DESCRIPTOR; + +typedef VOID (*CACHE_OPERATION)( + VOID + ); +typedef VOID (*LINE_OPERATION)( + UINTN + ); + +// +// ARM Processor Mode +// +typedef enum { + ARM_PROCESSOR_MODE_USER = 0x10, + ARM_PROCESSOR_MODE_FIQ = 0x11, + ARM_PROCESSOR_MODE_IRQ = 0x12, + ARM_PROCESSOR_MODE_SUPERVISOR = 0x13, + ARM_PROCESSOR_MODE_ABORT = 0x17, + ARM_PROCESSOR_MODE_HYP = 0x1A, + ARM_PROCESSOR_MODE_UNDEFINED = 0x1B, + ARM_PROCESSOR_MODE_SYSTEM = 0x1F, + ARM_PROCESSOR_MODE_MASK = 0x1F +} ARM_PROCESSOR_MODE; + +// +// ARM Cpu IDs +// +#define ARM_CPU_IMPLEMENTER_MASK (0xFFU << 24) +#define ARM_CPU_IMPLEMENTER_ARMLTD (0x41U << 24) +#define ARM_CPU_IMPLEMENTER_DEC (0x44U << 24) +#define ARM_CPU_IMPLEMENTER_MOT (0x4DU << 24) +#define ARM_CPU_IMPLEMENTER_QUALCOMM (0x51U << 24) +#define ARM_CPU_IMPLEMENTER_MARVELL (0x56U << 24) + +#define ARM_CPU_PRIMARY_PART_MASK (0xFFF << 4) +#define ARM_CPU_PRIMARY_PART_CORTEXA5 (0xC05 << 4) +#define ARM_CPU_PRIMARY_PART_CORTEXA7 (0xC07 << 4) +#define ARM_CPU_PRIMARY_PART_CORTEXA8 (0xC08 << 4) +#define ARM_CPU_PRIMARY_PART_CORTEXA9 (0xC09 << 4) +#define ARM_CPU_PRIMARY_PART_CORTEXA15 (0xC0F << 4) + +// +// ARM MP Core IDs +// +#define ARM_CORE_AFF0 0xFF +#define ARM_CORE_AFF1 (0xFF << 8) +#define ARM_CORE_AFF2 (0xFF << 16) +#define ARM_CORE_AFF3 (0xFFULL << 32) + +#define ARM_CORE_MASK ARM_CORE_AFF0 +#define ARM_CLUSTER_MASK ARM_CORE_AFF1 +#define GET_CORE_ID(MpId) ((MpId) & ARM_CORE_MASK) +#define GET_CLUSTER_ID(MpId) (((MpId) & ARM_CLUSTER_MASK) >> 8) +#define GET_MPID(ClusterId, CoreId) (((ClusterId) << 8) | (CoreId)) +#define GET_MPIDR_AFF0(MpId) ((MpId) & ARM_CORE_AFF0) +#define GET_MPIDR_AFF1(MpId) (((MpId) & ARM_CORE_AFF1) >> 8) +#define GET_MPIDR_AFF2(MpId) (((MpId) & ARM_CORE_AFF2) >> 16) +#define GET_MPIDR_AFF3(MpId) (((MpId) & ARM_CORE_AFF3) >> 32) +#define PRIMARY_CORE_ID (PcdGet32(PcdArmPrimaryCore) & ARM_CORE_MASK) + +/** Reads the CCSIDR register for the specified cache. + + @param CSSELR The CSSELR cache selection register value. + + @return The contents of the CCSIDR_EL1 register for the specified cache, when in AARCH64 mode. + Returns the contents of the CCSIDR register in AARCH32 mode. +**/ +UINTN +ReadCCSIDR ( + IN UINT32 CSSELR + ); + +/** Reads the CCSIDR2 for the specified cache. + + @param CSSELR The CSSELR cache selection register value + + @return The contents of the CCSIDR2 register for the specified cache. +**/ +UINT32 +ReadCCSIDR2 ( + IN UINT32 CSSELR + ); + +/** Reads the Cache Level ID (CLIDR) register. + + @return The contents of the CLIDR_EL1 register. +**/ +UINT32 +ReadCLIDR ( + VOID + ); + +UINTN +EFIAPI +ArmDataCacheLineLength ( + VOID + ); + +UINTN +EFIAPI +ArmInstructionCacheLineLength ( + VOID + ); + +UINTN +EFIAPI +ArmCacheWritebackGranule ( + VOID + ); + +UINTN +EFIAPI +ArmIsArchTimerImplemented ( + VOID + ); + +UINTN +EFIAPI +ArmCacheInfo ( + VOID + ); + +BOOLEAN +EFIAPI +ArmIsMpCore ( + VOID + ); + +VOID +EFIAPI +ArmInvalidateDataCache ( + VOID + ); + +VOID +EFIAPI +ArmCleanInvalidateDataCache ( + VOID + ); + +VOID +EFIAPI +ArmCleanDataCache ( + VOID + ); + +VOID +EFIAPI +ArmInvalidateInstructionCache ( + VOID + ); + +VOID +EFIAPI +ArmInvalidateDataCacheEntryByMVA ( + IN UINTN Address + ); + +VOID +EFIAPI +ArmCleanDataCacheEntryToPoUByMVA ( + IN UINTN Address + ); + +VOID +EFIAPI +ArmInvalidateInstructionCacheEntryToPoUByMVA ( + IN UINTN Address + ); + +VOID +EFIAPI +ArmCleanDataCacheEntryByMVA ( + IN UINTN Address + ); + +VOID +EFIAPI +ArmCleanInvalidateDataCacheEntryByMVA ( + IN UINTN Address + ); + +VOID +EFIAPI +ArmEnableDataCache ( + VOID + ); + +VOID +EFIAPI +ArmDisableDataCache ( + VOID + ); + +VOID +EFIAPI +ArmEnableInstructionCache ( + VOID + ); + +VOID +EFIAPI +ArmDisableInstructionCache ( + VOID + ); + +VOID +EFIAPI +ArmEnableMmu ( + VOID + ); + +VOID +EFIAPI +ArmDisableMmu ( + VOID + ); + +VOID +EFIAPI +ArmEnableCachesAndMmu ( + VOID + ); + +VOID +EFIAPI +ArmDisableCachesAndMmu ( + VOID + ); + +VOID +EFIAPI +ArmEnableInterrupts ( + VOID + ); + +UINTN +EFIAPI +ArmDisableInterrupts ( + VOID + ); + +BOOLEAN +EFIAPI +ArmGetInterruptState ( + VOID + ); + +VOID +EFIAPI +ArmEnableAsynchronousAbort ( + VOID + ); + +UINTN +EFIAPI +ArmDisableAsynchronousAbort ( + VOID + ); + +VOID +EFIAPI +ArmEnableIrq ( + VOID + ); + +UINTN +EFIAPI +ArmDisableIrq ( + VOID + ); + +VOID +EFIAPI +ArmEnableFiq ( + VOID + ); + +UINTN +EFIAPI +ArmDisableFiq ( + VOID + ); + +BOOLEAN +EFIAPI +ArmGetFiqState ( + VOID + ); + +/** + * Invalidate Data and Instruction TLBs + */ +VOID +EFIAPI +ArmInvalidateTlb ( + VOID + ); + +VOID +EFIAPI +ArmUpdateTranslationTableEntry ( + IN VOID *TranslationTableEntry, + IN VOID *Mva + ); + +VOID +EFIAPI +ArmSetDomainAccessControl ( + IN UINT32 Domain + ); + +VOID +EFIAPI +ArmSetTTBR0 ( + IN VOID *TranslationTableBase + ); + +VOID +EFIAPI +ArmSetTTBCR ( + IN UINT32 Bits + ); + +VOID * +EFIAPI +ArmGetTTBR0BaseAddress ( + VOID + ); + +BOOLEAN +EFIAPI +ArmMmuEnabled ( + VOID + ); + +VOID +EFIAPI +ArmEnableBranchPrediction ( + VOID + ); + +VOID +EFIAPI +ArmDisableBranchPrediction ( + VOID + ); + +VOID +EFIAPI +ArmSetLowVectors ( + VOID + ); + +VOID +EFIAPI +ArmSetHighVectors ( + VOID + ); + +VOID +EFIAPI +ArmDataMemoryBarrier ( + VOID + ); + +VOID +EFIAPI +ArmDataSynchronizationBarrier ( + VOID + ); + +VOID +EFIAPI +ArmInstructionSynchronizationBarrier ( + VOID + ); + +VOID +EFIAPI +ArmWriteVBar ( + IN UINTN VectorBase + ); + +UINTN +EFIAPI +ArmReadVBar ( + VOID + ); + +VOID +EFIAPI +ArmWriteAuxCr ( + IN UINT32 Bit + ); + +UINT32 +EFIAPI +ArmReadAuxCr ( + VOID + ); + +VOID +EFIAPI +ArmSetAuxCrBit ( + IN UINT32 Bits + ); + +VOID +EFIAPI +ArmUnsetAuxCrBit ( + IN UINT32 Bits + ); + +VOID +EFIAPI +ArmCallSEV ( + VOID + ); + +VOID +EFIAPI +ArmCallWFE ( + VOID + ); + +VOID +EFIAPI +ArmCallWFI ( + + VOID + ); + +UINTN +EFIAPI +ArmReadMpidr ( + VOID + ); + +UINTN +EFIAPI +ArmReadMidr ( + VOID + ); + +UINT32 +EFIAPI +ArmReadCpacr ( + VOID + ); + +VOID +EFIAPI +ArmWriteCpacr ( + IN UINT32 Access + ); + +VOID +EFIAPI +ArmEnableVFP ( + VOID + ); + +/** + Get the Secure Configuration Register value + + @return Value read from the Secure Configuration Register + +**/ +UINT32 +EFIAPI +ArmReadScr ( + VOID + ); + +/** + Set the Secure Configuration Register + + @param Value Value to write to the Secure Configuration Register + +**/ +VOID +EFIAPI +ArmWriteScr ( + IN UINT32 Value + ); + +UINT32 +EFIAPI +ArmReadMVBar ( + VOID + ); + +VOID +EFIAPI +ArmWriteMVBar ( + IN UINT32 VectorMonitorBase + ); + +UINT32 +EFIAPI +ArmReadSctlr ( + VOID + ); + +VOID +EFIAPI +ArmWriteSctlr ( + IN UINT32 Value + ); + +UINTN +EFIAPI +ArmReadHVBar ( + VOID + ); + +VOID +EFIAPI +ArmWriteHVBar ( + IN UINTN HypModeVectorBase + ); + +// +// Helper functions for accessing CPU ACTLR +// + +UINTN +EFIAPI +ArmReadCpuActlr ( + VOID + ); + +VOID +EFIAPI +ArmWriteCpuActlr ( + IN UINTN Val + ); + +VOID +EFIAPI +ArmSetCpuActlrBit ( + IN UINTN Bits + ); + +VOID +EFIAPI +ArmUnsetCpuActlrBit ( + IN UINTN Bits + ); + +// +// Accessors for the architected generic timer registers +// + +#define ARM_ARCH_TIMER_ENABLE (1 << 0) +#define ARM_ARCH_TIMER_IMASK (1 << 1) +#define ARM_ARCH_TIMER_ISTATUS (1 << 2) + +UINTN +EFIAPI +ArmReadCntFrq ( + VOID + ); + +VOID +EFIAPI +ArmWriteCntFrq ( + UINTN FreqInHz + ); + +UINT64 +EFIAPI +ArmReadCntPct ( + VOID + ); + +UINTN +EFIAPI +ArmReadCntkCtl ( + VOID + ); + +VOID +EFIAPI +ArmWriteCntkCtl ( + UINTN Val + ); + +UINTN +EFIAPI +ArmReadCntpTval ( + VOID + ); + +VOID +EFIAPI +ArmWriteCntpTval ( + UINTN Val + ); + +UINTN +EFIAPI +ArmReadCntpCtl ( + VOID + ); + +VOID +EFIAPI +ArmWriteCntpCtl ( + UINTN Val + ); + +UINTN +EFIAPI +ArmReadCntvTval ( + VOID + ); + +VOID +EFIAPI +ArmWriteCntvTval ( + UINTN Val + ); + +UINTN +EFIAPI +ArmReadCntvCtl ( + VOID + ); + +VOID +EFIAPI +ArmWriteCntvCtl ( + UINTN Val + ); + +UINT64 +EFIAPI +ArmReadCntvCt ( + VOID + ); + +UINT64 +EFIAPI +ArmReadCntpCval ( + VOID + ); + +VOID +EFIAPI +ArmWriteCntpCval ( + UINT64 Val + ); + +UINT64 +EFIAPI +ArmReadCntvCval ( + VOID + ); + +VOID +EFIAPI +ArmWriteCntvCval ( + UINT64 Val + ); + +UINT64 +EFIAPI +ArmReadCntvOff ( + VOID + ); + +VOID +EFIAPI +ArmWriteCntvOff ( + UINT64 Val + ); + +UINTN +EFIAPI +ArmGetPhysicalAddressBits ( + VOID + ); + +/// +/// ID Register Helper functions +/// + +/** + Check whether the CPU supports the GIC system register interface (any version) + + @return Whether GIC System Register Interface is supported + +**/ +BOOLEAN +EFIAPI +ArmHasGicSystemRegisters ( + VOID + ); + +/** Checks if CCIDX is implemented. + + @retval TRUE CCIDX is implemented. + @retval FALSE CCIDX is not implemented. +**/ +BOOLEAN +EFIAPI +ArmHasCcidx ( + VOID + ); + +#ifdef MDE_CPU_ARM +/// +/// AArch32-only ID Register Helper functions +/// + +/** + Check whether the CPU supports the Security extensions + + @return Whether the Security extensions are implemented + +**/ +BOOLEAN +EFIAPI +ArmHasSecurityExtensions ( + VOID + ); + +#endif // MDE_CPU_ARM + +#endif // ARM_LIB_H_ diff --git a/ArmPkg/Include/Library/ArmMmuLib.h b/ArmPkg/Include/Library/ArmMmuLib.h new file mode 100644 index 000000000..b745e2230 --- /dev/null +++ b/ArmPkg/Include/Library/ArmMmuLib.h @@ -0,0 +1,68 @@ +/** @file + + Copyright (c) 2015 - 2016, Linaro Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ARM_MMU_LIB_H_ +#define ARM_MMU_LIB_H_ + +#include + +#include + +EFI_STATUS +EFIAPI +ArmConfigureMmu ( + IN ARM_MEMORY_REGION_DESCRIPTOR *MemoryTable, + OUT VOID **TranslationTableBase OPTIONAL, + OUT UINTN *TranslationTableSize OPTIONAL + ); + +EFI_STATUS +EFIAPI +ArmSetMemoryRegionNoExec ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +EFI_STATUS +EFIAPI +ArmClearMemoryRegionNoExec ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +EFI_STATUS +EFIAPI +ArmSetMemoryRegionReadOnly ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +EFI_STATUS +EFIAPI +ArmClearMemoryRegionReadOnly ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +VOID +EFIAPI +ArmReplaceLiveTranslationEntry ( + IN UINT64 *Entry, + IN UINT64 Value, + IN UINT64 RegionStart, + IN BOOLEAN DisableMmu + ); + +EFI_STATUS +ArmSetMemoryAttributes ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length, + IN UINT64 Attributes + ); + +#endif // ARM_MMU_LIB_H_ diff --git a/ArmPkg/Include/Library/ArmMonitorLib.h b/ArmPkg/Include/Library/ArmMonitorLib.h new file mode 100644 index 000000000..d6e13b61d --- /dev/null +++ b/ArmPkg/Include/Library/ArmMonitorLib.h @@ -0,0 +1,42 @@ +/** @file + + Copyright (c) 2022, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef ARM_MONITOR_LIB_H_ +#define ARM_MONITOR_LIB_H_ + +/** The size of the SMC arguments is different between AArch64 and AArch32. + + The native size is used for the arguments. + It will be casted to either HVC or SMC args. +*/ +typedef struct { + UINTN Arg0; + UINTN Arg1; + UINTN Arg2; + UINTN Arg3; + UINTN Arg4; + UINTN Arg5; + UINTN Arg6; + UINTN Arg7; +} ARM_MONITOR_ARGS; + +/** Monitor call. + + An HyperVisor Call (HVC) or System Monitor Call (SMC) will be issued + depending on the default conduit. PcdMonitorConduitHvc determines the type + of the call: if true, do an HVC. + + @param [in,out] Args Arguments for the HVC/SMC. +**/ +VOID +EFIAPI +ArmMonitorCall ( + IN OUT ARM_MONITOR_ARGS *Args + ); + +#endif // ARM_MONITOR_LIB_H_ diff --git a/ArmPkg/Include/Library/ArmMtlLib.h b/ArmPkg/Include/Library/ArmMtlLib.h new file mode 100644 index 000000000..3050f0616 --- /dev/null +++ b/ArmPkg/Include/Library/ArmMtlLib.h @@ -0,0 +1,130 @@ +/** @file + + Copyright (c) 2017-2018, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#ifndef ARM_MTL_LIB_H_ +#define ARM_MTL_LIB_H_ + +#include + +// Ideally we don't need packed struct. However we can't rely on compilers. +#pragma pack(1) + +typedef struct { + UINT32 Reserved1; + UINT32 ChannelStatus; + UINT64 Reserved2; + UINT32 Flags; + UINT32 Length; + UINT32 MessageHeader; + + // NOTE: Since EDK2 does not allow flexible array member [] we declare + // here array of 1 element length. However below is used as a variable + // length array. + UINT32 Payload[1]; // size less object gives offset to payload. +} MTL_MAILBOX; + +#pragma pack() + +// Channel Type, Low-priority, and High-priority +typedef enum { + MTL_CHANNEL_TYPE_LOW = 0, + MTL_CHANNEL_TYPE_HIGH = 1 +} MTL_CHANNEL_TYPE; + +typedef struct { + UINT64 PhysicalAddress; + UINT32 ModifyMask; + UINT32 PreserveMask; +} MTL_DOORBELL; + +typedef struct { + MTL_CHANNEL_TYPE ChannelType; + MTL_MAILBOX *CONST MailBox; + MTL_DOORBELL DoorBell; +} MTL_CHANNEL; + +/** Wait until channel is free. + + @param[in] Channel Pointer to a channel. + @param[in] TimeOutInMicroSeconds Time out in micro seconds. + + @retval EFI_SUCCESS Channel is free. + @retval EFI_TIMEOUT Time out error. +**/ +EFI_STATUS +MtlWaitUntilChannelFree ( + IN MTL_CHANNEL *Channel, + IN UINTN TimeOutInMicroSeconds + ); + +/** Return the address of the message payload. + + @param[in] Channel Pointer to a channel. + + @retval UINT32* Pointer to the payload. +**/ +UINT32 * +MtlGetChannelPayload ( + IN MTL_CHANNEL *Channel + ); + +/** Return pointer to a channel for the requested channel type. + + @param[in] ChannelType ChannelType, Low or High priority channel. + MTL_CHANNEL_TYPE_LOW or + MTL_CHANNEL_TYPE_HIGH + + @param[out] Channel Holds pointer to the channel. + + @retval EFI_SUCCESS Pointer to channel is returned. + @retval EFI_UNSUPPORTED Requested channel type not supported. +**/ +EFI_STATUS +MtlGetChannel ( + IN MTL_CHANNEL_TYPE ChannelType, + OUT MTL_CHANNEL **Channel + ); + +/** Mark the channel busy and ring the doorbell. + + @param[in] Channel Pointer to a channel. + @param[in] MessageHeader Message header. + + @param[out] PayloadLength Message length. + + @retval EFI_SUCCESS Message sent successfully. + @retval EFI_DEVICE_ERROR Channel is busy. +**/ +EFI_STATUS +MtlSendMessage ( + IN MTL_CHANNEL *Channel, + IN UINT32 MessageHeader, + OUT UINT32 PayloadLength + ); + +/** Wait for a response on a channel. + + If channel is free after sending message, it implies SCP responded + with a response on the channel. + + @param[in] Channel Pointer to a channel. + + @retval EFI_SUCCESS Message received successfully. + @retval EFI_TIMEOUT Time out error. +**/ +EFI_STATUS +MtlReceiveMessage ( + IN MTL_CHANNEL *Channel, + OUT UINT32 *MessageHeader, + OUT UINT32 *PayloadLength + ); + +#endif /* ARM_MTL_LIB_H_ */ diff --git a/ArmPkg/Include/Library/ArmSmcLib.h b/ArmPkg/Include/Library/ArmSmcLib.h new file mode 100644 index 000000000..beef0175c --- /dev/null +++ b/ArmPkg/Include/Library/ArmSmcLib.h @@ -0,0 +1,113 @@ +/** @file +* +* Copyright (c) 2021, NUVIA Inc. All rights reserved.
+* Copyright (c) 2012-2014, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef ARM_SMC_LIB_H_ +#define ARM_SMC_LIB_H_ + +/** + * The size of the SMC arguments are different between AArch64 and AArch32. + * The native size is used for the arguments. + */ +typedef struct { + UINTN Arg0; + UINTN Arg1; + UINTN Arg2; + UINTN Arg3; + UINTN Arg4; + UINTN Arg5; + UINTN Arg6; + UINTN Arg7; +} ARM_SMC_ARGS; + +/** + Trigger an SMC call + + SMC calls can take up to 7 arguments and return up to 4 return values. + Therefore, the 4 first fields in the ARM_SMC_ARGS structure are used + for both input and output values. + +**/ +VOID +ArmCallSmc ( + IN OUT ARM_SMC_ARGS *Args + ); + +/** Trigger an SMC call with 3 arguments. + + @param Function The SMC function. + @param Arg1 Argument/result. + @param Arg2 Argument/result. + @param Arg3 Argument/result. + + @return The SMC error code. + +**/ +UINTN +ArmCallSmc3 ( + IN UINTN Function, + IN OUT UINTN *Arg1 OPTIONAL, + IN OUT UINTN *Arg2 OPTIONAL, + IN OUT UINTN *Arg3 OPTIONAL + ); + +/** Trigger an SMC call with 2 arguments. + + @param Function The SMC function. + @param Arg1 Argument/result. + @param Arg2 Argument/result. + @param Arg3 Result. + + @return The SMC error code. + +**/ +UINTN +ArmCallSmc2 ( + IN UINTN Function, + IN OUT UINTN *Arg1 OPTIONAL, + IN OUT UINTN *Arg2 OPTIONAL, + OUT UINTN *Arg3 OPTIONAL + ); + +/** Trigger an SMC call with 1 argument. + + @param Function The SMC function. + @param Arg1 Argument/result. + @param Arg2 Result. + @param Arg3 Result. + + @return The SMC error code. + +**/ +UINTN +ArmCallSmc1 ( + IN UINTN Function, + IN OUT UINTN *Arg1 OPTIONAL, + OUT UINTN *Arg2 OPTIONAL, + OUT UINTN *Arg3 OPTIONAL + ); + +/** Trigger an SMC call with 0 arguments. + + @param Function The SMC function. + @param Arg1 Result. + @param Arg2 Result. + @param Arg3 Result. + + @return The SMC error code. + +**/ +UINTN +ArmCallSmc0 ( + IN UINTN Function, + OUT UINTN *Arg1 OPTIONAL, + OUT UINTN *Arg2 OPTIONAL, + OUT UINTN *Arg3 OPTIONAL + ); + +#endif // ARM_SMC_LIB_H_ diff --git a/ArmPkg/Include/Library/ArmSvcLib.h b/ArmPkg/Include/Library/ArmSvcLib.h new file mode 100644 index 000000000..71a640b9c --- /dev/null +++ b/ArmPkg/Include/Library/ArmSvcLib.h @@ -0,0 +1,46 @@ +/** @file +* +* Copyright (c) 2016 - 2017, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef ARM_SVC_LIB_H_ +#define ARM_SVC_LIB_H_ + +/** + * The size of the SVC arguments are different between AArch64 and AArch32. + * The native size is used for the arguments. + */ +typedef struct { + UINTN Arg0; + UINTN Arg1; + UINTN Arg2; + UINTN Arg3; + UINTN Arg4; + UINTN Arg5; + UINTN Arg6; + UINTN Arg7; +} ARM_SVC_ARGS; + +/** + Trigger an SVC call + + SVC calls can take up to 8 arguments and return up to 8 return values. + Therefore, the 8 first fields in the ARM_SVC_ARGS structure are used + for both input and output values. + + @param[in, out] Args Arguments to be passed as part of the SVC call + The return values of the SVC call are also placed + in the same structure + + @retval None + +**/ +VOID +ArmCallSvc ( + IN OUT ARM_SVC_ARGS *Args + ); + +#endif // ARM_SVC_LIB_H_ diff --git a/ArmPkg/Include/Library/DefaultExceptionHandlerLib.h b/ArmPkg/Include/Library/DefaultExceptionHandlerLib.h new file mode 100644 index 000000000..63d5dc78d --- /dev/null +++ b/ArmPkg/Include/Library/DefaultExceptionHandlerLib.h @@ -0,0 +1,25 @@ +/** @file + + Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef DEFAULT_EXCEPTION_HANDLER_LIB_H_ +#define DEFAULT_EXCEPTION_HANDLER_LIB_H_ + +/** + This is the default action to take on an unexpected exception + + @param ExceptionType Type of the exception + @param SystemContext Register state at the time of the Exception + +**/ +VOID +DefaultExceptionHandler ( + IN EFI_EXCEPTION_TYPE ExceptionType, + IN OUT EFI_SYSTEM_CONTEXT SystemContext + ); + +#endif // DEFAULT_EXCEPTION_HANDLER_LIB_H_ diff --git a/ArmPkg/Include/Library/OemMiscLib.h b/ArmPkg/Include/Library/OemMiscLib.h new file mode 100644 index 000000000..541274999 --- /dev/null +++ b/ArmPkg/Include/Library/OemMiscLib.h @@ -0,0 +1,271 @@ +/** @file +* +* Copyright (c) 2022, Ampere Computing LLC. All rights reserved. +* Copyright (c) 2021, NUVIA Inc. All rights reserved. +* Copyright (c) 2015, Hisilicon Limited. All rights reserved. +* Copyright (c) 2015, Linaro Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef OEM_MISC_LIB_H_ +#define OEM_MISC_LIB_H_ + +#include +#include + +typedef enum { + CpuCacheL1 = 1, + CpuCacheL2, + CpuCacheL3, + CpuCacheL4, + CpuCacheL5, + CpuCacheL6, + CpuCacheL7, + CpuCacheLevelMax +} OEM_MISC_CPU_CACHE_LEVEL; + +typedef struct { + UINT8 Voltage; ///< Processor voltage + UINT16 CurrentSpeed; ///< Current clock speed in MHz + UINT16 MaxSpeed; ///< Maximum clock speed in MHz + UINT16 ExternalClock; ///< External clock speed in MHz + UINT16 CoreCount; ///< Number of cores available + UINT16 CoresEnabled; ///< Number of cores enabled + UINT16 ThreadCount; ///< Number of threads per processor +} OEM_MISC_PROCESSOR_DATA; + +typedef enum { + BiosVersionType00, + ProductNameType01, + SerialNumType01, + UuidType01, + SystemManufacturerType01, + VersionType01, + SkuNumberType01, + FamilyType01, + AssetTagType02, + SerialNumberType02, + BoardManufacturerType02, + ProductNameType02, + VersionType02, + SkuNumberType02, + ChassisLocationType02, + AssetTagType03, + SerialNumberType03, + VersionType03, + ChassisTypeType03, + ManufacturerType03, + SkuNumberType03, + ProcessorPartNumType04, + ProcessorSerialNumType04, + ProcessorVersionType04, + SmbiosHiiStringFieldMax +} OEM_MISC_SMBIOS_HII_STRING_FIELD; + +/* + * The following are functions that the each platform needs to + * implement in its OemMiscLib library. + */ + +/** Gets the CPU frequency of the specified processor. + + @param ProcessorIndex Index of the processor to get the frequency for. + + @return CPU frequency in Hz +**/ +UINTN +EFIAPI +OemGetCpuFreq ( + IN UINT8 ProcessorIndex + ); + +/** Gets information about the specified processor and stores it in + the structures provided. + + @param ProcessorIndex Index of the processor to get the information for. + @param ProcessorStatus Processor status. + @param ProcessorCharacteristics Processor characteritics. + @param MiscProcessorData Miscellaneous processor information. + + @return TRUE on success, FALSE on failure. +**/ +BOOLEAN +EFIAPI +OemGetProcessorInformation ( + IN UINTN ProcessorIndex, + IN OUT PROCESSOR_STATUS_DATA *ProcessorStatus, + IN OUT PROCESSOR_CHARACTERISTIC_FLAGS *ProcessorCharacteristics, + IN OUT OEM_MISC_PROCESSOR_DATA *MiscProcessorData + ); + +/** Gets information about the cache at the specified cache level. + + @param ProcessorIndex The processor to get information for. + @param CacheLevel The cache level to get information for. + @param DataCache Whether the cache is a data cache. + @param UnifiedCache Whether the cache is a unified cache. + @param SmbiosCacheTable The SMBIOS Type7 cache information structure. + + @return TRUE on success, FALSE on failure. +**/ +BOOLEAN +EFIAPI +OemGetCacheInformation ( + IN UINT8 ProcessorIndex, + IN UINT8 CacheLevel, + IN BOOLEAN DataCache, + IN BOOLEAN UnifiedCache, + IN OUT SMBIOS_TABLE_TYPE7 *SmbiosCacheTable + ); + +/** Gets the maximum number of processors supported by the platform. + + @return The maximum number of processors. +**/ +UINT8 +EFIAPI +OemGetMaxProcessors ( + VOID + ); + +/** Gets the type of chassis for the system. + + @retval The type of the chassis. +**/ +MISC_CHASSIS_TYPE +EFIAPI +OemGetChassisType ( + VOID + ); + +/** Returns whether the specified processor is present or not. + + @param ProcessIndex The processor index to check. + + @return TRUE is the processor is present, FALSE otherwise. +**/ +BOOLEAN +EFIAPI +OemIsProcessorPresent ( + IN UINTN ProcessorIndex + ); + +/** Updates the HII string for the specified field. + + @param HiiHandle The HII handle. + @param TokenToUpdate The string to update. + @param Field The field to get information about. +**/ +VOID +EFIAPI +OemUpdateSmbiosInfo ( + IN EFI_HII_HANDLE HiiHandle, + IN EFI_STRING_ID TokenToUpdate, + IN OEM_MISC_SMBIOS_HII_STRING_FIELD Field + ); + +/** Fetches the Type 32 boot information status. + + @return Boot status. +**/ +MISC_BOOT_INFORMATION_STATUS_DATA_TYPE +EFIAPI +OemGetBootStatus ( + VOID + ); + +/** Fetches the chassis status when it was last booted. + + @return Chassis status. +**/ +MISC_CHASSIS_STATE +EFIAPI +OemGetChassisBootupState ( + VOID + ); + +/** Fetches the chassis power supply/supplies status when last booted. + + @return Chassis power supply/supplies status. +**/ +MISC_CHASSIS_STATE +EFIAPI +OemGetChassisPowerSupplyState ( + VOID + ); + +/** Fetches the chassis thermal status when last booted. + + @return Chassis thermal status. +**/ +MISC_CHASSIS_STATE +EFIAPI +OemGetChassisThermalState ( + VOID + ); + +/** Fetches the chassis security status when last booted. + + @return Chassis security status. +**/ +MISC_CHASSIS_SECURITY_STATE +EFIAPI +OemGetChassisSecurityStatus ( + VOID + ); + +/** Fetches the chassis height in RMUs (Rack Mount Units). + + @return The height of the chassis. +**/ +UINT8 +EFIAPI +OemGetChassisHeight ( + VOID + ); + +/** Fetches the number of power cords. + + @return The number of power cords. +**/ +UINT8 +EFIAPI +OemGetChassisNumPowerCords ( + VOID + ); + +/** + Fetches the system UUID. + + @param[out] SystemUuid The pointer to the buffer to store the System UUID. + +**/ +VOID +EFIAPI +OemGetSystemUuid ( + OUT GUID *SystemUuid + ); + +/** Fetches the BIOS release. + + @return The BIOS release. +**/ +UINT16 +EFIAPI +OemGetBiosRelease ( + VOID + ); + +/** Fetches the embedded controller firmware release. + + @return The embedded controller firmware release. +**/ +UINT16 +EFIAPI +OemGetEmbeddedControllerFirmwareRelease ( + VOID + ); + +#endif // OEM_MISC_LIB_H_ diff --git a/ArmPkg/Include/Library/OpteeLib.h b/ArmPkg/Include/Library/OpteeLib.h new file mode 100644 index 000000000..593c32b13 --- /dev/null +++ b/ArmPkg/Include/Library/OpteeLib.h @@ -0,0 +1,120 @@ +/** @file + OP-TEE specific header file. + + Copyright (c) 2018, Linaro Ltd. All rights reserved.
+ Copyright (c) 2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef OPTEE_LIB_H_ +#define OPTEE_LIB_H_ + +/* + * The 'Trusted OS Call UID' is supposed to return the following UUID for + * OP-TEE OS. This is a 128-bit value. + */ +#define OPTEE_OS_UID0 0x384fb3e0 +#define OPTEE_OS_UID1 0xe7f811e3 +#define OPTEE_OS_UID2 0xaf630002 +#define OPTEE_OS_UID3 0xa5d5c51b + +#define OPTEE_MESSAGE_ATTRIBUTE_TYPE_NONE 0x0 +#define OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INPUT 0x1 +#define OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_OUTPUT 0x2 +#define OPTEE_MESSAGE_ATTRIBUTE_TYPE_VALUE_INOUT 0x3 +#define OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_INPUT 0x9 +#define OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_OUTPUT 0xa +#define OPTEE_MESSAGE_ATTRIBUTE_TYPE_MEMORY_INOUT 0xb + +#define OPTEE_MESSAGE_ATTRIBUTE_TYPE_MASK 0xff + +#define OPTEE_SUCCESS 0x00000000 +#define OPTEE_ORIGIN_COMMUNICATION 0x00000002 +#define OPTEE_ERROR_COMMUNICATION 0xFFFF000E + +typedef struct { + UINT64 BufferAddress; + UINT64 Size; + UINT64 SharedMemoryReference; +} OPTEE_MESSAGE_PARAM_MEMORY; + +typedef struct { + UINT64 A; + UINT64 B; + UINT64 C; +} OPTEE_MESSAGE_PARAM_VALUE; + +typedef union { + OPTEE_MESSAGE_PARAM_MEMORY Memory; + OPTEE_MESSAGE_PARAM_VALUE Value; +} OPTEE_MESSAGE_PARAM_UNION; + +typedef struct { + UINT64 Attribute; + OPTEE_MESSAGE_PARAM_UNION Union; +} OPTEE_MESSAGE_PARAM; + +#define OPTEE_MAX_CALL_PARAMS 4 + +typedef struct { + UINT32 Command; + UINT32 Function; + UINT32 Session; + UINT32 CancelId; + UINT32 Pad; + UINT32 Return; + UINT32 ReturnOrigin; + UINT32 NumParams; + + // NumParams tells the actual number of element in Params + OPTEE_MESSAGE_PARAM Params[OPTEE_MAX_CALL_PARAMS]; +} OPTEE_MESSAGE_ARG; + +typedef struct { + EFI_GUID Uuid; // [in] GUID/UUID of the Trusted Application + UINT32 Session; // [out] Session id + UINT32 Return; // [out] Return value + UINT32 ReturnOrigin; // [out] Origin of the return value +} OPTEE_OPEN_SESSION_ARG; + +typedef struct { + UINT32 Function; // [in] Trusted Application function, specific to the TA + UINT32 Session; // [in] Session id + UINT32 Return; // [out] Return value + UINT32 ReturnOrigin; // [out] Origin of the return value + OPTEE_MESSAGE_PARAM Params[OPTEE_MAX_CALL_PARAMS]; // Params for function to be invoked +} OPTEE_INVOKE_FUNCTION_ARG; + +BOOLEAN +EFIAPI +IsOpteePresent ( + VOID + ); + +EFI_STATUS +EFIAPI +OpteeInit ( + VOID + ); + +EFI_STATUS +EFIAPI +OpteeOpenSession ( + IN OUT OPTEE_OPEN_SESSION_ARG *OpenSessionArg + ); + +EFI_STATUS +EFIAPI +OpteeCloseSession ( + IN UINT32 Session + ); + +EFI_STATUS +EFIAPI +OpteeInvokeFunction ( + IN OUT OPTEE_INVOKE_FUNCTION_ARG *InvokeFunctionArg + ); + +#endif // OPTEE_LIB_H_ diff --git a/ArmPkg/Include/Library/SemihostLib.h b/ArmPkg/Include/Library/SemihostLib.h new file mode 100644 index 000000000..e0ff9251c --- /dev/null +++ b/ArmPkg/Include/Library/SemihostLib.h @@ -0,0 +1,132 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Portions copyright (c) 2011, 2012, ARM Ltd. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef SEMIHOSTING_LIB_H_ +#define SEMIHOSTING_LIB_H_ + +/* + * + * Please refer to ARM RVDS 3.0 Compiler and Libraries Guide for more information + * about the semihosting interface. + * + */ + +#define SEMIHOST_FILE_MODE_READ (0 << 2) +#define SEMIHOST_FILE_MODE_WRITE (1 << 2) +#define SEMIHOST_FILE_MODE_APPEND (2 << 2) +#define SEMIHOST_FILE_MODE_UPDATE (1 << 1) +#define SEMIHOST_FILE_MODE_BINARY (1 << 0) +#define SEMIHOST_FILE_MODE_ASCII (0 << 0) + +BOOLEAN +SemihostConnectionSupported ( + VOID + ); + +RETURN_STATUS +SemihostFileOpen ( + IN CHAR8 *FileName, + IN UINT32 Mode, + OUT UINTN *FileHandle + ); + +RETURN_STATUS +SemihostFileSeek ( + IN UINTN FileHandle, + IN UINTN Offset + ); + +RETURN_STATUS +SemihostFileRead ( + IN UINTN FileHandle, + IN OUT UINTN *Length, + OUT VOID *Buffer + ); + +RETURN_STATUS +SemihostFileWrite ( + IN UINTN FileHandle, + IN OUT UINTN *Length, + IN VOID *Buffer + ); + +RETURN_STATUS +SemihostFileClose ( + IN UINTN FileHandle + ); + +RETURN_STATUS +SemihostFileLength ( + IN UINTN FileHandle, + OUT UINTN *Length + ); + +/** + Get a temporary name for a file from the host running the debug agent. + + @param[out] Buffer Pointer to the buffer where the temporary name has to + be stored + @param[in] Identifier File name identifier (integer in the range 0 to 255) + @param[in] Length Length of the buffer to store the temporary name + + @retval RETURN_SUCCESS Temporary name returned + @retval RETURN_INVALID_PARAMETER Invalid buffer address + @retval RETURN_ABORTED Temporary name not returned + +**/ +RETURN_STATUS +SemihostFileTmpName ( + OUT VOID *Buffer, + IN UINT8 Identifier, + IN UINTN Length + ); + +RETURN_STATUS +SemihostFileRemove ( + IN CHAR8 *FileName + ); + +/** + Rename a specified file. + + @param[in] FileName Name of the file to rename. + @param[in] NewFileName The new name of the file. + + @retval RETURN_SUCCESS File Renamed + @retval RETURN_INVALID_PARAMETER Either the current or the new name is not specified + @retval RETURN_ABORTED Rename failed + +**/ +RETURN_STATUS +SemihostFileRename ( + IN CHAR8 *FileName, + IN CHAR8 *NewFileName + ); + +CHAR8 +SemihostReadCharacter ( + VOID + ); + +VOID +SemihostWriteCharacter ( + IN CHAR8 Character + ); + +VOID +SemihostWriteString ( + IN CHAR8 *String + ); + +UINT32 +SemihostSystem ( + IN CHAR8 *CommandLine + ); + +#endif // SEMIHOSTING_LIB_H_ diff --git a/ArmPkg/Include/Library/StandaloneMmMmuLib.h b/ArmPkg/Include/Library/StandaloneMmMmuLib.h new file mode 100644 index 000000000..c27020def --- /dev/null +++ b/ArmPkg/Include/Library/StandaloneMmMmuLib.h @@ -0,0 +1,36 @@ +/** @file + + Copyright (c) 2018, ARM Ltd. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#ifndef STANDALONE_MM_MMU_LIB_ +#define STANDALONE_MM_MMU_LIB_ + +EFI_STATUS +ArmSetMemoryRegionNoExec ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +EFI_STATUS +ArmClearMemoryRegionNoExec ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +EFI_STATUS +ArmSetMemoryRegionReadOnly ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +EFI_STATUS +ArmClearMemoryRegionReadOnly ( + IN EFI_PHYSICAL_ADDRESS BaseAddress, + IN UINT64 Length + ); + +#endif /* STANDALONE_MM_MMU_LIB_ */ diff --git a/ArmPkg/Include/Ppi/ArmMpCoreInfo.h b/ArmPkg/Include/Ppi/ArmMpCoreInfo.h new file mode 100644 index 000000000..c9f2ac6fc --- /dev/null +++ b/ArmPkg/Include/Ppi/ArmMpCoreInfo.h @@ -0,0 +1,52 @@ +/** @file +* +* Copyright (c) 2011, ARM Limited. All rights reserved. +* +* SPDX-License-Identifier: BSD-2-Clause-Patent +* +**/ + +#ifndef ARM_MP_CORE_INFO_PPI_H_ +#define ARM_MP_CORE_INFO_PPI_H_ + +#include + +#define ARM_MP_CORE_INFO_PPI_GUID \ + { 0x6847cc74, 0xe9ec, 0x4f8f, {0xa2, 0x9d, 0xab, 0x44, 0xe7, 0x54, 0xa8, 0xfc} } + +/** + This service of the EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI that migrates temporary RAM into + permanent memory. + + @param PeiServices Pointer to the PEI Services Table. + @param TemporaryMemoryBase Source Address in temporary memory from which the SEC or PEIM will copy the + Temporary RAM contents. + @param PermanentMemoryBase Destination Address in permanent memory into which the SEC or PEIM will copy the + Temporary RAM contents. + @param CopySize Amount of memory to migrate from temporary to permanent memory. + + @retval EFI_SUCCESS The data was successfully returned. + @retval EFI_INVALID_PARAMETER PermanentMemoryBase + CopySize > TemporaryMemoryBase when + TemporaryMemoryBase > PermanentMemoryBase. + +**/ +typedef +EFI_STATUS +(EFIAPI *ARM_MP_CORE_INFO_GET)( + OUT UINTN *ArmCoreCount, + OUT ARM_CORE_INFO **ArmCoreTable + ); + +/// +/// This service abstracts the ability to migrate contents of the platform early memory store. +/// Note: The name EFI_PEI_TEMPORARY_RAM_SUPPORT_PPI is different from the current PI 1.2 spec. +/// This PPI was optional. +/// +typedef struct { + ARM_MP_CORE_INFO_GET GetMpCoreInfo; +} ARM_MP_CORE_INFO_PPI; + +extern EFI_GUID gArmMpCoreInfoPpiGuid; +extern EFI_GUID gArmMpCoreInfoGuid; + +#endif // ARM_MP_CORE_INFO_PPI_H_ diff --git a/ArmPkg/Include/Protocol/ArmScmi.h b/ArmPkg/Include/Protocol/ArmScmi.h new file mode 100644 index 000000000..93edec769 --- /dev/null +++ b/ArmPkg/Include/Protocol/ArmScmi.h @@ -0,0 +1,20 @@ +/** @file + + Copyright (c) 2017-2018, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#ifndef ARM_SCMI_H_ +#define ARM_SCMI_H_ + +/* As per SCMI specification, maximum allowed ASCII string length + for various return values/parameters of a SCMI message. +*/ +#define SCMI_MAX_STR_LEN 16 + +#endif /* ARM_SCMI_H_ */ diff --git a/ArmPkg/Include/Protocol/ArmScmiBaseProtocol.h b/ArmPkg/Include/Protocol/ArmScmiBaseProtocol.h new file mode 100644 index 000000000..f6d97dd6d --- /dev/null +++ b/ArmPkg/Include/Protocol/ArmScmiBaseProtocol.h @@ -0,0 +1,168 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + + @par Specification Reference: + - Arm System Control and Management Interface - Platform Design Document + (https://developer.arm.com/documentation/den0056/) +**/ + +#ifndef ARM_SCMI_BASE_PROTOCOL_H_ +#define ARM_SCMI_BASE_PROTOCOL_H_ + +#include + +#define BASE_PROTOCOL_VERSION_V1 0x10000 +#define BASE_PROTOCOL_VERSION_V2 0x20000 + +#define NUM_PROTOCOL_MASK 0xFFU +#define NUM_AGENT_MASK 0xFFU + +#define NUM_AGENT_SHIFT 0x8 + +/** Returns total number of protocols that are + implemented (excluding the Base protocol) +*/ +#define SCMI_TOTAL_PROTOCOLS(Attr) (Attr & NUM_PROTOCOL_MASK) + +// Returns total number of agents in the system. +#define SCMI_TOTAL_AGENTS(Attr) ((Attr >> NUM_AGENT_SHIFT) & NUM_AGENT_MASK) + +#define ARM_SCMI_BASE_PROTOCOL_GUID { \ + 0xd7e5abe9, 0x33ab, 0x418e, {0x9f, 0x91, 0x72, 0xda, 0xe2, 0xba, 0x8e, 0x2f} \ + } + +extern EFI_GUID gArmScmiBaseProtocolGuid; + +typedef struct _SCMI_BASE_PROTOCOL SCMI_BASE_PROTOCOL; + +/** Return version of the Base protocol supported by SCP firmware. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] Version Version of the supported SCMI Base protocol. + + @retval EFI_SUCCESS The version of the protocol is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_BASE_GET_VERSION)( + IN SCMI_BASE_PROTOCOL *This, + OUT UINT32 *Version + ); + +/** Return total number of SCMI protocols supported by the SCP firmware. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] TotalProtocols Total number of SCMI protocols supported. + + @retval EFI_SUCCESS Total number of protocols supported are returned. + @retval EFI_DEVICE_ERROR SCP returns a SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_BASE_GET_TOTAL_PROTOCOLS)( + IN SCMI_BASE_PROTOCOL *This, + OUT UINT32 *TotalProtocols + ); + +/** Return vendor name. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] VendorIdentifier Null terminated ASCII string of up to + 16 bytes with a vendor name. + + @retval EFI_SUCCESS VendorIdentifier is returned. + @retval EFI_DEVICE_ERROR SCP returns a SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_BASE_DISCOVER_VENDOR)( + IN SCMI_BASE_PROTOCOL *This, + OUT UINT8 VendorIdentifier[SCMI_MAX_STR_LEN] + ); + +/** Return sub vendor name. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] VendorIdentifier Null terminated ASCII string of up to + 16 bytes with a vendor name. + + @retval EFI_SUCCESS VendorIdentifier is returned. + @retval EFI_DEVICE_ERROR SCP returns a SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_BASE_DISCOVER_SUB_VENDOR)( + IN SCMI_BASE_PROTOCOL *This, + OUT UINT8 VendorIdentifier[SCMI_MAX_STR_LEN] + ); + +/** Return implementation version. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] ImplementationVersion Vendor specific implementation version. + + @retval EFI_SUCCESS Implementation version is returned. + @retval EFI_DEVICE_ERROR SCP returns a SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION)( + IN SCMI_BASE_PROTOCOL *This, + OUT UINT32 *ImplementationVersion + ); + +/** Return list of protocols. + + @param[in] This A Pointer to SCMI_BASE_PROTOCOL Instance. + + @param[out] ProtocolListSize Size of the ProtocolList. + + @param[out] ProtocolList Protocol list. + + @retval EFI_SUCCESS List of protocols is returned. + @retval EFI_BUFFER_TOO_SMALL ProtocolListSize is too small for the result. + It has been updated to the size needed. + @retval EFI_DEVICE_ERROR SCP returns a SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_BASE_DISCOVER_LIST_PROTOCOLS)( + IN SCMI_BASE_PROTOCOL *This, + IN OUT UINT32 *ProtocolListSize, + OUT UINT8 *ProtocolList + ); + +// Base protocol. +typedef struct _SCMI_BASE_PROTOCOL { + SCMI_BASE_GET_VERSION GetVersion; + SCMI_BASE_GET_TOTAL_PROTOCOLS GetTotalProtocols; + SCMI_BASE_DISCOVER_VENDOR DiscoverVendor; + SCMI_BASE_DISCOVER_SUB_VENDOR DiscoverSubVendor; + SCMI_BASE_DISCOVER_IMPLEMENTATION_VERSION DiscoverImplementationVersion; + SCMI_BASE_DISCOVER_LIST_PROTOCOLS DiscoverListProtocols; +} SCMI_BASE_PROTOCOL; + +// SCMI Message IDs for Base protocol. +typedef enum { + ScmiMessageIdBaseDiscoverVendor = 0x3, + ScmiMessageIdBaseDiscoverSubVendor = 0x4, + ScmiMessageIdBaseDiscoverImplementationVersion = 0x5, + ScmiMessageIdBaseDiscoverListProtocols = 0x6 +} SCMI_MESSAGE_ID_BASE; + +#endif /* ARM_SCMI_BASE_PROTOCOL_H_ */ diff --git a/ArmPkg/Include/Protocol/ArmScmiClock2Protocol.h b/ArmPkg/Include/Protocol/ArmScmiClock2Protocol.h new file mode 100644 index 000000000..d37d23f40 --- /dev/null +++ b/ArmPkg/Include/Protocol/ArmScmiClock2Protocol.h @@ -0,0 +1,191 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#ifndef ARM_SCMI_CLOCK2_PROTOCOL_H_ +#define ARM_SCMI_CLOCK2_PROTOCOL_H_ + +#include +#include + +#define ARM_SCMI_CLOCK2_PROTOCOL_GUID {\ + 0xb8d8caf2, 0x9e94, 0x462c, { 0xa8, 0x34, 0x6c, 0x99, 0xfc, 0x05, 0xef, 0xcf } \ + } + +extern EFI_GUID gArmScmiClock2ProtocolGuid; + +#define SCMI_CLOCK2_PROTOCOL_VERSION 1 + +typedef struct _SCMI_CLOCK2_PROTOCOL SCMI_CLOCK2_PROTOCOL; + +// Protocol Interface functions. + +/** Return version of the clock management protocol supported by SCP firmware. + + @param[in] This A Pointer to SCMI_CLOCK2_PROTOCOL Instance. + + @param[out] Version Version of the supported SCMI Clock management protocol. + + @retval EFI_SUCCESS The version is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK2_GET_VERSION)( + IN SCMI_CLOCK2_PROTOCOL *This, + OUT UINT32 *Version + ); + +/** Return total number of clock devices supported by the clock management + protocol. + + @param[in] This A Pointer to SCMI_CLOCK2_PROTOCOL Instance. + + @param[out] TotalClocks Total number of clocks supported. + + @retval EFI_SUCCESS Total number of clocks supported is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK2_GET_TOTAL_CLOCKS)( + IN SCMI_CLOCK2_PROTOCOL *This, + OUT UINT32 *TotalClocks + ); + +/** Return attributes of a clock device. + + @param[in] This A Pointer to SCMI_CLOCK2_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + + @param[out] Enabled If TRUE, the clock device is enabled. + @param[out] ClockAsciiName A NULL terminated ASCII string with the clock + name, of up to 16 bytes. + + @retval EFI_SUCCESS Clock device attributes are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK2_GET_CLOCK_ATTRIBUTES)( + IN SCMI_CLOCK2_PROTOCOL *This, + IN UINT32 ClockId, + OUT BOOLEAN *Enabled, + OUT CHAR8 *ClockAsciiName + ); + +/** Return list of rates supported by a given clock device. + + @param[in] This A pointer to SCMI_CLOCK2_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + + @param[out] Format ScmiClockRateFormatDiscrete: Clock device + supports range of clock rates which are non-linear. + + ScmiClockRateFormatLinear: Clock device supports + range of linear clock rates from Min to Max in steps. + + @param[out] TotalRates Total number of rates. + + @param[in,out] RateArraySize Size of the RateArray. + + @param[out] RateArray List of clock rates. + + @retval EFI_SUCCESS List of clock rates are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval EFI_BUFFER_TOO_SMALL RateArraySize is too small for the result. + It has been updated to the size needed. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK2_DESCRIBE_RATES)( + IN SCMI_CLOCK2_PROTOCOL *This, + IN UINT32 ClockId, + OUT SCMI_CLOCK_RATE_FORMAT *Format, + OUT UINT32 *TotalRates, + IN OUT UINT32 *RateArraySize, + OUT SCMI_CLOCK_RATE *RateArray + ); + +/** Get clock rate. + + @param[in] This A Pointer to SCMI_CLOCK2_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + + @param[out] Rate Clock rate. + + @retval EFI_SUCCESS Clock rate is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK2_RATE_GET)( + IN SCMI_CLOCK2_PROTOCOL *This, + IN UINT32 ClockId, + OUT UINT64 *Rate + ); + +/** Set clock rate. + + @param[in] This A Pointer to SCMI_CLOCK2_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + @param[in] Rate Clock rate. + + @retval EFI_SUCCESS Clock rate set success. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK2_RATE_SET)( + IN SCMI_CLOCK2_PROTOCOL *This, + IN UINT32 ClockId, + IN UINT64 Rate + ); + +/** Enable/Disable specified clock. + Function is only available under gArmScmiClock2ProtocolGuid + + @param[in] This A Pointer to SCMI_CLOCK2_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + @param[in] Enable TRUE to enable, FALSE to disable. + + @retval EFI_SUCCESS Clock enable/disable successful. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK2_ENABLE)( + IN SCMI_CLOCK2_PROTOCOL *This, + IN UINT32 ClockId, + IN BOOLEAN Enable + ); + +typedef struct _SCMI_CLOCK2_PROTOCOL { + SCMI_CLOCK2_GET_VERSION GetVersion; + SCMI_CLOCK2_GET_TOTAL_CLOCKS GetTotalClocks; + SCMI_CLOCK2_GET_CLOCK_ATTRIBUTES GetClockAttributes; + SCMI_CLOCK2_DESCRIBE_RATES DescribeRates; + SCMI_CLOCK2_RATE_GET RateGet; + SCMI_CLOCK2_RATE_SET RateSet; + + // Extension to original ClockProtocol, added here so SCMI_CLOCK2_PROTOCOL + // can be cast to SCMI_CLOCK_PROTOCOL + UINTN Version; // For future expandability + SCMI_CLOCK2_ENABLE Enable; +} SCMI_CLOCK2_PROTOCOL; + +#endif /* ARM_SCMI_CLOCK2_PROTOCOL_H_ */ diff --git a/ArmPkg/Include/Protocol/ArmScmiClockProtocol.h b/ArmPkg/Include/Protocol/ArmScmiClockProtocol.h new file mode 100644 index 000000000..7cdc61ff7 --- /dev/null +++ b/ArmPkg/Include/Protocol/ArmScmiClockProtocol.h @@ -0,0 +1,217 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#ifndef ARM_SCMI_CLOCK_PROTOCOL_H_ +#define ARM_SCMI_CLOCK_PROTOCOL_H_ + +#include + +#define ARM_SCMI_CLOCK_PROTOCOL_GUID {\ + 0x91ce67a8, 0xe0aa, 0x4012, {0xb9, 0x9f, 0xb6, 0xfc, 0xf3, 0x4, 0x8e, 0xaa} \ + } + +extern EFI_GUID gArmScmiClockProtocolGuid; + +// Message Type for clock management protocol. +typedef enum { + ScmiMessageIdClockAttributes = 0x3, + ScmiMessageIdClockDescribeRates = 0x4, + ScmiMessageIdClockRateSet = 0x5, + ScmiMessageIdClockRateGet = 0x6, + ScmiMessageIdClockConfigSet = 0x7 +} SCMI_MESSAGE_ID_CLOCK; + +typedef enum { + ScmiClockRateFormatDiscrete, // Non-linear range. + ScmiClockRateFormatLinear // Linear range. +} SCMI_CLOCK_RATE_FORMAT; + +// Clock management protocol version. +#define SCMI_CLOCK_PROTOCOL_VERSION 0x10000 + +#define SCMI_CLOCK_PROTOCOL_PENDING_ASYNC_RATES_MASK 0xFFU +#define SCMI_CLOCK_PROTOCOL_PENDING_ASYNC_RATES_SHIFT 16 +#define SCMI_CLOCK_PROTOCOL_NUM_CLOCKS_MASK 0xFFFFU + +/** Total number of pending asynchronous clock rates changes + supported by the SCP, Attr Bits[23:16] +*/ +#define SCMI_CLOCK_PROTOCOL_MAX_ASYNC_CLK_RATES(Attr) ( \ + (Attr >> SCMI_CLOCK_PROTOCOL_PENDING_ASYNC_RATES_SHIFT) && \ + SCMI_CLOCK_PROTOCOL_PENDING_ASYNC_RATES_MASK) + +// Total of clock devices supported by the SCP, Attr Bits[15:0] +#define SCMI_CLOCK_PROTOCOL_TOTAL_CLKS(Attr) (Attr & SCMI_CLOCK_PROTOCOL_NUM_CLOCKS_MASK) + +#pragma pack(1) + +/* Depending on the format (linear/non-linear) supported by a clock device + either Rate or Min/Max/Step triplet is valid. +*/ +typedef struct { + UINT64 Min; + UINT64 Max; + UINT64 Step; +} SCMI_CLOCK_RATE_CONTINUOUS; + +typedef struct { + UINT64 Rate; +} SCMI_CLOCK_RATE_DISCRETE; + +typedef union { + SCMI_CLOCK_RATE_CONTINUOUS ContinuousRate; + SCMI_CLOCK_RATE_DISCRETE DiscreteRate; +} SCMI_CLOCK_RATE; + +#pragma pack() + +typedef struct _SCMI_CLOCK_PROTOCOL SCMI_CLOCK_PROTOCOL; + +// Protocol Interface functions. + +/** Return version of the clock management protocol supported by SCP firmware. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + + @param[out] Version Version of the supported SCMI Clock management protocol. + + @retval EFI_SUCCESS The version is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK_GET_VERSION)( + IN SCMI_CLOCK_PROTOCOL *This, + OUT UINT32 *Version + ); + +/** Return total number of clock devices supported by the clock management + protocol. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + + @param[out] TotalClocks Total number of clocks supported. + + @retval EFI_SUCCESS Total number of clocks supported is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK_GET_TOTAL_CLOCKS)( + IN SCMI_CLOCK_PROTOCOL *This, + OUT UINT32 *TotalClocks + ); + +/** Return attributes of a clock device. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + + @param[out] Enabled If TRUE, the clock device is enabled. + @param[out] ClockAsciiName A NULL terminated ASCII string with the clock + name, of up to 16 bytes. + + @retval EFI_SUCCESS Clock device attributes are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK_GET_CLOCK_ATTRIBUTES)( + IN SCMI_CLOCK_PROTOCOL *This, + IN UINT32 ClockId, + OUT BOOLEAN *Enabled, + OUT CHAR8 *ClockAsciiName + ); + +/** Return list of rates supported by a given clock device. + + @param[in] This A pointer to SCMI_CLOCK_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + + @param[out] Format ScmiClockRateFormatDiscrete: Clock device + supports range of clock rates which are non-linear. + + ScmiClockRateFormatLinear: Clock device supports + range of linear clock rates from Min to Max in steps. + + @param[out] TotalRates Total number of rates. + + @param[in,out] RateArraySize Size of the RateArray. + + @param[out] RateArray List of clock rates. + + @retval EFI_SUCCESS List of clock rates are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval EFI_BUFFER_TOO_SMALL RateArraySize is too small for the result. + It has been updated to the size needed. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK_DESCRIBE_RATES)( + IN SCMI_CLOCK_PROTOCOL *This, + IN UINT32 ClockId, + OUT SCMI_CLOCK_RATE_FORMAT *Format, + OUT UINT32 *TotalRates, + IN OUT UINT32 *RateArraySize, + OUT SCMI_CLOCK_RATE *RateArray + ); + +/** Get clock rate. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + + @param[out] Rate Clock rate. + + @retval EFI_SUCCESS Clock rate is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK_RATE_GET)( + IN SCMI_CLOCK_PROTOCOL *This, + IN UINT32 ClockId, + OUT UINT64 *Rate + ); + +/** Set clock rate. + + @param[in] This A Pointer to SCMI_CLOCK_PROTOCOL Instance. + @param[in] ClockId Identifier for the clock device. + @param[in] Rate Clock rate. + + @retval EFI_SUCCESS Clock rate set success. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_CLOCK_RATE_SET)( + IN SCMI_CLOCK_PROTOCOL *This, + IN UINT32 ClockId, + IN UINT64 Rate + ); + +typedef struct _SCMI_CLOCK_PROTOCOL { + SCMI_CLOCK_GET_VERSION GetVersion; + SCMI_CLOCK_GET_TOTAL_CLOCKS GetTotalClocks; + SCMI_CLOCK_GET_CLOCK_ATTRIBUTES GetClockAttributes; + SCMI_CLOCK_DESCRIBE_RATES DescribeRates; + SCMI_CLOCK_RATE_GET RateGet; + SCMI_CLOCK_RATE_SET RateSet; +} SCMI_CLOCK_PROTOCOL; + +#endif /* ARM_SCMI_CLOCK_PROTOCOL_H_ */ diff --git a/ArmPkg/Include/Protocol/ArmScmiPerformanceProtocol.h b/ArmPkg/Include/Protocol/ArmScmiPerformanceProtocol.h new file mode 100644 index 000000000..7e548e476 --- /dev/null +++ b/ArmPkg/Include/Protocol/ArmScmiPerformanceProtocol.h @@ -0,0 +1,258 @@ +/** @file + + Copyright (c) 2017-2021, Arm Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + + System Control and Management Interface V1.0 + http://infocenter.arm.com/help/topic/com.arm.doc.den0056a/ + DEN0056A_System_Control_and_Management_Interface.pdf +**/ + +#ifndef ARM_SCMI_PERFORMANCE_PROTOCOL_H_ +#define ARM_SCMI_PERFORMANCE_PROTOCOL_H_ + +#include + +#define PERFORMANCE_PROTOCOL_VERSION 0x10000 + +#define ARM_SCMI_PERFORMANCE_PROTOCOL_GUID { \ + 0x9b8ba84, 0x3dd3, 0x49a6, {0xa0, 0x5a, 0x31, 0x34, 0xa5, 0xf0, 0x7b, 0xad} \ + } + +extern EFI_GUID gArmScmiPerformanceProtocolGuid; + +typedef struct _SCMI_PERFORMANCE_PROTOCOL SCMI_PERFORMANCE_PROTOCOL; + +#pragma pack(1) + +#define POWER_IN_MW_SHIFT 16 +#define POWER_IN_MW_MASK 0x1 +#define NUM_PERF_DOMAINS_MASK 0xFFFF + +// Total number of performance domains, Attr Bits [15:0] +#define SCMI_PERF_TOTAL_DOMAINS(Attr) (Attr & NUM_PERF_DOMAINS_MASK) + +// A flag to express power values in mW or platform specific way, Attr Bit [16] +#define SCMI_PERF_POWER_IN_MW(Attr) ((Attr >> POWER_IN_MW_SHIFT) & \ + POWER_IN_MW_MASK) + +// Performance protocol attributes return values. +typedef struct { + UINT32 Attributes; + UINT64 StatisticsAddress; + UINT32 StatisticsLen; +} SCMI_PERFORMANCE_PROTOCOL_ATTRIBUTES; + +#define SCMI_PERF_SUPPORT_LVL_CHANGE_NOTIFY(Attr) ((Attr >> 28) & 0x1) +#define SCMI_PERF_SUPPORT_LIM_CHANGE_NOTIFY(Attr) ((Attr >> 29) & 0x1) +#define SCMI_PERF_SUPPORT_SET_LVL(Attr) ((Attr >> 30) & 0x1) +#define SCMI_PERF_SUPPORT_SET_LIM(Attr) ((Attr >> 31) & 0x1) +#define SCMI_PERF_RATE_LIMIT(RateLimit) (RateLimit & 0xFFF) + +// Performance protocol domain attributes. +typedef struct { + UINT32 Attributes; + UINT32 RateLimit; + UINT32 SustainedFreq; + UINT32 SustainedPerfLevel; + UINT8 Name[SCMI_MAX_STR_LEN]; +} SCMI_PERFORMANCE_DOMAIN_ATTRIBUTES; + +// Worst case latency in microseconds, Bits[15:0] +#define PERF_LATENCY_MASK 0xFFFF +#define SCMI_PERFORMANCE_PROTOCOL_LATENCY(Latency) (Latency & PERF_LATENCY_MASK) + +// Performance protocol performance level. +typedef struct { + UINT32 Level; + UINT32 PowerCost; + UINT32 Latency; +} SCMI_PERFORMANCE_LEVEL; + +// Performance protocol performance limit. +typedef struct { + UINT32 RangeMax; + UINT32 RangeMin; +} SCMI_PERFORMANCE_LIMITS; + +#pragma pack() + +/** Return version of the performance management protocol supported by SCP. + firmware. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + + @param[out] Version Version of the supported SCMI performance management + protocol. + + @retval EFI_SUCCESS The version is returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_PERFORMANCE_GET_VERSION)( + IN SCMI_PERFORMANCE_PROTOCOL *This, + OUT UINT32 *Version + ); + +/** Return protocol attributes of the performance management protocol. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + + @param[out] Attributes Protocol attributes. + + @retval EFI_SUCCESS Protocol attributes are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_PERFORMANCE_GET_ATTRIBUTES)( + IN SCMI_PERFORMANCE_PROTOCOL *This, + OUT SCMI_PERFORMANCE_PROTOCOL_ATTRIBUTES *Attributes + + ); + +/** Return performance domain attributes. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + + @param[out] Attributes Performance domain attributes. + + @retval EFI_SUCCESS Domain attributes are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_PERFORMANCE_GET_DOMAIN_ATTRIBUTES)( + IN SCMI_PERFORMANCE_PROTOCOL *This, + IN UINT32 DomainId, + OUT SCMI_PERFORMANCE_DOMAIN_ATTRIBUTES *DomainAttributes + ); + +/** Return list of performance domain levels of a given domain. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + + @param[out] NumLevels Total number of levels a domain can support. + + @param[in,out] LevelArraySize Size of the performance level array. + + @param[out] LevelArray Array of the performance levels. + + @retval EFI_SUCCESS Domain levels are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval EFI_BUFFER_TOO_SMALL LevelArraySize is too small for the result. + It has been updated to the size needed. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_PERFORMANCE_DESCRIBE_LEVELS)( + IN SCMI_PERFORMANCE_PROTOCOL *This, + IN UINT32 DomainId, + OUT UINT32 *NumLevels, + IN OUT UINT32 *LevelArraySize, + OUT SCMI_PERFORMANCE_LEVEL *LevelArray + ); + +/** Set performance limits of a domain. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + @param[in] Limit Performance limit to set. + + @retval EFI_SUCCESS Performance limits set successfully. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_PERFORMANCE_LIMITS_SET)( + IN SCMI_PERFORMANCE_PROTOCOL *This, + IN UINT32 DomainId, + IN SCMI_PERFORMANCE_LIMITS *Limits + ); + +/** Get performance limits of a domain. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + + @param[out] Limit Performance Limits of the domain. + + @retval EFI_SUCCESS Performance limits are returned. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_PERFORMANCE_LIMITS_GET)( + SCMI_PERFORMANCE_PROTOCOL *This, + UINT32 DomainId, + SCMI_PERFORMANCE_LIMITS *Limits + ); + +/** Set performance level of a domain. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + @param[in] Level Performance level of the domain. + + @retval EFI_SUCCESS Performance level set successfully. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_PERFORMANCE_LEVEL_SET)( + IN SCMI_PERFORMANCE_PROTOCOL *This, + IN UINT32 DomainId, + IN UINT32 Level + ); + +/** Get performance level of a domain. + + @param[in] This A Pointer to SCMI_PERFORMANCE_PROTOCOL Instance. + @param[in] DomainId Identifier for the performance domain. + + @param[out] Level Performance level of the domain. + + @retval EFI_SUCCESS Performance level got successfully. + @retval EFI_DEVICE_ERROR SCP returns an SCMI error. + @retval !(EFI_SUCCESS) Other errors. +**/ +typedef +EFI_STATUS +(EFIAPI *SCMI_PERFORMANCE_LEVEL_GET)( + IN SCMI_PERFORMANCE_PROTOCOL *This, + IN UINT32 DomainId, + OUT UINT32 *Level + ); + +typedef struct _SCMI_PERFORMANCE_PROTOCOL { + SCMI_PERFORMANCE_GET_VERSION GetVersion; + SCMI_PERFORMANCE_GET_ATTRIBUTES GetProtocolAttributes; + SCMI_PERFORMANCE_GET_DOMAIN_ATTRIBUTES GetDomainAttributes; + SCMI_PERFORMANCE_DESCRIBE_LEVELS DescribeLevels; + SCMI_PERFORMANCE_LIMITS_SET LimitsSet; + SCMI_PERFORMANCE_LIMITS_GET LimitsGet; + SCMI_PERFORMANCE_LEVEL_SET LevelSet; + SCMI_PERFORMANCE_LEVEL_GET LevelGet; +} SCMI_PERFORMANCE_PROTOCOL; + +typedef enum { + ScmiMessageIdPerformanceDomainAttributes = 0x3, + ScmiMessageIdPerformanceDescribeLevels = 0x4, + ScmiMessageIdPerformanceLimitsSet = 0x5, + ScmiMessageIdPerformanceLimitsGet = 0x6, + ScmiMessageIdPerformanceLevelSet = 0x7, + ScmiMessageIdPerformanceLevelGet = 0x8, +} SCMI_MESSAGE_ID_PERFORMANCE; + +#endif /* ARM_SCMI_PERFORMANCE_PROTOCOL_H_ */ diff --git a/ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.c b/ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.c new file mode 100644 index 000000000..d663a76a9 --- /dev/null +++ b/ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.c @@ -0,0 +1,284 @@ +/** @file + Generic ARM implementation of TimerLib.h + + Copyright (c) 2011 - 2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include +#include +#include +#include + +#define TICKS_PER_MICRO_SEC (PcdGet32 (PcdArmArchTimerFreqInHz)/1000000U) + +// Select appropriate multiply function for platform architecture. +#ifdef MDE_CPU_ARM +#define MULT_U64_X_N MultU64x32 +#else +#define MULT_U64_X_N MultU64x64 +#endif + +RETURN_STATUS +EFIAPI +TimerConstructor ( + VOID + ) +{ + // + // Check if the ARM Generic Timer Extension is implemented. + // + if (ArmIsArchTimerImplemented ()) { + // + // Check if Architectural Timer frequency is pre-determined by the platform + // (ie. nonzero). + // + if (PcdGet32 (PcdArmArchTimerFreqInHz) != 0) { + // + // Check if ticks/uS is not 0. The Architectural timer runs at constant + // frequency, irrespective of CPU frequency. According to Generic Timer + // Ref manual, lower bound of the frequency is in the range of 1-10MHz. + // + ASSERT (TICKS_PER_MICRO_SEC); + + #ifdef MDE_CPU_ARM + // + // Only set the frequency for ARMv7. We expect the secure firmware to + // have already done it. + // If the security extension is not implemented, set Timer Frequency + // here. + // + if (ArmHasSecurityExtensions ()) { + ArmGenericTimerSetTimerFreq (PcdGet32 (PcdArmArchTimerFreqInHz)); + } + + #endif + } + + // + // Architectural Timer Frequency must be set in Secure privileged + // mode (if secure extension is supported). + // If the reset value (0) is returned, just ASSERT. + // + ASSERT (ArmGenericTimerGetTimerFreq () != 0); + } else { + DEBUG ((DEBUG_ERROR, "ARM Architectural Timer is not available in the CPU, hence this library cannot be used.\n")); + ASSERT (0); + } + + return RETURN_SUCCESS; +} + +/** + A local utility function that returns the PCD value, if specified. + Otherwise it defaults to ArmGenericTimerGetTimerFreq. + + @return The timer frequency. + +**/ +STATIC +UINTN +EFIAPI +GetPlatformTimerFreq ( + ) +{ + UINTN TimerFreq; + + TimerFreq = PcdGet32 (PcdArmArchTimerFreqInHz); + if (TimerFreq == 0) { + TimerFreq = ArmGenericTimerGetTimerFreq (); + } + + return TimerFreq; +} + +/** + Stalls the CPU for the number of microseconds specified by MicroSeconds. + + @param MicroSeconds The minimum number of microseconds to delay. + + @return The value of MicroSeconds input. + +**/ +UINTN +EFIAPI +MicroSecondDelay ( + IN UINTN MicroSeconds + ) +{ + UINT64 TimerTicks64; + UINT64 SystemCounterVal; + + // Calculate counter ticks that represent requested delay: + // = MicroSeconds x TICKS_PER_MICRO_SEC + // = MicroSeconds x Frequency.10^-6 + TimerTicks64 = DivU64x32 ( + MULT_U64_X_N ( + MicroSeconds, + GetPlatformTimerFreq () + ), + 1000000U + ); + + // Read System Counter value + SystemCounterVal = ArmGenericTimerGetSystemCount (); + + TimerTicks64 += SystemCounterVal; + + // Wait until delay count expires. + while (SystemCounterVal < TimerTicks64) { + SystemCounterVal = ArmGenericTimerGetSystemCount (); + } + + return MicroSeconds; +} + +/** + Stalls the CPU for at least the given number of nanoseconds. + + Stalls the CPU for the number of nanoseconds specified by NanoSeconds. + + When the timer frequency is 1MHz, each tick corresponds to 1 microsecond. + Therefore, the nanosecond delay will be rounded up to the nearest 1 microsecond. + + @param NanoSeconds The minimum number of nanoseconds to delay. + + @return The value of NanoSeconds inputted. + +**/ +UINTN +EFIAPI +NanoSecondDelay ( + IN UINTN NanoSeconds + ) +{ + UINTN MicroSeconds; + + // Round up to 1us Tick Number + MicroSeconds = NanoSeconds / 1000; + MicroSeconds += ((NanoSeconds % 1000) == 0) ? 0 : 1; + + MicroSecondDelay (MicroSeconds); + + return NanoSeconds; +} + +/** + Retrieves the current value of a 64-bit free running performance counter. + + The counter can either count up by 1 or count down by 1. If the physical + performance counter counts by a larger increment, then the counter values + must be translated. The properties of the counter can be retrieved from + GetPerformanceCounterProperties(). + + @return The current value of the free running performance counter. + +**/ +UINT64 +EFIAPI +GetPerformanceCounter ( + VOID + ) +{ + // Just return the value of system count + return ArmGenericTimerGetSystemCount (); +} + +/** + Retrieves the 64-bit frequency in Hz and the range of performance counter + values. + + If StartValue is not NULL, then the value that the performance counter starts + with immediately after is it rolls over is returned in StartValue. If + EndValue is not NULL, then the value that the performance counter end with + immediately before it rolls over is returned in EndValue. The 64-bit + frequency of the performance counter in Hz is always returned. If StartValue + is less than EndValue, then the performance counter counts up. If StartValue + is greater than EndValue, then the performance counter counts down. For + example, a 64-bit free running counter that counts up would have a StartValue + of 0 and an EndValue of 0xFFFFFFFFFFFFFFFF. A 24-bit free running counter + that counts down would have a StartValue of 0xFFFFFF and an EndValue of 0. + + @param StartValue The value the performance counter starts with when it + rolls over. + @param EndValue The value that the performance counter ends with before + it rolls over. + + @return The frequency in Hz. + +**/ +UINT64 +EFIAPI +GetPerformanceCounterProperties ( + OUT UINT64 *StartValue OPTIONAL, + OUT UINT64 *EndValue OPTIONAL + ) +{ + if (StartValue != NULL) { + // Timer starts at 0 + *StartValue = (UINT64)0ULL; + } + + if (EndValue != NULL) { + // Timer counts up. + *EndValue = 0xFFFFFFFFFFFFFFFFUL; + } + + return (UINT64)ArmGenericTimerGetTimerFreq (); +} + +/** + Converts elapsed ticks of performance counter to time in nanoseconds. + + This function converts the elapsed ticks of running performance counter to + time value in unit of nanoseconds. + + @param Ticks The number of elapsed ticks of running performance counter. + + @return The elapsed time in nanoseconds. + +**/ +UINT64 +EFIAPI +GetTimeInNanoSecond ( + IN UINT64 Ticks + ) +{ + UINT64 NanoSeconds; + UINT32 Remainder; + UINT32 TimerFreq; + + TimerFreq = GetPlatformTimerFreq (); + // + // Ticks + // Time = --------- x 1,000,000,000 + // Frequency + // + NanoSeconds = MULT_U64_X_N ( + DivU64x32Remainder ( + Ticks, + TimerFreq, + &Remainder + ), + 1000000000U + ); + + // + // Frequency < 0x100000000, so Remainder < 0x100000000, then (Remainder * 1,000,000,000) + // will not overflow 64-bit. + // + NanoSeconds += DivU64x32 ( + MULT_U64_X_N ( + (UINT64)Remainder, + 1000000000U + ), + TimerFreq + ); + + return NanoSeconds; +} diff --git a/ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.inf b/ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.inf new file mode 100644 index 000000000..6f576ceab --- /dev/null +++ b/ArmPkg/Library/ArmArchTimerLib/ArmArchTimerLib.inf @@ -0,0 +1,32 @@ +#/** @file +# +# Copyright (c) 2011 - 2014, ARM Limited. All rights reserved.
+# SPDX-License-Identifier: BSD-2-Clause-Patent +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArmArchTimerLib + FILE_GUID = 82da1b44-d2d6-4a7d-bbf0-a0cb67964034 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = TimerLib + CONSTRUCTOR = TimerConstructor + +[Sources.common] + ArmArchTimerLib.c + +[Packages] + MdePkg/MdePkg.dec + EmbeddedPkg/EmbeddedPkg.dec + ArmPkg/ArmPkg.dec + +[LibraryClasses] + DebugLib + ArmLib + BaseLib + ArmGenericTimerCounterLib + +[Pcd] + gArmTokenSpaceGuid.PcdArmArchTimerFreqInHz diff --git a/ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.c b/ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.c new file mode 100644 index 000000000..bad5d244c --- /dev/null +++ b/ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.c @@ -0,0 +1,148 @@ +/** @file + + Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.
+ Copyright (c) 2011 - 2021, ARM Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ +#include +#include +#include +#include + +STATIC +VOID +CacheRangeOperation ( + IN VOID *Start, + IN UINTN Length, + IN LINE_OPERATION LineOperation, + IN UINTN LineLength + ) +{ + UINTN ArmCacheLineAlignmentMask; + // Align address (rounding down) + UINTN AlignedAddress; + UINTN EndAddress; + + ArmCacheLineAlignmentMask = LineLength - 1; + AlignedAddress = (UINTN)Start - ((UINTN)Start & ArmCacheLineAlignmentMask); + EndAddress = (UINTN)Start + Length; + + // Perform the line operation on an address in each cache line + while (AlignedAddress < EndAddress) { + LineOperation (AlignedAddress); + AlignedAddress += LineLength; + } + + ArmDataSynchronizationBarrier (); +} + +VOID +EFIAPI +InvalidateInstructionCache ( + VOID + ) +{ + ASSERT (FALSE); +} + +VOID +EFIAPI +InvalidateDataCache ( + VOID + ) +{ + ASSERT (FALSE); +} + +VOID * +EFIAPI +InvalidateInstructionCacheRange ( + IN VOID *Address, + IN UINTN Length + ) +{ + CacheRangeOperation ( + Address, + Length, + ArmCleanDataCacheEntryToPoUByMVA, + ArmDataCacheLineLength () + ); + CacheRangeOperation ( + Address, + Length, + ArmInvalidateInstructionCacheEntryToPoUByMVA, + ArmInstructionCacheLineLength () + ); + + ArmInstructionSynchronizationBarrier (); + + return Address; +} + +VOID +EFIAPI +WriteBackInvalidateDataCache ( + VOID + ) +{ + ASSERT (FALSE); +} + +VOID * +EFIAPI +WriteBackInvalidateDataCacheRange ( + IN VOID *Address, + IN UINTN Length + ) +{ + CacheRangeOperation ( + Address, + Length, + ArmCleanInvalidateDataCacheEntryByMVA, + ArmDataCacheLineLength () + ); + return Address; +} + +VOID +EFIAPI +WriteBackDataCache ( + VOID + ) +{ + ASSERT (FALSE); +} + +VOID * +EFIAPI +WriteBackDataCacheRange ( + IN VOID *Address, + IN UINTN Length + ) +{ + CacheRangeOperation ( + Address, + Length, + ArmCleanDataCacheEntryByMVA, + ArmDataCacheLineLength () + ); + return Address; +} + +VOID * +EFIAPI +InvalidateDataCacheRange ( + IN VOID *Address, + IN UINTN Length + ) +{ + CacheRangeOperation ( + Address, + Length, + ArmInvalidateDataCacheEntryByMVA, + ArmDataCacheLineLength () + ); + return Address; +} diff --git a/ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.inf b/ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.inf new file mode 100644 index 000000000..94261e104 --- /dev/null +++ b/ArmPkg/Library/ArmCacheMaintenanceLib/ArmCacheMaintenanceLib.inf @@ -0,0 +1,28 @@ +#/** @file +# Implement CacheMaintenanceLib for ARM architectures +# +# Copyright (c) 2008, Apple Inc. All rights reserved.
+# +# SPDX-License-Identifier: BSD-2-Clause-Patent +# +# +#**/ + +[Defines] + INF_VERSION = 0x00010005 + BASE_NAME = ArmCacheMaintenanceLib + FILE_GUID = 1A20BE1F-33AD-450C-B49A-7123FCA8B7F9 + MODULE_TYPE = BASE + VERSION_STRING = 1.0 + LIBRARY_CLASS = CacheMaintenanceLib + +[Sources.common] + ArmCacheMaintenanceLib.c + +[Packages] + ArmPkg/ArmPkg.dec + MdePkg/MdePkg.dec + +[LibraryClasses] + ArmLib + BaseLib diff --git a/ArmPkg/Library/ArmDisassemblerLib/Aarch64Disassembler.c b/ArmPkg/Library/ArmDisassemblerLib/Aarch64Disassembler.c new file mode 100644 index 000000000..ac334f0eb --- /dev/null +++ b/ArmPkg/Library/ArmDisassemblerLib/Aarch64Disassembler.c @@ -0,0 +1,42 @@ +/** @file + Default exception handler + + Copyright (c) 2014, ARM Limited. All rights reserved. + + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +/** + Place a disassembly of **OpCodePtr into buffer, and update OpCodePtr to + point to next instruction. + + @param OpCodePtrPtr Pointer to pointer of instruction to disassemble. + @param Thumb TRUE for Thumb(2), FALSE for ARM instruction stream + @param Extended TRUE dump hex for instruction too. + @param ItBlock Size of IT Block + @param Buf Buffer to sprintf disassembly into. + @param Size Size of Buf in bytes. + +**/ +VOID +DisassembleInstruction ( + IN UINT8 **OpCodePtr, + IN BOOLEAN Thumb, + IN BOOLEAN Extended, + IN OUT UINT32 *ItBlock, + OUT CHAR8 *Buf, + OUT UINTN Size + ) +{ + // Not yet supported for AArch64. + // Put error in the buffer as we have no return code and the buffer may be + // printed directly so needs a '\0'. + AsciiSPrint (Buf, Size, "AArch64 not supported"); + return; +} diff --git a/ArmPkg/Library/ArmDisassemblerLib/ArmDisassembler.c b/ArmPkg/Library/ArmDisassemblerLib/ArmDisassembler.c new file mode 100644 index 000000000..0e0906295 --- /dev/null +++ b/ArmPkg/Library/ArmDisassemblerLib/ArmDisassembler.c @@ -0,0 +1,465 @@ +/** @file + Default exception handler + + Copyright (c) 2008 - 2010, Apple Inc. All rights reserved.
+ Copyright (c) 2021, Arm Limited. All rights reserved.
+ + SPDX-License-Identifier: BSD-2-Clause-Patent + +**/ + +#include +#include +#include +#include + +CHAR8 *gCondition[] = { + "EQ", + "NE", + "CS", + "CC", + "MI", + "PL", + "VS", + "VC", + "HI", + "LS", + "GE", + "LT", + "GT", + "LE", + "", + "2" +}; + +#define COND(_a) gCondition[((_a) >> 28)] + +CHAR8 *gReg[] = { + "r0", + "r1", + "r2", + "r3", + "r4", + "r5", + "r6", + "r7", + "r8", + "r9", + "r10", + "r11", + "r12", + "sp", + "lr", + "pc" +}; + +CHAR8 *gLdmAdr[] = { + "DA", + "IA", + "DB", + "IB" +}; + +CHAR8 *gLdmStack[] = { + "FA", + "FD", + "EA", + "ED" +}; + +#define LDM_EXT(_reg, _off) ((_reg == 13) ? gLdmStack[(_off)] : gLdmAdr[(_off)]) + +#define SIGN(_U) ((_U) ? "" : "-") +#define WRITE(_Write) ((_Write) ? "!" : "") +#define BYTE(_B) ((_B) ? "B":"") +#define USER(_B) ((_B) ? "^" : "") + +CHAR8 mMregListStr[4*15 + 1]; + +CHAR8 * +MRegList ( + UINT32 OpCode + ) +{ + UINTN Index, Start, End; + BOOLEAN First; + + mMregListStr[0] = '\0'; + AsciiStrCatS (mMregListStr, sizeof mMregListStr, "{"); + for (Index = 0, First = TRUE; Index <= 15; Index++) { + if ((OpCode & (1 << Index)) != 0) { + Start = End = Index; + for (Index++; ((OpCode & (1 << Index)) != 0) && Index <= 15; Index++) { + End = Index; + } + + if (!First) { + AsciiStrCatS (mMregListStr, sizeof mMregListStr, ","); + } else { + First = FALSE; + } + + if (Start == End) { + AsciiStrCatS (mMregListStr, sizeof mMregListStr, gReg[Start]); + AsciiStrCatS (mMregListStr, sizeof mMregListStr, ", "); + } else { + AsciiStrCatS (mMregListStr, sizeof mMregListStr, gReg[Start]); + AsciiStrCatS (mMregListStr, sizeof mMregListStr, "-"); + AsciiStrCatS (mMregListStr, sizeof mMregListStr, gReg[End]); + } + } + } + + if (First) { + AsciiStrCatS (mMregListStr, sizeof mMregListStr, "ERROR"); + } + + AsciiStrCatS (mMregListStr, sizeof mMregListStr, "}"); + + // BugBug: Make caller pass in buffer it is cleaner + return mMregListStr; +} + +CHAR8 * +FieldMask ( + IN UINT32 Mask + ) +{ + return ""; +} + +UINT32 +RotateRight ( + IN UINT32 Op, + IN UINT32 Shift + ) +{ + return (Op >> Shift) | (Op << (32 - Shift)); +} + +/** + Place a disassembly of **OpCodePtr into buffer, and update OpCodePtr to + point to next instruction. + + We cheat and only decode instructions that access + memory. If the instruction is not found we dump the instruction in hex. + + @param OpCodePtr Pointer to pointer of ARM instruction to disassemble. + @param Buf Buffer to sprintf disassembly into. + @param Size Size of Buf in bytes. + @param Extended TRUE dump hex for instruction too. + +**/ +VOID +DisassembleArmInstruction ( + IN UINT32 **OpCodePtr, + OUT CHAR8 *Buf, + OUT UINTN Size, + IN BOOLEAN Extended + ) +{ + UINT32 OpCode; + CHAR8 *Type; + CHAR8 *Root; + BOOLEAN Imm, Pre, Up, WriteBack, Write, Load, Sign, Half; + UINT32 Rn, Rd, Rm; + UINT32 IMod, Offset8, Offset12; + UINT32 Index; + UINT32 ShiftImm, Shift; + + OpCode = **OpCodePtr; + + Imm = (OpCode & BIT25) == BIT25; // I + Pre = (OpCode & BIT24) == BIT24; // P + Up = (OpCode & BIT23) == BIT23; // U + WriteBack = (OpCode & BIT22) == BIT22; // B, also called S + Write = (OpCode & BIT21) == BIT21; // W + Load = (OpCode & BIT20) == BIT20; // L + Sign = (OpCode & BIT6) == BIT6; // S + Half = (OpCode & BIT5) == BIT5; // H + Rn = (OpCode >> 16) & 0xf; + Rd = (OpCode >> 12) & 0xf; + Rm = (OpCode & 0xf); + + if (Extended) { + Index = AsciiSPrint (Buf, Size, "0x%08x ", OpCode); + Buf += Index; + Size -= Index; + } + + // LDREX, STREX + if ((OpCode & 0x0fe000f0) == 0x01800090) { + if (Load) { + // A4.1.27 LDREX{} , [] + AsciiSPrint (Buf, Size, "LDREX%a %a, [%a]", COND (OpCode), gReg[Rd], gReg[Rn]); + } else { + // A4.1.103 STREX{} , , [] + AsciiSPrint (Buf, Size, "STREX%a %a, %a, [%a]", COND (OpCode), gReg[Rd], gReg[Rn], gReg[Rn]); + } + + return; + } + + // LDM/STM + if ((OpCode & 0x0e000000) == 0x08000000) { + if (Load) { + // A4.1.20 LDM{} {!}, + // A4.1.21 LDM{} , ^ + // A4.1.22 LDM{} {!}, ^ + AsciiSPrint (Buf, Size, "LDM%a%a, %a%a, %a", COND (OpCode), LDM_EXT (Rn, (OpCode >> 23) & 3), gReg[Rn], WRITE (Write), MRegList (OpCode), USER (WriteBack)); + } else { + // A4.1.97 STM{} {!}, + // A4.1.98 STM{} , ^ + AsciiSPrint (Buf, Size, "STM%a%a, %a%a, %a", COND (OpCode), LDM_EXT (Rn, (OpCode >> 23) & 3), gReg[Rn], WRITE (Write), MRegList (OpCode), USER (WriteBack)); + } + + return; + } + + // LDR/STR Address Mode 2 + if (((OpCode & 0x0c000000) == 0x04000000) || ((OpCode & 0xfd70f000) == 0xf550f000)) { + Offset12 = OpCode & 0xfff; + if ((OpCode & 0xfd70f000) == 0xf550f000) { + Index = AsciiSPrint (Buf, Size, "PLD"); + } else { + Index = AsciiSPrint (Buf, Size, "%a%a%a%a %a, ", Load ? "LDR" : "STR", COND (OpCode), BYTE (WriteBack), (!(Pre) && Write) ? "T" : "", gReg[Rd]); + } + + if (Pre) { + if (!Imm) { + // A5.2.2 [, #+/-] + // A5.2.5 [, #+/-] + AsciiSPrint (&Buf[Index], Size - Index, "[%a, #%a0x%x]%a", gReg[Rn], SIGN (Up), Offset12, WRITE (Write)); + } else if ((OpCode & 0x03000ff0) == 0x03000000) { + // A5.2.3 [, +/-] + // A5.2.6 [, +/-]! + AsciiSPrint (&Buf[Index], Size - Index, "[%a, #%a%a]%a", gReg[Rn], SIGN (Up), WRITE (Write)); + } else { + // A5.2.4 [, +/-, LSL #] + // A5.2.7 [, +/-, LSL #]! + ShiftImm = (OpCode >> 7) & 0x1f; + Shift = (OpCode >> 5) & 0x3; + if (Shift == 0x0) { + Type = "LSL"; + } else if (Shift == 0x1) { + Type = "LSR"; + if (ShiftImm == 0) { + ShiftImm = 32; + } + } else if (Shift == 0x2) { + Type = "ASR"; + } else if (ShiftImm == 0) { + AsciiSPrint (&Buf[Index], Size - Index, "[%a, #%a%a, %a, RRX]%a", gReg[Rn], SIGN (Up), gReg[Rm], WRITE (Write)); + return; + } else { + Type = "ROR"; + } + + AsciiSPrint (&Buf[Index], Size - Index, "[%a, #%a%a, %a, #%d]%a", gReg[Rn], SIGN (Up), gReg[Rm], Type, ShiftImm, WRITE (Write)); + } + } else { + // !Pre + if (!Imm) { + // A5.2.8 [], #+/- + AsciiSPrint (&Buf[Index], Size - Index, "[%a], #%a0x%x", gReg[Rn], SIGN (Up), Offset12); + } else if ((OpCode & 0x03000ff0) == 0x03000000) { + // A5.2.9 [], +/- + AsciiSPrint (&Buf[Index], Size - Index, "[%a], #%a%a", gReg[Rn], SIGN (Up), gReg[Rm]); + } else { + // A5.2.10 [], +/-, LSL # + ShiftImm = (OpCode >> 7) & 0x1f; + Shift = (OpCode >> 5) & 0x3; + + if (Shift == 0x0) { + Type = "LSL"; + } else if (Shift == 0x1) { + Type = "LSR"; + if (ShiftImm == 0) { + ShiftImm = 32; + } + } else if (Shift == 0x2) { + Type = "ASR"; + } else if (ShiftImm == 0) { + AsciiSPrint (&Buf[Index], Size - Index, "[%a], #%a%a, %a, RRX", gReg[Rn], SIGN (Up), gReg[Rm]); + // FIx me + return; + } else { + Type = "ROR"; + } + + AsciiSPrint (&Buf[Index], Size - Index, "[%a], #%a%a, %a, #%d", gReg[Rn], SIGN (Up), gReg[Rm], Type, ShiftImm); + } + } + + return; + } + + if ((OpCode & 0x0e000000) == 0x00000000) { + // LDR/STR address mode 3 + // LDR|STR{}H|SH|SB|D , + if (Load) { + if (!Sign) { + Root = "LDR%aH %a, "; + } else if (!Half) { + Root = "LDR%aSB %a, "; + } else { + Root = "LDR%aSH %a, "; + } + } else { + if (!Sign) { + Root = "STR%aH %a "; + } else if (!Half) { + Root = "LDR%aD %a "; + } else { + Root = "STR%aD %a "; + } + } + + Index = AsciiSPrint (Buf, Size, Root, COND (OpCode), gReg[Rd]); + + Sign = (OpCode & BIT6) == BIT6; + Half = (OpCode & BIT5) == BIT5; + Offset8 = ((OpCode >> 4) | (OpCode * 0xf)) & 0xff; + if (Pre & !Write) { + // Immediate offset/index + if (WriteBack) { + // A5.3.2 [, #+/-] + // A5.3.4 [, #+/-]! + AsciiSPrint (&Buf[Index], Size - Index, "[%a, #%a%d]%a", gReg[Rn], SIGN (Up), Offset8, WRITE (Write)); + } else { + // A5.3.3 [, +/-] + // A5.3.5 [, +/-]! + AsciiSPrint (&Buf[Index], Size - Index, "[%a, #%a%]a", gReg[Rn], SIGN (Up), gReg[Rm], WRITE (Write)); + } + } else { + // Register offset/index + if (WriteBack) { + // A5.3.6 [], #+/- + AsciiSPrint (&Buf[Index], Size - Index, "[%a], #%a%d", gReg[Rn], SIGN (Up), Offset8); + } else { + // A5.3.7 [], +/- + AsciiSPrint (&Buf[Index], Size - Index, "[%a], #%a%a", gReg[Rn], SIGN (Up), gReg[Rm]); + } + } + + return; + } + + if ((OpCode & 0x0fb000f0) == 0x01000050) { + // A4.1.108 SWP SWP{}B , , [] + // A4.1.109 SWPB SWP{}B , , [] + AsciiSPrint (Buf, Size, "SWP%a%a %a, %a, [%a]", COND (OpCode), BYTE (WriteBack), gReg[Rd], gReg[Rm], gReg[Rn]); + return; + } + + if ((OpCode & 0xfe5f0f00) == 0xf84d0500) { + // A4.1.90 SRS SRS #{!} + AsciiSPrint (Buf, Size, "SRS%a #0x%x%a", gLdmStack[(OpCode >> 23) & 3], OpCode & 0x1f, WRITE (Write)); + return; + } + + if ((OpCode & 0xfe500f00) == 0xf8100500) { + // A4.1.59 RFE {!} + AsciiSPrint (Buf, Size, "RFE%a %a", gLdmStack[(OpCode >> 23) & 3], gReg[Rn], WRITE (Write)); + return; + } + + if ((OpCode & 0xfff000f0) == 0xe1200070) { + // A4.1.7 BKPT + AsciiSPrint (Buf, Size, "BKPT %x", ((OpCode >> 8) | (OpCode & 0xf)) & 0xffff); + return; + } + + if ((OpCode & 0xfff10020) == 0xf1000000) { + // A4.1.16 CPS {, #} + if (((OpCode >> 6) & 0x7) == 0) { + AsciiSPrint (Buf, Size, "CPS #0x%x", (OpCode & 0x2f)); + } else { + IMod = (OpCode >> 18) & 0x3; + Index = AsciiSPrint ( + Buf, + Size, + "CPS%a %a%a%a", + (IMod == 3) ? "ID" : "IE", + ((OpCode & BIT8) != 0) ? "A" : "", + ((OpCode & BIT7) != 0) ? "I" : "", + ((OpCode & BIT6) != 0) ? "F" : "" + ); + if ((OpCode & BIT17) != 0) { + AsciiSPrint (&Buf[Index], Size - Index, ", #0x%x", OpCode & 0x1f); + } + } + + return; + } + + if ((OpCode & 0x0f000000) == 0x0f000000) { + // A4.1.107 SWI{} + AsciiSPrint (Buf, Size, "SWI%a %x", COND (OpCode), OpCode & 0x00ffffff); + return; + } + + if ((OpCode & 0x0fb00000) == 0x01000000) { + // A4.1.38 MRS{} , CPSR MRS{} , SPSR + AsciiSPrint (Buf, Size, "MRS%a %a, %a", COND (OpCode), gReg[Rd], WriteBack ? "SPSR" : "CPSR"); + return; + } + + if ((OpCode & 0x0db00000) == 0x01200000) { + // A4.1.38 MSR{} CPSR_, # MSR{} CPSR_, + if (Imm) { + // MSR{} CPSR_, # + AsciiSPrint (Buf, Size, "MRS%a %a_%a, #0x%x", COND (OpCode), WriteBack ? "SPSR" : "CPSR", FieldMask ((OpCode >> 16) & 0xf), RotateRight (OpCode & 0xf, ((OpCode >> 8) & 0xf) *2)); + } else { + // MSR{} CPSR_, + AsciiSPrint (Buf, Size, "MRS%a %a_%a, %a", COND (OpCode), WriteBack ? "SPSR" : "CPSR", gReg[Rd]); + } + + return; + } + + if ((OpCode & 0xff000010) == 0xfe000000) { + // A4.1.13 CDP{} , , , , , + AsciiSPrint (Buf, Size, "CDP%a 0x%x, 0x%x, CR%d, CR%d, CR%d, 0x%x", COND (OpCode), (OpCode >> 8) & 0xf, (OpCode >> 20) & 0xf, Rn, Rd, Rm, (OpCode >> 5) &0x7); + return; + } + + if ((OpCode & 0x0e000000) == 0x0c000000) { + // A4.1.19 LDC and A4.1.96 SDC + if ((OpCode & 0xf0000000) == 0xf0000000) { + Index = AsciiSPrint (Buf, Size, "%a2 0x%x, CR%d, ", Load ? "LDC" : "SDC", (OpCode >> 8) & 0xf, Rd); + } else { + Index = AsciiSPrint (Buf, Size, "%a%a 0x%x, CR%d, ", Load ? "LDC" : "SDC", COND (OpCode), (OpCode >> 8) & 0xf, Rd); + } + + if (!Pre) { + if (!Write) { + // A5.5.5.5 [],