VirtualBox

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

Last change on this file since 107166 was 107166, checked in by vboxsync, 7 weeks ago

Debugger/GDB: Add some basic arm64 support for the GDB remote stub, bugref:10393

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