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 // 24 EFI_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 37 UINTN 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 **/ 92 UINTN 93 MaxEfiException ( 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 **/ 106 UINTN 107 MaxRegisterCount ( 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 **/ 122 BOOLEAN 123 CheckIsa ( 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 **/ 142 UINTN * 143 FindPointerToRegister ( 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 **/ 162 CHAR8 * 163 BasicReadRegister ( 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 **/ 203 VOID 204 ReadNthRegister ( 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 **/ 232 VOID 233 EFIAPI 234 ReadGeneralRegisters ( 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 **/ 263 CHAR8 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 **/ 309 VOID 310 WriteNthRegister ( 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 346 VOID 347 EFIAPI 348 WriteGeneralRegisters ( 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 381 BOOLEAN mSingleStepActive = FALSE; 382 UINT32 mSingleStepPC; 383 UINT32 mSingleStepData; 384 UINTN mSingleStepDataSize; 385 386 typedef 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 396 LIST_ENTRY BreakpointList; 397 398 /** 399 Insert Single Step in the SystemContext 400 401 @param SystemContext Register content at time of the exception 402 **/ 403 VOID 404 AddSingleStep ( 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 **/ 434 VOID 435 RemoveSingleStep ( 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 **/ 461 VOID 462 EFIAPI 463 ContinueAtAddress ( 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 **/ 480 VOID 481 EFIAPI 482 SingleStep ( 483 IN EFI_SYSTEM_CONTEXT SystemContext, 484 IN CHAR8 *PacketData 485 ) 486 { 487 SendNotSupported (); 488 } 489 490 UINTN 491 GetBreakpointDataAddress ( 492 IN EFI_SYSTEM_CONTEXT SystemContext, 493 IN UINTN BreakpointNumber 494 ) 495 { 496 return 0; 497 } 498 499 UINTN 500 GetBreakpointDetected ( 501 IN EFI_SYSTEM_CONTEXT SystemContext 502 ) 503 { 504 return 0; 505 } 506 507 BREAK_TYPE 508 GetBreakpointType ( 509 IN EFI_SYSTEM_CONTEXT SystemContext, 510 IN UINTN BreakpointNumber 511 ) 512 { 513 return NotSupported; 514 } 515 516 ARM_SOFTWARE_BREAKPOINT * 517 SearchBreakpointList ( 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 538 VOID 539 SetBreakpoint ( 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 568 VOID 569 ClearBreakpoint ( 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 593 VOID 594 EFIAPI 595 InsertBreakPoint ( 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 626 VOID 627 EFIAPI 628 RemoveBreakPoint ( 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 659 VOID 660 InitializeProcessor ( 661 VOID 662 ) 663 { 664 // Initialize breakpoint list 665 InitializeListHead (&BreakpointList); 666 } 667 668 BOOLEAN 669 ValidateAddress ( 670 IN VOID *Address 671 ) 672 { 673 if ((UINT32)Address < 0x80000000) { 674 return FALSE; 675 } else { 676 return TRUE; 677 } 678 } 679 680 BOOLEAN 681 ValidateException ( 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