From nobody Mon Nov 10 11:20:56 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Authentication-Results: mx.zohomail.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail(p=none dis=none) header.from=gmail.com ARC-Seal: i=1; a=rsa-sha256; t=1556111271; cv=none; d=zoho.com; s=zohoarc; b=dyQje0Y16nWCNjpLUpEP1SZ+S995+N1cgXNEL7sa0g/CVPtFsTIp5Ujep9Bmhb+xRl6EutrC9lUavg5goKbm/hICVsEZ0htAEtj3ii4RDERXho/Q1RcZM3fdWCqcT9bw2FIB2qXZSIUSd5OU+7qnzZ1JMG5PmJx/AAX8tpDssLo= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zoho.com; s=zohoarc; t=1556111271; h=Content-Transfer-Encoding:Cc:Date:From:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:Sender:Subject:To:ARC-Authentication-Results; bh=0vsD8hBxgJ/GomROSQU/XCoJbb9UAqgdclAVsFYI8iY=; b=LP3k4fxhPShaRNOOuqe1cTEAotR60ngWcVXGRAYyJ40G8wJYLJqpQXXuKrHcAeA/xnhNrARAep/cyct2ppD6mguzUVBkmR1uyrsJZHaCnBC/6+yVtXQTqvPZr2NtfYSdfFZ9or0SbwvTk84dkB2Z8cU/ldIKsHtZzszNX5WpQYc= ARC-Authentication-Results: i=1; mx.zoho.com; dkim=fail; spf=pass (zoho.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=fail header.from= (p=none dis=none) header.from= Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1556111271234650.9737130134679; Wed, 24 Apr 2019 06:07:51 -0700 (PDT) Received: from localhost ([127.0.0.1]:41503 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hJHci-0000GU-KC for importer@patchew.org; Wed, 24 Apr 2019 09:07:40 -0400 Received: from eggs.gnu.org ([209.51.188.92]:48837) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1hJHY5-0005Wo-10 for qemu-devel@nongnu.org; Wed, 24 Apr 2019 09:03:09 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1hJHXq-0004rJ-99 for qemu-devel@nongnu.org; Wed, 24 Apr 2019 09:02:52 -0400 Received: from mail-wm1-x341.google.com ([2a00:1450:4864:20::341]:40067) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1hJHXp-0004qu-S0 for qemu-devel@nongnu.org; Wed, 24 Apr 2019 09:02:38 -0400 Received: by mail-wm1-x341.google.com with SMTP id z24so4972041wmi.5 for ; Wed, 24 Apr 2019 06:02:37 -0700 (PDT) Received: from linux.local ([31.154.166.148]) by smtp.gmail.com with ESMTPSA id z13sm12220150wrh.41.2019.04.24.06.02.33 (version=TLS1_2 cipher=ECDHE-RSA-CHACHA20-POLY1305 bits=256/256); Wed, 24 Apr 2019 06:02:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=0vsD8hBxgJ/GomROSQU/XCoJbb9UAqgdclAVsFYI8iY=; b=lCPyIF/mVNgoNvZ2oi1IFVActrEGlnxyfh/ePHk7LL7yvWy0TVBxYnCqSBBHLTG9aO eX+EwhDopT3067yl0KlJ8BxrB17vWSHpuSleSoY3ryIJId8a5iOxY46xl1PHJb5juqwO 6v6sax3hH0Y09now6xYTV1fd2yn581nzV+og8cB59WCh7exZfDSNTiJnUJ9lE4fQrx7h /a86dgVGtA8sVNo944jmkSU4LHszu5hlO+uHJM8dHTvXh23tLXIRgOyp/2kPOsvphmyn eBn49Q7Qjt/wYdbx57o6OlGKz2Zd4t2NIWsjwn7SCwhLf8HDLbyxXsLq2IgAEv+mtQOK aWvw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=0vsD8hBxgJ/GomROSQU/XCoJbb9UAqgdclAVsFYI8iY=; b=YlR0jyb0Dy8A9YBWMg1wtgRsVqtOmEGIkLPJJX2ANdeBv5TWhjmWDDhqW9H0l6AYMC rbaweCuZSRA3Sn2J1FipDyyHeqmQB4grNC2ArywBypkgvIi2D2xqDAfLXfGquLe2zqN3 7TFP2KlrJbFvDcARmWMrg3qMxI3wDGFnSS3rDqPzu+5f66l39gsaKvuRcvRDOY0D+EbR 5gxuofWSFsrXZ4I0uWmOV1HX37Kbc0ozVtq7A4/mk3RoEHTjfSoFce5diVF14/6ESGk3 2mJHuNdf/R4ibgw1c6L+TailCNc/eBAnn0Ba4rv7hpr5aa2xNRl/ZeXRfBnH+sqhOVh5 A8Aw== X-Gm-Message-State: APjAAAWYkHICuWuAKynYsKFEzTkXunuU8Kt6wVkXZLnV1IXdtxpV5rDI O7EaDxgT92XboPiu2rYtUJfiTyRW X-Google-Smtp-Source: APXvYqzqS/CCIHwP7On6BnhmxKD/7DFnYnUkyFmA4hVafQVcQMsR470j3AFi9fCX4d0FOPAknVWsGQ== X-Received: by 2002:a1c:cfcb:: with SMTP id f194mr6688645wmg.51.1556110955273; Wed, 24 Apr 2019 06:02:35 -0700 (PDT) From: Jon Doron To: qemu-devel@nongnu.org Date: Wed, 24 Apr 2019 16:02:22 +0300 Message-Id: <20190424130222.21537-1-arilou@gmail.com> X-Mailer: git-send-email 2.20.1 MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-detected-operating-system: by eggs.gnu.org: Genre and OS details not recognized. X-Received-From: 2a00:1450:4864:20::341 Subject: [Qemu-devel] [PATCH v2] gdbstub: Refactor gdb packets handler X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Jon Doron Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: "Qemu-devel" X-ZohoMail-DKIM: fail (Header signature does not verify) Content-Type: text/plain; charset="utf-8" Refactor the command packet handling code to ease the work of adding and extending commands that are currently not implemented. Signed-off-by: Jon Doron --- gdbstub.c | 1619 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 1128 insertions(+), 491 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index d54abd17cc..1fc6183472 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1130,14 +1130,6 @@ static GDBThreadIdKind read_thread_id(const char *bu= f, const char **end_buf, return GDB_ONE_THREAD; } =20 -static int is_query_packet(const char *p, const char *query, char separato= r) -{ - unsigned int query_len =3D strlen(query); - - return strncmp(p, query, query_len) =3D=3D 0 && - (p[query_len] =3D=3D '\0' || p[query_len] =3D=3D separator); -} - /** * gdb_handle_vcont - Parses and handles a vCont packet. * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if the= re is @@ -1268,528 +1260,1173 @@ out: return res; } =20 -static int gdb_handle_packet(GDBState *s, const char *line_buf) -{ - CPUState *cpu; - GDBProcess *process; - CPUClass *cc; - const char *p; - uint32_t pid, tid; - int ch, reg_size, type, res; - uint8_t mem_buf[MAX_PACKET_LENGTH]; - char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; - char thread_id[16]; - uint8_t *registers; - target_ulong addr, len; - GDBThreadIdKind thread_kind; - - trace_gdbstub_io_command(line_buf); +typedef union GdbCmdVariant { + const char *data; + uint8_t opcode; + unsigned long val_ul; + unsigned long long val_ull; + struct { + GDBThreadIdKind kind; + uint32_t pid; + uint32_t tid; + } thread_id; +} GdbCmdVariant; + +static const char *cmd_next_param(const char *param, const char delimiter) +{ + const char *delim; + static char all_delimiters[] =3D ",;:=3D"; + static char no_delimiter[] =3D "\0"; + char curr_delimiters[2] =3D {0}; + const char *delimiters; + + if (delimiter =3D=3D '?') { + delimiters =3D all_delimiters; + } else if (delimiter =3D=3D '0') { + delimiters =3D no_delimiter; + } else if (delimiter =3D=3D '.' && *param) { + return param + 1; + } else { + curr_delimiters[0] =3D delimiter; + delimiters =3D curr_delimiters; + } =20 - p =3D line_buf; - ch =3D *p++; - switch(ch) { - case '!': - put_packet(s, "OK"); - break; - case '?': - /* TODO: Make this return the correct value for user-mode. */ - snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, - gdb_fmt_thread_id(s, s->c_cpu, thread_id, sizeof(thread_i= d))); - put_packet(s, buf); - /* Remove all the breakpoints when this query is issued, - * because gdb is doing and initial connect and the state - * should be cleaned up. - */ - gdb_breakpoint_remove_all(); - break; - case 'c': - if (*p !=3D '\0') { - addr =3D strtoull(p, (char **)&p, 16); - gdb_set_cpu_pc(s, addr); - } - s->signal =3D 0; - gdb_continue(s); - return RS_IDLE; - case 'C': - s->signal =3D gdb_signal_to_target (strtoul(p, (char **)&p, 16)); - if (s->signal =3D=3D -1) - s->signal =3D 0; - gdb_continue(s); - return RS_IDLE; - case 'v': - if (strncmp(p, "Cont", 4) =3D=3D 0) { - p +=3D 4; - if (*p =3D=3D '?') { - put_packet(s, "vCont;c;C;s;S"); - break; + while (*param) { + delim =3D delimiters; + while (*delim) { + if (*param =3D=3D *delim) { + return param + 1; } + delim++; + } + param++; + } =20 - res =3D gdb_handle_vcont(s, p); + return param; +} =20 - if (res) { - if ((res =3D=3D -EINVAL) || (res =3D=3D -ERANGE)) { - put_packet(s, "E22"); - break; - } - goto unknown_command; - } - break; - } else if (strncmp(p, "Attach;", 7) =3D=3D 0) { - unsigned long pid; +static int cmd_parse_params(const char *data, const char *schema, + GdbCmdVariant *params, int *num_params) +{ + int curr_param; + const char *curr_schema, *curr_data; =20 - p +=3D 7; + *num_params =3D 0; =20 - if (qemu_strtoul(p, &p, 16, &pid)) { - put_packet(s, "E22"); - break; + if (!schema) { + return 0; + } + + curr_schema =3D schema; + curr_param =3D 0; + curr_data =3D data; + while (curr_schema[0] && curr_schema[1] && *curr_data) { + switch (curr_schema[0]) { + case 'l': + if (qemu_strtoul(curr_data, &curr_data, 16, + ¶ms[curr_param].val_ul)) { + return -EINVAL; } + curr_param++; + curr_data =3D cmd_next_param(curr_data, curr_schema[1]); + break; + case 'L': + if (qemu_strtou64(curr_data, &curr_data, 16, + (uint64_t *)¶ms[curr_param].val_ull)) { + return -EINVAL; + } + curr_param++; + curr_data =3D cmd_next_param(curr_data, curr_schema[1]); + break; + case 's': + params[curr_param].data =3D curr_data; + curr_param++; + curr_data =3D cmd_next_param(curr_data, curr_schema[1]); + break; + case 'o': + params[curr_param].opcode =3D *(uint8_t *)curr_data; + curr_param++; + curr_data =3D cmd_next_param(curr_data, curr_schema[1]); + break; + case 't': + params[curr_param].thread_id.kind =3D + read_thread_id(curr_data, &curr_data, + ¶ms[curr_param].thread_id.pid, + ¶ms[curr_param].thread_id.tid); + curr_param++; + curr_data =3D cmd_next_param(curr_data, curr_schema[1]); + break; + case 'x': + params[curr_param].data =3D curr_data; + curr_param++; + curr_data =3D cmd_next_param(curr_data, curr_schema[1]); + break; + case '?': + curr_data =3D cmd_next_param(curr_data, curr_schema[1]); + break; + default: + return -EINVAL; + } + curr_schema +=3D 2; + } =20 - process =3D gdb_get_process(s, pid); + *num_params =3D curr_param; + return 0; +} =20 - if (process =3D=3D NULL) { - put_packet(s, "E22"); - break; - } +typedef struct GdbCmdContext { + GDBState *s; + GdbCmdVariant *params; + int num_params; + uint8_t mem_buf[MAX_PACKET_LENGTH]; + char str_buf[MAX_PACKET_LENGTH + 1]; +} GdbCmdContext; =20 - cpu =3D get_first_cpu_in_process(s, process); +typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx); =20 - if (cpu =3D=3D NULL) { - /* Refuse to attach an empty process */ - put_packet(s, "E22"); - break; - } +/* + * cmd_startswith -> cmd is compared using startswith + * cmd_full_match -> cmd is compared using strcmp + * + * + * schema definitions: + * Each schema parameter entry consists of 2 chars, + * the first char represents the parameter type handling + * the second char represents the delimiter for the next parameter + * + * Currently supported schema types: + * 'l' -> unsigned long (stored in .val_ul) + * 'L' -> unsigned long long (stored in .val_ull) + * 's' -> string (stored in .data) + * 'o' -> single char (stored in .opcode) + * 't' -> thread id (stored in .thread_id) + * 'x' -> xml (stored in .data), must have a ':' delimiter + * '?' -> skip according to delimiter + * + * Currently supported delimiters: + * '?' -> Stop at any delimiter (",;:=3D\0") + * '0' -> Stop at "\0" + * '.' -> Skip 1 char unless reached "\0" + * Any other value is treated as the delimiter value itself + */ +typedef struct GdbCmdParseEntry { + GdbCmdHandler handler; + const char *cmd; + union { + int flags; + struct { + int cmd_startswith:1; + int cmd_full_match:1; + }; + }; + const char *schema; +} GdbCmdParseEntry; =20 - process->attached =3D true; +static inline int startswith(const char *string, const char *pattern) +{ + return !strncmp(string, pattern, strlen(pattern)); +} =20 - s->g_cpu =3D cpu; - s->c_cpu =3D cpu; +static int process_string_cmd(GDBState *s, void *user_ctx, const char *dat= a, + GdbCmdParseEntry *cmds, int num_cmds) +{ + int i, schema_len, max_num_params; + GdbCmdContext gdb_ctx; =20 - snprintf(buf, sizeof(buf), "T%02xthread:%s;", GDB_SIGNAL_TRAP, - gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id= ))); + if (!cmds) { + return -1; + } =20 - put_packet(s, buf); - break; - } else if (strncmp(p, "Kill;", 5) =3D=3D 0) { - /* Kill the target */ - put_packet(s, "OK"); - error_report("QEMU: Terminated via GDBstub"); - exit(0); - } else { - goto unknown_command; + for (i =3D 0; i < num_cmds; i++) { + if (!cmds[i].handler || !cmds[i].cmd || + (cmds[i].cmd_startswith && !startswith(data, cmds[i].cmd)) || + (cmds[i].cmd_full_match && strcmp(data, cmds[i].cmd))) { + continue; } - case 'k': - /* Kill the target */ - error_report("QEMU: Terminated via GDBstub"); - exit(0); - case 'D': - /* Detach packet */ - pid =3D 1; - - if (s->multiprocess) { - unsigned long lpid; - if (*p !=3D ';') { - put_packet(s, "E22"); - break; - } =20 - if (qemu_strtoul(p + 1, &p, 16, &lpid)) { - put_packet(s, "E22"); - break; + max_num_params =3D 0; + if (cmds[i].schema) { + schema_len =3D strlen(cmds[i].schema); + if (schema_len % 2) { + return -2; } =20 - pid =3D lpid; + max_num_params =3D schema_len / 2; } =20 - process =3D gdb_get_process(s, pid); - gdb_process_breakpoint_remove_all(s, process); - process->attached =3D false; + gdb_ctx.params =3D + (GdbCmdVariant *)alloca(sizeof(*gdb_ctx.params) * max_num_para= ms); + memset(gdb_ctx.params, 0, sizeof(*gdb_ctx.params) * max_num_params= ); =20 - if (pid =3D=3D gdb_get_cpu_pid(s, s->c_cpu)) { - s->c_cpu =3D gdb_first_attached_cpu(s); + if (cmd_parse_params(&data[strlen(cmds[i].cmd)], cmds[i].schema, + gdb_ctx.params, &gdb_ctx.num_params)) { + return -1; } =20 - if (pid =3D=3D gdb_get_cpu_pid(s, s->g_cpu)) { - s->g_cpu =3D gdb_first_attached_cpu(s); - } + gdb_ctx.s =3D s; + cmds[i].handler(&gdb_ctx, user_ctx); + return 0; + } =20 - if (s->c_cpu =3D=3D NULL) { - /* No more process attached */ - gdb_syscall_mode =3D GDB_SYS_DISABLED; - gdb_continue(s); - } - put_packet(s, "OK"); - break; - case 's': - if (*p !=3D '\0') { - addr =3D strtoull(p, (char **)&p, 16); - gdb_set_cpu_pc(s, addr); - } - cpu_single_step(s->c_cpu, sstep_flags); - gdb_continue(s); - return RS_IDLE; - case 'F': - { - target_ulong ret; - target_ulong err; - - ret =3D strtoull(p, (char **)&p, 16); - if (*p =3D=3D ',') { - p++; - err =3D strtoull(p, (char **)&p, 16); - } else { - err =3D 0; - } - if (*p =3D=3D ',') - p++; - type =3D *p; - if (s->current_syscall_cb) { - s->current_syscall_cb(s->c_cpu, ret, err); - s->current_syscall_cb =3D NULL; - } - if (type =3D=3D 'C') { - put_packet(s, "T02"); - } else { - gdb_continue(s); - } - } - break; - case 'g': - cpu_synchronize_state(s->g_cpu); - len =3D 0; - for (addr =3D 0; addr < s->g_cpu->gdb_num_g_regs; addr++) { - reg_size =3D gdb_read_register(s->g_cpu, mem_buf + len, addr); - len +=3D reg_size; - } - memtohex(buf, mem_buf, len); - put_packet(s, buf); - break; - case 'G': - cpu_synchronize_state(s->g_cpu); - registers =3D mem_buf; - len =3D strlen(p) / 2; - hextomem((uint8_t *)registers, p, len); - for (addr =3D 0; addr < s->g_cpu->gdb_num_g_regs && len > 0; addr+= +) { - reg_size =3D gdb_write_register(s->g_cpu, registers, addr); - len -=3D reg_size; - registers +=3D reg_size; - } - put_packet(s, "OK"); - break; - case 'm': - addr =3D strtoull(p, (char **)&p, 16); - if (*p =3D=3D ',') - p++; - len =3D strtoull(p, NULL, 16); + return -1; +} =20 - /* memtohex() doubles the required space */ - if (len > MAX_PACKET_LENGTH / 2) { - put_packet (s, "E22"); - break; - } +static void handle_extend_mode(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + put_packet(gdb_ctx->s, "OK"); +} =20 - if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, false) != =3D 0) { - put_packet (s, "E14"); - } else { - memtohex(buf, mem_buf, len); - put_packet(s, buf); - } - break; - case 'M': - addr =3D strtoull(p, (char **)&p, 16); - if (*p =3D=3D ',') - p++; - len =3D strtoull(p, (char **)&p, 16); - if (*p =3D=3D ':') - p++; - - /* hextomem() reads 2*len bytes */ - if (len > strlen(p) / 2) { - put_packet (s, "E22"); - break; - } - hextomem(mem_buf, p, len); - if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, - true) !=3D 0) { - put_packet(s, "E14"); - } else { - put_packet(s, "OK"); - } - break; - case 'p': - /* Older gdb are really dumb, and don't use 'g' if 'p' is avaialab= le. - This works, but can be very slow. Anything new enough to - understand XML also knows how to use this properly. */ - if (!gdb_has_xml) - goto unknown_command; - addr =3D strtoull(p, (char **)&p, 16); - reg_size =3D gdb_read_register(s->g_cpu, mem_buf, addr); - if (reg_size) { - memtohex(buf, mem_buf, reg_size); - put_packet(s, buf); - } else { - put_packet(s, "E14"); - } - break; - case 'P': - if (!gdb_has_xml) - goto unknown_command; - addr =3D strtoull(p, (char **)&p, 16); - if (*p =3D=3D '=3D') - p++; - reg_size =3D strlen(p) / 2; - hextomem(mem_buf, p, reg_size); - gdb_write_register(s->g_cpu, mem_buf, addr); - put_packet(s, "OK"); - break; - case 'Z': - case 'z': - type =3D strtoul(p, (char **)&p, 16); - if (*p =3D=3D ',') - p++; - addr =3D strtoull(p, (char **)&p, 16); - if (*p =3D=3D ',') - p++; - len =3D strtoull(p, (char **)&p, 16); - if (ch =3D=3D 'Z') - res =3D gdb_breakpoint_insert(addr, len, type); - else - res =3D gdb_breakpoint_remove(addr, len, type); - if (res >=3D 0) - put_packet(s, "OK"); - else if (res =3D=3D -ENOSYS) - put_packet(s, ""); - else - put_packet(s, "E22"); - break; - case 'H': - type =3D *p++; +static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + char thread_id[16]; =20 - thread_kind =3D read_thread_id(p, &p, &pid, &tid); - if (thread_kind =3D=3D GDB_READ_THREAD_ERR) { - put_packet(s, "E22"); - break; - } + /* TODO: Make this return the correct value for user-mode. */ + gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->c_cpu, thread_id, + sizeof(thread_id)); + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;", + GDB_SIGNAL_TRAP, thread_id); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); + /* + * Remove all the breakpoints when this query is issued, + * because gdb is doing and initial connect and the state + * should be cleaned up. + */ + gdb_breakpoint_remove_all(); +} =20 - if (thread_kind !=3D GDB_ONE_THREAD) { - put_packet(s, "OK"); - break; - } - cpu =3D gdb_get_cpu(s, pid, tid); - if (cpu =3D=3D NULL) { - put_packet(s, "E22"); - break; - } - switch (type) { - case 'c': - s->c_cpu =3D cpu; - put_packet(s, "OK"); - break; - case 'g': - s->g_cpu =3D cpu; - put_packet(s, "OK"); - break; - default: - put_packet(s, "E22"); - break; - } - break; - case 'T': - thread_kind =3D read_thread_id(p, &p, &pid, &tid); - if (thread_kind =3D=3D GDB_READ_THREAD_ERR) { - put_packet(s, "E22"); - break; - } - cpu =3D gdb_get_cpu(s, pid, tid); +static void handle_continue(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (gdb_ctx->num_params) { + gdb_set_cpu_pc(gdb_ctx->s, gdb_ctx->params[0].val_ull); + } =20 - if (cpu !=3D NULL) { - put_packet(s, "OK"); - } else { - put_packet(s, "E22"); - } - break; - case 'q': - case 'Q': - /* parse any 'q' packets here */ - if (!strcmp(p,"qemu.sstepbits")) { - /* Query Breakpoint bit definitions */ - snprintf(buf, sizeof(buf), "ENABLE=3D%x,NOIRQ=3D%x,NOTIMER=3D%= x", - SSTEP_ENABLE, - SSTEP_NOIRQ, - SSTEP_NOTIMER); - put_packet(s, buf); - break; - } else if (is_query_packet(p, "qemu.sstep", '=3D')) { - /* Display or change the sstep_flags */ - p +=3D 10; - if (*p !=3D '=3D') { - /* Display current setting */ - snprintf(buf, sizeof(buf), "0x%x", sstep_flags); - put_packet(s, buf); - break; - } - p++; - type =3D strtoul(p, (char **)&p, 16); - sstep_flags =3D type; - put_packet(s, "OK"); - break; - } else if (strcmp(p,"C") =3D=3D 0) { - /* - * "Current thread" remains vague in the spec, so always return - * the first thread of the current process (gdb returns the - * first thread). - */ - cpu =3D get_first_cpu_in_process(s, gdb_get_cpu_process(s, s->= g_cpu)); - snprintf(buf, sizeof(buf), "QC%s", - gdb_fmt_thread_id(s, cpu, thread_id, sizeof(thread_id= ))); - put_packet(s, buf); - break; - } else if (strcmp(p,"fThreadInfo") =3D=3D 0) { - s->query_cpu =3D gdb_first_attached_cpu(s); - goto report_cpuinfo; - } else if (strcmp(p,"sThreadInfo") =3D=3D 0) { - report_cpuinfo: - if (s->query_cpu) { - snprintf(buf, sizeof(buf), "m%s", - gdb_fmt_thread_id(s, s->query_cpu, - thread_id, sizeof(thread_id))); - put_packet(s, buf); - s->query_cpu =3D gdb_next_attached_cpu(s, s->query_cpu); - } else - put_packet(s, "l"); - break; - } else if (strncmp(p,"ThreadExtraInfo,", 16) =3D=3D 0) { - if (read_thread_id(p + 16, &p, &pid, &tid) =3D=3D GDB_READ_THR= EAD_ERR) { - put_packet(s, "E22"); - break; - } - cpu =3D gdb_get_cpu(s, pid, tid); - if (cpu !=3D NULL) { - cpu_synchronize_state(cpu); - - if (s->multiprocess && (s->process_num > 1)) { - /* Print the CPU model and name in multiprocess mode */ - ObjectClass *oc =3D object_get_class(OBJECT(cpu)); - const char *cpu_model =3D object_class_get_name(oc); - char *cpu_name =3D - object_get_canonical_path_component(OBJECT(cpu)); - len =3D snprintf((char *)mem_buf, sizeof(buf) / 2, - "%s %s [%s]", cpu_model, cpu_name, - cpu->halted ? "halted " : "running"); - g_free(cpu_name); - } else { - /* memtohex() doubles the required space */ - len =3D snprintf((char *)mem_buf, sizeof(buf) / 2, - "CPU#%d [%s]", cpu->cpu_index, - cpu->halted ? "halted " : "running"); - } - trace_gdbstub_op_extra_info((char *)mem_buf); - memtohex(buf, mem_buf, len); - put_packet(s, buf); - } - break; - } -#ifdef CONFIG_USER_ONLY - else if (strcmp(p, "Offsets") =3D=3D 0) { - TaskState *ts =3D s->c_cpu->opaque; + gdb_ctx->s->signal =3D 0; + gdb_continue(gdb_ctx->s); +} =20 - snprintf(buf, sizeof(buf), - "Text=3D" TARGET_ABI_FMT_lx ";Data=3D" TARGET_ABI_FMT= _lx - ";Bss=3D" TARGET_ABI_FMT_lx, - ts->info->code_offset, - ts->info->data_offset, - ts->info->data_offset); - put_packet(s, buf); - break; - } -#else /* !CONFIG_USER_ONLY */ - else if (strncmp(p, "Rcmd,", 5) =3D=3D 0) { - int len =3D strlen(p + 5); +static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + unsigned long signal =3D 0; =20 - if ((len % 2) !=3D 0) { - put_packet(s, "E01"); - break; - } - len =3D len / 2; - hextomem(mem_buf, p + 5, len); - mem_buf[len++] =3D 0; - qemu_chr_be_write(s->mon_chr, mem_buf, len); - put_packet(s, "OK"); - break; - } -#endif /* !CONFIG_USER_ONLY */ - if (is_query_packet(p, "Supported", ':')) { - snprintf(buf, sizeof(buf), "PacketSize=3D%x", MAX_PACKET_LENGT= H); - cc =3D CPU_GET_CLASS(first_cpu); - if (cc->gdb_core_xml_file !=3D NULL) { - pstrcat(buf, sizeof(buf), ";qXfer:features:read+"); - } + if (gdb_ctx->num_params) { + signal =3D gdb_ctx->params[0].val_ul; + } =20 - if (strstr(p, "multiprocess+")) { - s->multiprocess =3D true; - } - pstrcat(buf, sizeof(buf), ";multiprocess+"); + gdb_ctx->s->signal =3D gdb_signal_to_target(signal); + if (gdb_ctx->s->signal =3D=3D -1) { + gdb_ctx->s->signal =3D 0; + } + gdb_continue(gdb_ctx->s); +} =20 - put_packet(s, buf); - break; - } - if (strncmp(p, "Xfer:features:read:", 19) =3D=3D 0) { - const char *xml; - target_ulong total_len; - - process =3D gdb_get_cpu_process(s, s->g_cpu); - cc =3D CPU_GET_CLASS(s->g_cpu); - if (cc->gdb_core_xml_file =3D=3D NULL) { - goto unknown_command; - } +static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + put_packet(gdb_ctx->s, "vCont;c;C;s;S"); +} =20 - gdb_has_xml =3D true; - p +=3D 19; - xml =3D get_feature_xml(s, p, &p, process); - if (!xml) { - snprintf(buf, sizeof(buf), "E00"); - put_packet(s, buf); - break; - } +static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + int res; =20 - if (*p =3D=3D ':') - p++; - addr =3D strtoul(p, (char **)&p, 16); - if (*p =3D=3D ',') - p++; - len =3D strtoul(p, (char **)&p, 16); - - total_len =3D strlen(xml); - if (addr > total_len) { - snprintf(buf, sizeof(buf), "E00"); - put_packet(s, buf); - break; - } - if (len > (MAX_PACKET_LENGTH - 5) / 2) - len =3D (MAX_PACKET_LENGTH - 5) / 2; - if (len < total_len - addr) { - buf[0] =3D 'm'; - len =3D memtox(buf + 1, xml + addr, len); - } else { - buf[0] =3D 'l'; - len =3D memtox(buf + 1, xml + addr, total_len - addr); - } - put_packet_binary(s, buf, len + 1, true); - break; - } - if (is_query_packet(p, "Attached", ':')) { - put_packet(s, GDB_ATTACHED); - break; + if (!gdb_ctx->num_params) { + return; + } + + res =3D gdb_handle_vcont(gdb_ctx->s, gdb_ctx->params[0].data); + if ((res =3D=3D -EINVAL) || (res =3D=3D -ERANGE)) { + put_packet(gdb_ctx->s, "E22"); + } else if (res) { + put_packet(gdb_ctx->s, "\0"); + } +} + +static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + GDBProcess *process; + CPUState *cpu; + char thread_id[16]; + + strcpy(gdb_ctx->str_buf, "E22"); + if (!gdb_ctx->num_params) { + goto cleanup; + } + + process =3D gdb_get_process(gdb_ctx->s, gdb_ctx->params[0].val_ul); + if (!process) { + goto cleanup; + } + + cpu =3D get_first_cpu_in_process(gdb_ctx->s, process); + if (!cpu) { + goto cleanup; + } + + process->attached =3D true; + gdb_ctx->s->g_cpu =3D cpu; + gdb_ctx->s->c_cpu =3D cpu; + + gdb_fmt_thread_id(gdb_ctx->s, cpu, thread_id, sizeof(thread_id)); + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "T%02xthread:%s;", + GDB_SIGNAL_TRAP, thread_id); +cleanup: + put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + /* Kill the target */ + put_packet(gdb_ctx->s, "OK"); + error_report("QEMU: Terminated via GDBstub"); + exit(0); +} + +static GdbCmdParseEntry gdb_v_commands_table[] =3D { + /* Order is important if has same prefix */ + { + .handler =3D handle_v_cont_query, + .cmd =3D "Cont?", + .cmd_startswith =3D 1 + }, + { + .handler =3D handle_v_cont, + .cmd =3D "Cont", + .cmd_startswith =3D 1, + .schema =3D "s0" + }, + { + .handler =3D handle_v_attach, + .cmd =3D "Attach;", + .cmd_startswith =3D 1, + .schema =3D "l0" + }, + { + .handler =3D handle_v_kill, + .cmd =3D "Kill;", + .cmd_startswith =3D 1 + }, +}; + +static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (!gdb_ctx->num_params) { + return; + } + + if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + gdb_v_commands_table, + ARRAY_SIZE(gdb_v_commands_table))) { + put_packet(gdb_ctx->s, ""); + } +} + +static void handle_kill(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + /* Kill the target */ + error_report("QEMU: Terminated via GDBstub"); + exit(0); +} + +static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + GDBProcess *process; + GDBState *s =3D gdb_ctx->s; + uint32_t pid =3D 1; + + if (s->multiprocess) { + if (!gdb_ctx->num_params) { + put_packet(s, "E22"); + return; } - /* Unrecognised 'q' command. */ - goto unknown_command; =20 - default: - unknown_command: - /* put empty packet */ - buf[0] =3D '\0'; - put_packet(s, buf); - break; + pid =3D gdb_ctx->params[0].val_ul; + } + + process =3D gdb_get_process(s, pid); + gdb_process_breakpoint_remove_all(s, process); + process->attached =3D false; + + if (pid =3D=3D gdb_get_cpu_pid(s, s->c_cpu)) { + s->c_cpu =3D gdb_first_attached_cpu(s); } + + if (pid =3D=3D gdb_get_cpu_pid(s, s->g_cpu)) { + s->g_cpu =3D gdb_first_attached_cpu(s); + } + + if (!s->c_cpu) { + /* No more process attached */ + gdb_syscall_mode =3D GDB_SYS_DISABLED; + gdb_continue(s); + } + put_packet(s, "OK"); +} + +static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (gdb_ctx->num_params) { + gdb_set_cpu_pc(gdb_ctx->s, (target_ulong)gdb_ctx->params[0].val_ul= l); + } + + cpu_single_step(gdb_ctx->s->c_cpu, sstep_flags); + gdb_continue(gdb_ctx->s); +} + +static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + int num_syscall_params; + GdbCmdVariant syscall_params[3] =3D {0}; + + if (!gdb_ctx->num_params) { + return; + } + + if (cmd_parse_params(gdb_ctx->params[0].data, "L,L,o0", syscall_params, + &num_syscall_params)) { + return; + } + + if (!num_syscall_params) { + return; + } + + if (gdb_ctx->s->current_syscall_cb) { + gdb_ctx->s->current_syscall_cb(gdb_ctx->s->c_cpu, + (target_ulong)syscall_params[0].val= _ull, + (target_ulong)syscall_params[1].val= _ull); + gdb_ctx->s->current_syscall_cb =3D NULL; + } + + if (syscall_params[2].opcode =3D=3D (uint8_t)'C') { + put_packet(gdb_ctx->s, "T02"); + return; + } + + gdb_continue(gdb_ctx->s); +} + +static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + target_ulong addr, len; + + cpu_synchronize_state(gdb_ctx->s->g_cpu); + len =3D 0; + for (addr =3D 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs; addr++) { + len +=3D gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf + l= en, + addr); + } + + memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + target_ulong addr, len; + uint8_t *registers; + int reg_size; + + if (!gdb_ctx->num_params) { + return; + } + + cpu_synchronize_state(gdb_ctx->s->g_cpu); + registers =3D gdb_ctx->mem_buf; + len =3D strlen(gdb_ctx->params[0].data) / 2; + hextomem(registers, gdb_ctx->params[0].data, len); + for (addr =3D 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs && len > 0; + addr++) { + reg_size =3D gdb_write_register(gdb_ctx->s->g_cpu, registers, addr= ); + len -=3D reg_size; + registers +=3D reg_size; + } + put_packet(gdb_ctx->s, "OK"); +} + +static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (gdb_ctx->num_params < 2) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + /* memtohex() doubles the required space */ + if (gdb_ctx->params[1].val_ull > MAX_PACKET_LENGTH / 2) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + if (target_memory_rw_debug(gdb_ctx->s->g_cpu, gdb_ctx->params[0].val_u= ll, + gdb_ctx->mem_buf, + gdb_ctx->params[1].val_ull, false)) { + put_packet(gdb_ctx->s, "E14"); + return; + } + + memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, gdb_ctx->params[1].val_ul= l); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (gdb_ctx->num_params < 3) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + /* hextomem() reads 2*len bytes */ + if (gdb_ctx->params[1].val_ull > strlen(gdb_ctx->params[2].data) / 2) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + hextomem(gdb_ctx->mem_buf, gdb_ctx->params[2].data, + gdb_ctx->params[1].val_ull); + if (target_memory_rw_debug(gdb_ctx->s->g_cpu, gdb_ctx->params[0].val_u= ll, + gdb_ctx->mem_buf, + gdb_ctx->params[1].val_ull, true)) { + put_packet(gdb_ctx->s, "E14"); + return; + } + + put_packet(gdb_ctx->s, "OK"); +} + +static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + int reg_size; + + /* + * Older gdb are really dumb, and don't use 'g' if 'p' is avaialable. + * This works, but can be very slow. Anything new enough to + * understand XML also knows how to use this properly. + */ + if (!gdb_has_xml) { + put_packet(gdb_ctx->s, ""); + return; + } + + if (!gdb_ctx->num_params) { + put_packet(gdb_ctx->s, "E14"); + return; + } + + reg_size =3D gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf, + gdb_ctx->params[0].val_ull); + if (!reg_size) { + put_packet(gdb_ctx->s, "E14"); + return; + } + + memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, reg_size); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + int reg_size; + + if (!gdb_has_xml) { + put_packet(gdb_ctx->s, ""); + return; + } + + if (gdb_ctx->num_params < 2) { + put_packet(gdb_ctx->s, ""); + return; + } + + reg_size =3D strlen(gdb_ctx->params[1].data) / 2; + hextomem(gdb_ctx->mem_buf, gdb_ctx->params[1].data, reg_size); + gdb_write_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf, + gdb_ctx->params[0].val_ull); + put_packet(gdb_ctx->s, "OK"); +} + +static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + int res; + + if (gdb_ctx->num_params < 3) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + res =3D gdb_breakpoint_insert(gdb_ctx->params[1].val_ull, + gdb_ctx->params[2].val_ull, + gdb_ctx->params[0].val_ul); + if (res >=3D 0) { + put_packet(gdb_ctx->s, "OK"); + return; + } else if (res =3D=3D -ENOSYS) { + put_packet(gdb_ctx->s, ""); + return; + } + + put_packet(gdb_ctx->s, "E22"); +} + +static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + int res; + + if (gdb_ctx->num_params < 3) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + res =3D gdb_breakpoint_remove(gdb_ctx->params[1].val_ull, + gdb_ctx->params[2].val_ull, + gdb_ctx->params[0].val_ul); + if (res >=3D 0) { + put_packet(gdb_ctx->s, "OK"); + return; + } else if (res =3D=3D -ENOSYS) { + put_packet(gdb_ctx->s, ""); + return; + } + + put_packet(gdb_ctx->s, "E22"); +} + +static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + CPUState *cpu; + + if (gdb_ctx->num_params < 2) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + if (gdb_ctx->params[1].thread_id.kind =3D=3D GDB_READ_THREAD_ERR) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + if (gdb_ctx->params[1].thread_id.kind !=3D GDB_ONE_THREAD) { + put_packet(gdb_ctx->s, "OK"); + return; + } + + cpu =3D gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[1].thread_id.pid, + gdb_ctx->params[1].thread_id.tid); + if (!cpu) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + switch (gdb_ctx->params[0].opcode) { + case 'c': + gdb_ctx->s->c_cpu =3D cpu; + put_packet(gdb_ctx->s, "OK"); + break; + case 'g': + gdb_ctx->s->g_cpu =3D cpu; + put_packet(gdb_ctx->s, "OK"); + break; + default: + put_packet(gdb_ctx->s, "E22"); + break; + } +} + +static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + CPUState *cpu; + + if (!gdb_ctx->num_params) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + if (gdb_ctx->params[0].thread_id.kind =3D=3D GDB_READ_THREAD_ERR) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + cpu =3D gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[0].thread_id.pid, + gdb_ctx->params[0].thread_id.tid); + if (!cpu) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + put_packet(gdb_ctx->s, "OK"); +} + +static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user= _ctx) +{ + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), + "ENABLE=3D%x,NOIRQ=3D%x,NOTIMER=3D%x", SSTEP_ENABLE, + SSTEP_NOIRQ, SSTEP_NOTIMER); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (!gdb_ctx->num_params) { + return; + } + + sstep_flags =3D gdb_ctx->params[0].val_ul; + put_packet(gdb_ctx->s, "OK"); +} + +static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "0x%x", sstep_fla= gs); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + CPUState *cpu; + GDBProcess *process; + char thread_id[16]; + + /* + * "Current thread" remains vague in the spec, so always return + * the first thread of the current process (gdb returns the + * first thread). + */ + process =3D gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); + cpu =3D get_first_cpu_in_process(gdb_ctx->s, process); + gdb_fmt_thread_id(gdb_ctx->s, cpu, thread_id, sizeof(thread_id)); + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "QC%s", thread_id= ); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + char thread_id[16]; + + if (!gdb_ctx->s->query_cpu) { + put_packet(gdb_ctx->s, "l"); + return; + } + + gdb_fmt_thread_id(gdb_ctx->s, gdb_ctx->s->query_cpu, thread_id, + sizeof(thread_id)); + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "m%s", thread_id); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); + gdb_ctx->s->query_cpu =3D + gdb_next_attached_cpu(gdb_ctx->s, gdb_ctx->s->query_cpu); +} + +static void handle_query_first_threads(GdbCmdContext *gdb_ctx, void *user_= ctx) +{ + gdb_ctx->s->query_cpu =3D gdb_first_attached_cpu(gdb_ctx->s); + handle_query_threads(gdb_ctx, user_ctx); +} + +static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_c= tx) +{ + CPUState *cpu; + int len; + + if (!gdb_ctx->num_params || + gdb_ctx->params[0].thread_id.kind =3D=3D GDB_READ_THREAD_ERR) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + cpu =3D gdb_get_cpu(gdb_ctx->s, gdb_ctx->params[0].thread_id.pid, + gdb_ctx->params[0].thread_id.tid); + if (!cpu) { + return; + } + + cpu_synchronize_state(cpu); + + if (gdb_ctx->s->multiprocess && (gdb_ctx->s->process_num > 1)) { + /* Print the CPU model and name in multiprocess mode */ + ObjectClass *oc =3D object_get_class(OBJECT(cpu)); + const char *cpu_model =3D object_class_get_name(oc); + char *cpu_name =3D object_get_canonical_path_component(OBJECT(cpu)= ); + len =3D snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf= ) / 2, + "%s %s [%s]", cpu_model, cpu_name, + cpu->halted ? "halted " : "running"); + g_free(cpu_name); + } else { + /* memtohex() doubles the required space */ + len =3D snprintf((char *)gdb_ctx->mem_buf, sizeof(gdb_ctx->str_buf= ) / 2, + "CPU#%d [%s]", cpu->cpu_index, + cpu->halted ? "halted " : "running"); + } + trace_gdbstub_op_extra_info((char *)gdb_ctx->mem_buf); + memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +#ifdef CONFIG_USER_ONLY +static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + TaskState *ts; + + ts =3D gdb_ctx->s->c_cpu->opaque; + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), + "Text=3D" TARGET_ABI_FMT_lx ";Data=3D" TARGET_ABI_FMT_lx + ";Bss=3D" TARGET_ABI_FMT_lx, + ts->info->code_offset, + ts->info->data_offset, + ts->info->data_offset); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} +#else +static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + int len; + + if (!gdb_ctx->num_params) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + len =3D strlen(gdb_ctx->params[0].data); + if (len % 2) { + put_packet(gdb_ctx->s, "E01"); + return; + } + + len =3D len / 2; + hextomem(gdb_ctx->mem_buf, gdb_ctx->params[0].data, len); + gdb_ctx->mem_buf[len++] =3D 0; + qemu_chr_be_write(gdb_ctx->s->mon_chr, gdb_ctx->mem_buf, len); + put_packet(gdb_ctx->s, "OK"); + +} +#endif + +static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + CPUClass *cc; + + snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "PacketSize=3D%x", + MAX_PACKET_LENGTH); + cc =3D CPU_GET_CLASS(first_cpu); + if (cc->gdb_core_xml_file) { + pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), + ";qXfer:features:read+"); + } + + if (gdb_ctx->num_params && + strstr(gdb_ctx->params[0].data, "multiprocess+")) { + gdb_ctx->s->multiprocess =3D true; + } + + pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";multiprocess+"); + put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_= ctx) +{ + GDBProcess *process; + CPUClass *cc; + unsigned long len, total_len, addr; + const char *xml; + const char *p; + + if (gdb_ctx->num_params < 3) { + put_packet(gdb_ctx->s, "E22"); + return; + } + + process =3D gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); + cc =3D CPU_GET_CLASS(gdb_ctx->s->g_cpu); + if (!cc->gdb_core_xml_file) { + put_packet(gdb_ctx->s, ""); + return; + } + + gdb_has_xml =3D true; + p =3D gdb_ctx->params[0].data; + xml =3D get_feature_xml(gdb_ctx->s, p, &p, process); + if (!xml) { + put_packet(gdb_ctx->s, "E00"); + return; + } + + addr =3D gdb_ctx->params[1].val_ul; + len =3D gdb_ctx->params[2].val_ul; + total_len =3D strlen(xml); + if (addr > total_len) { + put_packet(gdb_ctx->s, "E00"); + return; + } + + if (len > (MAX_PACKET_LENGTH - 5) / 2) { + len =3D (MAX_PACKET_LENGTH - 5) / 2; + } + + if (len < total_len - addr) { + gdb_ctx->str_buf[0] =3D 'm'; + len =3D memtox(gdb_ctx->str_buf + 1, xml + addr, len); + } else { + gdb_ctx->str_buf[0] =3D 'l'; + len =3D memtox(gdb_ctx->str_buf + 1, xml + addr, total_len - addr); + } + + put_packet_binary(gdb_ctx->s, gdb_ctx->str_buf, len + 1, true); +} + +static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + put_packet(gdb_ctx->s, GDB_ATTACHED); +} + +static GdbCmdParseEntry gdb_gen_query_set_common_table[] =3D { + /* Order is important if has same prefix */ + { + .handler =3D handle_query_qemu_sstepbits, + .cmd =3D "qemu.sstepbits", + .cmd_full_match =3D 1 + }, + { + .handler =3D handle_query_qemu_sstep, + .cmd =3D "qemu.sstep", + .cmd_full_match =3D 1, + }, + { + .handler =3D handle_set_qemu_sstep, + .cmd =3D "qemu.sstep=3D", + .cmd_startswith =3D 1, + .schema =3D "l0" + }, +}; + +static GdbCmdParseEntry gdb_gen_query_table[] =3D { + { + .handler =3D handle_query_curr_tid, + .cmd =3D "C", + .cmd_full_match =3D 1 + }, + { + .handler =3D handle_query_threads, + .cmd =3D "sThreadInfo", + .cmd_full_match =3D 1 + }, + { + .handler =3D handle_query_first_threads, + .cmd =3D "fThreadInfo", + .cmd_full_match =3D 1 + }, + { + .handler =3D handle_query_thread_extra, + .cmd =3D "ThreadExtraInfo,", + .cmd_startswith =3D 1, + .schema =3D "t0" + }, +#ifdef CONFIG_USER_ONLY + { + .handler =3D handle_query_offsets, + .cmd =3D "Offsets", + .cmd_full_match =3D 1 + }, +#else + { + .handler =3D handle_query_rcmd, + .cmd =3D "Rcmd,", + .cmd_startswith =3D 1, + .schema =3D "s0" + }, +#endif + { + .handler =3D handle_query_supported, + .cmd =3D "Supported:", + .cmd_startswith =3D 1, + .schema =3D "s0" + }, + { + .handler =3D handle_query_supported, + .cmd =3D "Supported", + .cmd_full_match =3D 1, + .schema =3D "s0" + }, + { + .handler =3D handle_query_xfer_features, + .cmd =3D "Xfer:features:read:", + .cmd_startswith =3D 1, + .schema =3D "x:l,l0" + }, + { + .handler =3D handle_query_attached, + .cmd =3D "Attached:", + .cmd_startswith =3D 1 + }, + { + .handler =3D handle_query_attached, + .cmd =3D "Attached", + .cmd_full_match =3D 1 + }, +}; + +static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (!gdb_ctx->num_params) { + return; + } + + if (!process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + gdb_gen_query_set_common_table, + ARRAY_SIZE(gdb_gen_query_set_common_table))) { + return; + } + + if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + gdb_gen_query_table, + ARRAY_SIZE(gdb_gen_query_table))) { + put_packet(gdb_ctx->s, ""); + } +} + +static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) +{ + if (!gdb_ctx->num_params) { + return; + } + + if (!process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + gdb_gen_query_set_common_table, + ARRAY_SIZE(gdb_gen_query_set_common_table))) { + return; + } + + put_packet(gdb_ctx->s, ""); +} + +static GdbCmdParseEntry gdb_packet_table[0x100] =3D { + ['!'] =3D { + .handler =3D handle_extend_mode, + .cmd =3D "!", + .cmd_startswith =3D 1 + }, + ['?'] =3D { + .handler =3D handle_target_halt, + .cmd =3D "?", + .cmd_startswith =3D 1 + }, + ['c'] =3D { + .handler =3D handle_continue, + .cmd =3D "c", + .cmd_startswith =3D 1, + .schema =3D "L0" + }, + ['C'] =3D { + .handler =3D handle_cont_with_sig, + .cmd =3D "C", + .cmd_startswith =3D 1, + .schema =3D "l0" + }, + ['v'] =3D { + .handler =3D handle_v_commands, + .cmd =3D "v", + .cmd_startswith =3D 1, + .schema =3D "s0" + }, + ['k'] =3D { + .handler =3D handle_kill, + .cmd =3D "k", + .cmd_startswith =3D 1 + }, + ['D'] =3D { + .handler =3D handle_detach, + .cmd =3D "D", + .cmd_startswith =3D 1, + .schema =3D "?.l0" + }, + ['s'] =3D { + .handler =3D handle_step, + .cmd =3D "s", + .cmd_startswith =3D 1, + .schema =3D "L0" + }, + ['F'] =3D { + .handler =3D handle_file_io, + .cmd =3D "F", + .cmd_startswith =3D 1, + .schema =3D "s0" + }, + ['g'] =3D { + .handler =3D handle_read_all_regs, + .cmd =3D "g", + .cmd_startswith =3D 1 + }, + ['G'] =3D { + .handler =3D handle_write_all_regs, + .cmd =3D "G", + .cmd_startswith =3D 1, + .schema =3D "s0" + }, + ['m'] =3D { + .handler =3D handle_read_mem, + .cmd =3D "m", + .cmd_startswith =3D 1, + .schema =3D "L,L0" + }, + ['M'] =3D { + .handler =3D handle_write_mem, + .cmd =3D "M", + .cmd_startswith =3D 1, + .schema =3D "L,L:s0" + }, + ['p'] =3D { + .handler =3D handle_get_reg, + .cmd =3D "p", + .cmd_startswith =3D 1, + .schema =3D "L0" + }, + ['P'] =3D { + .handler =3D handle_set_reg, + .cmd =3D "P", + .cmd_startswith =3D 1, + .schema =3D "L?s0" + }, + ['Z'] =3D { + .handler =3D handle_insert_bp, + .cmd =3D "Z", + .cmd_startswith =3D 1, + .schema =3D "l?L?L0" + }, + ['z'] =3D { + .handler =3D handle_remove_bp, + .cmd =3D "z", + .cmd_startswith =3D 1, + .schema =3D "l?L?L0" + }, + ['H'] =3D { + .handler =3D handle_set_thread, + .cmd =3D "H", + .cmd_startswith =3D 1, + .schema =3D "o.t0" + }, + ['T'] =3D { + .handler =3D handle_thread_alive, + .cmd =3D "T", + .cmd_startswith =3D 1, + .schema =3D "t0" + }, + ['q'] =3D { + .handler =3D handle_gen_query, + .cmd =3D "q", + .cmd_startswith =3D 1, + .schema =3D "s0" + }, + ['Q'] =3D { + .handler =3D handle_gen_set, + .cmd =3D "Q", + .cmd_startswith =3D 1, + .schema =3D "s0" + }, +}; + +static int gdb_handle_packet(GDBState *s, const char *line_buf) +{ + trace_gdbstub_io_command(line_buf); + + if (process_string_cmd(s, NULL, line_buf, + &gdb_packet_table[*(uint8_t *)line_buf], 1)) { + put_packet(s, ""); + } + return RS_IDLE; } =20 --=20 2.20.1