/** @file TigerLake iPCM DXE driver This DXE driver provides the support of the calibration data. Copyright (c) 2019, Intel Corporation. All rights reserved.
This software and associated documentation (if any) is furnished under a license and may only be used or copied in accordance with the terms of the license. Except as permitted by such license, no part of this software or documentation may be reproduced, stored in a retrieval system, or transmitted in any form or by any means without the express written consent of Intel Corporation. **/ #include #include #include #include #include #include #include #include #include #include #include #include "..\..\Include\E3DongleLib.h" #include "E3DongleDxe.h" /** This function calculates an UINT8 checksum. @param[in] Buffer Pointer to buffer to checksum @param[in] Size Number of bytes to checksum @retval EFI_SUCCESS The function completed successfully. **/ UINT8 Checksum ( IN VOID *Buffer, IN UINTN Size ) { UINT8 Sum; UINT8 *Ptr; Sum = 0; /// /// Initialize pointer /// Ptr = Buffer; /// /// add all content of buffer /// while (Size--) { Sum = (UINT8) (Sum + (*Ptr++)); } /// /// check checksum /// return ((0xFF-Sum+1) & 0xFF); } /** This function calculates an UINT8 checksum. @param[in] Buffer Pointer to buffer to checksum @param[in] Size Number of bytes to checksum @retval EFI_SUCCESS The function completed successfully. **/ EFI_STATUS CheckChecksum ( IN VOID *Buffer, IN UINTN Size ) { /// /// check checksum /// if (Checksum(Buffer, Size) != 0) return EFI_CRC_ERROR; return EFI_SUCCESS; } EFI_STATUS ReadPDRData ( IN UINTN offset, IN UINTN length, OUT UINT8 *buf ) { EFI_STATUS Status; PCH_SPI_PROTOCOL *mSpiProtocol; Status = gBS->LocateProtocol ( &gPchSpiProtocolGuid, NULL, (VOID **)&mSpiProtocol ); if (EFI_ERROR (Status)) { Print(L"Cannot locate PchSpiProtocol!!!\n"); return Status; } Status = mSpiProtocol->FlashRead( mSpiProtocol, FlashRegionPlatformData, (UINT32) offset, (UINT32) length, (UINT8 *)buf ); if (EFI_ERROR (Status)) { Print(L"Cannot read the data in PDR!!!\n"); return Status; } return EFI_SUCCESS; } /** This function handles E3DongleReadyToBootCallback task at the ready to boot @param[in] Event The Event this notify function registered to. @param[in] Context Pointer to the context data registered to the Event. **/ E3_CALIBRATION_STATUS E3DongleUpdateCalibrationData ( IN EFI_EVENT Event, IN VOID *Context ) { E3_CALIBRATION_STATUS CalibrationDataStatus = E3DongleNotAttached; EFI_STATUS Status; E3_CALIBRATION_DATA_HEADER E3CalibrationDataHeader; E3_CALIBRATION_DATA_HEADER *pHeader; UINT8 * PdrBuffer = NULL; UINT8 * DongleBuffer = NULL; DEBUG ((DEBUG_INFO, "E3DongleUpdateCalibrationData: enter...!\n")); //gBS->CloseEvent (Event); // // Check if E3 Dongle is attached. // If no attached, exit directly. // if (!IsE3DongleAttached()) { //SwitchE3MuxMode(0); // switch to usb mode Print(L"E3 Dongle: Not attached. \n"); gBS->Stall(500000); return E3DongleNotAttached; } // // If E3 Dongle is attached, // 1. switch USB/I2C mux to I2C mode and set E3 chip to normal mode (default is slow mode) // 2. read the E3 calibration data from PDR region; // If no data, prompt that the board need E3 calibration before measurement, then wait for 10S and exit (CalibrationDataStatus = 1); // // 3. read the calibration data from E3 dongle; // If the data in E3 dongle is existing, valid and different from the data on the current board, ask user to confirm if it need to be updated (CalibrationDataStatus = 2). // If E3 dongle data is same as the data on the current board, prompt that the calibration in E3 dongle is same as the current one (CalibrationDataStatus = 3). // If E3 dongle data is not existing or not valid, prompt that the update progress until it's completed(CalibrationDataStatus = 4). // Print(L"E3 Dongle: Attached! \n"); //Turn on E3 chip power which is supplied by dongle ExternalPowerControlForE3Chip(TRUE); //SwitchE3MuxMode(1); // switch to i2c mode // Read the E3 calibration header from PDR (Platform Data Region) Status = ReadPDRData(E3_CALIBRATION_DATA_OFFSET_IN_PDR, sizeof (E3_CALIBRATION_DATA_HEADER), (UINT8*) &E3CalibrationDataHeader); if (EFI_ERROR (Status)) { Print(L"E3 Dongle Error: Cannot read the header in PDR!!!\n"); return E3ReadPDRError; } // Check if the signature in the header is valid if (E3CalibrationDataHeader.Signature != E3_CALIBRATION_DATA_HEADER_SIGNATURE) { Print(L"E3 Dongle Error: The data is not existing-invalid signature in PDR!!!\n"); return E3DataNotInPDR; } // Allocate the buffer for PDR. Status= gBS->AllocatePool(EfiBootServicesData, E3CalibrationDataHeader.Length, &PdrBuffer); if (EFI_ERROR (Status)) { Print(L"E3 Dongle Error: Cannot AllocatePool!!!\n"); return E3DataNotInPDR; } // Copy the header to the buffer CopyMem((VOID *)PdrBuffer, (VOID *)&E3CalibrationDataHeader, sizeof(E3CalibrationDataHeader)); // Read the data from PDR Status = ReadPDRData(E3CalibrationDataHeader.DataOffset, (UINTN)(E3CalibrationDataHeader.DataLength & 0xFFFF), (UINT8 *)(PdrBuffer + sizeof(E3CalibrationDataHeader))); if (EFI_ERROR (Status)) { Print(L"E3 Dongle Error: Cannot read the header in PDR!!!\n"); CalibrationDataStatus = E3ReadPDRError; goto Error_Handling; } // Check the checksum Status = CheckChecksum(PdrBuffer, E3CalibrationDataHeader.Length); if (EFI_ERROR (Status)) { Print(L"E3 Dongle Error: The data is not existing-invalid checksum in PDR!!!\n"); CalibrationDataStatus = E3DataNotInPDR; goto Error_Handling; } // Fix the data offset and recalculate the checksum in PdrBuffer pHeader = (E3_CALIBRATION_DATA_HEADER*)PdrBuffer; pHeader->DataOffset = sizeof(E3_CALIBRATION_DATA_HEADER); pHeader->Checksum = 0; pHeader->Checksum = Checksum(PdrBuffer, pHeader->Length); //Only for debug Print(L"E3 Dongle Debug: The header in PDR*****\n"); Print(L"Signature: 0x%X\n", pHeader->Signature); Print(L"Length: %d\n", pHeader->Length); Print(L"Revision: 0x%X\n", pHeader->Revision); Print(L"Checksum: 0x%X\n", pHeader->Checksum); Print(L"DataOffset: 0x%X\n", pHeader->DataOffset); Print(L"DataLength: 0x%X\n", pHeader->DataLength); Print(L"E3 Dongle Debug: *********************\n"); ZeroMem(&E3CalibrationDataHeader,sizeof(E3CalibrationDataHeader)); // Read the E3 dongle EEPROM Status = ReadE3DongleBuffer(0, sizeof(E3CalibrationDataHeader),(UINT8*)&E3CalibrationDataHeader); if (EFI_ERROR(Status)) { Print(L"E3 Dongle Error: cannot read the dongle EEPROM over I2C!!!\n"); CalibrationDataStatus = E3DataI2CError; goto Error_Handling; } // Check if the dongle has valid signature if (E3CalibrationDataHeader.Signature != E3_CALIBRATION_DATA_HEADER_SIGNATURE) { Print(L"E3 Dongle Error: The data is not existing-invalid signature in dongle!!!\n"); CalibrationDataStatus = E3DataNotInDongle; goto Update_Dongle; } // Allocate the buffer for dongle data Status= gBS->AllocatePool(EfiBootServicesData, E3CalibrationDataHeader.Length, &DongleBuffer); if (EFI_ERROR (Status)) { Print(L"E3 Dongle Error: Cannot AllocatePool!!!\n"); CalibrationDataStatus = E3DataCommonError; goto Update_Dongle; } // Read all data from dongle Status = ReadE3DongleBuffer(0, (UINTN)(E3CalibrationDataHeader.Length & 0xFFFF),DongleBuffer); if (EFI_ERROR(Status)) { Print(L"E3 Dongle Error: cannot read the dongle EEPROM over I2C!!!\n"); CalibrationDataStatus = E3DataI2CError; goto Error_Handling; } // Check if the data in E3 dongle is same as it in PDR if (CompareMem(PdrBuffer,DongleBuffer, E3CalibrationDataHeader.Length) == 0) { CalibrationDataStatus = E3DataAlreadyExistingAndSame; } else { CalibrationDataStatus = E3DataAlreadyExistingButDifferent; } Update_Dongle: // // Take the action according to the CalibrationDataStatus // if (CalibrationDataStatus == E3DataAlreadyExistingAndSame) { Print(L"E3 Dongle Info: the data has already been in the dongle, skip to update.\n"); } else { Print(L"E3 Dongle Info: start to update the data in the dongle EEPROM, please wait...\n"); Status = WriteE3DongleBuffer(0, (UINTN)(pHeader->Length & 0xFFFF),PdrBuffer); if (EFI_ERROR(Status)) { Print(L"E3 Dongle Error: cannot write the dongle EEPROM over I2C!!!\n"); CalibrationDataStatus = E3DataI2CError; goto Error_Handling; } // Allocate the buffer for dongle data Status= gBS->AllocatePool(EfiBootServicesData, pHeader->Length, &DongleBuffer); if (EFI_ERROR (Status)) { Print(L"E3 Dongle Error: Cannot AllocatePool!!!\n"); CalibrationDataStatus = E3DataCommonError; goto Update_Dongle; } // Read all data from dongle Status = ReadE3DongleBuffer(0, (UINTN)(pHeader->Length & 0xFFFF),DongleBuffer); if (EFI_ERROR(Status)) { Print(L"E3 Dongle Error: cannot read the dongle EEPROM over I2C!!!\n"); CalibrationDataStatus = E3DataI2CError; goto Error_Handling; } // Check if the data in E3 dongle is same as it in PDR if (CompareMem(PdrBuffer,DongleBuffer, pHeader->Length) == 0) { CalibrationDataStatus = E3DataUpdateSuccessful; Print(L"E3 Dongle: complete updating data in dongle.\n"); } else { CalibrationDataStatus = E3DataCommonError; Print(L"E3 Dongle: Failed to update data in dongle!!!!\n"); } //Print(L"E3 Dongle: PDR header=0x%x\n", *(UINT32*)(&PdrBuffer[0])); } Error_Handling: if (PdrBuffer != NULL) { gBS->FreePool(PdrBuffer); } if (DongleBuffer != NULL) { gBS->FreePool(DongleBuffer); } return CalibrationDataStatus; } /** This function handles E3DongleReadyToBootCallback task at the ready to boot @param[in] Event The Event this notify function registered to. @param[in] Context Pointer to the context data registered to the Event. **/ VOID EFIAPI E3DongleReadyToBootCallback ( IN EFI_EVENT Event, IN VOID *Context ) { E3_CALIBRATION_STATUS CalibrationDataStatus = E3DongleNotAttached; DEBUG ((DEBUG_INFO, "E3DongleReadyToBootCallback: enter...\n")); CalibrationDataStatus = E3DongleUpdateCalibrationData(Event, Context); gBS->CloseEvent (Event); } /** Registers callback for E3DongleReadyToBootCallback **/ VOID RegisterReadyToBootCallback ( VOID ) { EFI_STATUS Status; EFI_EVENT Event; Status = EfiCreateEventReadyToBootEx ( TPL_NOTIFY, E3DongleReadyToBootCallback, NULL, &Event ); ASSERT_EFI_ERROR (Status); } EFI_STATUS EFIAPI E3DongleDxeEntryPoint ( IN EFI_HANDLE ImageHandle, IN EFI_SYSTEM_TABLE *SystemTable ) { EFI_STATUS Status; UINTN VarDataSize; SETUP_DATA SystemConfiguration; DEBUG ((DEBUG_INFO, "E3DongleDxeEntryPoint: Enter...\n")); VarDataSize = sizeof (SETUP_DATA); Status = gRT->GetVariable ( L"Setup", &gSetupVariableGuid, NULL, &VarDataSize, &SystemConfiguration ); if (EFI_ERROR(Status)) { DEBUG ((DEBUG_INFO, "E3DongleDxeEntryPoint: Cannot get BIOS setup variable!\n")); return Status; } if (SystemConfiguration.iRPMMode == IRPM_BIOSETUP_DONGLEMODE) { DEBUG ((DEBUG_INFO, "E3DongleDxeEntryPoint: Dongle mode is enabled, register callback!\n")); RegisterReadyToBootCallback(); } //E3DongleUpdateCalibrationData(NULL, NULL); return EFI_SUCCESS; }