]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystem.c
MdeModulePkg: Replace BSD License with BSD+Patent License
[mirror_edk2.git] / MdeModulePkg / Universal / ResetSystemRuntimeDxe / ResetSystem.c
CommitLineData
51a0c5f2 1/** @file\r
cf6da556 2 Reset Architectural and Reset Notification protocols implementation.\r
51a0c5f2 3\r
e2531da3 4 Copyright (c) 2006 - 2019, Intel Corporation. All rights reserved.<BR>\r
51a0c5f2 5\r
9d510e61 6 SPDX-License-Identifier: BSD-2-Clause-Patent\r
51a0c5f2 7\r
8**/\r
9\r
10#include "ResetSystem.h"\r
11\r
4bf95a9f
RN
12GLOBAL_REMOVE_IF_UNREFERENCED CHAR16 *mResetTypeStr[] = {\r
13 L"Cold", L"Warm", L"Shutdown", L"PlatformSpecific"\r
14};\r
15\r
99a6529e
MK
16//\r
17// The current ResetSystem() notification recursion depth\r
18//\r
19UINTN mResetNotifyDepth = 0;\r
20\r
cf6da556
RN
21/**\r
22 Register a notification function to be called when ResetSystem() is called.\r
23\r
24 The RegisterResetNotify() function registers a notification function that is called when\r
25 ResetSystem()is called and prior to completing the reset of the platform.\r
26 The registered functions must not perform a platform reset themselves. These\r
27 notifications are intended only for the notification of components which may need some\r
28 special-purpose maintenance prior to the platform resetting.\r
29 The list of registered reset notification functions are processed if ResetSystem()is called\r
30 before ExitBootServices(). The list of registered reset notification functions is ignored if\r
31 ResetSystem()is called after ExitBootServices().\r
32\r
33 @param[in] This A pointer to the EFI_RESET_NOTIFICATION_PROTOCOL instance.\r
34 @param[in] ResetFunction Points to the function to be called when a ResetSystem() is executed.\r
35\r
36 @retval EFI_SUCCESS The reset notification function was successfully registered.\r
37 @retval EFI_INVALID_PARAMETER ResetFunction is NULL.\r
38 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to register the reset notification function.\r
39 @retval EFI_ALREADY_STARTED The reset notification function specified by ResetFunction has already been registered.\r
40\r
41**/\r
42EFI_STATUS\r
43EFIAPI\r
44RegisterResetNotify (\r
45 IN EFI_RESET_NOTIFICATION_PROTOCOL *This,\r
46 IN EFI_RESET_SYSTEM ResetFunction\r
47 )\r
48{\r
49 RESET_NOTIFICATION_INSTANCE *Instance;\r
50 LIST_ENTRY *Link;\r
51 RESET_NOTIFY_ENTRY *Entry;\r
52\r
53 if (ResetFunction == NULL) {\r
54 return EFI_INVALID_PARAMETER;\r
55 }\r
56\r
57 Instance = RESET_NOTIFICATION_INSTANCE_FROM_THIS (This);\r
58\r
59 for ( Link = GetFirstNode (&Instance->ResetNotifies)\r
60 ; !IsNull (&Instance->ResetNotifies, Link)\r
61 ; Link = GetNextNode (&Instance->ResetNotifies, Link)\r
62 ) {\r
63 Entry = RESET_NOTIFY_ENTRY_FROM_LINK (Link);\r
64 if (Entry->ResetNotify == ResetFunction) {\r
65 return EFI_ALREADY_STARTED;\r
66 }\r
67 }\r
68\r
69 ASSERT (IsNull (&Instance->ResetNotifies, Link));\r
70 Entry = AllocatePool (sizeof (*Entry));\r
71 if (Entry == NULL) {\r
72 return EFI_OUT_OF_RESOURCES;\r
73 }\r
74 Entry->Signature = RESET_NOTIFY_ENTRY_SIGNATURE;\r
75 Entry->ResetNotify = ResetFunction;\r
76 InsertTailList (&Instance->ResetNotifies, &Entry->Link);\r
77 return EFI_SUCCESS;\r
78}\r
79\r
80/**\r
81 Unregister a notification function.\r
82\r
83 The UnregisterResetNotify() function removes the previously registered\r
84 notification using RegisterResetNotify().\r
85\r
86 @param[in] This A pointer to the EFI_RESET_NOTIFICATION_PROTOCOL instance.\r
87 @param[in] ResetFunction The pointer to the ResetFunction being unregistered.\r
88\r
89 @retval EFI_SUCCESS The reset notification function was unregistered.\r
90 @retval EFI_INVALID_PARAMETER ResetFunction is NULL.\r
91 @retval EFI_INVALID_PARAMETER The reset notification function specified by ResetFunction was not previously\r
92 registered using RegisterResetNotify().\r
93\r
94**/\r
95EFI_STATUS\r
96EFIAPI\r
97UnregisterResetNotify (\r
98 IN EFI_RESET_NOTIFICATION_PROTOCOL *This,\r
99 IN EFI_RESET_SYSTEM ResetFunction\r
100 )\r
101{\r
102 RESET_NOTIFICATION_INSTANCE *Instance;\r
103 LIST_ENTRY *Link;\r
104 RESET_NOTIFY_ENTRY *Entry;\r
105\r
106 if (ResetFunction == NULL) {\r
107 return EFI_INVALID_PARAMETER;\r
108 }\r
109\r
110 Instance = RESET_NOTIFICATION_INSTANCE_FROM_THIS (This);\r
111\r
112 for ( Link = GetFirstNode (&Instance->ResetNotifies)\r
113 ; !IsNull (&Instance->ResetNotifies, Link)\r
114 ; Link = GetNextNode (&Instance->ResetNotifies, Link)\r
115 ) {\r
116 Entry = RESET_NOTIFY_ENTRY_FROM_LINK (Link);\r
117 if (Entry->ResetNotify == ResetFunction) {\r
118 RemoveEntryList (&Entry->Link);\r
119 FreePool (Entry);\r
120 return EFI_SUCCESS;\r
121 }\r
122 }\r
123\r
124 return EFI_INVALID_PARAMETER;\r
125}\r
126\r
127RESET_NOTIFICATION_INSTANCE mResetNotification = {\r
128 RESET_NOTIFICATION_INSTANCE_SIGNATURE,\r
129 {\r
130 RegisterResetNotify,\r
131 UnregisterResetNotify\r
132 },\r
133 INITIALIZE_LIST_HEAD_VARIABLE (mResetNotification.ResetNotifies)\r
134};\r
135\r
99a6529e
MK
136RESET_NOTIFICATION_INSTANCE mPlatformSpecificResetFilter = {\r
137 RESET_NOTIFICATION_INSTANCE_SIGNATURE,\r
138 {\r
139 RegisterResetNotify,\r
140 UnregisterResetNotify\r
141 },\r
142 INITIALIZE_LIST_HEAD_VARIABLE (mPlatformSpecificResetFilter.ResetNotifies)\r
143};\r
144\r
145RESET_NOTIFICATION_INSTANCE mPlatformSpecificResetHandler = {\r
146 RESET_NOTIFICATION_INSTANCE_SIGNATURE,\r
147 {\r
148 RegisterResetNotify,\r
149 UnregisterResetNotify\r
150 },\r
151 INITIALIZE_LIST_HEAD_VARIABLE (mPlatformSpecificResetHandler.ResetNotifies)\r
152};\r
153\r
51a0c5f2 154/**\r
155 The driver's entry point.\r
156\r
157 It initializes the Reset Architectural Protocol.\r
158\r
d1102dba 159 @param[in] ImageHandle The firmware allocated handle for the EFI image.\r
51a0c5f2 160 @param[in] SystemTable A pointer to the EFI System Table.\r
d1102dba 161\r
51a0c5f2 162 @retval EFI_SUCCESS The entry point is executed successfully.\r
163 @retval other Cannot install ResetArch protocol.\r
164\r
165**/\r
166EFI_STATUS\r
167EFIAPI\r
168InitializeResetSystem (\r
169 IN EFI_HANDLE ImageHandle,\r
170 IN EFI_SYSTEM_TABLE *SystemTable\r
171 )\r
172{\r
173 EFI_STATUS Status;\r
34861f43 174 EFI_HANDLE Handle;\r
51a0c5f2 175\r
176 //\r
177 // Make sure the Reset Architectural Protocol is not already installed in the system\r
178 //\r
179 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiResetArchProtocolGuid);\r
180\r
181 //\r
182 // Hook the runtime service table\r
183 //\r
e2531da3 184 gRT->ResetSystem = RuntimeServiceResetSystem;\r
51a0c5f2 185\r
186 //\r
187 // Now install the Reset RT AP on a new handle\r
188 //\r
34861f43 189 Handle = NULL;\r
51a0c5f2 190 Status = gBS->InstallMultipleProtocolInterfaces (\r
34861f43 191 &Handle,\r
cf6da556
RN
192 &gEfiResetArchProtocolGuid, NULL,\r
193 &gEfiResetNotificationProtocolGuid, &mResetNotification.ResetNotification,\r
99a6529e
MK
194 &gEdkiiPlatformSpecificResetFilterProtocolGuid, &mPlatformSpecificResetFilter.ResetNotification,\r
195 &gEdkiiPlatformSpecificResetHandlerProtocolGuid, &mPlatformSpecificResetHandler.ResetNotification,\r
51a0c5f2 196 NULL\r
197 );\r
198 ASSERT_EFI_ERROR (Status);\r
199\r
200 return Status;\r
201}\r
202\r
51a0c5f2 203/**\r
204 Resets the entire platform.\r
205\r
206 @param[in] ResetType The type of reset to perform.\r
207 @param[in] ResetStatus The status code for the reset.\r
37078045 208 @param[in] DataSize The size, in bytes, of ResetData.\r
51a0c5f2 209 @param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, or\r
210 EfiResetShutdown the data buffer starts with a Null-terminated\r
211 string, optionally followed by additional binary data.\r
37078045
RN
212 The string is a description that the caller may use to further\r
213 indicate the reason for the system reset. ResetData is only\r
214 valid if ResetStatus is something other than EFI_SUCCESS\r
215 unless the ResetType is EfiResetPlatformSpecific\r
216 where a minimum amount of ResetData is always required.\r
eeeabe40
SZ
217 For a ResetType of EfiResetPlatformSpecific the data buffer\r
218 also starts with a Null-terminated string that is followed\r
219 by an EFI_GUID that describes the specific type of reset to perform.\r
51a0c5f2 220**/\r
221VOID\r
222EFIAPI\r
e2531da3 223RuntimeServiceResetSystem (\r
51a0c5f2 224 IN EFI_RESET_TYPE ResetType,\r
225 IN EFI_STATUS ResetStatus,\r
226 IN UINTN DataSize,\r
227 IN VOID *ResetData OPTIONAL\r
228 )\r
229{\r
cf6da556
RN
230 LIST_ENTRY *Link;\r
231 RESET_NOTIFY_ENTRY *Entry;\r
99a6529e 232\r
37623a5c 233 //\r
e2531da3 234 // Only do REPORT_STATUS_CODE() on first call to RuntimeServiceResetSystem()\r
37623a5c 235 //\r
99a6529e
MK
236 if (mResetNotifyDepth == 0) {\r
237 //\r
238 // Indicate reset system runtime service is called.\r
239 //\r
240 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_EFI_RUNTIME_SERVICE | EFI_SW_RS_PC_RESET_SYSTEM));\r
241 }\r
51a0c5f2 242\r
99a6529e 243 mResetNotifyDepth++;\r
9c227e07
RN
244 DEBUG ((\r
245 DEBUG_INFO, "DXE ResetSystem2: ResetType %s, Call Depth = %d.\n",\r
246 mResetTypeStr[ResetType], mResetNotifyDepth\r
247 ));\r
4bf95a9f
RN
248\r
249 if (mResetNotifyDepth <= MAX_RESET_NOTIFY_DEPTH) {\r
250 if (!EfiAtRuntime ()) {\r
251 //\r
252 // Call reset notification functions registered through the\r
253 // EDKII_PLATFORM_SPECIFIC_RESET_FILTER_PROTOCOL.\r
254 //\r
255 for ( Link = GetFirstNode (&mPlatformSpecificResetFilter.ResetNotifies)\r
256 ; !IsNull (&mPlatformSpecificResetFilter.ResetNotifies, Link)\r
257 ; Link = GetNextNode (&mPlatformSpecificResetFilter.ResetNotifies, Link)\r
258 ) {\r
259 Entry = RESET_NOTIFY_ENTRY_FROM_LINK (Link);\r
260 Entry->ResetNotify (ResetType, ResetStatus, DataSize, ResetData);\r
261 }\r
262 //\r
263 // Call reset notification functions registered through the\r
264 // EFI_RESET_NOTIFICATION_PROTOCOL.\r
265 //\r
266 for ( Link = GetFirstNode (&mResetNotification.ResetNotifies)\r
267 ; !IsNull (&mResetNotification.ResetNotifies, Link)\r
268 ; Link = GetNextNode (&mResetNotification.ResetNotifies, Link)\r
269 ) {\r
270 Entry = RESET_NOTIFY_ENTRY_FROM_LINK (Link);\r
271 Entry->ResetNotify (ResetType, ResetStatus, DataSize, ResetData);\r
272 }\r
273 //\r
d1102dba 274 // call reset notification functions registered through the\r
4bf95a9f
RN
275 // EDKII_PLATFORM_SPECIFIC_RESET_HANDLER_PROTOCOL.\r
276 //\r
277 for ( Link = GetFirstNode (&mPlatformSpecificResetHandler.ResetNotifies)\r
278 ; !IsNull (&mPlatformSpecificResetHandler.ResetNotifies, Link)\r
279 ; Link = GetNextNode (&mPlatformSpecificResetHandler.ResetNotifies, Link)\r
280 ) {\r
281 Entry = RESET_NOTIFY_ENTRY_FROM_LINK (Link);\r
282 Entry->ResetNotify (ResetType, ResetStatus, DataSize, ResetData);\r
283 }\r
cf6da556 284 }\r
4bf95a9f
RN
285 } else {\r
286 ASSERT (ResetType < ARRAY_SIZE (mResetTypeStr));\r
287 DEBUG ((DEBUG_ERROR, "DXE ResetSystem2: Maximum reset call depth is met. Use the current reset type: %s!\n", mResetTypeStr[ResetType]));\r
cf6da556
RN
288 }\r
289\r
51a0c5f2 290 switch (ResetType) {\r
291 case EfiResetWarm:\r
292\r
51a0c5f2 293 ResetWarm ();\r
51a0c5f2 294 break;\r
295\r
296 case EfiResetCold:\r
297 ResetCold ();\r
298 break;\r
299\r
300 case EfiResetShutdown:\r
301 ResetShutdown ();\r
302 return ;\r
303\r
37078045
RN
304 case EfiResetPlatformSpecific:\r
305 ResetPlatformSpecific (DataSize, ResetData);\r
306 return;\r
307\r
51a0c5f2 308 default:\r
309 return ;\r
310 }\r
311\r
312 //\r
313 // Given we should have reset getting here would be bad\r
314 //\r
315 ASSERT (FALSE);\r
316}\r