/** @file This file provide the function to detect boot mode Copyright (c) 2013 Intel Corporation. SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "CommonHeader.h" #include EFI_PEI_PPI_DESCRIPTOR mPpiListRecoveryBootMode = { (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), &gEfiPeiBootInRecoveryModePpiGuid, NULL }; /** If the box was opened, it's boot with full config. If the box is closed, then 1. If it's first time to boot, it's boot with full config . 2. If the ChassisIntrution is selected, force to be a boot with full config 3. Otherwise it's boot with no change. @param PeiServices General purpose services available to every PEIM. @retval TRUE If it's boot with no change. @retval FALSE If boot with no change. **/ BOOLEAN IsBootWithNoChange ( IN EFI_PEI_SERVICES **PeiServices ) { BOOLEAN IsFirstBoot = FALSE; BOOLEAN EnableFastBoot = FALSE; IsFirstBoot = PcdGetBool(PcdBootState); EnableFastBoot = PcdGetBool (PcdEnableFastBoot); DEBUG ((EFI_D_INFO, "IsFirstBoot = %x , EnableFastBoot= %x. \n", IsFirstBoot, EnableFastBoot)); if ((!IsFirstBoot) && EnableFastBoot) { return TRUE; } else { return FALSE; } } /** Routine Description: This function is used to verify if the FV header is validate. @param FwVolHeader - The FV header that to be verified. @retval EFI_SUCCESS - The Fv header is valid. @retval EFI_NOT_FOUND - The Fv header is invalid. **/ EFI_STATUS ValidateFvHeader ( EFI_BOOT_MODE *BootMode ) { UINT16 *Ptr; UINT16 HeaderLength; UINT16 Checksum; EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader; if (BOOT_IN_RECOVERY_MODE == *BootMode) { DEBUG ((EFI_D_INFO, "Boot mode recovery\n")); return EFI_SUCCESS; } // // Let's check whether FvMain header is valid, if not enter into recovery mode // // // Verify the header revision, header signature, length // Length of FvBlock cannot be 2**64-1 // HeaderLength cannot be an odd number // FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)PcdGet32(PcdFlashFvMainBase); if ((FwVolHeader->Revision != EFI_FVH_REVISION)|| (FwVolHeader->Signature != EFI_FVH_SIGNATURE) || (FwVolHeader->FvLength == ((UINT64) -1)) || ((FwVolHeader->HeaderLength & 0x01) != 0) ) { return EFI_NOT_FOUND; } // // Verify the header checksum // HeaderLength = (UINT16) (FwVolHeader->HeaderLength / 2); Ptr = (UINT16 *) FwVolHeader; Checksum = 0; while (HeaderLength > 0) { Checksum = Checksum +*Ptr; Ptr++; HeaderLength--; } if (Checksum != 0) { return EFI_NOT_FOUND; } return EFI_SUCCESS; } /** Peform the boot mode determination logic If the box is closed, then 1. If it's first time to boot, it's boot with full config . 2. If the ChassisIntrution is selected, force to be a boot with full config 3. Otherwise it's boot with no change. @param PeiServices General purpose services available to every PEIM. @param BootMode The detected boot mode. @retval EFI_SUCCESS if the boot mode could be set **/ EFI_STATUS UpdateBootMode ( IN EFI_PEI_SERVICES **PeiServices, OUT EFI_BOOT_MODE *BootMode ) { EFI_STATUS Status; EFI_BOOT_MODE NewBootMode; PEI_CAPSULE_PPI *Capsule; UINT32 RegValue; NewBootMode = *BootMode; // // Read Sticky R/W Bits // RegValue = QNCAltPortRead (QUARK_SCSS_SOC_UNIT_SB_PORT_ID, QUARK_SCSS_SOC_UNIT_CFG_STICKY_RW); DEBUG ((EFI_D_ERROR, "RegValue = %08x\n", RegValue)); // // Check if we need to boot in recovery mode // if ((RegValue & B_CFG_STICKY_RW_FORCE_RECOVERY) != 0) { NewBootMode = BOOT_IN_RECOVERY_MODE; DEBUG ((EFI_D_ERROR, "RECOVERY from sticky bit\n"));; // // Clear force recovery sticky bit // QNCAltPortWrite ( QUARK_SCSS_SOC_UNIT_SB_PORT_ID, QUARK_SCSS_SOC_UNIT_CFG_STICKY_RW, RegValue &(~B_CFG_STICKY_RW_FORCE_RECOVERY) ); } else if (ValidateFvHeader (BootMode) != EFI_SUCCESS) { NewBootMode = BOOT_IN_RECOVERY_MODE; DEBUG ((EFI_D_ERROR, "RECOVERY from corrupt FV\n"));; } else if (QNCCheckS3AndClearState ()) { // // Determine if we're in capsule update mode // Status = PeiServicesLocatePpi ( &gPeiCapsulePpiGuid, 0, NULL, (VOID **)&Capsule ); if (Status == EFI_SUCCESS) { Status = Capsule->CheckCapsuleUpdate (PeiServices); if (Status == EFI_SUCCESS) { DEBUG ((EFI_D_INFO, "Boot mode Flash Update\n")); NewBootMode = BOOT_ON_FLASH_UPDATE; } else { DEBUG ((EFI_D_INFO, "Boot mode S3 resume\n")); NewBootMode = BOOT_ON_S3_RESUME; } } else { DEBUG ((EFI_D_INFO, "Boot mode S3 resume\n")); NewBootMode = BOOT_ON_S3_RESUME; } } else { // // Check if this is a power on reset // if (QNCCheckPowerOnResetAndClearState ()) { DEBUG ((EFI_D_INFO, "Power On Reset\n")); } if (IsBootWithNoChange (PeiServices)) { DEBUG ((EFI_D_INFO, "Boot with Minimum cfg\n")); NewBootMode = BOOT_ASSUMING_NO_CONFIGURATION_CHANGES; } else { DEBUG ((EFI_D_INFO, "Boot with Full cfg\n")); NewBootMode = BOOT_WITH_FULL_CONFIGURATION; } } if (NewBootMode == BOOT_IN_RECOVERY_MODE) { DEBUG ((EFI_D_INFO, "Boot mode recovery\n")); Status = PeiServicesInstallPpi (&mPpiListRecoveryBootMode); ASSERT_EFI_ERROR (Status); } Status = PeiServicesSetBootMode (NewBootMode); ASSERT_EFI_ERROR (Status); *BootMode = NewBootMode; return EFI_SUCCESS; }