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