]> git.proxmox.com Git - mirror_edk2.git/blob - SourceLevelDebugPkg/Library/DebugAgent/SecPeiDebugAgent/SecPeiDebugAgentLib.c
Add security check.
[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 ASSERT (MailboxLocationInHob != NULL);
285 *MailboxLocationInHob = (UINT64)(UINTN)NewMailbox;
286 SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationInHob);
287 //
288 // Update Debug Port Handle in new Mailbox
289 //
290 UpdateMailboxContent (NewMailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, (UINT64)(UINTN)(NewMailbox + 1));
291 //
292 // Set physical memory ready flag
293 //
294 SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1);
295
296 if (IsHostAttached ()) {
297 //
298 // Trigger one software interrupt to inform HOST
299 //
300 TriggerSoftInterrupt (MEMORY_READY_SIGNATURE);
301 }
302
303 //
304 // Restore interrupt state.
305 //
306 SetInterruptState (InterruptStatus);
307
308 return EFI_SUCCESS;
309 }
310
311 /**
312 Initialize debug agent.
313
314 This function is used to set up debug environment for SEC and PEI phase.
315
316 If InitFlag is DEBUG_AGENT_INIT_PREMEM_SEC, it will overirde IDT table entries
317 and initialize debug port. It will enable interrupt to support break-in feature.
318 It will set up debug agent Mailbox in cache-as-ramfrom. It will be called before
319 physical memory is ready.
320 If InitFlag is DEBUG_AGENT_INIT_POSTMEM_SEC, debug agent will build one GUIDed
321 HOB to copy debug agent Mailbox. It will be called after physical memory is ready.
322
323 This function is used to set up debug environment to support source level debugging.
324 If certain Debug Agent Library instance has to save some private data in the stack,
325 this function must work on the mode that doesn't return to the caller, then
326 the caller needs to wrap up all rest of logic after InitializeDebugAgent() into one
327 function and pass it into InitializeDebugAgent(). InitializeDebugAgent() is
328 responsible to invoke the passing-in function at the end of InitializeDebugAgent().
329
330 If the parameter Function is not NULL, Debug Agent Libary instance will invoke it by
331 passing in the Context to be its parameter.
332
333 If Function() is NULL, Debug Agent Library instance will return after setup debug
334 environment.
335
336 @param[in] InitFlag Init flag is used to decide the initialize process.
337 @param[in] Context Context needed according to InitFlag; it was optional.
338 @param[in] Function Continue function called by debug agent library; it was
339 optional.
340
341 **/
342 VOID
343 EFIAPI
344 InitializeDebugAgent (
345 IN UINT32 InitFlag,
346 IN VOID *Context, OPTIONAL
347 IN DEBUG_AGENT_CONTINUE Function OPTIONAL
348 )
349 {
350 DEBUG_AGENT_MAILBOX *Mailbox;
351 DEBUG_AGENT_MAILBOX MailboxInStack;
352 DEBUG_AGENT_PHASE2_CONTEXT Phase2Context;
353 DEBUG_AGENT_CONTEXT_POSTMEM_SEC *DebugAgentContext;
354 EFI_STATUS Status;
355 IA32_DESCRIPTOR *Ia32Idtr;
356 IA32_IDT_ENTRY *Ia32IdtEntry;
357 UINT64 DebugPortHandle;
358 UINT64 MailboxLocation;
359 UINT64 *MailboxLocationPointer;
360
361 DisableInterrupts ();
362
363 switch (InitFlag) {
364
365 case DEBUG_AGENT_INIT_PREMEM_SEC:
366
367 InitializeDebugIdt ();
368
369 MailboxLocation = (UINT64)(UINTN)&MailboxInStack;
370 Mailbox = &MailboxInStack;
371 ZeroMem ((VOID *) Mailbox, sizeof (DEBUG_AGENT_MAILBOX));
372 //
373 // Get and save debug port handle and set the length of memory block.
374 //
375 SetLocationSavedMailboxPointerInIdtEntry (&MailboxLocation);
376 //
377 // Force error message could be printed during the first shakehand between Target/HOST.
378 //
379 SetDebugFlag (DEBUG_AGENT_FLAG_PRINT_ERROR_LEVEL, DEBUG_AGENT_ERROR);
380 //
381 // Save init arch type when debug agent initialized
382 //
383 SetDebugFlag (DEBUG_AGENT_FLAG_INIT_ARCH, DEBUG_ARCH_SYMBOL);
384
385 InitializeDebugTimer ();
386
387 Phase2Context.Context = Context;
388 Phase2Context.Function = Function;
389 DebugPortInitialize ((VOID *) &Phase2Context, InitializeDebugAgentPhase2);
390 //
391 // If reaches here, it means Debug Port initialization failed.
392 //
393 DEBUG ((EFI_D_ERROR, "Debug Agent: Debug port initialization failed.\n"));
394
395 break;
396
397 case DEBUG_AGENT_INIT_POSTMEM_SEC:
398 Mailbox = GetMailboxPointer ();
399 //
400 // Memory has been ready
401 //
402 SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1);
403 if (IsHostAttached ()) {
404 //
405 // Trigger one software interrupt to inform HOST
406 //
407 TriggerSoftInterrupt (MEMORY_READY_SIGNATURE);
408 }
409
410 //
411 // Fix up Debug Port handle address and mailbox address
412 //
413 DebugAgentContext = (DEBUG_AGENT_CONTEXT_POSTMEM_SEC *) Context;
414 DebugPortHandle = (UINT64)(UINT32)(Mailbox->DebugPortHandle + DebugAgentContext->StackMigrateOffset);
415 UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle);
416 Mailbox = (DEBUG_AGENT_MAILBOX *) ((UINTN) Mailbox + DebugAgentContext->StackMigrateOffset);
417 MailboxLocation = (UINT64)(UINTN)Mailbox;
418 //
419 // Build mailbox location in HOB and fix-up its address
420 //
421 MailboxLocationPointer = BuildGuidDataHob (
422 &gEfiDebugAgentGuid,
423 &MailboxLocation,
424 sizeof (UINT64)
425 );
426 MailboxLocationPointer = (UINT64 *) ((UINTN) MailboxLocationPointer + DebugAgentContext->HeapMigrateOffset);
427 //
428 // Update IDT entry to save the location saved mailbox pointer
429 //
430 SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationPointer);
431 //
432 // Enable CPU interrupts so debug timer interrupts can be delivered
433 //
434 EnableInterrupts ();
435
436 break;
437
438 case DEBUG_AGENT_INIT_PEI:
439 //
440 // Check if Debug Agent has initialized before
441 //
442 if (IsDebugAgentInitialzed()) {
443 DEBUG ((EFI_D_WARN, "Debug Agent: It has already initialized in SEC Core!\n"));
444 break;
445 }
446 //
447 // Set up IDT entries
448 //
449 InitializeDebugIdt ();
450 //
451 // Build mailbox in HOB and setup Mailbox Set In Pei flag
452 //
453 Mailbox = AllocateZeroPool (sizeof (DEBUG_AGENT_MAILBOX));
454 MailboxLocation = (UINT64)(UINTN)Mailbox;
455 MailboxLocationPointer = BuildGuidDataHob (
456 &gEfiDebugAgentGuid,
457 &MailboxLocation,
458 sizeof (UINT64)
459 );
460
461 InitializeDebugTimer ();
462 //
463 // Update IDT entry to save the location pointer saved mailbox pointer
464 //
465 SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationPointer);
466 //
467 // Save init arch type when debug agent initialized
468 //
469 SetDebugFlag (DEBUG_AGENT_FLAG_INIT_ARCH, DEBUG_ARCH_SYMBOL);
470 //
471 // Register for a callback once memory has been initialized.
472 // If memery has been ready, the callback funtion will be invoked immediately
473 //
474 Status = PeiServicesNotifyPpi (&mMemoryDiscoveredNotifyList[0]);
475 ASSERT_EFI_ERROR (Status);
476 //
477 // Set HOB check flag if memory has not been ready yet
478 //
479 if (GetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY) == 0) {
480 SetDebugFlag (DEBUG_AGENT_FLAG_CHECK_MAILBOX_IN_HOB, 1);
481 }
482
483 Phase2Context.Context = Context;
484 Phase2Context.Function = Function;
485 DebugPortInitialize ((VOID *) &Phase2Context, InitializeDebugAgentPhase2);
486
487 FindAndReportModuleImageInfo (4);
488
489 break;
490
491 case DEBUG_AGENT_INIT_THUNK_PEI_IA32TOX64:
492 if (Context == NULL) {
493 DEBUG ((EFI_D_ERROR, "DebugAgent: Input parameter Context cannot be NULL!\n"));
494 CpuDeadLoop ();
495 } else {
496 Ia32Idtr = (IA32_DESCRIPTOR *) Context;
497 Ia32IdtEntry = (IA32_IDT_ENTRY *)(Ia32Idtr->Base);
498 MailboxLocationPointer = (UINT64 *) (UINTN) (Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetLow +
499 (Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetHigh << 16));
500 Mailbox = (DEBUG_AGENT_MAILBOX *) (UINTN)(*MailboxLocationPointer);
501 //
502 // Mailbox should valid and setup before executing thunk code
503 //
504 VerifyMailboxChecksum (Mailbox);
505
506 DebugPortHandle = (UINT64) (UINTN)DebugPortInitialize ((VOID *)(UINTN)Mailbox->DebugPortHandle, NULL);
507 UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle);
508 //
509 // Set up IDT entries
510 //
511 InitializeDebugIdt ();
512 //
513 // Update IDT entry to save location pointer saved the mailbox pointer
514 //
515 SetLocationSavedMailboxPointerInIdtEntry (MailboxLocationPointer);
516
517 FindAndReportModuleImageInfo (4);
518 }
519 break;
520
521 default:
522 //
523 // Only DEBUG_AGENT_INIT_PREMEM_SEC and DEBUG_AGENT_INIT_POSTMEM_SEC are allowed for this
524 // Debug Agent library instance.
525 //
526 DEBUG ((EFI_D_ERROR, "Debug Agent: The InitFlag value is not allowed!\n"));
527 CpuDeadLoop ();
528 break;
529 }
530
531 EnableInterrupts ();
532
533 //
534 // If Function is not NULL, invoke it always whatever debug agent was initialized sucesssfully or not.
535 //
536 if (Function != NULL) {
537 Function (Context);
538 }
539 }
540
541 /**
542 Caller provided function to be invoked at the end of DebugPortInitialize().
543
544 Refer to the descrption for DebugPortInitialize() for more details.
545
546 @param[in] Context The first input argument of DebugPortInitialize().
547 @param[in] DebugPortHandle Debug port handle created by Debug Communication Libary.
548
549 **/
550 VOID
551 EFIAPI
552 InitializeDebugAgentPhase2 (
553 IN VOID *Context,
554 IN DEBUG_PORT_HANDLE DebugPortHandle
555 )
556 {
557 DEBUG_AGENT_PHASE2_CONTEXT *Phase2Context;
558 UINT64 *MailboxLocation;
559 DEBUG_AGENT_MAILBOX *Mailbox;
560 EFI_SEC_PEI_HAND_OFF *SecCoreData;
561 UINT16 BufferSize;
562 UINT64 NewDebugPortHandle;
563
564 Phase2Context = (DEBUG_AGENT_PHASE2_CONTEXT *) Context;
565 MailboxLocation = GetLocationSavedMailboxPointerInIdtEntry ();
566 Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation);
567 BufferSize = PcdGet16(PcdDebugPortHandleBufferSize);
568 if (Phase2Context->Function == NULL && DebugPortHandle != NULL && BufferSize != 0) {
569 NewDebugPortHandle = (UINT64)(UINTN)AllocateCopyPool (BufferSize, DebugPortHandle);
570 } else {
571 NewDebugPortHandle = (UINT64)(UINTN)DebugPortHandle;
572 }
573 UpdateMailboxContent (Mailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, NewDebugPortHandle);
574
575 //
576 // Trigger one software interrupt to inform HOST
577 //
578 TriggerSoftInterrupt (SYSTEM_RESET_SIGNATURE);
579
580 //
581 // If Temporary RAM region is below 128 MB, then send message to
582 // host to disable low memory filtering.
583 //
584 SecCoreData = (EFI_SEC_PEI_HAND_OFF *)Phase2Context->Context;
585 if ((UINTN)SecCoreData->TemporaryRamBase < BASE_128MB && IsHostAttached ()) {
586 SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1);
587 TriggerSoftInterrupt (MEMORY_READY_SIGNATURE);
588 }
589
590 //
591 // Enable CPU interrupts so debug timer interrupts can be delivered
592 //
593 EnableInterrupts ();
594
595 //
596 // Call continuation function if it is not NULL.
597 //
598 if (Phase2Context->Function != NULL) {
599 Phase2Context->Function (Phase2Context->Context);
600 }
601 }