/** @file Provision EPID key. Copyright (c) 2019, vit9696. All rights reserved.
This program and the accompanying materials are licensed and made available under the terms and conditions of the BSD License which accompanies this distribution. The full text of the license may be found at http://opensource.org/licenses/bsd-license.php THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define FORCE_PROVISIONING 1 #define SA_MC_BUS 0x00 #define R_SA_PAVPC (0x58) #define MmPciAddress(Segment, Bus, Device, Function, Register) \ ((UINTN) (PciRead32 (PCI_LIB_ADDRESS (0,0,0,0x60)) & 0xFC000000) + \ (UINTN) (Bus << 20) + (UINTN) (Device << 15) + (UINTN) \ (Function << 12) + (UINTN) (Register)) #define MmPci32(Segment, Bus, Device, Function, Register) \ *((volatile UINT32 *) MmPciAddress (Segment, Bus, Device, Function, Register)) STATIC UINT8 mMeClientMap[HBM_ME_CLIENT_MAX]; STATIC UINT8 mMeClientActiveCount; extern UINT8 gDefaultAppleEpidCertificate[]; extern UINTN gDefaultAppleEpidCertificateSize; extern UINT8 gDefaultAppleGroupPublicKeys[]; extern UINTN gDefaultAppleGroupPublicKeysSize; STATIC EFI_STATUS ReadProvisioningDataFile ( IN EFI_GUID *FvNameGuid, OUT VOID **Buffer, OUT UINTN *BufferSize ) { UINTN Index; EFI_STATUS Status; UINT32 AuthenticationStatus; EFI_FIRMWARE_VOLUME_PROTOCOL *FirmwareVolumeInterface; UINTN NumOfHandles; EFI_HANDLE *HandleBuffer; Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiFirmwareVolumeProtocolGuid, NULL, &NumOfHandles, &HandleBuffer ); if (!EFI_ERROR (Status)) { for (Index = 0; Index < NumOfHandles; ++Index) { Status = gBS->HandleProtocol ( HandleBuffer[Index], &gEfiFirmwareVolumeProtocolGuid, (VOID **)&FirmwareVolumeInterface ); if (EFI_ERROR (Status)) { gBS->FreePool (HandleBuffer); return Status; } *Buffer = NULL; *BufferSize = 0; Status = FirmwareVolumeInterface->ReadSection ( FirmwareVolumeInterface, FvNameGuid, EFI_SECTION_RAW, 0, Buffer, BufferSize, &AuthenticationStatus ); if (!EFI_ERROR (Status)) { gBS->FreePool (HandleBuffer); return Status; } } gBS->FreePool (HandleBuffer); Status = EFI_NOT_FOUND; } // // Implement fallback for our firmware. // if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCPAVP: No %g in firmware, using default - %r\n", FvNameGuid, Status)); if (CompareGuid (&gAppleEpidCertificateFileGuid, FvNameGuid)) { *Buffer = AllocateCopyPool (gDefaultAppleEpidCertificateSize, gDefaultAppleEpidCertificate); *BufferSize = gDefaultAppleEpidCertificateSize; } else if (CompareGuid (&gAppleEpidGroupPublicKeysFileGuid, FvNameGuid)) { *Buffer = AllocateCopyPool (gDefaultAppleGroupPublicKeysSize, gDefaultAppleGroupPublicKeys); *BufferSize = gDefaultAppleGroupPublicKeysSize; } else { *Buffer = NULL; } if (*Buffer != NULL) { Status = EFI_SUCCESS; } else { Status = EFI_NOT_FOUND; } } return Status; } STATIC EFI_STATUS ReadProvisioningData ( OUT EPID_CERTIFICATE **EpidCertificate, OUT EPID_GROUP_PUBLIC_KEY **EpidGroupPublicKeys, OUT UINT32 *EpidGroupPublicKeysCount ) { EFI_STATUS Status; UINTN EpidCertificateSize; UINTN EpidGroupPublicKeysSize; Status = ReadProvisioningDataFile ( &gAppleEpidCertificateFileGuid, (VOID **)EpidCertificate, &EpidCertificateSize ); if (EFI_ERROR (Status)) { return Status; } Status = ReadProvisioningDataFile ( &gAppleEpidGroupPublicKeysFileGuid, (VOID **)EpidGroupPublicKeys, &EpidGroupPublicKeysSize ); if (EFI_ERROR (Status)) { gBS->FreePool (*EpidGroupPublicKeys); return Status; } if ( (EpidCertificateSize == EPID_CERTIFICATE_SIZE) && (EpidGroupPublicKeysSize % EPID_GROUP_PUBLIC_KEY_SIZE == 0)) { *EpidGroupPublicKeysCount = (UINT32)(EpidGroupPublicKeysSize / EPID_GROUP_PUBLIC_KEY_SIZE); return EFI_SUCCESS; } gBS->FreePool (*EpidCertificate); gBS->FreePool (*EpidGroupPublicKeys); return EFI_VOLUME_CORRUPTED; } STATIC VOID SetProvisioningVariable ( IN CHAR16 *Variable, IN UINT32 Value ) { #ifdef FORCE_PROVISIONING (VOID)Variable; (VOID)Value; #else gRT->SetVariable ( Variable, &gEfiGlobalVariableGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, sizeof (Value), &Value ); #endif } STATIC EFI_STATUS GetGroupPublicKey ( IN EPID_GROUP_PUBLIC_KEY *PublicKeys, IN UINTN PublicKeyCount, IN UINT32 Key, OUT EPID_GROUP_PUBLIC_KEY **ChosenPublicKey ) { EFI_STATUS Status; UINT32 Index; Status = EFI_NOT_FOUND; for (Index = 0; Index < PublicKeyCount; ++Index) { if (SwapBytes32 (PublicKeys[Index].GroupId) == Key) { *ChosenPublicKey = &PublicKeys[Index]; return EFI_SUCCESS; } } return Status; } STATIC BOOLEAN IsBuiltinGpuAvailable ( VOID ) { EFI_STATUS Status; UINT32 Value; EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *Interface; Status = gBS->LocateProtocol ( &gEfiPciRootBridgeIoProtocolGuid, NULL, (VOID **)&Interface ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCPAVP: Failed to find PCI root protocol - %r\n", Status)); return FALSE; } // // IGPU_DEVICE_ID = 0x2 // PCI_VENDOR_ID_OFFSET = 0x0 // (IGPU_DEVICE_ID << 16U | PCI_VENDOR_ID_OFFSET) // See EFI_PCI_ADDRESS // Status = Interface->Pci.Read ( Interface, EfiPciWidthUint32, 0x20000, 1, &Value ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCPAVP: Failed to read from IGPU device - %r\n", Status)); return FALSE; } DEBUG ((DEBUG_INFO, "OCPAVP: IGPU is %X\n", Value)); return Value != 0xFFFFFFFFU; } STATIC EFI_STATUS NeedsEpidProvisioning ( VOID ) { EFI_STATUS Status; UINT32 Data; UINTN DataSize; if (IsBuiltinGpuAvailable ()) { DataSize = sizeof (Data); Status = gRT->GetVariable ( APPLE_EPID_PROVISIONED_VARIABLE_NAME, &gEfiGlobalVariableGuid, NULL, &DataSize, &Data ); #ifdef FORCE_PROVISIONING Data = 0; #endif if (EFI_ERROR (Status) || (Data != 1)) { return EFI_SUCCESS; } } return EFI_NOT_FOUND; } STATIC EFI_STATUS NeedsFpfProvisioning ( VOID ) { EFI_STATUS Status; UINT32 Data; UINTN DataSize; APPLE_FPF_CONFIGURATION_HOB *Hob; #if 0 Hob = GetFirstGuidHob (&gAppleFpfConfigurationHobGuid); #else Hob = NULL; #endif DEBUG ((DEBUG_INFO, "OCPAVP: HOB for FPF is %p\n", Hob)); if ((Hob == NULL) || Hob->ShouldProvision) { DataSize = sizeof (Data); Status = gRT->GetVariable ( APPLE_FPF_PROVISIONED_VARIABLE_NAME, &gEfiGlobalVariableGuid, NULL, &DataSize, &Data ); if (EFI_ERROR (Status) || (Data != 1)) { return EFI_SUCCESS; } return EFI_NOT_FOUND; } return EFI_UNSUPPORTED; } EFI_STATUS OcPerformEpidProvisioning ( VOID ) { EFI_STATUS Status; UINTN Index; HECI_CLIENT_PROPERTIES Properties; EPID_GROUP_PUBLIC_KEY *EpidGroupPublicKeys; EPID_CERTIFICATE *EpidCertificate; UINT32 EpidGroupPublicKeysCount; UINT32 EpidStatus; UINT32 EpidGroupId; EPID_GROUP_PUBLIC_KEY *EpidCurrentGroupPublicKey; BOOLEAN SetVar; Status = NeedsEpidProvisioning (); DEBUG ((DEBUG_INFO, "OCPAVP: Needs provisioning EPID - %r\n", Status)); if (EFI_ERROR (Status)) { return EFI_ALREADY_STARTED; } Status = HeciLocateProtocol (); DEBUG ((DEBUG_INFO, "OCPAVP: HECI protocol lookup - %r\n", Status)); if (EFI_ERROR (Status)) { return Status; } Status = ReadProvisioningData ( &EpidCertificate, &EpidGroupPublicKeys, &EpidGroupPublicKeysCount ); DEBUG ((DEBUG_INFO, "OCPAVP: Provisioning data - %r\n", Status)); if (EFI_ERROR (Status)) { return Status; } // // Get Management Engine proccesses namespace. // Each App like PAVP or FPF have unique identifier represented as GUID. // Status = HeciGetClientMap (mMeClientMap, &mMeClientActiveCount); DEBUG ((DEBUG_INFO, "OCPAVP: Got %d clients - %r\n", mMeClientActiveCount, Status)); if (EFI_ERROR (Status)) { return Status; } Status = EFI_NOT_FOUND; for (Index = 0; Index < mMeClientActiveCount; ++Index) { Status = HeciGetClientProperties ( mMeClientMap[Index], &Properties ); DEBUG (( DEBUG_INFO, "OCPAVP: Client %u has %g protocol - %r\n", (UINT32)Index, Properties.ProtocolName, Status )); if (EFI_ERROR (Status)) { break; } if (CompareGuid (&Properties.ProtocolName, &gMePavpProtocolGuid)) { break; } } if (!EFI_ERROR (Status) && (Index != mMeClientActiveCount)) { DEBUG ((DEBUG_INFO, "OCPAVP: Found application at %u\n", (UINT32)Index)); Status = HeciConnectToClient (mMeClientMap[Index]); if (!EFI_ERROR (Status)) { EpidStatus = EpidGroupId = 0; Status = HeciPavpRequestProvisioning (&EpidStatus, &EpidGroupId); DEBUG ((DEBUG_INFO, "OCPAVP: Got EPID status %X and group id %x - %r\n", EpidStatus, EpidGroupId, Status)); } if (!EFI_ERROR (Status)) { if (EpidStatus == EPID_STATUS_PROVISIONED) { SetProvisioningVariable (APPLE_EPID_PROVISIONED_VARIABLE_NAME, 1); } else if (EpidStatus == EPID_STATUS_CAN_PROVISION) { Status = GetGroupPublicKey ( EpidGroupPublicKeys, EpidGroupPublicKeysCount, EpidGroupId, &EpidCurrentGroupPublicKey ); DEBUG ((DEBUG_INFO, "OCPAVP: Got EPID group public key - %r\n", Status)); if (!EFI_ERROR (Status)) { Status = HeciPavpPerformProvisioning (EpidCertificate, EpidCurrentGroupPublicKey, &SetVar); DEBUG ((DEBUG_INFO, "OCPAVP: Sent EPID certificate - %r / %d\n", Status, SetVar)); if (!EFI_ERROR (Status) || SetVar) { SetProvisioningVariable (APPLE_EPID_PROVISIONED_VARIABLE_NAME, 1); } } } } HeciDisconnectFromClients (); } else { DEBUG ((DEBUG_INFO, "OCPAVP: No EPID application found\n")); if (Index == mMeClientActiveCount) { // // Do not retry provisioning on incompatible firmware. // TODO: Do we really need this? // SetProvisioningVariable (APPLE_EPID_PROVISIONED_VARIABLE_NAME, 1); Status = EFI_NOT_FOUND; } } gBS->FreePool (EpidCertificate); gBS->FreePool (EpidGroupPublicKeys); return Status; } EFI_STATUS OcPerformFpfProvisioning ( VOID ) { EFI_STATUS Status; UINTN Index; HECI_CLIENT_PROPERTIES Properties; UINT32 FpfStatus; Status = NeedsFpfProvisioning (); DEBUG ((DEBUG_INFO, "OCPAVP: Needs provisioning FPF - %r\n", Status)); if (EFI_ERROR (Status)) { return EFI_ALREADY_STARTED; } Status = HeciLocateProtocol (); DEBUG ((DEBUG_INFO, "OCPAVP: HECI protocol lookup - %r\n", Status)); if (EFI_ERROR (Status)) { return Status; } // // Get Management Engine proccesses namespace. // Each App like PAVP or FPF have unique identifier represented as GUID. // Status = HeciGetClientMap (mMeClientMap, &mMeClientActiveCount); DEBUG ((DEBUG_INFO, "OCPAVP: Got %d clients - %r\n", mMeClientActiveCount, Status)); if (EFI_ERROR (Status)) { return Status; } Status = EFI_NOT_FOUND; for (Index = 0; Index < mMeClientActiveCount; ++Index) { Status = HeciGetClientProperties ( mMeClientMap[Index], &Properties ); DEBUG (( DEBUG_INFO, "OCPAVP: Client %u has %g protocol - %r\n", (UINT32)Index, Properties.ProtocolName, Status )); if (EFI_ERROR (Status)) { break; } if (CompareGuid (&Properties.ProtocolName, &gMeFpfProtocolGuid)) { break; } } if (!EFI_ERROR (Status) && (Index != mMeClientActiveCount)) { DEBUG ((DEBUG_INFO, "OCPAVP: Found application at %u\n", (UINT32)Index)); Status = HeciConnectToClient (mMeClientMap[Index]); // // I *think* FPF provisioning locks fuses from further update. // For this reason we do not want it. // if (!EFI_ERROR (Status)) { Status = HeciFpfGetStatus (&FpfStatus); DEBUG ((DEBUG_INFO, "OCPAVP: Got FPF status %u - %r\n", FpfStatus, Status)); if (!EFI_ERROR (Status)) { if (FpfStatus == 250) { Status = HeciFpfProvision (&FpfStatus); DEBUG ((DEBUG_INFO, "OCPAVP: Got FPF provisioning %u - %r\n", FpfStatus, Status)); if (!EFI_ERROR (Status) && (FpfStatus == 0)) { SetProvisioningVariable (APPLE_FPF_PROVISIONED_VARIABLE_NAME, 1); } else { Status = EFI_DEVICE_ERROR; } } else { Status = EFI_DEVICE_ERROR; } } HeciDisconnectFromClients (); } } else { DEBUG ((DEBUG_INFO, "OCPAVP: No FPF application found\n")); if (Index == mMeClientActiveCount) { // // Do not retry provisioning on incompatible firmware. // TODO: Do we really need this? // SetProvisioningVariable (APPLE_FPF_PROVISIONED_VARIABLE_NAME, 1); Status = EFI_NOT_FOUND; } } return Status; } STATIC VOID OcPerformProvisioning ( VOID ) { EFI_STATUS Status; DEBUG ((DEBUG_INFO, "OCPAVP: Checking PAVPC register...\n")); UINT32 PAVPC = MmPci32 (0, SA_MC_BUS, 0, 0, R_SA_PAVPC); DEBUG ((DEBUG_INFO, "OCPAVP: Current PAVPC is %X\n", PAVPC)); if ((PAVPC & BIT2) == 0) { MmPci32 (0, SA_MC_BUS, 0, 0, R_SA_PAVPC) = (PAVPC & (~BIT2)) | BIT4; PAVPC = MmPci32 (0, SA_MC_BUS, 0, 0, R_SA_PAVPC); DEBUG ((DEBUG_INFO, "OCPAVP: New PAVPC is %X\n", PAVPC)); } DEBUG ((DEBUG_INFO, "OCPAVP: Starting EPID provisioning\n")); Status = OcPerformEpidProvisioning (); DEBUG ((DEBUG_INFO, "OCPAVP: Done EPID provisioning - %r\n", Status)); #if 0 DEBUG ((DEBUG_INFO, "OCPAVP: Starting FPF provisioning\n")); Status = OcPerformFpfProvisioning (); DEBUG ((DEBUG_INFO, "OCPAVP: Done FPF provisioning - %r\n", Status)); #endif } EFI_STATUS EFIAPI UefiMain ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { OcPerformProvisioning (); gBS->Stall (SECONDS_TO_MICROSECONDS (3)); return EFI_SUCCESS; }