/** @file SPEC : Computrace implement requirement v1.2.doc ;****************************************************************************** ;* Copyright (c) 2013 - 2016, Insyde Software Corporation. 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 "L05Computrace.h" EFI_EVENT AfterDiskInfoEvent; VOID *DiskInfoEventRegistration; EFI_EVENT L05ComputraceStartUpEvent; VOID *L05ComputraceStartUpRegistration; EFI_HANDLE mImageHandle = NULL_HANDLE; UINT8 BatterySerialBuffer[L05_COMPUTRACE_SERIAL_LENGTH]; UINT8 HardDiskSerialBuffer[L05_COMPUTRACE_SERIAL_LENGTH]; UINT8 SystemSerialBuffer[L05_COMPUTRACE_SERIAL_LENGTH]; UINT32 ComputraceState; VOID EFIAPI DiksInfoProtocolNotify ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; UINTN BufferSize; EFI_HANDLE Handle; EFI_DISK_INFO_PROTOCOL *DiskInfo; EFI_BLOCK_IO_PROTOCOL *BlockIo; UINT32 IdDataSize; EFI_IDENTIFY_DATA IdData; Status = EFI_SUCCESS; BufferSize = 0; DiskInfo = NULL; BlockIo = NULL; IdDataSize = 0; BufferSize = sizeof (EFI_HANDLE); Status = gBS->LocateHandle ( ByRegisterNotify, NULL, DiskInfoEventRegistration, &BufferSize, &Handle ); if (EFI_ERROR (Status)) { return; } Status = gBS->HandleProtocol ( Handle, &gEfiDiskInfoProtocolGuid, &DiskInfo ); if (EFI_ERROR (Status)) { return; } Status = gBS->HandleProtocol ( Handle, &gEfiBlockIoProtocolGuid, &BlockIo ); if (EFI_ERROR (Status)) { return; } // // Skip CD-ROM or other Removable Media // if (BlockIo->Media->RemovableMedia) { return; } IdDataSize = sizeof (EFI_IDENTIFY_DATA); ZeroMem (&IdData, sizeof (EFI_IDENTIFY_DATA)); Status = DiskInfo->Identify (DiskInfo, &IdData, &IdDataSize); if (EFI_ERROR (Status)) { return; } CopyMem (HardDiskSerialBuffer, IdData.AtaData.SerialNo, sizeof (IdData.AtaData.SerialNo)); gBS->CloseEvent (AfterDiskInfoEvent); return; } EFI_STATUS GetSystemSerial ( VOID ) { EFI_STATUS Status; UINT8 Length; Status = EFI_SUCCESS; Length = L05_COMPUTRACE_SERIAL_LENGTH; Status = OemSvcGetSystemSerial (&Length, SystemSerialBuffer); return EFI_SUCCESS; } EFI_STATUS GetBatterySerial ( VOID ) { EFI_STATUS Status; UINT8 Length; Status = EFI_SUCCESS; Length = L05_COMPUTRACE_SERIAL_LENGTH; Status = OemSvcGetBatterySerial (&Length, BatterySerialBuffer); return EFI_SUCCESS; } EFI_STATUS SyncComputraceStateWithSetup ( VOID ) { EFI_STATUS Status; UINTN VarSize; SYSTEM_CONFIGURATION SetupVariable; Status = EFI_SUCCESS; // // Sync Computrace state with Setup varibale. // VarSize = sizeof (SYSTEM_CONFIGURATION); Status = gRT->GetVariable ( L"Setup", &gSystemConfigurationGuid, NULL, &VarSize, &SetupVariable ); if (EFI_ERROR (Status)) { // // Setup menu is default setting. // return Status; } if ((UINT8) ComputraceState != SetupVariable.ComputraceState) { SetupVariable.ComputraceState = (UINT8) ComputraceState; Status = gRT->SetVariable ( L"Setup", &gSystemConfigurationGuid, EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS | EFI_VARIABLE_NON_VOLATILE, VarSize, &SetupVariable ); } return Status; } EFI_STATUS EFIAPI LoadAndStartImage ( IN EFI_HANDLE *ImageHandle, IN EFI_GUID *ImageGuid ) { EFI_STATUS Status; UINTN FvHandleCount; EFI_HANDLE *FvHandleBuffer; UINTN Index; EFI_FIRMWARE_VOLUME2_PROTOCOL *Fv; UINTN ImageSize; EFI_FV_FILETYPE Type; EFI_FV_FILE_ATTRIBUTES FvFileAttributes; UINT32 AuthenticationStatus; EFI_DEVICE_PATH_PROTOCOL *DevicePath; MEDIA_FW_VOL_FILEPATH_DEVICE_PATH ImageNode; EFI_HANDLE NewImageHandle; UINTN ExitDataSize; CHAR16 *ExitData; // //Get ComputraceComponents.efi // Status = gBS->LocateHandleBuffer ( ByProtocol, &gEfiFirmwareVolume2ProtocolGuid, NULL, &FvHandleCount, &FvHandleBuffer ); if (EFI_ERROR (Status)) { return Status; } for (Index = 0; Index < FvHandleCount; Index++) { gBS->HandleProtocol ( FvHandleBuffer[Index], &gEfiFirmwareVolume2ProtocolGuid, (VOID **) &Fv ); Status = Fv->ReadFile ( Fv, ImageGuid, NULL, &ImageSize, &Type, &FvFileAttributes, &AuthenticationStatus ); if (EFI_ERROR (Status)) { continue; } // // Get FV device path. // DevicePath = DevicePathFromHandle (FvHandleBuffer[Index]); // // Get ComputraceComponents.efi device path. // EfiInitializeFwVolDevicepathNode (&ImageNode, ImageGuid); // // Append ComputraceComponents.efi device path to FV device path. // DevicePath = AppendDevicePathNode (DevicePath, (EFI_DEVICE_PATH_PROTOCOL *) &ImageNode); // // Load ComputraceComponents.efi. // Status = gBS->LoadImage ( FALSE, mImageHandle, DevicePath, NULL, 0, &NewImageHandle ); if (EFI_ERROR (Status)) { break; } // // Start ComputraceComponent.efi. // Status = gBS->StartImage (NewImageHandle, &ExitDataSize, &ExitData); if (EFI_ERROR (Status)) { break; } } return Status; } EFI_STATUS SaveL05ComputraceInfo ( IN EFI_L05_COMPUTRACE_AREA *L05ComputraceArea ) { EFI_STATUS Status; UINTN DataLength; UINTN L05ComputraceRegionBase; UINTN L05ComputraceRegionSize; EFI_SMM_FW_BLOCK_SERVICE_PROTOCOL *SmmFwb; Status = EFI_SUCCESS; DataLength = 0; SmmFwb = NULL; L05ComputraceRegionBase = (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionComputraceGuid, 1); L05ComputraceRegionSize = (UINTN) FdmGetNAtSize (&gL05H2OFlashMapRegionComputraceGuid, 1); if ((L05ComputraceRegionBase == (UINTN) L05_INVALID_VALUE) || (L05ComputraceRegionSize == (UINTN) L05_INVALID_VALUE)) { return EFI_UNSUPPORTED; } Status = gBS->LocateProtocol (&gEfiSmmFwBlockServiceProtocolGuid, NULL, (VOID **) &SmmFwb); if (EFI_ERROR (Status)) { return Status; } Status = SmmFwb->EraseBlocks (SmmFwb, L05ComputraceRegionBase, &L05ComputraceRegionSize); if (!EFI_ERROR (Status)) { DataLength = MIN (L05ComputraceRegionSize, sizeof (EFI_L05_COMPUTRACE_AREA)); Status = SmmFwb->Write ( SmmFwb, L05ComputraceRegionBase, &DataLength, (UINT8 *) L05ComputraceArea ); } return Status; } EFI_STATUS GetL05ComputraceInfo ( IN OUT EFI_L05_COMPUTRACE_AREA *L05ComputraceArea ) { if (((UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionComputraceGuid, 1) == (UINTN) L05_INVALID_VALUE) || ((UINTN) FdmGetNAtSize (&gL05H2OFlashMapRegionComputraceGuid, 1) == (UINTN) L05_INVALID_VALUE)) { return EFI_UNSUPPORTED; } CopyMem (L05ComputraceArea, (VOID *) (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionComputraceGuid, 1), sizeof (EFI_L05_COMPUTRACE_AREA)); return EFI_SUCCESS; } EFI_STATUS GetComputraceState ( IN OUT UINT32 *EfiL05ComputraceState ) { EFI_STATUS Status; EFI_L05_COMPUTRACE_AREA L05ComputraceArea; Status = EFI_SUCCESS; Status = GetL05ComputraceInfo (&L05ComputraceArea); if (EFI_ERROR (Status)) { return Status; } *EfiL05ComputraceState = L05ComputraceArea.ComputraceState; return Status; } EFI_STATUS SetComputraceState ( IN UINT32 *EfiL05ComputraceState ) { EFI_STATUS Status; EFI_L05_COMPUTRACE_AREA L05ComputraceArea; Status = EFI_SUCCESS; switch (*EfiL05ComputraceState) { case EFI_L05_COMPUTRACE_ENABLED: Status = GetL05ComputraceInfo (&L05ComputraceArea); if (EFI_ERROR (Status)) { return Status; } break; case EFI_L05_COMPUTRACE_DISABLED: // // Clear All Computrace Area when disable it. // SetMem (&L05ComputraceArea, sizeof (EFI_L05_COMPUTRACE_AREA), 0xFF); break; default: return EFI_UNSUPPORTED; } L05ComputraceArea.ComputraceState = *EfiL05ComputraceState; return SaveL05ComputraceInfo (&L05ComputraceArea); } /** Check Erase Flag is exist or not. @param None @retval TRUE Erase Flag is exist. @retval FALSE Erase Flag is not exist. **/ BOOLEAN IsExistingEraseFlag ( VOID ) { #ifdef L05_SPECIFIC_VARIABLE_SERVICE_ENABLE EFI_STATUS Status; EFI_L05_VARIABLE_PROTOCOL *L05VariablePtr; UINT32 DataLength; UINT8 ComputraceEraseFlag; #else UINTN EepromBase; #endif #ifdef L05_SPECIFIC_VARIABLE_SERVICE_ENABLE Status = EFI_SUCCESS; L05VariablePtr = NULL; Status = gBS->LocateProtocol (&gEfiL05VariableProtocolGuid, NULL, &L05VariablePtr); if (EFI_ERROR (Status)) { return Status; } ComputraceEraseFlag = 0; DataLength = sizeof (ComputraceEraseFlag); Status = L05VariablePtr->GetVariable ( L05VariablePtr, &gL05ComputraceEraseFlagGuid, &DataLength, &ComputraceEraseFlag ); if (EFI_ERROR (Status)) { return FALSE; } return (ComputraceEraseFlag == L05_ERASE_FLAG_ENABLE) ? TRUE : FALSE; #else EepromBase = (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionEepromGuid, 1); if (EepromBase == 0) { return FALSE; } return (((EFI_L05_EEPROM_MAP_120 *) EepromBase)->ComputraceEraseFlag[0] == L05_ERASE_FLAG_ENABLE) ? TRUE : FALSE; #endif } /** Clear Erase Flag. @param None @retval EFI_SUCCESS The operation completed successfully. @retval Others An unexpected error occurred. **/ EFI_STATUS ClearEraseFlag ( VOID ) { EFI_STATUS Status; #ifdef L05_SPECIFIC_VARIABLE_SERVICE_ENABLE EFI_L05_VARIABLE_PROTOCOL *L05VariablePtr; UINT8 ComputraceEraseFlag; UINT32 DataLength; #else EFI_SMM_FW_BLOCK_SERVICE_PROTOCOL *SmmFwb; UINTN EepromBase; UINTN EepromSize; UINT8 *EepromBuffer; #endif Status = EFI_SUCCESS; #ifdef L05_SPECIFIC_VARIABLE_SERVICE_ENABLE L05VariablePtr = NULL; Status = gBS->LocateProtocol (&gEfiL05VariableProtocolGuid, NULL, &L05VariablePtr); if (EFI_ERROR (Status)) { return Status; } ComputraceEraseFlag = L05_ERASE_FLAG_DISABLE; DataLength = sizeof (ComputraceEraseFlag); Status = L05VariablePtr->SetVariable ( L05VariablePtr, &gL05ComputraceEraseFlagGuid, DataLength, &ComputraceEraseFlag ); if (EFI_ERROR (Status)) { return Status; } #else SmmFwb = NULL; EepromBuffer = NULL; Status = gBS->LocateProtocol (&gEfiSmmFwBlockServiceProtocolGuid, NULL, (VOID **) &SmmFwb); if (EFI_ERROR (Status)) { return Status; } EepromBase = (UINTN) FdmGetNAtAddr (&gL05H2OFlashMapRegionEepromGuid, 1); EepromSize = (UINTN) FdmGetNAtSize (&gL05H2OFlashMapRegionEepromGuid, 1); if ((EepromBase == 0) || (EepromSize == 0)) { return EFI_UNSUPPORTED; } EepromBuffer = AllocatePages (EFI_SIZE_TO_PAGES (EepromSize)); if (EepromBuffer == NULL) { return EFI_OUT_OF_RESOURCES; } ZeroMem (EepromBuffer, (EFI_SIZE_TO_PAGES (EepromSize) * EFI_PAGE_SIZE)); CopyMem (EepromBuffer, (VOID *) EepromBase, EepromSize); ((EFI_L05_EEPROM_MAP_120 *) EepromBuffer)->ComputraceEraseFlag[0] = L05_ERASE_FLAG_DISABLE; Status = SmmFwb->EraseBlocks (SmmFwb, EepromBase, &EepromSize); if (EFI_ERROR (Status)) { return Status; } Status = SmmFwb->Write (SmmFwb, EepromBase, &EepromSize, EepromBuffer); if (EFI_ERROR (Status)) { return Status; } FreePages (EepromBuffer, EFI_SIZE_TO_PAGES (EepromSize)); #endif return Status; } /** Erase Computrace FV. @param ComputraceFvBase Point to Computrace FV. @param ComputraceFvSize Size of Computrace FV. @retval EFI_SUCCESS The operation completed successfully. @retval Others An unexpected error occurred. **/ EFI_STATUS EraseComputraceFv ( IN UINTN ComputraceFvBase, IN UINTN ComputraceFvSize ) { EFI_STATUS Status; EFI_SMM_FW_BLOCK_SERVICE_PROTOCOL *SmmFwb; UINTN EraseSize; Status = EFI_SUCCESS; SmmFwb = NULL; EraseSize = ComputraceFvSize; Status = gBS->LocateProtocol (&gEfiSmmFwBlockServiceProtocolGuid, NULL, (VOID **) &SmmFwb); if (EFI_ERROR (Status)) { return Status; } Status = SmmFwb->EraseBlocks (SmmFwb, ComputraceFvBase, &EraseSize); return Status; } /** Check Computrace Erase Request. @param None @retval EFI_SUCCESS The operation completed successfully. @retval EFI_ABORTED No need to do this operation. @retval Others An unexpected error occurred. **/ EFI_STATUS CheckComputraceEraseRequest ( VOID ) { UINTN ComputraceFvBase; UINTN ComputraceFvSize; EFI_FIRMWARE_VOLUME_HEADER *Fvh; ComputraceFvBase = 0; ComputraceFvSize = 0; Fvh = NULL; // // According to Lenovo Notebook Remove Computrace Requirement for PRC Version 1.32 draft // 3. Compatible Design // // // Step1: Check Erase Flag // if (!IsExistingEraseFlag ()) { return EFI_ABORTED; } // // Step2: Check Computrace FV already earsed or not // ComputraceFvBase = (UINTN) FdmGetAddressById (&gH2OFlashMapRegionFvGuid, &gL05H2OFlashMapRegionComputraceFvGuid, 1); ComputraceFvSize = (UINTN) FdmGetSizeById (&gH2OFlashMapRegionFvGuid, &gL05H2OFlashMapRegionComputraceFvGuid, 1); if ((ComputraceFvBase == 0) || (ComputraceFvSize == 0)) { return EFI_UNSUPPORTED; } Fvh = (EFI_FIRMWARE_VOLUME_HEADER *) ComputraceFvBase; if (Fvh->Signature != EFI_FVH_SIGNATURE) { return EFI_SUCCESS; } // // Step3: Check Computrace state is enabled & activated or not // if (ComputraceState == EFI_L05_COMPUTRACE_ENABLED) { // // Computrace state is enabled, so keep Computrace module in ROM // ClearEraseFlag (); return EFI_ABORTED; } EraseComputraceFv (ComputraceFvBase, ComputraceFvSize); gRT->ResetSystem (EfiResetCold, EFI_SUCCESS, 0, NULL); // // Waiting for system reset // CpuDeadLoop (); return EFI_SUCCESS; } EFI_STATUS L05ComputraceEntry ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; UINT16 SmiPort; BOOLEAN ComputraceSupported; SmiPort = 0; mImageHandle = ImageHandle; ComputraceSupported = TRUE; Status = GetComputraceState (&ComputraceState); if (EFI_ERROR (Status)) { return EFI_SUCCESS; } // // According to Lenovo Notebook Remove Computrace Requirement for PRC Version 1.32 draft // 2. Lenovo Requirements of Removing Computrace for PRC // 2.2 SMB // For SMB products, if Computrace function supported, then for the systems ship to PRC: // - Computrace module such as efiinstnats.efi / efiinstnats64.efi MUST be completely removed from SPI rom. // - Computrace SMM service code MUST be completely removed from SPI rom. // CheckComputraceEraseRequest (); // // Skip Computrace init if the OemSvc return platform doesn't support Computrace. // Status = OemSvcCheckComputraceSupportStatus (&ComputraceSupported); if (Status == EFI_MEDIA_CHANGED && !ComputraceSupported) { // // If Computrace is Enabled, and then ComputraceSupportStatus changes to unsupported. // The Computace string in SCU need to be hidden. // ComputraceState = EFI_L05_COMPUTRACE_DISABLED; Status = SyncComputraceStateWithSetup (); return EFI_SUCCESS; } // // Check SMI port address in CMOS. // SmiPort = SW_SMI_PORT; if (SmiPort != ReadCmos16 (EFI_L05_COMPUTRACE_CMOS_ADDRESS_LOW)) { WriteCmos16 (EFI_L05_COMPUTRACE_CMOS_ADDRESS_LOW, SmiPort); } // // Disable Computrace when first boot. // if (ComputraceState == EFI_L05_COMPUTRACE_NOT_SUPPORTED) { ComputraceState = EFI_L05_COMPUTRACE_DISABLED; SetComputraceState (&ComputraceState); if (EFI_ERROR (Status)) { return EFI_SUCCESS; } } Status = SyncComputraceStateWithSetup (); if (EFI_ERROR (Status)) { return EFI_SUCCESS; } switch (ComputraceState) { case EFI_L05_COMPUTRACE_ENABLING: case EFI_L05_COMPUTRACE_ENABLED: break; default : // // Only need to continue when Computrace enabled. // return EFI_SUCCESS; } // // Clear each serial buffer. // ZeroMem (BatterySerialBuffer, L05_COMPUTRACE_SERIAL_LENGTH); ZeroMem (HardDiskSerialBuffer, L05_COMPUTRACE_SERIAL_LENGTH); ZeroMem (SystemSerialBuffer, L05_COMPUTRACE_SERIAL_LENGTH); Status = gBS->CreateEvent ( EVT_NOTIFY_SIGNAL, TPL_CALLBACK, DiksInfoProtocolNotify, NULL, &AfterDiskInfoEvent ); if (EFI_ERROR (Status)) { return EFI_ABORTED; } Status = gBS->RegisterProtocolNotify ( &gEfiDiskInfoProtocolGuid, AfterDiskInfoEvent, &DiskInfoEventRegistration ); if (EFI_ERROR (Status)) { return EFI_ABORTED; } // // Register notify function to install WPBT on ReadyToBoot Event. // Status = EfiCreateEventReadyToBootEx ( TPL_CALLBACK, L05ComputraceFunction, NULL, &L05ComputraceStartUpEvent ); return Status; } EFI_STATUS GetComputraceSerialCrc ( IN OUT VOID *EfiL05ComputraceSerialCrc ) { EFI_STATUS Status; EFI_L05_COMPUTRACE_AREA L05ComputraceArea; Status = EFI_SUCCESS; Status = GetL05ComputraceInfo (&L05ComputraceArea); if (EFI_ERROR (Status)) { return Status; } CopyMem (EfiL05ComputraceSerialCrc, &L05ComputraceArea.ComputraceSerialCrc, sizeof (EFI_L05_COMPUTRACE_SERIAL_CRC)); return Status; } EFI_STATUS SetComputraceSerialCrc ( IN VOID *EfiL05ComputraceSerialCrc ) { EFI_STATUS Status; EFI_L05_COMPUTRACE_AREA L05ComputraceArea; Status = EFI_SUCCESS; Status = GetL05ComputraceInfo (&L05ComputraceArea); if (EFI_ERROR (Status)) { return Status; } CopyMem (&L05ComputraceArea.ComputraceSerialCrc, EfiL05ComputraceSerialCrc, sizeof (EFI_L05_COMPUTRACE_SERIAL_CRC)); return SaveL05ComputraceInfo (&L05ComputraceArea); } VOID EFIAPI L05ComputraceFunction ( IN EFI_EVENT Event, IN VOID *Context ) { EFI_STATUS Status; EFI_GUID DriverGuid = L05_COMPUTRACE_EFI_DRIVER_FILE_GUID; //WOD EFI_GUID WpbtGuid = L05_COMPUTRACE_EFI_WPBT_FILE_GUID; EFI_L05_COMPUTRACE_SERIAL_CRC SerialCrc; UINT32 BatteryCrc; UINT32 HardDiskCrc; UINT32 SystemCrc; Status = EFI_SUCCESS; gBS->CloseEvent (Event); GetBatterySerial (); GetSystemSerial (); gBS->CalculateCrc32 ((VOID *) BatterySerialBuffer, L05_COMPUTRACE_SERIAL_LENGTH, &BatteryCrc); gBS->CalculateCrc32 ((VOID *) HardDiskSerialBuffer, L05_COMPUTRACE_SERIAL_LENGTH, &HardDiskCrc); gBS->CalculateCrc32 ((VOID *) SystemSerialBuffer, L05_COMPUTRACE_SERIAL_LENGTH, &SystemCrc); if (ComputraceState == EFI_L05_COMPUTRACE_ENABLING) { // // If Computrace was just enabled, we should store CRC of each serial. // SerialCrc.BatterySerialCrc = BatteryCrc; SerialCrc.HardDiskSerialCrc = HardDiskCrc; SerialCrc.SystemSerialCrc = SystemCrc; SetComputraceSerialCrc (&SerialCrc); if (EFI_ERROR (Status)) { return; } // // After CRCs are stored, change the Computrace state. // ComputraceState = EFI_L05_COMPUTRACE_ENABLED; SetComputraceState (&ComputraceState); if (EFI_ERROR (Status)) { return; } } else { GetComputraceSerialCrc (&SerialCrc); if (EFI_ERROR (Status)) { return; } // // If all serials changes at the same time, we should auto-disable Computrace. // if ((BatteryCrc != SerialCrc.BatterySerialCrc) && (HardDiskCrc != SerialCrc.HardDiskSerialCrc) && (SystemCrc != SerialCrc.SystemSerialCrc) ) { ComputraceState = EFI_L05_COMPUTRACE_DISABLED; SetComputraceState (&ComputraceState); if (EFI_ERROR (Status)) { return; } Status = SyncComputraceStateWithSetup (); if (EFI_ERROR (Status)) { return; } } } // // Prepare to load Computrace EFI Driver. // //WOD Status = LoadAndStartImage((EFI_HANDLE*)Context, &WpbtGuid); Status = GetComputraceState (&ComputraceState); if (!EFI_ERROR (Status) && (ComputraceState == EFI_L05_COMPUTRACE_ENABLED)) { LoadAndStartImage ((EFI_HANDLE *) Context, &DriverGuid); } return; }