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