Add the beginning of a GDB based Debug Agent. IA-32 and X64 don't have low level...
[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 Check to see if this exception is related to ctrl-c handling.
471
472 In this scheme we dedicate FIQ to the ctrl-c handler so it is
473 independent of the rest of the system.
474
475 SaveAndSetDebugTimerInterrupt () can be used to
476
477 @param ExceptionType Exception that is being processed
478 @param SystemContext Register content at time of the exception
479
480 @return TRUE This was a ctrl-c check that did not find a ctrl-c
481 @return FALSE This was not a ctrl-c check or some one hit ctrl-c
482 **/
483 BOOLEAN
484 ProcessorControlC (
485 IN EFI_EXCEPTION_TYPE ExceptionType,
486 IN OUT EFI_SYSTEM_CONTEXT SystemContext
487 )
488 {
489 BOOLEAN Return = TRUE;
490
491 if (ExceptionType != EXCEPT_ARM_FIQ) {
492 // Skip it as it is not related to ctrl-c
493 return FALSE;
494 }
495
496 while (TRUE) {
497 if (!GdbIsCharAvailable ()) {
498 //
499 // No characters are pending so exit the loop
500 //
501 Return = TRUE;
502 break;
503 }
504
505 if (GdbGetChar () == 0x03) {
506 //
507 // We have a ctrl-c so exit and process exception for ctrl-c
508 //
509 Return = FALSE;
510 break;
511 }
512 }
513
514 DebugAgentTimerEndOfInterrupt ();
515
516 // Force an exit from the exception handler as we are done
517 return Return;
518 }
519
520
521 /**
522 Enable/Disable the interrupt of debug timer and return the interrupt state
523 prior to the operation.
524
525 If EnableStatus is TRUE, enable the interrupt of debug timer.
526 If EnableStatus is FALSE, disable the interrupt of debug timer.
527
528 @param[in] EnableStatus Enable/Disable.
529
530 @return FALSE always.
531
532 **/
533 BOOLEAN
534 EFIAPI
535 SaveAndSetDebugTimerInterrupt (
536 IN BOOLEAN EnableStatus
537 )
538 {
539 BOOLEAN FiqEnabled;
540
541 FiqEnabled = ArmGetFiqState ();
542
543 if (EnableStatus) {
544 DebugAgentTimerSetPeriod (100);
545 ArmEnableFiq ();
546 } else {
547 DebugAgentTimerSetPeriod (0);
548 ArmDisableFiq ();
549 }
550
551 return FiqEnabled;
552 }
553
554 VOID
555 GdbFPutString (
556 IN CHAR8 *String
557 );
558
559 /**
560 Initialize debug agent.
561
562 This function is used to set up debug enviroment. It may enable interrupts.
563
564 @param[in] InitFlag Init flag is used to decide initialize process.
565 @param[in] Context Context needed according to InitFlag, it was optional.
566
567 **/
568 VOID
569 EFIAPI
570 InitializeDebugAgent (
571 IN UINT32 InitFlag,
572 IN VOID *Context OPTIONAL
573 )
574 {
575 UINTN Offset;
576 UINTN Length;
577 BOOLEAN IrqEnabled;
578 BOOLEAN FiqEnabled;
579 UINT32 *VectorBase;
580
581
582 //
583 // Disable interrupts
584 //
585 IrqEnabled = ArmGetInterruptState ();
586 ArmDisableInterrupts ();
587
588 //
589 // EFI does not use the FIQ, but a debugger might so we must disable
590 // as we take over the exception vectors.
591 //
592 FiqEnabled = ArmGetFiqState ();
593 ArmDisableFiq ();
594
595 //
596 // Copy an implementation of the ARM exception vectors to PcdCpuVectorBaseAddress.
597 //
598 Length = (UINTN)ExceptionHandlersEnd - (UINTN)ExceptionHandlersStart;
599
600 //
601 // Reserve space for the exception handlers
602 //
603 VectorBase = (UINT32 *)(UINTN)PcdGet32 (PcdCpuVectorBaseAddress);
604
605
606 // Copy our assembly code into the page that contains the exception vectors.
607 CopyMem ((VOID *)VectorBase, (VOID *)ExceptionHandlersStart, Length);
608
609 //
610 // Patch in the common Assembly exception handler
611 //
612 Offset = (UINTN)CommonExceptionEntry - (UINTN)ExceptionHandlersStart;
613 *(UINTN *) (((UINT8 *)VectorBase) + Offset) = (UINTN)AsmCommonExceptionEntry;
614
615 // Flush Caches since we updated executable stuff
616 InvalidateInstructionCacheRange ((VOID *)PcdGet32(PcdCpuVectorBaseAddress), Length);
617
618 DebugAgentTimerIntialize ();
619
620 if (FiqEnabled) {
621 ArmEnableFiq ();
622 }
623
624 if (IrqEnabled) {
625 ArmEnableInterrupts ();
626 }
627
628 return;
629 }
630