1 /** @file
2 This is the driver that implements the QNC S3 Support protocol
3
4 Copyright (c) 2013-2016 Intel Corporation.
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 #include "QncS3Support.h"
16
17 //
18 // Global Variables
19 //
20 EFI_QNC_S3_SUPPORT_PROTOCOL mQncS3SupportProtocol;
21 QNC_S3_PARAMETER_HEADER *mS3Parameter;
22 UINT32 mQncS3ImageEntryPoint;
23 VOID *mQncS3ImageAddress;
24 UINTN mQncS3ImageSize;
25
26 extern EFI_GUID gQncS3CodeInLockBoxGuid;
27 extern EFI_GUID gQncS3ContextInLockBoxGuid;
28
29 /**
30
31 Create a buffer that is used to store context information for use with
32 dispatch functions.
33
34 @retval EFI_SUCCESS - Buffer allocated and initialized.
35
36 **/
37 EFI_STATUS
CreateContextBuffer(VOID)38 CreateContextBuffer (
39 VOID
40 )
41 {
42 EFI_STATUS Status;
43 EFI_PHYSICAL_ADDRESS Address;
44 UINT32 ContextStoreSize;
45
46 ContextStoreSize = EFI_PAGE_SIZE;
47
48 //
49 // Allcoate <4G EfiReservedMemory
50 //
51 Address = 0xFFFFFFFF;
52 Status = gBS->AllocatePages (AllocateMaxAddress, EfiReservedMemoryType, EFI_SIZE_TO_PAGES (ContextStoreSize), &Address);
53 if (EFI_ERROR (Status)) {
54 return Status;
55 }
56 mS3Parameter = (QNC_S3_PARAMETER_HEADER *) (UINTN) Address;
57
58 //
59 // Determine the maximum number of context entries that can be stored in this
60 // table.
61 //
62 mS3Parameter->MaxContexts = ((ContextStoreSize - sizeof(QNC_S3_PARAMETER_HEADER)) / sizeof(EFI_DISPATCH_CONTEXT_UNION)) + 1;
63 mS3Parameter->StorePosition = 0;
64
65 return Status;
66 }
67
68 //
69 // Functions
70 //
71 EFI_STATUS
72 EFIAPI
QncS3SupportEntryPoint(IN EFI_HANDLE ImageHandle,IN EFI_SYSTEM_TABLE * SystemTable)73 QncS3SupportEntryPoint (
74 IN EFI_HANDLE ImageHandle,
75 IN EFI_SYSTEM_TABLE *SystemTable
76 )
77 /*++
78
79 Routine Description:
80
81 QNC S3 support driver entry point
82
83 Arguments:
84
85 ImageHandle - Handle for the image of this driver
86 SystemTable - Pointer to the EFI System Table
87
88 Returns:
89
90 EFI_STATUS
91
92 --*/
93 {
94 EFI_STATUS Status;
95 VOID *TmpPtr;
96 EFI_EVENT Event;
97
98 //
99 // If the protocol is found execution is happening in ACPI NVS memory. If it
100 // is not found copy the driver into ACPI NVS memory and pass control to it.
101 //
102 Status = gBS->LocateProtocol (&gEfiCallerIdGuid, NULL, &TmpPtr);
103
104 //
105 // Load the QNC S3 image
106 //
107 if (EFI_ERROR (Status)) {
108 Status = LoadQncS3Image (SystemTable);
109 ASSERT_EFI_ERROR (Status);
110
111 } else {
112 DEBUG ((DEBUG_INFO, "QncS3SupportEntryPoint() in reserved memory - Begin\n"));
113 //
114 // Allocate and initialize context buffer.
115 //
116 Status = CreateContextBuffer ();
117
118 if (EFI_ERROR (Status)) {
119 return Status;
120 }
121 //
122 // Install the QNC S3 Support protocol
123 //
124 mQncS3SupportProtocol.SetDispatchItem = QncS3SetDispatchItem;
125 Status = gBS->InstallMultipleProtocolInterfaces (
126 &ImageHandle,
127 &gEfiQncS3SupportProtocolGuid,
128 &mQncS3SupportProtocol,
129 NULL
130 );
131
132 mQncS3ImageAddress = (VOID *)(UINTN)PcdGet64(PcdQncS3CodeInLockBoxAddress);
133 mQncS3ImageSize = (UINTN)PcdGet64(PcdQncS3CodeInLockBoxSize);
134 DEBUG ((DEBUG_INFO, "QncS3SupportEntry Code = %08x, Size = %08x\n", (UINTN)mQncS3ImageAddress, mQncS3ImageSize));
135 DEBUG ((DEBUG_INFO, "QncS3SupportEntry Contex = %08x, Size = %08x\n", (UINTN)mS3Parameter, EFI_PAGE_SIZE));
136 ASSERT (mQncS3ImageAddress != 0);
137
138 //
139 // Register EFI_END_OF_DXE_EVENT_GROUP_GUID event.
140 //
141 Status = gBS->CreateEventEx (
142 EVT_NOTIFY_SIGNAL,
143 TPL_CALLBACK,
144 QncS3BootEvent,
145 NULL,
146 &gEfiEndOfDxeEventGroupGuid,
147 &Event
148 );
149 ASSERT_EFI_ERROR (Status);
150
151 DEBUG ((DEBUG_INFO, "QncS3SupportEntryPoint() in reserved memory - End\n"));
152 }
153
154
155
156 return Status;
157 }
158
159 EFI_STATUS
160 EFIAPI
QncS3SetDispatchItem(IN EFI_QNC_S3_SUPPORT_PROTOCOL * This,IN EFI_QNC_S3_DISPATCH_ITEM * DispatchItem,OUT VOID ** S3DispatchEntryPoint,OUT VOID ** Context)161 QncS3SetDispatchItem (
162 IN EFI_QNC_S3_SUPPORT_PROTOCOL *This,
163 IN EFI_QNC_S3_DISPATCH_ITEM *DispatchItem,
164 OUT VOID **S3DispatchEntryPoint,
165 OUT VOID **Context
166 )
167 /*++
168
169 Routine Description:
170
171 Set an item to be dispatched at S3 resume time. At the same time, the entry point
172 of the QNC S3 support image is returned to be used in subsequent boot script save
173 call
174
175 Arguments:
176
177 This - Pointer to the protocol instance.
178 DispatchItem - The item to be dispatched.
179 S3DispatchEntryPoint - The entry point of the QNC S3 support image.
180
181 Returns:
182
183 EFI_STATUS - Successfully completed.
184 EFI_OUT_OF_RESOURCES - Out of resources.
185
186 --*/
187 {
188
189 DEBUG ((DEBUG_INFO, "QncS3SetDispatchItem() Start\n"));
190
191 //
192 // Set default values.
193 //
194 *S3DispatchEntryPoint = NULL;
195 *Context = NULL;
196
197 //
198 // Determine if this entry will fit.
199 //
200 if (mS3Parameter->StorePosition >= mS3Parameter->MaxContexts) {
201 DEBUG ((DEBUG_INFO, "QncS3SetDispatchItem exceeds max length - 0x%08x\n", (UINTN)mS3Parameter->MaxContexts));
202 return EFI_OUT_OF_RESOURCES;
203 }
204 //
205 // Calculate the size required;
206 // ** Always round up to be 8 byte aligned
207 //
208 switch (DispatchItem->Type) {
209 case QncS3ItemTypeInitPcieRootPortDownstream:
210 *S3DispatchEntryPoint = (VOID*) (UINTN)QncS3InitPcieRootPortDownstream;
211 *Context = &mS3Parameter->Contexts[mS3Parameter->StorePosition];
212 CopyMem (&mS3Parameter->Contexts[mS3Parameter->StorePosition], DispatchItem->Parameter, sizeof(UINT32));
213 DEBUG ((DEBUG_INFO, "QncS3InitPcieRootPortDownstream @ 0x%08x - context 0x%08x\n", (UINTN)*S3DispatchEntryPoint, (UINTN)*Context));
214 break;
215
216 default:
217 return EFI_UNSUPPORTED;
218
219 }
220
221 mS3Parameter->StorePosition ++;
222 DEBUG ((DEBUG_INFO, "QncS3SetDispatchItem() End\n"));
223
224 return EFI_SUCCESS;
225 }
226
227 EFI_STATUS
LoadQncS3Image(IN EFI_SYSTEM_TABLE * SystemTable)228 LoadQncS3Image (
229 IN EFI_SYSTEM_TABLE *SystemTable
230 )
231 /*++
232
233 Routine Description:
234
235 Load the QNC S3 Image into Efi Reserved Memory below 4G.
236
237 Arguments:
238
239 ImageEntryPoint the ImageEntryPoint after success loading
240
241 Returns:
242
243 EFI_STATUS
244
245 --*/
246 {
247 EFI_STATUS Status;
248 UINT8 *Buffer;
249 UINTN BufferSize;
250 VOID *FfsBuffer;
251 PE_COFF_LOADER_IMAGE_CONTEXT ImageContext;
252 EFI_HANDLE NewImageHandle;
253
254 //
255 // Install NULL protocol on module file handle to indicate that the entry point
256 // has been called for the first time.
257 //
258 NewImageHandle = NULL;
259 Status = gBS->InstallProtocolInterface (
260 &NewImageHandle,
261 &gEfiCallerIdGuid,
262 EFI_NATIVE_INTERFACE,
263 NULL
264 );
265
266
267 //
268 // Find this module so it can be loaded again.
269 //
270 Status = GetSectionFromAnyFv (
271 &gEfiCallerIdGuid,
272 EFI_SECTION_PE32,
273 0,
274 (VOID**) &Buffer,
275 &BufferSize
276 );
277 if (EFI_ERROR (Status)) {
278 return Status;
279 }
280
281
282 //
283 // Get information about the image being loaded.
284 //
285 ImageContext.Handle = Buffer;
286 ImageContext.ImageRead = PeCoffLoaderImageReadFromMemory;
287
288 //
289 // Get information about the image being loaded
290 //
291 Status = PeCoffLoaderGetImageInfo (&ImageContext);
292 ASSERT_EFI_ERROR (Status);
293 if (EFI_ERROR (Status)) {
294 return Status;
295 }
296
297 Status = gBS->AllocatePool (
298 EfiReservedMemoryType,
299 BufferSize + ImageContext.SectionAlignment,
300 &FfsBuffer
301 );
302 ASSERT_EFI_ERROR (Status);
303 if (EFI_ERROR (Status)) {
304 DEBUG ((DEBUG_INFO, "LoadQncS3Image failed for no enough space! \n"));
305 return EFI_OUT_OF_RESOURCES;
306 }
307
308 mQncS3ImageAddress = FfsBuffer;
309 mQncS3ImageSize = BufferSize + ImageContext.SectionAlignment;
310 Status = PcdSet64S (PcdQncS3CodeInLockBoxAddress, (UINT64)(UINTN)mQncS3ImageAddress);
311 ASSERT_EFI_ERROR (Status);
312 Status = PcdSet64S (PcdQncS3CodeInLockBoxSize, (UINT64)mQncS3ImageSize);
313 ASSERT_EFI_ERROR (Status);
314 //
315 // Align buffer on section boundary
316 //
317 ImageContext.ImageAddress = (PHYSICAL_ADDRESS)(UINTN)FfsBuffer;
318 if (ImageContext.SectionAlignment != 0) {
319 ImageContext.ImageAddress += ImageContext.SectionAlignment - 1;
320 ImageContext.ImageAddress &= ~(ImageContext.SectionAlignment - 1);
321 }
322
323 //
324 // Load the image to our new buffer
325 //
326 Status = PeCoffLoaderLoadImage (&ImageContext);
327 if (EFI_ERROR (Status)) {
328 gBS->FreePool (FfsBuffer);
329 DEBUG ((DEBUG_INFO, "LoadQncS3Image failed for PeCoffLoaderLoadImage failure! \n"));
330 return Status;
331 }
332
333 //
334 // Relocate the image in our new buffer
335 //
336 Status = PeCoffLoaderRelocateImage (&ImageContext);
337 if (EFI_ERROR (Status)) {
338 PeCoffLoaderUnloadImage (&ImageContext);
339 gBS->FreePool (FfsBuffer);
340 DEBUG ((DEBUG_INFO, "LoadQncS3Image failed for PeCoffLoaderRelocateImage failure! \n"));
341 return Status;
342 }
343
344 //
345 // Invalidate instruction cache and pass control to the image. This will perform
346 // the initialization of the module and publish the supporting protocols.
347 //
348 InvalidateInstructionCacheRange ((VOID *)(UINTN)ImageContext.ImageAddress, (UINTN)ImageContext.ImageSize);
349 Status = ((EFI_IMAGE_ENTRY_POINT)(UINTN)(ImageContext.EntryPoint)) (NewImageHandle, SystemTable);
350 if (EFI_ERROR (Status)) {
351 gBS->FreePool (FfsBuffer);
352 return Status;
353 }
354
355 return EFI_SUCCESS;
356
357 }
358
359 EFI_STATUS
QncS3InitPcieRootPortDownstream(IN EFI_HANDLE ImageHandle,IN VOID * Context)360 QncS3InitPcieRootPortDownstream (
361 IN EFI_HANDLE ImageHandle,
362 IN VOID *Context
363 )
364 /*++
365
366 Routine Description:
367 Perform Init Root Port Downstream devices on S3 resume
368
369 Arguments:
370 Parameter Parameters passed in from DXE
371
372 Returns:
373 EFI_STATUS
374
375 --*/
376 {
377 EFI_STATUS Status;
378
379 DEBUG ((DEBUG_INFO, "QncS3InitPcieRootPortDownstream() Begin\n"));
380
381 //
382 // Initialize the device behind the root port.
383 //
384 Status = PciExpressInit ();
385
386 //
387 // Not checking the error status here - downstream device not present does not
388 // mean an error of this root port. Our return status of EFI_SUCCESS means this
389 // port is enabled and outer function depends on this return status to do
390 // subsequent initializations.
391 //
392
393 if (Status != EFI_SUCCESS){
394 DEBUG ((DEBUG_INFO, "QncS3InitPcieRootPortDownstream() failed\n"));
395 }
396
397 DEBUG ((DEBUG_INFO, "QncS3InitPcieRootPortDownstream() End\n"));
398 return Status;
399 }
400
401 VOID
402 EFIAPI
QncS3BootEvent(IN EFI_EVENT Event,IN VOID * Context)403 QncS3BootEvent (
404 IN EFI_EVENT Event,
405 IN VOID *Context
406 )
407 {
408 EFI_STATUS Status;
409
410 //
411 // These 2 boxes will be restored by RestoreAllLockBoxInPlace in S3Resume automatically
412 //
413 DEBUG ((DEBUG_INFO, "SaveLockBox QncS3Code = %08x, Size = %08x\n", (UINTN)mQncS3ImageAddress, mQncS3ImageSize));
414 SaveLockBox(&gQncS3CodeInLockBoxGuid, mQncS3ImageAddress, mQncS3ImageSize);
415 Status = SetLockBoxAttributes (&gQncS3CodeInLockBoxGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
416 ASSERT_EFI_ERROR (Status);
417
418 DEBUG ((DEBUG_INFO, "SaveLockBox QncS3Context = %08x, Size = %08x\n", (UINTN)mS3Parameter, EFI_PAGE_SIZE));
419 SaveLockBox(&gQncS3ContextInLockBoxGuid, (VOID *)mS3Parameter, EFI_PAGE_SIZE);
420 Status = SetLockBoxAttributes (&gQncS3ContextInLockBoxGuid, LOCK_BOX_ATTRIBUTE_RESTORE_IN_PLACE);
421 ASSERT_EFI_ERROR (Status);
422 }
423
424