2018-12-15 13:23:59 +01:00
|
|
|
/** @file
|
|
|
|
MADT Table Generator
|
|
|
|
|
|
|
|
Copyright (c) 2017 - 2019, ARM Limited. All rights reserved.
|
2019-04-04 01:03:32 +02:00
|
|
|
SPDX-License-Identifier: BSD-2-Clause-Patent
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
@par Reference(s):
|
2019-04-09 15:44:37 +02:00
|
|
|
- ACPI 6.3 Specification - January 2019
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
**/
|
|
|
|
|
|
|
|
#include <Library/AcpiLib.h>
|
|
|
|
#include <Library/DebugLib.h>
|
|
|
|
#include <Library/MemoryAllocationLib.h>
|
|
|
|
#include <Protocol/AcpiTable.h>
|
|
|
|
|
|
|
|
// Module specific include files.
|
|
|
|
#include <AcpiTableGenerator.h>
|
|
|
|
#include <ConfigurationManagerObject.h>
|
|
|
|
#include <ConfigurationManagerHelper.h>
|
|
|
|
#include <Library/TableHelperLib.h>
|
|
|
|
#include <Protocol/ConfigurationManagerProtocol.h>
|
|
|
|
|
|
|
|
/** ARM standard MADT Generator
|
|
|
|
|
|
|
|
Requirements:
|
|
|
|
The following Configuration Manager Object(s) are required by
|
|
|
|
this Generator:
|
|
|
|
- EArmObjGicCInfo
|
|
|
|
- EArmObjGicDInfo
|
|
|
|
- EArmObjGicMsiFrameInfo (OPTIONAL)
|
|
|
|
- EArmObjGicRedistributorInfo (OPTIONAL)
|
|
|
|
- EArmObjGicItsInfo (OPTIONAL)
|
|
|
|
*/
|
|
|
|
|
|
|
|
/** This macro expands to a function that retrieves the GIC
|
|
|
|
CPU interface Information from the Configuration Manager.
|
|
|
|
*/
|
|
|
|
GET_OBJECT_LIST (
|
|
|
|
EObjNameSpaceArm,
|
|
|
|
EArmObjGicCInfo,
|
|
|
|
CM_ARM_GICC_INFO
|
|
|
|
);
|
|
|
|
|
|
|
|
/** This macro expands to a function that retrieves the GIC
|
|
|
|
Distributor Information from the Configuration Manager.
|
|
|
|
*/
|
|
|
|
|
|
|
|
GET_OBJECT_LIST (
|
|
|
|
EObjNameSpaceArm,
|
|
|
|
EArmObjGicDInfo,
|
|
|
|
CM_ARM_GICD_INFO
|
|
|
|
);
|
|
|
|
|
|
|
|
/** This macro expands to a function that retrieves the GIC
|
|
|
|
MSI Frame Information from the Configuration Manager.
|
|
|
|
*/
|
|
|
|
GET_OBJECT_LIST (
|
|
|
|
EObjNameSpaceArm,
|
|
|
|
EArmObjGicMsiFrameInfo,
|
|
|
|
CM_ARM_GIC_MSI_FRAME_INFO
|
|
|
|
);
|
|
|
|
|
|
|
|
/** This macro expands to a function that retrieves the GIC
|
|
|
|
Redistributor Information from the Configuration Manager.
|
|
|
|
*/
|
|
|
|
|
|
|
|
GET_OBJECT_LIST (
|
|
|
|
EObjNameSpaceArm,
|
|
|
|
EArmObjGicRedistributorInfo,
|
|
|
|
CM_ARM_GIC_REDIST_INFO
|
|
|
|
);
|
|
|
|
|
|
|
|
/** This macro expands to a function that retrieves the GIC
|
|
|
|
Interrupt Translation Service Information from the
|
|
|
|
Configuration Manager.
|
|
|
|
*/
|
|
|
|
GET_OBJECT_LIST (
|
|
|
|
EObjNameSpaceArm,
|
|
|
|
EArmObjGicItsInfo,
|
|
|
|
CM_ARM_GIC_ITS_INFO
|
|
|
|
);
|
|
|
|
|
|
|
|
/** This function updates the GIC CPU Interface Information in the
|
2019-04-09 15:44:37 +02:00
|
|
|
EFI_ACPI_6_3_GIC_STRUCTURE structure.
|
2018-12-15 13:23:59 +01:00
|
|
|
|
2019-04-09 15:44:37 +02:00
|
|
|
@param [in] Gicc Pointer to GIC CPU Interface structure.
|
|
|
|
@param [in] GicCInfo Pointer to the GIC CPU Interface Information.
|
|
|
|
@param [in] MadtRev MADT table revision.
|
2018-12-15 13:23:59 +01:00
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
VOID
|
|
|
|
AddGICC (
|
2019-04-09 15:44:37 +02:00
|
|
|
IN EFI_ACPI_6_3_GIC_STRUCTURE * CONST Gicc,
|
|
|
|
IN CONST CM_ARM_GICC_INFO * CONST GicCInfo,
|
|
|
|
IN CONST UINT8 MadtRev
|
2018-12-15 13:23:59 +01:00
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT (Gicc != NULL);
|
|
|
|
ASSERT (GicCInfo != NULL);
|
|
|
|
|
|
|
|
// UINT8 Type
|
2019-04-09 15:44:37 +02:00
|
|
|
Gicc->Type = EFI_ACPI_6_3_GIC;
|
2018-12-15 13:23:59 +01:00
|
|
|
// UINT8 Length
|
2019-04-09 15:44:37 +02:00
|
|
|
Gicc->Length = sizeof (EFI_ACPI_6_3_GIC_STRUCTURE);
|
2018-12-15 13:23:59 +01:00
|
|
|
// UINT16 Reserved
|
|
|
|
Gicc->Reserved = EFI_ACPI_RESERVED_WORD;
|
|
|
|
|
|
|
|
// UINT32 CPUInterfaceNumber
|
|
|
|
Gicc->CPUInterfaceNumber = GicCInfo->CPUInterfaceNumber;
|
|
|
|
// UINT32 AcpiProcessorUid
|
|
|
|
Gicc->AcpiProcessorUid = GicCInfo->AcpiProcessorUid;
|
|
|
|
// UINT32 Flags
|
|
|
|
Gicc->Flags = GicCInfo->Flags;
|
|
|
|
// UINT32 ParkingProtocolVersion
|
|
|
|
Gicc->ParkingProtocolVersion = GicCInfo->ParkingProtocolVersion;
|
|
|
|
// UINT32 PerformanceInterruptGsiv
|
|
|
|
Gicc->PerformanceInterruptGsiv = GicCInfo->PerformanceInterruptGsiv;
|
|
|
|
// UINT64 ParkedAddress
|
|
|
|
Gicc->ParkedAddress = GicCInfo->ParkedAddress;
|
|
|
|
|
|
|
|
// UINT64 PhysicalBaseAddress
|
|
|
|
Gicc->PhysicalBaseAddress = GicCInfo->PhysicalBaseAddress;
|
|
|
|
// UINT64 GICV
|
|
|
|
Gicc->GICV = GicCInfo->GICV;
|
|
|
|
// UINT64 GICH
|
|
|
|
Gicc->GICH = GicCInfo->GICH;
|
|
|
|
|
|
|
|
// UINT32 VGICMaintenanceInterrupt
|
|
|
|
Gicc->VGICMaintenanceInterrupt = GicCInfo->VGICMaintenanceInterrupt;
|
|
|
|
// UINT64 GICRBaseAddress
|
|
|
|
Gicc->GICRBaseAddress = GicCInfo->GICRBaseAddress;
|
|
|
|
|
|
|
|
// UINT64 MPIDR
|
|
|
|
Gicc->MPIDR = GicCInfo->MPIDR;
|
|
|
|
// UINT8 ProcessorPowerEfficiencyClass
|
|
|
|
Gicc->ProcessorPowerEfficiencyClass =
|
|
|
|
GicCInfo->ProcessorPowerEfficiencyClass;
|
2019-04-09 15:44:37 +02:00
|
|
|
// UINT8 Reserved2
|
|
|
|
Gicc->Reserved2 = EFI_ACPI_RESERVED_BYTE;
|
|
|
|
|
|
|
|
// UINT16 SpeOverflowInterrupt
|
|
|
|
if (MadtRev > EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION) {
|
|
|
|
Gicc->SpeOverflowInterrupt = GicCInfo->SpeOverflowInterrupt;
|
|
|
|
} else {
|
|
|
|
// Setting SpeOverflowInterrupt to 0 ensures backward compatibility with
|
|
|
|
// ACPI 6.2 by also clearing the Reserved2[1] and Reserved2[2] fields
|
|
|
|
// in EFI_ACPI_6_2_GIC_STRUCTURE.
|
|
|
|
Gicc->SpeOverflowInterrupt = 0;
|
|
|
|
}
|
2018-12-15 13:23:59 +01:00
|
|
|
}
|
|
|
|
|
2019-04-09 13:58:47 +02:00
|
|
|
/**
|
|
|
|
Function to test if two GIC CPU Interface information structures have the
|
|
|
|
same ACPI Processor UID.
|
|
|
|
|
|
|
|
@param [in] GicCInfo1 Pointer to the first GICC info structure.
|
|
|
|
@param [in] GicCInfo2 Pointer to the second GICC info structure.
|
|
|
|
@param [in] Index1 Index of GicCInfo1 in the shared list of GIC
|
|
|
|
CPU Interface Info structures.
|
|
|
|
@param [in] Index2 Index of GicCInfo2 in the shared list of GIC
|
|
|
|
CPU Interface Info structures.
|
|
|
|
|
|
|
|
@retval TRUE GicCInfo1 and GicCInfo2 have the same UID.
|
|
|
|
@retval FALSE GicCInfo1 and GicCInfo2 have different UIDs.
|
|
|
|
**/
|
|
|
|
BOOLEAN
|
|
|
|
EFIAPI
|
|
|
|
IsAcpiUidEqual (
|
|
|
|
IN CONST VOID * GicCInfo1,
|
|
|
|
IN CONST VOID * GicCInfo2,
|
|
|
|
IN UINTN Index1,
|
|
|
|
IN UINTN Index2
|
|
|
|
)
|
|
|
|
{
|
|
|
|
UINT32 Uid1;
|
|
|
|
UINT32 Uid2;
|
|
|
|
|
|
|
|
ASSERT ((GicCInfo1 != NULL) && (GicCInfo2 != NULL));
|
|
|
|
|
|
|
|
Uid1 = ((CM_ARM_GICC_INFO*)GicCInfo1)->AcpiProcessorUid;
|
|
|
|
Uid2 = ((CM_ARM_GICC_INFO*)GicCInfo2)->AcpiProcessorUid;
|
|
|
|
|
|
|
|
if (Uid1 == Uid2) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: GICC Info Structures %d and %d have the same ACPI " \
|
|
|
|
"Processor UID: 0x%x.\n",
|
|
|
|
Index1,
|
|
|
|
Index2,
|
|
|
|
Uid1
|
|
|
|
));
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
|
2018-12-15 13:23:59 +01:00
|
|
|
/** Add the GIC CPU Interface Information to the MADT Table.
|
|
|
|
|
2019-04-09 13:58:47 +02:00
|
|
|
This function also checks for duplicate ACPI Processor UIDs.
|
|
|
|
|
|
|
|
@param [in] Gicc Pointer to GIC CPU Interface structure list.
|
|
|
|
@param [in] GicCInfo Pointer to the GIC CPU Information list.
|
|
|
|
@param [in] GicCCount Count of GIC CPU Interfaces.
|
2019-04-09 15:44:37 +02:00
|
|
|
@param [in] MadtRev MADT table revision.
|
2019-04-09 13:58:47 +02:00
|
|
|
|
|
|
|
@retval EFI_SUCCESS GIC CPU Interface Information was added
|
|
|
|
successfully.
|
|
|
|
@retval EFI_INVALID_PARAMETER One or more invalid GIC CPU Info values were
|
|
|
|
provided and the generator failed to add the
|
|
|
|
information to the table.
|
2018-12-15 13:23:59 +01:00
|
|
|
**/
|
|
|
|
STATIC
|
2019-04-09 13:58:47 +02:00
|
|
|
EFI_STATUS
|
2018-12-15 13:23:59 +01:00
|
|
|
AddGICCList (
|
2019-04-09 15:44:37 +02:00
|
|
|
IN EFI_ACPI_6_3_GIC_STRUCTURE * Gicc,
|
2018-12-15 13:23:59 +01:00
|
|
|
IN CONST CM_ARM_GICC_INFO * GicCInfo,
|
2019-04-09 15:44:37 +02:00
|
|
|
IN UINT32 GicCCount,
|
|
|
|
IN CONST UINT8 MadtRev
|
2018-12-15 13:23:59 +01:00
|
|
|
)
|
|
|
|
{
|
2019-04-09 13:58:47 +02:00
|
|
|
BOOLEAN IsAcpiProcUidDuplicated;
|
|
|
|
|
2018-12-15 13:23:59 +01:00
|
|
|
ASSERT (Gicc != NULL);
|
|
|
|
ASSERT (GicCInfo != NULL);
|
|
|
|
|
2019-04-09 13:58:47 +02:00
|
|
|
IsAcpiProcUidDuplicated = FindDuplicateValue (
|
|
|
|
GicCInfo,
|
|
|
|
GicCCount,
|
|
|
|
sizeof (CM_ARM_GICC_INFO),
|
|
|
|
IsAcpiUidEqual
|
|
|
|
);
|
|
|
|
// Duplicate ACPI Processor UID was found so the GICC info provided
|
|
|
|
// is invalid
|
|
|
|
if (IsAcpiProcUidDuplicated) {
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
2018-12-15 13:23:59 +01:00
|
|
|
while (GicCCount-- != 0) {
|
2019-04-09 15:44:37 +02:00
|
|
|
AddGICC (Gicc++, GicCInfo++, MadtRev);
|
2018-12-15 13:23:59 +01:00
|
|
|
}
|
2019-04-09 13:58:47 +02:00
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
2018-12-15 13:23:59 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/** Update the GIC Distributor Information in the MADT Table.
|
|
|
|
|
|
|
|
@param [in] Gicd Pointer to GIC Distributor structure.
|
|
|
|
@param [in] GicDInfo Pointer to the GIC Distributor Information.
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
VOID
|
|
|
|
AddGICD (
|
2019-04-09 15:44:37 +02:00
|
|
|
EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE * CONST Gicd,
|
2018-12-15 13:23:59 +01:00
|
|
|
CONST CM_ARM_GICD_INFO * CONST GicDInfo
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT (Gicd != NULL);
|
|
|
|
ASSERT (GicDInfo != NULL);
|
|
|
|
|
|
|
|
// UINT8 Type
|
2019-04-09 15:44:37 +02:00
|
|
|
Gicd->Type = EFI_ACPI_6_3_GICD;
|
2018-12-15 13:23:59 +01:00
|
|
|
// UINT8 Length
|
2019-04-09 15:44:37 +02:00
|
|
|
Gicd->Length = sizeof (EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE);
|
2018-12-15 13:23:59 +01:00
|
|
|
// UINT16 Reserved
|
|
|
|
Gicd->Reserved1 = EFI_ACPI_RESERVED_WORD;
|
|
|
|
// UINT32 Identifier
|
2019-02-19 12:22:17 +01:00
|
|
|
// One, and only one, GIC distributor structure must be present
|
|
|
|
// in the MADT for an ARM based system
|
|
|
|
Gicd->GicId = 0;
|
2018-12-15 13:23:59 +01:00
|
|
|
// UINT64 PhysicalBaseAddress
|
|
|
|
Gicd->PhysicalBaseAddress = GicDInfo->PhysicalBaseAddress;
|
|
|
|
// UINT32 VectorBase
|
|
|
|
Gicd->SystemVectorBase = EFI_ACPI_RESERVED_DWORD;
|
|
|
|
// UINT8 GicVersion
|
|
|
|
Gicd->GicVersion = GicDInfo->GicVersion;
|
|
|
|
// UINT8 Reserved2[3]
|
|
|
|
Gicd->Reserved2[0] = EFI_ACPI_RESERVED_BYTE;
|
|
|
|
Gicd->Reserved2[1] = EFI_ACPI_RESERVED_BYTE;
|
|
|
|
Gicd->Reserved2[2] = EFI_ACPI_RESERVED_BYTE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Update the GIC MSI Frame Information.
|
|
|
|
|
|
|
|
@param [in] GicMsiFrame Pointer to GIC MSI Frame structure.
|
|
|
|
@param [in] GicMsiFrameInfo Pointer to the GIC MSI Frame Information.
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
VOID
|
|
|
|
AddGICMsiFrame (
|
2019-04-09 15:44:37 +02:00
|
|
|
IN EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE * CONST GicMsiFrame,
|
2018-12-15 13:23:59 +01:00
|
|
|
IN CONST CM_ARM_GIC_MSI_FRAME_INFO * CONST GicMsiFrameInfo
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT (GicMsiFrame != NULL);
|
|
|
|
ASSERT (GicMsiFrameInfo != NULL);
|
|
|
|
|
2019-04-09 15:44:37 +02:00
|
|
|
GicMsiFrame->Type = EFI_ACPI_6_3_GIC_MSI_FRAME;
|
|
|
|
GicMsiFrame->Length = sizeof (EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE);
|
2018-12-15 13:23:59 +01:00
|
|
|
GicMsiFrame->Reserved1 = EFI_ACPI_RESERVED_WORD;
|
|
|
|
GicMsiFrame->GicMsiFrameId = GicMsiFrameInfo->GicMsiFrameId;
|
|
|
|
GicMsiFrame->PhysicalBaseAddress = GicMsiFrameInfo->PhysicalBaseAddress;
|
|
|
|
|
|
|
|
GicMsiFrame->Flags = GicMsiFrameInfo->Flags;
|
|
|
|
GicMsiFrame->SPICount = GicMsiFrameInfo->SPICount;
|
|
|
|
GicMsiFrame->SPIBase = GicMsiFrameInfo->SPIBase;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Add the GIC MSI Frame Information to the MADT Table.
|
|
|
|
|
|
|
|
@param [in] GicMsiFrame Pointer to GIC MSI Frame structure list.
|
|
|
|
@param [in] GicMsiFrameInfo Pointer to the GIC MSI Frame info list.
|
|
|
|
@param [in] GicMsiFrameCount Count of GIC MSI Frames.
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
VOID
|
|
|
|
AddGICMsiFrameInfoList (
|
2019-04-09 15:44:37 +02:00
|
|
|
IN EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE * GicMsiFrame,
|
2018-12-15 13:23:59 +01:00
|
|
|
IN CONST CM_ARM_GIC_MSI_FRAME_INFO * GicMsiFrameInfo,
|
|
|
|
IN UINT32 GicMsiFrameCount
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT (GicMsiFrame != NULL);
|
|
|
|
ASSERT (GicMsiFrameInfo != NULL);
|
|
|
|
|
|
|
|
while (GicMsiFrameCount-- != 0) {
|
|
|
|
AddGICMsiFrame (GicMsiFrame++, GicMsiFrameInfo++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Update the GIC Redistributor Information.
|
|
|
|
|
|
|
|
@param [in] Gicr Pointer to GIC Redistributor structure.
|
|
|
|
@param [in] GicRedisributorInfo Pointer to the GIC Redistributor Info.
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
VOID
|
|
|
|
AddGICRedistributor (
|
2019-04-09 15:44:37 +02:00
|
|
|
IN EFI_ACPI_6_3_GICR_STRUCTURE * CONST Gicr,
|
2018-12-15 13:23:59 +01:00
|
|
|
IN CONST CM_ARM_GIC_REDIST_INFO * CONST GicRedisributorInfo
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT (Gicr != NULL);
|
|
|
|
ASSERT (GicRedisributorInfo != NULL);
|
|
|
|
|
2019-04-09 15:44:37 +02:00
|
|
|
Gicr->Type = EFI_ACPI_6_3_GICR;
|
|
|
|
Gicr->Length = sizeof (EFI_ACPI_6_3_GICR_STRUCTURE);
|
2018-12-15 13:23:59 +01:00
|
|
|
Gicr->Reserved = EFI_ACPI_RESERVED_WORD;
|
|
|
|
Gicr->DiscoveryRangeBaseAddress =
|
|
|
|
GicRedisributorInfo->DiscoveryRangeBaseAddress;
|
|
|
|
Gicr->DiscoveryRangeLength = GicRedisributorInfo->DiscoveryRangeLength;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Add the GIC Redistributor Information to the MADT Table.
|
|
|
|
|
|
|
|
@param [in] Gicr Pointer to GIC Redistributor structure list.
|
|
|
|
@param [in] GicRInfo Pointer to the GIC Distributor info list.
|
|
|
|
@param [in] GicRCount Count of GIC Distributors.
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
VOID
|
|
|
|
AddGICRedistributorList (
|
2019-04-09 15:44:37 +02:00
|
|
|
IN EFI_ACPI_6_3_GICR_STRUCTURE * Gicr,
|
2018-12-15 13:23:59 +01:00
|
|
|
IN CONST CM_ARM_GIC_REDIST_INFO * GicRInfo,
|
|
|
|
IN UINT32 GicRCount
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT (Gicr != NULL);
|
|
|
|
ASSERT (GicRInfo != NULL);
|
|
|
|
|
|
|
|
while (GicRCount-- != 0) {
|
|
|
|
AddGICRedistributor (Gicr++, GicRInfo++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Update the GIC Interrupt Translation Service Information
|
|
|
|
|
|
|
|
@param [in] GicIts Pointer to GIC ITS structure.
|
|
|
|
@param [in] GicItsInfo Pointer to the GIC ITS Information.
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
VOID
|
|
|
|
AddGICInterruptTranslationService (
|
2019-04-09 15:44:37 +02:00
|
|
|
IN EFI_ACPI_6_3_GIC_ITS_STRUCTURE * CONST GicIts,
|
2018-12-15 13:23:59 +01:00
|
|
|
IN CONST CM_ARM_GIC_ITS_INFO * CONST GicItsInfo
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT (GicIts != NULL);
|
|
|
|
ASSERT (GicItsInfo != NULL);
|
|
|
|
|
2019-04-09 15:44:37 +02:00
|
|
|
GicIts->Type = EFI_ACPI_6_3_GIC_ITS;
|
|
|
|
GicIts->Length = sizeof (EFI_ACPI_6_3_GIC_ITS_STRUCTURE);
|
2018-12-15 13:23:59 +01:00
|
|
|
GicIts->Reserved = EFI_ACPI_RESERVED_WORD;
|
|
|
|
GicIts->GicItsId = GicItsInfo->GicItsId;
|
|
|
|
GicIts->PhysicalBaseAddress = GicItsInfo->PhysicalBaseAddress;
|
|
|
|
GicIts->Reserved2 = EFI_ACPI_RESERVED_DWORD;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Add the GIC Interrupt Translation Service Information
|
|
|
|
to the MADT Table.
|
|
|
|
|
|
|
|
@param [in] GicIts Pointer to GIC ITS structure list.
|
|
|
|
@param [in] GicItsInfo Pointer to the GIC ITS list.
|
|
|
|
@param [in] GicItsCount Count of GIC ITS.
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
VOID
|
|
|
|
AddGICItsList (
|
2019-04-09 15:44:37 +02:00
|
|
|
IN EFI_ACPI_6_3_GIC_ITS_STRUCTURE * GicIts,
|
2018-12-15 13:23:59 +01:00
|
|
|
IN CONST CM_ARM_GIC_ITS_INFO * GicItsInfo,
|
|
|
|
IN UINT32 GicItsCount
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT (GicIts != NULL);
|
|
|
|
ASSERT (GicItsInfo != NULL);
|
|
|
|
|
|
|
|
while (GicItsCount-- != 0) {
|
|
|
|
AddGICInterruptTranslationService (GicIts++, GicItsInfo++);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Construct the MADT ACPI table.
|
|
|
|
|
|
|
|
This function invokes the Configuration Manager protocol interface
|
|
|
|
to get the required hardware information for generating the ACPI
|
|
|
|
table.
|
|
|
|
|
|
|
|
If this function allocates any resources then they must be freed
|
|
|
|
in the FreeXXXXTableResources function.
|
|
|
|
|
|
|
|
@param [in] This Pointer to the table generator.
|
|
|
|
@param [in] AcpiTableInfo Pointer to the ACPI Table Info.
|
|
|
|
@param [in] CfgMgrProtocol Pointer to the Configuration Manager
|
|
|
|
Protocol Interface.
|
|
|
|
@param [out] Table Pointer to the constructed ACPI Table.
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS Table generated successfully.
|
|
|
|
@retval EFI_INVALID_PARAMETER A parameter is invalid.
|
|
|
|
@retval EFI_NOT_FOUND The required object was not found.
|
|
|
|
@retval EFI_BAD_BUFFER_SIZE The size returned by the Configuration
|
|
|
|
Manager is less than the Object size for the
|
|
|
|
requested object.
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
|
|
|
BuildMadtTable (
|
|
|
|
IN CONST ACPI_TABLE_GENERATOR * CONST This,
|
|
|
|
IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo,
|
|
|
|
IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
|
|
|
|
OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
UINT32 TableSize;
|
|
|
|
UINT32 GicCCount;
|
|
|
|
UINT32 GicDCount;
|
|
|
|
UINT32 GicMSICount;
|
|
|
|
UINT32 GicRedistCount;
|
|
|
|
UINT32 GicItsCount;
|
|
|
|
CM_ARM_GICC_INFO * GicCInfo;
|
|
|
|
CM_ARM_GICD_INFO * GicDInfo;
|
|
|
|
CM_ARM_GIC_MSI_FRAME_INFO * GicMSIInfo;
|
|
|
|
CM_ARM_GIC_REDIST_INFO * GicRedistInfo;
|
|
|
|
CM_ARM_GIC_ITS_INFO * GicItsInfo;
|
|
|
|
UINT32 GicCOffset;
|
|
|
|
UINT32 GicDOffset;
|
|
|
|
UINT32 GicMSIOffset;
|
|
|
|
UINT32 GicRedistOffset;
|
|
|
|
UINT32 GicItsOffset;
|
|
|
|
|
2019-04-09 15:44:37 +02:00
|
|
|
EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER * Madt;
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
ASSERT (This != NULL);
|
|
|
|
ASSERT (AcpiTableInfo != NULL);
|
|
|
|
ASSERT (CfgMgrProtocol != NULL);
|
|
|
|
ASSERT (Table != NULL);
|
|
|
|
ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
|
|
|
|
ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
|
|
|
|
|
|
|
|
if ((AcpiTableInfo->AcpiTableRevision < This->MinAcpiTableRevision) ||
|
|
|
|
(AcpiTableInfo->AcpiTableRevision > This->AcpiTableRevision)) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: Requested table revision = %d, is not supported."
|
|
|
|
"Supported table revision: Minimum = %d, Maximum = %d\n",
|
|
|
|
AcpiTableInfo->AcpiTableRevision,
|
|
|
|
This->MinAcpiTableRevision,
|
|
|
|
This->AcpiTableRevision
|
|
|
|
));
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
*Table = NULL;
|
|
|
|
|
|
|
|
Status = GetEArmObjGicCInfo (
|
|
|
|
CfgMgrProtocol,
|
|
|
|
CM_NULL_TOKEN,
|
|
|
|
&GicCInfo,
|
|
|
|
&GicCCount
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: Failed to get GICC Info. Status = %r\n",
|
|
|
|
Status
|
|
|
|
));
|
|
|
|
goto error_handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GicCCount == 0) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: GIC CPU Interface information not provided.\n"
|
|
|
|
));
|
|
|
|
ASSERT (GicCCount != 0);
|
|
|
|
Status = EFI_INVALID_PARAMETER;
|
|
|
|
goto error_handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = GetEArmObjGicDInfo (
|
|
|
|
CfgMgrProtocol,
|
|
|
|
CM_NULL_TOKEN,
|
|
|
|
&GicDInfo,
|
|
|
|
&GicDCount
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: Failed to get GICD Info. Status = %r\n",
|
|
|
|
Status
|
|
|
|
));
|
|
|
|
goto error_handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GicDCount == 0) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: GIC Distributor information not provided.\n"
|
|
|
|
));
|
|
|
|
ASSERT (GicDCount != 0);
|
|
|
|
Status = EFI_INVALID_PARAMETER;
|
|
|
|
goto error_handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GicDCount > 1) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: One, and only one, GIC distributor must be present."
|
|
|
|
"GicDCount = %d\n",
|
|
|
|
GicDCount
|
|
|
|
));
|
|
|
|
ASSERT (GicDCount <= 1);
|
|
|
|
Status = EFI_INVALID_PARAMETER;
|
|
|
|
goto error_handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = GetEArmObjGicMsiFrameInfo (
|
|
|
|
CfgMgrProtocol,
|
|
|
|
CM_NULL_TOKEN,
|
|
|
|
&GicMSIInfo,
|
|
|
|
&GicMSICount
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: Failed to get GIC MSI Info. Status = %r\n",
|
|
|
|
Status
|
|
|
|
));
|
|
|
|
goto error_handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = GetEArmObjGicRedistributorInfo (
|
|
|
|
CfgMgrProtocol,
|
|
|
|
CM_NULL_TOKEN,
|
|
|
|
&GicRedistInfo,
|
|
|
|
&GicRedistCount
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: Failed to get GIC Redistributor Info. Status = %r\n",
|
|
|
|
Status
|
|
|
|
));
|
|
|
|
goto error_handler;
|
|
|
|
}
|
|
|
|
|
|
|
|
Status = GetEArmObjGicItsInfo (
|
|
|
|
CfgMgrProtocol,
|
|
|
|
CM_NULL_TOKEN,
|
|
|
|
&GicItsInfo,
|
|
|
|
&GicItsCount
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: Failed to get GIC ITS Info. Status = %r\n",
|
|
|
|
Status
|
|
|
|
));
|
|
|
|
goto error_handler;
|
|
|
|
}
|
|
|
|
|
2019-04-09 15:44:37 +02:00
|
|
|
TableSize = sizeof (EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER);
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
GicCOffset = TableSize;
|
2019-04-09 15:44:37 +02:00
|
|
|
TableSize += (sizeof (EFI_ACPI_6_3_GIC_STRUCTURE) * GicCCount);
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
GicDOffset = TableSize;
|
2019-04-09 15:44:37 +02:00
|
|
|
TableSize += (sizeof (EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE) * GicDCount);
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
GicMSIOffset = TableSize;
|
2019-04-09 15:44:37 +02:00
|
|
|
TableSize += (sizeof (EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE) * GicMSICount);
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
GicRedistOffset = TableSize;
|
2019-04-09 15:44:37 +02:00
|
|
|
TableSize += (sizeof (EFI_ACPI_6_3_GICR_STRUCTURE) * GicRedistCount);
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
GicItsOffset = TableSize;
|
2019-04-09 15:44:37 +02:00
|
|
|
TableSize += (sizeof (EFI_ACPI_6_3_GIC_ITS_STRUCTURE) * GicItsCount);
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
// Allocate the Buffer for MADT table
|
|
|
|
*Table = (EFI_ACPI_DESCRIPTION_HEADER*)AllocateZeroPool (TableSize);
|
|
|
|
if (*Table == NULL) {
|
|
|
|
Status = EFI_OUT_OF_RESOURCES;
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: Failed to allocate memory for MADT Table, Size = %d," \
|
|
|
|
" Status = %r\n",
|
|
|
|
TableSize,
|
|
|
|
Status
|
|
|
|
));
|
|
|
|
goto error_handler;
|
|
|
|
}
|
|
|
|
|
2019-04-09 15:44:37 +02:00
|
|
|
Madt = (EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_HEADER*)*Table;
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_INFO,
|
|
|
|
"MADT: Madt = 0x%p TableSize = 0x%x\n",
|
|
|
|
Madt,
|
|
|
|
TableSize
|
|
|
|
));
|
|
|
|
|
|
|
|
Status = AddAcpiHeader (
|
|
|
|
CfgMgrProtocol,
|
|
|
|
This,
|
|
|
|
&Madt->Header,
|
2019-02-14 12:01:20 +01:00
|
|
|
AcpiTableInfo,
|
2018-12-15 13:23:59 +01:00
|
|
|
TableSize
|
|
|
|
);
|
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: Failed to add ACPI header. Status = %r\n",
|
|
|
|
Status
|
|
|
|
));
|
|
|
|
goto error_handler;
|
|
|
|
}
|
|
|
|
|
2019-04-09 13:58:47 +02:00
|
|
|
Status = AddGICCList (
|
2019-04-09 15:44:37 +02:00
|
|
|
(EFI_ACPI_6_3_GIC_STRUCTURE*)((UINT8*)Madt + GicCOffset),
|
2018-12-15 13:23:59 +01:00
|
|
|
GicCInfo,
|
2019-04-09 15:44:37 +02:00
|
|
|
GicCCount,
|
|
|
|
Madt->Header.Revision
|
2018-12-15 13:23:59 +01:00
|
|
|
);
|
2019-04-09 13:58:47 +02:00
|
|
|
if (EFI_ERROR (Status)) {
|
|
|
|
DEBUG ((
|
|
|
|
DEBUG_ERROR,
|
|
|
|
"ERROR: MADT: Failed to add GICC structures. Status = %r\n",
|
|
|
|
Status
|
|
|
|
));
|
|
|
|
goto error_handler;
|
|
|
|
}
|
2018-12-15 13:23:59 +01:00
|
|
|
|
|
|
|
AddGICD (
|
2019-04-09 15:44:37 +02:00
|
|
|
(EFI_ACPI_6_3_GIC_DISTRIBUTOR_STRUCTURE*)((UINT8*)Madt + GicDOffset),
|
2018-12-15 13:23:59 +01:00
|
|
|
GicDInfo
|
|
|
|
);
|
|
|
|
|
|
|
|
if (GicMSICount != 0) {
|
|
|
|
AddGICMsiFrameInfoList (
|
2019-04-09 15:44:37 +02:00
|
|
|
(EFI_ACPI_6_3_GIC_MSI_FRAME_STRUCTURE*)((UINT8*)Madt + GicMSIOffset),
|
2018-12-15 13:23:59 +01:00
|
|
|
GicMSIInfo,
|
|
|
|
GicMSICount
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GicRedistCount != 0) {
|
|
|
|
AddGICRedistributorList (
|
2019-04-09 15:44:37 +02:00
|
|
|
(EFI_ACPI_6_3_GICR_STRUCTURE*)((UINT8*)Madt + GicRedistOffset),
|
2018-12-15 13:23:59 +01:00
|
|
|
GicRedistInfo,
|
|
|
|
GicRedistCount
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GicItsCount != 0) {
|
|
|
|
AddGICItsList (
|
2019-04-09 15:44:37 +02:00
|
|
|
(EFI_ACPI_6_3_GIC_ITS_STRUCTURE*)((UINT8*)Madt + GicItsOffset),
|
2018-12-15 13:23:59 +01:00
|
|
|
GicItsInfo,
|
|
|
|
GicItsCount
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
|
|
|
|
error_handler:
|
|
|
|
if (*Table != NULL) {
|
|
|
|
FreePool (*Table);
|
|
|
|
*Table = NULL;
|
|
|
|
}
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Free any resources allocated for constructing the MADT
|
|
|
|
|
|
|
|
@param [in] This Pointer to the table generator.
|
|
|
|
@param [in] AcpiTableInfo Pointer to the ACPI Table Info.
|
|
|
|
@param [in] CfgMgrProtocol Pointer to the Configuration Manager
|
|
|
|
Protocol Interface.
|
|
|
|
@param [in, out] Table Pointer to the ACPI Table.
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS The resources were freed successfully.
|
|
|
|
@retval EFI_INVALID_PARAMETER The table pointer is NULL or invalid.
|
|
|
|
**/
|
|
|
|
STATIC
|
|
|
|
EFI_STATUS
|
|
|
|
FreeMadtTableResources (
|
|
|
|
IN CONST ACPI_TABLE_GENERATOR * CONST This,
|
|
|
|
IN CONST CM_STD_OBJ_ACPI_TABLE_INFO * CONST AcpiTableInfo,
|
|
|
|
IN CONST EDKII_CONFIGURATION_MANAGER_PROTOCOL * CONST CfgMgrProtocol,
|
|
|
|
IN OUT EFI_ACPI_DESCRIPTION_HEADER ** CONST Table
|
|
|
|
)
|
|
|
|
{
|
|
|
|
ASSERT (This != NULL);
|
|
|
|
ASSERT (AcpiTableInfo != NULL);
|
|
|
|
ASSERT (CfgMgrProtocol != NULL);
|
|
|
|
ASSERT (AcpiTableInfo->TableGeneratorId == This->GeneratorID);
|
|
|
|
ASSERT (AcpiTableInfo->AcpiTableSignature == This->AcpiTableSignature);
|
|
|
|
|
|
|
|
if ((Table == NULL) || (*Table == NULL)) {
|
|
|
|
DEBUG ((DEBUG_ERROR, "ERROR: MADT: Invalid Table Pointer\n"));
|
|
|
|
ASSERT ((Table != NULL) && (*Table != NULL));
|
|
|
|
return EFI_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
|
|
|
|
FreePool (*Table);
|
|
|
|
*Table = NULL;
|
|
|
|
return EFI_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** The MADT Table Generator revision.
|
|
|
|
*/
|
|
|
|
#define MADT_GENERATOR_REVISION CREATE_REVISION (1, 0)
|
|
|
|
|
|
|
|
/** The interface for the MADT Table Generator.
|
|
|
|
*/
|
|
|
|
STATIC
|
|
|
|
CONST
|
|
|
|
ACPI_TABLE_GENERATOR MadtGenerator = {
|
|
|
|
// Generator ID
|
|
|
|
CREATE_STD_ACPI_TABLE_GEN_ID (EStdAcpiTableIdMadt),
|
|
|
|
// Generator Description
|
|
|
|
L"ACPI.STD.MADT.GENERATOR",
|
|
|
|
// ACPI Table Signature
|
2019-04-09 15:44:37 +02:00
|
|
|
EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_SIGNATURE,
|
2018-12-15 13:23:59 +01:00
|
|
|
// ACPI Table Revision supported by this Generator
|
2019-04-09 15:44:37 +02:00
|
|
|
EFI_ACPI_6_3_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION,
|
2018-12-15 13:23:59 +01:00
|
|
|
// Minimum supported ACPI Table Revision
|
|
|
|
EFI_ACPI_6_2_MULTIPLE_APIC_DESCRIPTION_TABLE_REVISION,
|
|
|
|
// Creator ID
|
|
|
|
TABLE_GENERATOR_CREATOR_ID_ARM,
|
|
|
|
// Creator Revision
|
|
|
|
MADT_GENERATOR_REVISION,
|
|
|
|
// Build Table function
|
|
|
|
BuildMadtTable,
|
|
|
|
// Free Resource function
|
|
|
|
FreeMadtTableResources,
|
|
|
|
// Extended build function not needed
|
|
|
|
NULL,
|
|
|
|
// Extended build function not implemented by the generator.
|
|
|
|
// Hence extended free resource function is not required.
|
|
|
|
NULL
|
|
|
|
};
|
|
|
|
|
|
|
|
/** Register the Generator with the ACPI Table Factory.
|
|
|
|
|
|
|
|
@param [in] ImageHandle The handle to the image.
|
|
|
|
@param [in] SystemTable Pointer to the System Table.
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS The Generator is registered.
|
|
|
|
@retval EFI_INVALID_PARAMETER A parameter is invalid.
|
|
|
|
@retval EFI_ALREADY_STARTED The Generator for the Table ID
|
|
|
|
is already registered.
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
|
|
|
AcpiMadtLibConstructor (
|
|
|
|
IN CONST EFI_HANDLE ImageHandle,
|
|
|
|
IN EFI_SYSTEM_TABLE * CONST SystemTable
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
Status = RegisterAcpiTableGenerator (&MadtGenerator);
|
|
|
|
DEBUG ((DEBUG_INFO, "MADT: Register Generator. Status = %r\n", Status));
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return Status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Deregister the Generator from the ACPI Table Factory.
|
|
|
|
|
|
|
|
@param [in] ImageHandle The handle to the image.
|
|
|
|
@param [in] SystemTable Pointer to the System Table.
|
|
|
|
|
|
|
|
@retval EFI_SUCCESS The Generator is deregistered.
|
|
|
|
@retval EFI_INVALID_PARAMETER A parameter is invalid.
|
|
|
|
@retval EFI_NOT_FOUND The Generator is not registered.
|
|
|
|
**/
|
|
|
|
EFI_STATUS
|
|
|
|
EFIAPI
|
|
|
|
AcpiMadtLibDestructor (
|
|
|
|
IN CONST EFI_HANDLE ImageHandle,
|
|
|
|
IN EFI_SYSTEM_TABLE * CONST SystemTable
|
|
|
|
)
|
|
|
|
{
|
|
|
|
EFI_STATUS Status;
|
|
|
|
Status = DeregisterAcpiTableGenerator (&MadtGenerator);
|
|
|
|
DEBUG ((DEBUG_INFO, "MADT: Deregister Generator. Status = %r\n", Status));
|
|
|
|
ASSERT_EFI_ERROR (Status);
|
|
|
|
return Status;
|
|
|
|
}
|