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