]> git.proxmox.com Git - mirror_edk2.git/blob - SourceLevelDebugPkg/Library/DebugAgent/DxeDebugAgent/DxeDebugAgentLib.c
a41bba2c4ba93b6e218078df70571c601eb71921
[mirror_edk2.git] / SourceLevelDebugPkg / Library / DebugAgent / DxeDebugAgent / DxeDebugAgentLib.c
1 /** @file
2 Debug Agent library implementation for Dxe Core and Dxr modules.
3
4 Copyright (c) 2010 - 2018, Intel Corporation. All rights reserved.<BR>
5 SPDX-License-Identifier: BSD-2-Clause-Patent
6
7 **/
8
9 #include "DxeDebugAgentLib.h"
10
11 DEBUG_AGENT_MAILBOX mMailbox;
12 DEBUG_AGENT_MAILBOX *mMailboxPointer = NULL;
13 IA32_IDT_GATE_DESCRIPTOR mIdtEntryTable[33];
14 BOOLEAN mDxeCoreFlag = FALSE;
15 BOOLEAN mMultiProcessorDebugSupport = FALSE;
16 VOID *mSavedIdtTable = NULL;
17 UINTN mSaveIdtTableSize = 0;
18 BOOLEAN mDebugAgentInitialized = FALSE;
19 BOOLEAN mSkipBreakpoint = FALSE;
20
21 /**
22 Check if debug agent support multi-processor.
23
24 @retval TRUE Multi-processor is supported.
25 @retval FALSE Multi-processor is not supported.
26
27 **/
28 BOOLEAN
29 MultiProcessorDebugSupport (
30 VOID
31 )
32 {
33 return mMultiProcessorDebugSupport;
34 }
35
36 /**
37 Internal constructor worker function.
38
39 It will register one callback function on EFI PCD Protocol.
40 It will allocate the NVS memory to store Mailbox and install configuration table
41 in system table to store its pointer.
42
43 **/
44 VOID
45 InternalConstructorWorker (
46 VOID
47 )
48 {
49 EFI_STATUS Status;
50 EFI_PHYSICAL_ADDRESS Address;
51 BOOLEAN DebugTimerInterruptState;
52 DEBUG_AGENT_MAILBOX *Mailbox;
53 DEBUG_AGENT_MAILBOX *NewMailbox;
54 EFI_HOB_GUID_TYPE *GuidHob;
55 EFI_VECTOR_HANDOFF_INFO *VectorHandoffInfo;
56
57 //
58 // Check persisted vector handoff info
59 //
60 Status = EFI_SUCCESS;
61 GuidHob = GetFirstGuidHob (&gEfiVectorHandoffInfoPpiGuid);
62 if ((GuidHob != NULL) && !mDxeCoreFlag) {
63 //
64 // Check if configuration table is installed or not if GUIDed HOB existed,
65 // only when Debug Agent is not linked by DXE Core
66 //
67 Status = EfiGetSystemConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID **)&VectorHandoffInfo);
68 }
69
70 if ((GuidHob == NULL) || (Status != EFI_SUCCESS)) {
71 //
72 // Install configuration table for persisted vector handoff info if GUIDed HOB cannot be found or
73 // configuration table does not exist
74 //
75 Status = gBS->InstallConfigurationTable (&gEfiVectorHandoffTableGuid, (VOID *)&mVectorHandoffInfoDebugAgent[0]);
76 if (EFI_ERROR (Status)) {
77 DEBUG ((DEBUG_ERROR, "DebugAgent: Cannot install configuration table for persisted vector handoff info!\n"));
78 CpuDeadLoop ();
79 }
80 }
81
82 //
83 // Install EFI Serial IO protocol on debug port
84 //
85 InstallSerialIo ();
86
87 Address = 0;
88 Status = gBS->AllocatePages (
89 AllocateAnyPages,
90 EfiACPIMemoryNVS,
91 EFI_SIZE_TO_PAGES (sizeof (DEBUG_AGENT_MAILBOX) + PcdGet16 (PcdDebugPortHandleBufferSize)),
92 &Address
93 );
94 if (EFI_ERROR (Status)) {
95 DEBUG ((DEBUG_ERROR, "DebugAgent: Cannot install configuration table for mailbox!\n"));
96 CpuDeadLoop ();
97 }
98
99 DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (FALSE);
100
101 NewMailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)Address;
102 //
103 // Copy Mailbox and Debug Port Handle buffer to new location in ACPI NVS memory, because original Mailbox
104 // and Debug Port Handle buffer may be free at runtime, SMM debug agent needs to access them
105 //
106 Mailbox = GetMailboxPointer ();
107 CopyMem (NewMailbox, Mailbox, sizeof (DEBUG_AGENT_MAILBOX));
108 CopyMem (NewMailbox + 1, (VOID *)(UINTN)Mailbox->DebugPortHandle, PcdGet16 (PcdDebugPortHandleBufferSize));
109 //
110 // Update Debug Port Handle in new Mailbox
111 //
112 UpdateMailboxContent (NewMailbox, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, (UINT64)(UINTN)(NewMailbox + 1));
113 mMailboxPointer = NewMailbox;
114
115 DebugTimerInterruptState = SaveAndSetDebugTimerInterrupt (DebugTimerInterruptState);
116
117 Status = gBS->InstallConfigurationTable (&gEfiDebugAgentGuid, (VOID *)mMailboxPointer);
118 if (EFI_ERROR (Status)) {
119 DEBUG ((DEBUG_ERROR, "DebugAgent: Failed to install configuration for mailbox!\n"));
120 CpuDeadLoop ();
121 }
122 }
123
124 /**
125 Debug Agent constructor function.
126
127 @param[in] ImageHandle The firmware allocated handle for the EFI image.
128 @param[in] SystemTable A pointer to the EFI System Table.
129
130 @retval RETURN_SUCCESS When this function completed.
131
132 **/
133 RETURN_STATUS
134 EFIAPI
135 DxeDebugAgentLibConstructor (
136 IN EFI_HANDLE ImageHandle,
137 IN EFI_SYSTEM_TABLE *SystemTable
138 )
139 {
140 if (mDxeCoreFlag) {
141 //
142 // Invoke internal constructor function only when DXE core links this library instance
143 //
144 InternalConstructorWorker ();
145 }
146
147 return RETURN_SUCCESS;
148 }
149
150 /**
151 Get the pointer to Mailbox from the configuration table.
152
153 @return Pointer to Mailbox.
154
155 **/
156 DEBUG_AGENT_MAILBOX *
157 GetMailboxFromConfigurationTable (
158 VOID
159 )
160 {
161 EFI_STATUS Status;
162 DEBUG_AGENT_MAILBOX *Mailbox;
163
164 Status = EfiGetSystemConfigurationTable (&gEfiDebugAgentGuid, (VOID **)&Mailbox);
165 if ((Status == EFI_SUCCESS) && (Mailbox != NULL)) {
166 VerifyMailboxChecksum (Mailbox);
167 return Mailbox;
168 } else {
169 return NULL;
170 }
171 }
172
173 /**
174 Get the pointer to Mailbox from the GUIDed HOB.
175
176 @param[in] HobStart The starting HOB pointer to search from.
177
178 @return Pointer to Mailbox.
179
180 **/
181 DEBUG_AGENT_MAILBOX *
182 GetMailboxFromHob (
183 IN VOID *HobStart
184 )
185 {
186 EFI_HOB_GUID_TYPE *GuidHob;
187 UINT64 *MailboxLocation;
188 DEBUG_AGENT_MAILBOX *Mailbox;
189
190 GuidHob = GetNextGuidHob (&gEfiDebugAgentGuid, HobStart);
191 if (GuidHob == NULL) {
192 return NULL;
193 }
194
195 MailboxLocation = (UINT64 *)(GET_GUID_HOB_DATA (GuidHob));
196 Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation);
197 VerifyMailboxChecksum (Mailbox);
198
199 return Mailbox;
200 }
201
202 /**
203 Get Debug Agent Mailbox pointer.
204
205 @return Mailbox pointer.
206
207 **/
208 DEBUG_AGENT_MAILBOX *
209 GetMailboxPointer (
210 VOID
211 )
212 {
213 AcquireMpSpinLock (&mDebugMpContext.MailboxSpinLock);
214 VerifyMailboxChecksum (mMailboxPointer);
215 ReleaseMpSpinLock (&mDebugMpContext.MailboxSpinLock);
216 return mMailboxPointer;
217 }
218
219 /**
220 Get debug port handle.
221
222 @return Debug port handle.
223
224 **/
225 DEBUG_PORT_HANDLE
226 GetDebugPortHandle (
227 VOID
228 )
229 {
230 return (DEBUG_PORT_HANDLE)(UINTN)(GetMailboxPointer ()->DebugPortHandle);
231 }
232
233 /**
234 Worker function to set up Debug Agent environment.
235
236 This function will set up IDT table and initialize the IDT entries and
237 initialize CPU LOCAL APIC timer.
238 It also tries to connect HOST if Debug Agent was not initialized before.
239
240 @param[in] Mailbox Pointer to Mailbox.
241
242 **/
243 VOID
244 SetupDebugAgentEnvironment (
245 IN DEBUG_AGENT_MAILBOX *Mailbox
246 )
247 {
248 IA32_DESCRIPTOR Idtr;
249 UINT16 IdtEntryCount;
250 UINT64 DebugPortHandle;
251 UINT32 DebugTimerFrequency;
252
253 if (mMultiProcessorDebugSupport) {
254 InitializeSpinLock (&mDebugMpContext.MpContextSpinLock);
255 InitializeSpinLock (&mDebugMpContext.DebugPortSpinLock);
256 InitializeSpinLock (&mDebugMpContext.MailboxSpinLock);
257 //
258 // Clear Break CPU index value
259 //
260 mDebugMpContext.BreakAtCpuIndex = (UINT32)-1;
261 }
262
263 //
264 // Get original IDT address and size.
265 //
266 AsmReadIdtr ((IA32_DESCRIPTOR *)&Idtr);
267 IdtEntryCount = (UINT16)((Idtr.Limit + 1) / sizeof (IA32_IDT_GATE_DESCRIPTOR));
268 if (IdtEntryCount < 33) {
269 ZeroMem (&mIdtEntryTable, sizeof (IA32_IDT_GATE_DESCRIPTOR) * 33);
270 //
271 // Copy original IDT table into new one
272 //
273 CopyMem (&mIdtEntryTable, (VOID *)Idtr.Base, Idtr.Limit + 1);
274 //
275 // Load new IDT table
276 //
277 Idtr.Limit = (UINT16)(sizeof (IA32_IDT_GATE_DESCRIPTOR) * 33 - 1);
278 Idtr.Base = (UINTN)&mIdtEntryTable;
279 AsmWriteIdtr ((IA32_DESCRIPTOR *)&Idtr);
280 }
281
282 //
283 // Initialize the IDT table entries to support source level debug.
284 //
285 InitializeDebugIdt ();
286
287 //
288 // If mMailboxPointer is not set before, set it
289 //
290 if (mMailboxPointer == NULL) {
291 if (Mailbox != NULL) {
292 //
293 // If Mailbox exists, copy it into one global variable
294 //
295 CopyMem (&mMailbox, Mailbox, sizeof (DEBUG_AGENT_MAILBOX));
296 } else {
297 ZeroMem (&mMailbox, sizeof (DEBUG_AGENT_MAILBOX));
298 }
299
300 mMailboxPointer = &mMailbox;
301 }
302
303 //
304 // Initialize Debug Timer hardware and save its initial count and frequency
305 //
306 mDebugMpContext.DebugTimerInitCount = InitializeDebugTimer (&DebugTimerFrequency, TRUE);
307 UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_TIMER_FREQUENCY, DebugTimerFrequency);
308 //
309 // Initialize debug communication port
310 //
311 DebugPortHandle = (UINT64)(UINTN)DebugPortInitialize ((VOID *)(UINTN)mMailboxPointer->DebugPortHandle, NULL);
312 UpdateMailboxContent (mMailboxPointer, DEBUG_MAILBOX_DEBUG_PORT_HANDLE_INDEX, DebugPortHandle);
313
314 if (Mailbox == NULL) {
315 //
316 // Trigger one software interrupt to inform HOST
317 //
318 TriggerSoftInterrupt (SYSTEM_RESET_SIGNATURE);
319 SetDebugFlag (DEBUG_AGENT_FLAG_MEMORY_READY, 1);
320 //
321 // Memory has been ready
322 //
323 if (IsHostAttached ()) {
324 //
325 // Trigger one software interrupt to inform HOST
326 //
327 TriggerSoftInterrupt (MEMORY_READY_SIGNATURE);
328 }
329 }
330 }
331
332 /**
333 Initialize debug agent.
334
335 This function is used to set up debug environment for DXE phase.
336
337 If this function is called by DXE Core, Context must be the pointer
338 to HOB list which will be used to get GUIDed HOB. It will enable
339 interrupt to support break-in feature.
340 If this function is called by DXE module, Context must be NULL. It
341 will enable interrupt to support break-in feature.
342
343 @param[in] InitFlag Init flag is used to decide initialize process.
344 @param[in] Context Context needed according to InitFlag.
345 @param[in] Function Continue function called by debug agent library; it was
346 optional.
347
348 **/
349 VOID
350 EFIAPI
351 InitializeDebugAgent (
352 IN UINT32 InitFlag,
353 IN VOID *Context OPTIONAL,
354 IN DEBUG_AGENT_CONTINUE Function OPTIONAL
355 )
356 {
357 UINT64 *MailboxLocation;
358 DEBUG_AGENT_MAILBOX *Mailbox;
359 BOOLEAN InterruptStatus;
360 VOID *HobList;
361 IA32_DESCRIPTOR IdtDescriptor;
362 IA32_DESCRIPTOR *Ia32Idtr;
363 IA32_IDT_ENTRY *Ia32IdtEntry;
364 BOOLEAN PeriodicMode;
365 UINTN TimerCycle;
366
367 if (InitFlag == DEBUG_AGENT_INIT_DXE_AP) {
368 //
369 // Check if CPU APIC Timer is working, otherwise initialize it.
370 //
371 InitializeLocalApicSoftwareEnable (TRUE);
372 GetApicTimerState (NULL, &PeriodicMode, NULL);
373 TimerCycle = GetApicTimerInitCount ();
374 if (!PeriodicMode || (TimerCycle == 0)) {
375 InitializeDebugTimer (NULL, FALSE);
376 }
377
378 //
379 // Invoked by AP, enable interrupt to let AP could receive IPI from other processors
380 //
381 EnableInterrupts ();
382 return;
383 }
384
385 //
386 // Disable Debug Timer interrupt
387 //
388 SaveAndSetDebugTimerInterrupt (FALSE);
389 //
390 // Save and disable original interrupt status
391 //
392 InterruptStatus = SaveAndDisableInterrupts ();
393
394 //
395 // Try to get mailbox firstly
396 //
397 HobList = NULL;
398 Mailbox = NULL;
399 MailboxLocation = NULL;
400
401 switch (InitFlag) {
402 case DEBUG_AGENT_INIT_DXE_LOAD:
403 //
404 // Check if Debug Agent has been initialized before
405 //
406 if (IsDebugAgentInitialzed ()) {
407 DEBUG ((DEBUG_INFO, "Debug Agent: The former agent will be overwritten by the new one!\n"));
408 }
409
410 mMultiProcessorDebugSupport = TRUE;
411 //
412 // Save original IDT table
413 //
414 AsmReadIdtr (&IdtDescriptor);
415 mSaveIdtTableSize = IdtDescriptor.Limit + 1;
416 mSavedIdtTable = AllocateCopyPool (mSaveIdtTableSize, (VOID *)IdtDescriptor.Base);
417 //
418 // Check if Debug Agent initialized in DXE phase
419 //
420 Mailbox = GetMailboxFromConfigurationTable ();
421 if (Mailbox == NULL) {
422 //
423 // Try to get mailbox from GUIDed HOB build in PEI
424 //
425 HobList = GetHobList ();
426 Mailbox = GetMailboxFromHob (HobList);
427 }
428
429 //
430 // Set up Debug Agent Environment and try to connect HOST if required
431 //
432 SetupDebugAgentEnvironment (Mailbox);
433 //
434 // For DEBUG_AGENT_INIT_S3, needn't to install configuration table and EFI Serial IO protocol
435 // For DEBUG_AGENT_INIT_DXE_CORE, InternalConstructorWorker() will invoked in Constructor()
436 //
437 InternalConstructorWorker ();
438 //
439 // Enable Debug Timer interrupt
440 //
441 SaveAndSetDebugTimerInterrupt (TRUE);
442 //
443 // Enable interrupt to receive Debug Timer interrupt
444 //
445 EnableInterrupts ();
446
447 mDebugAgentInitialized = TRUE;
448 FindAndReportModuleImageInfo (SIZE_4KB);
449
450 *(EFI_STATUS *)Context = EFI_SUCCESS;
451
452 break;
453
454 case DEBUG_AGENT_INIT_DXE_UNLOAD:
455 if (mDebugAgentInitialized) {
456 if (IsHostAttached ()) {
457 *(EFI_STATUS *)Context = EFI_ACCESS_DENIED;
458 //
459 // Enable Debug Timer interrupt again
460 //
461 SaveAndSetDebugTimerInterrupt (TRUE);
462 } else {
463 //
464 // Restore original IDT table
465 //
466 AsmReadIdtr (&IdtDescriptor);
467 IdtDescriptor.Limit = (UINT16)(mSaveIdtTableSize - 1);
468 CopyMem ((VOID *)IdtDescriptor.Base, mSavedIdtTable, mSaveIdtTableSize);
469 AsmWriteIdtr (&IdtDescriptor);
470 FreePool (mSavedIdtTable);
471 mDebugAgentInitialized = FALSE;
472 *(EFI_STATUS *)Context = EFI_SUCCESS;
473 }
474 } else {
475 *(EFI_STATUS *)Context = EFI_NOT_STARTED;
476 }
477
478 //
479 // Restore interrupt state.
480 //
481 SetInterruptState (InterruptStatus);
482 break;
483
484 case DEBUG_AGENT_INIT_DXE_CORE:
485 mDxeCoreFlag = TRUE;
486 mMultiProcessorDebugSupport = TRUE;
487 //
488 // Try to get mailbox from GUIDed HOB build in PEI
489 //
490 HobList = Context;
491 Mailbox = GetMailboxFromHob (HobList);
492 //
493 // Set up Debug Agent Environment and try to connect HOST if required
494 //
495 SetupDebugAgentEnvironment (Mailbox);
496 //
497 // Enable Debug Timer interrupt
498 //
499 SaveAndSetDebugTimerInterrupt (TRUE);
500 //
501 // Enable interrupt to receive Debug Timer interrupt
502 //
503 EnableInterrupts ();
504
505 break;
506
507 case DEBUG_AGENT_INIT_S3:
508
509 if (Context != NULL) {
510 Ia32Idtr = (IA32_DESCRIPTOR *)Context;
511 Ia32IdtEntry = (IA32_IDT_ENTRY *)(Ia32Idtr->Base);
512 MailboxLocation = (UINT64 *)((UINTN)Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetLow +
513 ((UINTN)Ia32IdtEntry[DEBUG_MAILBOX_VECTOR].Bits.OffsetHigh << 16));
514 Mailbox = (DEBUG_AGENT_MAILBOX *)(UINTN)(*MailboxLocation);
515 VerifyMailboxChecksum (Mailbox);
516 }
517
518 //
519 // Save Mailbox pointer in global variable
520 //
521 mMailboxPointer = Mailbox;
522 //
523 // Set up Debug Agent Environment and try to connect HOST if required
524 //
525 SetupDebugAgentEnvironment (Mailbox);
526 //
527 // Disable interrupt
528 //
529 DisableInterrupts ();
530 FindAndReportModuleImageInfo (SIZE_4KB);
531 if (GetDebugFlag (DEBUG_AGENT_FLAG_BREAK_BOOT_SCRIPT) == 1) {
532 //
533 // If Boot Script entry break is set, code will be break at here.
534 //
535 CpuBreakpoint ();
536 }
537
538 break;
539
540 default:
541 //
542 // Only DEBUG_AGENT_INIT_PREMEM_SEC and DEBUG_AGENT_INIT_POSTMEM_SEC are allowed for this
543 // Debug Agent library instance.
544 //
545 DEBUG ((DEBUG_ERROR, "Debug Agent: The InitFlag value is not allowed!\n"));
546 CpuDeadLoop ();
547 break;
548 }
549 }