]> git.proxmox.com Git - mirror_edk2.git/blob - EmbeddedPkg/GdbStub/Arm/Processor.c
EmbeddedPkg: Fix various typos
[mirror_edk2.git] / EmbeddedPkg / GdbStub / Arm / Processor.c
1 /** @file
2 Processor specific parts of the GDB stub
3
4 Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR>
5
6 SPDX-License-Identifier: BSD-2-Clause-Patent
7
8 **/
9
10 #include <GdbStubInternal.h>
11 #include <Library/CacheMaintenanceLib.h>
12 #include <Library/PrintLib.h>
13
14 //
15 // Array of exception types that need to be hooked by the debugger
16 // (efi, gdb) //efi number
17 //
18 EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = {
19 { EXCEPT_ARM_SOFTWARE_INTERRUPT, GDB_SIGTRAP }
20 // { EXCEPT_ARM_UNDEFINED_INSTRUCTION, GDB_SIGTRAP },
21 // { EXCEPT_ARM_PREFETCH_ABORT, GDB_SIGTRAP },
22 // { EXCEPT_ARM_DATA_ABORT, GDB_SIGEMT },
23 // { EXCEPT_ARM_RESERVED, GDB_SIGILL }
24 };
25
26 // Shut up some annoying RVCT warnings
27 #ifdef __CC_ARM
28 #pragma diag_suppress 1296
29 #endif
30
31 UINTN gRegisterOffsets[] = {
32 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R0),
33 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R1),
34 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R2),
35 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R3),
36 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R4),
37 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R5),
38 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R6),
39 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R7),
40 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R8),
41 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R9),
42 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R10),
43 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R11),
44 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, R12),
45 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, SP),
46 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, LR),
47 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, PC),
48 0x00000F01, // f0
49 0x00000F02,
50 0x00000F03,
51 0x00000F11, // f1
52 0x00000F12,
53 0x00000F13,
54 0x00000F21, // f2
55 0x00000F22,
56 0x00000F23,
57 0x00000F31, // f3
58 0x00000F32,
59 0x00000F33,
60 0x00000F41, // f4
61 0x00000F42,
62 0x00000F43,
63 0x00000F51, // f5
64 0x00000F52,
65 0x00000F53,
66 0x00000F61, // f6
67 0x00000F62,
68 0x00000F63,
69 0x00000F71, // f7
70 0x00000F72,
71 0x00000F73,
72 0x00000FFF, // fps
73 OFFSET_OF(EFI_SYSTEM_CONTEXT_ARM, CPSR)
74 };
75
76 // restore warnings for RVCT
77 #ifdef __CC_ARM
78 #pragma diag_default 1296
79 #endif
80
81 /**
82 Return the number of entries in the gExceptionType[]
83
84 @retval UINTN, the number of entries in the gExceptionType[] array.
85 **/
86 UINTN
87 MaxEfiException (
88 VOID
89 )
90 {
91 return sizeof (gExceptionType) / sizeof (EFI_EXCEPTION_TYPE_ENTRY);
92 }
93
94
95 /**
96 Return the number of entries in the gRegisters[]
97
98 @retval UINTN, the number of entries (registers) in the gRegisters[] array.
99 **/
100 UINTN
101 MaxRegisterCount (
102 VOID
103 )
104 {
105 return sizeof (gRegisterOffsets) / sizeof (UINTN);
106 }
107
108
109 /**
110 Check to see if the ISA is supported.
111 ISA = Instruction Set Architecture
112
113 @retval TRUE if Isa is supported
114
115 **/
116 BOOLEAN
117 CheckIsa (
118 IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa
119 )
120 {
121 if (Isa == IsaArm) {
122 return TRUE;
123 } else {
124 return FALSE;
125 }
126 }
127
128
129 /**
130 This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering
131 It is, by default, set to find the register pointer of the ARM member
132 @param SystemContext Register content at time of the exception
133 @param RegNumber The register to which we want to find a pointer
134 @retval the pointer to the RegNumber-th pointer
135 **/
136 UINTN *
137 FindPointerToRegister (
138 IN EFI_SYSTEM_CONTEXT SystemContext,
139 IN UINTN RegNumber
140 )
141 {
142 UINT8 *TempPtr;
143 ASSERT(gRegisterOffsets[RegNumber] < 0xF00);
144 TempPtr = ((UINT8 *)SystemContext.SystemContextArm) + gRegisterOffsets[RegNumber];
145 return (UINT32 *)TempPtr;
146 }
147
148
149 /**
150 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
151 @param SystemContext Register content at time of the exception
152 @param RegNumber the number of the register that we want to read
153 @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on.
154 @retval the pointer to the next character of the output buffer that is available to be written on.
155 **/
156 CHAR8 *
157 BasicReadRegister (
158 IN EFI_SYSTEM_CONTEXT SystemContext,
159 IN UINTN RegNumber,
160 IN CHAR8 *OutBufPtr
161 )
162 {
163 UINTN RegSize;
164 CHAR8 Char;
165
166 if (gRegisterOffsets[RegNumber] > 0xF00) {
167 AsciiSPrint (OutBufPtr, 9, "00000000");
168 OutBufPtr += 8;
169 return OutBufPtr;
170 }
171
172 RegSize = 0;
173 while (RegSize < 32) {
174 Char = mHexToStr[(UINT8)((*FindPointerToRegister (SystemContext, RegNumber) >> (RegSize+4)) & 0xf)];
175 if ((Char >= 'A') && (Char <= 'F')) {
176 Char = Char - 'A' + 'a';
177 }
178 *OutBufPtr++ = Char;
179
180 Char = mHexToStr[(UINT8)((*FindPointerToRegister (SystemContext, RegNumber) >> RegSize) & 0xf)];
181 if ((Char >= 'A') && (Char <= 'F')) {
182 Char = Char - 'A' + 'a';
183 }
184 *OutBufPtr++ = Char;
185
186 RegSize = RegSize + 8;
187 }
188 return OutBufPtr;
189 }
190
191
192 /**
193 Reads the n-th register's value into an output buffer and sends it as a packet
194 @param SystemContext Register content at time of the exception
195 @param InBuffer Pointer to the input buffer received from gdb server
196 **/
197 VOID
198 ReadNthRegister (
199 IN EFI_SYSTEM_CONTEXT SystemContext,
200 IN CHAR8 *InBuffer
201 )
202 {
203 UINTN RegNumber;
204 CHAR8 OutBuffer[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq)
205 CHAR8 *OutBufPtr; // pointer to the output buffer
206
207 RegNumber = AsciiStrHexToUintn (&InBuffer[1]);
208
209 if (RegNumber >= MaxRegisterCount ()) {
210 SendError (GDB_EINVALIDREGNUM);
211 return;
212 }
213
214 OutBufPtr = OutBuffer;
215 OutBufPtr = BasicReadRegister (SystemContext, RegNumber, OutBufPtr);
216
217 *OutBufPtr = '\0'; // the end of the buffer
218 SendPacket (OutBuffer);
219 }
220
221
222 /**
223 Reads the general registers into an output buffer and sends it as a packet
224 @param SystemContext Register content at time of the exception
225 **/
226 VOID
227 EFIAPI
228 ReadGeneralRegisters (
229 IN EFI_SYSTEM_CONTEXT SystemContext
230 )
231 {
232 UINTN Index;
233 CHAR8 *OutBuffer;
234 CHAR8 *OutBufPtr;
235 UINTN RegisterCount = MaxRegisterCount ();
236
237 // It is not safe to allocate pool here....
238 OutBuffer = AllocatePool ((RegisterCount * 8) + 1); // 8 bytes per register in string format plus a null to terminate
239 OutBufPtr = OutBuffer;
240 for (Index = 0; Index < RegisterCount; Index++) {
241 OutBufPtr = BasicReadRegister (SystemContext, Index, OutBufPtr);
242 }
243
244 *OutBufPtr = '\0';
245 SendPacket (OutBuffer);
246 FreePool (OutBuffer);
247 }
248
249
250 /**
251 Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr
252 @param SystemContext Register content at time of the exception
253 @param RegNumber the number of the register that we want to write
254 @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on.
255 @retval the pointer to the next character of the input buffer that can be used
256 **/
257 CHAR8
258 *BasicWriteRegister (
259 IN EFI_SYSTEM_CONTEXT SystemContext,
260 IN UINTN RegNumber,
261 IN CHAR8 *InBufPtr
262 )
263 {
264 UINTN RegSize;
265 UINTN TempValue; // the value transferred from a hex char
266 UINT32 NewValue; // the new value of the RegNumber-th Register
267
268 if (gRegisterOffsets[RegNumber] > 0xF00) {
269 return InBufPtr + 8;
270 }
271
272 NewValue = 0;
273 RegSize = 0;
274 while (RegSize < 32) {
275 TempValue = HexCharToInt (*InBufPtr++);
276
277 if ((INTN)TempValue < 0) {
278 SendError (GDB_EBADMEMDATA);
279 return NULL;
280 }
281
282 NewValue += (TempValue << (RegSize+4));
283 TempValue = HexCharToInt (*InBufPtr++);
284
285 if ((INTN)TempValue < 0) {
286 SendError (GDB_EBADMEMDATA);
287 return NULL;
288 }
289
290 NewValue += (TempValue << RegSize);
291 RegSize = RegSize + 8;
292 }
293 *(FindPointerToRegister (SystemContext, RegNumber)) = NewValue;
294 return InBufPtr;
295 }
296
297
298 /** ‘P n...=r...’
299 Writes the new value of n-th register received into the input buffer to the n-th register
300 @param SystemContext Register content at time of the exception
301 @param InBuffer Pointer to the input buffer received from gdb server
302 **/
303 VOID
304 WriteNthRegister (
305 IN EFI_SYSTEM_CONTEXT SystemContext,
306 IN CHAR8 *InBuffer
307 )
308 {
309 UINTN RegNumber;
310 CHAR8 RegNumBuffer[MAX_REG_NUM_BUF_SIZE]; // put the 'n..' part of the message into this array
311 CHAR8 *RegNumBufPtr;
312 CHAR8 *InBufPtr; // pointer to the input buffer
313
314 // find the register number to write
315 InBufPtr = &InBuffer[1];
316 RegNumBufPtr = RegNumBuffer;
317 while (*InBufPtr != '=') {
318 *RegNumBufPtr++ = *InBufPtr++;
319 }
320 *RegNumBufPtr = '\0';
321 RegNumber = AsciiStrHexToUintn (RegNumBuffer);
322
323 // check if this is a valid Register Number
324 if (RegNumber >= MaxRegisterCount ()) {
325 SendError (GDB_EINVALIDREGNUM);
326 return;
327 }
328 InBufPtr++; // skips the '=' character
329 BasicWriteRegister (SystemContext, RegNumber, InBufPtr);
330 SendSuccess();
331 }
332
333
334 /** ‘G XX...’
335 Writes the new values received into the input buffer to the general registers
336 @param SystemContext Register content at time of the exception
337 @param InBuffer Pointer to the input buffer received from gdb server
338 **/
339
340 VOID
341 EFIAPI
342 WriteGeneralRegisters (
343 IN EFI_SYSTEM_CONTEXT SystemContext,
344 IN CHAR8 *InBuffer
345 )
346 {
347 UINTN i;
348 CHAR8 *InBufPtr; /// pointer to the input buffer
349 UINTN MinLength;
350 UINTN RegisterCount = MaxRegisterCount ();
351
352 MinLength = (RegisterCount * 8) + 1; // 'G' plus the registers in ASCII format
353
354 if (AsciiStrLen (InBuffer) < MinLength) {
355 //Bad message. Message is not the right length
356 SendError (GDB_EBADBUFSIZE);
357 return;
358 }
359
360 InBufPtr = &InBuffer[1];
361
362 // Read the new values for the registers from the input buffer to an array, NewValueArray.
363 // The values in the array are in the gdb ordering
364 for (i = 0; i < RegisterCount; i++) {
365 InBufPtr = BasicWriteRegister (SystemContext, i, InBufPtr);
366 }
367
368 SendSuccess ();
369 }
370
371 // What about Thumb?
372 // Use SWI 0xdbdbdb as the debug instruction
373 #define GDB_ARM_BKPT 0xefdbdbdb
374
375 BOOLEAN mSingleStepActive = FALSE;
376 UINT32 mSingleStepPC;
377 UINT32 mSingleStepData;
378 UINTN mSingleStepDataSize;
379
380 typedef struct {
381 LIST_ENTRY Link;
382 UINT64 Signature;
383 UINT32 Address;
384 UINT32 Instruction;
385 } ARM_SOFTWARE_BREAKPOINT;
386
387 #define ARM_SOFTWARE_BREAKPOINT_SIGNATURE SIGNATURE_64('A', 'R', 'M', 'B', 'R', 'K', 'P', 'T')
388 #define ARM_SOFTWARE_BREAKPOINT_FROM_LINK(a) CR(a, ARM_SOFTWARE_BREAKPOINT, Link, ARM_SOFTWARE_BREAKPOINT_SIGNATURE)
389
390 LIST_ENTRY BreakpointList;
391
392 /**
393 Insert Single Step in the SystemContext
394
395 @param SystemContext Register content at time of the exception
396 **/
397 VOID
398 AddSingleStep (
399 IN EFI_SYSTEM_CONTEXT SystemContext
400 )
401 {
402 if (mSingleStepActive) {
403 // Currently don't support nesting
404 return;
405 }
406 mSingleStepActive = TRUE;
407
408 mSingleStepPC = SystemContext.SystemContextArm->PC;
409
410 mSingleStepDataSize = sizeof (UINT32);
411 mSingleStepData = (*(UINT32 *)mSingleStepPC);
412 *(UINT32 *)mSingleStepPC = GDB_ARM_BKPT;
413 if (*(UINT32 *)mSingleStepPC != GDB_ARM_BKPT) {
414 // For some reason our breakpoint did not take
415 mSingleStepActive = FALSE;
416 }
417
418 InvalidateInstructionCacheRange ((VOID *)mSingleStepPC, mSingleStepDataSize);
419 //DEBUG((EFI_D_ERROR, "AddSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, mSingleStepData, *(UINT32 *)mSingleStepPC));
420 }
421
422
423 /**
424 Remove Single Step in the SystemContext
425
426 @param SystemContext Register content at time of the exception
427 **/
428 VOID
429 RemoveSingleStep (
430 IN EFI_SYSTEM_CONTEXT SystemContext
431 )
432 {
433 if (!mSingleStepActive) {
434 return;
435 }
436
437 if (mSingleStepDataSize == sizeof (UINT16)) {
438 *(UINT16 *)mSingleStepPC = (UINT16)mSingleStepData;
439 } else {
440 //DEBUG((EFI_D_ERROR, "RemoveSingleStep at 0x%08x (was: 0x%08x is:0x%08x)\n", SystemContext.SystemContextArm->PC, *(UINT32 *)mSingleStepPC, mSingleStepData));
441 *(UINT32 *)mSingleStepPC = mSingleStepData;
442 }
443 InvalidateInstructionCacheRange ((VOID *)mSingleStepPC, mSingleStepDataSize);
444 mSingleStepActive = FALSE;
445 }
446
447
448
449 /**
450 Continue. addr is Address to resume. If addr is omitted, resume at current
451 Address.
452
453 @param SystemContext Register content at time of the exception
454 **/
455 VOID
456 EFIAPI
457 ContinueAtAddress (
458 IN EFI_SYSTEM_CONTEXT SystemContext,
459 IN CHAR8 *PacketData
460 )
461 {
462 if (PacketData[1] != '\0') {
463 SystemContext.SystemContextArm->PC = AsciiStrHexToUintn (&PacketData[1]);
464 }
465 }
466
467
468 /** ‘s [addr ]’
469 Single step. addr is the Address at which to resume. If addr is omitted, resume
470 at same Address.
471
472 @param SystemContext Register content at time of the exception
473 **/
474 VOID
475 EFIAPI
476 SingleStep (
477 IN EFI_SYSTEM_CONTEXT SystemContext,
478 IN CHAR8 *PacketData
479 )
480 {
481 SendNotSupported ();
482 }
483
484 UINTN
485 GetBreakpointDataAddress (
486 IN EFI_SYSTEM_CONTEXT SystemContext,
487 IN UINTN BreakpointNumber
488 )
489 {
490 return 0;
491 }
492
493 UINTN
494 GetBreakpointDetected (
495 IN EFI_SYSTEM_CONTEXT SystemContext
496 )
497 {
498 return 0;
499 }
500
501 BREAK_TYPE
502 GetBreakpointType (
503 IN EFI_SYSTEM_CONTEXT SystemContext,
504 IN UINTN BreakpointNumber
505 )
506 {
507 return NotSupported;
508 }
509
510 ARM_SOFTWARE_BREAKPOINT *
511 SearchBreakpointList (
512 IN UINT32 Address
513 )
514 {
515 LIST_ENTRY *Current;
516 ARM_SOFTWARE_BREAKPOINT *Breakpoint;
517
518 Current = GetFirstNode (&BreakpointList);
519 while (!IsNull (&BreakpointList, Current)) {
520 Breakpoint = ARM_SOFTWARE_BREAKPOINT_FROM_LINK(Current);
521
522 if (Address == Breakpoint->Address) {
523 return Breakpoint;
524 }
525
526 Current = GetNextNode (&BreakpointList, Current);
527 }
528
529 return NULL;
530 }
531
532 VOID
533 SetBreakpoint (
534 IN UINT32 Address
535 )
536 {
537 ARM_SOFTWARE_BREAKPOINT *Breakpoint;
538
539 Breakpoint = SearchBreakpointList (Address);
540
541 if (Breakpoint != NULL) {
542 return;
543 }
544
545 // create and fill breakpoint structure
546 Breakpoint = AllocatePool (sizeof(ARM_SOFTWARE_BREAKPOINT));
547
548 Breakpoint->Signature = ARM_SOFTWARE_BREAKPOINT_SIGNATURE;
549 Breakpoint->Address = Address;
550 Breakpoint->Instruction = *(UINT32 *)Address;
551
552 // Add it to the list
553 InsertTailList (&BreakpointList, &Breakpoint->Link);
554
555 // Insert the software breakpoint
556 *(UINT32 *)Address = GDB_ARM_BKPT;
557 InvalidateInstructionCacheRange ((VOID *)Address, 4);
558
559 //DEBUG((EFI_D_ERROR, "SetBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, Breakpoint->Instruction, *(UINT32 *)Address));
560 }
561
562 VOID
563 ClearBreakpoint (
564 IN UINT32 Address
565 )
566 {
567 ARM_SOFTWARE_BREAKPOINT *Breakpoint;
568
569 Breakpoint = SearchBreakpointList (Address);
570
571 if (Breakpoint == NULL) {
572 return;
573 }
574
575 // Add it to the list
576 RemoveEntryList (&Breakpoint->Link);
577
578 // Restore the original instruction
579 *(UINT32 *)Address = Breakpoint->Instruction;
580 InvalidateInstructionCacheRange ((VOID *)Address, 4);
581
582 //DEBUG((EFI_D_ERROR, "ClearBreakpoint at 0x%08x (was: 0x%08x is:0x%08x)\n", Address, GDB_ARM_BKPT, *(UINT32 *)Address));
583
584 FreePool (Breakpoint);
585 }
586
587 VOID
588 EFIAPI
589 InsertBreakPoint (
590 IN EFI_SYSTEM_CONTEXT SystemContext,
591 IN CHAR8 *PacketData
592 )
593 {
594 UINTN Type;
595 UINTN Address;
596 UINTN Length;
597 UINTN ErrorCode;
598
599 ErrorCode = ParseBreakpointPacket (PacketData, &Type, &Address, &Length);
600 if (ErrorCode > 0) {
601 SendError ((UINT8)ErrorCode);
602 return;
603 }
604
605 switch (Type) {
606 case 0: //Software breakpoint
607 break;
608
609 default :
610 DEBUG((EFI_D_ERROR, "Insert breakpoint default: %x\n", Type));
611 SendError (GDB_EINVALIDBRKPOINTTYPE);
612 return;
613 }
614
615 SetBreakpoint (Address);
616
617 SendSuccess ();
618 }
619
620 VOID
621 EFIAPI
622 RemoveBreakPoint (
623 IN EFI_SYSTEM_CONTEXT SystemContext,
624 IN CHAR8 *PacketData
625 )
626 {
627 UINTN Type;
628 UINTN Address;
629 UINTN Length;
630 UINTN ErrorCode;
631
632 //Parse breakpoint packet data
633 ErrorCode = ParseBreakpointPacket (PacketData, &Type, &Address, &Length);
634 if (ErrorCode > 0) {
635 SendError ((UINT8)ErrorCode);
636 return;
637 }
638
639 switch (Type) {
640 case 0: //Software breakpoint
641 break;
642
643 default:
644 SendError (GDB_EINVALIDBRKPOINTTYPE);
645 return;
646 }
647
648 ClearBreakpoint (Address);
649
650 SendSuccess ();
651 }
652
653 VOID
654 InitializeProcessor (
655 VOID
656 )
657 {
658 // Initialize breakpoint list
659 InitializeListHead (&BreakpointList);
660 }
661
662 BOOLEAN
663 ValidateAddress (
664 IN VOID *Address
665 )
666 {
667 if ((UINT32)Address < 0x80000000) {
668 return FALSE;
669 } else {
670 return TRUE;
671 }
672 }
673
674 BOOLEAN
675 ValidateException (
676 IN EFI_EXCEPTION_TYPE ExceptionType,
677 IN OUT EFI_SYSTEM_CONTEXT SystemContext
678 )
679 {
680 UINT32 ExceptionAddress;
681 UINT32 Instruction;
682
683 // Is it a debugger SWI?
684 ExceptionAddress = SystemContext.SystemContextArm->PC -= 4;
685 Instruction = *(UINT32 *)ExceptionAddress;
686 if (Instruction != GDB_ARM_BKPT) {
687 return FALSE;
688 }
689
690 // Special for SWI-based exception handling. SWI sets up the context
691 // to return to the instruction following the SWI instruction - NOT what we want
692 // for a debugger!
693 SystemContext.SystemContextArm->PC = ExceptionAddress;
694
695 return TRUE;
696 }
697