VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGCGdbRemoteStub.cpp@ 84694

Last change on this file since 84694 was 84694, checked in by vboxsync, 4 years ago

Debugger/DBGCGdbRemoteStub: Track registered trace points to allow removal later on, bugref:5217

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 88.2 KB
Line 
1/* $Id: DBGCGdbRemoteStub.cpp 84694 2020-06-05 12:55:11Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, GDB Remote Stub.
4 */
5
6/*
7 * Copyright (C) 2010-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18
19/*********************************************************************************************************************************
20* Header Files *
21*********************************************************************************************************************************/
22#include <VBox/dbg.h>
23#include <VBox/vmm/dbgf.h>
24#include <VBox/vmm/vmapi.h> /* VMR3GetVM() */
25#include <VBox/vmm/hm.h> /* HMR3IsEnabled */
26#include <VBox/vmm/nem.h> /* NEMR3IsEnabled */
27#include <iprt/cdefs.h>
28#include <iprt/err.h>
29#include <iprt/list.h>
30#include <iprt/mem.h>
31#include <iprt/string.h>
32
33#include <stdlib.h>
34
35#include "DBGCInternal.h"
36
37
38/*********************************************************************************************************************************
39* Defined Constants And Macros *
40*********************************************************************************************************************************/
41
42/** Character indicating the start of a packet. */
43#define GDBSTUB_PKT_START '$'
44/** Character indicating the end of a packet (excluding the checksum). */
45#define GDBSTUB_PKT_END '#'
46/** The escape character. */
47#define GDBSTUB_PKT_ESCAPE '{'
48/** The out-of-band interrupt character. */
49#define GDBSTUB_OOB_INTERRUPT 0x03
50
51
52/** Indicate support for the 'qXfer:features:read' packet to support the target description. */
53#define GDBSTUBCTX_FEATURES_F_TGT_DESC RT_BIT(0)
54
55
56/*********************************************************************************************************************************
57* Structures and Typedefs *
58*********************************************************************************************************************************/
59
60/**
61 * Trace point type.
62 */
63typedef enum GDBSTUBTPTYPE
64{
65 /** Invalid type, do not use. */
66 GDBSTUBTPTYPE_INVALID = 0,
67 /** An instruction software trace point. */
68 GDBSTUBTPTYPE_EXEC_SW,
69 /** An instruction hardware trace point. */
70 GDBSTUBTPTYPE_EXEC_HW,
71 /** A memory read trace point. */
72 GDBSTUBTPTYPE_MEM_READ,
73 /** A memory write trace point. */
74 GDBSTUBTPTYPE_MEM_WRITE,
75 /** A memory access trace point. */
76 GDBSTUBTPTYPE_MEM_ACCESS,
77 /** 32bit hack. */
78 GDBSTUBTPTYPE_32BIT_HACK = 0x7fffffff
79} GDBSTUBTPTYPE;
80
81
82/**
83 * GDB stub receive state.
84 */
85typedef enum GDBSTUBRECVSTATE
86{
87 /** Invalid state. */
88 GDBSTUBRECVSTATE_INVALID = 0,
89 /** Waiting for the start character. */
90 GDBSTUBRECVSTATE_PACKET_WAIT_FOR_START,
91 /** Reiceiving the packet body up until the END character. */
92 GDBSTUBRECVSTATE_PACKET_RECEIVE_BODY,
93 /** Receiving the checksum. */
94 GDBSTUBRECVSTATE_PACKET_RECEIVE_CHECKSUM,
95 /** Blow up the enum to 32bits for easier alignment of members in structs. */
96 GDBSTUBRECVSTATE_32BIT_HACK = 0x7fffffff
97} GDBSTUBRECVSTATE;
98
99
100/**
101 * A tracepoint descriptor.
102 */
103typedef struct GDBSTUBTP
104{
105 /** List node for the list of tracepoints. */
106 RTLISTNODE NdTps;
107 /** The breakpoint number from the DBGF API. */
108 uint32_t iBp;
109 /** The tracepoint type for identification. */
110 GDBSTUBTPTYPE enmTpType;
111 /** The tracepoint address for identification. */
112 uint64_t GdbTgtAddr;
113 /** The tracepoint kind for identification. */
114 uint64_t uKind;
115} GDBSTUBTP;
116/** Pointer to a tracepoint. */
117typedef GDBSTUBTP *PGDBSTUBTP;
118
119
120/**
121 * GDB stub context data.
122 */
123typedef struct GDBSTUBCTX
124{
125 /** Internal debugger console data. */
126 DBGC Dbgc;
127 /** The current state when receiving a new packet. */
128 GDBSTUBRECVSTATE enmState;
129 /** Maximum number of bytes the packet buffer can hold. */
130 size_t cbPktBufMax;
131 /** Current offset into the packet buffer. */
132 size_t offPktBuf;
133 /** The size of the packet (minus the start, end characters and the checksum). */
134 size_t cbPkt;
135 /** Pointer to the packet buffer data. */
136 uint8_t *pbPktBuf;
137 /** Number of bytes left for the checksum. */
138 size_t cbChksumRecvLeft;
139 /** Send packet checksum. */
140 uint8_t uChkSumSend;
141 /** Feature flags supported we negotiated with the remote end. */
142 uint32_t fFeatures;
143 /** Pointer to the XML target description. */
144 char *pachTgtXmlDesc;
145 /** Size of the XML target description. */
146 size_t cbTgtXmlDesc;
147 /** Flag whether the stub is in extended mode. */
148 bool fExtendedMode;
149 /** Flag whether was something was output using the 'O' packet since it was reset last. */
150 bool fOutput;
151 /** List of registered trace points.
152 * GDB removes breakpoints/watchpoints using the parameters they were
153 * registered with while we only use the BP number form DBGF internally.
154 * Means we have to track all registration so we can remove them later on. */
155 RTLISTANCHOR LstTps;
156} GDBSTUBCTX;
157/** Pointer to the GDB stub context data. */
158typedef GDBSTUBCTX *PGDBSTUBCTX;
159/** Pointer to const GDB stub context data. */
160typedef const GDBSTUBCTX *PCGDBSTUBCTX;
161/** Pointer to a GDB stub context data pointer. */
162typedef PGDBSTUBCTX *PPGDBSTUBCTX;
163
164
165/**
166 * Specific query packet processor callback.
167 *
168 * @returns Status code.
169 * @param pThis The GDB stub context.
170 * @param pbVal Pointer to the remaining value.
171 * @param cbVal Size of the remaining value in bytes.
172 */
173typedef DECLCALLBACK(int) FNGDBSTUBQPKTPROC(PGDBSTUBCTX pThis, const uint8_t *pbVal, size_t cbVal);
174typedef FNGDBSTUBQPKTPROC *PFNGDBSTUBQPKTPROC;
175
176
177/**
178 * 'q' packet processor.
179 */
180typedef struct GDBSTUBQPKTPROC
181{
182 /** Name */
183 const char *pszName;
184 /** Length of name in characters (without \0 terminator). */
185 uint32_t cchName;
186 /** The callback to call for processing the particular query. */
187 PFNGDBSTUBQPKTPROC pfnProc;
188} GDBSTUBQPKTPROC;
189/** Pointer to a 'q' packet processor entry. */
190typedef GDBSTUBQPKTPROC *PGDBSTUBQPKTPROC;
191/** Pointer to a const 'q' packet processor entry. */
192typedef const GDBSTUBQPKTPROC *PCGDBSTUBQPKTPROC;
193
194
195/**
196 * 'v' packet processor.
197 */
198typedef struct GDBSTUBVPKTPROC
199{
200 /** Name */
201 const char *pszName;
202 /** Length of name in characters (without \0 terminator). */
203 uint32_t cchName;
204 /** Replay to a query packet (ends with ?). */
205 const char *pszReplyQ;
206 /** Length of the query reply (without \0 terminator). */
207 uint32_t cchReplyQ;
208 /** The callback to call for processing the particular query. */
209 PFNGDBSTUBQPKTPROC pfnProc;
210} GDBSTUBVPKTPROC;
211/** Pointer to a 'q' packet processor entry. */
212typedef GDBSTUBVPKTPROC *PGDBSTUBVPKTPROC;
213/** Pointer to a const 'q' packet processor entry. */
214typedef const GDBSTUBVPKTPROC *PCGDBSTUBVPKTPROC;
215
216
217/**
218 * Feature callback.
219 *
220 * @returns Status code.
221 * @param pThis The GDB stub context.
222 * @param pbVal Pointer to the value.
223 * @param cbVal Size of the value in bytes.
224 */
225typedef DECLCALLBACK(int) FNGDBSTUBFEATHND(PGDBSTUBCTX pThis, const uint8_t *pbVal, size_t cbVal);
226typedef FNGDBSTUBFEATHND *PFNGDBSTUBFEATHND;
227
228
229/**
230 * GDB feature descriptor.
231 */
232typedef struct GDBSTUBFEATDESC
233{
234 /** Feature name */
235 const char *pszName;
236 /** Length of the feature name in characters (without \0 terminator). */
237 uint32_t cchName;
238 /** The callback to call for processing the particular feature. */
239 PFNGDBSTUBFEATHND pfnHandler;
240 /** Flag whether the feature requires a value. */
241 bool fVal;
242} GDBSTUBFEATDESC;
243/** Pointer to a GDB feature descriptor. */
244typedef GDBSTUBFEATDESC *PGDBSTUBFEATDESC;
245/** Pointer to a const GDB feature descriptor. */
246typedef const GDBSTUBFEATDESC *PCGDBSTUBFEATDESC;
247
248
249/*********************************************************************************************************************************
250* Internal Functions *
251*********************************************************************************************************************************/
252
253
254/**
255 * Tries to find a trace point with the given parameters in the list of registered trace points.
256 *
257 * @returns Pointer to the trace point registration record if found or NULL if none was found.
258 * @param pThis The GDB stub context.
259 * @param enmTpType The trace point type.
260 * @param GdbTgtAddr Target address given by GDB.
261 * @param uKind Trace point kind.
262 */
263static PGDBSTUBTP dbgcGdbStubTpFind(PGDBSTUBCTX pThis, GDBSTUBTPTYPE enmTpType, uint64_t GdbTgtAddr, uint64_t uKind)
264{
265 PGDBSTUBTP pTpCur = NULL;
266 RTListForEach(&pThis->LstTps, pTpCur, GDBSTUBTP, NdTps)
267 {
268 if ( pTpCur->enmTpType == enmTpType
269 && pTpCur->GdbTgtAddr == GdbTgtAddr
270 && pTpCur->uKind == uKind)
271 return pTpCur;
272 }
273
274 return NULL;
275}
276
277
278/**
279 * Registers a new trace point.
280 *
281 * @returns VBox status code.
282 * @param pThis The GDB stub context.
283 * @param enmTpType The trace point type.
284 * @param GdbTgtAddr Target address given by GDB.
285 * @param uKind Trace point kind.
286 * @param iBp The internal DBGF breakpoint ID this trace point was registered with.
287 */
288static int dbgcGdbStubTpRegister(PGDBSTUBCTX pThis, GDBSTUBTPTYPE enmTpType, uint64_t GdbTgtAddr, uint64_t uKind, uint32_t iBp)
289{
290 int rc = VERR_ALREADY_EXISTS;
291
292 /* Can't register a tracepoint with the same parameters twice or we can't decide whom to remove later on. */
293 PGDBSTUBTP pTp = dbgcGdbStubTpFind(pThis, enmTpType, GdbTgtAddr, uKind);
294 if (!pTp)
295 {
296 pTp = (PGDBSTUBTP)RTMemAllocZ(sizeof(*pTp));
297 if (pTp)
298 {
299 pTp->enmTpType = enmTpType;
300 pTp->GdbTgtAddr = GdbTgtAddr;
301 pTp->uKind = uKind;
302 pTp->iBp = iBp;
303 RTListAppend(&pThis->LstTps, &pTp->NdTps);
304 rc = VINF_SUCCESS;
305 }
306 else
307 rc = VERR_NO_MEMORY;
308 }
309
310 return rc;
311}
312
313
314/**
315 * Deregisters the given trace point (needs to be unregistered from DBGF by the caller before).
316 *
317 * @returns nothing.
318 * @param pTp The trace point to deregister.
319 */
320static void dbgcGdbStubTpDeregister(PGDBSTUBTP pTp)
321{
322 RTListNodeRemove(&pTp->NdTps);
323 RTMemFree(pTp);
324}
325
326
327/**
328 * Converts a given to the hexadecimal value if valid.
329 *
330 * @returns The hexadecimal value the given character represents 0-9,a-f,A-F or 0xff on error.
331 * @param ch The character to convert.
332 */
333DECLINLINE(uint8_t) dbgcGdbStubCtxChrToHex(char ch)
334{
335 if (ch >= '0' && ch <= '9')
336 return ch - '0';
337 if (ch >= 'A' && ch <= 'F')
338 return ch - 'A' + 0xa;
339 if (ch >= 'a' && ch <= 'f')
340 return ch - 'a' + 0xa;
341
342 return 0xff;
343}
344
345
346/**
347 * Converts a 4bit hex number to the appropriate character.
348 *
349 * @returns Character representing the 4bit hex number.
350 * @param uHex The 4 bit hex number.
351 */
352DECLINLINE(char) dbgcGdbStubCtxHexToChr(uint8_t uHex)
353{
354 if (uHex < 0xa)
355 return '0' + uHex;
356 if (uHex <= 0xf)
357 return 'A' + uHex - 0xa;
358
359 return 'X';
360}
361
362
363/**
364 * Wrapper for the I/O interface write callback.
365 *
366 * @returns Status code.
367 * @param pThis The GDB stub context.
368 * @param pvPkt The packet data to send.
369 * @param cbPkt Size of the packet in bytes.
370 */
371DECLINLINE(int) dbgcGdbStubCtxWrite(PGDBSTUBCTX pThis, const void *pvPkt, size_t cbPkt)
372{
373 return pThis->Dbgc.pBack->pfnWrite(pThis->Dbgc.pBack, pvPkt, cbPkt, NULL /*pcbWritten*/);
374}
375
376
377/**
378 * Starts transmission of a new reply packet.
379 *
380 * @returns Status code.
381 * @param pThis The GDB stub context.
382 */
383static int dbgcGdbStubCtxReplySendBegin(PGDBSTUBCTX pThis)
384{
385 pThis->uChkSumSend = 0;
386
387 uint8_t chPktStart = GDBSTUB_PKT_START;
388 return dbgcGdbStubCtxWrite(pThis, &chPktStart, sizeof(chPktStart));
389}
390
391
392/**
393 * Sends the given data in the reply.
394 *
395 * @returns Status code.
396 * @param pThis The GDB stub context.
397 * @param pvReplyData The reply data to send.
398 * @param cbReplyData Size of the reply data in bytes.
399 */
400static int dbgcGdbStubCtxReplySendData(PGDBSTUBCTX pThis, const void *pvReplyData, size_t cbReplyData)
401{
402 /* Update checksum. */
403 const uint8_t *pbData = (const uint8_t *)pvReplyData;
404 for (uint32_t i = 0; i < cbReplyData; i++)
405 pThis->uChkSumSend += pbData[i];
406
407 return dbgcGdbStubCtxWrite(pThis, pvReplyData, cbReplyData);
408}
409
410
411/**
412 * Finishes transmission of the current reply by sending the packet end character and the checksum.
413 *
414 * @returns Status code.
415 * @param pThis The GDB stub context.
416 */
417static int dbgcGdbStubCtxReplySendEnd(PGDBSTUBCTX pThis)
418{
419 uint8_t achPktEnd[3];
420
421 achPktEnd[0] = GDBSTUB_PKT_END;
422 achPktEnd[1] = dbgcGdbStubCtxHexToChr(pThis->uChkSumSend >> 4);
423 achPktEnd[2] = dbgcGdbStubCtxHexToChr(pThis->uChkSumSend & 0xf);
424
425 return dbgcGdbStubCtxWrite(pThis, &achPktEnd[0], sizeof(achPktEnd));
426}
427
428
429/**
430 * Sends the given reply packet, doing the framing, checksumming, etc. in one call.
431 *
432 * @returns Status code.
433 * @param pThis The GDB stub context.
434 * @param pvReplyPkt The reply packet to send.
435 * @param cbReplyPkt Size of the reply packet in bytes.
436 */
437static int dbgcGdbStubCtxReplySend(PGDBSTUBCTX pThis, const void *pvReplyPkt, size_t cbReplyPkt)
438{
439 int rc = dbgcGdbStubCtxReplySendBegin(pThis);
440 if (RT_SUCCESS(rc))
441 {
442 rc = dbgcGdbStubCtxReplySendData(pThis, pvReplyPkt, cbReplyPkt);
443 if (RT_SUCCESS(rc))
444 rc = dbgcGdbStubCtxReplySendEnd(pThis);
445 }
446
447 return rc;
448}
449
450
451/**
452 * Encodes the given buffer as a hexstring string it into the given destination buffer.
453 *
454 * @returns Status code.
455 * @param pbDst Where store the resulting hex string on success.
456 * @param cbDst Size of the destination buffer in bytes.
457 * @param pvSrc The data to encode.
458 * @param cbSrc Number of bytes to encode.
459 */
460DECLINLINE(int) dbgcGdbStubCtxEncodeBinaryAsHex(uint8_t *pbDst, size_t cbDst, const void *pvSrc, size_t cbSrc)
461{
462 return RTStrPrintHexBytes((char *)pbDst, cbDst, pvSrc, cbSrc, RTSTRPRINTHEXBYTES_F_UPPER);
463}
464
465
466/**
467 * Decodes the given ASCII hexstring as binary data up until the given separator is found or the end of the string is reached.
468 *
469 * @returns Status code.
470 * @param pbBuf The buffer containing the hexstring to convert.
471 * @param cbBuf Size of the buffer in bytes.
472 * @param puVal Where to store the decoded integer.
473 * @param chSep The character to stop conversion at.
474 * @param ppbSep Where to store the pointer in the buffer where the separator was found, optional.
475 */
476static int dbgcGdbStubCtxParseHexStringAsInteger(const uint8_t *pbBuf, size_t cbBuf, uint64_t *puVal, uint8_t chSep, const uint8_t **ppbSep)
477{
478 uint64_t uVal = 0;
479
480 while ( cbBuf
481 && *pbBuf != chSep)
482 {
483 uVal = uVal * 16 + dbgcGdbStubCtxChrToHex(*pbBuf++);
484 cbBuf--;
485 }
486
487 *puVal = uVal;
488
489 if (ppbSep)
490 *ppbSep = pbBuf;
491
492 return VINF_SUCCESS;
493}
494
495
496/**
497 * Decodes the given ASCII hexstring as a byte buffer up until the given separator is found or the end of the string is reached.
498 *
499 * @returns Status code.
500 * @param pbBuf The buffer containing the hexstring to convert.
501 * @param cbBuf Size of the buffer in bytes.
502 * @param pvDst Where to store the decoded data.
503 * @param cbDst Maximum buffer size in bytes.
504 * @param pcbDecoded Where to store the number of consumed bytes from the input.
505 */
506DECLINLINE(int) dbgcGdbStubCtxParseHexStringAsByteBuf(const uint8_t *pbBuf, size_t cbBuf, void *pvDst, size_t cbDst, size_t *pcbDecoded)
507{
508 size_t cbDecode = RT_MIN(cbBuf, cbDst * 2);
509
510 if (pcbDecoded)
511 *pcbDecoded = cbDecode;
512
513 return RTStrConvertHexBytes((const char *)pbBuf, pvDst, cbDecode, 0 /* fFlags*/);
514}
515
516#if 0 /*unused for now*/
517/**
518 * Sends a 'OK' part of a reply packet only (packet start and end needs to be handled separately).
519 *
520 * @returns Status code.
521 * @param pThis The GDB stub context.
522 */
523static int dbgcGdbStubCtxReplySendOkData(PGDBSTUBCTX pThis)
524{
525 char achOk[2] = { 'O', 'K' };
526 return dbgcGdbStubCtxReplySendData(pThis, &achOk[0], sizeof(achOk));
527}
528#endif
529
530
531/**
532 * Sends a 'OK' reply packet.
533 *
534 * @returns Status code.
535 * @param pThis The GDB stub context.
536 */
537static int dbgcGdbStubCtxReplySendOk(PGDBSTUBCTX pThis)
538{
539 char achOk[2] = { 'O', 'K' };
540 return dbgcGdbStubCtxReplySend(pThis, &achOk[0], sizeof(achOk));
541}
542
543#if 0 /*unused for now*/
544/**
545 * Sends a 'E NN' part of a reply packet only (packet start and end needs to be handled separately).
546 *
547 * @returns Status code.
548 * @param pThis The GDB stub context.
549 * @param uErr The error code to send.
550 */
551static int dbgcGdbStubCtxReplySendErrData(PGDBSTUBCTX pThis, uint8_t uErr)
552{
553 char achErr[3] = { 'E', 0, 0 };
554 achErr[1] = dbgcGdbStubCtxHexToChr(uErr >> 4);
555 achErr[2] = dbgcGdbStubCtxHexToChr(uErr & 0xf);
556 return dbgcGdbStubCtxReplySendData(pThis, &achErr[0], sizeof(achErr));
557}
558#endif
559
560/**
561 * Sends a 'E NN' reply packet.
562 *
563 * @returns Status code.
564 * @param pThis The GDB stub context.
565 * @param uErr The error code to send.
566 */
567static int dbgcGdbStubCtxReplySendErr(PGDBSTUBCTX pThis, uint8_t uErr)
568{
569 char achErr[3] = { 'E', 0, 0 };
570 achErr[1] = dbgcGdbStubCtxHexToChr(uErr >> 4);
571 achErr[2] = dbgcGdbStubCtxHexToChr(uErr & 0xf);
572 return dbgcGdbStubCtxReplySend(pThis, &achErr[0], sizeof(achErr));
573}
574
575
576/**
577 * Sends a signal trap (S 05) packet to indicate that the target has stopped.
578 *
579 * @returns Status code.
580 * @param pThis The GDB stub context.
581 */
582static int dbgcGdbStubCtxReplySendSigTrap(PGDBSTUBCTX pThis)
583{
584 uint8_t achSigTrap[3] = { 'S', '0', '5' };
585 return dbgcGdbStubCtxReplySend(pThis, &achSigTrap[0], sizeof(achSigTrap));
586}
587
588
589/**
590 * Sends a GDB stub status code indicating an error using the error reply packet.
591 *
592 * @returns Status code.
593 * @param pThis The GDB stub context.
594 * @param rc The status code to send.
595 */
596static int dbgcGdbStubCtxReplySendErrSts(PGDBSTUBCTX pThis, int rc)
597{
598 /** @todo convert error codes maybe. */
599 return dbgcGdbStubCtxReplySendErr(pThis, (-rc) & 0xff);
600}
601
602
603/**
604 * Ensures that there is at least the given amount of bytes of free space left in the packet buffer.
605 *
606 * @returns Status code (error when increasing the buffer failed).
607 * @param pThis The GDB stub context.
608 * @param cbSpace Number of bytes required.
609 */
610static int dbgcGdbStubCtxEnsurePktBufSpace(PGDBSTUBCTX pThis, size_t cbSpace)
611{
612 if (pThis->cbPktBufMax - pThis->offPktBuf >= cbSpace)
613 return VINF_SUCCESS;
614
615 /* Slow path allocate new buffer and copy content over. */
616 int rc = VINF_SUCCESS;
617 size_t cbPktBufMaxNew = pThis->cbPktBufMax + cbSpace;
618 void *pvNew = RTMemRealloc(pThis->pbPktBuf, cbPktBufMaxNew);
619 if (pvNew)
620 {
621 pThis->pbPktBuf = (uint8_t *)pvNew;
622 pThis->cbPktBufMax = cbPktBufMaxNew;
623 }
624 else
625 rc = VERR_NO_MEMORY;
626
627 return rc;
628}
629
630
631/**
632 * Parses the arguments of a 'Z' and 'z' packet.
633 *
634 * @returns Status code.
635 * @param pbArgs Pointer to the start of the first argument.
636 * @param cbArgs Number of argument bytes.
637 * @param penmTpType Where to store the tracepoint type on success.
638 * @param pGdbTgtAddr Where to store the address on success.
639 * @param puKind Where to store the kind argument on success.
640 */
641static int dbgcGdbStubCtxParseTpPktArgs(const uint8_t *pbArgs, size_t cbArgs, GDBSTUBTPTYPE *penmTpType, uint64_t *pGdbTgtAddr, uint64_t *puKind)
642{
643 const uint8_t *pbPktSep = NULL;
644 uint64_t uType = 0;
645
646 int rc = dbgcGdbStubCtxParseHexStringAsInteger(pbArgs, cbArgs, &uType,
647 ',', &pbPktSep);
648 if (RT_SUCCESS(rc))
649 {
650 cbArgs -= (uintptr_t)(pbPktSep - pbArgs) - 1;
651 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbPktSep + 1, cbArgs, pGdbTgtAddr,
652 ',', &pbPktSep);
653 if (RT_SUCCESS(rc))
654 {
655 cbArgs -= (uintptr_t)(pbPktSep - pbArgs) - 1;
656 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbPktSep + 1, cbArgs, puKind,
657 GDBSTUB_PKT_END, NULL);
658 if (RT_SUCCESS(rc))
659 {
660 switch (uType)
661 {
662 case 0:
663 *penmTpType = GDBSTUBTPTYPE_EXEC_SW;
664 break;
665 case 1:
666 *penmTpType = GDBSTUBTPTYPE_EXEC_HW;
667 break;
668 case 2:
669 *penmTpType = GDBSTUBTPTYPE_MEM_WRITE;
670 break;
671 case 3:
672 *penmTpType = GDBSTUBTPTYPE_MEM_READ;
673 break;
674 case 4:
675 *penmTpType = GDBSTUBTPTYPE_MEM_ACCESS;
676 break;
677 default:
678 rc = VERR_INVALID_PARAMETER;
679 break;
680 }
681 }
682 }
683 }
684
685 return rc;
686}
687
688
689/**
690 * Processes the 'TStatus' query.
691 *
692 * @returns Status code.
693 * @param pThis The GDB stub context.
694 * @param pbArgs Pointer to the start of the arguments in the packet.
695 * @param cbArgs Size of arguments in bytes.
696 */
697static int dbgcGdbStubCtxPktProcessQueryTStatus(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
698{
699 RT_NOREF(pbArgs, cbArgs);
700
701 char achReply[2] = { 'T', '0' };
702 return dbgcGdbStubCtxReplySend(pThis, &achReply[0], sizeof(achReply));
703}
704
705
706/**
707 * @copydoc FNGDBSTUBQPKTPROC
708 */
709static int dbgcGdbStubCtxPktProcessFeatXmlRegs(PGDBSTUBCTX pThis, const uint8_t *pbVal, size_t cbVal)
710{
711 /*
712 * xmlRegisters contain a list of supported architectures delimited by ','.
713 * Check that the architecture is in the supported list.
714 */
715 while (cbVal)
716 {
717 /* Find the next delimiter. */
718 size_t cbThisVal = cbVal;
719 const uint8_t *pbDelim = (const uint8_t *)memchr(pbVal, ',', cbVal);
720 if (pbDelim)
721 cbThisVal = pbDelim - pbVal;
722
723 size_t cchArch = sizeof("i386:x86-64") - 1;
724 if (!memcmp(pbVal, "i386:x86-64", RT_MIN(cbVal, cchArch)))
725 {
726 /* Set the flag to support the qXfer:features:read packet. */
727 pThis->fFeatures |= GDBSTUBCTX_FEATURES_F_TGT_DESC;
728 break;
729 }
730
731 cbVal -= cbThisVal + (pbDelim ? 1 : 0);
732 pbVal = pbDelim + (pbDelim ? 1 : 0);
733 }
734
735 return VINF_SUCCESS;
736}
737
738
739/**
740 * Features which can be reported by the remote GDB which we might support.
741 *
742 * @note The sorting matters for features which start the same, the longest must come first.
743 */
744static const GDBSTUBFEATDESC g_aGdbFeatures[] =
745{
746#define GDBSTUBFEATDESC_INIT(a_Name, a_pfnHnd, a_fVal) { a_Name, sizeof(a_Name) - 1, a_pfnHnd, a_fVal }
747 GDBSTUBFEATDESC_INIT("xmlRegisters", dbgcGdbStubCtxPktProcessFeatXmlRegs, true),
748#undef GDBSTUBFEATDESC_INIT
749};
750
751
752/**
753 * Calculates the feature length of the next feature pointed to by the given arguments buffer.
754 *
755 * @returns Status code.
756 * @param pbArgs Pointer to the start of the arguments in the packet.
757 * @param cbArgs Size of arguments in bytes.
758 * @param pcbArg Where to store the size of the argument in bytes on success (excluding the delimiter).
759 * @param pfTerminator Whereto store the flag whether the packet terminator (#) was seen as a delimiter.
760 */
761static int dbgcGdbStubCtxQueryPktQueryFeatureLen(const uint8_t *pbArgs, size_t cbArgs, size_t *pcbArg, bool *pfTerminator)
762{
763 const uint8_t *pbArgCur = pbArgs;
764
765 while ( cbArgs
766 && *pbArgCur != ';'
767 && *pbArgCur != GDBSTUB_PKT_END)
768 {
769 cbArgs--;
770 pbArgCur++;
771 }
772
773 if ( !cbArgs
774 && *pbArgCur != ';'
775 && *pbArgCur != GDBSTUB_PKT_END)
776 return VERR_NET_PROTOCOL_ERROR;
777
778 *pcbArg = pbArgCur - pbArgs;
779 *pfTerminator = *pbArgCur == GDBSTUB_PKT_END ? true : false;
780
781 return VINF_SUCCESS;
782}
783
784
785/**
786 * Sends the reply to the 'qSupported' packet.
787 *
788 * @returns Status code.
789 * @param pThis The GDB stub context.
790 */
791static int dbgcGdbStubCtxPktProcessQuerySupportedReply(PGDBSTUBCTX pThis)
792{
793 /** @todo Enhance. */
794 if (pThis->fFeatures & GDBSTUBCTX_FEATURES_F_TGT_DESC)
795 return dbgcGdbStubCtxReplySend(pThis, "qXfer:features:read+", sizeof("qXfer:features:read+") - 1);
796
797 return dbgcGdbStubCtxReplySend(pThis, NULL, 0);
798}
799
800
801/**
802 * Processes the 'Supported' query.
803 *
804 * @returns Status code.
805 * @param pThis The GDB stub context.
806 * @param pbArgs Pointer to the start of the arguments in the packet.
807 * @param cbArgs Size of arguments in bytes.
808 */
809static int dbgcGdbStubCtxPktProcessQuerySupported(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
810{
811 /* Skip the : following the qSupported start. */
812 if ( cbArgs < 1
813 || pbArgs[0] != ':')
814 return VERR_NET_PROTOCOL_ERROR;
815
816 cbArgs--;
817 pbArgs++;
818
819 /*
820 * Each feature but the last one are separated by ; and the last one is delimited by the # packet end symbol.
821 * We first determine the boundaries of the reported feature and pass it to the appropriate handler.
822 */
823 int rc = VINF_SUCCESS;
824 while ( cbArgs
825 && RT_SUCCESS(rc))
826 {
827 bool fTerminator = false;
828 size_t cbArg = 0;
829 rc = dbgcGdbStubCtxQueryPktQueryFeatureLen(pbArgs, cbArgs, &cbArg, &fTerminator);
830 if (RT_SUCCESS(rc))
831 {
832 /* Search for the feature handler. */
833 for (uint32_t i = 0; i < RT_ELEMENTS(g_aGdbFeatures); i++)
834 {
835 PCGDBSTUBFEATDESC pFeatDesc = &g_aGdbFeatures[i];
836
837 if ( cbArg > pFeatDesc->cchName /* At least one character must come after the feature name ('+', '-' or '='). */
838 && !memcmp(pFeatDesc->pszName, pbArgs, pFeatDesc->cchName))
839 {
840 /* Found, execute handler after figuring out whether there is a value attached. */
841 const uint8_t *pbVal = pbArgs + pFeatDesc->cchName;
842 size_t cbVal = cbArg - pFeatDesc->cchName;
843
844 if (pFeatDesc->fVal)
845 {
846 if ( *pbVal == '='
847 && cbVal > 1)
848 {
849 pbVal++;
850 cbVal--;
851 }
852 else
853 rc = VERR_NET_PROTOCOL_ERROR;
854 }
855 else if ( cbVal != 1
856 || ( *pbVal != '+'
857 && *pbVal != '-')) /* '+' and '-' are allowed to indicate support for a particular feature. */
858 rc = VERR_NET_PROTOCOL_ERROR;
859
860 if (RT_SUCCESS(rc))
861 rc = pFeatDesc->pfnHandler(pThis, pbVal, cbVal);
862 break;
863 }
864 }
865
866 cbArgs -= cbArg;
867 pbArgs += cbArg;
868 if (!fTerminator)
869 {
870 cbArgs--;
871 pbArgs++;
872 }
873 else
874 break;
875 }
876 }
877
878 /* If everything went alright send the reply with our supported features. */
879 if (RT_SUCCESS(rc))
880 rc = dbgcGdbStubCtxPktProcessQuerySupportedReply(pThis);
881
882 return rc;
883}
884
885
886/**
887 * Sends the reply to a 'qXfer:object:read:...' request.
888 *
889 * @returns Status code.
890 * @param pThis The GDB stub context.
891 * @param offRead Where to start reading from within the object.
892 * @param cbRead How much to read.
893 * @param pbObj The start of the object.
894 * @param cbObj Size of the object.
895 */
896static int dbgcGdbStubCtxQueryXferReadReply(PGDBSTUBCTX pThis, uint32_t offRead, size_t cbRead, const uint8_t *pbObj, size_t cbObj)
897{
898 int rc = VINF_SUCCESS;
899 if (offRead < cbObj)
900 {
901 /** @todo Escaping */
902 size_t cbThisRead = offRead + cbRead < cbObj ? cbRead : cbObj - offRead;
903
904 rc = dbgcGdbStubCtxEnsurePktBufSpace(pThis, cbThisRead + 1);
905 if (RT_SUCCESS(rc))
906 {
907 uint8_t *pbPktBuf = pThis->pbPktBuf;
908 *pbPktBuf++ = cbThisRead < cbRead ? 'l' : 'm';
909 memcpy(pbPktBuf, pbObj + offRead, cbThisRead);
910 rc = dbgcGdbStubCtxReplySend(pThis, pThis->pbPktBuf, cbThisRead + 1);
911 }
912 else
913 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NO_MEMORY);
914 }
915 else if (offRead == cbObj)
916 rc = dbgcGdbStubCtxReplySend(pThis, "l", sizeof("l") - 1);
917 else
918 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
919
920 return rc;
921}
922
923
924/**
925 * Parses the annex:offset,length part of a 'qXfer:object:read:...' request.
926 *
927 * @returns Status code.
928 * @param pbArgs Start of the arguments beginning with annex.
929 * @param cbArgs Number of bytes remaining for the arguments.
930 * @param ppchAnnex Where to store the pointer to the beginning of the annex on success.
931 * @param pcchAnnex Where to store the number of characters for the annex on success.
932 * @param poffRead Where to store the offset on success.
933 * @param pcbRead Where to store the length on success.
934 */
935static int dbgcGdbStubCtxPktProcessQueryXferParseAnnexOffLen(const uint8_t *pbArgs, size_t cbArgs, const char **ppchAnnex, size_t *pcchAnnex,
936 uint32_t *poffRead, size_t *pcbRead)
937{
938 int rc = VINF_SUCCESS;
939 const uint8_t *pbSep = (const uint8_t *)memchr(pbArgs, ':', cbArgs);
940 if (pbSep)
941 {
942 *ppchAnnex = (const char *)pbArgs;
943 *pcchAnnex = pbSep - pbArgs;
944
945 pbSep++;
946 cbArgs -= *pcchAnnex + 1;
947
948 uint64_t u64Tmp = 0;
949 const uint8_t *pbLenStart = NULL;
950 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbSep, cbArgs, &u64Tmp, ',', &pbLenStart);
951 if ( RT_SUCCESS(rc)
952 && (uint32_t)u64Tmp == u64Tmp)
953 {
954 *poffRead = (uint32_t)u64Tmp;
955 cbArgs -= pbLenStart - pbSep;
956
957 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbLenStart + 1, cbArgs, &u64Tmp, '#', &pbLenStart);
958 if ( RT_SUCCESS(rc)
959 && (size_t)u64Tmp == u64Tmp)
960 *pcbRead = (size_t)u64Tmp;
961 else
962 rc = VERR_NET_PROTOCOL_ERROR;
963 }
964 else
965 rc = VERR_NET_PROTOCOL_ERROR;
966 }
967 else
968 rc = VERR_NET_PROTOCOL_ERROR;
969
970 return rc;
971}
972
973
974/**
975 * GDB registers.
976 */
977static const struct GDBREGDESC
978{
979 /** Register name. */
980 const char *pszName;
981 /** DBGF register index. */
982 DBGFREG enmReg;
983 /** Bitsize */
984 uint32_t cBits;
985 /** Type. */
986 const char *pszType;
987 /** Group. */
988 const char *pszGroup;
989} g_aGdbRegs[] =
990{
991#define DBGREG_DESC_INIT_INT64(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 64, "int64", NULL }
992#define DBGREG_DESC_INIT_INT32(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 32, "int32", NULL }
993#define DBGREG_DESC_INIT_DATA_PTR(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 64, "data_ptr", NULL }
994#define DBGREG_DESC_INIT_CODE_PTR(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 64, "code_ptr", NULL }
995#define DBGREG_DESC_INIT_X87(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 80, "i387_ext", NULL }
996#define DBGREG_DESC_INIT_X87_CTRL(a_Name, a_enmDbgfReg) { a_Name, a_enmDbgfReg, 32, "int", "float" }
997 DBGREG_DESC_INIT_INT64( "rax", DBGFREG_RAX),
998 DBGREG_DESC_INIT_INT64( "rbx", DBGFREG_RBX),
999 DBGREG_DESC_INIT_INT64( "rcx", DBGFREG_RCX),
1000 DBGREG_DESC_INIT_INT64( "rdx", DBGFREG_RDX),
1001 DBGREG_DESC_INIT_INT64( "rsi", DBGFREG_RSI),
1002 DBGREG_DESC_INIT_INT64( "rdi", DBGFREG_RDI),
1003 DBGREG_DESC_INIT_DATA_PTR("rbp", DBGFREG_RBP),
1004 DBGREG_DESC_INIT_DATA_PTR("rsp", DBGFREG_RSP),
1005 DBGREG_DESC_INIT_INT64( "r8", DBGFREG_R8),
1006 DBGREG_DESC_INIT_INT64( "r9", DBGFREG_R9),
1007 DBGREG_DESC_INIT_INT64( "r10", DBGFREG_R10),
1008 DBGREG_DESC_INIT_INT64( "r11", DBGFREG_R11),
1009 DBGREG_DESC_INIT_INT64( "r12", DBGFREG_R12),
1010 DBGREG_DESC_INIT_INT64( "r13", DBGFREG_R13),
1011 DBGREG_DESC_INIT_INT64( "r14", DBGFREG_R14),
1012 DBGREG_DESC_INIT_INT64( "r15", DBGFREG_R15),
1013 DBGREG_DESC_INIT_CODE_PTR("rip", DBGFREG_RIP),
1014 DBGREG_DESC_INIT_INT32( "eflags", DBGFREG_FLAGS),
1015 DBGREG_DESC_INIT_INT32( "cs", DBGFREG_CS),
1016 DBGREG_DESC_INIT_INT32( "ss", DBGFREG_SS),
1017 DBGREG_DESC_INIT_INT32( "ds", DBGFREG_DS),
1018 DBGREG_DESC_INIT_INT32( "es", DBGFREG_ES),
1019 DBGREG_DESC_INIT_INT32( "fs", DBGFREG_FS),
1020 DBGREG_DESC_INIT_INT32( "gs", DBGFREG_GS),
1021
1022 DBGREG_DESC_INIT_X87( "st0", DBGFREG_ST0),
1023 DBGREG_DESC_INIT_X87( "st1", DBGFREG_ST1),
1024 DBGREG_DESC_INIT_X87( "st2", DBGFREG_ST2),
1025 DBGREG_DESC_INIT_X87( "st3", DBGFREG_ST3),
1026 DBGREG_DESC_INIT_X87( "st4", DBGFREG_ST4),
1027 DBGREG_DESC_INIT_X87( "st5", DBGFREG_ST5),
1028 DBGREG_DESC_INIT_X87( "st6", DBGFREG_ST6),
1029 DBGREG_DESC_INIT_X87( "st7", DBGFREG_ST7),
1030
1031 DBGREG_DESC_INIT_X87_CTRL("fctrl", DBGFREG_FCW),
1032 DBGREG_DESC_INIT_X87_CTRL("fstat", DBGFREG_FSW),
1033 DBGREG_DESC_INIT_X87_CTRL("ftag", DBGFREG_FTW),
1034 DBGREG_DESC_INIT_X87_CTRL("fop", DBGFREG_FOP),
1035 DBGREG_DESC_INIT_X87_CTRL("fioff", DBGFREG_FPUIP),
1036 DBGREG_DESC_INIT_X87_CTRL("fiseg", DBGFREG_FPUCS),
1037 DBGREG_DESC_INIT_X87_CTRL("fooff", DBGFREG_FPUDP),
1038 DBGREG_DESC_INIT_X87_CTRL("foseg", DBGFREG_FPUDS)
1039
1040#undef DBGREG_DESC_INIT_CODE_PTR
1041#undef DBGREG_DESC_INIT_DATA_PTR
1042#undef DBGREG_DESC_INIT_INT32
1043#undef DBGREG_DESC_INIT_INT64
1044};
1045
1046
1047/**
1048 * Creates the target XML description.
1049 *
1050 * @returns Status code.
1051 * @param pThis The GDB stub context.
1052 */
1053static int dbgcGdbStubCtxTgtXmlDescCreate(PGDBSTUBCTX pThis)
1054{
1055 static const char s_szXmlTgtHdr[] =
1056 "<?xml version=\"1.0\"?>\n"
1057 "<!DOCTYPE target SYSTEM \"gdb-target.dtd\">\n"
1058 "<target version=\"1.0\">\n"
1059 " <architecture>i386:x86-64</architecture>\n"
1060 " <feature name=\"org.gnu.gdb.i386.core\">\n";
1061 static const char s_szXmlTgtFooter[] =
1062 " </feature>\n"
1063 "</target>\n";
1064
1065 int rc = VINF_SUCCESS;
1066
1067 pThis->pachTgtXmlDesc = (char *)RTStrAlloc(_32K);
1068 if (pThis->pachTgtXmlDesc)
1069 {
1070 size_t cbLeft = _32K;
1071 char *pachXmlCur = pThis->pachTgtXmlDesc;
1072 pThis->cbTgtXmlDesc = cbLeft;
1073
1074 rc = RTStrCatP(&pachXmlCur, &cbLeft, &s_szXmlTgtHdr[0]);
1075 if (RT_SUCCESS(rc))
1076 {
1077 /* Register */
1078 for (uint32_t i = 0; i < RT_ELEMENTS(g_aGdbRegs) && RT_SUCCESS(rc); i++)
1079 {
1080 const struct GDBREGDESC *pReg = &g_aGdbRegs[i];
1081
1082 ssize_t cchStr = 0;
1083 if (pReg->pszGroup)
1084 cchStr = RTStrPrintf2(pachXmlCur, cbLeft,
1085 "<reg name=\"%s\" bitsize=\"%u\" regnum=\"%u\" type=\"%s\" group=\"%s\"/>\n",
1086 pReg->pszName, pReg->cBits, i, pReg->pszType, pReg->pszGroup);
1087 else
1088 cchStr = RTStrPrintf2(pachXmlCur, cbLeft,
1089 "<reg name=\"%s\" bitsize=\"%u\" regnum=\"%u\" type=\"%s\"/>\n",
1090 pReg->pszName, pReg->cBits, i, pReg->pszType);
1091
1092 if (cchStr > 0)
1093 {
1094 pachXmlCur += cchStr;
1095 cbLeft -= cchStr;
1096 }
1097 else
1098 rc = VERR_BUFFER_OVERFLOW;
1099 }
1100 }
1101
1102 if (RT_SUCCESS(rc))
1103 rc = RTStrCatP(&pachXmlCur, &cbLeft, &s_szXmlTgtFooter[0]);
1104
1105 pThis->cbTgtXmlDesc -= cbLeft;
1106 }
1107 else
1108 rc = VERR_NO_MEMORY;
1109
1110 return rc;
1111}
1112
1113
1114/**
1115 * Returns the GDB register descriptor describing the given DBGF register enum.
1116 *
1117 * @returns Pointer to the GDB register descriptor or NULL if not found.
1118 * @param idxReg The register to look for.
1119 */
1120static const GDBREGDESC *dbgcGdbStubRegGet(uint32_t idxReg)
1121{
1122 if (RT_LIKELY(idxReg < RT_ELEMENTS(g_aGdbRegs)))
1123 return &g_aGdbRegs[idxReg];
1124
1125 return NULL;
1126}
1127
1128
1129/**
1130 * Processes the 'Xfer:features:read' query.
1131 *
1132 * @returns Status code.
1133 * @param pThis The GDB stub context.
1134 * @param pbArgs Pointer to the start of the arguments in the packet.
1135 * @param cbArgs Size of arguments in bytes.
1136 */
1137static int dbgcGdbStubCtxPktProcessQueryXferFeatRead(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1138{
1139 /* Skip the : following the Xfer:features:read start. */
1140 if ( cbArgs < 1
1141 || pbArgs[0] != ':')
1142 return VERR_NET_PROTOCOL_ERROR;
1143
1144 cbArgs--;
1145 pbArgs++;
1146
1147 int rc = VINF_SUCCESS;
1148 if (pThis->fFeatures & GDBSTUBCTX_FEATURES_F_TGT_DESC)
1149 {
1150 /* Create the target XML description if not existing. */
1151 if (!pThis->pachTgtXmlDesc)
1152 rc = dbgcGdbStubCtxTgtXmlDescCreate(pThis);
1153
1154 if (RT_SUCCESS(rc))
1155 {
1156 /* Parse annex, offset and length and return the data. */
1157 const char *pchAnnex = NULL;
1158 size_t cchAnnex = 0;
1159 uint32_t offRead = 0;
1160 size_t cbRead = 0;
1161
1162 rc = dbgcGdbStubCtxPktProcessQueryXferParseAnnexOffLen(pbArgs, cbArgs,
1163 &pchAnnex, &cchAnnex,
1164 &offRead, &cbRead);
1165 if (RT_SUCCESS(rc))
1166 {
1167 /* Check whether the annex is supported. */
1168 if ( cchAnnex == sizeof("target.xml") - 1
1169 && !memcmp(pchAnnex, "target.xml", cchAnnex))
1170 rc = dbgcGdbStubCtxQueryXferReadReply(pThis, offRead, cbRead, (const uint8_t *)pThis->pachTgtXmlDesc,
1171 pThis->cbTgtXmlDesc);
1172 else
1173 rc = dbgcGdbStubCtxReplySendErr(pThis, 0);
1174 }
1175 else
1176 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1177 }
1178 else
1179 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1180 }
1181 else
1182 rc = dbgcGdbStubCtxReplySend(pThis, NULL, 0); /* Not supported. */
1183
1184 return rc;
1185}
1186
1187
1188/**
1189 * Processes the 'Rcmd' query.
1190 *
1191 * @returns Status code.
1192 * @param pThis The GDB stub context.
1193 * @param pbArgs Pointer to the start of the arguments in the packet.
1194 * @param cbArgs Size of arguments in bytes.
1195 */
1196static int dbgcGdbStubCtxPktProcessQueryRcmd(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1197{
1198 /* Skip the , following the qRcmd start. */
1199 if ( cbArgs < 1
1200 || pbArgs[0] != ',')
1201 return VERR_NET_PROTOCOL_ERROR;
1202
1203 cbArgs--;
1204 pbArgs++;
1205
1206 /* Decode the command. */
1207 /** @todo Make this dynamic. */
1208 char szCmd[_4K];
1209 RT_ZERO(szCmd);
1210
1211 if (cbArgs / 2 >= sizeof(szCmd))
1212 return VERR_NET_PROTOCOL_ERROR;
1213
1214 size_t cbDecoded = 0;
1215 int rc = RTStrConvertHexBytesEx((const char *)pbArgs, &szCmd[0], sizeof(szCmd), 0 /*fFlags*/,
1216 NULL /* ppszNext */, &cbDecoded);
1217 if (rc == VWRN_TRAILING_CHARS)
1218 rc = VINF_SUCCESS;
1219 if (RT_SUCCESS(rc))
1220 {
1221 szCmd[cbDecoded] = '\0'; /* Ensure zero termination. */
1222
1223 pThis->fOutput = false;
1224 rc = dbgcEvalCommand(&pThis->Dbgc, &szCmd[0], cbDecoded, false /*fNoExecute*/);
1225 dbgcGdbStubCtxReplySendOk(pThis);
1226 if ( rc != VERR_DBGC_QUIT
1227 && rc != VWRN_DBGC_CMD_PENDING)
1228 rc = VINF_SUCCESS; /* ignore other statuses */
1229 }
1230
1231 return rc;
1232}
1233
1234
1235/**
1236 * List of supported query packets.
1237 */
1238static const GDBSTUBQPKTPROC g_aQPktProcs[] =
1239{
1240#define GDBSTUBQPKTPROC_INIT(a_Name, a_pfnProc) { a_Name, sizeof(a_Name) - 1, a_pfnProc }
1241 GDBSTUBQPKTPROC_INIT("TStatus", dbgcGdbStubCtxPktProcessQueryTStatus),
1242 GDBSTUBQPKTPROC_INIT("Supported", dbgcGdbStubCtxPktProcessQuerySupported),
1243 GDBSTUBQPKTPROC_INIT("Xfer:features:read", dbgcGdbStubCtxPktProcessQueryXferFeatRead),
1244 GDBSTUBQPKTPROC_INIT("Rcmd", dbgcGdbStubCtxPktProcessQueryRcmd),
1245#undef GDBSTUBQPKTPROC_INIT
1246};
1247
1248
1249/**
1250 * Processes a 'q' packet, sending the appropriate reply.
1251 *
1252 * @returns Status code.
1253 * @param pThis The GDB stub context.
1254 * @param pbQuery The query packet data (without the 'q').
1255 * @param cbQuery Size of the remaining query packet in bytes.
1256 */
1257static int dbgcGdbStubCtxPktProcessQuery(PGDBSTUBCTX pThis, const uint8_t *pbQuery, size_t cbQuery)
1258{
1259 /* Search the query and execute the processor or return an empty reply if not supported. */
1260 for (uint32_t i = 0; i < RT_ELEMENTS(g_aQPktProcs); i++)
1261 {
1262 size_t cbCmp = g_aQPktProcs[i].cchName < cbQuery ? g_aQPktProcs[i].cchName : cbQuery;
1263
1264 if (!memcmp(pbQuery, g_aQPktProcs[i].pszName, cbCmp))
1265 return g_aQPktProcs[i].pfnProc(pThis, pbQuery + cbCmp, cbQuery - cbCmp);
1266 }
1267
1268 return dbgcGdbStubCtxReplySend(pThis, NULL, 0);
1269}
1270
1271
1272/**
1273 * Processes a 'vCont[;action[:thread-id]]' packet.
1274 *
1275 * @returns Status code.
1276 * @param pThis The GDB stub context.
1277 * @param pbArgs Pointer to the start of the arguments in the packet.
1278 * @param cbArgs Size of arguments in bytes.
1279 */
1280static int dbgcGdbStubCtxPktProcessVCont(PGDBSTUBCTX pThis, const uint8_t *pbArgs, size_t cbArgs)
1281{
1282 int rc = VINF_SUCCESS;
1283
1284 /* Skip the ; following the identifier. */
1285 if ( cbArgs < 2
1286 || pbArgs[0] != ';')
1287 return dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1288
1289 pbArgs++;
1290 cbArgs--;
1291
1292 /** @todo For now we don't care about multiple threads and ignore thread IDs and multiple actions. */
1293 switch (pbArgs[0])
1294 {
1295 case 'c':
1296 {
1297 if (DBGFR3IsHalted(pThis->Dbgc.pUVM))
1298 DBGFR3Resume(pThis->Dbgc.pUVM);
1299 break;
1300 }
1301 case 's':
1302 {
1303 PDBGFADDRESS pStackPop = NULL;
1304 RTGCPTR cbStackPop = 0;
1305 rc = DBGFR3StepEx(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGF_STEP_F_INTO, NULL,
1306 pStackPop, cbStackPop, 1 /*cMaxSteps*/);
1307 if (RT_FAILURE(rc))
1308 dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1309 break;
1310 }
1311 case 't':
1312 {
1313 if (!DBGFR3IsHalted(pThis->Dbgc.pUVM))
1314 rc = DBGFR3Halt(pThis->Dbgc.pUVM);
1315 /* The reply will be send in the event loop. */
1316 break;
1317 }
1318 default:
1319 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1320 }
1321
1322 return rc;
1323}
1324
1325
1326/**
1327 * List of supported 'v<identifier>' packets.
1328 */
1329static const GDBSTUBVPKTPROC g_aVPktProcs[] =
1330{
1331#define GDBSTUBVPKTPROC_INIT(a_Name, a_pszReply, a_pfnProc) { a_Name, sizeof(a_Name) - 1, a_pszReply, sizeof(a_pszReply) - 1, a_pfnProc }
1332 GDBSTUBVPKTPROC_INIT("Cont", "vCont;s;c;t", dbgcGdbStubCtxPktProcessVCont)
1333#undef GDBSTUBVPKTPROC_INIT
1334};
1335
1336
1337/**
1338 * Processes a 'v<identifier>' packet, sending the appropriate reply.
1339 *
1340 * @returns Status code.
1341 * @param pThis The GDB stub context.
1342 * @param pbPktRem The remaining packet data (without the 'v').
1343 * @param cbPktRem Size of the remaining packet in bytes.
1344 */
1345static int dbgcGdbStubCtxPktProcessV(PGDBSTUBCTX pThis, const uint8_t *pbPktRem, size_t cbPktRem)
1346{
1347 /* Determine the end of the identifier, delimiters are '?', ';' or end of packet. */
1348 bool fQuery = false;
1349 const uint8_t *pbDelim = (const uint8_t *)memchr(pbPktRem, '?', cbPktRem);
1350 if (!pbDelim)
1351 pbDelim = (const uint8_t *)memchr(pbPktRem, ';', cbPktRem);
1352 else
1353 fQuery = true;
1354
1355 size_t cchId = 0;
1356 if (pbDelim) /* Delimiter found, calculate length. */
1357 cchId = pbDelim - pbPktRem;
1358 else /* Not found, size goes till end of packet. */
1359 cchId = cbPktRem;
1360
1361 /* Search the query and execute the processor or return an empty reply if not supported. */
1362 for (uint32_t i = 0; i < RT_ELEMENTS(g_aVPktProcs); i++)
1363 {
1364 PCGDBSTUBVPKTPROC pVProc = &g_aVPktProcs[i];
1365
1366 if ( pVProc->cchName == cchId
1367 && !memcmp(pbPktRem, pVProc->pszName, cchId))
1368 {
1369 /* Just send the static reply for a query and execute the processor for everything else. */
1370 if (fQuery)
1371 return dbgcGdbStubCtxReplySend(pThis, pVProc->pszReplyQ, pVProc->cchReplyQ);
1372
1373 /* Execute the handler. */
1374 return pVProc->pfnProc(pThis, pbPktRem + cchId, cbPktRem - cchId);
1375 }
1376 }
1377
1378 return dbgcGdbStubCtxReplySend(pThis, NULL, 0);
1379}
1380
1381
1382/**
1383 * Processes a completely received packet.
1384 *
1385 * @returns Status code.
1386 * @param pThis The GDB stub context.
1387 */
1388static int dbgcGdbStubCtxPktProcess(PGDBSTUBCTX pThis)
1389{
1390 int rc = VINF_SUCCESS;
1391
1392 if (pThis->cbPkt >= 1)
1393 {
1394 switch (pThis->pbPktBuf[1])
1395 {
1396 case '!': /* Enabled extended mode. */
1397 {
1398 pThis->fExtendedMode = true;
1399 rc = dbgcGdbStubCtxReplySendOk(pThis);
1400 break;
1401 }
1402 case '?':
1403 {
1404 /* Return signal state. */
1405 rc = dbgcGdbStubCtxReplySendSigTrap(pThis);
1406 break;
1407 }
1408 case 's': /* Single step, response will be sent in the event loop. */
1409 {
1410 PDBGFADDRESS pStackPop = NULL;
1411 RTGCPTR cbStackPop = 0;
1412 rc = DBGFR3StepEx(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, DBGF_STEP_F_INTO, NULL,
1413 pStackPop, cbStackPop, 1 /*cMaxSteps*/);
1414 if (RT_FAILURE(rc))
1415 dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1416 break;
1417 }
1418 case 'c': /* Continue, no response */
1419 {
1420 if (DBGFR3IsHalted(pThis->Dbgc.pUVM))
1421 DBGFR3Resume(pThis->Dbgc.pUVM);
1422 break;
1423 }
1424 case 'g': /* Read general registers. */
1425 {
1426 uint32_t idxRegMax = 0;
1427 size_t cbRegs = 0;
1428 for (;;)
1429 {
1430 const GDBREGDESC *pReg = &g_aGdbRegs[idxRegMax++];
1431 cbRegs += pReg->cBits / 8;
1432 if (pReg->enmReg == DBGFREG_SS) /* Up to this seems to belong to the general register set. */
1433 break;
1434 }
1435
1436 size_t cbReplyPkt = cbRegs * 2 + 1; /* One byte needs two characters. */
1437 rc = dbgcGdbStubCtxEnsurePktBufSpace(pThis, cbReplyPkt);
1438 if (RT_SUCCESS(rc))
1439 {
1440 size_t cbLeft = cbReplyPkt;
1441 uint8_t *pbReply = pThis->pbPktBuf;
1442
1443 for (uint32_t i = 0; i < idxRegMax && RT_SUCCESS(rc); i++)
1444 {
1445 const GDBREGDESC *pReg = &g_aGdbRegs[i];
1446 size_t cbReg = pReg->cBits / 8;
1447 union
1448 {
1449 uint32_t u32;
1450 uint64_t u64;
1451 uint8_t au8[8];
1452 } RegVal;
1453
1454 if (pReg->cBits == 32)
1455 rc = DBGFR3RegCpuQueryU32(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, pReg->enmReg, &RegVal.u32);
1456 else
1457 rc = DBGFR3RegCpuQueryU64(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, pReg->enmReg, &RegVal.u64);
1458
1459 if (RT_SUCCESS(rc))
1460 rc = dbgcGdbStubCtxEncodeBinaryAsHex(pbReply, cbLeft, &RegVal.au8[0], cbReg);
1461
1462 pbReply += cbReg * 2;
1463 cbLeft -= cbReg * 2;
1464 }
1465
1466 if (RT_SUCCESS(rc))
1467 rc = dbgcGdbStubCtxReplySend(pThis, pThis->pbPktBuf, cbReplyPkt);
1468 else
1469 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1470 }
1471
1472 break;
1473 }
1474 case 'm': /* Read memory. */
1475 {
1476 uint64_t GdbTgtAddr = 0;
1477 const uint8_t *pbPktSep = NULL;
1478
1479 rc = dbgcGdbStubCtxParseHexStringAsInteger(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &GdbTgtAddr,
1480 ',', &pbPktSep);
1481 if (RT_SUCCESS(rc))
1482 {
1483 size_t cbProcessed = pbPktSep - &pThis->pbPktBuf[2];
1484 uint64_t cbRead = 0;
1485 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbPktSep + 1, pThis->cbPkt - 1 - cbProcessed - 1, &cbRead, GDBSTUB_PKT_END, NULL);
1486 if (RT_SUCCESS(rc))
1487 {
1488 size_t cbReplyPkt = cbRead * 2 + 1; /* One byte needs two characters. */
1489
1490 rc = dbgcGdbStubCtxEnsurePktBufSpace(pThis, cbReplyPkt);
1491 if (RT_SUCCESS(rc))
1492 {
1493 uint8_t *pbPktBuf = pThis->pbPktBuf;
1494 size_t cbPktBufLeft = cbReplyPkt;
1495 DBGFADDRESS AddrRead;
1496
1497 DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &AddrRead, GdbTgtAddr);
1498
1499 while ( cbRead
1500 && RT_SUCCESS(rc))
1501 {
1502 uint8_t abTmp[_4K];
1503 size_t cbThisRead = RT_MIN(cbRead, sizeof(abTmp));
1504
1505 rc = DBGFR3MemRead(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, &AddrRead, &abTmp[0], cbThisRead);
1506 if (RT_FAILURE(rc))
1507 break;
1508
1509 rc = dbgcGdbStubCtxEncodeBinaryAsHex(pbPktBuf, cbPktBufLeft, &abTmp[0], cbThisRead);
1510 if (RT_FAILURE(rc))
1511 break;
1512
1513 DBGFR3AddrAdd(&AddrRead, cbThisRead);
1514 cbRead -= cbThisRead;
1515 pbPktBuf += cbThisRead;
1516 cbPktBufLeft -= cbThisRead;
1517 }
1518
1519 if (RT_SUCCESS(rc))
1520 rc = dbgcGdbStubCtxReplySend(pThis, pThis->pbPktBuf, cbReplyPkt);
1521 else
1522 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1523 }
1524 else
1525 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1526 }
1527 else
1528 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1529 }
1530 else
1531 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1532 break;
1533 }
1534 case 'M': /* Write memory. */
1535 {
1536 uint64_t GdbTgtAddr = 0;
1537 const uint8_t *pbPktSep = NULL;
1538
1539 rc = dbgcGdbStubCtxParseHexStringAsInteger(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &GdbTgtAddr,
1540 ',', &pbPktSep);
1541 if (RT_SUCCESS(rc))
1542 {
1543 size_t cbProcessed = pbPktSep - &pThis->pbPktBuf[2];
1544 uint64_t cbWrite = 0;
1545 rc = dbgcGdbStubCtxParseHexStringAsInteger(pbPktSep + 1, pThis->cbPkt - 1 - cbProcessed - 1, &cbWrite, ':', &pbPktSep);
1546 if (RT_SUCCESS(rc))
1547 {
1548 cbProcessed = pbPktSep - &pThis->pbPktBuf[2];
1549 const uint8_t *pbDataCur = pbPktSep + 1;
1550 size_t cbDataLeft = pThis->cbPkt - 1 - cbProcessed - 1 - 1;
1551 DBGFADDRESS AddrWrite;
1552
1553 DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &AddrWrite, GdbTgtAddr);
1554
1555 while ( cbWrite
1556 && RT_SUCCESS(rc))
1557 {
1558 uint8_t abTmp[_4K];
1559 size_t cbThisWrite = RT_MIN(cbWrite, sizeof(abTmp));
1560 size_t cbDecoded = 0;
1561
1562 rc = dbgcGdbStubCtxParseHexStringAsByteBuf(pbDataCur, cbDataLeft, &abTmp[0], cbThisWrite, &cbDecoded);
1563 if (!rc)
1564 rc = DBGFR3MemWrite(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, &AddrWrite, &abTmp[0], cbThisWrite);
1565
1566 DBGFR3AddrAdd(&AddrWrite, cbThisWrite);
1567 cbWrite -= cbThisWrite;
1568 pbDataCur += cbDecoded;
1569 cbDataLeft -= cbDecoded;
1570 }
1571
1572 if (RT_SUCCESS(rc))
1573 rc = dbgcGdbStubCtxReplySendOk(pThis);
1574 else
1575 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1576 }
1577 else
1578 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1579 }
1580 else
1581 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1582 break;
1583 }
1584 case 'p': /* Read a single register */
1585 {
1586 uint64_t uReg = 0;
1587 rc = dbgcGdbStubCtxParseHexStringAsInteger(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &uReg,
1588 GDBSTUB_PKT_END, NULL);
1589 if (RT_SUCCESS(rc))
1590 {
1591 DBGFREGVAL RegVal;
1592 DBGFREGVALTYPE enmType;
1593 const GDBREGDESC *pReg = dbgcGdbStubRegGet(uReg);
1594 if (RT_LIKELY(pReg))
1595 {
1596 rc = DBGFR3RegNmQuery(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, pReg->pszName, &RegVal, &enmType);
1597 if (RT_SUCCESS(rc))
1598 {
1599 size_t cbReg = pReg->cBits / 8;
1600 size_t cbReplyPkt = cbReg * 2 + 1; /* One byte needs two characters. */
1601
1602 /* Encode data and send. */
1603 rc = dbgcGdbStubCtxEnsurePktBufSpace(pThis, cbReplyPkt);
1604 if (RT_SUCCESS(rc))
1605 {
1606 rc = dbgcGdbStubCtxEncodeBinaryAsHex(pThis->pbPktBuf, pThis->cbPktBufMax, &RegVal.au8[0], cbReg);
1607 if (RT_SUCCESS(rc))
1608 rc = dbgcGdbStubCtxReplySend(pThis, pThis->pbPktBuf, cbReplyPkt);
1609 else
1610 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1611 }
1612 else
1613 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1614 }
1615 else
1616 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1617 }
1618 else
1619 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1620 }
1621 else
1622 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1623 break;
1624 }
1625 case 'P': /* Write a single register */
1626 {
1627 uint64_t uReg = 0;
1628 const uint8_t *pbPktSep = NULL;
1629 rc = dbgcGdbStubCtxParseHexStringAsInteger(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &uReg,
1630 '=', &pbPktSep);
1631 if (RT_SUCCESS(rc))
1632 {
1633 const GDBREGDESC *pReg = dbgcGdbStubRegGet(uReg);
1634
1635 if (pReg)
1636 {
1637 DBGFREGVAL RegVal;
1638 DBGFREGVALTYPE enmValType = pReg->cBits == 64 ? DBGFREGVALTYPE_U64 : DBGFREGVALTYPE_U32;
1639 size_t cbProcessed = pbPktSep - &pThis->pbPktBuf[2];
1640 rc = dbgcGdbStubCtxParseHexStringAsByteBuf(pbPktSep + 1, pThis->cbPkt - 1 - cbProcessed - 1, &RegVal.au8[0], pReg->cBits / 8, NULL);
1641 if (RT_SUCCESS(rc))
1642 {
1643 rc = DBGFR3RegNmSet(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, pReg->pszName, &RegVal, enmValType);
1644 if (RT_SUCCESS(rc))
1645 rc = dbgcGdbStubCtxReplySendOk(pThis);
1646 else
1647 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1648 }
1649 }
1650 else
1651 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NET_PROTOCOL_ERROR);
1652 }
1653 else
1654 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1655 break;
1656 }
1657 case 'Z': /* Insert a breakpoint/watchpoint. */
1658 {
1659 GDBSTUBTPTYPE enmTpType = GDBSTUBTPTYPE_INVALID;
1660 uint64_t GdbTgtTpAddr = 0;
1661 uint64_t uKind = 0;
1662
1663 rc = dbgcGdbStubCtxParseTpPktArgs(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &enmTpType, &GdbTgtTpAddr, &uKind);
1664 if (RT_SUCCESS(rc))
1665 {
1666 uint32_t iBp = 0;
1667 DBGFADDRESS BpAddr;
1668 DBGFR3AddrFromFlat(pThis->Dbgc.pUVM, &BpAddr, GdbTgtTpAddr);
1669
1670 switch (enmTpType)
1671 {
1672 case GDBSTUBTPTYPE_EXEC_SW:
1673 {
1674 rc = DBGFR3BpSetInt3(pThis->Dbgc.pUVM, pThis->Dbgc.idCpu, &BpAddr,
1675 1 /*iHitTrigger*/, UINT64_MAX /*iHitDisable*/, &iBp);
1676 break;
1677 }
1678 case GDBSTUBTPTYPE_EXEC_HW:
1679 {
1680 rc = DBGFR3BpSetReg(pThis->Dbgc.pUVM, &BpAddr,
1681 1 /*iHitTrigger*/, UINT64_MAX /*iHitDisable*/,
1682 X86_DR7_RW_EO, 1 /*cb*/, &iBp);
1683 break;
1684 }
1685 case GDBSTUBTPTYPE_MEM_ACCESS:
1686 case GDBSTUBTPTYPE_MEM_READ:
1687 {
1688 rc = DBGFR3BpSetReg(pThis->Dbgc.pUVM, &BpAddr,
1689 1 /*iHitTrigger*/, UINT64_MAX /*iHitDisable*/,
1690 X86_DR7_RW_RW, uKind /*cb*/, &iBp);
1691 break;
1692 }
1693 case GDBSTUBTPTYPE_MEM_WRITE:
1694 {
1695 rc = DBGFR3BpSetReg(pThis->Dbgc.pUVM, &BpAddr,
1696 1 /*iHitTrigger*/, UINT64_MAX /*iHitDisable*/,
1697 X86_DR7_RW_WO, uKind /*cb*/, &iBp);
1698 break;
1699 }
1700 default:
1701 AssertMsgFailed(("Invalid trace point type %d\n", enmTpType));
1702 }
1703
1704 if (RT_SUCCESS(rc))
1705 {
1706 rc = dbgcBpAdd(&pThis->Dbgc, iBp, NULL /*pszCmd*/);
1707 if (RT_SUCCESS(rc))
1708 {
1709 rc = dbgcGdbStubTpRegister(pThis, enmTpType, GdbTgtTpAddr, uKind, iBp);
1710 if (RT_SUCCESS(rc))
1711 rc = dbgcGdbStubCtxReplySendOk(pThis);
1712 else
1713 dbgcBpDelete(&pThis->Dbgc, iBp);
1714 }
1715
1716 if (RT_FAILURE(rc))
1717 {
1718 DBGFR3BpClear(pThis->Dbgc.pUVM, iBp);
1719 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1720 }
1721 }
1722 else
1723 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1724 }
1725 else
1726 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1727 break;
1728 }
1729 case 'z': /* Remove a breakpoint/watchpoint. */
1730 {
1731 GDBSTUBTPTYPE enmTpType = GDBSTUBTPTYPE_INVALID;
1732 uint64_t GdbTgtTpAddr = 0;
1733 uint64_t uKind = 0;
1734
1735 rc = dbgcGdbStubCtxParseTpPktArgs(&pThis->pbPktBuf[2], pThis->cbPkt - 1, &enmTpType, &GdbTgtTpAddr, &uKind);
1736 if (RT_SUCCESS(rc))
1737 {
1738 PGDBSTUBTP pTp = dbgcGdbStubTpFind(pThis, enmTpType, GdbTgtTpAddr, uKind);
1739 if (pTp)
1740 {
1741 int rc2 = DBGFR3BpClear(pThis->Dbgc.pUVM, pTp->iBp);
1742 if (RT_SUCCESS(rc2) || rc2 == VERR_DBGF_BP_NOT_FOUND)
1743 dbgcBpDelete(&pThis->Dbgc, pTp->iBp);
1744
1745 if (RT_SUCCESS(rc2))
1746 {
1747 dbgcGdbStubTpDeregister(pTp);
1748 rc = dbgcGdbStubCtxReplySendOk(pThis);
1749 }
1750 else
1751 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1752 }
1753 else
1754 rc = dbgcGdbStubCtxReplySendErrSts(pThis, VERR_NOT_FOUND);
1755 }
1756 else
1757 rc = dbgcGdbStubCtxReplySendErrSts(pThis, rc);
1758 break;
1759 }
1760 case 'q': /* Query packet */
1761 {
1762 rc = dbgcGdbStubCtxPktProcessQuery(pThis, &pThis->pbPktBuf[2], pThis->cbPkt - 1);
1763 break;
1764 }
1765 case 'v': /* Multiletter identifier (verbose?) */
1766 {
1767 rc = dbgcGdbStubCtxPktProcessV(pThis, &pThis->pbPktBuf[2], pThis->cbPkt - 1);
1768 break;
1769 }
1770 case 'R': /* Restart target. */
1771 {
1772 rc = dbgcGdbStubCtxReplySend(pThis, NULL, 0);
1773 break;
1774 }
1775 case 'k': /* Kill target. */
1776 {
1777 /* This is what the 'harakiri' command is doing. */
1778 for (;;)
1779 exit(126);
1780 break;
1781 }
1782 case 'D': /* Detach */
1783 {
1784 rc = dbgcGdbStubCtxReplySendOk(pThis);
1785 if (RT_SUCCESS(rc))
1786 rc = VERR_DBGC_QUIT;
1787 break;
1788 }
1789 default:
1790 /* Not supported, send empty reply. */
1791 rc = dbgcGdbStubCtxReplySend(pThis, NULL, 0);
1792 }
1793 }
1794
1795 return rc;
1796}
1797
1798
1799/**
1800 * Resets the packet buffer.
1801 *
1802 * @returns nothing.
1803 * @param pThis The GDB stub context.
1804 */
1805static void dbgcGdbStubCtxPktBufReset(PGDBSTUBCTX pThis)
1806{
1807 pThis->offPktBuf = 0;
1808 pThis->cbPkt = 0;
1809 pThis->cbChksumRecvLeft = 2;
1810}
1811
1812
1813/**
1814 * Resets the given GDB stub context to the initial state.
1815 *
1816 * @returns nothing.
1817 * @param pThis The GDB stub context.
1818 */
1819static void dbgcGdbStubCtxReset(PGDBSTUBCTX pThis)
1820{
1821 pThis->enmState = GDBSTUBRECVSTATE_PACKET_WAIT_FOR_START;
1822 dbgcGdbStubCtxPktBufReset(pThis);
1823}
1824
1825
1826/**
1827 * Searches for the start character in the current data buffer.
1828 *
1829 * @returns Status code.
1830 * @param pThis The GDB stub context.
1831 * @param cbData Number of new bytes in the packet buffer.
1832 * @param pcbProcessed Where to store the amount of bytes processed.
1833 */
1834static int dbgcGdbStubCtxPktBufSearchStart(PGDBSTUBCTX pThis, size_t cbData, size_t *pcbProcessed)
1835{
1836 int rc = VINF_SUCCESS;
1837 const uint8_t *pbStart = (const uint8_t *)memchr(pThis->pbPktBuf, GDBSTUB_PKT_START, cbData);
1838 if (pbStart)
1839 {
1840 /* Found the start character, align the start to the beginning of the packet buffer and advance the state machine. */
1841 memmove(pThis->pbPktBuf, pbStart, cbData - (pbStart - pThis->pbPktBuf));
1842 pThis->enmState = GDBSTUBRECVSTATE_PACKET_RECEIVE_BODY;
1843 *pcbProcessed = (uintptr_t)(pbStart - pThis->pbPktBuf);
1844 pThis->offPktBuf = 0;
1845 }
1846 else
1847 {
1848 /* Check for out of band characters. */
1849 if (memchr(pThis->pbPktBuf, GDBSTUB_OOB_INTERRUPT, cbData) != NULL)
1850 {
1851 /* Stop target and send packet to indicate the target has stopped. */
1852 if (!DBGFR3IsHalted(pThis->Dbgc.pUVM))
1853 rc = DBGFR3Halt(pThis->Dbgc.pUVM);
1854 /* The reply will be send in the event loop. */
1855 }
1856
1857 /* Not found, ignore the received data and reset the packet buffer. */
1858 dbgcGdbStubCtxPktBufReset(pThis);
1859 *pcbProcessed = cbData;
1860 }
1861
1862 return rc;
1863}
1864
1865
1866/**
1867 * Searches for the end character in the current data buffer.
1868 *
1869 * @returns Status code.
1870 * @param pThis The GDB stub context.
1871 * @param cbData Number of new bytes in the packet buffer.
1872 * @param pcbProcessed Where to store the amount of bytes processed.
1873 */
1874static int dbgcGdbStubCtxPktBufSearchEnd(PGDBSTUBCTX pThis, size_t cbData, size_t *pcbProcessed)
1875{
1876 const uint8_t *pbEnd = (const uint8_t *)memchr(&pThis->pbPktBuf[pThis->offPktBuf], GDBSTUB_PKT_END, cbData);
1877 if (pbEnd)
1878 {
1879 /* Found the end character, next comes the checksum. */
1880 pThis->enmState = GDBSTUBRECVSTATE_PACKET_RECEIVE_CHECKSUM;
1881
1882 *pcbProcessed = (uintptr_t)(pbEnd - &pThis->pbPktBuf[pThis->offPktBuf]) + 1;
1883 pThis->offPktBuf += *pcbProcessed;
1884 pThis->cbPkt = pThis->offPktBuf - 1; /* Don't account for the start and end character. */
1885 }
1886 else
1887 {
1888 /* Not found, still in the middle of a packet. */
1889 /** @todo Look for out of band characters. */
1890 *pcbProcessed = cbData;
1891 pThis->offPktBuf += cbData;
1892 }
1893
1894 return VINF_SUCCESS;
1895}
1896
1897
1898/**
1899 * Processes the checksum.
1900 *
1901 * @returns Status code.
1902 * @param pThis The GDB stub context.
1903 * @param cbData Number of new bytes in the packet buffer.
1904 * @param pcbProcessed Where to store the amount of bytes processed.
1905 */
1906static int dbgcGdbStubCtxPktBufProcessChksum(PGDBSTUBCTX pThis, size_t cbData, size_t *pcbProcessed)
1907{
1908 int rc = VINF_SUCCESS;
1909 size_t cbChksumProcessed = (cbData < pThis->cbChksumRecvLeft) ? cbData : pThis->cbChksumRecvLeft;
1910
1911 pThis->cbChksumRecvLeft -= cbChksumProcessed;
1912 if (!pThis->cbChksumRecvLeft)
1913 {
1914 /* Verify checksum of the whole packet. */
1915 uint8_t uChkSum = dbgcGdbStubCtxChrToHex(pThis->pbPktBuf[pThis->offPktBuf]) << 4
1916 | dbgcGdbStubCtxChrToHex(pThis->pbPktBuf[pThis->offPktBuf + 1]);
1917
1918 uint8_t uSum = 0;
1919 for (size_t i = 1; i < pThis->cbPkt; i++)
1920 uSum += pThis->pbPktBuf[i];
1921
1922 if (uSum == uChkSum)
1923 {
1924 /* Checksum matches, send acknowledge and continue processing the complete payload. */
1925 char chAck = '+';
1926 rc = dbgcGdbStubCtxWrite(pThis, &chAck, sizeof(chAck));
1927 if (RT_SUCCESS(rc))
1928 rc = dbgcGdbStubCtxPktProcess(pThis);
1929 }
1930 else
1931 {
1932 /* Send NACK and reset for the next packet. */
1933 char chAck = '-';
1934 rc = dbgcGdbStubCtxWrite(pThis, &chAck, sizeof(chAck));
1935 }
1936
1937 dbgcGdbStubCtxReset(pThis);
1938 }
1939
1940 *pcbProcessed += cbChksumProcessed;
1941 return rc;
1942}
1943
1944
1945/**
1946 * Process read data in the packet buffer based on the current state.
1947 *
1948 * @returns Status code.
1949 * @param pThis The GDB stub context.
1950 * @param cbData Number of new bytes in the packet buffer.
1951 */
1952static int dbgcGdbStubCtxPktBufProcess(PGDBSTUBCTX pThis, size_t cbData)
1953{
1954 int rc = VINF_SUCCESS;
1955
1956 while ( cbData
1957 && RT_SUCCESS(rc))
1958 {
1959 size_t cbProcessed = 0;
1960
1961 switch (pThis->enmState)
1962 {
1963 case GDBSTUBRECVSTATE_PACKET_WAIT_FOR_START:
1964 {
1965 rc = dbgcGdbStubCtxPktBufSearchStart(pThis, cbData, &cbProcessed);
1966 break;
1967 }
1968 case GDBSTUBRECVSTATE_PACKET_RECEIVE_BODY:
1969 {
1970 rc = dbgcGdbStubCtxPktBufSearchEnd(pThis, cbData, &cbProcessed);
1971 break;
1972 }
1973 case GDBSTUBRECVSTATE_PACKET_RECEIVE_CHECKSUM:
1974 {
1975 rc = dbgcGdbStubCtxPktBufProcessChksum(pThis, cbData, &cbProcessed);
1976 break;
1977 }
1978 default:
1979 /* Should never happen. */
1980 rc = VERR_INTERNAL_ERROR;
1981 }
1982
1983 cbData -= cbProcessed;
1984 }
1985
1986 return rc;
1987}
1988
1989
1990/**
1991 * Receive data and processes complete packets.
1992 *
1993 * @returns Status code.
1994 * @param pThis The GDB stub context.
1995 */
1996static int dbgcGdbStubCtxRecv(PGDBSTUBCTX pThis)
1997{
1998 /*
1999 * Read in 32 bytes chunks for now (need some peek API to get the amount of bytes actually available
2000 * to make it a bit more optimized).
2001 */
2002 int rc = dbgcGdbStubCtxEnsurePktBufSpace(pThis, 32);
2003 if (RT_SUCCESS(rc))
2004 {
2005 size_t cbThisRead = 32;
2006 rc = pThis->Dbgc.pBack->pfnRead(pThis->Dbgc.pBack, &pThis->pbPktBuf[pThis->offPktBuf], cbThisRead, &cbThisRead);
2007 if (RT_SUCCESS(rc))
2008 rc = dbgcGdbStubCtxPktBufProcess(pThis, cbThisRead);
2009 }
2010
2011 return rc;
2012}
2013
2014
2015/**
2016 * Processes debugger events.
2017 *
2018 * @returns VBox status code.
2019 * @param pThis The GDB stub context data.
2020 * @param pEvent Pointer to event data.
2021 */
2022static int dbgcGdbStubCtxProcessEvent(PGDBSTUBCTX pThis, PCDBGFEVENT pEvent)
2023{
2024 /*
2025 * Process the event.
2026 */
2027 PDBGC pDbgc = &pThis->Dbgc;
2028 pThis->Dbgc.pszScratch = &pThis->Dbgc.achInput[0];
2029 pThis->Dbgc.iArg = 0;
2030 int rc = VINF_SUCCESS;
2031 switch (pEvent->enmType)
2032 {
2033 /*
2034 * The first part is events we have initiated with commands.
2035 */
2036 case DBGFEVENT_HALT_DONE:
2037 {
2038 rc = dbgcGdbStubCtxReplySendSigTrap(pThis);
2039 break;
2040 }
2041
2042
2043 /*
2044 * The second part is events which can occur at any time.
2045 */
2046 case DBGFEVENT_FATAL_ERROR:
2047 {
2048 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbf event: Fatal error! (%s)\n",
2049 dbgcGetEventCtx(pEvent->enmCtx));
2050 if (RT_SUCCESS(rc))
2051 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2052 break;
2053 }
2054
2055 case DBGFEVENT_BREAKPOINT:
2056 case DBGFEVENT_BREAKPOINT_IO:
2057 case DBGFEVENT_BREAKPOINT_MMIO:
2058 case DBGFEVENT_BREAKPOINT_HYPER:
2059 {
2060 rc = dbgcBpExec(pDbgc, pEvent->u.Bp.iBp);
2061 switch (rc)
2062 {
2063 case VERR_DBGC_BP_NOT_FOUND:
2064 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Unknown breakpoint %u! (%s)\n",
2065 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
2066 break;
2067
2068 case VINF_DBGC_BP_NO_COMMAND:
2069 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! (%s)\n",
2070 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
2071 break;
2072
2073 case VINF_BUFFER_OVERFLOW:
2074 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: Breakpoint %u! Command too long to execute! (%s)\n",
2075 pEvent->u.Bp.iBp, dbgcGetEventCtx(pEvent->enmCtx));
2076 break;
2077
2078 default:
2079 break;
2080 }
2081 if (RT_SUCCESS(rc) && DBGFR3IsHalted(pDbgc->pUVM))
2082 {
2083 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2084
2085 /* Set the resume flag to ignore the breakpoint when resuming execution. */
2086 if ( RT_SUCCESS(rc)
2087 && pEvent->enmType == DBGFEVENT_BREAKPOINT)
2088 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r eflags.rf = 1");
2089 }
2090
2091 rc = dbgcGdbStubCtxReplySendSigTrap(pThis);
2092 break;
2093 }
2094
2095 case DBGFEVENT_STEPPED:
2096 case DBGFEVENT_STEPPED_HYPER:
2097 {
2098 rc = dbgcGdbStubCtxReplySendSigTrap(pThis);
2099 break;
2100 }
2101
2102 case DBGFEVENT_ASSERTION_HYPER:
2103 {
2104 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2105 "\ndbgf event: Hypervisor Assertion! (%s)\n"
2106 "%s"
2107 "%s"
2108 "\n",
2109 dbgcGetEventCtx(pEvent->enmCtx),
2110 pEvent->u.Assert.pszMsg1,
2111 pEvent->u.Assert.pszMsg2);
2112 if (RT_SUCCESS(rc))
2113 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2114 break;
2115 }
2116
2117 case DBGFEVENT_DEV_STOP:
2118 {
2119 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2120 "\n"
2121 "dbgf event: DBGFSTOP (%s)\n"
2122 "File: %s\n"
2123 "Line: %d\n"
2124 "Function: %s\n",
2125 dbgcGetEventCtx(pEvent->enmCtx),
2126 pEvent->u.Src.pszFile,
2127 pEvent->u.Src.uLine,
2128 pEvent->u.Src.pszFunction);
2129 if (RT_SUCCESS(rc) && pEvent->u.Src.pszMessage && *pEvent->u.Src.pszMessage)
2130 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL,
2131 "Message: %s\n",
2132 pEvent->u.Src.pszMessage);
2133 if (RT_SUCCESS(rc))
2134 rc = pDbgc->CmdHlp.pfnExec(&pDbgc->CmdHlp, "r");
2135 break;
2136 }
2137
2138
2139 case DBGFEVENT_INVALID_COMMAND:
2140 {
2141 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Invalid command event!\n");
2142 break;
2143 }
2144
2145 case DBGFEVENT_POWERING_OFF:
2146 {
2147 pThis->Dbgc.fReady = false;
2148 pThis->Dbgc.pBack->pfnSetReady(pThis->Dbgc.pBack, false);
2149 rc = VERR_GENERAL_FAILURE;
2150 break;
2151 }
2152
2153#if 0
2154 default:
2155 {
2156 /*
2157 * Probably a generic event. Look it up to find its name.
2158 */
2159 PCDBGCSXEVT pEvtDesc = dbgcEventLookup(pEvent->enmType);
2160 if (pEvtDesc)
2161 {
2162 if (pEvtDesc->enmKind == kDbgcSxEventKind_Interrupt)
2163 {
2164 Assert(pEvtDesc->pszDesc);
2165 Assert(pEvent->u.Generic.cArgs == 1);
2166 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s no %#llx! (%s)\n",
2167 pEvtDesc->pszDesc, pEvent->u.Generic.auArgs[0], pEvtDesc->pszName);
2168 }
2169 else if (pEvtDesc->fFlags & DBGCSXEVT_F_BUGCHECK)
2170 {
2171 Assert(pEvent->u.Generic.cArgs >= 5);
2172 char szDetails[512];
2173 DBGFR3FormatBugCheck(pDbgc->pUVM, szDetails, sizeof(szDetails), pEvent->u.Generic.auArgs[0],
2174 pEvent->u.Generic.auArgs[1], pEvent->u.Generic.auArgs[2],
2175 pEvent->u.Generic.auArgs[3], pEvent->u.Generic.auArgs[4]);
2176 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s %s%s!\n%s", pEvtDesc->pszName,
2177 pEvtDesc->pszDesc ? "- " : "", pEvtDesc->pszDesc ? pEvtDesc->pszDesc : "",
2178 szDetails);
2179 }
2180 else if ( (pEvtDesc->fFlags & DBGCSXEVT_F_TAKE_ARG)
2181 || pEvent->u.Generic.cArgs > 1
2182 || ( pEvent->u.Generic.cArgs == 1
2183 && pEvent->u.Generic.auArgs[0] != 0))
2184 {
2185 if (pEvtDesc->pszDesc)
2186 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s - %s!",
2187 pEvtDesc->pszName, pEvtDesc->pszDesc);
2188 else
2189 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s!", pEvtDesc->pszName);
2190 if (pEvent->u.Generic.cArgs <= 1)
2191 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " arg=%#llx\n", pEvent->u.Generic.auArgs[0]);
2192 else
2193 {
2194 for (uint32_t i = 0; i < pEvent->u.Generic.cArgs; i++)
2195 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, " args[%u]=%#llx", i, pEvent->u.Generic.auArgs[i]);
2196 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\n");
2197 }
2198 }
2199 else
2200 {
2201 if (pEvtDesc->pszDesc)
2202 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s - %s!\n",
2203 pEvtDesc->pszName, pEvtDesc->pszDesc);
2204 else
2205 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf event: %s!\n", pEvtDesc->pszName);
2206 }
2207 }
2208 else
2209 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "\ndbgf/dbgc error: Unknown event %d!\n", pEvent->enmType);
2210 break;
2211 }
2212 }
2213
2214 /*
2215 * Prompt, anyone?
2216 */
2217 if (fPrintPrompt && RT_SUCCESS(rc))
2218 {
2219 rc = pDbgc->CmdHlp.pfnPrintf(&pDbgc->CmdHlp, NULL, "VBoxDbg> ");
2220 pDbgc->fReady = true;
2221 if (RT_SUCCESS(rc))
2222 pDbgc->pBack->pfnSetReady(pDbgc->pBack, true);
2223 pDbgc->cMultiStepsLeft = 0;
2224 }
2225#else
2226 default:
2227 break;
2228 }
2229#endif
2230
2231 return rc;
2232}
2233
2234
2235/**
2236 * Run the debugger console.
2237 *
2238 * @returns VBox status code.
2239 * @param pThis Pointer to the GDB stub context.
2240 */
2241int dbgcGdbStubRun(PGDBSTUBCTX pThis)
2242{
2243 /*
2244 * We're ready for commands now.
2245 */
2246 pThis->Dbgc.fReady = true;
2247 pThis->Dbgc.pBack->pfnSetReady(pThis->Dbgc.pBack, true);
2248
2249 /*
2250 * Main Debugger Loop.
2251 *
2252 * This loop will either block on waiting for input or on waiting on
2253 * debug events. If we're forwarding the log we cannot wait for long
2254 * before we must flush the log.
2255 */
2256 int rc;
2257 for (;;)
2258 {
2259 rc = VERR_SEM_OUT_OF_TURN;
2260 if (pThis->Dbgc.pUVM)
2261 rc = DBGFR3QueryWaitable(pThis->Dbgc.pUVM);
2262
2263 if (RT_SUCCESS(rc))
2264 {
2265 /*
2266 * Wait for a debug event.
2267 */
2268 PCDBGFEVENT pEvent;
2269 rc = DBGFR3EventWait(pThis->Dbgc.pUVM, 32, &pEvent);
2270 if (RT_SUCCESS(rc))
2271 {
2272 rc = dbgcGdbStubCtxProcessEvent(pThis, pEvent);
2273 if (RT_FAILURE(rc))
2274 break;
2275 }
2276 else if (rc != VERR_TIMEOUT)
2277 break;
2278
2279 /*
2280 * Check for input.
2281 */
2282 if (pThis->Dbgc.pBack->pfnInput(pThis->Dbgc.pBack, 0))
2283 {
2284 rc = dbgcGdbStubCtxRecv(pThis);
2285 if (RT_FAILURE(rc))
2286 break;
2287 }
2288 }
2289 else if (rc == VERR_SEM_OUT_OF_TURN)
2290 {
2291 /*
2292 * Wait for input.
2293 */
2294 if (pThis->Dbgc.pBack->pfnInput(pThis->Dbgc.pBack, 1000))
2295 {
2296 rc = dbgcGdbStubCtxRecv(pThis);
2297 if (RT_FAILURE(rc))
2298 break;
2299 }
2300 }
2301 else
2302 break;
2303 }
2304
2305 return rc;
2306}
2307
2308
2309/**
2310 * @copydoc DBGC::pfnOutput
2311 */
2312static DECLCALLBACK(int) dbgcOutputGdb(void *pvUser, const char *pachChars, size_t cbChars)
2313{
2314 PGDBSTUBCTX pThis = (PGDBSTUBCTX)pvUser;
2315
2316 pThis->fOutput = true;
2317 int rc = dbgcGdbStubCtxReplySendBegin(pThis);
2318 if (RT_SUCCESS(rc))
2319 {
2320 uint8_t chConOut = 'O';
2321 rc = dbgcGdbStubCtxReplySendData(pThis, &chConOut, sizeof(chConOut));
2322 if (RT_SUCCESS(rc))
2323 {
2324 /* Convert the characters to hex. */
2325 const char *pachCur = pachChars;
2326
2327 while ( cbChars
2328 && RT_SUCCESS(rc))
2329 {
2330 uint8_t achHex[512 + 1];
2331 size_t cbThisSend = RT_MIN((sizeof(achHex) - 1) / 2, cbChars); /* Each character needs two bytes. */
2332
2333 rc = dbgcGdbStubCtxEncodeBinaryAsHex(&achHex[0], cbThisSend * 2 + 1, pachCur, cbThisSend);
2334 if (RT_SUCCESS(rc))
2335 rc = dbgcGdbStubCtxReplySendData(pThis, &achHex[0], cbThisSend * 2);
2336
2337 pachCur += cbThisSend;
2338 cbChars -= cbThisSend;
2339 }
2340 }
2341
2342 dbgcGdbStubCtxReplySendEnd(pThis);
2343 }
2344
2345 return rc;
2346}
2347
2348
2349/**
2350 * Creates a GDB stub context instance with the given backend.
2351 *
2352 * @returns VBox status code.
2353 * @param ppGdbStubCtx Where to store the pointer to the GDB stub context instance on success.
2354 * @param pBack The backend to use for I/O.
2355 * @param fFlags Flags controlling the behavior.
2356 */
2357static int dbgcGdbStubCtxCreate(PPGDBSTUBCTX ppGdbStubCtx, PDBGCBACK pBack, unsigned fFlags)
2358{
2359 /*
2360 * Validate input.
2361 */
2362 AssertPtrReturn(pBack, VERR_INVALID_POINTER);
2363 AssertMsgReturn(!fFlags, ("%#x", fFlags), VERR_INVALID_PARAMETER);
2364
2365 /*
2366 * Allocate and initialize.
2367 */
2368 PGDBSTUBCTX pThis = (PGDBSTUBCTX)RTMemAllocZ(sizeof(*pThis));
2369 if (!pThis)
2370 return VERR_NO_MEMORY;
2371
2372 dbgcInitCmdHlp(&pThis->Dbgc);
2373 /*
2374 * This is compied from the native debug console (will be used for monitor commands)
2375 * in DBGCConsole.cpp. Try to keep both functions in sync.
2376 */
2377 pThis->Dbgc.pBack = pBack;
2378 pThis->Dbgc.pfnOutput = dbgcOutputGdb;
2379 pThis->Dbgc.pvOutputUser = pThis;
2380 pThis->Dbgc.pVM = NULL;
2381 pThis->Dbgc.pUVM = NULL;
2382 pThis->Dbgc.idCpu = 0;
2383 pThis->Dbgc.hDbgAs = DBGF_AS_GLOBAL;
2384 pThis->Dbgc.pszEmulation = "CodeView/WinDbg";
2385 pThis->Dbgc.paEmulationCmds = &g_aCmdsCodeView[0];
2386 pThis->Dbgc.cEmulationCmds = g_cCmdsCodeView;
2387 pThis->Dbgc.paEmulationFuncs = &g_aFuncsCodeView[0];
2388 pThis->Dbgc.cEmulationFuncs = g_cFuncsCodeView;
2389 //pThis->Dbgc.fLog = false;
2390 pThis->Dbgc.fRegTerse = true;
2391 pThis->Dbgc.fStepTraceRegs = true;
2392 //pThis->Dbgc.cPagingHierarchyDumps = 0;
2393 //pThis->Dbgc.DisasmPos = {0};
2394 //pThis->Dbgc.SourcePos = {0};
2395 //pThis->Dbgc.DumpPos = {0};
2396 pThis->Dbgc.pLastPos = &pThis->Dbgc.DisasmPos;
2397 //pThis->Dbgc.cbDumpElement = 0;
2398 //pThis->Dbgc.cVars = 0;
2399 //pThis->Dbgc.paVars = NULL;
2400 //pThis->Dbgc.pPlugInHead = NULL;
2401 //pThis->Dbgc.pFirstBp = NULL;
2402 //pThis->Dbgc.abSearch = {0};
2403 //pThis->Dbgc.cbSearch = 0;
2404 pThis->Dbgc.cbSearchUnit = 1;
2405 pThis->Dbgc.cMaxSearchHits = 1;
2406 //pThis->Dbgc.SearchAddr = {0};
2407 //pThis->Dbgc.cbSearchRange = 0;
2408
2409 //pThis->Dbgc.uInputZero = 0;
2410 //pThis->Dbgc.iRead = 0;
2411 //pThis->Dbgc.iWrite = 0;
2412 //pThis->Dbgc.cInputLines = 0;
2413 //pThis->Dbgc.fInputOverflow = false;
2414 pThis->Dbgc.fReady = true;
2415 pThis->Dbgc.pszScratch = &pThis->Dbgc.achScratch[0];
2416 //pThis->Dbgc.iArg = 0;
2417 //pThis->Dbgc.rcOutput = 0;
2418 //pThis->Dbgc.rcCmd = 0;
2419
2420 //pThis->Dbgc.pszHistoryFile = NULL;
2421 //pThis->Dbgc.pszGlobalInitScript = NULL;
2422 //pThis->Dbgc.pszLocalInitScript = NULL;
2423
2424 dbgcEvalInit();
2425
2426 /* Init the GDB stub specific parts. */
2427 pThis->cbPktBufMax = 0;
2428 pThis->pbPktBuf = NULL;
2429 pThis->fFeatures = GDBSTUBCTX_FEATURES_F_TGT_DESC;
2430 pThis->pachTgtXmlDesc = NULL;
2431 pThis->cbTgtXmlDesc = 0;
2432 pThis->fExtendedMode = false;
2433 pThis->fOutput = false;
2434 RTListInit(&pThis->LstTps);
2435 dbgcGdbStubCtxReset(pThis);
2436
2437 *ppGdbStubCtx = pThis;
2438 return VINF_SUCCESS;
2439}
2440
2441
2442/**
2443 * Destroys the given GDB stub context.
2444 *
2445 * @returns nothing.
2446 * @param pThis The GDB stub context to destroy.
2447 */
2448static void dbgcGdbStubDestroy(PGDBSTUBCTX pThis)
2449{
2450 AssertPtr(pThis);
2451
2452 /* Detach from the VM. */
2453 if (pThis->Dbgc.pUVM)
2454 DBGFR3Detach(pThis->Dbgc.pUVM);
2455
2456 /* Free config strings. */
2457 RTStrFree(pThis->Dbgc.pszGlobalInitScript);
2458 pThis->Dbgc.pszGlobalInitScript = NULL;
2459 RTStrFree(pThis->Dbgc.pszLocalInitScript);
2460 pThis->Dbgc.pszLocalInitScript = NULL;
2461 RTStrFree(pThis->Dbgc.pszHistoryFile);
2462 pThis->Dbgc.pszHistoryFile = NULL;
2463
2464 /* Finally, free the instance memory. */
2465 RTMemFree(pThis);
2466}
2467
2468
2469DECLHIDDEN(int) dbgcGdbStubCreate(PUVM pUVM, PDBGCBACK pBack, unsigned fFlags)
2470{
2471 /*
2472 * Validate input.
2473 */
2474 AssertPtrNullReturn(pUVM, VERR_INVALID_VM_HANDLE);
2475 PVM pVM = NULL;
2476 if (pUVM)
2477 {
2478 pVM = VMR3GetVM(pUVM);
2479 AssertPtrReturn(pVM, VERR_INVALID_VM_HANDLE);
2480 }
2481
2482 /*
2483 * Allocate and initialize instance data
2484 */
2485 PGDBSTUBCTX pThis;
2486 int rc = dbgcGdbStubCtxCreate(&pThis, pBack, fFlags);
2487 if (RT_FAILURE(rc))
2488 return rc;
2489 if (!HMR3IsEnabled(pUVM) && !NEMR3IsEnabled(pUVM))
2490 pThis->Dbgc.hDbgAs = DBGF_AS_RC_AND_GC_GLOBAL;
2491
2492 /*
2493 * Attach to the specified VM.
2494 */
2495 if (RT_SUCCESS(rc) && pUVM)
2496 {
2497 rc = DBGFR3Attach(pUVM);
2498 if (RT_SUCCESS(rc))
2499 {
2500 pThis->Dbgc.pVM = pVM;
2501 pThis->Dbgc.pUVM = pUVM;
2502 pThis->Dbgc.idCpu = 0;
2503 rc = pThis->Dbgc.CmdHlp.pfnPrintf(&pThis->Dbgc.CmdHlp, NULL,
2504 "Current VM is %08x, CPU #%u\n" /** @todo get and print the VM name! */
2505 , pThis->Dbgc.pVM, pThis->Dbgc.idCpu);
2506 }
2507 else
2508 rc = pThis->Dbgc.CmdHlp.pfnVBoxError(&pThis->Dbgc.CmdHlp, rc, "When trying to attach to VM %p\n", pThis->Dbgc.pVM);
2509 }
2510
2511 /*
2512 * Load plugins.
2513 */
2514 if (RT_SUCCESS(rc))
2515 {
2516 if (pVM)
2517 DBGFR3PlugInLoadAll(pThis->Dbgc.pUVM);
2518 dbgcEventInit(&pThis->Dbgc);
2519 //dbgcRunInitScripts(pDbgc); Not yet
2520
2521 if (!DBGFR3IsHalted(pThis->Dbgc.pUVM))
2522 rc = DBGFR3Halt(pThis->Dbgc.pUVM);
2523
2524 /*
2525 * Run the debugger main loop.
2526 */
2527 rc = dbgcGdbStubRun(pThis);
2528 dbgcEventTerm(&pThis->Dbgc);
2529 }
2530
2531 /*
2532 * Cleanup console debugger session.
2533 */
2534 dbgcGdbStubDestroy(pThis);
2535 return rc == VERR_DBGC_QUIT ? VINF_SUCCESS : rc;
2536}
2537
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette