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