]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystem.c
MdeModulePkg/ResetSystemRuntimeDxe: Add platform filter and handler
[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
34861f43 4 Copyright (c) 2006 - 2017, Intel Corporation. All rights reserved.<BR>\r
51a0c5f2 5\r
6 This program and the accompanying materials\r
7 are licensed and made available under the terms and conditions of the BSD License\r
8 which accompanies this distribution. The full text of the license may be found at\r
9 http://opensource.org/licenses/bsd-license.php\r
10\r
11 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
12 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
13\r
14**/\r
15\r
16#include "ResetSystem.h"\r
17\r
99a6529e
MK
18//\r
19// The current ResetSystem() notification recursion depth\r
20//\r
21UINTN mResetNotifyDepth = 0;\r
22\r
cf6da556
RN
23/**\r
24 Register a notification function to be called when ResetSystem() is called.\r
25\r
26 The RegisterResetNotify() function registers a notification function that is called when\r
27 ResetSystem()is called and prior to completing the reset of the platform.\r
28 The registered functions must not perform a platform reset themselves. These\r
29 notifications are intended only for the notification of components which may need some\r
30 special-purpose maintenance prior to the platform resetting.\r
31 The list of registered reset notification functions are processed if ResetSystem()is called\r
32 before ExitBootServices(). The list of registered reset notification functions is ignored if\r
33 ResetSystem()is called after ExitBootServices().\r
34\r
35 @param[in] This A pointer to the EFI_RESET_NOTIFICATION_PROTOCOL instance.\r
36 @param[in] ResetFunction Points to the function to be called when a ResetSystem() is executed.\r
37\r
38 @retval EFI_SUCCESS The reset notification function was successfully registered.\r
39 @retval EFI_INVALID_PARAMETER ResetFunction is NULL.\r
40 @retval EFI_OUT_OF_RESOURCES There are not enough resources available to register the reset notification function.\r
41 @retval EFI_ALREADY_STARTED The reset notification function specified by ResetFunction has already been registered.\r
42\r
43**/\r
44EFI_STATUS\r
45EFIAPI\r
46RegisterResetNotify (\r
47 IN EFI_RESET_NOTIFICATION_PROTOCOL *This,\r
48 IN EFI_RESET_SYSTEM ResetFunction\r
49 )\r
50{\r
51 RESET_NOTIFICATION_INSTANCE *Instance;\r
52 LIST_ENTRY *Link;\r
53 RESET_NOTIFY_ENTRY *Entry;\r
54\r
55 if (ResetFunction == NULL) {\r
56 return EFI_INVALID_PARAMETER;\r
57 }\r
58\r
59 Instance = RESET_NOTIFICATION_INSTANCE_FROM_THIS (This);\r
60\r
61 for ( Link = GetFirstNode (&Instance->ResetNotifies)\r
62 ; !IsNull (&Instance->ResetNotifies, Link)\r
63 ; Link = GetNextNode (&Instance->ResetNotifies, Link)\r
64 ) {\r
65 Entry = RESET_NOTIFY_ENTRY_FROM_LINK (Link);\r
66 if (Entry->ResetNotify == ResetFunction) {\r
67 return EFI_ALREADY_STARTED;\r
68 }\r
69 }\r
70\r
71 ASSERT (IsNull (&Instance->ResetNotifies, Link));\r
72 Entry = AllocatePool (sizeof (*Entry));\r
73 if (Entry == NULL) {\r
74 return EFI_OUT_OF_RESOURCES;\r
75 }\r
76 Entry->Signature = RESET_NOTIFY_ENTRY_SIGNATURE;\r
77 Entry->ResetNotify = ResetFunction;\r
78 InsertTailList (&Instance->ResetNotifies, &Entry->Link);\r
79 return EFI_SUCCESS;\r
80}\r
81\r
82/**\r
83 Unregister a notification function.\r
84\r
85 The UnregisterResetNotify() function removes the previously registered\r
86 notification using RegisterResetNotify().\r
87\r
88 @param[in] This A pointer to the EFI_RESET_NOTIFICATION_PROTOCOL instance.\r
89 @param[in] ResetFunction The pointer to the ResetFunction being unregistered.\r
90\r
91 @retval EFI_SUCCESS The reset notification function was unregistered.\r
92 @retval EFI_INVALID_PARAMETER ResetFunction is NULL.\r
93 @retval EFI_INVALID_PARAMETER The reset notification function specified by ResetFunction was not previously\r
94 registered using RegisterResetNotify().\r
95\r
96**/\r
97EFI_STATUS\r
98EFIAPI\r
99UnregisterResetNotify (\r
100 IN EFI_RESET_NOTIFICATION_PROTOCOL *This,\r
101 IN EFI_RESET_SYSTEM ResetFunction\r
102 )\r
103{\r
104 RESET_NOTIFICATION_INSTANCE *Instance;\r
105 LIST_ENTRY *Link;\r
106 RESET_NOTIFY_ENTRY *Entry;\r
107\r
108 if (ResetFunction == NULL) {\r
109 return EFI_INVALID_PARAMETER;\r
110 }\r
111\r
112 Instance = RESET_NOTIFICATION_INSTANCE_FROM_THIS (This);\r
113\r
114 for ( Link = GetFirstNode (&Instance->ResetNotifies)\r
115 ; !IsNull (&Instance->ResetNotifies, Link)\r
116 ; Link = GetNextNode (&Instance->ResetNotifies, Link)\r
117 ) {\r
118 Entry = RESET_NOTIFY_ENTRY_FROM_LINK (Link);\r
119 if (Entry->ResetNotify == ResetFunction) {\r
120 RemoveEntryList (&Entry->Link);\r
121 FreePool (Entry);\r
122 return EFI_SUCCESS;\r
123 }\r
124 }\r
125\r
126 return EFI_INVALID_PARAMETER;\r
127}\r
128\r
129RESET_NOTIFICATION_INSTANCE mResetNotification = {\r
130 RESET_NOTIFICATION_INSTANCE_SIGNATURE,\r
131 {\r
132 RegisterResetNotify,\r
133 UnregisterResetNotify\r
134 },\r
135 INITIALIZE_LIST_HEAD_VARIABLE (mResetNotification.ResetNotifies)\r
136};\r
137\r
99a6529e
MK
138RESET_NOTIFICATION_INSTANCE mPlatformSpecificResetFilter = {\r
139 RESET_NOTIFICATION_INSTANCE_SIGNATURE,\r
140 {\r
141 RegisterResetNotify,\r
142 UnregisterResetNotify\r
143 },\r
144 INITIALIZE_LIST_HEAD_VARIABLE (mPlatformSpecificResetFilter.ResetNotifies)\r
145};\r
146\r
147RESET_NOTIFICATION_INSTANCE mPlatformSpecificResetHandler = {\r
148 RESET_NOTIFICATION_INSTANCE_SIGNATURE,\r
149 {\r
150 RegisterResetNotify,\r
151 UnregisterResetNotify\r
152 },\r
153 INITIALIZE_LIST_HEAD_VARIABLE (mPlatformSpecificResetHandler.ResetNotifies)\r
154};\r
155\r
51a0c5f2 156/**\r
157 The driver's entry point.\r
158\r
159 It initializes the Reset Architectural Protocol.\r
160\r
161 @param[in] ImageHandle The firmware allocated handle for the EFI image. \r
162 @param[in] SystemTable A pointer to the EFI System Table.\r
163 \r
164 @retval EFI_SUCCESS The entry point is executed successfully.\r
165 @retval other Cannot install ResetArch protocol.\r
166\r
167**/\r
168EFI_STATUS\r
169EFIAPI\r
170InitializeResetSystem (\r
171 IN EFI_HANDLE ImageHandle,\r
172 IN EFI_SYSTEM_TABLE *SystemTable\r
173 )\r
174{\r
175 EFI_STATUS Status;\r
34861f43 176 EFI_HANDLE Handle;\r
51a0c5f2 177\r
178 //\r
179 // Make sure the Reset Architectural Protocol is not already installed in the system\r
180 //\r
181 ASSERT_PROTOCOL_ALREADY_INSTALLED (NULL, &gEfiResetArchProtocolGuid);\r
182\r
183 //\r
184 // Hook the runtime service table\r
185 //\r
186 gRT->ResetSystem = ResetSystem;\r
187\r
188 //\r
189 // Now install the Reset RT AP on a new handle\r
190 //\r
34861f43 191 Handle = NULL;\r
51a0c5f2 192 Status = gBS->InstallMultipleProtocolInterfaces (\r
34861f43 193 &Handle,\r
cf6da556
RN
194 &gEfiResetArchProtocolGuid, NULL,\r
195 &gEfiResetNotificationProtocolGuid, &mResetNotification.ResetNotification,\r
99a6529e
MK
196 &gEdkiiPlatformSpecificResetFilterProtocolGuid, &mPlatformSpecificResetFilter.ResetNotification,\r
197 &gEdkiiPlatformSpecificResetHandlerProtocolGuid, &mPlatformSpecificResetHandler.ResetNotification,\r
51a0c5f2 198 NULL\r
199 );\r
200 ASSERT_EFI_ERROR (Status);\r
201\r
202 return Status;\r
203}\r
204\r
51a0c5f2 205/**\r
206 Put the system into S3 power state. \r
207**/\r
208VOID\r
209DoS3 (\r
210 VOID\r
211 )\r
212{\r
213 EnterS3WithImmediateWake ();\r
214\r
215 //\r
216 // Should not return\r
217 //\r
218 CpuDeadLoop ();\r
219}\r
220\r
221/**\r
222 Resets the entire platform.\r
223\r
224 @param[in] ResetType The type of reset to perform.\r
225 @param[in] ResetStatus The status code for the reset.\r
37078045 226 @param[in] DataSize The size, in bytes, of ResetData.\r
51a0c5f2 227 @param[in] ResetData For a ResetType of EfiResetCold, EfiResetWarm, or\r
228 EfiResetShutdown the data buffer starts with a Null-terminated\r
229 string, optionally followed by additional binary data.\r
37078045
RN
230 The string is a description that the caller may use to further\r
231 indicate the reason for the system reset. ResetData is only\r
232 valid if ResetStatus is something other than EFI_SUCCESS\r
233 unless the ResetType is EfiResetPlatformSpecific\r
234 where a minimum amount of ResetData is always required.\r
eeeabe40
SZ
235 For a ResetType of EfiResetPlatformSpecific the data buffer\r
236 also starts with a Null-terminated string that is followed\r
237 by an EFI_GUID that describes the specific type of reset to perform.\r
51a0c5f2 238**/\r
239VOID\r
240EFIAPI\r
241ResetSystem (\r
242 IN EFI_RESET_TYPE ResetType,\r
243 IN EFI_STATUS ResetStatus,\r
244 IN UINTN DataSize,\r
245 IN VOID *ResetData OPTIONAL\r
246 )\r
247{\r
cf6da556
RN
248 EFI_STATUS Status;\r
249 UINTN Size;\r
250 UINTN CapsuleDataPtr;\r
251 LIST_ENTRY *Link;\r
252 RESET_NOTIFY_ENTRY *Entry;\r
99a6529e
MK
253\r
254 //\r
255 // Above the maximum recursion depth, so do the smallest amount of\r
256 // work to perform a cold reset.\r
257 //\r
258 if (mResetNotifyDepth >= MAX_RESET_NOTIFY_DEPTH) {\r
259 ResetCold ();\r
260 ASSERT (FALSE);\r
261 return;\r
262 }\r
263\r
37623a5c 264 //\r
99a6529e 265 // Only do REPORT_STATUS_CODE() on first call to ResetSystem()\r
37623a5c 266 //\r
99a6529e
MK
267 if (mResetNotifyDepth == 0) {\r
268 //\r
269 // Indicate reset system runtime service is called.\r
270 //\r
271 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, (EFI_SOFTWARE_EFI_RUNTIME_SERVICE | EFI_SW_RS_PC_RESET_SYSTEM));\r
272 }\r
51a0c5f2 273\r
99a6529e
MK
274 mResetNotifyDepth++;\r
275 if (!EfiAtRuntime () && mResetNotifyDepth < MAX_RESET_NOTIFY_DEPTH) {\r
276 //\r
277 // Call reset notification functions registered through the\r
278 // EDKII_PLATFORM_SPECIFIC_RESET_FILTER_PROTOCOL.\r
279 //\r
280 for ( Link = GetFirstNode (&mPlatformSpecificResetFilter.ResetNotifies)\r
281 ; !IsNull (&mPlatformSpecificResetFilter.ResetNotifies, Link)\r
282 ; Link = GetNextNode (&mPlatformSpecificResetFilter.ResetNotifies, Link)\r
283 ) {\r
284 Entry = RESET_NOTIFY_ENTRY_FROM_LINK (Link);\r
285 Entry->ResetNotify (ResetType, ResetStatus, DataSize, ResetData);\r
286 }\r
287 //\r
288 // Call reset notification functions registered through the\r
289 // EFI_RESET_NOTIFICATION_PROTOCOL.\r
290 //\r
cf6da556
RN
291 for ( Link = GetFirstNode (&mResetNotification.ResetNotifies)\r
292 ; !IsNull (&mResetNotification.ResetNotifies, Link)\r
293 ; Link = GetNextNode (&mResetNotification.ResetNotifies, Link)\r
294 ) {\r
295 Entry = RESET_NOTIFY_ENTRY_FROM_LINK (Link);\r
99a6529e
MK
296 Entry->ResetNotify (ResetType, ResetStatus, DataSize, ResetData);\r
297 }\r
298 //\r
299 // call reset notification functions registered through the \r
300 // EDKII_PLATFORM_SPECIFIC_RESET_HANDLER_PROTOCOL.\r
301 //\r
302 for ( Link = GetFirstNode (&mPlatformSpecificResetHandler.ResetNotifies)\r
303 ; !IsNull (&mPlatformSpecificResetHandler.ResetNotifies, Link)\r
304 ; Link = GetNextNode (&mPlatformSpecificResetHandler.ResetNotifies, Link)\r
305 ) {\r
306 Entry = RESET_NOTIFY_ENTRY_FROM_LINK (Link);\r
cf6da556
RN
307 Entry->ResetNotify (ResetType, ResetStatus, DataSize, ResetData);\r
308 }\r
309 }\r
310\r
51a0c5f2 311 switch (ResetType) {\r
312 case EfiResetWarm:\r
313\r
314 //\r
315 //Check if there are pending capsules to process\r
316 //\r
317 Size = sizeof (CapsuleDataPtr);\r
318 Status = EfiGetVariable (\r
319 EFI_CAPSULE_VARIABLE_NAME,\r
320 &gEfiCapsuleVendorGuid,\r
321 NULL,\r
322 &Size,\r
323 (VOID *) &CapsuleDataPtr\r
324 );\r
325\r
326 if (Status == EFI_SUCCESS) {\r
327 //\r
328 //Process capsules across a system reset.\r
329 //\r
330 DoS3();\r
331 }\r
332\r
333 ResetWarm ();\r
334\r
335 break;\r
336\r
337 case EfiResetCold:\r
338 ResetCold ();\r
339 break;\r
340\r
341 case EfiResetShutdown:\r
342 ResetShutdown ();\r
343 return ;\r
344\r
37078045
RN
345 case EfiResetPlatformSpecific:\r
346 ResetPlatformSpecific (DataSize, ResetData);\r
347 return;\r
348\r
51a0c5f2 349 default:\r
350 return ;\r
351 }\r
352\r
353 //\r
354 // Given we should have reset getting here would be bad\r
355 //\r
356 ASSERT (FALSE);\r
357}\r