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