1 /** @file
2   Reset System lib using PSCI hypervisor or secure monitor calls
3 
4   Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
5   Copyright (c) 2013, ARM Ltd. All rights reserved.<BR>
6   Copyright (c) 2014-2020, Linaro Ltd. All rights reserved.<BR>
7   Copyright (c) 2019, Intel Corporation. All rights reserved.<BR>
8 
9   SPDX-License-Identifier: BSD-2-Clause-Patent
10 
11 **/
12 
13 #include <PiPei.h>
14 
15 #include <libfdt.h>
16 #include <Library/ArmHvcLib.h>
17 #include <Library/ArmSmcLib.h>
18 #include <Library/BaseLib.h>
19 #include <Library/DebugLib.h>
20 #include <Library/HobLib.h>
21 #include <Library/ResetSystemLib.h>
22 
23 #include <IndustryStandard/ArmStdSmc.h>
24 
25 typedef enum {
26   PsciMethodUnknown,
27   PsciMethodSmc,
28   PsciMethodHvc,
29 } PSCI_METHOD;
30 
31 STATIC
32 PSCI_METHOD
DiscoverPsciMethod(VOID)33 DiscoverPsciMethod (
34   VOID
35   )
36 {
37   VOID                            *DeviceTreeBase;
38   INT32                           Node, Prev;
39   INT32                           Len;
40   CONST CHAR8                     *Compatible;
41   CONST CHAR8                     *CompatibleItem;
42   CONST VOID                      *Prop;
43 
44   DeviceTreeBase = (VOID*)(UINTN)PcdGet64 (PcdDeviceTreeInitialBaseAddress);
45   ASSERT (fdt_check_header (DeviceTreeBase) == 0);
46 
47   //
48   // Enumerate all FDT nodes looking for the PSCI node and capture the method
49   //
50   for (Prev = 0;; Prev = Node) {
51     Node = fdt_next_node (DeviceTreeBase, Prev, NULL);
52     if (Node < 0) {
53       break;
54     }
55 
56     Compatible = fdt_getprop (DeviceTreeBase, Node, "compatible", &Len);
57     if (Compatible == NULL) {
58       continue;
59     }
60 
61     //
62     // Iterate over the NULL-separated items in the compatible string
63     //
64     for (CompatibleItem = Compatible; CompatibleItem < Compatible + Len;
65       CompatibleItem += 1 + AsciiStrLen (CompatibleItem)) {
66 
67       if (AsciiStrCmp (CompatibleItem, "arm,psci-0.2") != 0) {
68         continue;
69       }
70 
71       Prop = fdt_getprop (DeviceTreeBase, Node, "method", NULL);
72       if (!Prop) {
73         DEBUG ((DEBUG_ERROR, "%a: Missing PSCI method property\n",
74           __FUNCTION__));
75         return PsciMethodUnknown;
76       }
77 
78       if (AsciiStrnCmp (Prop, "hvc", 3) == 0) {
79         return PsciMethodHvc;
80       } else if (AsciiStrnCmp (Prop, "smc", 3) == 0) {
81         return PsciMethodSmc;
82       } else {
83         DEBUG ((DEBUG_ERROR, "%a: Unknown PSCI method \"%a\"\n", __FUNCTION__,
84           Prop));
85         return PsciMethodUnknown;
86       }
87     }
88   }
89   return PsciMethodUnknown;
90 }
91 
92 STATIC
93 VOID
PerformPsciAction(IN UINTN Arg0)94 PerformPsciAction (
95   IN  UINTN     Arg0
96   )
97 {
98   ARM_SMC_ARGS ArmSmcArgs;
99   ARM_HVC_ARGS ArmHvcArgs;
100 
101   ArmSmcArgs.Arg0 = Arg0;
102   ArmHvcArgs.Arg0 = Arg0;
103 
104   switch (DiscoverPsciMethod ()) {
105   case PsciMethodHvc:
106     ArmCallHvc (&ArmHvcArgs);
107     break;
108 
109   case PsciMethodSmc:
110     ArmCallSmc (&ArmSmcArgs);
111     break;
112 
113   default:
114     DEBUG ((DEBUG_ERROR, "%a: no PSCI method defined\n", __FUNCTION__));
115     ASSERT (FALSE);
116   }
117 }
118 
119 /**
120   This function causes a system-wide reset (cold reset), in which
121   all circuitry within the system returns to its initial state. This type of reset
122   is asynchronous to system operation and operates without regard to
123   cycle boundaries.
124 
125   If this function returns, it means that the system does not support cold reset.
126 **/
127 VOID
128 EFIAPI
ResetCold(VOID)129 ResetCold (
130   VOID
131   )
132 {
133   // Send a PSCI 0.2 SYSTEM_RESET command
134   PerformPsciAction (ARM_SMC_ID_PSCI_SYSTEM_RESET);
135 }
136 
137 /**
138   This function causes a system-wide initialization (warm reset), in which all processors
139   are set to their initial state. Pending cycles are not corrupted.
140 
141   If this function returns, it means that the system does not support warm reset.
142 **/
143 VOID
144 EFIAPI
ResetWarm(VOID)145 ResetWarm (
146   VOID
147   )
148 {
149   // Map a warm reset into a cold reset
150   ResetCold ();
151 }
152 
153 /**
154   This function causes the system to enter a power state equivalent
155   to the ACPI G2/S5 or G3 states.
156 
157   If this function returns, it means that the system does not support shutdown reset.
158 **/
159 VOID
160 EFIAPI
ResetShutdown(VOID)161 ResetShutdown (
162   VOID
163   )
164 {
165   // Send a PSCI 0.2 SYSTEM_OFF command
166   PerformPsciAction (ARM_SMC_ID_PSCI_SYSTEM_OFF);
167 }
168 
169 /**
170   This function causes a systemwide reset. The exact type of the reset is
171   defined by the EFI_GUID that follows the Null-terminated Unicode string passed
172   into ResetData. If the platform does not recognize the EFI_GUID in ResetData
173   the platform must pick a supported reset type to perform.The platform may
174   optionally log the parameters from any non-normal reset that occurs.
175 
176   @param[in]  DataSize   The size, in bytes, of ResetData.
177   @param[in]  ResetData  The data buffer starts with a Null-terminated string,
178                          followed by the EFI_GUID.
179 **/
180 VOID
181 EFIAPI
ResetPlatformSpecific(IN UINTN DataSize,IN VOID * ResetData)182 ResetPlatformSpecific (
183   IN UINTN   DataSize,
184   IN VOID    *ResetData
185   )
186 {
187   // Map the platform specific reset as reboot
188   ResetCold ();
189 }
190 
191 /**
192   The ResetSystem function resets the entire platform.
193 
194   @param[in] ResetType      The type of reset to perform.
195   @param[in] ResetStatus    The status code for the reset.
196   @param[in] DataSize       The size, in bytes, of ResetData.
197   @param[in] ResetData      For a ResetType of EfiResetCold, EfiResetWarm, or EfiResetShutdown
198                             the data buffer starts with a Null-terminated string, optionally
199                             followed by additional binary data. The string is a description
200                             that the caller may use to further indicate the reason for the
201                             system reset.
202 **/
203 VOID
204 EFIAPI
ResetSystem(IN EFI_RESET_TYPE ResetType,IN EFI_STATUS ResetStatus,IN UINTN DataSize,IN VOID * ResetData OPTIONAL)205 ResetSystem (
206   IN EFI_RESET_TYPE               ResetType,
207   IN EFI_STATUS                   ResetStatus,
208   IN UINTN                        DataSize,
209   IN VOID                         *ResetData OPTIONAL
210   )
211 {
212   switch (ResetType) {
213   case EfiResetWarm:
214     ResetWarm ();
215     break;
216 
217   case EfiResetCold:
218     ResetCold ();
219     break;
220 
221   case EfiResetShutdown:
222     ResetShutdown ();
223     return;
224 
225   case EfiResetPlatformSpecific:
226     ResetPlatformSpecific (DataSize, ResetData);
227     return;
228 
229   default:
230     return;
231   }
232 }
233