969eba7b |
1 | /** @file |
2 | Processor specific parts of the GDB stub |
3 | |
60274cca |
4 | Copyright (c) 2008 - 2009, Apple Inc. All rights reserved.<BR> |
969eba7b |
5 | |
60274cca |
6 | This program and the accompanying materials |
969eba7b |
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 <GdbDebugAgent.h>\r |
17 | |
18 | // |
19 | // Array of exception types that need to be hooked by the debugger |
20 | // {EFI mapping, GDB mapping} |
21 | // |
22 | EFI_EXCEPTION_TYPE_ENTRY gExceptionType[] = { |
23 | { EXCEPT_IA32_DIVIDE_ERROR, GDB_SIGFPE }, |
24 | { EXCEPT_IA32_DEBUG, GDB_SIGTRAP }, |
25 | { EXCEPT_IA32_NMI, GDB_SIGEMT }, |
26 | { EXCEPT_IA32_BREAKPOINT, GDB_SIGTRAP }, |
27 | { EXCEPT_IA32_OVERFLOW, GDB_SIGSEGV }, |
28 | { EXCEPT_IA32_BOUND, GDB_SIGSEGV }, |
29 | { EXCEPT_IA32_INVALID_OPCODE, GDB_SIGILL }, |
30 | { EXCEPT_IA32_DOUBLE_FAULT, GDB_SIGEMT }, |
31 | { EXCEPT_IA32_STACK_FAULT, GDB_SIGSEGV }, |
32 | { EXCEPT_IA32_GP_FAULT, GDB_SIGSEGV }, |
33 | { EXCEPT_IA32_PAGE_FAULT, GDB_SIGSEGV }, |
34 | { EXCEPT_IA32_FP_ERROR, GDB_SIGEMT }, |
35 | { EXCEPT_IA32_ALIGNMENT_CHECK, GDB_SIGEMT }, |
36 | { EXCEPT_IA32_MACHINE_CHECK, GDB_SIGEMT } |
37 | }; |
38 | |
39 | |
40 | // The offsets of registers SystemContext. |
41 | // The fields in the array are in the gdb ordering. |
42 | // |
43 | //16 regs |
44 | UINTN gRegisterOffsets[] = { |
45 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Eax), |
46 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ecx), |
47 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Edx), |
48 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ebx), |
49 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Esp), |
50 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ebp), |
51 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Esi), |
52 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Edi), |
53 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Eip), |
54 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Eflags), |
55 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Cs), |
56 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ss), |
57 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Ds), |
58 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Es), |
59 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Fs), |
60 | OFFSET_OF(EFI_SYSTEM_CONTEXT_IA32, Gs) |
61 | }; |
62 | |
63 | |
64 | //Debug only.. |
65 | VOID |
66 | PrintReg ( |
67 | IN EFI_SYSTEM_CONTEXT SystemContext |
68 | ) |
69 | { |
70 | Print ((CHAR16 *)L"EAX: %x ", SystemContext.SystemContextIa32->Eax); |
71 | Print ((CHAR16 *)L"ECX: %x ", SystemContext.SystemContextIa32->Ecx); |
72 | Print ((CHAR16 *)L"EDX: %x ", SystemContext.SystemContextIa32->Edx); |
73 | Print ((CHAR16 *)L"EBX: %x ", SystemContext.SystemContextIa32->Ebx); |
74 | Print ((CHAR16 *)L"ESP: %x ", SystemContext.SystemContextIa32->Esp); |
75 | Print ((CHAR16 *)L"EBP: %x ", SystemContext.SystemContextIa32->Ebp); |
76 | Print ((CHAR16 *)L"ESI: %x ", SystemContext.SystemContextIa32->Esi); |
77 | Print ((CHAR16 *)L"EDI: %x ", SystemContext.SystemContextIa32->Edi); |
78 | Print ((CHAR16 *)L"EIP: %x\n", SystemContext.SystemContextIa32->Eip); |
79 | Print ((CHAR16 *)L"EFlags: %x\n", SystemContext.SystemContextIa32->Eflags); |
80 | } |
81 | |
82 | //Debug only.. |
83 | VOID |
84 | PrintDRreg ( |
85 | IN EFI_SYSTEM_CONTEXT SystemContext |
86 | ) |
87 | { |
88 | Print ((CHAR16 *)L"DR0: %x ", SystemContext.SystemContextIa32->Dr0); |
89 | Print ((CHAR16 *)L"DR1: %x ", SystemContext.SystemContextIa32->Dr1); |
90 | Print ((CHAR16 *)L"DR2: %x ", SystemContext.SystemContextIa32->Dr2); |
91 | Print ((CHAR16 *)L"DR3: %x ", SystemContext.SystemContextIa32->Dr3); |
92 | Print ((CHAR16 *)L"DR6: %x ", SystemContext.SystemContextIa32->Dr6); |
93 | Print ((CHAR16 *)L"DR7: %x\n", SystemContext.SystemContextIa32->Dr7); |
94 | } |
95 | |
96 | |
97 | /** |
98 | Return the number of entries in the gExceptionType[] |
99 | |
100 | @retval UINTN, the number of entries in the gExceptionType[] array. |
101 | **/ |
102 | UINTN |
103 | MaxEfiException ( |
104 | VOID |
105 | ) |
106 | { |
107 | return sizeof (gExceptionType)/sizeof (EFI_EXCEPTION_TYPE_ENTRY); |
108 | } |
109 | |
110 | |
111 | /** |
112 | Check to see if the ISA is supported. |
113 | ISA = Instruction Set Architecture |
114 | |
115 | @retval TRUE if Isa is supported, |
116 | FALSE otherwise. |
117 | **/ |
118 | BOOLEAN |
119 | CheckIsa ( |
120 | IN EFI_INSTRUCTION_SET_ARCHITECTURE Isa |
121 | ) |
122 | { |
123 | return (BOOLEAN)(Isa == IsaIa32); |
124 | } |
125 | |
126 | |
127 | /** |
128 | This takes in the register number and the System Context, and returns a pointer to the RegNumber-th register in gdb ordering |
129 | It is, by default, set to find the register pointer of the IA32 member |
130 | |
131 | @param SystemContext Register content at time of the exception |
132 | @param RegNumber The register to which we want to find a pointer |
133 | @retval the pointer to the RegNumber-th pointer |
134 | **/ |
135 | UINTN * |
136 | FindPointerToRegister( |
137 | IN EFI_SYSTEM_CONTEXT SystemContext, |
138 | IN UINTN RegNumber |
139 | ) |
140 | { |
141 | UINT8 *TempPtr; |
142 | TempPtr = ((UINT8 *)SystemContext.SystemContextIa32) + gRegisterOffsets[RegNumber]; |
143 | return (UINTN *)TempPtr; |
144 | } |
145 | |
146 | |
147 | /** |
148 | Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr |
149 | |
150 | @param SystemContext Register content at time of the exception |
151 | @param RegNumber the number of the register that we want to read |
152 | @param OutBufPtr pointer to the output buffer's end. the new data will be added from this point on. |
153 | @retval the pointer to the next character of the output buffer that is available to be written on. |
154 | **/ |
155 | CHAR8 * |
156 | BasicReadRegister ( |
157 | IN EFI_SYSTEM_CONTEXT SystemContext, |
158 | IN UINTN RegNumber, |
159 | IN CHAR8 *OutBufPtr |
160 | ) |
161 | { |
162 | UINTN RegSize; |
163 | |
164 | RegSize = 0; |
165 | while (RegSize < REG_SIZE) { |
166 | *OutBufPtr++ = mHexToStr[((*FindPointerToRegister(SystemContext, RegNumber) >> (RegSize+4)) & 0xf)]; |
167 | *OutBufPtr++ = mHexToStr[((*FindPointerToRegister(SystemContext, RegNumber) >> RegSize) & 0xf)]; |
168 | RegSize = RegSize + 8; |
169 | } |
170 | return OutBufPtr; |
171 | } |
172 | |
173 | |
174 | /** ‘p n’ |
175 | Reads the n-th register's value into an output buffer and sends it as a packet |
176 | |
177 | @param SystemContext Register content at time of the exception |
178 | @param InBuffer Pointer to the input buffer received from gdb server |
179 | **/ |
180 | VOID |
181 | EFIAPI |
182 | ReadNthRegister ( |
183 | IN EFI_SYSTEM_CONTEXT SystemContext, |
184 | IN CHAR8 *InBuffer |
185 | ) |
186 | { |
187 | UINTN RegNumber; |
188 | CHAR8 OutBuffer[9]; // 1 reg=8 hex chars, and the end '\0' (escape seq) |
189 | CHAR8 *OutBufPtr; // pointer to the output buffer |
190 | |
191 | RegNumber = AsciiStrHexToUintn (&InBuffer[1]); |
192 | |
193 | if ((RegNumber < 0) || (RegNumber >= sizeof (gRegisterOffsets)/sizeof (UINTN))) { |
194 | SendError (GDB_EINVALIDREGNUM); |
195 | return; |
196 | } |
197 | |
198 | OutBufPtr = OutBuffer; |
199 | OutBufPtr = BasicReadRegister(SystemContext, RegNumber, OutBufPtr); |
200 | |
201 | *OutBufPtr = '\0'; // the end of the buffer |
202 | SendPacket(OutBuffer); |
203 | } |
204 | |
205 | |
206 | /** ‘g’ |
207 | Reads the general registers into an output buffer and sends it as a packet |
208 | |
209 | @param SystemContext Register content at time of the exception |
210 | **/ |
211 | VOID |
212 | EFIAPI |
213 | ReadGeneralRegisters ( |
214 | IN EFI_SYSTEM_CONTEXT SystemContext |
215 | ) |
216 | { |
217 | UINTN i; |
218 | CHAR8 OutBuffer[129]; // 16 regs, 8 hex chars each, and the end '\0' (escape seq) |
219 | CHAR8 *OutBufPtr; // pointer to the output buffer |
220 | |
221 | OutBufPtr = OutBuffer; |
222 | for(i = 0 ; i < sizeof (gRegisterOffsets)/sizeof (UINTN) ; i++) { // there are only 16 registers to read |
223 | OutBufPtr = BasicReadRegister(SystemContext, i, OutBufPtr); |
224 | } |
225 | |
226 | *OutBufPtr = '\0'; // the end of the buffer |
227 | SendPacket(OutBuffer); |
228 | } |
229 | |
230 | |
231 | /** |
232 | Adds the RegNumber-th register's value to the output buffer, starting at the given OutBufPtr |
233 | |
234 | @param SystemContext Register content at time of the exception |
235 | @param RegNumber the number of the register that we want to write |
236 | @param InBufPtr pointer to the output buffer. the new data will be extracted from the input buffer from this point on. |
237 | @retval the pointer to the next character of the input buffer that can be used |
238 | **/ |
239 | CHAR8 * |
240 | BasicWriteRegister ( |
241 | IN EFI_SYSTEM_CONTEXT SystemContext, |
242 | IN UINTN RegNumber, |
243 | IN CHAR8 *InBufPtr |
244 | ) |
245 | { |
246 | UINTN RegSize; |
247 | UINTN TempValue; // the value transferred from a hex char |
248 | UINT32 NewValue; // the new value of the RegNumber-th Register |
249 | |
250 | NewValue = 0; |
251 | RegSize = 0; |
252 | while (RegSize < REG_SIZE) { |
253 | TempValue = HexCharToInt(*InBufPtr++); |
254 | |
255 | if (TempValue < 0) { |
256 | SendError (GDB_EBADMEMDATA); |
257 | return NULL; |
258 | } |
259 | |
260 | NewValue += (TempValue << (RegSize+4)); |
261 | TempValue = HexCharToInt(*InBufPtr++); |
262 | |
263 | if (TempValue < 0) { |
264 | SendError (GDB_EBADMEMDATA); |
265 | return NULL; |
266 | } |
267 | |
268 | NewValue += (TempValue << RegSize); |
269 | RegSize = RegSize + 8; |
270 | } |
271 | *(FindPointerToRegister(SystemContext, RegNumber)) = NewValue; |
272 | return InBufPtr; |
273 | } |
274 | |
275 | |
276 | /** ‘P n...=r...’ |
277 | Writes the new value of n-th register received into the input buffer to the n-th register |
278 | |
279 | @param SystemContext Register content at time of the exception |
280 | @param InBuffer Ponter to the input buffer received from gdb server |
281 | **/ |
282 | VOID |
283 | EFIAPI |
284 | WriteNthRegister ( |
285 | IN EFI_SYSTEM_CONTEXT SystemContext, |
286 | IN CHAR8 *InBuffer |
287 | ) |
288 | { |
289 | UINTN RegNumber; |
290 | CHAR8 RegNumBuffer[MAX_REG_NUM_BUF_SIZE]; // put the 'n..' part of the message into this array |
291 | CHAR8 *RegNumBufPtr; |
292 | CHAR8 *InBufPtr; // pointer to the input buffer |
293 | |
294 | // find the register number to write |
295 | InBufPtr = &InBuffer[1]; |
296 | RegNumBufPtr = RegNumBuffer; |
297 | while (*InBufPtr != '=') { |
298 | *RegNumBufPtr++ = *InBufPtr++; |
299 | } |
300 | *RegNumBufPtr = '\0'; |
301 | RegNumber = AsciiStrHexToUintn (RegNumBuffer); |
302 | |
303 | // check if this is a valid Register Number |
304 | if ((RegNumber < 0) || (RegNumber >= sizeof (gRegisterOffsets)/sizeof (UINTN))) { |
305 | SendError (GDB_EINVALIDREGNUM); |
306 | return; |
307 | } |
308 | InBufPtr++; // skips the '=' character |
309 | BasicWriteRegister (SystemContext, RegNumber, InBufPtr); |
310 | SendSuccess(); |
311 | } |
312 | |
313 | |
314 | /** ‘G XX...’ |
315 | Writes the new values received into the input buffer to the general registers |
316 | |
317 | @param SystemContext Register content at time of the exception |
318 | @param InBuffer Pointer to the input buffer received from gdb server |
319 | **/ |
320 | VOID |
321 | EFIAPI |
322 | WriteGeneralRegisters ( |
323 | IN EFI_SYSTEM_CONTEXT SystemContext, |
324 | IN CHAR8 *InBuffer |
325 | ) |
326 | { |
327 | UINTN i; |
328 | CHAR8 *InBufPtr; /// pointer to the input buffer |
329 | |
330 | // check to see if the buffer is the right size which is |
331 | // 1 (for 'G') + 16 (for 16 registers) * 8 ( for 8 hex chars each) = 129 |
332 | if (AsciiStrLen(InBuffer) != 129) { // 16 regs, 8 hex chars each, and the end '\0' (escape seq) |
333 | //Bad message. Message is not the right length |
334 | SendError (GDB_EBADBUFSIZE); |
335 | return; |
336 | } |
337 | |
338 | InBufPtr = &InBuffer[1]; |
339 | |
340 | // Read the new values for the registers from the input buffer to an array, NewValueArray. |
341 | // The values in the array are in the gdb ordering |
342 | for(i=0; i < sizeof (gRegisterOffsets)/sizeof (UINTN); i++) { // there are only 16 registers to write |
343 | InBufPtr = BasicWriteRegister(SystemContext, i, InBufPtr); |
344 | } |
345 | |
346 | SendSuccess(); |
347 | } |
348 | |
349 | /** ‘c [addr ]’ |
350 | Continue. addr is Address to resume. If addr is omitted, resume at current |
351 | Address. |
352 | |
353 | @param SystemContext Register content at time of the exception |
354 | **/ |
355 | VOID |
356 | EFIAPI |
357 | ContinueAtAddress ( |
358 | IN EFI_SYSTEM_CONTEXT SystemContext, |
359 | IN CHAR8 *PacketData |
360 | ) |
361 | { |
362 | if (PacketData[1] != '\0') { |
363 | SystemContext.SystemContextIa32->Eip = AsciiStrHexToUintn (&PacketData[1]); |
364 | } |
365 | } |
366 | |
367 | |
368 | /** ‘s [addr ]’ |
369 | Single step. addr is the Address at which to resume. If addr is omitted, resume |
370 | at same Address. |
371 | |
372 | @param SystemContext Register content at time of the exception |
373 | **/ |
374 | VOID |
375 | EFIAPI |
376 | SingleStep ( |
377 | IN EFI_SYSTEM_CONTEXT SystemContext, |
378 | IN CHAR8 *PacketData |
379 | ) |
380 | { |
381 | SendNotSupported();\r |
382 | } |
383 | |
384 | |
385 | /** |
386 | Returns breakpoint data address from DR0-DR3 based on the input breakpoint number |
387 | |
388 | @param SystemContext Register content at time of the exception |
389 | @param BreakpointNumber Breakpoint number |
390 | |
391 | @retval Address Data address from DR0-DR3 based on the breakpoint number. |
392 | |
393 | **/ |
394 | UINTN |
395 | GetBreakpointDataAddress ( |
396 | IN EFI_SYSTEM_CONTEXT SystemContext, |
397 | IN UINTN BreakpointNumber |
398 | ) |
399 | { |
400 | UINTN Address; |
401 | |
402 | if (BreakpointNumber == 1) { |
403 | Address = SystemContext.SystemContextIa32->Dr0; |
404 | } else if (BreakpointNumber == 2) { |
405 | Address = SystemContext.SystemContextIa32->Dr1; |
406 | } else if (BreakpointNumber == 3) { |
407 | Address = SystemContext.SystemContextIa32->Dr2; |
408 | } else if (BreakpointNumber == 4) { |
409 | Address = SystemContext.SystemContextIa32->Dr3; |
410 | } else { |
411 | Address = 0; |
412 | } |
413 | |
414 | return Address; |
415 | } |
416 | |
417 | |
418 | /** |
419 | Returns currently detected breakpoint value based on the register DR6 B0-B3 field. |
420 | If no breakpoint is detected then it returns 0. |
421 | |
422 | @param SystemContext Register content at time of the exception |
423 | |
424 | @retval {1-4} Currently detected breakpoint value |
425 | @retval 0 No breakpoint detected. |
426 | |
427 | **/ |
428 | UINTN |
429 | GetBreakpointDetected ( |
430 | IN EFI_SYSTEM_CONTEXT SystemContext |
431 | ) |
432 | { |
433 | IA32_DR6 Dr6; |
434 | UINTN BreakpointNumber; |
435 | |
436 | Dr6.UintN = SystemContext.SystemContextIa32->Dr6; |
437 | |
438 | if (Dr6.Bits.B0 == 1) { |
439 | BreakpointNumber = 1; |
440 | } else if (Dr6.Bits.B1 == 1) { |
441 | BreakpointNumber = 2; |
442 | } else if (Dr6.Bits.B2 == 1) { |
443 | BreakpointNumber = 3; |
444 | } else if (Dr6.Bits.B3 == 1) { |
445 | BreakpointNumber = 4; |
446 | } else { |
447 | BreakpointNumber = 0; //No breakpoint detected |
448 | } |
449 | |
450 | return BreakpointNumber; |
451 | } |
452 | |
453 | |
454 | /** |
455 | Returns Breakpoint type (InstructionExecution, DataWrite, DataRead or DataReadWrite) |
456 | based on the Breakpoint number |
457 | |
458 | @param SystemContext Register content at time of the exception |
459 | @param BreakpointNumber Breakpoint number |
460 | |
461 | @retval BREAK_TYPE Breakpoint type value read from register DR7 RWn field |
462 | For unknown value, it returns NotSupported. |
463 | |
464 | **/ |
465 | BREAK_TYPE |
466 | GetBreakpointType ( |
467 | IN EFI_SYSTEM_CONTEXT SystemContext, |
468 | IN UINTN BreakpointNumber |
469 | ) |
470 | { |
471 | IA32_DR7 Dr7; |
472 | BREAK_TYPE Type = NotSupported; //Default is NotSupported type |
473 | |
474 | Dr7.UintN = SystemContext.SystemContextIa32->Dr7; |
475 | |
476 | if (BreakpointNumber == 1) { |
477 | Type = (BREAK_TYPE) Dr7.Bits.RW0; |
478 | } else if (BreakpointNumber == 2) { |
479 | Type = (BREAK_TYPE) Dr7.Bits.RW1; |
480 | } else if (BreakpointNumber == 3) { |
481 | Type = (BREAK_TYPE) Dr7.Bits.RW2; |
482 | } else if (BreakpointNumber == 4) { |
483 | Type = (BREAK_TYPE) Dr7.Bits.RW3; |
484 | } |
485 | |
486 | return Type; |
487 | } |
488 | |
489 | |
490 | /** |
491 | Parses Length and returns the length which DR7 LENn field accepts. |
492 | For example: If we receive 1-Byte length then we should return 0. |
493 | Zero gets written to DR7 LENn field. |
494 | |
495 | @param Length Breakpoint length in Bytes (1 byte, 2 byte, 4 byte) |
496 | |
497 | @retval Length Appropriate converted values which DR7 LENn field accepts. |
498 | |
499 | **/ |
500 | UINTN |
501 | ConvertLengthData ( |
502 | IN UINTN Length |
503 | ) |
504 | { |
505 | if (Length == 1) { //1-Byte length |
506 | return 0; |
507 | } else if (Length == 2) { //2-Byte length |
508 | return 1; |
509 | } else if (Length == 4) { //4-Byte length |
510 | return 3; |
511 | } else { //Undefined or 8-byte length |
512 | return 2; |
513 | } |
514 | } |
515 | |
516 | |
517 | /** |
518 | Finds the next free debug register. If all the registers are occupied then |
519 | EFI_OUT_OF_RESOURCES is returned. |
520 | |
521 | @param SystemContext Register content at time of the exception |
522 | @param Register Register value (0 - 3 for the first free debug register) |
523 | |
524 | @retval EFI_STATUS Appropriate status value. |
525 | |
526 | **/ |
527 | EFI_STATUS |
528 | FindNextFreeDebugRegister ( |
529 | IN EFI_SYSTEM_CONTEXT SystemContext, |
530 | OUT UINTN *Register |
531 | ) |
532 | { |
533 | IA32_DR7 Dr7; |
534 | |
535 | Dr7.UintN = SystemContext.SystemContextIa32->Dr7; |
536 | |
537 | if (Dr7.Bits.G0 == 0) { |
538 | *Register = 0; |
539 | } else if (Dr7.Bits.G1 == 0) { |
540 | *Register = 1; |
541 | } else if (Dr7.Bits.G2 == 0) { |
542 | *Register = 2; |
543 | } else if (Dr7.Bits.G3 == 0) { |
544 | *Register = 3; |
545 | } else { |
546 | return EFI_OUT_OF_RESOURCES; |
547 | } |
548 | |
549 | return EFI_SUCCESS; |
550 | } |
551 | |
552 | |
553 | /** |
554 | Enables the debug register. Writes Address value to appropriate DR0-3 register. |
555 | Sets LENn, Gn, RWn bits in DR7 register. |
556 | |
557 | @param SystemContext Register content at time of the exception |
558 | @param Register Register value (0 - 3) |
559 | @param Address Breakpoint address value |
560 | @param Type Breakpoint type (Instruction, Data write, Data read |
561 | or write etc.) |
562 | |
563 | @retval EFI_STATUS Appropriate status value. |
564 | |
565 | **/ |
566 | EFI_STATUS |
567 | EnableDebugRegister ( |
568 | IN EFI_SYSTEM_CONTEXT SystemContext, |
569 | IN UINTN Register, |
570 | IN UINTN Address, |
571 | IN UINTN Length, |
572 | IN UINTN Type |
573 | ) |
574 | { |
575 | IA32_DR7 Dr7; |
576 | |
577 | //Convert length data |
578 | Length = ConvertLengthData (Length); |
579 | |
580 | //For Instruction execution, length should be 0 |
581 | //(Ref. Intel reference manual 18.2.4) |
582 | if ((Type == 0) && (Length != 0)) { |
583 | return EFI_INVALID_PARAMETER; |
584 | } |
585 | |
586 | //Hardware doesn't support ReadWatch (z3 packet) type. GDB can handle |
587 | //software breakpoint. We should send empty packet in both these cases. |
588 | if ((Type == (BREAK_TYPE)DataRead) || |
589 | (Type == (BREAK_TYPE)SoftwareBreakpoint)) { |
590 | return EFI_UNSUPPORTED; |
591 | } |
592 | |
593 | //Read DR7 so appropriate Gn, RWn and LENn bits can be modified. |
594 | Dr7.UintN = SystemContext.SystemContextIa32->Dr7; |
595 | |
596 | if (Register == 0) { |
597 | SystemContext.SystemContextIa32->Dr0 = Address; |
598 | Dr7.Bits.G0 = 1; |
599 | Dr7.Bits.RW0 = Type; |
600 | Dr7.Bits.LEN0 = Length; |
601 | } else if (Register == 1) { |
602 | SystemContext.SystemContextIa32->Dr1 = Address; |
603 | Dr7.Bits.G1 = 1; |
604 | Dr7.Bits.RW1 = Type; |
605 | Dr7.Bits.LEN1 = Length; |
606 | } else if (Register == 2) { |
607 | SystemContext.SystemContextIa32->Dr2 = Address; |
608 | Dr7.Bits.G2 = 1; |
609 | Dr7.Bits.RW2 = Type; |
610 | Dr7.Bits.LEN2 = Length; |
611 | } else if (Register == 3) { |
612 | SystemContext.SystemContextIa32->Dr3 = Address; |
613 | Dr7.Bits.G3 = 1; |
614 | Dr7.Bits.RW3 = Type; |
615 | Dr7.Bits.LEN3 = Length; |
616 | } else { |
617 | return EFI_INVALID_PARAMETER; |
618 | } |
619 | |
620 | //Update Dr7 with appropriate Gn, RWn and LENn bits |
621 | SystemContext.SystemContextIa32->Dr7 = Dr7.UintN; |
622 | |
623 | return EFI_SUCCESS; |
624 | } |
625 | |
626 | |
627 | /** |
628 | Returns register number 0 - 3 for the maching debug register. |
629 | This function compares incoming Address, Type, Length and |
630 | if there is a match then it returns the appropriate register number. |
631 | In case of mismatch, function returns EFI_NOT_FOUND message. |
632 | |
633 | @param SystemContext Register content at time of the exception |
634 | @param Address Breakpoint address value |
635 | @param Length Breakpoint length value |
636 | @param Type Breakpoint type (Instruction, Data write, |
637 | Data read or write etc.) |
638 | @param Register Register value to be returned |
639 | |
640 | @retval EFI_STATUS Appropriate status value. |
641 | |
642 | **/ |
643 | EFI_STATUS |
644 | FindMatchingDebugRegister ( |
645 | IN EFI_SYSTEM_CONTEXT SystemContext, |
646 | IN UINTN Address, |
647 | IN UINTN Length, |
648 | IN UINTN Type, |
649 | OUT UINTN *Register |
650 | ) |
651 | { |
652 | IA32_DR7 Dr7; |
653 | |
654 | //Hardware doesn't support ReadWatch (z3 packet) type. GDB can handle |
655 | //software breakpoint. We should send empty packet in both these cases. |
656 | if ((Type == (BREAK_TYPE)DataRead) || |
657 | (Type == (BREAK_TYPE)SoftwareBreakpoint)) { |
658 | return EFI_UNSUPPORTED; |
659 | } |
660 | |
661 | //Convert length data |
662 | Length = ConvertLengthData(Length); |
663 | |
664 | Dr7.UintN = SystemContext.SystemContextIa32->Dr7; |
665 | |
666 | if ((Dr7.Bits.G0 == 1) && |
667 | (Dr7.Bits.LEN0 == Length) && |
668 | (Dr7.Bits.RW0 == Type) && |
669 | (Address == SystemContext.SystemContextIa32->Dr0)) { |
670 | *Register = 0; |
671 | } else if ((Dr7.Bits.G1 == 1) && |
672 | (Dr7.Bits.LEN1 == Length) && |
673 | (Dr7.Bits.RW1 == Type) && |
674 | (Address == SystemContext.SystemContextIa32->Dr1)) { |
675 | *Register = 1; |
676 | } else if ((Dr7.Bits.G2 == 1) && |
677 | (Dr7.Bits.LEN2 == Length) && |
678 | (Dr7.Bits.RW2 == Type) && |
679 | (Address == SystemContext.SystemContextIa32->Dr2)) { |
680 | *Register = 2; |
681 | } else if ((Dr7.Bits.G3 == 1) && |
682 | (Dr7.Bits.LEN3 == Length) && |
683 | (Dr7.Bits.RW3 == Type) && |
684 | (Address == SystemContext.SystemContextIa32->Dr3)) { |
685 | *Register = 3; |
686 | } else { |
687 | Print ((CHAR16 *)L"No match found..\n"); |
688 | return EFI_NOT_FOUND; |
689 | } |
690 | |
691 | return EFI_SUCCESS; |
692 | } |
693 | |
694 | |
695 | /** |
696 | Disables the particular debug register. |
697 | |
698 | @param SystemContext Register content at time of the exception |
699 | @param Register Register to be disabled |
700 | |
701 | @retval EFI_STATUS Appropriate status value. |
702 | |
703 | **/ |
704 | EFI_STATUS |
705 | DisableDebugRegister ( |
706 | IN EFI_SYSTEM_CONTEXT SystemContext, |
707 | IN UINTN Register |
708 | ) |
709 | { |
710 | IA32_DR7 Dr7; |
711 | UINTN Address = 0; |
712 | |
713 | //Read DR7 register so appropriate Gn, RWn and LENn bits can be turned off. |
714 | Dr7.UintN = SystemContext.SystemContextIa32->Dr7; |
715 | |
716 | if (Register == 0) { |
717 | SystemContext.SystemContextIa32->Dr0 = Address; |
718 | Dr7.Bits.G0 = 0; |
719 | Dr7.Bits.RW0 = 0; |
720 | Dr7.Bits.LEN0 = 0; |
721 | } else if (Register == 1) { |
722 | SystemContext.SystemContextIa32->Dr1 = Address; |
723 | Dr7.Bits.G1 = 0; |
724 | Dr7.Bits.RW1 = 0; |
725 | Dr7.Bits.LEN1 = 0; |
726 | } else if (Register == 2) { |
727 | SystemContext.SystemContextIa32->Dr2 = Address; |
728 | Dr7.Bits.G2 = 0; |
729 | Dr7.Bits.RW2 = 0; |
730 | Dr7.Bits.LEN2 = 0; |
731 | } else if (Register == 3) { |
732 | SystemContext.SystemContextIa32->Dr3 = Address; |
733 | Dr7.Bits.G3 = 0; |
734 | Dr7.Bits.RW3 = 0; |
735 | Dr7.Bits.LEN3 = 0; |
736 | } else { |
737 | return EFI_INVALID_PARAMETER; |
738 | } |
739 | |
740 | //Update DR7 register so appropriate Gn, RWn and LENn bits can be turned off. |
741 | SystemContext.SystemContextIa32->Dr7 = Dr7.UintN; |
742 | |
743 | return EFI_SUCCESS; |
744 | } |
745 | |
746 | |
747 | /** |
748 | ‘Z1, [addr], [length]’ |
749 | ‘Z2, [addr], [length]’ |
750 | ‘Z3, [addr], [length]’ |
751 | ‘Z4, [addr], [length]’ |
752 | |
753 | Insert hardware breakpoint/watchpoint at address addr of size length |
754 | |
755 | @param SystemContext Register content at time of the exception |
756 | @param *PacketData Pointer to the Payload data for the packet |
757 | |
758 | **/ |
759 | VOID |
760 | EFIAPI |
761 | InsertBreakPoint ( |
762 | IN EFI_SYSTEM_CONTEXT SystemContext, |
763 | IN CHAR8 *PacketData |
764 | ) |
765 | { |
766 | UINTN Type; |
767 | UINTN Address; |
768 | UINTN Length; |
769 | UINTN Register; |
770 | EFI_STATUS Status; |
771 | BREAK_TYPE BreakType = NotSupported; |
772 | UINTN ErrorCode; |
773 | |
774 | ErrorCode = ParseBreakpointPacket (PacketData, &Type, &Address, &Length); |
775 | if (ErrorCode > 0) { |
776 | SendError ((UINT8)ErrorCode); |
777 | return; |
778 | } |
779 | |
780 | switch (Type) { |
781 | |
782 | case 0: //Software breakpoint |
783 | BreakType = SoftwareBreakpoint; |
784 | break; |
785 | |
786 | case 1: //Hardware breakpoint |
787 | BreakType = InstructionExecution; |
788 | break; |
789 | |
790 | case 2: //Write watchpoint |
791 | BreakType = DataWrite; |
792 | break; |
793 | |
794 | case 3: //Read watchpoint |
795 | BreakType = DataRead; |
796 | break; |
797 | |
798 | case 4: //Access watchpoint |
799 | BreakType = DataReadWrite; |
800 | break; |
801 | |
802 | default : |
803 | Print ((CHAR16 *)L"Insert breakpoint default: %x\n", Type); |
804 | SendError (GDB_EINVALIDBRKPOINTTYPE); |
805 | return; |
806 | } |
807 | |
808 | // Find next free debug register |
809 | Status = FindNextFreeDebugRegister (SystemContext, &Register); |
810 | if (EFI_ERROR(Status)) { |
811 | Print ((CHAR16 *)L"No space left on device\n"); |
812 | SendError (GDB_ENOSPACE); |
813 | return; |
814 | } |
815 | |
816 | // Write Address, length data at particular DR register |
817 | Status = EnableDebugRegister (SystemContext, Register, Address, Length, (UINTN)BreakType); |
818 | if (EFI_ERROR(Status)) { |
819 | if (Status == EFI_UNSUPPORTED) { |
820 | SendNotSupported(); |
821 | return; |
822 | } |
823 | |
824 | SendError (GDB_EINVALIDARG); |
825 | return; |
826 | } |
827 | |
828 | SendSuccess (); |
829 | } |
830 | |
831 | |
832 | /** |
833 | ‘z1, [addr], [length]’ |
834 | ‘z2, [addr], [length]’ |
835 | ‘z3, [addr], [length]’ |
836 | ‘z4, [addr], [length]’ |
837 | |
838 | Remove hardware breakpoint/watchpoint at address addr of size length |
839 | |
840 | @param *PacketData Pointer to the Payload data for the packet |
841 | |
842 | **/ |
843 | VOID |
844 | EFIAPI |
845 | RemoveBreakPoint ( |
846 | IN EFI_SYSTEM_CONTEXT SystemContext, |
847 | IN CHAR8 *PacketData |
848 | ) |
849 | { |
850 | UINTN Type; |
851 | UINTN Address; |
852 | UINTN Length; |
853 | UINTN Register; |
854 | BREAK_TYPE BreakType = NotSupported; |
855 | EFI_STATUS Status; |
856 | UINTN ErrorCode; |
857 | |
858 | //Parse breakpoint packet data |
859 | ErrorCode = ParseBreakpointPacket (PacketData, &Type, &Address, &Length); |
860 | if (ErrorCode > 0) { |
861 | SendError ((UINT8)ErrorCode); |
862 | return; |
863 | } |
864 | |
865 | switch (Type) { |
866 | |
867 | case 0: //Software breakpoint |
868 | BreakType = SoftwareBreakpoint; |
869 | break; |
870 | |
871 | case 1: //Hardware breakpoint |
872 | BreakType = InstructionExecution; |
873 | break; |
874 | |
875 | case 2: //Write watchpoint |
876 | BreakType = DataWrite; |
877 | break; |
878 | |
879 | case 3: //Read watchpoint |
880 | BreakType = DataRead; |
881 | break; |
882 | |
883 | case 4: //Access watchpoint |
884 | BreakType = DataReadWrite; |
885 | break; |
886 | |
887 | default : |
888 | SendError (GDB_EINVALIDBRKPOINTTYPE); |
889 | return; |
890 | } |
891 | |
892 | //Find matching debug register |
893 | Status = FindMatchingDebugRegister (SystemContext, Address, Length, (UINTN)BreakType, &Register); |
894 | if (EFI_ERROR(Status)) { |
895 | if (Status == EFI_UNSUPPORTED) { |
896 | SendNotSupported(); |
897 | return; |
898 | } |
899 | |
900 | SendError (GDB_ENOSPACE); |
901 | return; |
902 | } |
903 | |
904 | //Remove breakpoint |
905 | Status = DisableDebugRegister(SystemContext, Register); |
906 | if (EFI_ERROR(Status)) { |
907 | SendError (GDB_EINVALIDARG); |
908 | return; |
909 | } |
910 | |
911 | SendSuccess (); |
912 | } |
913 | |
914 | |
915 | /**\r |
916 | Initialize debug agent.\r |
917 | \r |
918 | This function is used to set up debug enviroment. It may enable interrupts.\r |
919 | \r |
920 | @param[in] InitFlag Init flag is used to decide initialize process.\r |
921 | @param[in] Context Context needed according to InitFlag, it was optional.\r |
922 | \r |
923 | **/\r |
924 | VOID\r |
925 | EFIAPI\r |
926 | InitializeDebugAgent (\r |
927 | IN UINT32 InitFlag,\r |
928 | IN VOID *Context OPTIONAL\r |
929 | )\r |
930 | {\r |
931 | // BugBug: Add the code to build an GDT/IDT\r |
932 | }\r |
933 | \r |