]> git.proxmox.com Git - mirror_edk2.git/blob - SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgent/SecPeiDebugAgentLib.c
0132942dea6b7e6cbb9f1fdce727de3c09b0e273
[mirror_edk2.git] / SourceLevelDebugPkg / Library / DebugAgent / SecPeiDebugAgent / SecPeiDebugAgentLib.c
1 /** @file
2 SEC Core Debug Agent Library instance implementition.
3
4 Copyright (c) 2010 - 2013, Intel Corporation. All rights reserved.<BR>
5 This program and the accompanying materials
6 are licensed and made available under the terms and conditions of the BSD License
7 which accompanies this distribution. The full text of the license may be found at
8 http://opensource.org/licenses/bsd-license.php.
9
10 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
11 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
12
13 **/
14
15 #include "SecPeiDebugAgentLib.h"
16
17 BOOLEAN mSkipBreakpoint = FALSE;
18
19 EFI_PEI_NOTIFY_DESCRIPTOR mMemoryDiscoveredNotifyList[1] = {
20 {
21 (EFI_PEI_PPI_DESCRIPTOR_NOTIFY_CALLBACK | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
22 &gEfiPeiMemoryDiscoveredPpiGuid,
23 DebugAgentCallbackMemoryDiscoveredPpi
24 }
25 };
26
27 /**
28 Check if debug agent support multi-processor.
29
30 @retval TRUE Multi-processor is supported.
31 @retval FALSE Multi-processor is not supported.
32
33 **/
34 BOOLEAN
35 MultiProcessorDebugSupport (
36 VOID
37 )
38 {
39 return FALSE;
40 }
41
42 /**
43 Read the Attach/Break-in symbols from the debug port.
44
45 @param[in] Handle Pointer to Debug Port handle.
46 @param[out] BreakSymbol Returned break symbol.
47
48 @retval EFI_SUCCESS Read the symbol in BreakSymbol.
49 @retval EFI_NOT_FOUND No read the break symbol.
50
51 **/
52 EFI_STATUS
53 DebugReadBreakSymbol (
54 IN DEBUG_PORT_HANDLE Handle,
55 OUT UINT8 *BreakSymbol
56 )
57 {
58 EFI_STATUS Status;
59 DEBUG_PACKET_HEADER DebugHeader;
60 UINT8 *Data8;
61
62 *BreakSymbol = 0;
63 //
64 // If Debug Port buffer has data, read it till it was break symbol or Debug Port buffer empty.
65 //
66 Data8 = (UINT8 *) &DebugHeader;
67 while (TRUE) {
68 //
69 // If start symbol is not received
70 //
71 if (!DebugPortPollBuffer (Handle)) {
72 //
73 // If no data in Debug Port, exit
74 //
75 break;
76 }
77 //
78 // Try to read the start symbol
79 //
80 DebugPortReadBuffer (Handle, Data8, 1, 0);
81 if (*Data8 == DEBUG_STARTING_SYMBOL_ATTACH) {
82 *BreakSymbol = *Data8;
83 DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Debug Timer attach symbol received %x", *BreakSymbol);
84 return EFI_SUCCESS;
85 }
86 if (*Data8 == DEBUG_STARTING_SYMBOL_NORMAL) {
87 Status = ReadRemainingBreakPacket (Handle, &DebugHeader);
88 if (Status == EFI_SUCCESS) {
89 *BreakSymbol = DebugHeader.Command;
90 DebugAgentMsgPrint (DEBUG_AGENT_INFO, "Debug Timer break symbol received %x", *BreakSymbol);
91 return EFI_SUCCESS;
92 }
93 if (Status == EFI_TIMEOUT) {
94 break;
95 }
96 }
97 }
98
99 return EFI_NOT_FOUND;
100 }
101
102 /**
103 Get the pointer to location saved Mailbox pointer from IDT entry.
104
105 **/
106 VOID *
107 GetLocationSavedMailboxPointerInIdtEntry (
108 VOID
109 )
110 {
111 UINTN *MailboxLocation;
112
113 MailboxLocation = (UINTN *) GetExceptionHandlerInIdtEntry (DEBUG_MAILBOX_VECTOR);
114 //
115 // *MailboxLocation is the pointer to Mailbox
116 //
117 VerifyMailboxChecksum ((DEBUG_AGENT_MAILBOX *) (*MailboxLocation));
118 return MailboxLocation;
119 }
120
121 /**
122 Set the pointer of Mailbox into IDT entry before memory is ready.
123
124 @param[in] MailboxLocation Pointer to location saved Mailbox pointer.
125
126 **/
127 VOID
128 SetLocationSavedMailboxPointerInIdtEntry (
129 IN VOID *MailboxLocation
130 )
131 {
132 SetExceptionHandlerInIdtEntry (DEBUG_MAILBOX_VECTOR, MailboxLocation);
133 }
134
135 /**
136 Get the location of Mailbox pointer from the GUIDed HOB.
137
138 @return Pointer to the location saved Mailbox pointer.
139
140 **/
141 UINT64 *
142 GetMailboxLocationFromHob (
143 VOID
144 )
145 {
146 EFI_HOB_GUID_TYPE *GuidHob;
147
148 GuidHob = GetFirstGuidHob (&gEfiDebugAgentGuid);
149 if (GuidHob == NULL) {
150 return NULL;
151 }
152 return (UINT64 *) (GET_GUID_HOB_DATA(GuidHob));
153 }
154
155 /**
156 Get Debug Agent Mailbox pointer.
157
158 @return Mailbox pointer.
159
160 **/
161 DEBUG_AGENT_MAILBOX *
162 GetMailboxPointer (
163 VOID
164 )
165 {
166 UINT64 DebugPortHandle;
167 UINT64 *MailboxLocationInIdt;
168 UINT64 *MailboxLocationInHob;
169 DEBUG_AGENT_MAILBOX *Mailbox;
170
171 //
172 // Get mailbox from IDT entry firstly
173 //
174 MailboxLocationInIdt = GetLocationSavedMailboxPointerInIdtEntry ();
175 Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocationInIdt);
176 //
177 // Cannot used GetDebugFlag() to get Debug Flag to avoid GetMailboxPointer() nested
178 //
179 if (Mailbox->DebugFlag.Bits.CheckMailboxInHob != 1 ||
180 Mailbox->DebugFlag.Bits.InitArch != DEBUG_ARCH_SYMBOL) {
181 //
182 // If mailbox was setup in SEC or the current CPU arch is different from the init arch
183 // Debug Agent initialized, return the mailbox from IDT entry directly.
184 // Otherwise, we need to check the mailbox location saved in GUIDed HOB further.
185 //
186 return Mailbox;
187 }
188
189 MailboxLocationInHob = GetMailboxLocationFromHob ();
190 //
191 // Compare mailbox in IDT enry with mailbox in HOB,
192 // need to fix mailbox location if HOB moved by PEI CORE
193 //
194 if (MailboxLocationInHob != MailboxLocationInIdt && MailboxLocationInHob != NULL) {
195 Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocationInHob);
196 //
197 // Fix up Debug Port handler and save new mailbox in IDT entry
198 //
199 Mailbox = (DEBUG_AGENT_MAILBOX *)((UINTN)Mailbox + ((UINTN)(MailboxLocationInHob) - (UINTN)MailboxLocationInIdt));
200 DebugPortHandle = (UINT64)((UINTN)Mailbox->DebugPortHandle + ((UINTN)(MailboxLocationInHob) - (UINTN)MailboxLocationInIdt));
201 UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle);
202 *MailboxLocationInHob = (UINT64)(UINTN)Mailbox;
203 SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationInHob);
204 //
205 // Clean CheckMailboxInHob flag
206 //
207 Mailbox->DebugFlag.Bits.CheckMailboxInHob = 0;
208 UpdateMailboxChecksum (Mailbox);
209 }
210
211 return Mailbox;
212 }
213
214 /**
215 Get debug port handle.
216
217 @return Debug port handle.
218
219 **/
220 DEBUG_PORT_HANDLE
221 GetDebugPortHandle (
222 VOID
223 )
224 {
225 DEBUG_AGENT_MAILBOX *DebugAgentMailbox;
226
227 DebugAgentMailbox = GetMailboxPointer ();
228
229 return (DEBUG_PORT_HANDLE) (UINTN)(DebugAgentMailbox->DebugPortHandle);
230 }
231
232 /**
233 Debug Agent provided notify callback function on Memory Discovered PPI.
234
235 @param[in] PeiServices Indirect reference to the PEI Services Table.
236 @param[in] NotifyDescriptor Address of the notification descriptor data structure.
237 @param[in] Ppi Address of the PPI that was installed.
238
239 @retval EFI_SUCCESS If the function completed successfully.
240
241 **/
242 EFI_STATUS
243 EFIAPI
244 DebugAgentCallbackMemoryDiscoveredPpi (
245 IN EFI_PEI_SERVICES **PeiServices,
246 IN EFI_PEI_NOTIFY_DESCRIPTOR *NotifyDescriptor,
247 IN VOID *Ppi
248 )
249 {
250 EFI_STATUS Status;
251 DEBUG_AGENT_MAILBOX *Mailbox;
252 BOOLEAN InterruptStatus;
253 EFI_PHYSICAL_ADDRESS Address;
254 DEBUG_AGENT_MAILBOX *NewMailbox;
255 UINT64 *MailboxLocationInHob;
256
257 //
258 // Save and disable original interrupt status
259 //
260 InterruptStatus = SaveAndDisableInterrupts ();
261
262 //
263 // Allocate ACPI NVS memory for new Mailbox and Debug Port Handle buffer
264 //
265 Status = PeiServicesAllocatePages (
266 EfiACPIMemoryNVS,
267 EFI_SIZE_TO_PAGES (sizeof(DEBUG_AGENT_MAILBOX) + PcdGet16(PcdDebugPortHandleBufferSize)),
268 &Address
269 );
270 ASSERT_EFI_ERROR (Status);
271 NewMailbox = (DEBUG_AGENT_MAILBOX *) (UINTN) Address;
272 //
273 // Copy Mailbox and Debug Port Handle buffer to new location in ACPI NVS memory, because original Mailbox
274 // and Debug Port Handle buffer in the allocated pool that may be marked as free by DXE Core after DXE Core
275 // reallocates the HOB.
276 //
277 Mailbox = GetMailboxPointer ();
278 CopyMem (NewMailbox, Mailbox, sizeof (DEBUG_AGENT_MAILBOX));
279 CopyMem (NewMailbox + 1, (VOID *)(UINTN)Mailbox->DebugPortHandle, PcdGet16(PcdDebugPortHandleBufferSize));
280 //
281 // Update Mailbox Location pointer in GUIDed HOB and IDT entry with new one
282 //
283 MailboxLocationInHob = GetMailboxLocationFromHob ();
284 *MailboxLocationInHob = (UINT64)(UINTN)NewMailbox;
285 SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationInHob);
286 //
287 // Update Debug Port Handle in new Mailbox
288 //
289 UpdateMailboxContent (NewMailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, (UINT64)(UINTN)(NewMailbox + 1));
290 //
291 // Set physical memory ready flag
292 //
293 SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1);
294
295 if (IsHostAttached ()) {
296 //
297 // Trigger one software interrupt to inform HOST
298 //
299 TriggerSoftInterrupt (MEMORY_READY_SIGNATURE);
300 }
301
302 //
303 // Restore interrupt state.
304 //
305 SetInterruptState (InterruptStatus);
306
307 return EFI_SUCCESS;
308 }
309
310 /**
311 Initialize debug agent.
312
313 This function is used to set up debug environment for SEC and PEI phase.
314
315 If InitFlag is DEBUG_AGENT_INIT_PREMEM_SEC, it will overirde IDT table entries
316 and initialize debug port. It will enable interrupt to support break-in feature.
317 It will set up debug agent Mailbox in cache-as-ramfrom. It will be called before
318 physical memory is ready.
319 If InitFlag is DEBUG_AGENT_INIT_POSTMEM_SEC, debug agent will build one GUIDed
320 HOB to copy debug agent Mailbox. It will be called after physical memory is ready.
321
322 This function is used to set up debug environment to support source level debugging.
323 If certain Debug Agent Library instance has to save some private data in the stack,
324 this function must work on the mode that doesn't return to the caller, then
325 the caller needs to wrap up all rest of logic after InitializeDebugAgent() into one
326 function and pass it into InitializeDebugAgent(). InitializeDebugAgent() is
327 responsible to invoke the passing-in function at the end of InitializeDebugAgent().
328
329 If the parameter Function is not NULL, Debug Agent Libary instance will invoke it by
330 passing in the Context to be its parameter.
331
332 If Function() is NULL, Debug Agent Library instance will return after setup debug
333 environment.
334
335 @param[in] InitFlag Init flag is used to decide the initialize process.
336 @param[in] Context Context needed according to InitFlag; it was optional.
337 @param[in] Function Continue function called by debug agent library; it was
338 optional.
339
340 **/
341 VOID
342 EFIAPI
343 InitializeDebugAgent (
344 IN UINT32 InitFlag,
345 IN VOID *Context, OPTIONAL
346 IN DEBUG_AGENT_CONTINUE Function OPTIONAL
347 )
348 {
349 DEBUG_AGENT_MAILBOX *Mailbox;
350 DEBUG_AGENT_MAILBOX MailboxInStack;
351 DEBUG_AGENT_PHASE2_CONTEXT Phase2Context;
352 DEBUG_AGENT_CONTEXT_POSTMEM_SEC *DebugAgentContext;
353 EFI_STATUS Status;
354 IA32_DESCRIPTOR *Ia32Idtr;
355 IA32_IDT_ENTRY *Ia32IdtEntry;
356 UINT64 DebugPortHandle;
357 UINT64 MailboxLocation;
358 UINT64 *MailboxLocationPointer;
359
360 DisableInterrupts ();
361
362 switch (InitFlag) {
363
364 case DEBUG_AGENT_INIT_PREMEM_SEC:
365
366 InitializeDebugIdt ();
367
368 MailboxLocation = (UINT64)(UINTN)&MailboxInStack;
369 Mailbox = &MailboxInStack;
370 ZeroMem ((VOID *) Mailbox, sizeof (DEBUG_AGENT_MAILBOX));
371 //
372 // Get and save debug port handle and set the length of memory block.
373 //
374 SetLocationSavedMailboxPointerInIdtEntry (&MailboxLocation);
375 //
376 // Force error message could be printed during the first shakehand between Target/HOST.
377 //
378 SetDebugFlag (DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL, DEBUG_AGENT_ERROR);
379 //
380 // Save init arch type when debug agent initialized
381 //
382 SetDebugFlag (DEBUG_AGENT_FLAG_INIT_ARCH, sizeof (UINTN) / 4);
383
384 InitializeDebugTimer ();
385
386 Phase2Context.Context = Context;
387 Phase2Context.Function = Function;
388 DebugPortInitialize ((VOID *) &Phase2Context, InitializeDebugAgentPhase2);
389 //
390 // If reaches here, it means Debug Port initialization failed.
391 //
392 DEBUG ((EFI_D_ERROR, "Debug Agent: Debug port initialization failed.\n"));
393
394 break;
395
396 case DEBUG_AGENT_INIT_POSTMEM_SEC:
397 Mailbox = GetMailboxPointer ();
398 //
399 // Memory has been ready
400 //
401 SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1);
402 if (IsHostAttached ()) {
403 //
404 // Trigger one software interrupt to inform HOST
405 //
406 TriggerSoftInterrupt (MEMORY_READY_SIGNATURE);
407 }
408
409 //
410 // Fix up Debug Port handle address and mailbox address
411 //
412 DebugAgentContext = (DEBUG_AGENT_CONTEXT_POSTMEM_SEC *) Context;
413 DebugPortHandle = (UINT64)(UINT32)(Mailbox->DebugPortHandle + DebugAgentContext->StackMigrateOffset);
414 UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle);
415 Mailbox = (DEBUG_AGENT_MAILBOX *) ((UINTN) Mailbox + DebugAgentContext->StackMigrateOffset);
416 MailboxLocation = (UINT64)(UINTN)Mailbox;
417 //
418 // Build mailbox location in HOB and fix-up its address
419 //
420 MailboxLocationPointer = BuildGuidDataHob (
421 &gEfiDebugAgentGuid,
422 &MailboxLocation,
423 sizeof (UINT64)
424 );
425 MailboxLocationPointer = (UINT64 *) ((UINTN) MailboxLocationPointer + DebugAgentContext->HeapMigrateOffset);
426 //
427 // Update IDT entry to save the location saved mailbox pointer
428 //
429 SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationPointer);
430 //
431 // Enable CPU interrupts so debug timer interrupts can be delivered
432 //
433 EnableInterrupts ();
434
435 break;
436
437 case DEBUG_AGENT_INIT_PEI:
438 //
439 // Check if Debug Agent has initialized before
440 //
441 if (IsDebugAgentInitialzed()) {
442 DEBUG ((EFI_D_WARN, "Debug Agent: It has already initialized in SEC Core!\n"));
443 break;
444 }
445 //
446 // Set up IDT entries
447 //
448 InitializeDebugIdt ();
449 //
450 // Build mailbox in HOB and setup Mailbox Set In Pei flag
451 //
452 Mailbox = AllocateZeroPool (sizeof (DEBUG_AGENT_MAILBOX));
453 MailboxLocation = (UINT64)(UINTN)Mailbox;
454 MailboxLocationPointer = BuildGuidDataHob (
455 &gEfiDebugAgentGuid,
456 &MailboxLocation,
457 sizeof (UINT64)
458 );
459
460 InitializeDebugTimer ();
461 //
462 // Update IDT entry to save the location pointer saved mailbox pointer
463 //
464 SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationPointer);
465 //
466 // Save init arch type when debug agent initialized
467 //
468 SetDebugFlag (DEBUG_AGENT_FLAG_INIT_ARCH, DEBUG_ARCH_SYMBOL);
469 //
470 // Register for a callback once memory has been initialized.
471 // If memery has been ready, the callback funtion will be invoked immediately
472 //
473 Status = PeiServicesNotifyPpi (&mMemoryDiscoveredNotifyList[0]);
474 ASSERT_EFI_ERROR (Status);
475 //
476 // Set HOB check flag if memory has not been ready yet
477 //
478 if (GetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY) == 0) {
479 SetDebugFlag (DEBUG_AGENT_FLAG_CHECK_MAILBOX_IN_HOB, 1);
480 }
481
482 Phase2Context.Context = Context;
483 Phase2Context.Function = Function;
484 DebugPortInitialize ((VOID *) &Phase2Context, InitializeDebugAgentPhase2);
485
486 FindAndReportModuleImageInfo (4);
487
488 break;
489
490 case DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64:
491 if (Context == NULL) {
492 DEBUG ((EFI_D_ERROR, "DebugAgent: Input parameter Context cannot be NULL!\n"));
493 CpuDeadLoop ();
494 } else {
495 Ia32Idtr = (IA32_DESCRIPTOR *) Context;
496 Ia32IdtEntry = (IA32_IDT_ENTRY *)(Ia32Idtr->Base);
497 MailboxLocationPointer = (UINT64 *) (UINTN) (Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetLow +
498 (Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetHigh << 16));
499 Mailbox = (DEBUG_AGENT_MAILBOX *) (UINTN)(*MailboxLocationPointer);
500 //
501 // Mailbox should valid and setup before executing thunk code
502 //
503 VerifyMailboxChecksum (Mailbox);
504
505 DebugPortHandle = (UINT64) (UINTN)DebugPortInitialize ((VOID *)(UINTN)Mailbox->DebugPortHandle, NULL);
506 UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle);
507 //
508 // Set up IDT entries
509 //
510 InitializeDebugIdt ();
511 //
512 // Update IDT entry to save location pointer saved the mailbox pointer
513 //
514 SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationPointer);
515
516 FindAndReportModuleImageInfo (4);
517 }
518 break;
519
520 default:
521 //
522 // Only DEBUG_AGENT_INIT_PREMEM_SEC and DEBUG_AGENT_INIT_POSTMEM_SEC are allowed for this
523 // Debug Agent library instance.
524 //
525 DEBUG ((EFI_D_ERROR, "Debug Agent: The InitFlag value is not allowed!\n"));
526 CpuDeadLoop ();
527 break;
528 }
529
530 EnableInterrupts ();
531
532 //
533 // If Function is not NULL, invoke it always whatever debug agent was initialized sucesssfully or not.
534 //
535 if (Function != NULL) {
536 Function (Context);
537 }
538 }
539
540 /**
541 Caller provided function to be invoked at the end of DebugPortInitialize().
542
543 Refer to the descrption for DebugPortInitialize() for more details.
544
545 @param[in] Context The first input argument of DebugPortInitialize().
546 @param[in] DebugPortHandle Debug port handle created by Debug Communication Libary.
547
548 **/
549 VOID
550 EFIAPI
551 InitializeDebugAgentPhase2 (
552 IN VOID *Context,
553 IN DEBUG_PORT_HANDLE DebugPortHandle
554 )
555 {
556 DEBUG_AGENT_PHASE2_CONTEXT *Phase2Context;
557 UINT64 *MailboxLocation;
558 DEBUG_AGENT_MAILBOX *Mailbox;
559 EFI_SEC_PEI_HAND_OFF *SecCoreData;
560 UINT16 BufferSize;
561 UINT64 NewDebugPortHandle;
562
563 Phase2Context = (DEBUG_AGENT_PHASE2_CONTEXT *) Context;
564 MailboxLocation = GetLocationSavedMailboxPointerInIdtEntry ();
565 Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation);
566 BufferSize = PcdGet16(PcdDebugPortHandleBufferSize);
567 if (Phase2Context->Function == NULL && DebugPortHandle != NULL && BufferSize != 0) {
568 NewDebugPortHandle = (UINT64)(UINTN)AllocateCopyPool (BufferSize, DebugPortHandle);
569 } else {
570 NewDebugPortHandle = (UINT64)(UINTN)DebugPortHandle;
571 }
572 UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, NewDebugPortHandle);
573
574 //
575 // Trigger one software interrupt to inform HOST
576 //
577 TriggerSoftInterrupt (SYSTEM_RESET_SIGNATURE);
578
579 //
580 // If Temporary RAM region is below 128 MB, then send message to
581 // host to disable low memory filtering.
582 //
583 SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Phase2Context->Context;
584 if ((UINTN)SecCoreData->TemporaryRamBase < BASE_128MB && IsHostAttached ()) {
585 SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1);
586 TriggerSoftInterrupt (MEMORY_READY_SIGNATURE);
587 }
588
589 //
590 // Enable CPU interrupts so debug timer interrupts can be delivered
591 //
592 EnableInterrupts ();
593
594 //
595 // Call continuation function if it is not NULL.
596 //
597 if (Phase2Context->Function != NULL) {
598 Phase2Context->Function (Phase2Context->Context);
599 }
600 }