remote_gdb.cc revision 12466
111661Stushar@ece.gatech.edu/*
211661Stushar@ece.gatech.edu * Copyright 2015 LabWare
311661Stushar@ece.gatech.edu * Copyright 2014 Google, Inc.
411661Stushar@ece.gatech.edu * Copyright (c) 2002-2005 The Regents of The University of Michigan
511661Stushar@ece.gatech.edu * All rights reserved.
611661Stushar@ece.gatech.edu *
711661Stushar@ece.gatech.edu * Redistribution and use in source and binary forms, with or without
811661Stushar@ece.gatech.edu * modification, are permitted provided that the following conditions are
911661Stushar@ece.gatech.edu * met: redistributions of source code must retain the above copyright
1011661Stushar@ece.gatech.edu * notice, this list of conditions and the following disclaimer;
1111661Stushar@ece.gatech.edu * redistributions in binary form must reproduce the above copyright
1211661Stushar@ece.gatech.edu * notice, this list of conditions and the following disclaimer in the
1311661Stushar@ece.gatech.edu * documentation and/or other materials provided with the distribution;
1411661Stushar@ece.gatech.edu * neither the name of the copyright holders nor the names of its
1511661Stushar@ece.gatech.edu * contributors may be used to endorse or promote products derived from
1611661Stushar@ece.gatech.edu * this software without specific prior written permission.
1711661Stushar@ece.gatech.edu *
1811661Stushar@ece.gatech.edu * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1911661Stushar@ece.gatech.edu * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
2011661Stushar@ece.gatech.edu * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
2111661Stushar@ece.gatech.edu * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
2211661Stushar@ece.gatech.edu * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
2311661Stushar@ece.gatech.edu * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
2411661Stushar@ece.gatech.edu * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2511661Stushar@ece.gatech.edu * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2611661Stushar@ece.gatech.edu * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2711661Stushar@ece.gatech.edu * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
2811661Stushar@ece.gatech.edu * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2911661Stushar@ece.gatech.edu *
3011661Stushar@ece.gatech.edu * Authors: Nathan Binkert
3111793Sbrandon.potter@amd.com *          Boris Shingarov
3211793Sbrandon.potter@amd.com */
3311661Stushar@ece.gatech.edu
3411661Stushar@ece.gatech.edu/*
3511661Stushar@ece.gatech.edu * Copyright (c) 1990, 1993 The Regents of the University of California
3611661Stushar@ece.gatech.edu * All rights reserved
3711661Stushar@ece.gatech.edu *
3811661Stushar@ece.gatech.edu * This software was developed by the Computer Systems Engineering group
3912334Sgabeblack@google.com * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and
4011661Stushar@ece.gatech.edu * contributed to Berkeley.
4111661Stushar@ece.gatech.edu *
4211661Stushar@ece.gatech.edu * All advertising materials mentioning features or use of this software
4311661Stushar@ece.gatech.edu * must display the following acknowledgement:
4411661Stushar@ece.gatech.edu *      This product includes software developed by the University of
4511661Stushar@ece.gatech.edu *      California, Lawrence Berkeley Laboratories.
4611661Stushar@ece.gatech.edu *
4711661Stushar@ece.gatech.edu * Redistribution and use in source and binary forms, with or without
4811661Stushar@ece.gatech.edu * modification, are permitted provided that the following conditions
4911661Stushar@ece.gatech.edu * are met:
5011661Stushar@ece.gatech.edu * 1. Redistributions of source code must retain the above copyright
5111661Stushar@ece.gatech.edu *    notice, this list of conditions and the following disclaimer.
5211661Stushar@ece.gatech.edu * 2. Redistributions in binary form must reproduce the above copyright
5311661Stushar@ece.gatech.edu *    notice, this list of conditions and the following disclaimer in the
5411661Stushar@ece.gatech.edu *    documentation and/or other materials provided with the distribution.
5511661Stushar@ece.gatech.edu * 3. All advertising materials mentioning features or use of this software
5611661Stushar@ece.gatech.edu *    must display the following acknowledgement:
5711661Stushar@ece.gatech.edu *      This product includes software developed by the University of
5811661Stushar@ece.gatech.edu *      California, Berkeley and its contributors.
5911661Stushar@ece.gatech.edu * 4. Neither the name of the University nor the names of its contributors
6011661Stushar@ece.gatech.edu *    may be used to endorse or promote products derived from this software
6111661Stushar@ece.gatech.edu *    without specific prior written permission.
6211661Stushar@ece.gatech.edu *
6311661Stushar@ece.gatech.edu * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
6411661Stushar@ece.gatech.edu * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
6511661Stushar@ece.gatech.edu * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
6611661Stushar@ece.gatech.edu * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
6711661Stushar@ece.gatech.edu * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
6811661Stushar@ece.gatech.edu * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
6911661Stushar@ece.gatech.edu * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
7011661Stushar@ece.gatech.edu * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
7111661Stushar@ece.gatech.edu * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
7211661Stushar@ece.gatech.edu * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
7311661Stushar@ece.gatech.edu * SUCH DAMAGE.
7411661Stushar@ece.gatech.edu *
7511661Stushar@ece.gatech.edu *      @(#)kgdb_stub.c 8.4 (Berkeley) 1/12/94
7611661Stushar@ece.gatech.edu */
7711661Stushar@ece.gatech.edu
7811661Stushar@ece.gatech.edu/*-
7912129Sspwilson2@wisc.edu * Copyright (c) 2001 The NetBSD Foundation, Inc.
8012129Sspwilson2@wisc.edu * All rights reserved.
8111661Stushar@ece.gatech.edu *
8211661Stushar@ece.gatech.edu * This code is derived from software contributed to The NetBSD Foundation
8311661Stushar@ece.gatech.edu * by Jason R. Thorpe.
8411661Stushar@ece.gatech.edu *
8511661Stushar@ece.gatech.edu * Redistribution and use in source and binary forms, with or without
8611661Stushar@ece.gatech.edu * modification, are permitted provided that the following conditions
8711661Stushar@ece.gatech.edu * are met:
8811661Stushar@ece.gatech.edu * 1. Redistributions of source code must retain the above copyright
8911661Stushar@ece.gatech.edu *    notice, this list of conditions and the following disclaimer.
9011661Stushar@ece.gatech.edu * 2. Redistributions in binary form must reproduce the above copyright
9111661Stushar@ece.gatech.edu *    notice, this list of conditions and the following disclaimer in the
9211661Stushar@ece.gatech.edu *    documentation and/or other materials provided with the distribution.
9311661Stushar@ece.gatech.edu * 3. All advertising materials mentioning features or use of this software
9411661Stushar@ece.gatech.edu *    must display the following acknowledgement:
9511661Stushar@ece.gatech.edu *      This product includes software developed by the NetBSD
9612680Sgiacomo.travaglini@arm.com *      Foundation, Inc. and its contributors.
9711661Stushar@ece.gatech.edu * 4. Neither the name of The NetBSD Foundation nor the names of its
9811661Stushar@ece.gatech.edu *    contributors may be used to endorse or promote products derived
9911661Stushar@ece.gatech.edu *    from this software without specific prior written permission.
10011661Stushar@ece.gatech.edu *
10111661Stushar@ece.gatech.edu * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
10211661Stushar@ece.gatech.edu * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
10311661Stushar@ece.gatech.edu * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
10411661Stushar@ece.gatech.edu * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
10511661Stushar@ece.gatech.edu * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
10611661Stushar@ece.gatech.edu * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
10711661Stushar@ece.gatech.edu * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
10811661Stushar@ece.gatech.edu * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
10911661Stushar@ece.gatech.edu * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
11011661Stushar@ece.gatech.edu * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
11111661Stushar@ece.gatech.edu * POSSIBILITY OF SUCH DAMAGE.
11211661Stushar@ece.gatech.edu */
11313784Sgabeblack@google.com
11413784Sgabeblack@google.com/*
11511661Stushar@ece.gatech.edu * $NetBSD: kgdb_stub.c,v 1.8 2001/07/07 22:58:00 wdk Exp $
11611661Stushar@ece.gatech.edu *
11711661Stushar@ece.gatech.edu * Taken from NetBSD
11811661Stushar@ece.gatech.edu *
11913784Sgabeblack@google.com * "Stub" to allow remote cpu to debug over a serial line using gdb.
12011661Stushar@ece.gatech.edu */
12111661Stushar@ece.gatech.edu
12211661Stushar@ece.gatech.edu#include "base/remote_gdb.hh"
12311661Stushar@ece.gatech.edu
12411661Stushar@ece.gatech.edu#include <sys/signal.h>
12511661Stushar@ece.gatech.edu#include <unistd.h>
12611661Stushar@ece.gatech.edu
12711661Stushar@ece.gatech.edu#include <csignal>
12811661Stushar@ece.gatech.edu#include <cstdint>
12911661Stushar@ece.gatech.edu#include <cstdio>
13011661Stushar@ece.gatech.edu#include <string>
13111661Stushar@ece.gatech.edu
13211661Stushar@ece.gatech.edu#include "arch/vtophys.hh"
13311661Stushar@ece.gatech.edu#include "base/intmath.hh"
13411661Stushar@ece.gatech.edu#include "base/socket.hh"
13512749Sgiacomo.travaglini@arm.com#include "base/trace.hh"
13611661Stushar@ece.gatech.edu#include "config/the_isa.hh"
13711661Stushar@ece.gatech.edu#include "cpu/base.hh"
13811661Stushar@ece.gatech.edu#include "cpu/static_inst.hh"
13911661Stushar@ece.gatech.edu#include "cpu/thread_context.hh"
14011661Stushar@ece.gatech.edu#include "debug/GDBAll.hh"
14111661Stushar@ece.gatech.edu#include "mem/fs_translating_port_proxy.hh"
14211661Stushar@ece.gatech.edu#include "mem/port.hh"
14311661Stushar@ece.gatech.edu#include "mem/se_translating_port_proxy.hh"
14411661Stushar@ece.gatech.edu#include "sim/full_system.hh"
14511661Stushar@ece.gatech.edu#include "sim/system.hh"
14611661Stushar@ece.gatech.edu
14711661Stushar@ece.gatech.eduusing namespace std;
14811661Stushar@ece.gatech.eduusing namespace TheISA;
14911661Stushar@ece.gatech.edu
15011661Stushar@ece.gatech.edustatic const char GDBStart = '$';
15111661Stushar@ece.gatech.edustatic const char GDBEnd = '#';
15211661Stushar@ece.gatech.edustatic const char GDBGoodP = '+';
15311661Stushar@ece.gatech.edustatic const char GDBBadP = '-';
15411661Stushar@ece.gatech.edu
15511661Stushar@ece.gatech.edustatic const int GDBPacketBufLen = 1024;
15611661Stushar@ece.gatech.edu
15711661Stushar@ece.gatech.eduvector<BaseRemoteGDB *> debuggers;
15811661Stushar@ece.gatech.edu
15911661Stushar@ece.gatech.educlass HardBreakpoint : public PCEvent
16011661Stushar@ece.gatech.edu{
16111661Stushar@ece.gatech.edu  private:
16211661Stushar@ece.gatech.edu    BaseRemoteGDB *gdb;
16311661Stushar@ece.gatech.edu
16411661Stushar@ece.gatech.edu  public:
16511661Stushar@ece.gatech.edu    int refcount;
16611661Stushar@ece.gatech.edu
16711661Stushar@ece.gatech.edu  public:
16811661Stushar@ece.gatech.edu    HardBreakpoint(BaseRemoteGDB *_gdb, PCEventQueue *q, Addr pc)
16911661Stushar@ece.gatech.edu        : PCEvent(q, "HardBreakpoint Event", pc),
17011661Stushar@ece.gatech.edu          gdb(_gdb), refcount(0)
17111661Stushar@ece.gatech.edu    {
17211661Stushar@ece.gatech.edu        DPRINTF(GDBMisc, "creating hardware breakpoint at %#x\n", evpc);
17311661Stushar@ece.gatech.edu    }
17411661Stushar@ece.gatech.edu
17511661Stushar@ece.gatech.edu    const std::string name() const override { return gdb->name() + ".hwbkpt"; }
17611661Stushar@ece.gatech.edu
17711661Stushar@ece.gatech.edu    void
17811661Stushar@ece.gatech.edu    process(ThreadContext *tc) override
17911661Stushar@ece.gatech.edu    {
18011661Stushar@ece.gatech.edu        DPRINTF(GDBMisc, "handling hardware breakpoint at %#x\n", pc());
18111661Stushar@ece.gatech.edu
18211661Stushar@ece.gatech.edu        if (tc == gdb->tc)
18311661Stushar@ece.gatech.edu            gdb->trap(SIGTRAP);
18411661Stushar@ece.gatech.edu    }
18511661Stushar@ece.gatech.edu};
18611661Stushar@ece.gatech.edu
18711661Stushar@ece.gatech.edunamespace {
18811661Stushar@ece.gatech.edu
18911661Stushar@ece.gatech.edu// Exception to throw when the connection to the client is broken.
19011661Stushar@ece.gatech.edustruct BadClient
19111661Stushar@ece.gatech.edu{
19211661Stushar@ece.gatech.edu    const char *warning;
19311661Stushar@ece.gatech.edu    BadClient(const char *_warning=NULL) : warning(_warning)
19411661Stushar@ece.gatech.edu    {}
19511661Stushar@ece.gatech.edu};
19611661Stushar@ece.gatech.edu
19711661Stushar@ece.gatech.edu// Exception to throw when an error needs to be reported to the client.
19811661Stushar@ece.gatech.edustruct CmdError
19911661Stushar@ece.gatech.edu{
20011661Stushar@ece.gatech.edu    string error;
20111661Stushar@ece.gatech.edu    CmdError(std::string _error) : error(_error)
20211661Stushar@ece.gatech.edu    {}
20311661Stushar@ece.gatech.edu};
20411661Stushar@ece.gatech.edu
20511661Stushar@ece.gatech.edu// Exception to throw when something isn't supported.
20611661Stushar@ece.gatech.educlass Unsupported {};
20711661Stushar@ece.gatech.edu
20811661Stushar@ece.gatech.edu// Convert a hex digit into an integer.
20911661Stushar@ece.gatech.edu// This returns -1 if the argument passed is no valid hex digit.
21011661Stushar@ece.gatech.eduint
21111661Stushar@ece.gatech.edudigit2i(char c)
21211661Stushar@ece.gatech.edu{
21311661Stushar@ece.gatech.edu    if (c >= '0' && c <= '9')
21411661Stushar@ece.gatech.edu        return (c - '0');
21511661Stushar@ece.gatech.edu    else if (c >= 'a' && c <= 'f')
21611661Stushar@ece.gatech.edu        return (c - 'a' + 10);
21711661Stushar@ece.gatech.edu    else if (c >= 'A' && c <= 'F')
21811661Stushar@ece.gatech.edu        return (c - 'A' + 10);
21911661Stushar@ece.gatech.edu    else
22011661Stushar@ece.gatech.edu        return (-1);
22111661Stushar@ece.gatech.edu}
22211661Stushar@ece.gatech.edu
22311661Stushar@ece.gatech.edu// Convert the low 4 bits of an integer into an hex digit.
22411661Stushar@ece.gatech.educhar
22511661Stushar@ece.gatech.edui2digit(int n)
22611661Stushar@ece.gatech.edu{
22711661Stushar@ece.gatech.edu    return ("0123456789abcdef"[n & 0x0f]);
22811661Stushar@ece.gatech.edu}
22911661Stushar@ece.gatech.edu
23011661Stushar@ece.gatech.edu// Convert a byte array into an hex string.
23111661Stushar@ece.gatech.eduvoid
23211661Stushar@ece.gatech.edumem2hex(char *vdst, const char *vsrc, int len)
23311661Stushar@ece.gatech.edu{
23411661Stushar@ece.gatech.edu    char *dst = vdst;
23511661Stushar@ece.gatech.edu    const char *src = vsrc;
23611661Stushar@ece.gatech.edu
23711661Stushar@ece.gatech.edu    while (len--) {
23811661Stushar@ece.gatech.edu        *dst++ = i2digit(*src >> 4);
23911661Stushar@ece.gatech.edu        *dst++ = i2digit(*src++);
24011661Stushar@ece.gatech.edu    }
24111661Stushar@ece.gatech.edu    *dst = '\0';
24211661Stushar@ece.gatech.edu}
24311661Stushar@ece.gatech.edu
24411661Stushar@ece.gatech.edu// Convert an hex string into a byte array.
24511661Stushar@ece.gatech.edu// This returns a pointer to the character following the last valid
24611661Stushar@ece.gatech.edu// hex digit. If the string ends in the middle of a byte, NULL is
24711661Stushar@ece.gatech.edu// returned.
24811661Stushar@ece.gatech.educonst char *
24911661Stushar@ece.gatech.eduhex2mem(char *vdst, const char *src, int maxlen)
25011661Stushar@ece.gatech.edu{
25111661Stushar@ece.gatech.edu    char *dst = vdst;
25211661Stushar@ece.gatech.edu    int msb, lsb;
25311661Stushar@ece.gatech.edu
25411661Stushar@ece.gatech.edu    while (*src && maxlen--) {
25511661Stushar@ece.gatech.edu        msb = digit2i(*src++);
25611661Stushar@ece.gatech.edu        if (msb < 0)
25711661Stushar@ece.gatech.edu            return (src - 1);
25811661Stushar@ece.gatech.edu        lsb = digit2i(*src++);
25911661Stushar@ece.gatech.edu        if (lsb < 0)
26011661Stushar@ece.gatech.edu            return (NULL);
26111661Stushar@ece.gatech.edu        *dst++ = (msb << 4) | lsb;
26211661Stushar@ece.gatech.edu    }
26311661Stushar@ece.gatech.edu    return src;
26411661Stushar@ece.gatech.edu}
26511661Stushar@ece.gatech.edu
26611661Stushar@ece.gatech.edu// Convert an hex string into an integer.
26711661Stushar@ece.gatech.edu// This returns a pointer to the character following the last valid
26811661Stushar@ece.gatech.edu// hex digit.
26911661Stushar@ece.gatech.eduAddr
27011661Stushar@ece.gatech.eduhex2i(const char **srcp)
27111661Stushar@ece.gatech.edu{
27211661Stushar@ece.gatech.edu    const char *src = *srcp;
27311661Stushar@ece.gatech.edu    Addr r = 0;
27411661Stushar@ece.gatech.edu    int nibble;
27511661Stushar@ece.gatech.edu
27611661Stushar@ece.gatech.edu    while ((nibble = digit2i(*src)) >= 0) {
27711661Stushar@ece.gatech.edu        r *= 16;
27811661Stushar@ece.gatech.edu        r += nibble;
27912748Sgiacomo.travaglini@arm.com        src++;
28011661Stushar@ece.gatech.edu    }
28111661Stushar@ece.gatech.edu    *srcp = src;
28211661Stushar@ece.gatech.edu    return r;
28311661Stushar@ece.gatech.edu}
28411661Stushar@ece.gatech.edu
28511661Stushar@ece.gatech.eduenum GdbBreakpointType {
28611661Stushar@ece.gatech.edu    GdbSoftBp = '0',
28711661Stushar@ece.gatech.edu    GdbHardBp = '1',
28811661Stushar@ece.gatech.edu    GdbWriteWp = '2',
28911661Stushar@ece.gatech.edu    GdbReadWp = '3',
29011661Stushar@ece.gatech.edu    GdbAccWp = '4',
29111661Stushar@ece.gatech.edu};
29211661Stushar@ece.gatech.edu
29311661Stushar@ece.gatech.educonst char *
29411661Stushar@ece.gatech.edubreak_type(char c)
29511661Stushar@ece.gatech.edu{
29612749Sgiacomo.travaglini@arm.com    switch(c) {
29711661Stushar@ece.gatech.edu      case GdbSoftBp: return "software breakpoint";
29811661Stushar@ece.gatech.edu      case GdbHardBp: return "hardware breakpoint";
29911661Stushar@ece.gatech.edu      case GdbWriteWp: return "write watchpoint";
30011661Stushar@ece.gatech.edu      case GdbReadWp: return "read watchpoint";
30112749Sgiacomo.travaglini@arm.com      case GdbAccWp: return "access watchpoint";
30212749Sgiacomo.travaglini@arm.com      default: return "unknown breakpoint/watchpoint";
30311661Stushar@ece.gatech.edu    }
30411661Stushar@ece.gatech.edu}
30511661Stushar@ece.gatech.edu
30611661Stushar@ece.gatech.edustd::map<Addr, HardBreakpoint *> hardBreakMap;
30712749Sgiacomo.travaglini@arm.com
30811661Stushar@ece.gatech.eduEventQueue *
30911661Stushar@ece.gatech.edugetComInstEventQueue(ThreadContext *tc)
31011661Stushar@ece.gatech.edu{
31111661Stushar@ece.gatech.edu    return tc->getCpuPtr()->comInstEventQueue[tc->threadId()];
31211661Stushar@ece.gatech.edu}
31311661Stushar@ece.gatech.edu
31411661Stushar@ece.gatech.edu}
31511661Stushar@ece.gatech.edu
31611661Stushar@ece.gatech.eduBaseRemoteGDB::BaseRemoteGDB(System *_system, ThreadContext *c, int _port) :
31711661Stushar@ece.gatech.edu        connectEvent(nullptr), dataEvent(nullptr), _port(_port), fd(-1),
31811661Stushar@ece.gatech.edu        active(false), attached(false), sys(_system), tc(c),
31911661Stushar@ece.gatech.edu        trapEvent(this), singleStepEvent(*this)
32011661Stushar@ece.gatech.edu{
32111661Stushar@ece.gatech.edu    debuggers.push_back(this);
32211661Stushar@ece.gatech.edu}
32311661Stushar@ece.gatech.edu
32411661Stushar@ece.gatech.eduBaseRemoteGDB::~BaseRemoteGDB()
32511661Stushar@ece.gatech.edu{
32611661Stushar@ece.gatech.edu    delete connectEvent;
32711661Stushar@ece.gatech.edu    delete dataEvent;
32811661Stushar@ece.gatech.edu}
32911661Stushar@ece.gatech.edu
33011661Stushar@ece.gatech.edustring
33111661Stushar@ece.gatech.eduBaseRemoteGDB::name()
33211661Stushar@ece.gatech.edu{
33311661Stushar@ece.gatech.edu    return sys->name() + ".remote_gdb";
33411661Stushar@ece.gatech.edu}
33511661Stushar@ece.gatech.edu
33611661Stushar@ece.gatech.eduvoid
33711661Stushar@ece.gatech.eduBaseRemoteGDB::listen()
33811661Stushar@ece.gatech.edu{
33911661Stushar@ece.gatech.edu    if (ListenSocket::allDisabled()) {
34011661Stushar@ece.gatech.edu        warn_once("Sockets disabled, not accepting gdb connections");
34111661Stushar@ece.gatech.edu        return;
34211661Stushar@ece.gatech.edu    }
34311661Stushar@ece.gatech.edu
34411661Stushar@ece.gatech.edu    while (!listener.listen(_port, true)) {
34511661Stushar@ece.gatech.edu        DPRINTF(GDBMisc, "Can't bind port %d\n", _port);
34611661Stushar@ece.gatech.edu        _port++;
34711661Stushar@ece.gatech.edu    }
34811661Stushar@ece.gatech.edu
34911661Stushar@ece.gatech.edu    connectEvent = new ConnectEvent(this, listener.getfd(), POLLIN);
35011661Stushar@ece.gatech.edu    pollQueue.schedule(connectEvent);
35111661Stushar@ece.gatech.edu
35211661Stushar@ece.gatech.edu    ccprintf(cerr, "%d: %s: listening for remote gdb on port %d\n",
35311661Stushar@ece.gatech.edu             curTick(), name(), _port);
35411661Stushar@ece.gatech.edu}
35511661Stushar@ece.gatech.edu
35611661Stushar@ece.gatech.eduvoid
35711661Stushar@ece.gatech.eduBaseRemoteGDB::connect()
35811661Stushar@ece.gatech.edu{
359    panic_if(!listener.islistening(),
360             "Cannot accept GDB connections if we're not listening!");
361
362    int sfd = listener.accept(true);
363
364    if (sfd != -1) {
365        if (isAttached())
366            close(sfd);
367        else
368            attach(sfd);
369    }
370}
371
372int
373BaseRemoteGDB::port() const
374{
375    panic_if(!listener.islistening(),
376             "Remote GDB port is unknown until listen() has been called.\n");
377    return _port;
378}
379
380void
381BaseRemoteGDB::attach(int f)
382{
383    fd = f;
384
385    dataEvent = new DataEvent(this, fd, POLLIN);
386    pollQueue.schedule(dataEvent);
387
388    attached = true;
389    DPRINTFN("remote gdb attached\n");
390}
391
392void
393BaseRemoteGDB::detach()
394{
395    attached = false;
396    active = false;
397    clearSingleStep();
398    close(fd);
399    fd = -1;
400
401    pollQueue.remove(dataEvent);
402    DPRINTFN("remote gdb detached\n");
403}
404
405// This function does all command processing for interfacing to a
406// remote gdb.  Note that the error codes are ignored by gdb at
407// present, but might eventually become meaningful. (XXX) It might
408// makes sense to use POSIX errno values, because that is what the
409// gdb/remote.c functions want to return.
410bool
411BaseRemoteGDB::trap(int type)
412{
413
414    if (!attached)
415        return false;
416
417    DPRINTF(GDBMisc, "trap: PC=%s\n", tc->pcState());
418
419    clearSingleStep();
420
421    /*
422     * The first entry to this function is normally through
423     * a breakpoint trap in kgdb_connect(), in which case we
424     * must advance past the breakpoint because gdb will not.
425     *
426     * On the first entry here, we expect that gdb is not yet
427     * listening to us, so just enter the interaction loop.
428     * After the debugger is "active" (connected) it will be
429     * waiting for a "signaled" message from us.
430     */
431    if (!active) {
432        active = true;
433    } else {
434        // Tell remote host that an exception has occurred.
435        send(csprintf("S%02x", type).c_str());
436    }
437
438    // Stick frame regs into our reg cache.
439    regCachePtr = gdbRegs();
440    regCachePtr->getRegs(tc);
441
442    char data[GDBPacketBufLen + 1];
443    GdbCommand::Context cmdCtx;
444    cmdCtx.type = type;
445    cmdCtx.data = &data[1];
446
447    for (;;) {
448        try {
449            size_t datalen = recv(data, sizeof(data));
450            if (datalen < 1)
451                throw BadClient();
452
453            data[datalen] = 0; // Sentinel
454            cmdCtx.cmd_byte = data[0];
455            cmdCtx.len = datalen - 1;
456
457            auto cmdIt = command_map.find(cmdCtx.cmd_byte);
458            if (cmdIt == command_map.end()) {
459                DPRINTF(GDBMisc, "Unknown command: %c(%#x)\n",
460                        cmdCtx.cmd_byte, cmdCtx.cmd_byte);
461                throw Unsupported();
462            }
463            cmdCtx.cmd = &(cmdIt->second);
464
465            if (!(this->*(cmdCtx.cmd->func))(cmdCtx))
466                break;
467
468        } catch (BadClient &e) {
469            if (e.warning)
470                warn(e.warning);
471            detach();
472            break;
473        } catch (Unsupported &e) {
474            send("");
475        } catch (CmdError &e) {
476            send(e.error.c_str());
477        } catch (...) {
478            panic("Unrecognzied GDB exception.");
479        }
480    }
481
482    return true;
483}
484
485void
486BaseRemoteGDB::incomingData(int revent)
487{
488    if (trapEvent.scheduled()) {
489        warn("GDB trap event has already been scheduled!");
490        return;
491    }
492
493    if (revent & POLLIN) {
494        trapEvent.type(SIGILL);
495        scheduleInstCommitEvent(&trapEvent, 0);
496    } else if (revent & POLLNVAL) {
497        descheduleInstCommitEvent(&trapEvent);
498        detach();
499    }
500}
501
502uint8_t
503BaseRemoteGDB::getbyte()
504{
505    uint8_t b;
506    if (::read(fd, &b, sizeof(b)) == sizeof(b))
507        return b;
508
509    throw BadClient("Couldn't read data from debugger.");
510}
511
512void
513BaseRemoteGDB::putbyte(uint8_t b)
514{
515    if (::write(fd, &b, sizeof(b)) == sizeof(b))
516        return;
517
518    throw BadClient("Couldn't write data to the debugger.");
519}
520
521// Receive a packet from gdb
522int
523BaseRemoteGDB::recv(char *bp, int maxlen)
524{
525    char *p;
526    uint8_t c;
527    int csum;
528    int len;
529
530    do {
531        p = bp;
532        csum = len = 0;
533        // Find the beginning of a packet
534        while ((c = getbyte()) != GDBStart);
535
536        // Read until you find the end of the data in the packet, and keep
537        // track of the check sum.
538        while (len < maxlen) {
539            c = getbyte();
540            if (c == GDBEnd)
541                break;
542            c &= 0x7f;
543            csum += c;
544            *p++ = c;
545            len++;
546        }
547
548        // Mask the check sum, and terminate the command string.
549        csum &= 0xff;
550        *p = '\0';
551
552        // If the command was too long, report an error.
553        if (len >= maxlen) {
554            putbyte(GDBBadP);
555            continue;
556        }
557
558        // Bring in the checksum. If the check sum matches, csum will be 0.
559        csum -= digit2i(getbyte()) * 16;
560        csum -= digit2i(getbyte());
561
562        // If the check sum was correct
563        if (csum == 0) {
564            // Report that the packet was received correctly
565            putbyte(GDBGoodP);
566            // Sequence present?
567            if (bp[2] == ':') {
568                putbyte(bp[0]);
569                putbyte(bp[1]);
570                len -= 3;
571                memcpy(bp, bp+3, len);
572            }
573            break;
574        }
575        // Otherwise, report that there was a mistake.
576        putbyte(GDBBadP);
577    } while (1);
578
579    DPRINTF(GDBRecv, "recv:  %s\n", bp);
580
581    return len;
582}
583
584// Send a packet to gdb
585void
586BaseRemoteGDB::send(const char *bp)
587{
588    const char *p;
589    uint8_t csum, c;
590
591    DPRINTF(GDBSend, "send:  %s\n", bp);
592
593    do {
594        p = bp;
595        // Start sending a packet
596        putbyte(GDBStart);
597        // Send the contents, and also keep a check sum.
598        for (csum = 0; (c = *p); p++) {
599            putbyte(c);
600            csum += c;
601        }
602        // Send the ending character.
603        putbyte(GDBEnd);
604        // Send the checksum.
605        putbyte(i2digit(csum >> 4));
606        putbyte(i2digit(csum));
607        // Try transmitting over and over again until the other end doesn't
608        // send an error back.
609        c = getbyte();
610    } while ((c & 0x7f) == GDBBadP);
611}
612
613// Read bytes from kernel address space for debugger.
614bool
615BaseRemoteGDB::read(Addr vaddr, size_t size, char *data)
616{
617    static Addr lastaddr = 0;
618    static size_t lastsize = 0;
619
620    if (vaddr < 10) {
621      DPRINTF(GDBRead, "read:  reading memory location zero!\n");
622      vaddr = lastaddr + lastsize;
623    }
624
625    DPRINTF(GDBRead, "read:  addr=%#x, size=%d", vaddr, size);
626
627    if (FullSystem) {
628        FSTranslatingPortProxy &proxy = tc->getVirtProxy();
629        proxy.readBlob(vaddr, (uint8_t*)data, size);
630    } else {
631        SETranslatingPortProxy &proxy = tc->getMemProxy();
632        proxy.readBlob(vaddr, (uint8_t*)data, size);
633    }
634
635#if TRACING_ON
636    if (DTRACE(GDBRead)) {
637        if (DTRACE(GDBExtra)) {
638            char buf[1024];
639            mem2hex(buf, data, size);
640            DPRINTFNR(": %s\n", buf);
641        } else
642            DPRINTFNR("\n");
643    }
644#endif
645
646    return true;
647}
648
649// Write bytes to kernel address space for debugger.
650bool
651BaseRemoteGDB::write(Addr vaddr, size_t size, const char *data)
652{
653    static Addr lastaddr = 0;
654    static size_t lastsize = 0;
655
656    if (vaddr < 10) {
657      DPRINTF(GDBWrite, "write: writing memory location zero!\n");
658      vaddr = lastaddr + lastsize;
659    }
660
661    if (DTRACE(GDBWrite)) {
662        DPRINTFN("write: addr=%#x, size=%d", vaddr, size);
663        if (DTRACE(GDBExtra)) {
664            char buf[1024];
665            mem2hex(buf, data, size);
666            DPRINTFNR(": %s\n", buf);
667        } else
668            DPRINTFNR("\n");
669    }
670    if (FullSystem) {
671        FSTranslatingPortProxy &proxy = tc->getVirtProxy();
672        proxy.writeBlob(vaddr, (uint8_t*)data, size);
673    } else {
674        SETranslatingPortProxy &proxy = tc->getMemProxy();
675        proxy.writeBlob(vaddr, (uint8_t*)data, size);
676    }
677
678    return true;
679}
680
681void
682BaseRemoteGDB::singleStep()
683{
684    if (!singleStepEvent.scheduled())
685        scheduleInstCommitEvent(&singleStepEvent, 1);
686    trap(SIGTRAP);
687}
688
689void
690BaseRemoteGDB::clearSingleStep()
691{
692    descheduleInstCommitEvent(&singleStepEvent);
693}
694
695void
696BaseRemoteGDB::setSingleStep()
697{
698    if (!singleStepEvent.scheduled())
699        scheduleInstCommitEvent(&singleStepEvent, 1);
700}
701
702void
703BaseRemoteGDB::insertSoftBreak(Addr addr, size_t len)
704{
705    if (!checkBpLen(len))
706        throw BadClient("Invalid breakpoint length\n");
707
708    return insertHardBreak(addr, len);
709}
710
711void
712BaseRemoteGDB::removeSoftBreak(Addr addr, size_t len)
713{
714    if (!checkBpLen(len))
715        throw BadClient("Invalid breakpoint length.\n");
716
717    return removeHardBreak(addr, len);
718}
719
720void
721BaseRemoteGDB::insertHardBreak(Addr addr, size_t len)
722{
723    if (!checkBpLen(len))
724        throw BadClient("Invalid breakpoint length\n");
725
726    DPRINTF(GDBMisc, "Inserting hardware breakpoint at %#x\n", addr);
727
728    HardBreakpoint *&bkpt = hardBreakMap[addr];
729    if (bkpt == 0)
730        bkpt = new HardBreakpoint(this, &sys->pcEventQueue, addr);
731
732    bkpt->refcount++;
733}
734
735void
736BaseRemoteGDB::removeHardBreak(Addr addr, size_t len)
737{
738    if (!checkBpLen(len))
739        throw BadClient("Invalid breakpoint length\n");
740
741    DPRINTF(GDBMisc, "Removing hardware breakpoint at %#x\n", addr);
742
743    auto i = hardBreakMap.find(addr);
744    if (i == hardBreakMap.end())
745        throw CmdError("E0C");
746
747    HardBreakpoint *hbp = (*i).second;
748    if (--hbp->refcount == 0) {
749        delete hbp;
750        hardBreakMap.erase(i);
751    }
752}
753
754void
755BaseRemoteGDB::clearTempBreakpoint(Addr &bkpt)
756{
757    DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt);
758    removeHardBreak(bkpt, sizeof(TheISA::MachInst));
759    bkpt = 0;
760}
761
762void
763BaseRemoteGDB::setTempBreakpoint(Addr bkpt)
764{
765    DPRINTF(GDBMisc, "setTempBreakpoint: addr=%#x\n", bkpt);
766    insertHardBreak(bkpt, sizeof(TheISA::MachInst));
767}
768
769void
770BaseRemoteGDB::scheduleInstCommitEvent(Event *ev, int delta)
771{
772    EventQueue *eq = getComInstEventQueue(tc);
773    // Here "ticks" aren't simulator ticks which measure time, they're
774    // instructions committed by the CPU.
775    eq->schedule(ev, eq->getCurTick() + delta);
776}
777
778void
779BaseRemoteGDB::descheduleInstCommitEvent(Event *ev)
780{
781    if (ev->scheduled())
782        getComInstEventQueue(tc)->deschedule(ev);
783}
784
785std::map<char, BaseRemoteGDB::GdbCommand> BaseRemoteGDB::command_map = {
786    // last signal
787    { '?', { "KGDB_SIGNAL", &BaseRemoteGDB::cmd_signal } },
788    // set baud (deprecated)
789    { 'b', { "KGDB_SET_BAUD", &BaseRemoteGDB::cmd_unsupported } },
790    // set breakpoint (deprecated)
791    { 'B', { "KGDB_SET_BREAK", &BaseRemoteGDB::cmd_unsupported } },
792    // resume
793    { 'c', { "KGDB_CONT", &BaseRemoteGDB::cmd_cont } },
794    // continue with signal
795    { 'C', { "KGDB_ASYNC_CONT", &BaseRemoteGDB::cmd_async_cont } },
796    // toggle debug flags (deprecated)
797    { 'd', { "KGDB_DEBUG", &BaseRemoteGDB::cmd_unsupported } },
798    // detach remote gdb
799    { 'D', { "KGDB_DETACH", &BaseRemoteGDB::cmd_detach } },
800    // read general registers
801    { 'g', { "KGDB_REG_R", &BaseRemoteGDB::cmd_reg_r } },
802    // write general registers
803    { 'G', { "KGDB_REG_W", &BaseRemoteGDB::cmd_reg_w } },
804    // set thread
805    { 'H', { "KGDB_SET_THREAD", &BaseRemoteGDB::cmd_set_thread } },
806    // step a single cycle
807    { 'i', { "KGDB_CYCLE_STEP", &BaseRemoteGDB::cmd_unsupported } },
808    // signal then cycle step
809    { 'I', { "KGDB_SIG_CYCLE_STEP", &BaseRemoteGDB::cmd_unsupported } },
810    // kill program
811    { 'k', { "KGDB_KILL", &BaseRemoteGDB::cmd_detach } },
812    // read memory
813    { 'm', { "KGDB_MEM_R", &BaseRemoteGDB::cmd_mem_r } },
814    // write memory
815    { 'M', { "KGDB_MEM_W", &BaseRemoteGDB::cmd_mem_w } },
816    // read register
817    { 'p', { "KGDB_READ_REG", &BaseRemoteGDB::cmd_unsupported } },
818    // write register
819    { 'P', { "KGDB_SET_REG", &BaseRemoteGDB::cmd_unsupported } },
820    // query variable
821    { 'q', { "KGDB_QUERY_VAR", &BaseRemoteGDB::cmd_query_var } },
822    // set variable
823    { 'Q', { "KGDB_SET_VAR", &BaseRemoteGDB::cmd_unsupported } },
824    // reset system (deprecated)
825    { 'r', { "KGDB_RESET", &BaseRemoteGDB::cmd_unsupported } },
826    // step
827    { 's', { "KGDB_STEP", &BaseRemoteGDB::cmd_step } },
828    // signal and step
829    { 'S', { "KGDB_ASYNC_STEP", &BaseRemoteGDB::cmd_async_step } },
830    // find out if the thread is alive
831    { 'T', { "KGDB_THREAD_ALIVE", &BaseRemoteGDB::cmd_unsupported } },
832    // target exited
833    { 'W', { "KGDB_TARGET_EXIT", &BaseRemoteGDB::cmd_unsupported } },
834    // write memory
835    { 'X', { "KGDB_BINARY_DLOAD", &BaseRemoteGDB::cmd_unsupported } },
836    // remove breakpoint or watchpoint
837    { 'z', { "KGDB_CLR_HW_BKPT", &BaseRemoteGDB::cmd_clr_hw_bkpt } },
838    // insert breakpoint or watchpoint
839    { 'Z', { "KGDB_SET_HW_BKPT", &BaseRemoteGDB::cmd_set_hw_bkpt } },
840};
841
842bool
843BaseRemoteGDB::checkBpLen(size_t len)
844{
845    return len == sizeof(MachInst);
846}
847
848bool
849BaseRemoteGDB::cmd_unsupported(GdbCommand::Context &ctx)
850{
851    DPRINTF(GDBMisc, "Unsupported command: %s\n", ctx.cmd->name);
852    DDUMP(GDBMisc, ctx.data, ctx.len);
853    throw Unsupported();
854}
855
856
857bool
858BaseRemoteGDB::cmd_signal(GdbCommand::Context &ctx)
859{
860    send(csprintf("S%02x", ctx.type).c_str());
861    return true;
862}
863
864bool
865BaseRemoteGDB::cmd_cont(GdbCommand::Context &ctx)
866{
867    const char *p = ctx.data;
868    if (ctx.len) {
869        Addr newPc = hex2i(&p);
870        tc->pcState(newPc);
871    }
872    clearSingleStep();
873    return false;
874}
875
876bool
877BaseRemoteGDB::cmd_async_cont(GdbCommand::Context &ctx)
878{
879    const char *p = ctx.data;
880    hex2i(&p);
881    if (*p++ == ';') {
882        Addr newPc = hex2i(&p);
883        tc->pcState(newPc);
884    }
885    clearSingleStep();
886    return false;
887}
888
889bool
890BaseRemoteGDB::cmd_detach(GdbCommand::Context &ctx)
891{
892    detach();
893    return false;
894}
895
896bool
897BaseRemoteGDB::cmd_reg_r(GdbCommand::Context &ctx)
898{
899    char buf[2 * regCachePtr->size() + 1];
900    buf[2 * regCachePtr->size()] = '\0';
901    mem2hex(buf, regCachePtr->data(), regCachePtr->size());
902    send(buf);
903    return true;
904}
905
906bool
907BaseRemoteGDB::cmd_reg_w(GdbCommand::Context &ctx)
908{
909    const char *p = ctx.data;
910    p = hex2mem(regCachePtr->data(), p, regCachePtr->size());
911    if (p == NULL || *p != '\0')
912        throw CmdError("E01");
913
914    regCachePtr->setRegs(tc);
915    send("OK");
916
917    return true;
918}
919
920bool
921BaseRemoteGDB::cmd_set_thread(GdbCommand::Context &ctx)
922{
923    const char *p = ctx.data + 1; // Ignore the subcommand byte.
924    if (hex2i(&p) != 0)
925        throw CmdError("E01");
926    send("OK");
927    return true;
928}
929
930bool
931BaseRemoteGDB::cmd_mem_r(GdbCommand::Context &ctx)
932{
933    const char *p = ctx.data;
934    Addr addr = hex2i(&p);
935    if (*p++ != ',')
936        throw CmdError("E02");
937    size_t len = hex2i(&p);
938    if (*p != '\0')
939        throw CmdError("E03");
940    if (!acc(addr, len))
941        throw CmdError("E05");
942
943    char buf[len];
944    if (!read(addr, len, buf))
945        throw CmdError("E05");
946
947    char temp[2 * len + 1];
948    temp[2 * len] = '\0';
949    mem2hex(temp, buf, len);
950    send(temp);
951    return true;
952}
953
954bool
955BaseRemoteGDB::cmd_mem_w(GdbCommand::Context &ctx)
956{
957    const char *p = ctx.data;
958    Addr addr = hex2i(&p);
959    if (*p++ != ',')
960        throw CmdError("E06");
961    size_t len = hex2i(&p);
962    if (*p++ != ':')
963        throw CmdError("E07");
964    if (len * 2 > ctx.len - (p - ctx.data))
965        throw CmdError("E08");
966    char buf[len];
967    p = (char *)hex2mem(buf, p, len);
968    if (p == NULL)
969        throw CmdError("E09");
970    if (!acc(addr, len))
971        throw CmdError("E0A");
972    if (!write(addr, len, buf))
973        throw CmdError("E0B");
974    send("OK");
975    return true;
976}
977
978bool
979BaseRemoteGDB::cmd_query_var(GdbCommand::Context &ctx)
980{
981    if (string(ctx.data, ctx.len - 1) != "C")
982        throw Unsupported();
983    send("QC0");
984    return true;
985}
986
987bool
988BaseRemoteGDB::cmd_async_step(GdbCommand::Context &ctx)
989{
990    const char *p = ctx.data;
991    hex2i(&p); // Ignore the subcommand byte.
992    if (*p++ == ';') {
993        Addr newPc = hex2i(&p);
994        tc->pcState(newPc);
995    }
996    setSingleStep();
997    return false;
998}
999
1000bool
1001BaseRemoteGDB::cmd_step(GdbCommand::Context &ctx)
1002{
1003    if (ctx.len) {
1004        const char *p = ctx.data;
1005        Addr newPc = hex2i(&p);
1006        tc->pcState(newPc);
1007    }
1008    setSingleStep();
1009    return false;
1010}
1011
1012bool
1013BaseRemoteGDB::cmd_clr_hw_bkpt(GdbCommand::Context &ctx)
1014{
1015    const char *p = ctx.data;
1016    char subcmd = *p++;
1017    if (*p++ != ',')
1018        throw CmdError("E0D");
1019    Addr addr = hex2i(&p);
1020    if (*p++ != ',')
1021        throw CmdError("E0D");
1022    size_t len = hex2i(&p);
1023
1024    DPRINTF(GDBMisc, "clear %s, addr=%#x, len=%d\n",
1025            break_type(subcmd), addr, len);
1026
1027    switch (subcmd) {
1028      case GdbSoftBp:
1029        removeSoftBreak(addr, len);
1030        break;
1031      case GdbHardBp:
1032        removeHardBreak(addr, len);
1033        break;
1034      case GdbWriteWp:
1035      case GdbReadWp:
1036      case GdbAccWp:
1037      default: // unknown
1038        throw Unsupported();
1039    }
1040    send("OK");
1041
1042    return true;
1043}
1044
1045bool
1046BaseRemoteGDB::cmd_set_hw_bkpt(GdbCommand::Context &ctx)
1047{
1048    const char *p = ctx.data;
1049    char subcmd = *p++;
1050    if (*p++ != ',')
1051        throw CmdError("E0D");
1052    Addr addr = hex2i(&p);
1053    if (*p++ != ',')
1054        throw CmdError("E0D");
1055    size_t len = hex2i(&p);
1056
1057    DPRINTF(GDBMisc, "set %s, addr=%#x, len=%d\n",
1058            break_type(subcmd), addr, len);
1059
1060    switch (subcmd) {
1061      case GdbSoftBp:
1062        insertSoftBreak(addr, len);
1063        break;
1064      case GdbHardBp:
1065        insertHardBreak(addr, len);
1066        break;
1067      case GdbWriteWp:
1068      case GdbReadWp:
1069      case GdbAccWp:
1070      default: // unknown
1071        throw Unsupported();
1072    }
1073    send("OK");
1074
1075    return true;
1076}
1077