401 lines
9.9 KiB
C
401 lines
9.9 KiB
C
/** @file
|
|
Console redirection SMM drvier
|
|
;******************************************************************************
|
|
;* Copyright (c) 2012 - 2016, Insyde Software Corp. All Rights Reserved.
|
|
;*
|
|
;* You may not reproduce, distribute, publish, display, perform, modify, adapt,
|
|
;* transmit, broadcast, present, recite, release, license or otherwise exploit
|
|
;* any part of this publication in any form, by any means, without the prior
|
|
;* written permission of Insyde Software Corporation.
|
|
;*
|
|
;******************************************************************************
|
|
*/
|
|
#include <Library/UefiBootServicesTableLib.h>
|
|
#include <Library/UefiLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/PrintLib.h>
|
|
#include <Library/SmmServicesTableLib.h>
|
|
#include <Library/IoLib.h>
|
|
#include <Library/VariableLib.h>
|
|
|
|
#include <Protocol/SmmCpu.h>
|
|
#include <Protocol/SmmSwDispatch2.h>
|
|
#include <Protocol/SmmReadyToLock.h>
|
|
#include <Protocol/Uart16550Access.h>
|
|
#include <Protocol/ConsoleRedirectionService.h>
|
|
|
|
#include "CrServiceSmm.h"
|
|
|
|
//
|
|
// Driver
|
|
//
|
|
#define SMI_PORT PcdGet16 (PcdSoftwareSmiPort)
|
|
#define SW_SMI_CRS PcdGet8 (PcdH2OCrSoftwareSmi)
|
|
#define REG_COUNT 5
|
|
|
|
EFI_SMM_CPU_PROTOCOL *mSmmCpu = NULL;
|
|
CR_REG_ARRAY SaveReg[REG_COUNT] = {0};
|
|
UINTN gDeviceCount = 0;
|
|
UART_16550_DEVICE_INFO *gDeviceInfo;
|
|
|
|
|
|
/**
|
|
This fucntion uses to read saved CPU double word register by CPU index
|
|
|
|
@param[In] RegisterNum Register number which want to get
|
|
@param[In] CpuIndex CPU index number to get register.
|
|
@param[Out] RegisterData pointer to output register data
|
|
|
|
@retval EFI_SUCCESS Read double word register successfully
|
|
@return Other Any error occured while disabling all secure boot SMI functions successful.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
GetDwordRegisterByCpuIndex (
|
|
IN EFI_SMM_SAVE_STATE_REGISTER RegisterNum,
|
|
IN UINTN CpuIndex,
|
|
OUT UINT32 *RegisterData
|
|
)
|
|
{
|
|
UINTN Width;
|
|
|
|
Width = sizeof (UINT32);
|
|
*RegisterData = 0;
|
|
|
|
return mSmmCpu->ReadSaveState (
|
|
mSmmCpu,
|
|
Width,
|
|
RegisterNum,
|
|
CpuIndex,
|
|
RegisterData
|
|
);
|
|
}
|
|
|
|
/**
|
|
This fucntion uses to write saved CPU double word register by CPU index
|
|
|
|
@param[In] RegisterNum Register number which want to get
|
|
@param[In] CpuIndex CPU index number to get register.
|
|
@param[Out] RegisterData pointer to output register data
|
|
|
|
@retval EFI_SUCCESS Read double word register successfully
|
|
@return Other Any error occured while disabling all secure boot SMI functions successful.
|
|
|
|
**/
|
|
EFI_STATUS
|
|
SetDwordRegisterByCpuIndex (
|
|
IN EFI_SMM_SAVE_STATE_REGISTER RegisterNum,
|
|
IN UINTN CpuIndex,
|
|
IN UINT32 *RegisterData
|
|
)
|
|
{
|
|
UINTN Width;
|
|
|
|
Width = sizeof (UINT32);
|
|
|
|
return mSmmCpu->WriteSaveState (
|
|
mSmmCpu,
|
|
Width,
|
|
RegisterNum,
|
|
CpuIndex,
|
|
RegisterData
|
|
);
|
|
}
|
|
|
|
UINTN
|
|
GetDeviceAddress (
|
|
UINTN UartId,
|
|
UINTN RegIndex
|
|
)
|
|
{
|
|
UINTN Index;
|
|
UART_16550_DEVICE_INFO *DevInfo;
|
|
UINTN Address;
|
|
|
|
if (RegIndex > 7) {
|
|
return 0;
|
|
}
|
|
|
|
Address = 0;
|
|
|
|
for (Index = 0; Index < gDeviceCount; Index++) {
|
|
if (UartId == gDeviceInfo[Index].UartUid) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index < gDeviceCount) {
|
|
DevInfo = &gDeviceInfo[Index];
|
|
Address = DevInfo->BaseAddress + (DevInfo->RegisterByteWidth * RegIndex);
|
|
}
|
|
|
|
return Address;
|
|
}
|
|
|
|
EFI_STATUS
|
|
DoCrSmmFunction (
|
|
CR_REG_ARRAY *Reg,
|
|
UINTN CmdCount,
|
|
UINTN CpuIndex
|
|
)
|
|
{
|
|
UINTN Address;
|
|
|
|
|
|
while (CmdCount != 0) {
|
|
|
|
Address = GetDeviceAddress (Reg->Ebx.Reg16 >> 8, Reg->Ebx.Reg8); // Device index BH, Register Index BL
|
|
|
|
if (Address == 0) {
|
|
DEBUG ((DEBUG_WARN, "[CrServiceSmm] Device or Register is invalid.\n"));
|
|
|
|
} else {
|
|
switch (Reg->Ecx.Reg8) {
|
|
case MEM_W:
|
|
MmioWrite8( Address, (UINT8)Reg->Ecx.Reg.Data);
|
|
break;
|
|
|
|
case MEM_R:
|
|
Reg->Eax.Reg8 = MmioRead8( Address);
|
|
break;
|
|
|
|
case IO_W:
|
|
IoWrite8( Address, (UINT8)Reg->Ecx.Reg.Data);
|
|
break;
|
|
|
|
case IO_R:
|
|
Reg->Eax.Reg8 = IoRead8( Address);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (Reg->Ecx.Reg8 == MEM_R || Reg->Ecx.Reg8 == IO_R) {
|
|
SetDwordRegisterByCpuIndex (EFI_SMM_SAVE_STATE_REGISTER_RAX, CpuIndex, &Reg->Eax.Reg32);
|
|
}
|
|
}
|
|
|
|
CmdCount--;
|
|
Reg++;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
InitUartDeviceInfo (
|
|
VOID
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN VarSize;
|
|
VOID *VarData;
|
|
|
|
VarSize = 0;
|
|
VarData = NULL;
|
|
|
|
Status = CommonGetVariableDataAndSize ( CR_UART_DEVICE_INFO_VAR_NAME, &gH2OConsoleRedirectionServiceProtocolGuid, &VarSize, &VarData);
|
|
if (EFI_ERROR(Status)) {
|
|
ASSERT (FALSE);
|
|
return Status;
|
|
}
|
|
|
|
if ((VarSize % sizeof(UART_16550_DEVICE_INFO)) != 0) {
|
|
DEBUG ((DEBUG_ERROR, "[CrServiceSmm] Variable Size is invalid. UnitSize=%d, VarSize=%d\n", sizeof(UART_16550_DEVICE_INFO),VarSize));
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
gDeviceCount = VarSize/sizeof(UART_16550_DEVICE_INFO);
|
|
gDeviceInfo = (UART_16550_DEVICE_INFO *) VarData;
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
BOOLEAN
|
|
IsUartDeviceInfoUnInit (
|
|
VOID
|
|
)
|
|
{
|
|
if (gDeviceCount == 0) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
EFI_STATUS
|
|
CrServiceSmiCallback (
|
|
IN EFI_HANDLE DispatchHandle,
|
|
IN CONST VOID *Context OPTIONAL,
|
|
IN OUT VOID *CommBuffer OPTIONAL,
|
|
IN OUT UINTN *CommBufferSize OPTIONAL
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
UINTN Index;
|
|
UINTN CpuIndex;
|
|
CR_REG_ARRAY *Reg;
|
|
static BOOLEAN SaveMode = FALSE;
|
|
static UINTN SaveCount = 0;
|
|
|
|
//
|
|
// This smm callbak need Uart 16550 device info for reference, If device info is un-initialize, it will read the variable for initailize it.
|
|
//
|
|
if (IsUartDeviceInfoUnInit ()) {
|
|
Status = InitUartDeviceInfo ();
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Select the variable to save current REG.
|
|
//
|
|
if (SaveMode) {
|
|
if (SaveCount >= REG_COUNT) {
|
|
//
|
|
// replace last cmd.
|
|
//
|
|
SaveCount = REG_COUNT - 1;
|
|
ASSERT (FALSE);
|
|
}
|
|
Reg = &SaveReg[SaveCount];
|
|
} else {
|
|
Reg = &SaveReg[0];
|
|
}
|
|
|
|
for (Index = 0; Index < gSmst->NumberOfCpus; Index++) {
|
|
Status = GetDwordRegisterByCpuIndex (EFI_SMM_SAVE_STATE_REGISTER_RAX, Index, &Reg->Eax.Reg32);
|
|
ASSERT_EFI_ERROR(Status);
|
|
Status = GetDwordRegisterByCpuIndex (EFI_SMM_SAVE_STATE_REGISTER_RDX, Index, &Reg->Edx.Reg32);
|
|
ASSERT_EFI_ERROR(Status);
|
|
//
|
|
// Find out which CPU triggered PnP SMI
|
|
//
|
|
if ((Reg->Eax.CrsSmi == SW_SMI_CRS) && (Reg->Edx.SmiPort == PcdGet16 (PcdSoftwareSmiPort))) {
|
|
//
|
|
// Cpu found!
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
if (Index == gSmst->NumberOfCpus) {
|
|
//
|
|
// Error out due to CPU not found
|
|
//
|
|
return EFI_NOT_FOUND;
|
|
}
|
|
|
|
//
|
|
// ESI : Signatures
|
|
// AL : Soft SMI vale
|
|
// EBX : MMIO Address
|
|
// CL : Function (see. enum SMI_FUNC)
|
|
// CH : Data
|
|
// DX : SmiPort = 0xB2
|
|
//
|
|
CpuIndex = Index;
|
|
Status = GetDwordRegisterByCpuIndex (EFI_SMM_SAVE_STATE_REGISTER_RAX, CpuIndex, &Reg->Eax.Reg32);
|
|
ASSERT_EFI_ERROR(Status);
|
|
Status = GetDwordRegisterByCpuIndex (EFI_SMM_SAVE_STATE_REGISTER_RBX, CpuIndex, &Reg->Ebx.Reg32);
|
|
ASSERT_EFI_ERROR(Status);
|
|
Status = GetDwordRegisterByCpuIndex (EFI_SMM_SAVE_STATE_REGISTER_RCX, CpuIndex, &Reg->Ecx.Reg32);
|
|
ASSERT_EFI_ERROR(Status);
|
|
Status = GetDwordRegisterByCpuIndex (EFI_SMM_SAVE_STATE_REGISTER_RSI, CpuIndex, &Reg->Esi.Reg32);
|
|
ASSERT_EFI_ERROR(Status);
|
|
|
|
if (Reg->Esi.Signature != SIGNATURE_32 ('$', 'C', 'R', 'S')) {
|
|
return EFI_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (SaveMode == FALSE) {
|
|
|
|
if (Reg->Ecx.Reg8 == QUEUE_CMD) {
|
|
//
|
|
// Start Queue MODE.
|
|
//
|
|
SaveMode = TRUE;
|
|
SaveCount = 0;
|
|
return EFI_SUCCESS;
|
|
} else {
|
|
//
|
|
// Do one CMD.
|
|
//
|
|
return DoCrSmmFunction (Reg, 1, CpuIndex);
|
|
}
|
|
|
|
} else if (SaveMode == TRUE) {
|
|
|
|
if (Reg->Ecx.Reg8 == QUEUE_CMD) {
|
|
//
|
|
// Do the all of CMDs in the Queue.
|
|
//
|
|
SaveMode = FALSE;
|
|
return DoCrSmmFunction (Reg, SaveCount, CpuIndex);
|
|
|
|
} else {
|
|
//
|
|
// Store the CMD into the Queue.
|
|
//
|
|
SaveCount++;
|
|
}
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|
|
EFI_STATUS
|
|
EFIAPI
|
|
CrServiceSmmEntryPoint (
|
|
IN EFI_HANDLE ImageHandle,
|
|
IN EFI_SYSTEM_TABLE *SystemTable
|
|
)
|
|
{
|
|
EFI_STATUS Status;
|
|
EFI_HANDLE Handle;
|
|
EFI_SMM_SW_REGISTER_CONTEXT SwContext;
|
|
EFI_SMM_SW_DISPATCH2_PROTOCOL *SwDispatch;
|
|
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmSwDispatch2ProtocolGuid,
|
|
NULL,
|
|
(VOID **) &SwDispatch
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Software SMI for CR services callback function
|
|
//
|
|
Handle = NULL;
|
|
SwContext.SwSmiInputValue = SW_SMI_CRS;
|
|
Status = SwDispatch->Register (
|
|
SwDispatch,
|
|
CrServiceSmiCallback,
|
|
&SwContext,
|
|
&Handle
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
ASSERT_EFI_ERROR (Status);
|
|
return Status;
|
|
}
|
|
|
|
//
|
|
// Locate Smm Cpu protocol for Cpu save state manipulation
|
|
//
|
|
Status = gSmst->SmmLocateProtocol (
|
|
&gEfiSmmCpuProtocolGuid,
|
|
NULL,
|
|
(VOID **)&mSmmCpu
|
|
);
|
|
if (EFI_ERROR (Status)) {
|
|
return EFI_OUT_OF_RESOURCES;
|
|
}
|
|
|
|
return EFI_SUCCESS;
|
|
}
|
|
|