Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

gdbmach.c 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251
  1. /*
  2. * Copyright (C) 2008 Stefan Hajnoczi <stefanha@gmail.com>.
  3. * Copyright (C) 2016 Michael Brown <mbrown@fensystems.co.uk>.
  4. *
  5. * This program is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU General Public License as
  7. * published by the Free Software Foundation; either version 2 of the
  8. * License, or any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful, but
  11. * WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. * General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program; if not, write to the Free Software
  17. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  18. * 02110-1301, USA.
  19. *
  20. * You can also choose to distribute this program under the terms of
  21. * the Unmodified Binary Distribution Licence (as given in the file
  22. * COPYING.UBDL), provided that you have satisfied its requirements.
  23. */
  24. FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
  25. #include <stddef.h>
  26. #include <stdio.h>
  27. #include <errno.h>
  28. #include <assert.h>
  29. #include <ipxe/uaccess.h>
  30. #include <ipxe/gdbstub.h>
  31. #include <librm.h>
  32. #include <gdbmach.h>
  33. /** @file
  34. *
  35. * GDB architecture-specific bits for x86
  36. *
  37. */
  38. /** Number of hardware breakpoints */
  39. #define NUM_HWBP 4
  40. /** Debug register 7: Global breakpoint enable */
  41. #define DR7_G( bp ) ( 2 << ( 2 * (bp) ) )
  42. /** Debug register 7: Global exact breakpoint enable */
  43. #define DR7_GE ( 1 << 9 )
  44. /** Debug register 7: Break on data writes */
  45. #define DR7_RWLEN_WRITE 0x11110000
  46. /** Debug register 7: Break on data access */
  47. #define DR7_RWLEN_ACCESS 0x33330000
  48. /** Debug register 7: One-byte length */
  49. #define DR7_RWLEN_1 0x00000000
  50. /** Debug register 7: Two-byte length */
  51. #define DR7_RWLEN_2 0x44440000
  52. /** Debug register 7: Four-byte length */
  53. #define DR7_RWLEN_4 0xcccc0000
  54. /** Debug register 7: Eight-byte length */
  55. #define DR7_RWLEN_8 0x88880000
  56. /** Debug register 7: Breakpoint R/W and length mask */
  57. #define DR7_RWLEN_MASK( bp ) ( 0xf0000 << ( 4 * (bp) ) )
  58. /** Hardware breakpoint addresses (debug registers 0-3) */
  59. static unsigned long dr[NUM_HWBP];
  60. /** Active value of debug register 7 */
  61. static unsigned long dr7 = DR7_GE;
  62. /**
  63. * Update debug registers
  64. *
  65. */
  66. static void gdbmach_update ( void ) {
  67. /* Set debug registers */
  68. __asm__ __volatile__ ( "mov %0, %%dr0" : : "r" ( dr[0] ) );
  69. __asm__ __volatile__ ( "mov %0, %%dr1" : : "r" ( dr[1] ) );
  70. __asm__ __volatile__ ( "mov %0, %%dr2" : : "r" ( dr[2] ) );
  71. __asm__ __volatile__ ( "mov %0, %%dr3" : : "r" ( dr[3] ) );
  72. __asm__ __volatile__ ( "mov %0, %%dr7" : : "r" ( dr7 ) );
  73. }
  74. /**
  75. * Find reusable or available hardware breakpoint
  76. *
  77. * @v addr Linear address
  78. * @v rwlen Control bits
  79. * @ret bp Hardware breakpoint, or negative error
  80. */
  81. static int gdbmach_find ( unsigned long addr, unsigned int rwlen ) {
  82. unsigned int i;
  83. int bp = -ENOENT;
  84. /* Look for a reusable or available breakpoint */
  85. for ( i = 0 ; i < NUM_HWBP ; i++ ) {
  86. /* If breakpoint is not enabled, then it is available */
  87. if ( ! ( dr7 & DR7_G ( i ) ) ) {
  88. bp = i;
  89. continue;
  90. }
  91. /* If breakpoint is enabled and has the same address
  92. * and control bits, then reuse it.
  93. */
  94. if ( ( dr[i] == addr ) &&
  95. ( ( ( dr7 ^ rwlen ) & DR7_RWLEN_MASK ( i ) ) == 0 ) ) {
  96. bp = i;
  97. break;
  98. }
  99. }
  100. return bp;
  101. }
  102. /**
  103. * Set hardware breakpoint
  104. *
  105. * @v type GDB breakpoint type
  106. * @v addr Virtual address
  107. * @v len Length
  108. * @v enable Enable (not disable) breakpoint
  109. * @ret rc Return status code
  110. */
  111. int gdbmach_set_breakpoint ( int type, unsigned long addr, size_t len,
  112. int enable ) {
  113. unsigned int rwlen;
  114. unsigned long mask;
  115. int bp;
  116. /* Parse breakpoint type */
  117. switch ( type ) {
  118. case GDBMACH_WATCH:
  119. rwlen = DR7_RWLEN_WRITE;
  120. break;
  121. case GDBMACH_AWATCH:
  122. rwlen = DR7_RWLEN_ACCESS;
  123. break;
  124. default:
  125. return -ENOTSUP;
  126. }
  127. /* Parse breakpoint length */
  128. switch ( len ) {
  129. case 1:
  130. rwlen |= DR7_RWLEN_1;
  131. break;
  132. case 2:
  133. rwlen |= DR7_RWLEN_2;
  134. break;
  135. case 4:
  136. rwlen |= DR7_RWLEN_4;
  137. break;
  138. case 8:
  139. rwlen |= DR7_RWLEN_8;
  140. break;
  141. default:
  142. return -ENOTSUP;
  143. }
  144. /* Convert to linear address */
  145. if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) )
  146. addr = virt_to_phys ( ( void * ) addr );
  147. /* Find reusable or available hardware breakpoint */
  148. bp = gdbmach_find ( addr, rwlen );
  149. if ( bp < 0 )
  150. return ( enable ? -ENOBUFS : 0 );
  151. /* Configure this breakpoint */
  152. DBGC ( &dr[0], "GDB bp %d at %p+%zx type %d (%sabled)\n",
  153. bp, ( ( void * ) addr ), len, type, ( enable ? "en" : "dis" ) );
  154. dr[bp] = addr;
  155. mask = DR7_RWLEN_MASK ( bp );
  156. dr7 = ( ( dr7 & ~mask ) | ( rwlen & mask ) );
  157. mask = DR7_G ( bp );
  158. dr7 &= ~mask;
  159. if ( enable )
  160. dr7 |= mask;
  161. /* Update debug registers */
  162. gdbmach_update();
  163. return 0;
  164. }
  165. /**
  166. * Handle exception
  167. *
  168. * @v signo GDB signal number
  169. * @v regs Register dump
  170. */
  171. __asmcall void gdbmach_handler ( int signo, gdbreg_t *regs ) {
  172. unsigned long dr7_disabled = DR7_GE;
  173. unsigned long dr6_clear = 0;
  174. /* Temporarily disable breakpoints */
  175. __asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7_disabled ) );
  176. /* Handle exception */
  177. DBGC ( &dr[0], "GDB signal %d\n", signo );
  178. DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) );
  179. gdbstub_handler ( signo, regs );
  180. DBGC ( &dr[0], "GDB signal %d returning\n", signo );
  181. DBGC2_HDA ( &dr[0], 0, regs, ( GDBMACH_NREGS * sizeof ( *regs ) ) );
  182. /* Clear breakpoint status register */
  183. __asm__ __volatile__ ( "mov %0, %%dr6\n" : : "r" ( dr6_clear ) );
  184. /* Re-enable breakpoints */
  185. __asm__ __volatile__ ( "mov %0, %%dr7\n" : : "r" ( dr7 ) );
  186. }
  187. /**
  188. * CPU exception vectors
  189. *
  190. * Note that we cannot intercept anything from INT8 (double fault)
  191. * upwards, since these overlap by default with IRQ0-7.
  192. */
  193. static void * gdbmach_vectors[] = {
  194. gdbmach_sigfpe, /* Divide by zero */
  195. gdbmach_sigtrap, /* Debug trap */
  196. NULL, /* Non-maskable interrupt */
  197. gdbmach_sigtrap, /* Breakpoint */
  198. gdbmach_sigstkflt, /* Overflow */
  199. gdbmach_sigstkflt, /* Bound range exceeded */
  200. gdbmach_sigill, /* Invalid opcode */
  201. };
  202. /**
  203. * Initialise GDB
  204. */
  205. void gdbmach_init ( void ) {
  206. unsigned int i;
  207. /* Hook CPU exception vectors */
  208. for ( i = 0 ; i < ( sizeof ( gdbmach_vectors ) /
  209. sizeof ( gdbmach_vectors[0] ) ) ; i++ ) {
  210. if ( gdbmach_vectors[i] )
  211. set_interrupt_vector ( i, gdbmach_vectors[i] );
  212. }
  213. }