From: "Leif Lindholm" <leif@nuviainc.com>
To: Rebecca Cran <rebecca@nuviainc.com>
Cc: devel@edk2.groups.io, Ard Biesheuvel <ard.biesheuvel@arm.com>,
nd@arm.com, Sami Mujawar <Sami.Mujawar@arm.com>,
Liming Gao <gaoliming@byosoft.com.cn>,
Michael D Kinney <michael.d.kinney@intel.com>,
Zhiguang Liu <zhiguang.liu@intel.com>,
Samer El-Haj-Mahmoud <Samer.El-Haj-Mahmoud@arm.com>
Subject: Re: [PATCH v6 14/22] ArmPkg: Add Universal/Smbios/ProcessorSubClassDxe
Date: Mon, 25 Jan 2021 19:04:17 +0000 [thread overview]
Message-ID: <20210125190417.GE1664@vanye> (raw)
In-Reply-To: <20210114163628.31952-15-rebecca@nuviainc.com>
On Thu, Jan 14, 2021 at 09:36:20 -0700, Rebecca Cran wrote:
> ProcessorSubClassDxe provides SMBIOS CPU information using generic
> methods combined with calls into OemMiscLib.
>
> Signed-off-by: Rebecca Cran <rebecca@nuviainc.com>
> Reviewed-by: Samer El-Haj-Mahmoud <Samer.El-Haj-Mahmoud@arm.com>
> ---
> ArmPkg/ArmPkg.dsc | 2 +
> ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClassDxe.inf | 58 ++
> ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClass.c | 863 ++++++++++++++++++++
> ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClassStrings.uni | 24 +
> 4 files changed, 947 insertions(+)
>
> diff --git a/ArmPkg/ArmPkg.dsc b/ArmPkg/ArmPkg.dsc
> index 0f77a6da4483..fce86cb6d710 100644
> --- a/ArmPkg/ArmPkg.dsc
> +++ b/ArmPkg/ArmPkg.dsc
> @@ -148,6 +148,8 @@ [Components.common]
> ArmPkg/Drivers/ArmCrashDumpDxe/ArmCrashDumpDxe.inf
> ArmPkg/Drivers/ArmScmiDxe/ArmScmiDxe.inf
>
> + ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClassDxe.inf
> +
> [Components.AARCH64]
> ArmPkg/Drivers/MmCommunicationDxe/MmCommunication.inf
> ArmPkg/Library/ArmMmuLib/ArmMmuPeiLib.inf
> diff --git a/ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClassDxe.inf b/ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClassDxe.inf
> new file mode 100644
> index 000000000000..c8ef3ee56b72
> --- /dev/null
> +++ b/ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClassDxe.inf
> @@ -0,0 +1,58 @@
> +#/** @file
> +# ProcessorSubClassDxe.inf
> +#
> +# 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
> +#
> +#**/
> +
> +
> +[Defines]
> + INF_VERSION = 1.29
> + BASE_NAME = ProcessorSubClass
> + FILE_GUID = f3fe0e33-ea38-4069-9fb5-be23407207c7
> + MODULE_TYPE = DXE_DRIVER
> + VERSION_STRING = 1.0
> + ENTRY_POINT = ProcessorSubClassEntryPoint
> +
> +[Sources]
> + ProcessorSubClass.c
> + ProcessorSubClassStrings.uni
> +
> +[Packages]
> + ArmPkg/ArmPkg.dec
> + MdeModulePkg/MdeModulePkg.dec
> + MdePkg/MdePkg.dec
> +
> +[LibraryClasses]
> + ArmLib
> + ArmSmcLib
> + BaseLib
> + BaseMemoryLib
> + DebugLib
> + HiiLib
> + IoLib
> + MemoryAllocationLib
> + OemMiscLib
> + PcdLib
> + PrintLib
> + UefiDriverEntryPoint
> +
> +[Protocols]
> + gEfiSmbiosProtocolGuid # PROTOCOL ALWAYS_CONSUMED
> +
> +[Pcd]
> + gArmTokenSpaceGuid.PcdProcessorManufacturer
> + gArmTokenSpaceGuid.PcdProcessorVersion
> + gArmTokenSpaceGuid.PcdProcessorSerialNumber
> + gArmTokenSpaceGuid.PcdProcessorAssetTag
> + gArmTokenSpaceGuid.PcdProcessorPartNumber
> +
> +[Guids]
> +
> +
> +[Depex]
> + gEfiSmbiosProtocolGuid
> diff --git a/ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClass.c b/ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClass.c
> new file mode 100644
> index 000000000000..85cbe2a67f37
> --- /dev/null
> +++ b/ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClass.c
> @@ -0,0 +1,863 @@
> +/** @file
> + ProcessorSubClass.c
> +
> + Copyright (c) 2021, NUVIA Inc. All rights reserved.<BR>
> + Copyright (c) 2015, Hisilicon Limited. All rights reserved.
> + Copyright (c) 2015, Linaro Limited. All rights reserved.
> + SPDX-License-Identifier: BSD-2-Clause-Patent
> +
> +**/
> +
> +#include <Uefi.h>
> +#include <Protocol/Smbios.h>
> +#include <IndustryStandard/ArmStdSmc.h>
> +#include <IndustryStandard/SmBios.h>
> +#include <Library/ArmLib.h>
> +#include <Library/ArmSmcLib.h>
> +#include <Library/ArmLib/ArmLibPrivate.h>
> +#include <Library/BaseLib.h>
> +#include <Library/BaseMemoryLib.h>
> +#include <Library/DebugLib.h>
> +#include <Library/HiiLib.h>
> +#include <Library/IoLib.h>
> +#include <Library/MemoryAllocationLib.h>
> +#include <Library/OemMiscLib.h>
> +#include <Library/PcdLib.h>
> +#include <Library/PrintLib.h>
> +#include <Library/UefiBootServicesTableLib.h>
> +#include <Library/UefiLib.h>
> +
> +extern UINT8 ProcessorSubClassStrings[];
> +
> +#define CACHE_SOCKETED_SHIFT 3
> +#define CACHE_LOCATION_SHIFT 5
> +#define CACHE_ENABLED_SHIFT 7
> +#define CACHE_OPERATION_MODE_SHIFT 8
> +
> +// Sets the HII variable `x` if `pcd` isn't empty
> +#define SET_HII_STRING_IF_PCD_NOT_EMPTY(pcd, x) \
> + x##Str = (CHAR16 *)PcdGetPtr (pcd); \
> + if (StrLen (x##Str) > 0) { \
> + HiiSetString (mHiiHandle, x, x##Str, NULL); \
> + } \
I am not a fan of preprocessor macros that require local variables
with magic names to exist.
Can this be rewritten as a helper function?
> +
> +typedef enum {
> + CacheModeWriteThrough = 0, ///< Cache is write-through
> + CacheModeWriteBack, ///< Cache is write-back
> + CacheModeVariesWithAddress, ///< Cache mode varies by address
> + CacheModeUnknown, ///< Cache mode is unknown
> + CacheModeMax
> +} CACHE_OPERATION_MODE;
> +
> +typedef enum {
> + CacheLocationInternal = 0, ///< Cache is internal to the processor
> + CacheLocationExternal, ///< Cache is external to the processor
> + CacheLocationReserved, ///< Reserved
> + CacheLocationUnknown, ///< Cache location is unknown
> + CacheLocationMax
> +} CACHE_LOCATION;
> +
> +EFI_HII_HANDLE mHiiHandle;
> +
> +EFI_SMBIOS_PROTOCOL *mSmbios;
> +
> +SMBIOS_TABLE_TYPE4 mSmbiosProcessorTableTemplate = {
> + { // Hdr
> + EFI_SMBIOS_TYPE_PROCESSOR_INFORMATION, // Type
> + sizeof (SMBIOS_TABLE_TYPE4), // Length
> + 0 // Handle
> + },
> + 1, // Socket
> + CentralProcessor, // ProcessorType
> + ProcessorFamilyIndicatorFamily2, // ProcessorFamily
> + 2, // ProcessorManufacture
> + { // ProcessorId
> + { // Signature
> + 0
> + },
> + { // FeatureFlags
> + 0
> + }
> + },
> + 3, // ProcessorVersion
> + { // Voltage
> + 0
> + },
> + 0, // ExternalClock
> + 0, // MaxSpeed
> + 0, // CurrentSpeed
> + 0, // Status
> + ProcessorUpgradeUnknown, // ProcessorUpgrade
> + 0xFFFF, // L1CacheHandle
> + 0xFFFF, // L2CacheHandle
> + 0xFFFF, // L3CacheHandle
> + 4, // SerialNumber
> + 5, // AssetTag
> + 6, // PartNumber
> + 0, // CoreCount
> + 0, //EnabledCoreCount
> + 0, // ThreadCount
> + 0, // ProcessorCharacteristics
> + ProcessorFamilyARM, // ProcessorFamily2
> + 0, // CoreCount2
> + 0, // EnabledCoreCount2
> + 0 // ThreadCount2
> +};
> +
> +
> +/** Fetches the specified processor's frequency in Hz.
> +
> + @param ProcessorNumber The processor number
> +
> + @return The clock frequency in MHz
> +
> +**/
> +UINT16
> +GetCpuFrequency (
> + IN UINT8 ProcessorNumber
> + )
> +{
> + return (UINT16)(OemGetCpuFreq (ProcessorNumber) / 1000 / 1000);
> +}
> +
> +/** Gets a description of the specified cache.
> +
> + @param[in] CacheLevel Zero-based cache level (e.g. L1 cache is 0)
> + @param[in] InstructionOrUnifiedCache Whether this is an instruction or unified
> + cache, or a data cache
> + @param[out] CacheSocketStr The description of the specified cache
> +
> + @return The number of Unicode characters in CacheSocketStr not including the
> + terminating NUL
> +**/
> +UINTN
> +GetCacheSocketStr (
> + IN UINT8 CacheLevel,
> + IN BOOLEAN InstructionOrUnifiedCache,
> + OUT CHAR16 *CacheSocketStr
> + )
> +{
> + UINTN CacheSocketStrLen;
> +
> + if (CacheLevel == CpuCacheL1
> + && InstructionOrUnifiedCache) {
> + CacheSocketStrLen = UnicodeSPrint (
> + CacheSocketStr,
> + SMBIOS_STRING_MAX_LENGTH - 1,
> + L"L%x Instruction Cache",
> + CacheLevel + 1);
> + } else if (CacheLevel == CpuCacheL1 && !InstructionOrUnifiedCache) {
> + CacheSocketStrLen = UnicodeSPrint (CacheSocketStr,
> + SMBIOS_STRING_MAX_LENGTH - 1,
> + L"L%x Data Cache",
> + CacheLevel + 1);
> + } else {
> + CacheSocketStrLen = UnicodeSPrint (CacheSocketStr,
> + SMBIOS_STRING_MAX_LENGTH - 1,
> + L"L%x Cache",
> + CacheLevel + 1);
> + }
> +
> + return CacheSocketStrLen;
> +}
> +
> +/** Fills in the Type 7 record with the cache architecture information
> + read from the CPU registers.
> +
> + @param[in] CacheLevel Cache level (e.g. L1)
> + @param[in] InstructionOrUnifiedCache Cache type is instruction or
> + unified, not data cache
> + @param[in] CcidxSupported Whether CCIDX is supported
> + @param[in] CacheType The type of cache supported at this cache level
> + @param[out] Type7Record The Type 7 record to fill in
> +
> +**/
> +VOID
> +SetCacheArchitectureInformation (
> + IN UINT8 CacheLevel,
> + IN BOOLEAN InstructionOrUnifiedCache,
> + IN BOOLEAN CcidxSupported,
> + IN CLIDR_CACHE_TYPE CacheType,
> + OUT SMBIOS_TABLE_TYPE7 *Type7Record
> + )
> +{
> + CSSELR_DATA Csselr;
> + CCSIDR_DATA Ccsidr;
> +#if defined(MDE_CPU_ARM)
> + CSSIDR2_DATA Ccsidr2;
> +#endif
This function is too long - and part of that is because it inlines
architecture-specific information with conditionals.
Please break out the architecture-specific aspects into generic
functions implemented separately in ARM/AArch64 source files.
> + UINT8 Associativity;
> + UINT32 CacheSize32;
> + UINT16 CacheSize16;
> + UINT64 CacheSize64;
> +
> + Csselr.Data = 0;
> + Csselr.Bits.Level = CacheLevel;
> +
> + if (InstructionOrUnifiedCache) {
> + if (CacheType == ClidrCacheTypeInstructionOnly ||
> + CacheType == ClidrCacheTypeSeparate) {
> +
> + Csselr.Bits.InD = CsselrCacheTypeInstruction;
> + Type7Record->SystemCacheType = CacheTypeInstruction;
> + } else {
> + Csselr.Bits.InD = CsselrCacheTypeDataOrUnified;
> + if (CacheType == ClidrCacheTypeDataOnly) {
> + Type7Record->SystemCacheType = CacheTypeData;
> + } else {
> + Type7Record->SystemCacheType = CacheTypeUnified;
> + }
> + }
> + } else {
> + Type7Record->SystemCacheType = CacheTypeData;
> + Csselr.Bits.InD = CsselrCacheTypeDataOrUnified;
> + }
> +
> + // Read the CCSIDR register to get the cache architecture
> + Ccsidr.Data = ReadCCSIDR (Csselr.Data);
> +
> + if (CcidxSupported) {
> +#if defined(MDE_CPU_ARM)
> + Ccsidr2.Data = ReadCCSIDR2 (Csselr.Data);
> + CacheSize64 = (UINT64)(1 << (Ccsidr.BitsCcidxAA32.LineSize + 4)) *
> + (Ccsidr.BitsCcidxAA32.Associativity + 1) *
> + (Ccsidr2.Bits.NumSets + 1);
> + Associativity = Ccsidr.BitsCcidxAA32.Associativity;
> +#else
> + CacheSize64 = (UINT64)(1 << (Ccsidr.BitsCcidxAA64.LineSize + 4)) *
> + (Ccsidr.BitsCcidxAA64.Associativity + 1) *
> + (Ccsidr.BitsCcidxAA64.NumSets + 1);
> + Associativity = Ccsidr.BitsCcidxAA64.Associativity;
> +#endif
> + } else {
> + CacheSize64 = (1 << (Ccsidr.BitsNonCcidx.LineSize + 4)) *
> + (Ccsidr.BitsNonCcidx.Associativity + 1) *
> + (Ccsidr.BitsNonCcidx.NumSets + 1);
> + Associativity = Ccsidr.BitsNonCcidx.Associativity;
> + }
> +
> + CacheSize64 /= 1024; // Minimum granularity is 1K
> +
> + // Encode the cache size into the format SMBIOS wants
> + if (CacheSize64 < MAX_INT16) {
> + CacheSize16 = CacheSize64;
> + CacheSize32 = CacheSize16;
> + } else if ((CacheSize64 / 64) < MAX_INT16) {
> + CacheSize16 = (1 << 15) | (CacheSize64 / 64);
> + CacheSize32 = CacheSize16;
> + } else {
> + if ((CacheSize64 / 1024) <= 2047) {
> + CacheSize32 = CacheSize64;
> + } else {
> + CacheSize32 = (1 << 31) | (CacheSize64 / 64);
> + }
> +
> + CacheSize16 = -1;
> + }
> +
> + Type7Record->Associativity = Associativity + 1;
> + Type7Record->MaximumCacheSize = CacheSize16;
> + Type7Record->InstalledSize = CacheSize16;
> + Type7Record->MaximumCacheSize2 = CacheSize32;
> + Type7Record->InstalledSize2 = CacheSize32;
> +
> + switch (Associativity + 1) {
> + case 2:
> + Type7Record->Associativity = CacheAssociativity2Way;
> + break;
> + case 4:
> + Type7Record->Associativity = CacheAssociativity4Way;
> + break;
> + case 8:
> + Type7Record->Associativity = CacheAssociativity8Way;
> + break;
> + case 16:
> + Type7Record->Associativity = CacheAssociativity16Way;
> + break;
> + case 12:
> + Type7Record->Associativity = CacheAssociativity12Way;
> + break;
> + case 24:
> + Type7Record->Associativity = CacheAssociativity24Way;
> + break;
> + case 32:
> + Type7Record->Associativity = CacheAssociativity32Way;
> + break;
> + case 48:
> + Type7Record->Associativity = CacheAssociativity48Way;
> + break;
> + case 64:
> + Type7Record->Associativity = CacheAssociativity64Way;
> + break;
> + case 20:
> + Type7Record->Associativity = CacheAssociativity20Way;
> + break;
> + default:
> + Type7Record->Associativity = CacheAssociativityOther;
> + break;
> + }
> +
> + Type7Record->CacheConfiguration = (CacheModeUnknown << CACHE_OPERATION_MODE_SHIFT) |
> + (1 << CACHE_ENABLED_SHIFT) |
> + (CacheLocationUnknown << CACHE_LOCATION_SHIFT) |
> + (0 << CACHE_SOCKETED_SHIFT) |
> + CacheLevel;
> +}
> +
> +
> +/** Allocates and initializes an SMBIOS_TABLE_TYPE7 structure
> +
> + @param[in] CacheLevel The cache level (L1-L7)
> + @param[in] InstructionOrUnifiedCache The cache type is instruction or
> + unified, not a data cache.
> +
> + @return A pointer to the Type 7 structure. Returns NULL on failure.
> +
> +**/
> +SMBIOS_TABLE_TYPE7 *
> +AllocateAndInitCacheInformation (
> + IN UINT8 CacheLevel,
> + IN BOOLEAN InstructionOrUnifiedCache
> + )
> +{
> + SMBIOS_TABLE_TYPE7 *Type7Record;
> + EFI_STRING CacheSocketStr;
> + UINTN CacheSocketStrLen;
> + UINTN StringBufferSize;
> + CHAR8 *OptionalStrStart;
> + UINTN TableSize;
> +
> + // Allocate and fetch the cache description
> + StringBufferSize = sizeof (CHAR16) * SMBIOS_STRING_MAX_LENGTH;
> + CacheSocketStr = AllocateZeroPool (StringBufferSize);
> + if (CacheSocketStr == NULL) {
> + return NULL;
> + }
> +
> + CacheSocketStrLen = GetCacheSocketStr (CacheLevel,
> + InstructionOrUnifiedCache,
> + CacheSocketStr);
> +
> + TableSize = sizeof (SMBIOS_TABLE_TYPE7) + CacheSocketStrLen + 1 + 1;
> + Type7Record = AllocateZeroPool (TableSize);
> + if (Type7Record == NULL) {
> + FreePool(CacheSocketStr);
> + return NULL;
> + }
> +
> + Type7Record->Hdr.Type = EFI_SMBIOS_TYPE_CACHE_INFORMATION;
> + Type7Record->Hdr.Length = sizeof (SMBIOS_TABLE_TYPE7);
> + Type7Record->Hdr.Handle = SMBIOS_HANDLE_PI_RESERVED;
> +
> + Type7Record->SocketDesignation = 1;
> +
> + Type7Record->SupportedSRAMType.Unknown = 1;
> + Type7Record->CurrentSRAMType.Unknown = 1;
> + Type7Record->CacheSpeed = 0;
> + Type7Record->ErrorCorrectionType = CacheErrorUnknown;
> +
> + OptionalStrStart = (CHAR8 *)(Type7Record + 1);
> + UnicodeStrToAsciiStrS (CacheSocketStr, OptionalStrStart, CacheSocketStrLen + 1);
> + FreePool (CacheSocketStr);
> +
> + return Type7Record;
> +}
> +
> +
> +/**
> + Add Type 7 SMBIOS Record for Cache Information.
> +
> + @param[in] ProcessorIndex Processor number of specified processor.
> + @param[out] L1CacheHandle Pointer to the handle of the L1 Cache SMBIOS record.
> + @param[out] L2CacheHandle Pointer to the handle of the L2 Cache SMBIOS record.
> + @param[out] L3CacheHandle Pointer to the handle of the L3 Cache SMBIOS record.
> +
> +**/
> +VOID
> +AddSmbiosCacheTypeTable (
> + IN UINTN ProcessorIndex,
> + OUT EFI_SMBIOS_HANDLE *L1CacheHandle,
> + OUT EFI_SMBIOS_HANDLE *L2CacheHandle,
> + OUT EFI_SMBIOS_HANDLE *L3CacheHandle
> + )
> +{
> + EFI_STATUS Status;
> + SMBIOS_TABLE_TYPE7 *Type7Record;
> + EFI_SMBIOS_HANDLE SmbiosHandle;
> + UINT8 CacheLevel;
> + UINT8 CacheSubLevel;
> + CLIDR_DATA Clidr;
> + BOOLEAN CcidxSupported;
> + UINT8 MaxCacheLevel;
> + CLIDR_CACHE_TYPE CacheType;
> +
> + Status = EFI_SUCCESS;
> +
> + MaxCacheLevel = 0;
> +
> + // Read the CLIDR register to find out what caches are present.
> + Clidr.Data = ReadCLIDR ();
> +
> + // Get the cache type for the L1 cache. If it's 0, there are no caches.
> + if (CLIDR_GET_CACHE_TYPE (Clidr.Data, 0) == ClidrCacheTypeNone) {
> + return;
> + }
> +
> + for (CacheLevel = 1; CacheLevel < MAX_ARM_CACHE_LEVEL; CacheLevel++) {
> + if (CLIDR_GET_CACHE_TYPE (Clidr.Data, CacheLevel) == ClidrCacheTypeNone) {
> + MaxCacheLevel = CacheLevel;
> + break;
> + }
> + }
> +
> + CcidxSupported = ArmHasCcidx ();
> +
> + for (CacheLevel = 0; CacheLevel < MaxCacheLevel; CacheLevel++) {
> + Type7Record = NULL;
> +
> + CacheType = CLIDR_GET_CACHE_TYPE (Clidr.Data, CacheLevel);
> +
> + // At each level of cache, we can have a single type (unified, instruction or data),
> + // or two types - separate data and instruction caches. If we have separate
> + // instruction and data caches, then on the first iteration (CacheSubLevel = 0)
> + // process the instruction cache.
> + for (CacheSubLevel = 0; CacheSubLevel <= 1; CacheSubLevel++) {
> + // If there's no separate data/instruction cache, skip the second iteration
> + if (CacheSubLevel > 0 && CacheType != ClidrCacheTypeSeparate) {
> + continue;
> + }
> +
> + Type7Record = AllocateAndInitCacheInformation (CacheLevel,
> + (CacheSubLevel == 0));
> + if (Type7Record == NULL) {
> + continue;
> + }
> +
> + SetCacheArchitectureInformation(CacheLevel,
> + (CacheSubLevel == 0),
> + CcidxSupported,
Again, please separate the architectural code into architectural
source files.
> + CacheType,
> + Type7Record);
> +
> + // Allow the platform to fill in other information such as speed, SRAM type etc.
> + if (!OemGetCacheInformation (ProcessorIndex, CacheLevel,
> + (CacheSubLevel == 0), Type7Record)) {
> + continue;
> + }
> +
> + SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
> + // Finally, install the table
> + Status = mSmbios->Add (mSmbios, NULL, &SmbiosHandle,
> + (EFI_SMBIOS_TABLE_HEADER *)Type7Record);
> + if (EFI_ERROR (Status)) {
> + continue;
> + }
> +
> + // Config L1/L2/L3 Cache Handle
> + switch (CacheLevel) {
> + case CpuCacheL1:
> + *L1CacheHandle = SmbiosHandle;
> + break;
> + case CpuCacheL2:
> + *L2CacheHandle = SmbiosHandle;
> + break;
> + case CpuCacheL3:
> + *L3CacheHandle = SmbiosHandle;
> + break;
> + default:
> + break;
Weird indentation.
> + }
> + }
> + }
> +}
> +
> +/** Fills in the Type 4 CPU processor ID field.
> +
> + @param[out] Type4Record The SMBIOS Type 4 record to fill in
> +
> +**/
> +VOID
> +SetProcessorIdField (
> + OUT SMBIOS_TABLE_TYPE4 *Type4Record
> +)
> +{
> + ARM_SMC_ARGS Args;
> + INT32 SmcCallStatus;
> + INT32 Jep106Code;
> + INT32 SocRevision;
> + BOOLEAN Arm64SocIdSupported;
> + UINT64 *ProcessorId;
> + PROCESSOR_CHARACTERISTIC_FLAGS *ProcessorCharacteristicFlags;
> +
> + Arm64SocIdSupported = FALSE;
> +
> + Args.Arg0 = SMCCC_VERSION;
> + ArmCallSmc (&Args);
> + SmcCallStatus = (INT32)Args.Arg0;
> +
> + if (SmcCallStatus < 0 || (SmcCallStatus >> 16) >= 1) {
> + Args.Arg0 = SMCCC_ARCH_FEATURES;
> + Args.Arg1 = SMCCC_ARCH_SOC_ID;
> + ArmCallSmc (&Args);
> +
> + if (Args.Arg0 >= 0) {
> + ProcessorCharacteristicFlags =
> + (PROCESSOR_CHARACTERISTIC_FLAGS*)&Type4Record->ProcessorCharacteristics;
> + Args.Arg0 = SMCCC_ARCH_SOC_ID;
> + Args.Arg1 = 0;
> + ArmCallSmc (&Args);
> + SmcCallStatus = (int)Args.Arg0;
> +
> + if (SmcCallStatus >= 0) {
> + Arm64SocIdSupported = TRUE;
> + ProcessorCharacteristicFlags->ProcessorArm64SocId = 1;
> + Jep106Code = (int)Args.Arg0;
> + } else {
> + ProcessorCharacteristicFlags->ProcessorArm64SocId = 0;
> + }
> + Args.Arg0 = SMCCC_ARCH_SOC_ID;
> + Args.Arg1 = 1;
> + ArmCallSmc (&Args);
> + SmcCallStatus = (int)Args.Arg0;
> +
> + if (SmcCallStatus >= 0) {
> + SocRevision = (int)Args.Arg0;
> + }
> + }
> + }
> +
> + ProcessorId = (UINT64 *)&Type4Record->ProcessorId;
> +
> + if (Arm64SocIdSupported) {
> + *ProcessorId = ((UINT64)Jep106Code << 32) | SocRevision;
> + } else {
> + *ProcessorId = ArmReadMidr ();
> + }
> +}
> +
> +
> +/** Allocates a Type 4 Processor Information structure and sets the
> + strings following the data fields.
> +
> + @param[out] Type4Record The Type 4 structure to allocate and initialize
> + @param[in] ProcessorIndex The index of the processor socket
> + @param[in] Populated Whether the specified processor socket is
> + populated.
> +
> + @retval EFI_SUCCESS The Type 4 structure was successfully
> + allocated and the strings initialized.
> + @retval EFI_OUT_OF_RESOURCES Could not allocate memory needed.
> +**/
> +EFI_STATUS
> +AllocateType4AndSetProcessorInformationStrings (
> + SMBIOS_TABLE_TYPE4 **Type4Record,
> + UINT8 ProcessorIndex,
> + BOOLEAN Populated
> + )
> +{
> + EFI_STATUS Status;
> + EFI_STRING_ID ProcessorManu;
> + EFI_STRING_ID ProcessorVersion;
> + EFI_STRING_ID SerialNumber;
> + EFI_STRING_ID AssetTag;
> + EFI_STRING_ID PartNumber;
> + EFI_STRING ProcessorSocketStr;
> + EFI_STRING ProcessorManuStr;
> + EFI_STRING ProcessorVersionStr;
> + EFI_STRING SerialNumberStr;
> + EFI_STRING AssetTagStr;
> + EFI_STRING PartNumberStr;
> + CHAR8 *OptionalStrStart;
> + CHAR8 *StrStart;
> + UINTN ProcessorSocketStrLen;
> + UINTN ProcessorManuStrLen;
> + UINTN ProcessorVersionStrLen;
> + UINTN SerialNumberStrLen;
> + UINTN AssetTagStrLen;
> + UINTN PartNumberStrLen;
> + UINTN TotalSize;
> + UINTN StringBufferSize;
> +
> + Status = EFI_SUCCESS;
> +
> + ProcessorManuStr = NULL;
> + ProcessorVersionStr = NULL;
> + SerialNumberStr = NULL;
> + AssetTagStr = NULL;
> + PartNumberStr = NULL;
> +
> + if (Populated) {
> + ProcessorManu = STRING_TOKEN (STR_PROCESSOR_MANUFACTURE);
> + ProcessorVersion = STRING_TOKEN (STR_PROCESSOR_VERSION);
> + SerialNumber = STRING_TOKEN (STR_PROCESSOR_SERIAL_NUMBER);
> + AssetTag = STRING_TOKEN (STR_PROCESSOR_ASSET_TAG);
> + PartNumber = STRING_TOKEN (STR_PROCESSOR_PART_NUMBER);
> +
> + SET_HII_STRING_IF_PCD_NOT_EMPTY(PcdProcessorManufacturer, ProcessorManu);
> + SET_HII_STRING_IF_PCD_NOT_EMPTY(PcdProcessorVersion, ProcessorVersion);
> + SET_HII_STRING_IF_PCD_NOT_EMPTY(PcdProcessorSerialNumber, SerialNumber);
> + SET_HII_STRING_IF_PCD_NOT_EMPTY(PcdProcessorAssetTag, AssetTag);
> + SET_HII_STRING_IF_PCD_NOT_EMPTY(PcdProcessorPartNumber, PartNumber);
> + } else {
> + ProcessorManu = STRING_TOKEN (STR_PROCESSOR_UNKNOWN);
> + ProcessorVersion = STRING_TOKEN (STR_PROCESSOR_UNKNOWN);
> + SerialNumber = STRING_TOKEN (STR_PROCESSOR_UNKNOWN);
> + AssetTag = STRING_TOKEN (STR_PROCESSOR_UNKNOWN);
> + PartNumber = STRING_TOKEN (STR_PROCESSOR_UNKNOWN);
> + }
> +
> + // Processor Socket Designation
> + StringBufferSize = sizeof (CHAR16) * SMBIOS_STRING_MAX_LENGTH;
> + ProcessorSocketStr = AllocateZeroPool (StringBufferSize);
> + if (ProcessorSocketStr == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + ProcessorSocketStrLen = UnicodeSPrint (ProcessorSocketStr, StringBufferSize,
> + L"CPU%02d", ProcessorIndex + 1);
> +
> + // Processor Manufacture
> + ProcessorManuStr = HiiGetPackageString (&gEfiCallerIdGuid, ProcessorManu, NULL);
> + ProcessorManuStrLen = StrLen (ProcessorManuStr);
> +
> + // Processor Version
> + ProcessorVersionStr = HiiGetPackageString (&gEfiCallerIdGuid, ProcessorVersion, NULL);
> + ProcessorVersionStrLen = StrLen (ProcessorVersionStr);
> +
> + // Serial Number
> + SerialNumberStr = HiiGetPackageString (&gEfiCallerIdGuid, SerialNumber, NULL);
> + SerialNumberStrLen = StrLen (SerialNumberStr);
> +
> + // Asset Tag
> + AssetTagStr = HiiGetPackageString (&gEfiCallerIdGuid, AssetTag, NULL);
> + AssetTagStrLen = StrLen (AssetTagStr);
> +
> + // Part Number
> + PartNumberStr = HiiGetPackageString (&gEfiCallerIdGuid, PartNumber, NULL);
> + PartNumberStrLen = StrLen (PartNumberStr);
> +
> + TotalSize = sizeof (SMBIOS_TABLE_TYPE4) +
> + ProcessorSocketStrLen + 1 +
> + ProcessorManuStrLen + 1 +
> + ProcessorVersionStrLen + 1 +
> + SerialNumberStrLen + 1 +
> + AssetTagStrLen + 1 +
> + PartNumberStrLen + 1 + 1;
> +
> + *Type4Record = AllocateZeroPool (TotalSize);
> + if (*Type4Record == NULL) {
> + Status = EFI_OUT_OF_RESOURCES;
> + goto Exit;
> + }
> +
> + CopyMem (*Type4Record, &mSmbiosProcessorTableTemplate, sizeof (SMBIOS_TABLE_TYPE4));
> +
> + OptionalStrStart = (CHAR8 *)(*Type4Record + 1);
> + UnicodeStrToAsciiStrS (
> + ProcessorSocketStr,
> + OptionalStrStart,
> + ProcessorSocketStrLen + 1
> + );
> +
> + StrStart = OptionalStrStart + ProcessorSocketStrLen + 1;
> + UnicodeStrToAsciiStrS (
> + ProcessorManuStr,
> + StrStart,
> + ProcessorManuStrLen + 1
> + );
> +
> + StrStart += ProcessorManuStrLen + 1;
> + UnicodeStrToAsciiStrS (
> + ProcessorVersionStr,
> + StrStart,
> + ProcessorVersionStrLen + 1
> + );
> +
> + StrStart += ProcessorVersionStrLen + 1;
> + UnicodeStrToAsciiStrS (
> + SerialNumberStr,
> + StrStart,
> + SerialNumberStrLen + 1
> + );
> +
> + StrStart += SerialNumberStrLen + 1;
> + UnicodeStrToAsciiStrS (
> + AssetTagStr,
> + StrStart,
> + AssetTagStrLen + 1
> + );
> +
> + StrStart += AssetTagStrLen + 1;
> + UnicodeStrToAsciiStrS (
> + PartNumberStr,
> + StrStart,
> + PartNumberStrLen + 1
> + );
> +
> +Exit:
> + FreePool (ProcessorSocketStr);
> + FreePool (ProcessorManuStr);
> + FreePool (ProcessorVersionStr);
> + FreePool (SerialNumberStr);
> + FreePool (AssetTagStr);
> + FreePool (PartNumberStr);
> +
> + return Status;
> +}
> +
> +/**
> + Add Type 4 SMBIOS Record for Processor Information.
> +
> + @param[in] ProcessorIndex Processor index of specified processor.
> +
> +**/
> +EFI_STATUS
> +AddSmbiosProcessorTypeTable (
> + IN UINTN ProcessorIndex
> + )
> +{
> + EFI_STATUS Status;
> + SMBIOS_TABLE_TYPE4 *Type4Record;
> + EFI_SMBIOS_HANDLE SmbiosHandle;
> + EFI_SMBIOS_HANDLE L1CacheHandle;
> + EFI_SMBIOS_HANDLE L2CacheHandle;
> + EFI_SMBIOS_HANDLE L3CacheHandle;
> + UINT8 *LegacyVoltage;
> + PROCESSOR_STATUS_DATA ProcessorStatus;
> + OEM_MISC_PROCESSOR_DATA MiscProcessorData;
> + BOOLEAN SocketPopulated;
> + UINTN MainIdRegister;
> +
> + Type4Record = NULL;
> +
> + MiscProcessorData.Voltage = 0;
> + MiscProcessorData.CurrentSpeed = 0;
> + MiscProcessorData.CoreCount = 0;
> + MiscProcessorData.CoresEnabled = 0;
> + MiscProcessorData.ThreadCount = 0;
> + MiscProcessorData.MaxSpeed = 0;
> + L1CacheHandle = 0xFFFF;
> + L2CacheHandle = 0xFFFF;
> + L3CacheHandle = 0xFFFF;
> +
> + SocketPopulated = OemIsSocketPresent(ProcessorIndex);
> +
> + Status = AllocateType4AndSetProcessorInformationStrings (
> + &Type4Record,
> + ProcessorIndex,
> + SocketPopulated
> + );
> + if (EFI_ERROR (Status)) {
> + return Status;
> + }
> +
> + OemGetProcessorInformation (ProcessorIndex,
> + &ProcessorStatus,
> + (PROCESSOR_CHARACTERISTIC_FLAGS*)
> + &Type4Record->ProcessorCharacteristics,
> + &MiscProcessorData);
> +
> + if (SocketPopulated) {
> + AddSmbiosCacheTypeTable (ProcessorIndex, &L1CacheHandle,
> + &L2CacheHandle, &L3CacheHandle);
> + }
> +
> + LegacyVoltage = (UINT8*)&Type4Record->Voltage;
> +
> + *LegacyVoltage = MiscProcessorData.Voltage;
> + Type4Record->CurrentSpeed = MiscProcessorData.CurrentSpeed;
> + Type4Record->MaxSpeed = MiscProcessorData.MaxSpeed;
> + Type4Record->Status = ProcessorStatus.Data;
> + Type4Record->L1CacheHandle = L1CacheHandle;
> + Type4Record->L2CacheHandle = L2CacheHandle;
> + Type4Record->L3CacheHandle = L3CacheHandle;
> + Type4Record->CoreCount = MiscProcessorData.CoreCount;
> + Type4Record->CoreCount2 = MiscProcessorData.CoreCount;
> + Type4Record->EnabledCoreCount = MiscProcessorData.CoresEnabled;
> + Type4Record->EnabledCoreCount2 = MiscProcessorData.CoresEnabled;
> + Type4Record->ThreadCount = MiscProcessorData.ThreadCount;
> + Type4Record->ThreadCount2 = MiscProcessorData.ThreadCount;
> +
> + Type4Record->CurrentSpeed = GetCpuFrequency (ProcessorIndex);
> + Type4Record->ExternalClock = (UINT16)(ArmReadCntFrq () / 1000 / 1000);
Architectural function.
If some functions are high-level enough to use the same for
ARM/AArch64 (like this one), they can certainly coexist in an
ArmCommon.c, ArmHelpers.c or similar.
/
Leif
> +
> + SetProcessorIdField (Type4Record);
> +
> + MainIdRegister = ArmReadMidr ();
> + if (((MainIdRegister >> 16) & 0xF) < 8) {
> + Type4Record->ProcessorFamily2 = ProcessorFamilyARM;
> + } else {
> + if (sizeof (VOID*) == 4) {
> + Type4Record->ProcessorFamily2 = ProcessorFamilyARMv7;
> + } else {
> + Type4Record->ProcessorFamily2 = ProcessorFamilyARMv8;
> + }
> + }
> +
> + SmbiosHandle = SMBIOS_HANDLE_PI_RESERVED;
> + Status = mSmbios->Add (mSmbios, NULL, &SmbiosHandle,
> + (EFI_SMBIOS_TABLE_HEADER *)Type4Record);
> +
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "[%a]:[%dL] Smbios Type04 Table Log Failed! %r \n",
> + __FUNCTION__, __LINE__, Status));
> + }
> + FreePool (Type4Record);
> +
> + return Status;
> +}
> +
> +/**
> + Standard EFI driver point.
> +
> + @param ImageHandle Handle for the image of this driver
> + @param SystemTable Pointer to the EFI System Table
> +
> + @retval EFI_SUCCESS The data was successfully stored.
> +
> +**/
> +EFI_STATUS
> +EFIAPI
> +ProcessorSubClassEntryPoint(
> + IN EFI_HANDLE ImageHandle,
> + IN EFI_SYSTEM_TABLE *SystemTable
> + )
> +{
> + EFI_STATUS Status;
> + UINT32 SocketIndex;
> +
> + //
> + // Locate dependent protocols
> + //
> + Status = gBS->LocateProtocol (&gEfiSmbiosProtocolGuid, NULL, (VOID**)&mSmbios);
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "Could not locate SMBIOS protocol. %r\n", Status));
> + return Status;
> + }
> +
> + //
> + // Add our default strings to the HII database. They will be modified later.
> + //
> + mHiiHandle = HiiAddPackages (&gEfiCallerIdGuid,
> + NULL,
> + ProcessorSubClassStrings,
> + NULL,
> + NULL
> + );
> + if (mHiiHandle == NULL) {
> + return EFI_OUT_OF_RESOURCES;
> + }
> +
> + //
> + // Add SMBIOS tables for populated sockets.
> + //
> + for (SocketIndex = 0; SocketIndex < OemGetProcessorMaxSockets(); SocketIndex++) {
> + Status = AddSmbiosProcessorTypeTable (SocketIndex);
> + if (EFI_ERROR (Status)) {
> + DEBUG ((DEBUG_ERROR, "Add Processor Type Table Failed! %r.\n", Status));
> + return Status;
> + }
> + }
> +
> + return Status;
> +}
> diff --git a/ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClassStrings.uni b/ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClassStrings.uni
> new file mode 100644
> index 000000000000..22b3c64d9fe2
> --- /dev/null
> +++ b/ArmPkg/Universal/Smbios/ProcessorSubClassDxe/ProcessorSubClassStrings.uni
> @@ -0,0 +1,24 @@
> +/** @file
> + SMBIOS Type 4 strings
> +
> + 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
> +
> +**/
> +
> +/=#
> +
> +#langdef en-US "English"
> +
> +//
> +// Processor Information
> +//
> +#string STR_PROCESSOR_SOCKET_DESIGNATION #language en-US "Not Specified"
> +#string STR_PROCESSOR_MANUFACTURE #language en-US "Not Specified"
> +#string STR_PROCESSOR_VERSION #language en-US "Not Specified"
> +#string STR_PROCESSOR_SERIAL_NUMBER #language en-US "Not Specified"
> +#string STR_PROCESSOR_ASSET_TAG #language en-US "Not Specified"
> +#string STR_PROCESSOR_PART_NUMBER #language en-US "Not Specified"
> +#string STR_PROCESSOR_UNKNOWN #language en-US "Unknown"
> --
> 2.26.2
>
next prev parent reply other threads:[~2021-01-25 19:04 UTC|newest]
Thread overview: 52+ messages / expand[flat|nested] mbox.gz Atom feed top
2021-01-14 16:36 [PATCH v6 00/22] ArmPkg,MdePkg: Add Universal/Smbios, and related changes Rebecca Cran
2021-01-14 16:36 ` [PATCH v6 01/22] BaseTools: Fix the build report crash issue Rebecca Cran
2021-01-14 16:36 ` [PATCH v6 02/22] ArmPkg: Add ARM SMC Architecture functions to ArmStdSmc.h Rebecca Cran
2021-01-15 20:55 ` [edk2-devel] " Samer El-Haj-Mahmoud
2021-01-14 16:36 ` [PATCH v6 03/22] MdePkg: Update IndustryStandard/SmBios.h with processor status data Rebecca Cran
2021-01-15 0:46 ` 回复: " gaoliming
2021-01-15 20:54 ` [edk2-devel] " Samer El-Haj-Mahmoud
2021-01-14 16:36 ` [PATCH v6 04/22] ArmPkg: Add register encoding definition for MMFR2 Rebecca Cran
2021-01-14 16:36 ` [PATCH v6 05/22] ArmPkg: Add helper to read the Memory Model Features Register 2 Rebecca Cran
2021-01-26 9:35 ` [edk2-devel] " Philippe Mathieu-Daudé
2021-01-14 16:36 ` [PATCH v6 06/22] ArmPkg: Add helper function to read the Memory Model Feature Register 4 Rebecca Cran
2021-01-14 16:36 ` [PATCH v6 07/22] ArmPkg: Fix the return type of the ReadCCSIDR function Rebecca Cran
2021-01-14 16:36 ` [PATCH v6 08/22] ArmPkg: Update ArmLibPrivate.h with cache register definitions Rebecca Cran
2021-01-14 16:36 ` [PATCH v6 09/22] ArmPkg: Add definition of the maximum cache level in ARMv8-A Rebecca Cran
2021-01-14 16:36 ` [PATCH v6 10/22] ArmPkg: Add helper to read CCIDX status Rebecca Cran
2021-01-14 16:36 ` [PATCH v6 11/22] ArmPkg: Add helper to read the CCSIDR2 register Rebecca Cran
2021-01-18 8:51 ` Sami Mujawar
2021-01-14 16:36 ` [PATCH v6 12/22] ArmPkg: Add Library/OemMiscLib.h Rebecca Cran
2021-01-15 20:56 ` [edk2-devel] " Samer El-Haj-Mahmoud
2021-01-25 17:51 ` Leif Lindholm
2021-01-14 16:36 ` [PATCH v6 13/22] ArmPkg: Add Universal/Smbios/OemMiscLibNull Rebecca Cran
2021-01-15 20:57 ` [edk2-devel] " Samer El-Haj-Mahmoud
2021-01-25 17:51 ` Leif Lindholm
2021-01-14 16:36 ` [PATCH v6 14/22] ArmPkg: Add Universal/Smbios/ProcessorSubClassDxe Rebecca Cran
2021-01-25 19:04 ` Leif Lindholm [this message]
2021-01-28 4:53 ` Rebecca Cran
2021-01-28 11:32 ` Leif Lindholm
2021-01-28 11:45 ` [edk2-devel] " Laszlo Ersek
2021-01-14 16:36 ` [PATCH v6 15/22] ArmPkg: Add Universal/Smbios/SmbiosMiscDxe/Type00 Rebecca Cran
2021-01-15 21:09 ` [edk2-devel] " Samer El-Haj-Mahmoud
2021-01-25 19:08 ` Leif Lindholm
2021-01-14 16:36 ` [PATCH v6 16/22] ArmPkg: Add Universal/Smbios/SmbiosMiscDxe/Type01 Rebecca Cran
2021-01-15 21:09 ` [edk2-devel] " Samer El-Haj-Mahmoud
2021-01-25 19:09 ` Leif Lindholm
2021-01-14 16:36 ` [PATCH v6 17/22] ArmPkg: Add Universal/Smbios/SmbiosMiscDxe/Type02 Rebecca Cran
2021-01-25 19:10 ` Leif Lindholm
2021-01-25 19:23 ` [edk2-devel] " Samer El-Haj-Mahmoud
2021-01-14 16:36 ` [PATCH v6 18/22] ArmPkg: Add Universal/Smbios/SmbiosMiscDxe/Type03 Rebecca Cran
2021-01-25 19:11 ` Leif Lindholm
2021-01-25 19:23 ` [edk2-devel] " Samer El-Haj-Mahmoud
2021-01-14 16:36 ` [PATCH v6 19/22] ArmPkg: Add Universal/Smbios/SmbiosMiscDxe/Type13 Rebecca Cran
2021-01-25 19:11 ` Leif Lindholm
2021-01-14 16:36 ` [PATCH v6 20/22] ArmPkg: Add Universal/Smbios/SmbiosMiscDxe/Type32 Rebecca Cran
2021-01-15 21:10 ` [edk2-devel] " Samer El-Haj-Mahmoud
2021-01-25 19:20 ` Rebecca Cran
2021-01-25 19:23 ` Samer El-Haj-Mahmoud
2021-01-25 19:12 ` Leif Lindholm
2021-01-14 16:36 ` [PATCH v6 21/22] ArmPkg: Add SMBIOS PCDs to ArmPkg.dec Rebecca Cran
2021-01-14 16:36 ` [PATCH v6 22/22] ArmPkg: Add Universal/Smbios/SmbiosMiscDxe Rebecca Cran
2021-01-25 19:15 ` Leif Lindholm
2021-01-25 19:23 ` [edk2-devel] " Samer El-Haj-Mahmoud
[not found] ` <165A268B3006F467.32022@groups.io>
2021-01-14 16:40 ` [edk2-devel] [PATCH v6 01/22] BaseTools: Fix the build report crash issue Rebecca Cran
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-list from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20210125190417.GE1664@vanye \
--to=devel@edk2.groups.io \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox