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