--- /dev/null
+/** @file\r
+This file provide the function to detect boot mode\r
+\r
+Copyright (c) 2013 Intel Corporation.\r
+\r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions of the BSD License\r
+which accompanies this distribution. The full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+\r
+#include "CommonHeader.h"\r
+#include <Pi/PiFirmwareVolume.h>\r
+\r
+EFI_PEI_PPI_DESCRIPTOR mPpiListRecoveryBootMode = {\r
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+ &gEfiPeiBootInRecoveryModePpiGuid,\r
+ NULL\r
+};\r
+\r
+/**\r
+ If the box was opened, it's boot with full config.\r
+ If the box is closed, then\r
+ 1. If it's first time to boot, it's boot with full config .\r
+ 2. If the ChassisIntrution is selected, force to be a boot with full config\r
+ 3. Otherwise it's boot with no change.\r
+\r
+ @param PeiServices General purpose services available to every PEIM.\r
+\r
+ @retval TRUE If it's boot with no change.\r
+\r
+ @retval FALSE If boot with no change.\r
+**/\r
+BOOLEAN\r
+IsBootWithNoChange (\r
+ IN EFI_PEI_SERVICES **PeiServices\r
+ )\r
+{\r
+ BOOLEAN IsFirstBoot = FALSE;\r
+\r
+ BOOLEAN EnableFastBoot = FALSE;\r
+ IsFirstBoot = PcdGetBool(PcdBootState);\r
+ EnableFastBoot = PcdGetBool (PcdEnableFastBoot);\r
+\r
+ DEBUG ((EFI_D_INFO, "IsFirstBoot = %x , EnableFastBoot= %x. \n", IsFirstBoot, EnableFastBoot));\r
+\r
+ if ((!IsFirstBoot) && EnableFastBoot) {\r
+ return TRUE;\r
+ } else {\r
+ return FALSE;\r
+ }\r
+}\r
+\r
+\r
+/**\r
+\r
+Routine Description:\r
+\r
+ This function is used to verify if the FV header is validate.\r
+\r
+ @param FwVolHeader - The FV header that to be verified.\r
+\r
+ @retval EFI_SUCCESS - The Fv header is valid.\r
+ @retval EFI_NOT_FOUND - The Fv header is invalid.\r
+\r
+**/\r
+EFI_STATUS\r
+ValidateFvHeader (\r
+ EFI_BOOT_MODE *BootMode\r
+ )\r
+{\r
+ UINT16 *Ptr;\r
+ UINT16 HeaderLength;\r
+ UINT16 Checksum;\r
+\r
+ EFI_FIRMWARE_VOLUME_HEADER *FwVolHeader;\r
+\r
+ if (BOOT_IN_RECOVERY_MODE == *BootMode) {\r
+ DEBUG ((EFI_D_INFO, "Boot mode recovery\n"));\r
+ return EFI_SUCCESS;\r
+ }\r
+ //\r
+ // Let's check whether FvMain header is valid, if not enter into recovery mode\r
+ //\r
+ //\r
+ // Verify the header revision, header signature, length\r
+ // Length of FvBlock cannot be 2**64-1\r
+ // HeaderLength cannot be an odd number\r
+ //\r
+ FwVolHeader = (EFI_FIRMWARE_VOLUME_HEADER *)(UINTN)PcdGet32(PcdFlashFvMainBase);\r
+ if ((FwVolHeader->Revision != EFI_FVH_REVISION)||\r
+ (FwVolHeader->Signature != EFI_FVH_SIGNATURE) ||\r
+ (FwVolHeader->FvLength == ((UINT64) -1)) ||\r
+ ((FwVolHeader->HeaderLength & 0x01) != 0)\r
+ ) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+ //\r
+ // Verify the header checksum\r
+ //\r
+ HeaderLength = (UINT16) (FwVolHeader->HeaderLength / 2);\r
+ Ptr = (UINT16 *) FwVolHeader;\r
+ Checksum = 0;\r
+ while (HeaderLength > 0) {\r
+ Checksum = Checksum +*Ptr;\r
+ Ptr++;\r
+ HeaderLength--;\r
+ }\r
+\r
+ if (Checksum != 0) {\r
+ return EFI_NOT_FOUND;\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+\r
+ Check whether go to recovery path\r
+ @retval TRUE Go to recovery path\r
+ @retval FALSE Go to normal path\r
+\r
+**/\r
+BOOLEAN\r
+OemRecoveryBootMode ()\r
+{\r
+ return PlatformIsBootWithRecoveryStage1 ();\r
+}\r
+\r
+/**\r
+ Peform the boot mode determination logic\r
+ If the box is closed, then\r
+ 1. If it's first time to boot, it's boot with full config .\r
+ 2. If the ChassisIntrution is selected, force to be a boot with full config\r
+ 3. Otherwise it's boot with no change.\r
+\r
+ @param PeiServices General purpose services available to every PEIM.\r
+\r
+ @param BootMode The detected boot mode.\r
+\r
+ @retval EFI_SUCCESS if the boot mode could be set\r
+**/\r
+EFI_STATUS\r
+UpdateBootMode (\r
+ IN EFI_PEI_SERVICES **PeiServices,\r
+ OUT EFI_BOOT_MODE *BootMode\r
+ )\r
+{\r
+ EFI_STATUS Status;\r
+ EFI_BOOT_MODE NewBootMode;\r
+ PEI_CAPSULE_PPI *Capsule;\r
+ CHAR8 UserSelection;\r
+ UINT32 Straps32;\r
+\r
+ //\r
+ // Read Straps. Used later if recovery boot.\r
+ //\r
+ Straps32 = QNCAltPortRead (QUARK_SCSS_SOC_UNIT_SB_PORT_ID, QUARK_SCSS_SOC_UNIT_STPDDRCFG);\r
+\r
+ //\r
+ // Check if we need to boot in recovery mode\r
+ //\r
+ if ((ValidateFvHeader (BootMode) != EFI_SUCCESS)) {\r
+ DEBUG ((EFI_D_INFO, "Force Boot mode recovery\n"));\r
+ NewBootMode = BOOT_IN_RECOVERY_MODE;\r
+ Status = PeiServicesInstallPpi (&mPpiListRecoveryBootMode);\r
+ ASSERT_EFI_ERROR (Status);\r
+ if (OemRecoveryBootMode () == FALSE) {\r
+ DEBUG ((EFI_D_INFO, "Recovery stage1 not Active, reboot to activate recovery stage1 image\n"));\r
+ OemInitiateRecoveryAndWait ();\r
+ }\r
+ } else if (OemRecoveryBootMode ()) {\r
+ DEBUG ((EFI_D_INFO, "Boot mode recovery\n"));\r
+ NewBootMode = BOOT_IN_RECOVERY_MODE;\r
+ Status = PeiServicesInstallPpi (&mPpiListRecoveryBootMode);\r
+ ASSERT_EFI_ERROR (Status);\r
+ } else if (QNCCheckS3AndClearState ()) {\r
+ //\r
+ // Determine if we're in capsule update mode\r
+ //\r
+ Status = PeiServicesLocatePpi (\r
+ &gPeiCapsulePpiGuid,\r
+ 0,\r
+ NULL,\r
+ (VOID **)&Capsule\r
+ );\r
+ if (Status == EFI_SUCCESS) {\r
+ Status = Capsule->CheckCapsuleUpdate (PeiServices);\r
+ if (Status == EFI_SUCCESS) {\r
+ DEBUG ((EFI_D_INFO, "Boot mode Flash Update\n"));\r
+ NewBootMode = BOOT_ON_FLASH_UPDATE;\r
+ } else {\r
+ DEBUG ((EFI_D_INFO, "Boot mode S3 resume\n"));\r
+ NewBootMode = BOOT_ON_S3_RESUME;\r
+ }\r
+ } else {\r
+ DEBUG ((EFI_D_INFO, "Boot mode S3 resume\n"));\r
+ NewBootMode = BOOT_ON_S3_RESUME;\r
+ }\r
+ } else {\r
+ //\r
+ // Check if this is a power on reset\r
+ //\r
+ if (QNCCheckPowerOnResetAndClearState ()) {\r
+ DEBUG ((EFI_D_INFO, "Power On Reset\n"));\r
+ }\r
+ if (IsBootWithNoChange (PeiServices)) {\r
+ DEBUG ((EFI_D_INFO, "Boot with Minimum cfg\n"));\r
+ NewBootMode = BOOT_ASSUMING_NO_CONFIGURATION_CHANGES;\r
+ } else {\r
+ DEBUG ((EFI_D_INFO, "Boot with Full cfg\n"));\r
+ NewBootMode = BOOT_WITH_FULL_CONFIGURATION;\r
+ }\r
+ }\r
+ *BootMode = NewBootMode;\r
+ Status = PeiServicesSetBootMode (NewBootMode);\r
+ ASSERT_EFI_ERROR (Status);\r
+\r
+ //\r
+ // If Recovery Boot then prompt the user to insert a USB key with recovery nodule and\r
+ // continue with the recovery. Also give the user a chance to retry a normal boot.\r
+ //\r
+ if (NewBootMode == BOOT_IN_RECOVERY_MODE) {\r
+ if ((Straps32 & B_STPDDRCFG_FORCE_RECOVERY) == 0) {\r
+ DEBUG ((EFI_D_ERROR, "*****************************************************************\n"));\r
+ DEBUG ((EFI_D_ERROR, "***** Force Recovery Jumper Detected. *****\n"));\r
+ DEBUG ((EFI_D_ERROR, "***** Attempting auto recovery of system flash. *****\n"));\r
+ DEBUG ((EFI_D_ERROR, "***** Expecting USB key with recovery module connected. *****\n"));\r
+ DEBUG ((EFI_D_ERROR, "***** PLEASE REMOVE FORCE RECOVERY JUMPER. *****\n"));\r
+ DEBUG ((EFI_D_ERROR, "*****************************************************************\n"));\r
+ } else {\r
+ DEBUG ((EFI_D_ERROR, "*****************************************************************\n"));\r
+ DEBUG ((EFI_D_ERROR, "***** ERROR: System boot failure!!!!!!! *****\n"));\r
+ DEBUG ((EFI_D_ERROR, "***** - Press 'R' if you wish to force system recovery *****\n"));\r
+ DEBUG ((EFI_D_ERROR, "***** (connect USB key with recovery module first) *****\n"));\r
+ DEBUG ((EFI_D_ERROR, "***** - Press any other key to attempt another boot *****\n"));\r
+ DEBUG ((EFI_D_ERROR, "*****************************************************************\n"));\r
+\r
+ UserSelection = PlatformDebugPortGetChar8 ();\r
+ if ((UserSelection != 'R') && (UserSelection != 'r')) {\r
+ DEBUG ((EFI_D_ERROR, "New boot attempt selected........\n"));\r
+ //\r
+ // Initialte the cold reset\r
+ //\r
+ ResetCold ();\r
+ }\r
+ DEBUG ((EFI_D_ERROR, "Recovery boot selected..........\n"));\r
+ }\r
+ }\r
+\r
+ return EFI_SUCCESS;\r
+}\r