Clean up using FIQ as ctrl-c checker so you can break in from the debugger.
[mirror_edk2.git] / EmbeddedPkg / Library / GdbDebugAgent / Arm / Processor.c
1 /** @file
2 Processor specific parts of the GDB stub
3
4 Copyright (c) 2008-2010, Apple Inc. All rights reserved.
5
6 All rights reserved. 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
16
17 #include <GdbDebugAgent.h>
18 #include <Library/CacheMaintenanceLib.h>
19 #include <Library/PrintLib.h>
20 #include <Library/ArmLib.h>
21
22 //
23 // Externs from the exception handler assembly file
24 //
25 VOID
26 ExceptionHandlersStart (
27 VOID
28 );
29
30 VOID
31 ExceptionHandlersEnd (
32 VOID
33 );
34
35 VOID
36 CommonExceptionEntry (
37 VOID
38 );
39
40 VOID
41 AsmCommonExceptionEntry (
42 VOID
43 );
44
45
46 //
47 // Array of exception types that need to be hooked by the debugger
48 // (efi, gdb) //efi number
49 //
50 EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = {
51 { EXCEPT_ARM_SOFTWARE_INTERRUPT, GDB_SIGTRAP },
52 { EXCEPT_ARM_UNDEFINED_INSTRUCTION, GDB_SIGTRAP },
53 { EXCEPT_ARM_PREFETCH_ABORT, GDB_SIGTRAP },
54 { EXCEPT_ARM_DATA_ABORT, GDB_SIGTRAP }, // GDB_SIGEMT
55 { EXCEPT_ARM_RESERVED, GDB_SIGTRAP }, // GDB_SIGILL
56 { EXCEPT_ARM_FIQ, GDB_SIGINT } // Used for ctrl-c
57 };
58
59 // Shut up some annoying RVCT warnings
60 #ifdef __CC_ARM
61 #pragma diag_suppress 1296
62 #endif
63
64 UINTN gRegisterOffsets[] = {
65 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R0),
66 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R1),
67 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R2),
68 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R3),
69 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R4),
70 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R5),
71 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R6),
72 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R7),
73 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R8),
74 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R9),
75 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R10),
76 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R11),
77 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R12),
78 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, SP),
79 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, LR),
80 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, PC),
81 0x00000F01, // f0
82 0x00000F02,
83 0x00000F03,
84 0x00000F11, // f1
85 0x00000F12,
86 0x00000F13,
87 0x00000F21, // f2
88 0x00000F22,
89 0x00000F23,
90 0x00000F31, // f3
91 0x00000F32,
92 0x00000F33,
93 0x00000F41, // f4
94 0x00000F42,
95 0x00000F43,
96 0x00000F51, // f5
97 0x00000F52,
98 0x00000F53,
99 0x00000F61, // f6
100 0x00000F62,
101 0x00000F63,
102 0x00000F71, // f7
103 0x00000F72,
104 0x00000F73,
105 0x00000FFF, // fps
106 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, CPSR)
107 };
108
109 // restore warnings for RVCT
110 #ifdef __CC_ARM
111 #pragma diag_default 1296
112 #endif
113
114
115 /**
116 Return the number of entries in the gExceptionType[]
117
118 @retval UINTN, the number of entries in the gExceptionType[] array.
119 **/
120 UINTN
121 MaxEfiException (
122 VOID
123 )
124 {
125 return sizeof (gExceptionType)/sizeof (EFI_EXCEPTION_TYPE_ENTRY);
126 }
127
128
129
130
131 /**
132 Check to see if the ISA is supported.
133 ISA = Instruction Set Architecture
134
135 @retval TRUE if Isa is supported
136
137 **/
138 BOOLEAN
139 CheckIsa (
140 IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
141 )
142 {
143 if (Isa == IsaArm) {
144 return TRUE;
145 } else {
146 return FALSE;
147 }
148 }
149
150
151 /**
152 This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering
153 It is, by default, set to find the register pointer of the ARM member
154 @param SystemContext Register content at time of the exception
155 @param RegNumber The register to which we want to find a pointer
156 @retval the pointer to the RegNumber-th pointer
157 **/
158 UINTN *
159 FindPointerToRegister(
160 IN EFI_SYSTEM_CONTEXT SystemContext,
161 IN UINTN RegNumber
162 )
163 {
164 UINT8 *TempPtr;
165 ASSERT(gRegisterOffsets[RegNumber] < 0xF00);
166 TempPtr = ((UINT8 *)SystemContext.SystemContextArm) + gRegisterOffsets[RegNumber];
167 return (UINT32 *)TempPtr;
168 }
169
170
171 /**
172 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
173 @param SystemContext Register content at time of the exception
174 @param RegNumber the number of the register that we want to read
175 @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on.
176 @retval the pointer to the next character of the output buffer that is available to be written on.
177 **/
178 CHAR8 *
179 BasicReadRegister (
180 IN EFI_SYSTEM_CONTEXT SystemContext,
181 IN UINTN RegNumber,
182 IN CHAR8 *OutBufPtr
183 )
184 {
185 UINTN RegSize;
186 CHAR8 Char;
187
188 if (gRegisterOffsets[RegNumber] > 0xF00) {
189 AsciiSPrint(OutBufPtr, 9, "00000000");
190 OutBufPtr += 8;
191 return OutBufPtr;
192 }
193
194 RegSize = 0;
195 while (RegSize < 32) {
196 Char = mHexToStr[(UINT8)((*FindPointerToRegister(SystemContext, RegNumber) >> (RegSize+4)) & 0xf)];
197 if ((Char >= 'A') && (Char <= 'F')) {
198 Char = Char - 'A' + 'a';
199 }
200 *OutBufPtr++ = Char;
201
202 Char = mHexToStr[(UINT8)((*FindPointerToRegister(SystemContext, RegNumber) >> RegSize) & 0xf)];
203 if ((Char >= 'A') && (Char <= 'F')) {
204 Char = Char - 'A' + 'a';
205 }
206 *OutBufPtr++ = Char;
207
208 RegSize = RegSize + 8;
209 }
210 return OutBufPtr;
211 }
212
213
214 /** ‘p n’
215 Reads the n-th register's value into an output buffer and sends it as a packet
216 @param SystemContext Register content at time of the exception
217 @param InBuffer Pointer to the input buffer received from gdb server
218 **/
219 VOID
220 ReadNthRegister (
221 IN EFI_SYSTEM_CONTEXT SystemContext,
222 IN CHAR8 *InBuffer
223 )
224 {
225 UINTN RegNumber;
226 CHAR8 OutBuffer[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq)
227 CHAR8 *OutBufPtr; // pointer to the output buffer
228
229 RegNumber = AsciiStrHexToUintn (&InBuffer[1]);
230
231 if (RegNumber >= (sizeof (gRegisterOffsets)/sizeof (UINTN))) {
232 SendError (GDB_EINVALIDREGNUM);
233 return;
234 }
235
236 OutBufPtr = OutBuffer;
237 OutBufPtr = BasicReadRegister (SystemContext, RegNumber, OutBufPtr);
238
239 *OutBufPtr = '\0'; // the end of the buffer
240 SendPacket(OutBuffer);
241 }
242
243
244 /** ‘g’
245 Reads the general registers into an output buffer and sends it as a packet
246 @param SystemContext Register content at time of the exception
247 **/
248 VOID
249 EFIAPI
250 ReadGeneralRegisters (
251 IN EFI_SYSTEM_CONTEXT SystemContext
252 )
253 {
254 UINTN Index;
255 // a UINT32 takes 8 ascii characters
256 CHAR8 OutBuffer[(sizeof (gRegisterOffsets) * 2) + 1];
257 CHAR8 *OutBufPtr;
258
259 // It is not safe to allocate pool here....
260 OutBufPtr = OutBuffer;
261 for (Index = 0; Index < (sizeof (gRegisterOffsets)/sizeof (UINTN)); Index++) {
262 OutBufPtr = BasicReadRegister (SystemContext, Index, OutBufPtr);
263 }
264
265 *OutBufPtr = '\0';
266 SendPacket(OutBuffer);
267 }
268
269
270 /**
271 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
272 @param SystemContext Register content at time of the exception
273 @param RegNumber the number of the register that we want to write
274 @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on.
275 @retval the pointer to the next character of the input buffer that can be used
276 **/
277 CHAR8 *
278 BasicWriteRegister (
279 IN EFI_SYSTEM_CONTEXT SystemContext,
280 IN UINTN RegNumber,
281 IN CHAR8 *InBufPtr
282 )
283 {
284 UINTN RegSize;
285 UINTN TempValue; // the value transferred from a hex char
286 UINT32 NewValue; // the new value of the RegNumber-th Register
287
288 if (gRegisterOffsets[RegNumber] > 0xF00) {
289 return InBufPtr + 8;
290 }
291
292 NewValue = 0;
293 RegSize = 0;
294 while (RegSize < 32) {
295 TempValue = HexCharToInt(*InBufPtr++);
296
297 if ((INTN)TempValue < 0) {
298 SendError (GDB_EBADMEMDATA);
299 return NULL;
300 }
301
302 NewValue += (TempValue << (RegSize+4));
303 TempValue = HexCharToInt(*InBufPtr++);
304
305 if ((INTN)TempValue < 0) {
306 SendError (GDB_EBADMEMDATA);
307 return NULL;
308 }
309
310 NewValue += (TempValue << RegSize);
311 RegSize = RegSize + 8;
312 }
313 *(FindPointerToRegister(SystemContext, RegNumber)) = NewValue;
314 return InBufPtr;
315 }
316
317
318 /** ‘P n...=r...’
319 Writes the new value of n-th register received into the input buffer to the n-th register
320 @param SystemContext Register content at time of the exception
321 @param InBuffer Ponter to the input buffer received from gdb server
322 **/
323 VOID
324 WriteNthRegister (
325 IN EFI_SYSTEM_CONTEXT SystemContext,
326 IN CHAR8 *InBuffer
327 )
328 {
329 UINTN RegNumber;
330 CHAR8 RegNumBuffer[MAX_REG_NUM_BUF_SIZE]; // put the 'n..' part of the message into this array
331 CHAR8 *RegNumBufPtr;
332 CHAR8 *InBufPtr; // pointer to the input buffer
333
334 // find the register number to write
335 InBufPtr = &InBuffer[1];
336 RegNumBufPtr = RegNumBuffer;
337 while (*InBufPtr != '=') {
338 *RegNumBufPtr++ = *InBufPtr++;
339 }
340 *RegNumBufPtr = '\0';
341 RegNumber = AsciiStrHexToUintn (RegNumBuffer);
342
343 // check if this is a valid Register Number
344 if (RegNumber >= (sizeof (gRegisterOffsets)/sizeof (UINTN))) {
345 SendError (GDB_EINVALIDREGNUM);
346 return;
347 }
348 InBufPtr++; // skips the '=' character
349 BasicWriteRegister (SystemContext, RegNumber, InBufPtr);
350 SendSuccess();
351 }
352
353
354 /** ‘G XX...’
355 Writes the new values received into the input buffer to the general registers
356 @param SystemContext Register content at time of the exception
357 @param InBuffer Pointer to the input buffer received from gdb server
358 **/
359
360 VOID
361 EFIAPI
362 WriteGeneralRegisters (
363 IN EFI_SYSTEM_CONTEXT SystemContext,
364 IN CHAR8 *InBuffer
365 )
366 {
367 UINTN i;
368 CHAR8 *InBufPtr; /// pointer to the input buffer
369 UINTN MinLength;
370 UINTN RegisterCount = (sizeof (gRegisterOffsets)/sizeof (UINTN));
371
372 MinLength = (RegisterCount * 8) + 1; // 'G' plus the registers in ASCII format
373
374 if (AsciiStrLen(InBuffer) < MinLength) {
375 //Bad message. Message is not the right length
376 SendError (GDB_EBADBUFSIZE);
377 return;
378 }
379
380 InBufPtr = &InBuffer[1];
381
382 // Read the new values for the registers from the input buffer to an array, NewValueArray.
383 // The values in the array are in the gdb ordering
384 for(i = 0; i < RegisterCount; i++) {
385 InBufPtr = BasicWriteRegister (SystemContext, i, InBufPtr);
386 }
387
388 SendSuccess ();
389 }
390
391
392
393
394 /** ‘c [addr ]’
395 Continue. addr is Address to resume. If addr is omitted, resume at current
396 Address.
397
398 @param SystemContext Register content at time of the exception
399 **/
400 VOID
401 EFIAPI
402 ContinueAtAddress (
403 IN EFI_SYSTEM_CONTEXT SystemContext,
404 IN CHAR8 *PacketData
405 )
406 {
407 if (PacketData[1] != '\0') {
408 SystemContext.SystemContextArm->PC = AsciiStrHexToUintn(&PacketData[1]);
409 }
410 }
411
412
413 /** ‘s [addr ]’
414 Single step. addr is the Address at which to resume. If addr is omitted, resume
415 at same Address.
416
417 @param SystemContext Register content at time of the exception
418 **/
419 VOID
420 EFIAPI
421 SingleStep (
422 IN EFI_SYSTEM_CONTEXT SystemContext,
423 IN CHAR8 *PacketData
424 )
425 {
426 SendNotSupported();
427 }
428
429
430 VOID
431 EFIAPI
432 InsertBreakPoint (
433 IN EFI_SYSTEM_CONTEXT SystemContext,
434 IN CHAR8 *PacketData
435 )
436 {
437 SendNotSupported ();
438 }
439
440 VOID
441 EFIAPI
442 RemoveBreakPoint (
443 IN EFI_SYSTEM_CONTEXT SystemContext,
444 IN CHAR8 *PacketData
445 )
446 {
447 SendNotSupported ();
448 }
449
450
451 /**
452 Send the T signal with the given exception type (in gdb order) and possibly
453 with n:r pairs related to the watchpoints
454
455 @param SystemContext Register content at time of the exception
456 @param GdbExceptionType GDB exception type
457 **/
458 VOID
459 ProcessorSendTSignal (
460 IN EFI_SYSTEM_CONTEXT SystemContext,
461 IN UINT8 GdbExceptionType,
462 IN OUT CHAR8 *TSignalPtr,
463 IN UINTN SizeOfBuffer
464 )
465 {
466 *TSignalPtr = '\0';
467 }
468
469 /**
470 FIQ state is only changed by FIQ exception. We don't want to take FIQ
471 ticks in the GDB stub. The stub disables FIQ on entry, but this is the
472 third instruction that executes in the execption handler. Thus we have
473 a crack we need to test for.
474
475 @param PC PC of execption
476
477 @return TRUE We are in the GDB stub exception preamble
478 @return FALSE We are not in GDB stub code
479 **/
480 BOOLEAN
481 InFiqCrack (
482 IN UINT32 PC
483 )
484 {
485 UINT32 VectorBase = PcdGet32 (PcdCpuVectorBaseAddress);
486 UINT32 Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;
487
488 if ((PC >= VectorBase) && (PC <= (VectorBase + Length))) {
489 return TRUE;
490 }
491
492 return FALSE;
493 }
494
495
496 /**
497 Check to see if this exception is related to ctrl-c handling.
498
499 In this scheme we dedicate FIQ to the ctrl-c handler so it is
500 independent of the rest of the system.
501
502 SaveAndSetDebugTimerInterrupt () can be used to
503
504 @param ExceptionType Exception that is being processed
505 @param SystemContext Register content at time of the exception
506
507 @return TRUE This was a ctrl-c check that did not find a ctrl-c
508 @return FALSE This was not a ctrl-c check or some one hit ctrl-c
509 **/
510 BOOLEAN
511 ProcessorControlC (
512 IN EFI_EXCEPTION_TYPE ExceptionType,
513 IN OUT EFI_SYSTEM_CONTEXT SystemContext
514 )
515 {
516 CHAR8 Char;
517 BOOLEAN Return = TRUE;
518
519 if (ExceptionType != EXCEPT_ARM_FIQ) {
520 // Skip it as it is not related to ctrl-c
521 return FALSE;
522 }
523
524 if (InFiqCrack (SystemContext.SystemContextArm->PC)) {
525 // We are in our own interrupt preable, so skip this tick.
526 // We never want to let gdb see the debug stub running if we can help it
527 return FALSE;
528 }
529
530 while (TRUE) {
531 if (!GdbIsCharAvailable ()) {
532 //
533 // No characters are pending so exit the loop
534 //
535 Return = TRUE;
536 break;
537 }
538
539 Char = GdbGetChar ();
540 if (Char == 0x03) {
541 //
542 // We have a ctrl-c so exit and process exception for ctrl-c
543 //
544 Return = FALSE;
545 break;
546 }
547 }
548
549 DebugAgentTimerEndOfInterrupt ();
550
551 // Force an exit from the exception handler as we are done
552 return Return;
553 }
554
555
556 /**
557 Enable/Disable the interrupt of debug timer and return the interrupt state
558 prior to the operation.
559
560 If EnableStatus is TRUE, enable the interrupt of debug timer.
561 If EnableStatus is FALSE, disable the interrupt of debug timer.
562
563 @param[in] EnableStatus Enable/Disable.
564
565 @retval TRUE Debug timer interrupt were enabled on entry to this call.
566 @retval FALSE Debug timer interrupt were disabled on entry to this call.
567
568 **/
569 BOOLEAN
570 EFIAPI
571 SaveAndSetDebugTimerInterrupt (
572 IN BOOLEAN EnableStatus
573 )
574 {
575 BOOLEAN FiqEnabled;
576
577 FiqEnabled = ArmGetFiqState ();
578
579 if (EnableStatus) {
580 DebugAgentTimerSetPeriod (PcdGet32 (PcdGdbTimerPeriodMilliseconds));
581 ArmEnableFiq ();
582 } else {
583 DebugAgentTimerSetPeriod (0);
584 ArmDisableFiq ();
585 }
586
587 return FiqEnabled;
588 }
589
590
591
592 VOID
593 GdbFPutString (
594 IN CHAR8 *String
595 );
596
597 /**
598 Initialize debug agent.
599
600 This function is used to set up debug enviroment. It may enable interrupts.
601
602 @param[in] InitFlag Init flag is used to decide initialize process.
603 @param[in] Context Context needed according to InitFlag, it was optional.
604
605 **/
606 VOID
607 EFIAPI
608 InitializeDebugAgent (
609 IN UINT32 InitFlag,
610 IN VOID *Context OPTIONAL
611 )
612 {
613 UINTN Offset;
614 UINTN Length;
615 BOOLEAN IrqEnabled;
616 UINT32 *VectorBase;
617
618
619 //
620 // Disable interrupts
621 //
622 IrqEnabled = ArmGetInterruptState ();
623 ArmDisableInterrupts ();
624 ArmDisableFiq ();
625
626 //
627 // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.
628 //
629 Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;
630
631 //
632 // Reserve space for the exception handlers
633 //
634 VectorBase = (UINT32 *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress);
635
636
637 // Copy our assembly code into the page that contains the exception vectors.
638 CopyMem ((VOID *)VectorBase, (VOID *)ExceptionHandlersStart, Length);
639
640 //
641 // Patch in the common Assembly exception handler
642 //
643 Offset = (UINTN)CommonExceptionEntry - (UINTN)ExceptionHandlersStart;
644 *(UINTN *) (((UINT8 *)VectorBase) + Offset) = (UINTN)AsmCommonExceptionEntry;
645
646 // Flush Caches since we updated executable stuff
647 InvalidateInstructionCacheRange ((VOID *)PcdGet32(PcdCpuVectorBaseAddress), Length);
648
649 // setup a timer so gdb can break in via ctrl-c
650 DebugAgentTimerIntialize ();
651
652 if (IrqEnabled) {
653 ArmEnableInterrupts ();
654 }
655
656 return;
657 }
658