Home | History | Annotate | Download | only in PlatformPei
      1 /** @file
      2 
      3   Copyright (c) 2004  - 2014, Intel Corporation. All rights reserved.<BR>
      4 
      5   This program and the accompanying materials are licensed and made available under
      7   the terms and conditions of the BSD License that accompanies this distribution.
      9   The full text of the license may be found at
     11   http://opensource.org/licenses/bsd-license.php.
     13 
     15   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
     17   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
     19 
     21 
     23 Module Name:
     24 
     25 
     26   BootMode.c
     27 
     28 Abstract:
     29 
     30   EFI PEIM to provide the platform support functionality on the Thurley.
     31 
     32 
     33 --*/
     34 #include "CommonHeader.h"
     35 #include "Platform.h"
     36 #include "PlatformBaseAddresses.h"
     37 #include "PchAccess.h"
     38 #include "PlatformBootMode.h"
     39 #include <Guid/SetupVariable.h>
     40 
     41 #include <Guid/BootState.h>
     42 
     43 //
     44 // Priority of our boot modes, highest priority first
     45 //
     46 EFI_BOOT_MODE mBootModePriority[] = {
     47   BOOT_IN_RECOVERY_MODE,
     48   BOOT_WITH_DEFAULT_SETTINGS,
     49   BOOT_ON_FLASH_UPDATE,
     50   BOOT_ON_S2_RESUME,
     51   BOOT_ON_S3_RESUME,
     52   BOOT_ON_S4_RESUME,
     53   BOOT_WITH_MINIMAL_CONFIGURATION,
     54   BOOT_ASSUMING_NO_CONFIGURATION_CHANGES,
     55   BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS,
     56   BOOT_WITH_FULL_CONFIGURATION,
     57   BOOT_ON_S5_RESUME
     58 };
     59 
     60 EFI_PEI_NOTIFY_DESCRIPTOR mCapsuleNotifyList[1] = {
     61   {
     62     (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
     63     &gPeiCapsulePpiGuid,
     64     CapsulePpiNotifyCallback
     65   }
     66 };
     67 
     68 BOOLEAN
     69 GetSleepTypeAfterWakeup (
     70   IN  CONST EFI_PEI_SERVICES          **PeiServices,
     71   OUT UINT16                    *SleepType
     72   );
     73 
     74 EFI_STATUS
     75 EFIAPI
     76 CapsulePpiNotifyCallback (
     77   IN EFI_PEI_SERVICES           **PeiServices,
     78   IN EFI_PEI_NOTIFY_DESCRIPTOR  *NotifyDescriptor,
     79   IN VOID                       *Ppi
     80   )
     81 {
     82   EFI_STATUS      Status;
     83   EFI_BOOT_MODE   BootMode;
     84   PEI_CAPSULE_PPI *Capsule;
     85 
     86   Status = (*PeiServices)->GetBootMode((const EFI_PEI_SERVICES **)PeiServices, &BootMode);
     87   ASSERT_EFI_ERROR (Status);
     88 
     89   if (BootMode == BOOT_ON_S3_RESUME) {
     90     //
     91     // Determine if we're in capsule update mode
     92     //
     93     Status = (*PeiServices)->LocatePpi ((const EFI_PEI_SERVICES **)PeiServices,
     94                                         &gPeiCapsulePpiGuid,
     95                                         0,
     96                                         NULL,
     97                                         (VOID **)&Capsule
     98                                         );
     99 
    100     if (Status == EFI_SUCCESS) {
    101       if (Capsule->CheckCapsuleUpdate ((EFI_PEI_SERVICES**)PeiServices) == EFI_SUCCESS) {
    102         BootMode = BOOT_ON_FLASH_UPDATE;
    103         Status = (*PeiServices)->SetBootMode((const EFI_PEI_SERVICES **)PeiServices, BootMode);
    104         ASSERT_EFI_ERROR (Status);
    105       }
    106     }
    107   }
    108 
    109   return Status;
    110 }
    111 
    112 /**
    113   Check CMOS register bit to determine if previous boot was successful
    114 
    115   @param PeiServices    pointer to the PEI Service Table
    116 
    117   @retval TRUE          - Previous Boot was success
    118   @retval FALSE         - Previous Boot wasn't success
    119 
    120 **/
    121 BOOLEAN
    122 IsPreviousBootSuccessful(
    123   IN CONST EFI_PEI_SERVICES   **PeiServices
    124 
    125   )
    126 {
    127   EFI_STATUS                      Status;
    128   BOOLEAN                         BootState;
    129   UINTN                           DataSize;
    130   CHAR16                          VarName[] = BOOT_STATE_VARIABLE_NAME;
    131   EFI_PEI_READ_ONLY_VARIABLE2_PPI *PeiVar;
    132 
    133   Status = (**PeiServices).LocatePpi (
    134                              PeiServices,
    135                              &gEfiPeiReadOnlyVariable2PpiGuid,
    136                              0,
    137                              NULL,
    138                                 (void **)&PeiVar
    139                              );
    140   ASSERT_EFI_ERROR (Status);
    141 
    142   //
    143   // Get last Boot State Variable to confirm that it is not a first boot .
    144   //
    145 
    146   DataSize = sizeof (BOOLEAN);
    147   Status = PeiVar->GetVariable (
    148                      PeiVar,
    149                      VarName,
    150                      &gEfiBootStateGuid,
    151                      NULL,
    152                      &DataSize,
    153                      &BootState
    154                      );
    155   if (EFI_ERROR (Status) || (BootState == TRUE)) {
    156     return FALSE;
    157   }
    158 
    159   DEBUG ((EFI_D_INFO, "Previous boot cycle successfully completed handover to OS\n"));
    160   return TRUE;
    161 }
    162 #ifdef NOCS_S3_SUPPORT
    163 EFI_STATUS
    164 UpdateBootMode (
    165   IN CONST EFI_PEI_SERVICES     **PeiServices
    166   )
    167 {
    168   EFI_STATUS      Status;
    169   EFI_BOOT_MODE   BootMode;
    170   UINT16          SleepType;
    171   CHAR16          *strBootMode;
    172 
    173   Status = (*PeiServices)->GetBootMode(PeiServices, &BootMode);
    174   ASSERT_EFI_ERROR (Status);
    175   if (BootMode  == BOOT_IN_RECOVERY_MODE){
    176     return Status;
    177   }
    178 
    179   //
    180   // Let's assume things are OK if not told otherwise
    181   //
    182   BootMode = BOOT_WITH_FULL_CONFIGURATION;
    183 
    184   if (GetSleepTypeAfterWakeup (PeiServices, &SleepType)) {
    185     switch (SleepType) {
    186       case V_PCH_ACPI_PM1_CNT_S3:
    187         BootMode = BOOT_ON_S3_RESUME;
    188         Status = (*PeiServices)->NotifyPpi (PeiServices, &mCapsuleNotifyList[0]);
    189         ASSERT_EFI_ERROR (Status);
    190         break;
    191 
    192       case V_PCH_ACPI_PM1_CNT_S4:
    193         BootMode = BOOT_ON_S4_RESUME;
    194         break;
    195 
    196       case V_PCH_ACPI_PM1_CNT_S5:
    197         BootMode = BOOT_ON_S5_RESUME;
    198         break;
    199     } // switch (SleepType)
    200   }
    201 
    202   if (IsFastBootEnabled (PeiServices) && IsPreviousBootSuccessful (PeiServices)) {
    203     DEBUG ((EFI_D_INFO, "Prioritizing Boot mode to BOOT_WITH_MINIMAL_CONFIGURATION\n"));
    204     PrioritizeBootMode (&BootMode, BOOT_WITH_MINIMAL_CONFIGURATION);
    205   }
    206 
    207   switch (BootMode) {
    208     case BOOT_WITH_FULL_CONFIGURATION:
    209       strBootMode = L"BOOT_WITH_FULL_CONFIGURATION";
    210       break;
    211     case BOOT_WITH_MINIMAL_CONFIGURATION:
    212       strBootMode = L"BOOT_WITH_MINIMAL_CONFIGURATION";
    213       break;
    214     case BOOT_ASSUMING_NO_CONFIGURATION_CHANGES:
    215       strBootMode = L"BOOT_ASSUMING_NO_CONFIGURATION_CHANGES";
    216       break;
    217     case BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS:
    218       strBootMode = L"BOOT_WITH_FULL_CONFIGURATION_PLUS_DIAGNOSTICS";
    219       break;
    220     case BOOT_WITH_DEFAULT_SETTINGS:
    221       strBootMode = L"BOOT_WITH_DEFAULT_SETTINGS";
    222       break;
    223     case BOOT_ON_S4_RESUME:
    224       strBootMode = L"BOOT_ON_S4_RESUME";
    225       break;
    226     case BOOT_ON_S5_RESUME:
    227       strBootMode = L"BOOT_ON_S5_RESUME";
    228       break;
    229     case BOOT_ON_S2_RESUME:
    230       strBootMode = L"BOOT_ON_S2_RESUME";
    231       break;
    232     case BOOT_ON_S3_RESUME:
    233       strBootMode = L"BOOT_ON_S3_RESUME";
    234 
    235       break;
    236     case BOOT_ON_FLASH_UPDATE:
    237       strBootMode = L"BOOT_ON_FLASH_UPDATE";
    238       break;
    239     case BOOT_IN_RECOVERY_MODE:
    240       strBootMode = L"BOOT_IN_RECOVERY_MODE";
    241       break;
    242     default:
    243       strBootMode = L"Unknown boot mode";
    244   } // switch (BootMode)
    245 
    246   DEBUG ((EFI_D_ERROR, "Setting BootMode to %s\n", strBootMode));
    247   Status = (*PeiServices)->SetBootMode(PeiServices, BootMode);
    248   ASSERT_EFI_ERROR (Status);
    249 
    250   return Status;
    251 }
    252 #endif
    253 
    254 /**
    255   Get sleep type after wakeup
    256 
    257   @param PeiServices       Pointer to the PEI Service Table.
    258   @param SleepType         Sleep type to be returned.
    259 
    260   @retval TRUE              A wake event occured without power failure.
    261   @retval FALSE             Power failure occured or not a wakeup.
    262 
    263 **/
    264 BOOLEAN
    265 GetSleepTypeAfterWakeup (
    266   IN  CONST EFI_PEI_SERVICES          **PeiServices,
    267   OUT UINT16                    *SleepType
    268   )
    269 {
    270   UINT16  Pm1Sts;
    271   UINT16  Pm1Cnt;
    272   UINT16  GenPmCon1;
    273   //
    274   // VLV BIOS Specification 0.6.2 - Section 18.4, "Power Failure Consideration"
    275   //
    276   // When the SUS_PWR_FLR bit is set, it indicates the SUS well power is lost.
    277   // This bit is in the SUS Well and defaults to 1b1 based on RSMRST# assertion (not cleared by any type of reset).
    278   // System BIOS should follow cold boot path if SUS_PWR_FLR (PBASE + 0x20[14]),
    279   // GEN_RST_STS (PBASE + 0x20[9]) or PWRBTNOR_STS (ABASE + 0x00[11]) is set to 1b1
    280   // regardless of the value in the SLP_TYP (ABASE + 0x04[12:10]) field.
    281   //
    282   GenPmCon1 = MmioRead16 (PMC_BASE_ADDRESS + R_PCH_PMC_GEN_PMCON_1);
    283 
    284   //
    285   // Read the ACPI registers
    286   //
    287   Pm1Sts  = IoRead16 (ACPI_BASE_ADDRESS + R_PCH_ACPI_PM1_STS);
    288   Pm1Cnt  = IoRead16 (ACPI_BASE_ADDRESS + R_PCH_ACPI_PM1_CNT);
    289 
    290   if ((GenPmCon1 & (B_PCH_PMC_GEN_PMCON_SUS_PWR_FLR | B_PCH_PMC_GEN_PMCON_GEN_RST_STS)) ||
    291      (Pm1Sts & B_PCH_ACPI_PM1_STS_PRBTNOR)) {
    292 	  //
    293     // If power failure indicator, then don't attempt s3 resume.
    294     // Clear PM1_CNT of S3 and set it to S5 as we just had a power failure, and memory has
    295     // lost already.  This is to make sure no one will use PM1_CNT to check for S3 after
    296     // power failure.
    297 	  //
    298     if ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S3) {
    299       Pm1Cnt = ((Pm1Cnt & ~B_PCH_ACPI_PM1_CNT_SLP_TYP) | V_PCH_ACPI_PM1_CNT_S5);
    300       IoWrite16 (ACPI_BASE_ADDRESS + R_PCH_ACPI_PM1_CNT, Pm1Cnt);
    301     }
    302 	  //
    303     // Clear Wake Status (WAK_STS)
    304     //
    305   }
    306   //
    307   // Get sleep type if a wake event occurred and there is no power failure
    308   //
    309   if ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S3) {
    310     *SleepType = Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP;
    311     return TRUE;
    312   } else if ((Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP) == V_PCH_ACPI_PM1_CNT_S4) {
    313     *SleepType = Pm1Cnt & B_PCH_ACPI_PM1_CNT_SLP_TYP;
    314     return TRUE;
    315   }
    316   return FALSE;
    317 }
    318 
    319 BOOLEAN
    320 EFIAPI
    321 IsFastBootEnabled (
    322   IN CONST EFI_PEI_SERVICES           **PeiServices
    323   )
    324 {
    325   EFI_STATUS                      Status;
    326   EFI_PEI_READ_ONLY_VARIABLE2_PPI *PeiReadOnlyVarPpi;
    327   UINTN                           VarSize;
    328   SYSTEM_CONFIGURATION            SystemConfiguration;
    329   BOOLEAN                         FastBootEnabledStatus;
    330 
    331   FastBootEnabledStatus = FALSE;
    332   Status = (**PeiServices).LocatePpi (
    333                              PeiServices,
    334                              &gEfiPeiReadOnlyVariable2PpiGuid,
    335                              0,
    336                              NULL,
    337                              (void **)&PeiReadOnlyVarPpi
    338                              );
    339   if (Status == EFI_SUCCESS) {
    340     VarSize = sizeof (SYSTEM_CONFIGURATION);
    341     Status = PeiReadOnlyVarPpi->GetVariable (
    342                                   PeiReadOnlyVarPpi,
    343                                   PLATFORM_SETUP_VARIABLE_NAME,
    344                                   &gEfiSetupVariableGuid,
    345                                   NULL,
    346                                   &VarSize,
    347                                   &SystemConfiguration
    348                                   );
    349     if (Status == EFI_SUCCESS) {
    350       if (SystemConfiguration.FastBoot != 0) {
    351         FastBootEnabledStatus = TRUE;
    352       }
    353     }
    354   }
    355 
    356   return FastBootEnabledStatus;
    357 }
    358 
    359 /**
    360   Given the current boot mode, and a proposed new boot mode, determine
    361   which has priority. If the new boot mode has higher priority, then
    362   make it the current boot mode.
    363 
    364   @param CurrentBootMode    pointer to current planned boot mode
    365   @param NewBootMode        proposed boot mode
    366 
    367   @retval EFI_NOT_FOUND      if either boot mode is not recognized
    368   @retval EFI_SUCCESS        if both boot mode values were recognized and
    369                              were processed.
    370 **/
    371 EFI_STATUS
    372 PrioritizeBootMode (
    373   IN OUT EFI_BOOT_MODE    *CurrentBootMode,
    374   IN EFI_BOOT_MODE        NewBootMode
    375   )
    376 {
    377   UINT32 CurrentIndex;
    378   UINT32 NewIndex;
    379 
    380   //
    381   // Find the position of the current boot mode in our priority array
    382   //
    383   for ( CurrentIndex = 0;
    384         CurrentIndex < ARRAY_SIZE (mBootModePriority);
    385         CurrentIndex++) {
    386     if (mBootModePriority[CurrentIndex] == *CurrentBootMode) {
    387       break;
    388     }
    389   }
    390   if (CurrentIndex >= ARRAY_SIZE (mBootModePriority)) {
    391     return EFI_NOT_FOUND;
    392   }
    393 
    394   //
    395   // Find the position of the new boot mode in our priority array
    396   //
    397   for ( NewIndex = 0;
    398         NewIndex < ARRAY_SIZE (mBootModePriority);
    399         NewIndex++) {
    400     if (mBootModePriority[NewIndex] == NewBootMode) {
    401       //
    402       // If this new boot mode occurs before the current boot mode in the
    403       // priority array, then take it.
    404       //
    405       if (NewIndex < CurrentIndex) {
    406         *CurrentBootMode = NewBootMode;
    407       }
    408       return EFI_SUCCESS;
    409     }
    410   }
    411   return EFI_NOT_FOUND;
    412 }
    413