/** @file This file implements interaction with HECI. Copyright (c) 2019, vit9696. All rights reserved.
Portions copyright (c) 2019, savvas. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause **/ #include #include #include #include #include #include #include #include #include #include #include STATIC UINT8 mCurrentMeClientRequestedReceiveMsg; STATIC UINT8 mCurrentMeClientCanReceiveMsg; STATIC UINT8 mCurrentMeClientAddress; STATIC EFI_HECI_PROTOCOL *mHeci; STATIC EFI_HECI2_PROTOCOL *mHeci2; STATIC BOOLEAN mSendingHeciCommand; STATIC BOOLEAN mSendingHeciCommandPerClient; EFI_STATUS HeciReadMessage ( IN UINT32 Blocking, IN UINT32 *MessageBody, IN OUT UINT32 *Length ) { if (mHeci != NULL) { return mHeci->ReadMsg ( Blocking, MessageBody, Length ); } if (mHeci2 != NULL) { return mHeci2->ReadMsg ( HECI_DEFAULT_DEVICE, Blocking, MessageBody, Length ); } DEBUG ((DEBUG_INFO, "OCME: No ME protocol loaded, cannot read message\n")); return EFI_NOT_FOUND; } EFI_STATUS HeciSendMessage ( IN UINT32 *Message, IN UINT32 Length, IN UINT8 HostAddress, IN UINT8 MEAddress ) { if (mHeci != NULL) { return mHeci->SendMsg ( Message, Length, HostAddress, MEAddress ); } if (mHeci2 != NULL) { return mHeci2->SendMsg ( HECI_DEFAULT_DEVICE, Message, Length, HostAddress, MEAddress ); } DEBUG ((DEBUG_INFO, "OCME: No ME protocol loaded, cannot send message\n")); return EFI_NOT_FOUND; } EFI_STATUS HeciLocateProtocol ( VOID ) { EFI_STATUS Status; if ((mHeci != NULL) || (mHeci2 != NULL)) { return EFI_SUCCESS; } Status = gBS->LocateProtocol ( &gEfiHeciProtocolGuid, NULL, (VOID **)&mHeci ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCME: Falling back to HECI 2 protocol - %r\n", Status)); // // Ensure we don't have both set // mHeci = NULL; Status = gBS->LocateProtocol ( &gEfiHeci2ProtocolGuid, NULL, (VOID **)&mHeci2 ); } if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCME: Failed to find any HECI protocol - %r\n", Status)); } return Status; } VOID HeciUpdateReceiveMsgStatus ( VOID ) { EFI_STATUS Status; UINT32 Size; HBM_FLOW_CONTROL Command; if (mSendingHeciCommandPerClient) { ZeroMem (&Command, sizeof (Command)); Size = sizeof (Command); Status = HeciReadMessage ( BLOCKING, (UINT32 *)&Command, &Size ); if (!EFI_ERROR (Status) && (Command.Command.Fields.Command == FLOW_CONTROL)) { ++mCurrentMeClientCanReceiveMsg; } } } EFI_STATUS HeciGetResponse ( OUT VOID *MessageData, IN UINT32 ResponseSize ) { EFI_STATUS Status; HBM_FLOW_CONTROL Command; Status = EFI_NOT_READY; STATIC_ASSERT (sizeof (HBM_FLOW_CONTROL) == 8, "Invalid ME command size"); if (mSendingHeciCommandPerClient || mSendingHeciCommand) { ZeroMem (MessageData, ResponseSize); // // Note, this was reworked to make more sense. // https://github.com/osy86/OpenCorePkg/commit/8f7188d41876109aec2fe3a721f69daf979dd268.diff // if (!mCurrentMeClientRequestedReceiveMsg) { ZeroMem (&Command, sizeof (Command)); Command.Command.Fields.Command = FLOW_CONTROL; Command.MeAddress = mCurrentMeClientAddress; Command.HostAddress = HBM_CLIENT_ADDRESS; Status = HeciSendMessage ( (UINT32 *)&Command, sizeof (Command), HBM_HOST_ADDRESS, HBM_ME_ADDRESS ); if (!EFI_ERROR (Status)) { ++mCurrentMeClientRequestedReceiveMsg; } } Status = HeciReadMessage ( BLOCKING, MessageData, &ResponseSize ); if (!EFI_ERROR (Status)) { --mCurrentMeClientRequestedReceiveMsg; } } return Status; } EFI_STATUS HeciSendMessageWithResponse ( IN OUT VOID *MessageData, IN UINT32 RequestSize, IN UINT32 ResponseSize ) { HECI_BUS_MESSAGE *Message; HBM_COMMAND Command; EFI_STATUS Status; mSendingHeciCommand = TRUE; Message = (HECI_BUS_MESSAGE *)MessageData; Command = Message->Command; Status = HeciSendMessage ( MessageData, RequestSize, HBM_HOST_ADDRESS, HBM_ME_ADDRESS ); if (!EFI_ERROR (Status)) { Status = HeciGetResponse (MessageData, ResponseSize); if ( !EFI_ERROR (Status) && (Command.Fields.Command != Message->Command.Fields.Command)) { Status = EFI_PROTOCOL_ERROR; } } mSendingHeciCommand = FALSE; return Status; } EFI_STATUS HeciGetClientMap ( OUT UINT8 *ClientMap, OUT UINT8 *ClientActiveCount ) { EFI_STATUS Status; HBM_HOST_ENUMERATION_BUFFER Command; UINTN Index; UINTN Index2; UINT8 *ValidAddressesPtr; UINT32 ValidAddresses; *ClientActiveCount = 0; Status = HeciLocateProtocol (); if (EFI_ERROR (Status)) { return Status; } STATIC_ASSERT (sizeof (Command.Request) == 4, "Invalid ME command size"); STATIC_ASSERT (sizeof (Command.Response) == 36, "Invalid ME command size"); ZeroMem (&Command, sizeof (Command)); Command.Request.Command.Fields.Command = HOST_ENUMERATION_REQUEST; Status = HeciSendMessageWithResponse ( &Command, sizeof (Command.Request), sizeof (Command.Response) ); if (EFI_ERROR (Status)) { return Status; } ValidAddressesPtr = &Command.Response.ValidAddresses[0]; for (Index = 0; Index < HBM_ME_CLIENT_MAX; Index += OC_CHAR_BIT) { ValidAddresses = *ValidAddressesPtr; for (Index2 = 0; Index2 < OC_CHAR_BIT; Index2++) { if ((ValidAddresses & (1U << Index2)) != 0) { ClientMap[*ClientActiveCount] = (UINT8)(Index + Index2); ++(*ClientActiveCount); } } ++ValidAddressesPtr; } return Status; } EFI_STATUS HeciGetClientProperties ( IN UINT8 Address, OUT HECI_CLIENT_PROPERTIES *Properties ) { EFI_STATUS Status; HBM_HOST_CLIENT_PROPERTIES_BUFFER Command; Status = HeciLocateProtocol (); if (EFI_ERROR (Status)) { return Status; } STATIC_ASSERT (sizeof (Command.Request) == 4, "Invalid ME command size"); STATIC_ASSERT (sizeof (Command.Response) == 28, "Invalid ME command size"); ZeroMem (&Command, sizeof (Command)); Command.Request.Command.Fields.Command = HOST_CLIENT_PROPERTIES_REQUEST; Command.Request.Address = Address; Status = HeciSendMessageWithResponse ( &Command, sizeof (Command.Request), sizeof (Command.Response) ); CopyMem ( Properties, &Command.Response.ClientProperties, sizeof (*Properties) ); return Status; } EFI_STATUS HeciConnectToClient ( IN UINT8 Address ) { EFI_STATUS Status; HBM_CLIENT_CONNECT_BUFFER Command; Status = HeciLocateProtocol (); if (EFI_ERROR (Status)) { return Status; } ZeroMem (&Command, sizeof (Command)); STATIC_ASSERT (sizeof (Command.Request) == 4, "Invalid ME command size"); Command.Request.Command.Fields.Command = CLIENT_CONNECT_REQUEST; Command.Request.MeAddress = Address; Command.Request.HostAddress = HBM_CLIENT_ADDRESS; Status = HeciSendMessageWithResponse ( &Command, sizeof (Command.Request), sizeof (Command.Response) ); DEBUG ((DEBUG_INFO, "OCME: Connect to client %X code %d - %r\n", Address, Command.Response.Status, Status)); if (EFI_ERROR (Status)) { return Status; } switch (Command.Response.Status) { case HBM_CLIENT_CONNECT_NOT_FOUND: return EFI_NOT_FOUND; case HBM_CLIENT_CONNECT_ALREADY_CONNECTED: return EFI_ALREADY_STARTED; case HBM_CLIENT_CONNECT_OUT_OF_RESOURCES: return EFI_OUT_OF_RESOURCES; case HBM_CLIENT_CONNECT_INVALID_PARAMETER: return EFI_INVALID_PARAMETER; default: mSendingHeciCommandPerClient = TRUE; mCurrentMeClientRequestedReceiveMsg = 0; mCurrentMeClientCanReceiveMsg = 0; mCurrentMeClientAddress = Address; return EFI_SUCCESS; } } EFI_STATUS HeciSendMessagePerClient ( IN VOID *Message, IN UINT32 Size ) { EFI_STATUS Status; Status = EFI_SUCCESS; if (mSendingHeciCommandPerClient) { if (!mCurrentMeClientCanReceiveMsg) { HeciUpdateReceiveMsgStatus (); } Status = HeciSendMessage ( Message, Size, HBM_CLIENT_ADDRESS, mCurrentMeClientAddress ); if (!EFI_ERROR (Status)) { --mCurrentMeClientCanReceiveMsg; } } return Status; } EFI_STATUS HeciDisconnectFromClients ( VOID ) { EFI_STATUS Status; HBM_CLIENT_DISCONNECT_BUFFER Command; Status = EFI_SUCCESS; if (mSendingHeciCommandPerClient) { // // Note, this is different between HECI 1 and HECI 2. // HECI 1 has 4 byte response, and it does not require HeciUpdateReceiveMsgStatus. // STATIC_ASSERT (sizeof (Command.Request) == 4, "Invalid ME command req size"); STATIC_ASSERT (sizeof (Command.Response) == 8, "Invalid ME command rsp size"); if (!mCurrentMeClientCanReceiveMsg) { HeciUpdateReceiveMsgStatus (); } ZeroMem (&Command, sizeof (Command)); Command.Request.Command.Fields.Command = CLIENT_DISCONNECT_REQUEST; Command.Request.MeAddress = mCurrentMeClientAddress; Command.Request.HostAddress = HBM_CLIENT_ADDRESS; ++mCurrentMeClientRequestedReceiveMsg; Status = HeciSendMessageWithResponse ( &Command, sizeof (Command.Request), sizeof (Command.Response) ); DEBUG (( DEBUG_INFO, "OCME: Disconnect from client %X code %d - %r\n", mCurrentMeClientAddress, Command.Response.Status, Status )); if (!EFI_ERROR (Status)) { mSendingHeciCommandPerClient = FALSE; } } return Status; } EFI_STATUS HeciPavpRequestProvisioning ( OUT UINT32 *EpidStatus, OUT UINT32 *EpidGroupId ) { EFI_STATUS Status; ME_PAVP_PROVISION_REQUEST_BUFFER Command; STATIC_ASSERT (sizeof (Command.Request) == 16, "Invalid ME command size"); STATIC_ASSERT (sizeof (Command.Response) == 24, "Invalid ME command size"); ZeroMem (&Command, sizeof (Command)); Command.Request.Header.Version = ME_PAVP_PROTOCOL_VERSION; Command.Request.Header.Command = ME_PAVP_PROVISION_REQUEST_COMMAND; HeciSendMessagePerClient (&Command, sizeof (Command.Request)); ZeroMem (&Command, sizeof (Command)); Status = HeciGetResponse (&Command, sizeof (Command.Response)); if (!EFI_ERROR (Status)) { *EpidStatus = Command.Response.Status; *EpidGroupId = Command.Response.GroupId; } return Status; } EFI_STATUS HeciPavpPerformProvisioning ( IN EPID_CERTIFICATE *EpidCertificate, IN EPID_GROUP_PUBLIC_KEY *EpidGroupPublicKey, OUT BOOLEAN *SetVar OPTIONAL ) { EFI_STATUS Status; ME_PAVP_PROVISION_PERFORM_BUFFER Command; UINTN Index; STATIC_ASSERT (sizeof (Command.Request) == 1284, "Invalid ME command size"); STATIC_ASSERT (sizeof (Command.Response) == 16, "Invalid ME command size"); if (SetVar != NULL) { *SetVar = FALSE; } ZeroMem (&Command, sizeof (Command)); Command.Request.Header.Version = ME_PAVP_PROTOCOL_VERSION; Command.Request.Header.Command = ME_PAVP_PROVISION_PERFORM_COMMAND; Command.Request.Header.PayloadSize = ME_PAVP_PROVISION_PERFORM_PAYLOAD_SIZE; CopyMem (&Command.Request.Certificate, EpidCertificate, sizeof (Command.Request.Certificate)); CopyMem (&Command.Request.PublicKey, EpidGroupPublicKey, sizeof (Command.Request.PublicKey)); Status = HeciSendMessagePerClient (&Command, sizeof (Command.Request)); ZeroMem (&Command, sizeof (Command)); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCME: Failed to send provisioning command - %r\n", Status)); return EFI_DEVICE_ERROR; } for (Index = 0; Index < 3; ++Index) { Status = HeciGetResponse (&Command, sizeof (Command.Response)); if (Status != EFI_TIMEOUT) { break; } } DEBUG (( DEBUG_INFO, "OCME: Finished provisioning command with status %x - %r\n", Command.Response.Header.Status, Status )); if (EFI_ERROR (Status)) { return EFI_DEVICE_ERROR; } if (Command.Response.Header.Status != EPID_STATUS_PROVISIONED) { Status = EFI_DEVICE_ERROR; } if (Command.Response.Header.Status == EPID_STATUS_FAIL_PROVISION) { if (SetVar != NULL) { *SetVar = TRUE; } } return Status; } EFI_STATUS HeciFpfGetStatus ( OUT UINT32 *FpfStatus ) { EFI_STATUS Status; UINT32 Response[11]; UINT32 Request[4]; ZeroMem (Request, sizeof (Request)); Request[0] = 3; HeciSendMessagePerClient (Request, sizeof (Request)); ZeroMem (Response, sizeof (Response)); Status = HeciGetResponse (Response, sizeof (Response)); if (!EFI_ERROR (Status)) { *FpfStatus = Response[1]; } return Status; } EFI_STATUS HeciFpfProvision ( OUT UINT32 *FpfStatus ) { EFI_STATUS Status; UINT32 Response[2]; UINT32 Request[3]; ZeroMem (Request, sizeof (Request)); Request[0] = 5; Request[1] = 1; Request[2] = 255; HeciSendMessagePerClient (Request, sizeof (Request)); ZeroMem (Response, sizeof (Response)); Status = HeciGetResponse (Response, sizeof (Response)); if (!EFI_ERROR (Status)) { *FpfStatus = Response[1]; } return Status; }