1 /** @file
2 
3   Copyright (c) 2004  - 2014, Intel Corporation. All rights reserved.<BR>
4 
5 
6   This program and the accompanying materials are licensed and made available under
7 
8   the terms and conditions of the BSD License that accompanies this distribution.
9 
10   The full text of the license may be found at
11 
12   http://opensource.org/licenses/bsd-license.php.
13 
14 
15 
16   THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
17 
18   WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
19 
20 
21 
22 
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 };
CapsulePpiNotifyCallback(IN EFI_PEI_SERVICES ** PeiServices,IN EFI_PEI_NOTIFY_DESCRIPTOR * NotifyDescriptor,IN VOID * Ppi)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 /**
IsPreviousBootSuccessful(IN CONST EFI_PEI_SERVICES ** PeiServices)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
GetSleepTypeAfterWakeup(IN CONST EFI_PEI_SERVICES ** PeiServices,OUT UINT16 * SleepType)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 1�b1 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 1�b1
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.
PrioritizeBootMode(IN OUT EFI_BOOT_MODE * CurrentBootMode,IN EFI_BOOT_MODE NewBootMode)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 < sizeof (mBootModePriority) / sizeof (mBootModePriority[0]);
385         CurrentIndex++) {
386     if (mBootModePriority[CurrentIndex] == *CurrentBootMode) {
387       break;
388     }
389   }
390   if (CurrentIndex >= sizeof (mBootModePriority) / sizeof (mBootModePriority[0])) {
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 < sizeof (mBootModePriority) / sizeof (mBootModePriority[0]);
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