/** @file Provides an interface to the SMM SPI Device driver. gSpiDeviceProtocolGuid (DXE: SpiDeviceSmmDxe) | | via gEfiSmmCommunicationProtocolGuid V gSmmSpiDeviceProtocolGuid (SMM: SpiDeviceSmm) | | V gEfiSmmSpi2ProtocolGuid (SMM: SpiSmm) Copyright (c) 2015, Intel Corporation. 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 "SpiDevice.h" #include "SpiDeviceSmmComm.h" EFI_SMM_COMMUNICATION_PROTOCOL *mSmmComm = NULL; SPI_DEVICE_PROTOCOL mSpiDevProtocol = { SpiRead, SpiWrite, SpiErase, SpiLock, SpiSetRange, SpiLockRanges }; VOID EFIAPI SmmSpiDeviceReady ( IN EFI_EVENT Event, IN VOID *Context ); EFI_STATUS CreateCommBuffer ( OUT VOID **CommBuffer, OUT VOID **DataArea, IN UINTN DataSize, IN UINTN Function ) { EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER *SmmSpiDevFunctionHeader; // // Allocate communication buffer. // SmmCommunicateHeader = AllocatePool (DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER_SIZE); if (SmmCommunicateHeader == NULL) { return EFI_OUT_OF_RESOURCES; } // // Fill in new structure will data from caller. // CopyGuid (&SmmCommunicateHeader->HeaderGuid, &gSmmSpiDeviceProtocolGuid); SmmCommunicateHeader->MessageLength = DataSize + SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER_SIZE; SmmSpiDevFunctionHeader = (SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER*) SmmCommunicateHeader->Data; SmmSpiDevFunctionHeader->Function = Function; // // Assign return values. // *CommBuffer = SmmCommunicateHeader; if (DataArea != NULL) { *DataArea = SmmSpiDevFunctionHeader->Data; } return EFI_SUCCESS; } EFI_STATUS SendCommBuffer ( IN OUT EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader, IN UINTN DataSize ) { EFI_STATUS Status; UINTN CommSize; SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER *SmmSpiDevFunctionHeader; // // Compute actual size of communication data. // CommSize = DataSize + SMM_COMMUNICATE_HEADER_SIZE + SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER_SIZE; // // Send the message to be processed in SMM. // Status = mSmmComm->Communicate (mSmmComm, SmmCommunicateHeader, &CommSize); if (EFI_ERROR (Status)) { return Status; } // // Get the return value from the SMM function. // SmmSpiDevFunctionHeader = (SMM_SPI_DEV_COMMUNICATE_FUNCTION_HEADER*) SmmCommunicateHeader->Data; return SmmSpiDevFunctionHeader->ReturnStatus; } EFI_STATUS EFIAPI SpiRead ( IN UINTN SpiOffset, IN OUT UINTN *Size, OUT UINT8 *Buffer ) { EFI_STATUS Status; UINTN DataSize; EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; SMM_SPI_DEV_READ_WRITE_ERASE_HEADER *SpiDevReadHeader; // // Validate input parameters. // if (Size == NULL || Buffer == NULL) { return EFI_INVALID_PARAMETER; } // // Determine the actual data size required for the transaction. // DataSize = *Size + sizeof(SMM_SPI_DEV_READ_WRITE_ERASE_HEADER); // // Create the communication buffer. // Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, (VOID**) &SpiDevReadHeader, DataSize, SPI_DEV_FUNCTION_READ); if (EFI_ERROR (Status)) { return Status; } // // Fill in communication buffer parameters. // SpiDevReadHeader->Offset = SpiOffset; SpiDevReadHeader->Size = *Size; // // Communicate request to SMM driver and fill in return values. // Status = SendCommBuffer (SmmCommunicateHeader, DataSize); *Size = SpiDevReadHeader->Size; if (!EFI_ERROR (Status)) { CopyMem (Buffer, (UINT8*)(SpiDevReadHeader + 1), *Size); } return Status; } EFI_STATUS EFIAPI SpiWrite ( IN UINTN SpiOffset, IN OUT UINTN *Size, IN UINT8 *Buffer ) { EFI_STATUS Status; UINTN DataSize; EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; SMM_SPI_DEV_READ_WRITE_ERASE_HEADER *SpiDevWriteHeader; // // Validate input parameters. // if (Size == NULL || Buffer == NULL) { return EFI_INVALID_PARAMETER; } // // Determine the actual data size required for the transaction. // DataSize = *Size + sizeof(SMM_SPI_DEV_READ_WRITE_ERASE_HEADER); // // Create the communication buffer. // Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, (VOID**) &SpiDevWriteHeader, DataSize, SPI_DEV_FUNCTION_WRITE); if (EFI_ERROR (Status)) { return Status; } // // Fill in communication buffer parameters. // SpiDevWriteHeader->Offset = SpiOffset; SpiDevWriteHeader->Size = *Size; CopyMem ((UINT8*)(SpiDevWriteHeader + 1), Buffer, *Size); // // Communicate request to SMM driver and fill in return values. // Status = SendCommBuffer (SmmCommunicateHeader, DataSize); *Size = SpiDevWriteHeader->Size; return Status; } EFI_STATUS EFIAPI SpiErase ( IN UINTN SpiOffset, IN OUT UINTN Size ) { EFI_STATUS Status; UINTN DataSize; EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; SMM_SPI_DEV_READ_WRITE_ERASE_HEADER *SpiDevEraseHeader; // // Determine the actual data size required for the transaction. // DataSize = sizeof(SMM_SPI_DEV_READ_WRITE_ERASE_HEADER); // // Create the communication buffer. // Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, (VOID**) &SpiDevEraseHeader, DataSize, SPI_DEV_FUNCTION_ERASE); if (EFI_ERROR (Status)) { return Status; } // // Fill in communication buffer parameters. // SpiDevEraseHeader->Offset = SpiOffset; SpiDevEraseHeader->Size = Size; // // Communicate request to SMM driver and fill in return values. // Status = SendCommBuffer (SmmCommunicateHeader, DataSize); return Status; } EFI_STATUS EFIAPI SpiLock ( IN UINTN SpiOffset, IN OUT UINTN Size, IN BOOLEAN Lock ) { EFI_STATUS Status; UINTN DataSize; EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; SMM_SPI_DEV_LOCK_HEADER *SmmSpiDevLockHeader; // // Compute data size required for the transaction. // DataSize = sizeof(SMM_SPI_DEV_LOCK_HEADER); // // Create the communication buffer. // Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, (VOID**) &SmmSpiDevLockHeader, DataSize, SPI_DEV_FUNCTION_LOCK); if (EFI_ERROR (Status)) { return Status; } // // Fill in communication buffer parameters. // SmmSpiDevLockHeader->Offset = SpiOffset; SmmSpiDevLockHeader->Size = Size; SmmSpiDevLockHeader->Lock = Lock; // // Communicate request to SMM driver and fill in return values. // Status = SendCommBuffer (SmmCommunicateHeader, DataSize); return Status; } EFI_STATUS EFIAPI SpiSetRange ( IN UINTN SpiOffset, IN UINTN Size, IN BOOLEAN ReadLock, IN BOOLEAN WriteLock ) { EFI_STATUS Status; UINTN DataSize; EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; SMM_SPI_DEV_SET_RANGE_HEADER *SmmSpiDevSetRangeHeader; // // Compute data size required for the transaction. // DataSize = sizeof(SMM_SPI_DEV_SET_RANGE_HEADER); // // Create the communication buffer. // Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, (VOID**) &SmmSpiDevSetRangeHeader, DataSize, SPI_DEV_FUNCTION_SET_RANGE); if (EFI_ERROR (Status)) { return Status; } // // Fill in communication buffer parameters. // SmmSpiDevSetRangeHeader->Offset = SpiOffset; SmmSpiDevSetRangeHeader->Size = Size; SmmSpiDevSetRangeHeader->ReadLock = ReadLock; SmmSpiDevSetRangeHeader->WriteLock = WriteLock; // // Communicate request to SMM driver and fill in return values. // Status = SendCommBuffer (SmmCommunicateHeader, DataSize); return Status; } EFI_STATUS EFIAPI SpiLockRanges ( ) { EFI_STATUS Status; EFI_SMM_COMMUNICATE_HEADER *SmmCommunicateHeader; // // Create the communication buffer. // Status = CreateCommBuffer ((VOID**) &SmmCommunicateHeader, NULL, 0, SPI_DEV_FUNCTION_LOCK_RANGES); if (EFI_ERROR (Status)) { return Status; } // // Communicate request to SMM driver and fill in return values. // Status = SendCommBuffer (SmmCommunicateHeader, 0); return Status; } EFI_STATUS EFIAPI InitSpiDevice ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { VOID *SmmSpiDeviceReg; // // Register for a callback when the SMM version of the SPI Device protocol // is installed. // EfiCreateProtocolNotifyEvent ( &gSmmSpiDeviceProtocolGuid, TPL_CALLBACK, SmmSpiDeviceReady, NULL, &SmmSpiDeviceReg ); return EFI_SUCCESS; } VOID EFIAPI SmmSpiDeviceReady ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_HANDLE Handle; SPI_DEVICE_PROTOCOL *SmmSpiDevice; EFI_STATUS Status; // // Locate the protocol first just to make sure it was actually installed. // Status = gBS->LocateProtocol ( &gSmmSpiDeviceProtocolGuid, NULL, (VOID **) &SmmSpiDevice ); if (EFI_ERROR (Status)) { return; } // // SMM Service installed so get communication link to SMM // Status = gBS->LocateProtocol ( &gEfiSmmCommunicationProtocolGuid, NULL, (VOID **) &mSmmComm ); ASSERT_EFI_ERROR (Status); // // Install DXE protocol so it can be used by drivers. // Handle = NULL; Status = gBS->InstallProtocolInterface ( &Handle, &gSpiDeviceProtocolGuid, EFI_NATIVE_INTERFACE, &mSpiDevProtocol ); ASSERT_EFI_ERROR (Status); }