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