[Qemu-devel] [PATCH v8 03/27] gdbstub: Implement thread_alive (T pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 43 --- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 621d689868..c47ef7dd9c 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1498,6 +1498,30 @@ static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(s, "OK"); } +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 == GDB_READ_THREAD_ERR) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +cpu = 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 int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1798,17 +1822,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'T': -thread_kind = read_thread_id(p, &p, &pid, &tid); -if (thread_kind == GDB_READ_THREAD_ERR) { -put_packet(s, "E22"); -break; -} -cpu = gdb_get_cpu(s, pid, tid); - -if (cpu != NULL) { -put_packet(s, "OK"); -} else { -put_packet(s, "E22"); +{ +static const GdbCmdParseEntry thread_alive_cmd_desc = { +.handler = handle_thread_alive, +.cmd = "T", +.cmd_startswith = 1, +.schema = "t0" +}; +cmd_parser = &thread_alive_cmd_desc; } break; case 'q': -- 2.20.1
[Qemu-devel] [PATCH v8 01/27] gdbstub: Add infrastructure to parse cmd packets
Signed-off-by: Jon Doron --- gdbstub.c | 200 ++ 1 file changed, 200 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index d54abd17cc..d5e0f3878a 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1268,6 +1268,206 @@ out: return res; } +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) +{ +static const char all_delimiters[] = ",;:="; +char curr_delimiters[2] = {0}; +const char *delimiters; + +if (delimiter == '?') { +delimiters = all_delimiters; +} else if (delimiter == '0') { +return strchr(param, '\0'); +} else if (delimiter == '.' && *param) { +return param + 1; +} else { +curr_delimiters[0] = delimiter; +delimiters = curr_delimiters; +} + +param += strcspn(param, delimiters); +if (*param) { +param++; +} +return param; +} + +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; + +*num_params = 0; + +if (!schema) { +return 0; +} + +curr_schema = schema; +curr_param = 0; +curr_data = 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 = 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 = cmd_next_param(curr_data, curr_schema[1]); +break; +case 's': +params[curr_param].data = curr_data; +curr_param++; +curr_data = cmd_next_param(curr_data, curr_schema[1]); +break; +case 'o': +params[curr_param].opcode = *(uint8_t *)curr_data; +curr_param++; +curr_data = cmd_next_param(curr_data, curr_schema[1]); +break; +case 't': +params[curr_param].thread_id.kind = +read_thread_id(curr_data, &curr_data, + ¶ms[curr_param].thread_id.pid, + ¶ms[curr_param].thread_id.tid); +curr_param++; +curr_data = cmd_next_param(curr_data, curr_schema[1]); +break; +case '?': +curr_data = cmd_next_param(curr_data, curr_schema[1]); +break; +default: +return -EINVAL; +} +curr_schema += 2; +} + +*num_params = curr_param; +return 0; +} + +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; + +typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx); + +/* + * cmd_startswith -> cmd is compared using startswith + * + * + * 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) + * '?' -> skip according to delimiter + * + * Currently supported delimiters: + * '?' -> Stop at any delimiter (",;:=\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; +}; +}; +const char *schema; +} GdbCmdParseEntry; + +static inline int startswith(const char *string, const char *pattern) +{ + return !strncmp(string, pattern, strlen(pattern)); +} + +static int process_string_cmd(
[Qemu-devel] [PATCH v8 09/27] gdbstub: Implement set register (P pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 39 ++- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index b42425b24c..10e3f12a68 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1634,6 +1634,27 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, "E22"); } +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 = 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 int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1878,15 +1899,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'P': -if (!gdb_has_xml) -goto unknown_command; -addr = strtoull(p, (char **)&p, 16); -if (*p == '=') -p++; -reg_size = strlen(p) / 2; -hextomem(mem_buf, p, reg_size); -gdb_write_register(s->g_cpu, mem_buf, addr); -put_packet(s, "OK"); +{ +static const GdbCmdParseEntry set_reg_cmd_desc = { +.handler = handle_set_reg, +.cmd = "P", +.cmd_startswith = 1, +.schema = "L?s0" +}; +cmd_parser = &set_reg_cmd_desc; +} break; case 'Z': { -- 2.20.1
[Qemu-devel] [PATCH v8 00/27] gdbstub: Refactor command packets handler
This patch series refactors the old gdbstub command packets handler with a new infrastructure which should ease extending and adding new and missing gdb command packets. version 8 changes: - Add new command to display the Supported qemu generic query/sets - kvm: Add API to read/write a MSR - Add new commands specific for qemu: * Command to swap the memory GDB sees to be the physical memory * Commands to read and write a MSR version 7 changes: - Fixed few checkpatch complaints - Feedback from Alex Bennee version 4-6 changes: - mostly feedback from Richard Henderson version 3 changes - Split the single patch to many individual patches for easier reviewing version 2 changes - Code convention fixes Jon Doron (27): gdbstub: Add infrastructure to parse cmd packets gdbstub: Implement deatch (D pkt) with new infra gdbstub: Implement thread_alive (T pkt) with new infra gdbstub: Implement continue (c pkt) with new infra gdbstub: Implement continue with signal (C pkt) with new infra gdbstub: Implement set_thread (H pkt) with new infra gdbstub: Implement insert breakpoint (Z pkt) with new infra gdbstub: Implement remove breakpoint (z pkt) with new infra gdbstub: Implement set register (P pkt) with new infra gdbstub: Implement get register (p pkt) with new infra gdbstub: Implement write memory (M pkt) with new infra gdbstub: Implement read memory (m pkt) with new infra gdbstub: Implement write all registers (G pkt) with new infra gdbstub: Implement read all registers (g pkt) with new infra gdbstub: Implement file io (F pkt) with new infra gdbstub: Implement step (s pkt) with new infra gdbstub: Implement v commands with new infra gdbstub: Implement generic query (q pkt) with new infra gdbstub: Implement generic set (Q pkt) with new infra gdbstub: Implement target halted (? pkt) with new infra gdbstub: Clear unused variables in gdb_handle_packet gdbstub: Implement generic query qemu.Supported gdbstub: Implement qemu physical memory mode gdbstub: Add another handler for setting qemu.sstep kvm: Add API to read/write a CPU MSR value gdbstub: Add support to read a MSR for KVM target gdbstub: Add support to write a MSR for KVM target accel/kvm/kvm-all.c | 39 + gdbstub.c| 1807 ++ include/sysemu/kvm.h |2 + 3 files changed, 1359 insertions(+), 489 deletions(-) -- 2.20.1
[Qemu-devel] [PATCH v8 02/27] gdbstub: Implement deatch (D pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 90 ++- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index d5e0f3878a..621d689868 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1418,11 +1418,6 @@ static inline int startswith(const char *string, const char *pattern) return !strncmp(string, pattern, strlen(pattern)); } -static int process_string_cmd( -GDBState *s, void *user_ctx, const char *data, -const GdbCmdParseEntry *cmds, int num_cmds) -__attribute__((unused)); - static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, const GdbCmdParseEntry *cmds, int num_cmds) { @@ -1468,6 +1463,41 @@ static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, return -1; } +static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +GDBProcess *process; +GDBState *s = gdb_ctx->s; +uint32_t pid = 1; + +if (s->multiprocess) { +if (!gdb_ctx->num_params) { +put_packet(s, "E22"); +return; +} + +pid = gdb_ctx->params[0].val_ul; +} + +process = gdb_get_process(s, pid); +gdb_process_breakpoint_remove_all(s, process); +process->attached = false; + +if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { +s->c_cpu = gdb_first_attached_cpu(s); +} + +if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { +s->g_cpu = gdb_first_attached_cpu(s); +} + +if (!s->c_cpu) { +/* No more process attached */ +gdb_syscall_mode = GDB_SYS_DISABLED; +gdb_continue(s); +} +put_packet(s, "OK"); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1482,6 +1512,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) uint8_t *registers; target_ulong addr, len; GDBThreadIdKind thread_kind; +const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); @@ -1582,42 +1613,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) error_report("QEMU: Terminated via GDBstub"); exit(0); case 'D': -/* Detach packet */ -pid = 1; - -if (s->multiprocess) { -unsigned long lpid; -if (*p != ';') { -put_packet(s, "E22"); -break; -} - -if (qemu_strtoul(p + 1, &p, 16, &lpid)) { -put_packet(s, "E22"); -break; -} - -pid = lpid; -} - -process = gdb_get_process(s, pid); -gdb_process_breakpoint_remove_all(s, process); -process->attached = false; - -if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { -s->c_cpu = gdb_first_attached_cpu(s); -} - -if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { -s->g_cpu = gdb_first_attached_cpu(s); -} - -if (s->c_cpu == NULL) { -/* No more process attached */ -gdb_syscall_mode = GDB_SYS_DISABLED; -gdb_continue(s); +{ +static const GdbCmdParseEntry detach_cmd_desc = { +.handler = handle_detach, +.cmd = "D", +.cmd_startswith = 1, +.schema = "?.l0" +}; +cmd_parser = &detach_cmd_desc; } -put_packet(s, "OK"); break; case 's': if (*p != '\0') { @@ -1990,6 +1994,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, buf); break; } + +if (cmd_parser && +process_string_cmd(s, NULL, line_buf, cmd_parser, 1)) { +put_packet(s, ""); +} + return RS_IDLE; } -- 2.20.1
[Qemu-devel] [PATCH v8 10/27] gdbstub: Implement get register (p pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 50 ++ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 10e3f12a68..e9a3d0c2bc 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1655,6 +1655,36 @@ static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx) 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 = 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 int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1884,18 +1914,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'p': -/* 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) -goto unknown_command; -addr = strtoull(p, (char **)&p, 16); -reg_size = 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"); +{ +static const GdbCmdParseEntry get_reg_cmd_desc = { +.handler = handle_get_reg, +.cmd = "p", +.cmd_startswith = 1, +.schema = "L0" +}; +cmd_parser = &get_reg_cmd_desc; } break; case 'P': -- 2.20.1
[Qemu-devel] [PATCH v8 17/27] gdbstub: Implement v commands with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 170 +++--- 1 file changed, 110 insertions(+), 60 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 9b0556f8be..d56d0fd235 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1815,6 +1815,106 @@ static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_continue(gdb_ctx->s); } +static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +put_packet(gdb_ctx->s, "vCont;c;C;s;S"); +} + +static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +int res; + +if (!gdb_ctx->num_params) { +return; +} + +res = gdb_handle_vcont(gdb_ctx->s, gdb_ctx->params[0].data); +if ((res == -EINVAL) || (res == -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 = gdb_get_process(gdb_ctx->s, gdb_ctx->params[0].val_ul); +if (!process) { +goto cleanup; +} + +cpu = get_first_cpu_in_process(gdb_ctx->s, process); +if (!cpu) { +goto cleanup; +} + +process->attached = true; +gdb_ctx->s->g_cpu = cpu; +gdb_ctx->s->c_cpu = 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[] = { +/* Order is important if has same prefix */ +{ +.handler = handle_v_cont_query, +.cmd = "Cont?", +.cmd_startswith = 1 +}, +{ +.handler = handle_v_cont, +.cmd = "Cont", +.cmd_startswith = 1, +.schema = "s0" +}, +{ +.handler = handle_v_attach, +.cmd = "Attach;", +.cmd_startswith = 1, +.schema = "l0" +}, +{ +.handler = handle_v_kill, +.cmd = "Kill;", +.cmd_startswith = 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 int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1822,7 +1922,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) CPUClass *cc; const char *p; uint32_t pid, tid; -int ch, type, res; +int ch, type; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; char thread_id[16]; @@ -1871,66 +1971,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'v': -if (strncmp(p, "Cont", 4) == 0) { -p += 4; -if (*p == '?') { -put_packet(s, "vCont;c;C;s;S"); -break; -} - -res = gdb_handle_vcont(s, p); - -if (res) { -if ((res == -EINVAL) || (res == -ERANGE)) { -put_packet(s, "E22"); -break; -} -goto unknown_command; -} -break; -} else if (strncmp(p, "Attach;", 7) == 0) { -unsigned long pid; - -p += 7; - -if (qemu_strtoul(p, &p, 16, &pid)) { -put_packet(s, "E22"); -break; -} - -process = gdb_get_process(s, pid); - -if (process == NULL) { -put_packet(s, "E22"); -break; -} - -cpu = get_first_cpu_in_process(s, process); - -if (cpu == NULL) { -/* Refuse to attach an empty process */ -put_packet(s, "E22"); -break; -} - -process->attached = true; - -s->g_cpu = cpu; -s->c_cpu = cpu; - -snprintf(buf, sizeof(buf), "T%02xthread:
[Qemu-devel] [PATCH v8 05/27] gdbstub: Implement continue with signal (C pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 30 +- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 89f1ab6524..469aaeb875 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1532,6 +1532,21 @@ static void handle_continue(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_continue(gdb_ctx->s); } +static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +unsigned long signal = 0; + +if (gdb_ctx->num_params) { +signal = gdb_ctx->params[0].val_ul; +} + +gdb_ctx->s->signal = gdb_signal_to_target(signal); +if (gdb_ctx->s->signal == -1) { +gdb_ctx->s->signal = 0; +} +gdb_continue(gdb_ctx->s); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1579,11 +1594,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'C': -s->signal = gdb_signal_to_target (strtoul(p, (char **)&p, 16)); -if (s->signal == -1) -s->signal = 0; -gdb_continue(s); -return RS_IDLE; +{ +static const GdbCmdParseEntry cont_with_sig_cmd_desc = { +.handler = handle_cont_with_sig, +.cmd = "C", +.cmd_startswith = 1, +.schema = "l0" +}; +cmd_parser = &cont_with_sig_cmd_desc; +} +break; case 'v': if (strncmp(p, "Cont", 4) == 0) { p += 4; -- 2.20.1
[Qemu-devel] [PATCH v8 27/27] gdbstub: Add support to write a MSR for KVM target
gdb> maint packet Qqemu.kvm.Wrmsr:MsrIndex,Value Signed-off-by: Jon Doron --- gdbstub.c | 29 - 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/gdbstub.c b/gdbstub.c index d5cdda190a..2d9a8e6942 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2145,7 +2145,8 @@ static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) "sstepbits;sstep;PhyMemMode"); if (kvm_enabled()) { -pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";kvm.Rdmsr"); +pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), +";kvm.Rdmsr;kvm.Wrmsr"); } put_packet(gdb_ctx->s, gdb_ctx->str_buf); @@ -2196,6 +2197,26 @@ static void handle_query_kvm_read_msr(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, gdb_ctx->str_buf); } +static void handle_set_kvm_write_msr(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +if (!kvm_enabled()) { +return; +} + +if (gdb_ctx->num_params < 2) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +if (kvm_arch_write_msr(gdbserver_state->c_cpu, gdb_ctx->params[0].val_ul, + gdb_ctx->params[1].val_ull)) { +put_packet(gdb_ctx->s, "E00"); +return; +} + +put_packet(gdb_ctx->s, "OK"); +} + static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { /* Order is important if has same prefix */ { @@ -2302,6 +2323,12 @@ static GdbCmdParseEntry gdb_gen_set_table[] = { .cmd_startswith = 1, .schema = "l0" }, +{ +.handler = handle_set_kvm_write_msr, +.cmd = "qemu.kvm.Wrmsr:", +.cmd_startswith = 1, +.schema = "l,L0" +}, }; static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) -- 2.20.1
[Qemu-devel] [PATCH v8 23/27] gdbstub: Implement qemu physical memory mode
Add a new query/set which changes the memory GDB sees to physical memory only. gdb> maint packet qqemu.PhyMemMode will reply the current phy_mem_mode state (1 for enabled, 0 for disabled) gdb> maint packet Qqemu.PhyMemMode:1 Will make GDB read/write only to physical memory, set to 0 to disable Signed-off-by: Jon Doron --- gdbstub.c | 58 --- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 00c07d6ec0..6daf779af4 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -46,11 +46,23 @@ #define GDB_ATTACHED "1" #endif +static int phy_memory_mode = 0; + static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, uint8_t *buf, int len, bool is_write) { -CPUClass *cc = CPU_GET_CLASS(cpu); +CPUClass *cc; +if (phy_memory_mode) { +if (is_write) { +cpu_physical_memory_write(addr, buf, len); +} else { +cpu_physical_memory_read(addr, buf, len); +} +return 0; +} + +cc = CPU_GET_CLASS(cpu); if (cc->memory_rw_debug) { return cc->memory_rw_debug(cpu, addr, buf, len, is_write); } @@ -2129,7 +2141,29 @@ static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) { -put_packet(gdb_ctx->s, "sstepbits;sstep"); +put_packet(gdb_ctx->s, "sstepbits;sstep;PhyMemMode"); +} + +static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, + void *user_ctx) +{ +snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "%d", phy_memory_mode); +put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +if (!gdb_ctx->num_params) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +if (!gdb_ctx->params[0].val_ul) { +phy_memory_mode = 0; +} else { +phy_memory_mode = 1; +} +put_packet(gdb_ctx->s, "OK"); } static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { @@ -2212,6 +2246,20 @@ static GdbCmdParseEntry gdb_gen_query_table[] = { .handler = handle_query_qemu_supported, .cmd = "qemu.Supported", }, +{ +.handler = handle_query_qemu_phy_mem_mode, +.cmd = "qemu.PhyMemMode", +}, +}; + +static GdbCmdParseEntry gdb_gen_set_table[] = { +/* Order is important if has same prefix */ +{ +.handler = handle_set_qemu_phy_mem_mode, +.cmd = "qemu.PhyMemMode:", +.cmd_startswith = 1, +.schema = "l0" +}, }; static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -2245,7 +2293,11 @@ static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) return; } -put_packet(gdb_ctx->s, ""); +if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + gdb_gen_set_table, + ARRAY_SIZE(gdb_gen_set_table))) { +put_packet(gdb_ctx->s, ""); +} } static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) -- 2.20.1
[Qemu-devel] [PATCH v8 13/27] gdbstub: Implement write all registers (G pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 41 +++-- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index daa602edc3..adfe39b3a3 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1734,6 +1734,29 @@ static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx) 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 = gdb_ctx->mem_buf; +len = strlen(gdb_ctx->params[0].data) / 2; +hextomem(registers, gdb_ctx->params[0].data, len); +for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs && len > 0; + addr++) { +reg_size = gdb_write_register(gdb_ctx->s->g_cpu, registers, addr); +len -= reg_size; +registers += reg_size; +} +put_packet(gdb_ctx->s, "OK"); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1745,7 +1768,6 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) 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; const GdbCmdParseEntry *cmd_parser = NULL; @@ -1911,16 +1933,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, buf); break; case 'G': -cpu_synchronize_state(s->g_cpu); -registers = mem_buf; -len = strlen(p) / 2; -hextomem((uint8_t *)registers, p, len); -for (addr = 0; addr < s->g_cpu->gdb_num_g_regs && len > 0; addr++) { -reg_size = gdb_write_register(s->g_cpu, registers, addr); -len -= reg_size; -registers += reg_size; +{ +static const GdbCmdParseEntry write_all_regs_cmd_desc = { +.handler = handle_write_all_regs, +.cmd = "G", +.cmd_startswith = 1, +.schema = "s0" +}; +cmd_parser = &write_all_regs_cmd_desc; } -put_packet(s, "OK"); break; case 'm': { -- 2.20.1
[Qemu-devel] [PATCH v8 06/27] gdbstub: Implement set_thread (H pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 79 ++- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 469aaeb875..21cdaf4678 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1547,6 +1547,47 @@ static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_continue(gdb_ctx->s); } +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 == GDB_READ_THREAD_ERR) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +if (gdb_ctx->params[1].thread_id.kind != GDB_ONE_THREAD) { +put_packet(gdb_ctx->s, "OK"); +return; +} + +cpu = 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 = cpu; +put_packet(gdb_ctx->s, "OK"); +break; +case 'g': +gdb_ctx->s->g_cpu = cpu; +put_packet(gdb_ctx->s, "OK"); +break; +default: +put_packet(gdb_ctx->s, "E22"); +break; +} +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1560,7 +1601,6 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) char thread_id[16]; uint8_t *registers; target_ulong addr, len; -GDBThreadIdKind thread_kind; const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); @@ -1823,35 +1863,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "E22"); break; case 'H': -type = *p++; - -thread_kind = read_thread_id(p, &p, &pid, &tid); -if (thread_kind == GDB_READ_THREAD_ERR) { -put_packet(s, "E22"); -break; -} - -if (thread_kind != GDB_ONE_THREAD) { -put_packet(s, "OK"); -break; -} -cpu = gdb_get_cpu(s, pid, tid); -if (cpu == NULL) { -put_packet(s, "E22"); -break; -} -switch (type) { -case 'c': -s->c_cpu = cpu; -put_packet(s, "OK"); -break; -case 'g': -s->g_cpu = cpu; -put_packet(s, "OK"); -break; -default: - put_packet(s, "E22"); - break; +{ +static const GdbCmdParseEntry set_thread_cmd_desc = { +.handler = handle_set_thread, +.cmd = "H", +.cmd_startswith = 1, +.schema = "o.t0" +}; +cmd_parser = &set_thread_cmd_desc; } break; case 'T': -- 2.20.1
[Qemu-devel] [PATCH v8 16/27] gdbstub: Implement step (s pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 25 +++-- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 9fe130f30d..9b0556f8be 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1805,6 +1805,16 @@ static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_continue(gdb_ctx->s); } +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_ull); +} + +cpu_single_step(gdb_ctx->s->c_cpu, sstep_flags); +gdb_continue(gdb_ctx->s); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1937,13 +1947,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 's': -if (*p != '\0') { -addr = strtoull(p, (char **)&p, 16); -gdb_set_cpu_pc(s, addr); +{ +static const GdbCmdParseEntry step_cmd_desc = { +.handler = handle_step, +.cmd = "s", +.cmd_startswith = 1, +.schema = "L0" +}; +cmd_parser = &step_cmd_desc; } -cpu_single_step(s->c_cpu, sstep_flags); -gdb_continue(s); -return RS_IDLE; +break; case 'F': { static const GdbCmdParseEntry file_io_cmd_desc = { -- 2.20.1
[Qemu-devel] [PATCH v8 24/27] gdbstub: Add another handler for setting qemu.sstep
Follow GDB general query/set packet conventions, qemu.sstep can now be set with the following command as well: gdb> maint packet Qqemu.sstep:Value Signed-off-by: Jon Doron --- gdbstub.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 6daf779af4..bceceeec57 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2260,6 +2260,12 @@ static GdbCmdParseEntry gdb_gen_set_table[] = { .cmd_startswith = 1, .schema = "l0" }, +{ +.handler = handle_set_qemu_sstep, +.cmd = "qemu.sstep:", +.cmd_startswith = 1, +.schema = "l0" +}, }; static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) -- 2.20.1
[Qemu-devel] [PATCH v8 21/27] gdbstub: Clear unused variables in gdb_handle_packet
Signed-off-by: Jon Doron --- gdbstub.c | 11 ++- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index d678191705..8bdfae4b29 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2259,17 +2259,11 @@ static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) static int gdb_handle_packet(GDBState *s, const char *line_buf) { -const char *p; -int ch; -uint8_t mem_buf[MAX_PACKET_LENGTH]; -char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); -p = line_buf; -ch = *p++; -switch(ch) { +switch (line_buf[0]) { case '!': put_packet(s, "OK"); break; @@ -2486,8 +2480,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) break; default: /* put empty packet */ -buf[0] = '\0'; -put_packet(s, buf); +put_packet(s, ""); break; } -- 2.20.1
[Qemu-devel] [PATCH v8 20/27] gdbstub: Implement target halted (? pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 36 ++-- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 2fd0d66f4d..d678191705 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2239,13 +2239,30 @@ static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, ""); } +static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +char thread_id[16]; + +/* 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(); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { const char *p; int ch; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; -char thread_id[16]; const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); @@ -2257,15 +2274,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) 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_id))); -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(); +{ +static const GdbCmdParseEntry target_halted_cmd_desc = { +.handler = handle_target_halt, +.cmd = "?", +.cmd_startswith = 1 +}; +cmd_parser = &target_halted_cmd_desc; +} break; case 'c': { -- 2.20.1
[Qemu-devel] [PATCH v8 22/27] gdbstub: Implement generic query qemu.Supported
qemu.Supported query reply back with the supported qemu query/set commands (commands are seperated with a semicolon from each other). gdb> maint packet qqemu.Supported Signed-off-by: Jon Doron --- gdbstub.c | 9 + 1 file changed, 9 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 8bdfae4b29..00c07d6ec0 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2127,6 +2127,11 @@ static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, GDB_ATTACHED); } +static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +put_packet(gdb_ctx->s, "sstepbits;sstep"); +} + static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { /* Order is important if has same prefix */ { @@ -2203,6 +2208,10 @@ static GdbCmdParseEntry gdb_gen_query_table[] = { .handler = handle_query_attached, .cmd = "Attached", }, +{ +.handler = handle_query_qemu_supported, +.cmd = "qemu.Supported", +}, }; static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) -- 2.20.1
[Qemu-devel] [PATCH v8 25/27] kvm: Add API to read/write a CPU MSR value
Signed-off-by: Jon Doron --- accel/kvm/kvm-all.c | 39 +++ include/sysemu/kvm.h | 2 ++ 2 files changed, 41 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 524c4ddfbd..35207d910b 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2444,6 +2444,45 @@ void kvm_remove_all_breakpoints(CPUState *cpu) } #endif /* !KVM_CAP_SET_GUEST_DEBUG */ +int kvm_arch_read_msr(CPUState *cpu, uint32_t index, uint64_t *value) +{ +struct { +struct kvm_msrs info; +struct kvm_msr_entry entries[1]; +} msr_data; +int ret; + +msr_data.info.nmsrs = 1; +msr_data.entries[0].index = index; +ret = kvm_vcpu_ioctl(cpu, KVM_GET_MSRS, &msr_data); +if (ret < 0) { +return ret; +} + +*value = msr_data.entries[0].data; +return 0; +} + +int kvm_arch_write_msr(CPUState *cpu, uint32_t index, uint64_t value) +{ +struct { +struct kvm_msrs info; +struct kvm_msr_entry entries[1]; +} msr_data; +int ret; + +msr_data.info.nmsrs = 1; +msr_data.entries[0].index = index; +msr_data.entries[0].reserved = 0; +msr_data.entries[0].data = value; +ret = kvm_vcpu_ioctl(cpu, KVM_SET_MSRS, &msr_data); +if (ret < 0) { +return ret; +} + +return 0; +} + static int kvm_set_signal_mask(CPUState *cpu, const sigset_t *sigset) { KVMState *s = kvm_state; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index a6d1cd190f..409b1a5444 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -462,6 +462,8 @@ int kvm_vm_check_extension(KVMState *s, unsigned int extension); uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, uint32_t index, int reg); uint32_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index); +int kvm_arch_read_msr(CPUState *cpu, uint32_t index, uint64_t *value); +int kvm_arch_write_msr(CPUState *cpu, uint32_t index, uint64_t value); void kvm_set_sigmask_len(KVMState *s, unsigned int sigmask_len); -- 2.20.1
[Qemu-devel] [PATCH v8 15/27] gdbstub: Implement file io (F pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 62 +++ 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 3478ac778d..9fe130f30d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1772,6 +1772,39 @@ static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, gdb_ctx->str_buf); } +static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +int num_syscall_params; +GdbCmdVariant syscall_params[3] = {}; + +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 = NULL; +} + +if (syscall_params[2].opcode == (uint8_t)'C') { +put_packet(gdb_ctx->s, "T02"); +return; +} + +gdb_continue(gdb_ctx->s); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1913,28 +1946,13 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) return RS_IDLE; case 'F': { -target_ulong ret; -target_ulong err; - -ret = strtoull(p, (char **)&p, 16); -if (*p == ',') { -p++; -err = strtoull(p, (char **)&p, 16); -} else { -err = 0; -} -if (*p == ',') -p++; -type = *p; -if (s->current_syscall_cb) { -s->current_syscall_cb(s->c_cpu, ret, err); -s->current_syscall_cb = NULL; -} -if (type == 'C') { -put_packet(s, "T02"); -} else { -gdb_continue(s); -} +static const GdbCmdParseEntry file_io_cmd_desc = { +.handler = handle_file_io, +.cmd = "F", +.cmd_startswith = 1, +.schema = "s0" +}; +cmd_parser = &file_io_cmd_desc; } break; case 'g': -- 2.20.1
[Qemu-devel] [PATCH v8 07/27] gdbstub: Implement insert breakpoint (Z pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 33 + 1 file changed, 33 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 21cdaf4678..36c7353a22 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1588,6 +1588,29 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) } } +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 = gdb_breakpoint_insert(gdb_ctx->params[1].val_ull, +gdb_ctx->params[2].val_ull, +gdb_ctx->params[0].val_ul); +if (res >= 0) { +put_packet(gdb_ctx->s, "OK"); +return; +} else if (res == -ENOSYS) { +put_packet(gdb_ctx->s, ""); +return; +} + +put_packet(gdb_ctx->s, "E22"); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1843,6 +1866,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "OK"); break; case 'Z': +{ +static const GdbCmdParseEntry insert_bp_cmd_desc = { +.handler = handle_insert_bp, +.cmd = "Z", +.cmd_startswith = 1, +.schema = "l?L?L0" +}; +cmd_parser = &insert_bp_cmd_desc; +} +break; case 'z': type = strtoul(p, (char **)&p, 16); if (*p == ',') -- 2.20.1
[Qemu-devel] [PATCH v8 08/27] gdbstub: Implement remove breakpoint (z pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 49 - 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 36c7353a22..b42425b24c 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1611,6 +1611,29 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) 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 = gdb_breakpoint_remove(gdb_ctx->params[1].val_ull, +gdb_ctx->params[2].val_ull, +gdb_ctx->params[0].val_ul); +if (res >= 0) { +put_packet(gdb_ctx->s, "OK"); +return; +} else if (res == -ENOSYS) { +put_packet(gdb_ctx->s, ""); +return; +} + +put_packet(gdb_ctx->s, "E22"); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1877,23 +1900,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'z': -type = strtoul(p, (char **)&p, 16); -if (*p == ',') -p++; -addr = strtoull(p, (char **)&p, 16); -if (*p == ',') -p++; -len = strtoull(p, (char **)&p, 16); -if (ch == 'Z') -res = gdb_breakpoint_insert(addr, len, type); -else -res = gdb_breakpoint_remove(addr, len, type); -if (res >= 0) - put_packet(s, "OK"); -else if (res == -ENOSYS) -put_packet(s, ""); -else -put_packet(s, "E22"); +{ +static const GdbCmdParseEntry remove_bp_cmd_desc = { +.handler = handle_remove_bp, +.cmd = "z", +.cmd_startswith = 1, +.schema = "l?L?L0" +}; +cmd_parser = &remove_bp_cmd_desc; +} break; case 'H': { -- 2.20.1
[Qemu-devel] [PATCH v8 14/27] gdbstub: Implement read all registers (g pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 31 +++ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index adfe39b3a3..3478ac778d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1757,6 +1757,21 @@ static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, "OK"); } +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 = 0; +for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs; addr++) { +len += gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf + len, + addr); +} + +memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); +put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1764,7 +1779,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) CPUClass *cc; const char *p; uint32_t pid, tid; -int ch, reg_size, type, res; +int ch, type, res; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; char thread_id[16]; @@ -1923,14 +1938,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'g': -cpu_synchronize_state(s->g_cpu); -len = 0; -for (addr = 0; addr < s->g_cpu->gdb_num_g_regs; addr++) { -reg_size = gdb_read_register(s->g_cpu, mem_buf + len, addr); -len += reg_size; +{ +static const GdbCmdParseEntry read_all_regs_cmd_desc = { +.handler = handle_read_all_regs, +.cmd = "g", +.cmd_startswith = 1 +}; +cmd_parser = &read_all_regs_cmd_desc; } -memtohex(buf, mem_buf, len); -put_packet(s, buf); break; case 'G': { -- 2.20.1
[Qemu-devel] [PATCH v8 19/27] gdbstub: Implement generic set (Q pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 213 +++--- 1 file changed, 25 insertions(+), 188 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 83ae8738cc..2fd0d66f4d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1130,14 +1130,6 @@ static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, return GDB_ONE_THREAD; } -static int is_query_packet(const char *p, const char *query, char separator) -{ -unsigned int query_len = strlen(query); - -return strncmp(p, query, query_len) == 0 && -(p[query_len] == '\0' || p[query_len] == separator); -} - /** * gdb_handle_vcont - Parses and handles a vCont packet. * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there is @@ -2232,18 +2224,28 @@ static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) } } +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 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, type; +int ch; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; char thread_id[16]; -target_ulong addr, len; const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); @@ -2456,182 +2458,17 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'Q': -/* parse any 'q' packets here */ -if (!strcmp(p,"qemu.sstepbits")) { -/* Query Breakpoint bit definitions */ -snprintf(buf, sizeof(buf), "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", - SSTEP_ENABLE, - SSTEP_NOIRQ, - SSTEP_NOTIMER); -put_packet(s, buf); -break; -} else if (is_query_packet(p, "qemu.sstep", '=')) { -/* Display or change the sstep_flags */ -p += 10; -if (*p != '=') { -/* Display current setting */ -snprintf(buf, sizeof(buf), "0x%x", sstep_flags); -put_packet(s, buf); -break; -} -p++; -type = strtoul(p, (char **)&p, 16); -sstep_flags = type; -put_packet(s, "OK"); -break; -} else if (strcmp(p,"C") == 0) { -/* - * "Current thread" remains vague in the spec, so always return - * the first thread of the current process (gdb returns the - * first thread). - */ -cpu = 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") == 0) { -s->query_cpu = gdb_first_attached_cpu(s); -goto report_cpuinfo; -} else if (strcmp(p,"sThreadInfo") == 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 = gdb_next_attached_cpu(s, s->query_cpu); -} else -put_packet(s, "l"); -break; -} else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) { -if (read_thread_id(p + 16, &p, &pid, &tid) == GDB_READ_THREAD_ERR) { -put_packet(s, "E22"); -break; -} -cpu = gdb_get_cpu(s, pid, tid); -if (cpu != NULL) { -cpu_synchronize_state(cpu); - -if (s->multiprocess && (s->process_num > 1)) { -/* Print the CPU model and name in multiprocess mode */ -ObjectClass *oc = object_get_class(OBJECT(cpu)); -const char *cpu_model = object_class_get_name(oc); -char *cpu_name = -object_get_canonical_path_component(OBJECT(cpu)); -len = snprintf((char *)mem_buf, sizeof(buf) / 2,
[Qemu-devel] [PATCH v8 26/27] gdbstub: Add support to read a MSR for KVM target
gdb> maint packet qqemu.kvm.Rdmsr:MsrIndex Signed-off-by: Jon Doron --- gdbstub.c | 38 +- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/gdbstub.c b/gdbstub.c index bceceeec57..d5cdda190a 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2141,7 +2141,14 @@ static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) { -put_packet(gdb_ctx->s, "sstepbits;sstep;PhyMemMode"); +snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), + "sstepbits;sstep;PhyMemMode"); + +if (kvm_enabled()) { +pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";kvm.Rdmsr"); +} + +put_packet(gdb_ctx->s, gdb_ctx->str_buf); } static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, @@ -2166,6 +2173,29 @@ static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, "OK"); } +static void handle_query_kvm_read_msr(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +uint64_t msr_val; + +if (!kvm_enabled()) { +return; +} + +if (!gdb_ctx->num_params) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +if (kvm_arch_read_msr(gdbserver_state->c_cpu, gdb_ctx->params[0].val_ul, + &msr_val)) { +put_packet(gdb_ctx->s, "E00"); +return; +} + +snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "0x%" PRIx64, msr_val); +put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { /* Order is important if has same prefix */ { @@ -2250,6 +2280,12 @@ static GdbCmdParseEntry gdb_gen_query_table[] = { .handler = handle_query_qemu_phy_mem_mode, .cmd = "qemu.PhyMemMode", }, +{ +.handler = handle_query_kvm_read_msr, +.cmd = "qemu.kvm.Rdmsr:", +.cmd_startswith = 1, +.schema = "l0" +}, }; static GdbCmdParseEntry gdb_gen_set_table[] = { -- 2.20.1
[Qemu-devel] [PATCH v8 11/27] gdbstub: Implement write memory (M pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 51 +-- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index e9a3d0c2bc..8dc2e1d507 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1685,6 +1685,31 @@ static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) 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_ull, + 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 int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1893,24 +1918,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'M': -addr = strtoull(p, (char **)&p, 16); -if (*p == ',') -p++; -len = strtoull(p, (char **)&p, 16); -if (*p == ':') -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) != 0) { -put_packet(s, "E14"); -} else { -put_packet(s, "OK"); +{ +static const GdbCmdParseEntry write_mem_cmd_desc = { +.handler = handle_write_mem, +.cmd = "M", +.cmd_startswith = 1, +.schema = "L,L:s0" +}; +cmd_parser = &write_mem_cmd_desc; } break; case 'p': -- 2.20.1
[Qemu-devel] [PATCH v8 12/27] gdbstub: Implement read memory (m pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 48 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 8dc2e1d507..daa602edc3 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1710,6 +1710,30 @@ static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx) 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_ull, + 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_ull); +put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1899,22 +1923,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "OK"); break; case 'm': -addr = strtoull(p, (char **)&p, 16); -if (*p == ',') -p++; -len = strtoull(p, NULL, 16); - -/* memtohex() doubles the required space */ -if (len > MAX_PACKET_LENGTH / 2) { -put_packet (s, "E22"); -break; -} - -if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, false) != 0) { -put_packet (s, "E14"); -} else { -memtohex(buf, mem_buf, len); -put_packet(s, buf); +{ +static const GdbCmdParseEntry read_mem_cmd_desc = { +.handler = handle_read_mem, +.cmd = "m", +.cmd_startswith = 1, +.schema = "L,L0" +}; +cmd_parser = &read_mem_cmd_desc; } break; case 'M': -- 2.20.1
[Qemu-devel] [PATCH v8 18/27] gdbstub: Implement generic query (q pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 327 ++ 1 file changed, 327 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index d56d0fd235..83ae8738cc 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1915,6 +1915,323 @@ static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx) } } +static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), + "ENABLE=%x,NOIRQ=%x,NOTIMER=%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 = 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_flags); +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 = gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); +cpu = 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 = +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 = 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_ctx) +{ +CPUState *cpu; +int len; + +if (!gdb_ctx->num_params || +gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +cpu = 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 = object_get_class(OBJECT(cpu)); +const char *cpu_model = object_class_get_name(oc); +char *cpu_name = object_get_canonical_path_component(OBJECT(cpu)); +len = 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 = 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 = gdb_ctx->s->c_cpu->opaque; +snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), + "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx + ";Bss=" 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) { +
[Qemu-devel] [PATCH v8 04/27] gdbstub: Implement continue (c pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 25 +++-- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index c47ef7dd9c..89f1ab6524 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1522,6 +1522,16 @@ static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, "OK"); } +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); +} + +gdb_ctx->s->signal = 0; +gdb_continue(gdb_ctx->s); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1558,13 +1568,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) gdb_breakpoint_remove_all(); break; case 'c': -if (*p != '\0') { -addr = strtoull(p, (char **)&p, 16); -gdb_set_cpu_pc(s, addr); +{ +static const GdbCmdParseEntry continue_cmd_desc = { +.handler = handle_continue, +.cmd = "c", +.cmd_startswith = 1, +.schema = "L0" +}; +cmd_parser = &continue_cmd_desc; } -s->signal = 0; -gdb_continue(s); -return RS_IDLE; +break; case 'C': s->signal = gdb_signal_to_target (strtoul(p, (char **)&p, 16)); if (s->signal == -1) -- 2.20.1
[Qemu-devel] [PATCH v9 01/27] gdbstub: Add infrastructure to parse cmd packets
Signed-off-by: Jon Doron --- gdbstub.c | 200 ++ 1 file changed, 200 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index d54abd17cc..d5e0f3878a 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1268,6 +1268,206 @@ out: return res; } +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) +{ +static const char all_delimiters[] = ",;:="; +char curr_delimiters[2] = {0}; +const char *delimiters; + +if (delimiter == '?') { +delimiters = all_delimiters; +} else if (delimiter == '0') { +return strchr(param, '\0'); +} else if (delimiter == '.' && *param) { +return param + 1; +} else { +curr_delimiters[0] = delimiter; +delimiters = curr_delimiters; +} + +param += strcspn(param, delimiters); +if (*param) { +param++; +} +return param; +} + +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; + +*num_params = 0; + +if (!schema) { +return 0; +} + +curr_schema = schema; +curr_param = 0; +curr_data = 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 = 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 = cmd_next_param(curr_data, curr_schema[1]); +break; +case 's': +params[curr_param].data = curr_data; +curr_param++; +curr_data = cmd_next_param(curr_data, curr_schema[1]); +break; +case 'o': +params[curr_param].opcode = *(uint8_t *)curr_data; +curr_param++; +curr_data = cmd_next_param(curr_data, curr_schema[1]); +break; +case 't': +params[curr_param].thread_id.kind = +read_thread_id(curr_data, &curr_data, + ¶ms[curr_param].thread_id.pid, + ¶ms[curr_param].thread_id.tid); +curr_param++; +curr_data = cmd_next_param(curr_data, curr_schema[1]); +break; +case '?': +curr_data = cmd_next_param(curr_data, curr_schema[1]); +break; +default: +return -EINVAL; +} +curr_schema += 2; +} + +*num_params = curr_param; +return 0; +} + +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; + +typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx); + +/* + * cmd_startswith -> cmd is compared using startswith + * + * + * 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) + * '?' -> skip according to delimiter + * + * Currently supported delimiters: + * '?' -> Stop at any delimiter (",;:=\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; +}; +}; +const char *schema; +} GdbCmdParseEntry; + +static inline int startswith(const char *string, const char *pattern) +{ + return !strncmp(string, pattern, strlen(pattern)); +} + +static int process_string_cmd(
[Qemu-devel] [PATCH v9 02/27] gdbstub: Implement deatch (D pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 90 ++- 1 file changed, 50 insertions(+), 40 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index d5e0f3878a..621d689868 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1418,11 +1418,6 @@ static inline int startswith(const char *string, const char *pattern) return !strncmp(string, pattern, strlen(pattern)); } -static int process_string_cmd( -GDBState *s, void *user_ctx, const char *data, -const GdbCmdParseEntry *cmds, int num_cmds) -__attribute__((unused)); - static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, const GdbCmdParseEntry *cmds, int num_cmds) { @@ -1468,6 +1463,41 @@ static int process_string_cmd(GDBState *s, void *user_ctx, const char *data, return -1; } +static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +GDBProcess *process; +GDBState *s = gdb_ctx->s; +uint32_t pid = 1; + +if (s->multiprocess) { +if (!gdb_ctx->num_params) { +put_packet(s, "E22"); +return; +} + +pid = gdb_ctx->params[0].val_ul; +} + +process = gdb_get_process(s, pid); +gdb_process_breakpoint_remove_all(s, process); +process->attached = false; + +if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { +s->c_cpu = gdb_first_attached_cpu(s); +} + +if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { +s->g_cpu = gdb_first_attached_cpu(s); +} + +if (!s->c_cpu) { +/* No more process attached */ +gdb_syscall_mode = GDB_SYS_DISABLED; +gdb_continue(s); +} +put_packet(s, "OK"); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1482,6 +1512,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) uint8_t *registers; target_ulong addr, len; GDBThreadIdKind thread_kind; +const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); @@ -1582,42 +1613,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) error_report("QEMU: Terminated via GDBstub"); exit(0); case 'D': -/* Detach packet */ -pid = 1; - -if (s->multiprocess) { -unsigned long lpid; -if (*p != ';') { -put_packet(s, "E22"); -break; -} - -if (qemu_strtoul(p + 1, &p, 16, &lpid)) { -put_packet(s, "E22"); -break; -} - -pid = lpid; -} - -process = gdb_get_process(s, pid); -gdb_process_breakpoint_remove_all(s, process); -process->attached = false; - -if (pid == gdb_get_cpu_pid(s, s->c_cpu)) { -s->c_cpu = gdb_first_attached_cpu(s); -} - -if (pid == gdb_get_cpu_pid(s, s->g_cpu)) { -s->g_cpu = gdb_first_attached_cpu(s); -} - -if (s->c_cpu == NULL) { -/* No more process attached */ -gdb_syscall_mode = GDB_SYS_DISABLED; -gdb_continue(s); +{ +static const GdbCmdParseEntry detach_cmd_desc = { +.handler = handle_detach, +.cmd = "D", +.cmd_startswith = 1, +.schema = "?.l0" +}; +cmd_parser = &detach_cmd_desc; } -put_packet(s, "OK"); break; case 's': if (*p != '\0') { @@ -1990,6 +1994,12 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, buf); break; } + +if (cmd_parser && +process_string_cmd(s, NULL, line_buf, cmd_parser, 1)) { +put_packet(s, ""); +} + return RS_IDLE; } -- 2.20.1
[Qemu-devel] [PATCH v9 00/27] gdbstub: Refactor command packets handler
This patch series refactors the old gdbstub command packets handler with a new infrastructure which should ease extending and adding new and missing gdb command packets. version 9 changes: - checkpatch fixes version 8 changes: - Add new command to display the Supported qemu generic query/sets - kvm: Add API to read/write a MSR - Add new commands specific for qemu: * Command to swap the memory GDB sees to be the physical memory * Commands to read and write a MSR version 7 changes: - Fixed few checkpatch complaints - Feedback from Alex Bennee version 4-6 changes: - mostly feedback from Richard Henderson version 3 changes - Split the single patch to many individual patches for easier reviewing version 2 changes - Code convention fixes Jon Doron (27): gdbstub: Add infrastructure to parse cmd packets gdbstub: Implement deatch (D pkt) with new infra gdbstub: Implement thread_alive (T pkt) with new infra gdbstub: Implement continue (c pkt) with new infra gdbstub: Implement continue with signal (C pkt) with new infra gdbstub: Implement set_thread (H pkt) with new infra gdbstub: Implement insert breakpoint (Z pkt) with new infra gdbstub: Implement remove breakpoint (z pkt) with new infra gdbstub: Implement set register (P pkt) with new infra gdbstub: Implement get register (p pkt) with new infra gdbstub: Implement write memory (M pkt) with new infra gdbstub: Implement read memory (m pkt) with new infra gdbstub: Implement write all registers (G pkt) with new infra gdbstub: Implement read all registers (g pkt) with new infra gdbstub: Implement file io (F pkt) with new infra gdbstub: Implement step (s pkt) with new infra gdbstub: Implement v commands with new infra gdbstub: Implement generic query (q pkt) with new infra gdbstub: Implement generic set (Q pkt) with new infra gdbstub: Implement target halted (? pkt) with new infra gdbstub: Clear unused variables in gdb_handle_packet gdbstub: Implement generic query qemu.Supported gdbstub: Implement qemu physical memory mode gdbstub: Add another handler for setting qemu.sstep kvm: Add API to read/write a CPU MSR value gdbstub: Add support to read a MSR for KVM target gdbstub: Add support to write a MSR for KVM target accel/kvm/kvm-all.c | 39 + gdbstub.c| 1807 ++ include/sysemu/kvm.h |2 + 3 files changed, 1359 insertions(+), 489 deletions(-) -- 2.20.1
[Qemu-devel] [PATCH v9 03/27] gdbstub: Implement thread_alive (T pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 43 --- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 621d689868..c47ef7dd9c 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1498,6 +1498,30 @@ static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(s, "OK"); } +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 == GDB_READ_THREAD_ERR) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +cpu = 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 int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1798,17 +1822,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'T': -thread_kind = read_thread_id(p, &p, &pid, &tid); -if (thread_kind == GDB_READ_THREAD_ERR) { -put_packet(s, "E22"); -break; -} -cpu = gdb_get_cpu(s, pid, tid); - -if (cpu != NULL) { -put_packet(s, "OK"); -} else { -put_packet(s, "E22"); +{ +static const GdbCmdParseEntry thread_alive_cmd_desc = { +.handler = handle_thread_alive, +.cmd = "T", +.cmd_startswith = 1, +.schema = "t0" +}; +cmd_parser = &thread_alive_cmd_desc; } break; case 'q': -- 2.20.1
[Qemu-devel] [PATCH v9 09/27] gdbstub: Implement set register (P pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 39 ++- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index b42425b24c..10e3f12a68 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1634,6 +1634,27 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, "E22"); } +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 = 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 int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1878,15 +1899,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'P': -if (!gdb_has_xml) -goto unknown_command; -addr = strtoull(p, (char **)&p, 16); -if (*p == '=') -p++; -reg_size = strlen(p) / 2; -hextomem(mem_buf, p, reg_size); -gdb_write_register(s->g_cpu, mem_buf, addr); -put_packet(s, "OK"); +{ +static const GdbCmdParseEntry set_reg_cmd_desc = { +.handler = handle_set_reg, +.cmd = "P", +.cmd_startswith = 1, +.schema = "L?s0" +}; +cmd_parser = &set_reg_cmd_desc; +} break; case 'Z': { -- 2.20.1
[Qemu-devel] [PATCH v9 04/27] gdbstub: Implement continue (c pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 25 +++-- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index c47ef7dd9c..89f1ab6524 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1522,6 +1522,16 @@ static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, "OK"); } +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); +} + +gdb_ctx->s->signal = 0; +gdb_continue(gdb_ctx->s); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1558,13 +1568,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) gdb_breakpoint_remove_all(); break; case 'c': -if (*p != '\0') { -addr = strtoull(p, (char **)&p, 16); -gdb_set_cpu_pc(s, addr); +{ +static const GdbCmdParseEntry continue_cmd_desc = { +.handler = handle_continue, +.cmd = "c", +.cmd_startswith = 1, +.schema = "L0" +}; +cmd_parser = &continue_cmd_desc; } -s->signal = 0; -gdb_continue(s); -return RS_IDLE; +break; case 'C': s->signal = gdb_signal_to_target (strtoul(p, (char **)&p, 16)); if (s->signal == -1) -- 2.20.1
[Qemu-devel] [PATCH v9 12/27] gdbstub: Implement read memory (m pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 48 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 8dc2e1d507..daa602edc3 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1710,6 +1710,30 @@ static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx) 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_ull, + 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_ull); +put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1899,22 +1923,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "OK"); break; case 'm': -addr = strtoull(p, (char **)&p, 16); -if (*p == ',') -p++; -len = strtoull(p, NULL, 16); - -/* memtohex() doubles the required space */ -if (len > MAX_PACKET_LENGTH / 2) { -put_packet (s, "E22"); -break; -} - -if (target_memory_rw_debug(s->g_cpu, addr, mem_buf, len, false) != 0) { -put_packet (s, "E14"); -} else { -memtohex(buf, mem_buf, len); -put_packet(s, buf); +{ +static const GdbCmdParseEntry read_mem_cmd_desc = { +.handler = handle_read_mem, +.cmd = "m", +.cmd_startswith = 1, +.schema = "L,L0" +}; +cmd_parser = &read_mem_cmd_desc; } break; case 'M': -- 2.20.1
[Qemu-devel] [PATCH v9 05/27] gdbstub: Implement continue with signal (C pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 30 +- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 89f1ab6524..469aaeb875 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1532,6 +1532,21 @@ static void handle_continue(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_continue(gdb_ctx->s); } +static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +unsigned long signal = 0; + +if (gdb_ctx->num_params) { +signal = gdb_ctx->params[0].val_ul; +} + +gdb_ctx->s->signal = gdb_signal_to_target(signal); +if (gdb_ctx->s->signal == -1) { +gdb_ctx->s->signal = 0; +} +gdb_continue(gdb_ctx->s); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1579,11 +1594,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'C': -s->signal = gdb_signal_to_target (strtoul(p, (char **)&p, 16)); -if (s->signal == -1) -s->signal = 0; -gdb_continue(s); -return RS_IDLE; +{ +static const GdbCmdParseEntry cont_with_sig_cmd_desc = { +.handler = handle_cont_with_sig, +.cmd = "C", +.cmd_startswith = 1, +.schema = "l0" +}; +cmd_parser = &cont_with_sig_cmd_desc; +} +break; case 'v': if (strncmp(p, "Cont", 4) == 0) { p += 4; -- 2.20.1
[Qemu-devel] [PATCH v9 14/27] gdbstub: Implement read all registers (g pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 31 +++ 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index adfe39b3a3..3478ac778d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1757,6 +1757,21 @@ static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, "OK"); } +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 = 0; +for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs; addr++) { +len += gdb_read_register(gdb_ctx->s->g_cpu, gdb_ctx->mem_buf + len, + addr); +} + +memtohex(gdb_ctx->str_buf, gdb_ctx->mem_buf, len); +put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1764,7 +1779,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) CPUClass *cc; const char *p; uint32_t pid, tid; -int ch, reg_size, type, res; +int ch, type, res; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; char thread_id[16]; @@ -1923,14 +1938,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'g': -cpu_synchronize_state(s->g_cpu); -len = 0; -for (addr = 0; addr < s->g_cpu->gdb_num_g_regs; addr++) { -reg_size = gdb_read_register(s->g_cpu, mem_buf + len, addr); -len += reg_size; +{ +static const GdbCmdParseEntry read_all_regs_cmd_desc = { +.handler = handle_read_all_regs, +.cmd = "g", +.cmd_startswith = 1 +}; +cmd_parser = &read_all_regs_cmd_desc; } -memtohex(buf, mem_buf, len); -put_packet(s, buf); break; case 'G': { -- 2.20.1
[Qemu-devel] [PATCH v9 07/27] gdbstub: Implement insert breakpoint (Z pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 33 + 1 file changed, 33 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 21cdaf4678..36c7353a22 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1588,6 +1588,29 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx) } } +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 = gdb_breakpoint_insert(gdb_ctx->params[1].val_ull, +gdb_ctx->params[2].val_ull, +gdb_ctx->params[0].val_ul); +if (res >= 0) { +put_packet(gdb_ctx->s, "OK"); +return; +} else if (res == -ENOSYS) { +put_packet(gdb_ctx->s, ""); +return; +} + +put_packet(gdb_ctx->s, "E22"); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1843,6 +1866,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "OK"); break; case 'Z': +{ +static const GdbCmdParseEntry insert_bp_cmd_desc = { +.handler = handle_insert_bp, +.cmd = "Z", +.cmd_startswith = 1, +.schema = "l?L?L0" +}; +cmd_parser = &insert_bp_cmd_desc; +} +break; case 'z': type = strtoul(p, (char **)&p, 16); if (*p == ',') -- 2.20.1
[Qemu-devel] [PATCH v9 16/27] gdbstub: Implement step (s pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 25 +++-- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 9fe130f30d..9b0556f8be 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1805,6 +1805,16 @@ static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_continue(gdb_ctx->s); } +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_ull); +} + +cpu_single_step(gdb_ctx->s->c_cpu, sstep_flags); +gdb_continue(gdb_ctx->s); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1937,13 +1947,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 's': -if (*p != '\0') { -addr = strtoull(p, (char **)&p, 16); -gdb_set_cpu_pc(s, addr); +{ +static const GdbCmdParseEntry step_cmd_desc = { +.handler = handle_step, +.cmd = "s", +.cmd_startswith = 1, +.schema = "L0" +}; +cmd_parser = &step_cmd_desc; } -cpu_single_step(s->c_cpu, sstep_flags); -gdb_continue(s); -return RS_IDLE; +break; case 'F': { static const GdbCmdParseEntry file_io_cmd_desc = { -- 2.20.1
[Qemu-devel] [PATCH v9 17/27] gdbstub: Implement v commands with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 170 +++--- 1 file changed, 110 insertions(+), 60 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 9b0556f8be..d56d0fd235 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1815,6 +1815,106 @@ static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_continue(gdb_ctx->s); } +static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +put_packet(gdb_ctx->s, "vCont;c;C;s;S"); +} + +static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +int res; + +if (!gdb_ctx->num_params) { +return; +} + +res = gdb_handle_vcont(gdb_ctx->s, gdb_ctx->params[0].data); +if ((res == -EINVAL) || (res == -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 = gdb_get_process(gdb_ctx->s, gdb_ctx->params[0].val_ul); +if (!process) { +goto cleanup; +} + +cpu = get_first_cpu_in_process(gdb_ctx->s, process); +if (!cpu) { +goto cleanup; +} + +process->attached = true; +gdb_ctx->s->g_cpu = cpu; +gdb_ctx->s->c_cpu = 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[] = { +/* Order is important if has same prefix */ +{ +.handler = handle_v_cont_query, +.cmd = "Cont?", +.cmd_startswith = 1 +}, +{ +.handler = handle_v_cont, +.cmd = "Cont", +.cmd_startswith = 1, +.schema = "s0" +}, +{ +.handler = handle_v_attach, +.cmd = "Attach;", +.cmd_startswith = 1, +.schema = "l0" +}, +{ +.handler = handle_v_kill, +.cmd = "Kill;", +.cmd_startswith = 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 int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1822,7 +1922,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) CPUClass *cc; const char *p; uint32_t pid, tid; -int ch, type, res; +int ch, type; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; char thread_id[16]; @@ -1871,66 +1971,16 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'v': -if (strncmp(p, "Cont", 4) == 0) { -p += 4; -if (*p == '?') { -put_packet(s, "vCont;c;C;s;S"); -break; -} - -res = gdb_handle_vcont(s, p); - -if (res) { -if ((res == -EINVAL) || (res == -ERANGE)) { -put_packet(s, "E22"); -break; -} -goto unknown_command; -} -break; -} else if (strncmp(p, "Attach;", 7) == 0) { -unsigned long pid; - -p += 7; - -if (qemu_strtoul(p, &p, 16, &pid)) { -put_packet(s, "E22"); -break; -} - -process = gdb_get_process(s, pid); - -if (process == NULL) { -put_packet(s, "E22"); -break; -} - -cpu = get_first_cpu_in_process(s, process); - -if (cpu == NULL) { -/* Refuse to attach an empty process */ -put_packet(s, "E22"); -break; -} - -process->attached = true; - -s->g_cpu = cpu; -s->c_cpu = cpu; - -snprintf(buf, sizeof(buf), "T%02xthread:
[Qemu-devel] [PATCH v9 10/27] gdbstub: Implement get register (p pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 50 ++ 1 file changed, 38 insertions(+), 12 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 10e3f12a68..e9a3d0c2bc 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1655,6 +1655,36 @@ static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx) 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 = 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 int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1884,18 +1914,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'p': -/* 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) -goto unknown_command; -addr = strtoull(p, (char **)&p, 16); -reg_size = 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"); +{ +static const GdbCmdParseEntry get_reg_cmd_desc = { +.handler = handle_get_reg, +.cmd = "p", +.cmd_startswith = 1, +.schema = "L0" +}; +cmd_parser = &get_reg_cmd_desc; } break; case 'P': -- 2.20.1
[Qemu-devel] [PATCH v9 22/27] gdbstub: Implement generic query qemu.Supported
qemu.Supported query reply back with the supported qemu query/set commands (commands are seperated with a semicolon from each other). gdb> maint packet qqemu.Supported Signed-off-by: Jon Doron --- gdbstub.c | 9 + 1 file changed, 9 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 8bdfae4b29..00c07d6ec0 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2127,6 +2127,11 @@ static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, GDB_ATTACHED); } +static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +put_packet(gdb_ctx->s, "sstepbits;sstep"); +} + static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { /* Order is important if has same prefix */ { @@ -2203,6 +2208,10 @@ static GdbCmdParseEntry gdb_gen_query_table[] = { .handler = handle_query_attached, .cmd = "Attached", }, +{ +.handler = handle_query_qemu_supported, +.cmd = "qemu.Supported", +}, }; static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) -- 2.20.1
[Qemu-devel] [PATCH v9 08/27] gdbstub: Implement remove breakpoint (z pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 49 - 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 36c7353a22..b42425b24c 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1611,6 +1611,29 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx) 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 = gdb_breakpoint_remove(gdb_ctx->params[1].val_ull, +gdb_ctx->params[2].val_ull, +gdb_ctx->params[0].val_ul); +if (res >= 0) { +put_packet(gdb_ctx->s, "OK"); +return; +} else if (res == -ENOSYS) { +put_packet(gdb_ctx->s, ""); +return; +} + +put_packet(gdb_ctx->s, "E22"); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1877,23 +1900,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'z': -type = strtoul(p, (char **)&p, 16); -if (*p == ',') -p++; -addr = strtoull(p, (char **)&p, 16); -if (*p == ',') -p++; -len = strtoull(p, (char **)&p, 16); -if (ch == 'Z') -res = gdb_breakpoint_insert(addr, len, type); -else -res = gdb_breakpoint_remove(addr, len, type); -if (res >= 0) - put_packet(s, "OK"); -else if (res == -ENOSYS) -put_packet(s, ""); -else -put_packet(s, "E22"); +{ +static const GdbCmdParseEntry remove_bp_cmd_desc = { +.handler = handle_remove_bp, +.cmd = "z", +.cmd_startswith = 1, +.schema = "l?L?L0" +}; +cmd_parser = &remove_bp_cmd_desc; +} break; case 'H': { -- 2.20.1
[Qemu-devel] [PATCH v9 11/27] gdbstub: Implement write memory (M pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 51 +-- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index e9a3d0c2bc..8dc2e1d507 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1685,6 +1685,31 @@ static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx) 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_ull, + 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 int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1893,24 +1918,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'M': -addr = strtoull(p, (char **)&p, 16); -if (*p == ',') -p++; -len = strtoull(p, (char **)&p, 16); -if (*p == ':') -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) != 0) { -put_packet(s, "E14"); -} else { -put_packet(s, "OK"); +{ +static const GdbCmdParseEntry write_mem_cmd_desc = { +.handler = handle_write_mem, +.cmd = "M", +.cmd_startswith = 1, +.schema = "L,L:s0" +}; +cmd_parser = &write_mem_cmd_desc; } break; case 'p': -- 2.20.1
[Qemu-devel] [PATCH v9 20/27] gdbstub: Implement target halted (? pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 36 ++-- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 2fd0d66f4d..d678191705 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2239,13 +2239,30 @@ static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, ""); } +static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +char thread_id[16]; + +/* 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(); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { const char *p; int ch; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; -char thread_id[16]; const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); @@ -2257,15 +2274,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) 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_id))); -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(); +{ +static const GdbCmdParseEntry target_halted_cmd_desc = { +.handler = handle_target_halt, +.cmd = "?", +.cmd_startswith = 1 +}; +cmd_parser = &target_halted_cmd_desc; +} break; case 'c': { -- 2.20.1
[Qemu-devel] [PATCH v9 06/27] gdbstub: Implement set_thread (H pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 79 ++- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 469aaeb875..21cdaf4678 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1547,6 +1547,47 @@ static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx) gdb_continue(gdb_ctx->s); } +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 == GDB_READ_THREAD_ERR) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +if (gdb_ctx->params[1].thread_id.kind != GDB_ONE_THREAD) { +put_packet(gdb_ctx->s, "OK"); +return; +} + +cpu = 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 = cpu; +put_packet(gdb_ctx->s, "OK"); +break; +case 'g': +gdb_ctx->s->g_cpu = cpu; +put_packet(gdb_ctx->s, "OK"); +break; +default: +put_packet(gdb_ctx->s, "E22"); +break; +} +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1560,7 +1601,6 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) char thread_id[16]; uint8_t *registers; target_ulong addr, len; -GDBThreadIdKind thread_kind; const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); @@ -1823,35 +1863,14 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, "E22"); break; case 'H': -type = *p++; - -thread_kind = read_thread_id(p, &p, &pid, &tid); -if (thread_kind == GDB_READ_THREAD_ERR) { -put_packet(s, "E22"); -break; -} - -if (thread_kind != GDB_ONE_THREAD) { -put_packet(s, "OK"); -break; -} -cpu = gdb_get_cpu(s, pid, tid); -if (cpu == NULL) { -put_packet(s, "E22"); -break; -} -switch (type) { -case 'c': -s->c_cpu = cpu; -put_packet(s, "OK"); -break; -case 'g': -s->g_cpu = cpu; -put_packet(s, "OK"); -break; -default: - put_packet(s, "E22"); - break; +{ +static const GdbCmdParseEntry set_thread_cmd_desc = { +.handler = handle_set_thread, +.cmd = "H", +.cmd_startswith = 1, +.schema = "o.t0" +}; +cmd_parser = &set_thread_cmd_desc; } break; case 'T': -- 2.20.1
[Qemu-devel] [PATCH v9 15/27] gdbstub: Implement file io (F pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 62 +++ 1 file changed, 40 insertions(+), 22 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 3478ac778d..9fe130f30d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1772,6 +1772,39 @@ static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, gdb_ctx->str_buf); } +static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +int num_syscall_params; +GdbCmdVariant syscall_params[3] = {}; + +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 = NULL; +} + +if (syscall_params[2].opcode == (uint8_t)'C') { +put_packet(gdb_ctx->s, "T02"); +return; +} + +gdb_continue(gdb_ctx->s); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1913,28 +1946,13 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) return RS_IDLE; case 'F': { -target_ulong ret; -target_ulong err; - -ret = strtoull(p, (char **)&p, 16); -if (*p == ',') { -p++; -err = strtoull(p, (char **)&p, 16); -} else { -err = 0; -} -if (*p == ',') -p++; -type = *p; -if (s->current_syscall_cb) { -s->current_syscall_cb(s->c_cpu, ret, err); -s->current_syscall_cb = NULL; -} -if (type == 'C') { -put_packet(s, "T02"); -} else { -gdb_continue(s); -} +static const GdbCmdParseEntry file_io_cmd_desc = { +.handler = handle_file_io, +.cmd = "F", +.cmd_startswith = 1, +.schema = "s0" +}; +cmd_parser = &file_io_cmd_desc; } break; case 'g': -- 2.20.1
[Qemu-devel] [PATCH v9 23/27] gdbstub: Implement qemu physical memory mode
Add a new query/set which changes the memory GDB sees to physical memory only. gdb> maint packet qqemu.PhyMemMode will reply the current phy_mem_mode state (1 for enabled, 0 for disabled) gdb> maint packet Qqemu.PhyMemMode:1 Will make GDB read/write only to physical memory, set to 0 to disable Signed-off-by: Jon Doron --- gdbstub.c | 58 --- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 00c07d6ec0..88ff6224e6 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -46,11 +46,23 @@ #define GDB_ATTACHED "1" #endif +static int phy_memory_mode; + static inline int target_memory_rw_debug(CPUState *cpu, target_ulong addr, uint8_t *buf, int len, bool is_write) { -CPUClass *cc = CPU_GET_CLASS(cpu); +CPUClass *cc; +if (phy_memory_mode) { +if (is_write) { +cpu_physical_memory_write(addr, buf, len); +} else { +cpu_physical_memory_read(addr, buf, len); +} +return 0; +} + +cc = CPU_GET_CLASS(cpu); if (cc->memory_rw_debug) { return cc->memory_rw_debug(cpu, addr, buf, len, is_write); } @@ -2129,7 +2141,29 @@ static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) { -put_packet(gdb_ctx->s, "sstepbits;sstep"); +put_packet(gdb_ctx->s, "sstepbits;sstep;PhyMemMode"); +} + +static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, + void *user_ctx) +{ +snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "%d", phy_memory_mode); +put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + +static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +if (!gdb_ctx->num_params) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +if (!gdb_ctx->params[0].val_ul) { +phy_memory_mode = 0; +} else { +phy_memory_mode = 1; +} +put_packet(gdb_ctx->s, "OK"); } static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { @@ -2212,6 +2246,20 @@ static GdbCmdParseEntry gdb_gen_query_table[] = { .handler = handle_query_qemu_supported, .cmd = "qemu.Supported", }, +{ +.handler = handle_query_qemu_phy_mem_mode, +.cmd = "qemu.PhyMemMode", +}, +}; + +static GdbCmdParseEntry gdb_gen_set_table[] = { +/* Order is important if has same prefix */ +{ +.handler = handle_set_qemu_phy_mem_mode, +.cmd = "qemu.PhyMemMode:", +.cmd_startswith = 1, +.schema = "l0" +}, }; static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) @@ -2245,7 +2293,11 @@ static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx) return; } -put_packet(gdb_ctx->s, ""); +if (process_string_cmd(gdb_ctx->s, NULL, gdb_ctx->params[0].data, + gdb_gen_set_table, + ARRAY_SIZE(gdb_gen_set_table))) { +put_packet(gdb_ctx->s, ""); +} } static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) -- 2.20.1
[Qemu-devel] [PATCH v9 18/27] gdbstub: Implement generic query (q pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 327 ++ 1 file changed, 327 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index d56d0fd235..83ae8738cc 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1915,6 +1915,323 @@ static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx) } } +static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), + "ENABLE=%x,NOIRQ=%x,NOTIMER=%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 = 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_flags); +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 = gdb_get_cpu_process(gdb_ctx->s, gdb_ctx->s->g_cpu); +cpu = 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 = +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 = 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_ctx) +{ +CPUState *cpu; +int len; + +if (!gdb_ctx->num_params || +gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +cpu = 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 = object_get_class(OBJECT(cpu)); +const char *cpu_model = object_class_get_name(oc); +char *cpu_name = object_get_canonical_path_component(OBJECT(cpu)); +len = 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 = 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 = gdb_ctx->s->c_cpu->opaque; +snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), + "Text=" TARGET_ABI_FMT_lx ";Data=" TARGET_ABI_FMT_lx + ";Bss=" 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) { +
[Qemu-devel] [PATCH v9 13/27] gdbstub: Implement write all registers (G pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 41 +++-- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index daa602edc3..adfe39b3a3 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1734,6 +1734,29 @@ static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx) 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 = gdb_ctx->mem_buf; +len = strlen(gdb_ctx->params[0].data) / 2; +hextomem(registers, gdb_ctx->params[0].data, len); +for (addr = 0; addr < gdb_ctx->s->g_cpu->gdb_num_g_regs && len > 0; + addr++) { +reg_size = gdb_write_register(gdb_ctx->s->g_cpu, registers, addr); +len -= reg_size; +registers += reg_size; +} +put_packet(gdb_ctx->s, "OK"); +} + static int gdb_handle_packet(GDBState *s, const char *line_buf) { CPUState *cpu; @@ -1745,7 +1768,6 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) 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; const GdbCmdParseEntry *cmd_parser = NULL; @@ -1911,16 +1933,15 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) put_packet(s, buf); break; case 'G': -cpu_synchronize_state(s->g_cpu); -registers = mem_buf; -len = strlen(p) / 2; -hextomem((uint8_t *)registers, p, len); -for (addr = 0; addr < s->g_cpu->gdb_num_g_regs && len > 0; addr++) { -reg_size = gdb_write_register(s->g_cpu, registers, addr); -len -= reg_size; -registers += reg_size; +{ +static const GdbCmdParseEntry write_all_regs_cmd_desc = { +.handler = handle_write_all_regs, +.cmd = "G", +.cmd_startswith = 1, +.schema = "s0" +}; +cmd_parser = &write_all_regs_cmd_desc; } -put_packet(s, "OK"); break; case 'm': { -- 2.20.1
[Qemu-devel] [PATCH v9 25/27] kvm: Add API to read/write a CPU MSR value
Signed-off-by: Jon Doron --- accel/kvm/kvm-all.c | 39 +++ include/sysemu/kvm.h | 2 ++ 2 files changed, 41 insertions(+) diff --git a/accel/kvm/kvm-all.c b/accel/kvm/kvm-all.c index 524c4ddfbd..35207d910b 100644 --- a/accel/kvm/kvm-all.c +++ b/accel/kvm/kvm-all.c @@ -2444,6 +2444,45 @@ void kvm_remove_all_breakpoints(CPUState *cpu) } #endif /* !KVM_CAP_SET_GUEST_DEBUG */ +int kvm_arch_read_msr(CPUState *cpu, uint32_t index, uint64_t *value) +{ +struct { +struct kvm_msrs info; +struct kvm_msr_entry entries[1]; +} msr_data; +int ret; + +msr_data.info.nmsrs = 1; +msr_data.entries[0].index = index; +ret = kvm_vcpu_ioctl(cpu, KVM_GET_MSRS, &msr_data); +if (ret < 0) { +return ret; +} + +*value = msr_data.entries[0].data; +return 0; +} + +int kvm_arch_write_msr(CPUState *cpu, uint32_t index, uint64_t value) +{ +struct { +struct kvm_msrs info; +struct kvm_msr_entry entries[1]; +} msr_data; +int ret; + +msr_data.info.nmsrs = 1; +msr_data.entries[0].index = index; +msr_data.entries[0].reserved = 0; +msr_data.entries[0].data = value; +ret = kvm_vcpu_ioctl(cpu, KVM_SET_MSRS, &msr_data); +if (ret < 0) { +return ret; +} + +return 0; +} + static int kvm_set_signal_mask(CPUState *cpu, const sigset_t *sigset) { KVMState *s = kvm_state; diff --git a/include/sysemu/kvm.h b/include/sysemu/kvm.h index a6d1cd190f..409b1a5444 100644 --- a/include/sysemu/kvm.h +++ b/include/sysemu/kvm.h @@ -462,6 +462,8 @@ int kvm_vm_check_extension(KVMState *s, unsigned int extension); uint32_t kvm_arch_get_supported_cpuid(KVMState *env, uint32_t function, uint32_t index, int reg); uint32_t kvm_arch_get_supported_msr_feature(KVMState *s, uint32_t index); +int kvm_arch_read_msr(CPUState *cpu, uint32_t index, uint64_t *value); +int kvm_arch_write_msr(CPUState *cpu, uint32_t index, uint64_t value); void kvm_set_sigmask_len(KVMState *s, unsigned int sigmask_len); -- 2.20.1
[Qemu-devel] [PATCH v9 24/27] gdbstub: Add another handler for setting qemu.sstep
Follow GDB general query/set packet conventions, qemu.sstep can now be set with the following command as well: gdb> maint packet Qqemu.sstep:Value Signed-off-by: Jon Doron --- gdbstub.c | 6 ++ 1 file changed, 6 insertions(+) diff --git a/gdbstub.c b/gdbstub.c index 88ff6224e6..34da10260d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2260,6 +2260,12 @@ static GdbCmdParseEntry gdb_gen_set_table[] = { .cmd_startswith = 1, .schema = "l0" }, +{ +.handler = handle_set_qemu_sstep, +.cmd = "qemu.sstep:", +.cmd_startswith = 1, +.schema = "l0" +}, }; static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) -- 2.20.1
[Qemu-devel] [PATCH v9 21/27] gdbstub: Clear unused variables in gdb_handle_packet
Signed-off-by: Jon Doron --- gdbstub.c | 11 ++- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index d678191705..8bdfae4b29 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2259,17 +2259,11 @@ static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx) static int gdb_handle_packet(GDBState *s, const char *line_buf) { -const char *p; -int ch; -uint8_t mem_buf[MAX_PACKET_LENGTH]; -char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); -p = line_buf; -ch = *p++; -switch(ch) { +switch (line_buf[0]) { case '!': put_packet(s, "OK"); break; @@ -2486,8 +2480,7 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) break; default: /* put empty packet */ -buf[0] = '\0'; -put_packet(s, buf); +put_packet(s, ""); break; } -- 2.20.1
[Qemu-devel] [PATCH v9 26/27] gdbstub: Add support to read a MSR for KVM target
gdb> maint packet qqemu.kvm.Rdmsr:MsrIndex Signed-off-by: Jon Doron --- gdbstub.c | 38 +- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/gdbstub.c b/gdbstub.c index 34da10260d..f48c3a2b5f 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2141,7 +2141,14 @@ static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx) static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) { -put_packet(gdb_ctx->s, "sstepbits;sstep;PhyMemMode"); +snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), + "sstepbits;sstep;PhyMemMode"); + +if (kvm_enabled()) { +pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";kvm.Rdmsr"); +} + +put_packet(gdb_ctx->s, gdb_ctx->str_buf); } static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, @@ -2166,6 +2173,29 @@ static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, "OK"); } +static void handle_query_kvm_read_msr(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +uint64_t msr_val; + +if (!kvm_enabled()) { +return; +} + +if (!gdb_ctx->num_params) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +if (kvm_arch_read_msr(gdbserver_state->c_cpu, gdb_ctx->params[0].val_ul, + &msr_val)) { +put_packet(gdb_ctx->s, "E00"); +return; +} + +snprintf(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), "0x%" PRIx64, msr_val); +put_packet(gdb_ctx->s, gdb_ctx->str_buf); +} + static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { /* Order is important if has same prefix */ { @@ -2250,6 +2280,12 @@ static GdbCmdParseEntry gdb_gen_query_table[] = { .handler = handle_query_qemu_phy_mem_mode, .cmd = "qemu.PhyMemMode", }, +{ +.handler = handle_query_kvm_read_msr, +.cmd = "qemu.kvm.Rdmsr:", +.cmd_startswith = 1, +.schema = "l0" +}, }; static GdbCmdParseEntry gdb_gen_set_table[] = { -- 2.20.1
[Qemu-devel] [PATCH v9 19/27] gdbstub: Implement generic set (Q pkt) with new infra
Signed-off-by: Jon Doron --- gdbstub.c | 213 +++--- 1 file changed, 25 insertions(+), 188 deletions(-) diff --git a/gdbstub.c b/gdbstub.c index 83ae8738cc..2fd0d66f4d 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -1130,14 +1130,6 @@ static GDBThreadIdKind read_thread_id(const char *buf, const char **end_buf, return GDB_ONE_THREAD; } -static int is_query_packet(const char *p, const char *query, char separator) -{ -unsigned int query_len = strlen(query); - -return strncmp(p, query, query_len) == 0 && -(p[query_len] == '\0' || p[query_len] == separator); -} - /** * gdb_handle_vcont - Parses and handles a vCont packet. * returns -ENOTSUP if a command is unsupported, -EINVAL or -ERANGE if there is @@ -2232,18 +2224,28 @@ static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) } } +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 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, type; +int ch; uint8_t mem_buf[MAX_PACKET_LENGTH]; char buf[sizeof(mem_buf) + 1 /* trailing NUL */]; char thread_id[16]; -target_ulong addr, len; const GdbCmdParseEntry *cmd_parser = NULL; trace_gdbstub_io_command(line_buf); @@ -2456,182 +2458,17 @@ static int gdb_handle_packet(GDBState *s, const char *line_buf) } break; case 'Q': -/* parse any 'q' packets here */ -if (!strcmp(p,"qemu.sstepbits")) { -/* Query Breakpoint bit definitions */ -snprintf(buf, sizeof(buf), "ENABLE=%x,NOIRQ=%x,NOTIMER=%x", - SSTEP_ENABLE, - SSTEP_NOIRQ, - SSTEP_NOTIMER); -put_packet(s, buf); -break; -} else if (is_query_packet(p, "qemu.sstep", '=')) { -/* Display or change the sstep_flags */ -p += 10; -if (*p != '=') { -/* Display current setting */ -snprintf(buf, sizeof(buf), "0x%x", sstep_flags); -put_packet(s, buf); -break; -} -p++; -type = strtoul(p, (char **)&p, 16); -sstep_flags = type; -put_packet(s, "OK"); -break; -} else if (strcmp(p,"C") == 0) { -/* - * "Current thread" remains vague in the spec, so always return - * the first thread of the current process (gdb returns the - * first thread). - */ -cpu = 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") == 0) { -s->query_cpu = gdb_first_attached_cpu(s); -goto report_cpuinfo; -} else if (strcmp(p,"sThreadInfo") == 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 = gdb_next_attached_cpu(s, s->query_cpu); -} else -put_packet(s, "l"); -break; -} else if (strncmp(p,"ThreadExtraInfo,", 16) == 0) { -if (read_thread_id(p + 16, &p, &pid, &tid) == GDB_READ_THREAD_ERR) { -put_packet(s, "E22"); -break; -} -cpu = gdb_get_cpu(s, pid, tid); -if (cpu != NULL) { -cpu_synchronize_state(cpu); - -if (s->multiprocess && (s->process_num > 1)) { -/* Print the CPU model and name in multiprocess mode */ -ObjectClass *oc = object_get_class(OBJECT(cpu)); -const char *cpu_model = object_class_get_name(oc); -char *cpu_name = -object_get_canonical_path_component(OBJECT(cpu)); -len = snprintf((char *)mem_buf, sizeof(buf) / 2,
[Qemu-devel] [PATCH v9 27/27] gdbstub: Add support to write a MSR for KVM target
gdb> maint packet Qqemu.kvm.Wrmsr:MsrIndex,Value Signed-off-by: Jon Doron --- gdbstub.c | 29 - 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/gdbstub.c b/gdbstub.c index f48c3a2b5f..a434a3749e 100644 --- a/gdbstub.c +++ b/gdbstub.c @@ -2145,7 +2145,8 @@ static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx) "sstepbits;sstep;PhyMemMode"); if (kvm_enabled()) { -pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), ";kvm.Rdmsr"); +pstrcat(gdb_ctx->str_buf, sizeof(gdb_ctx->str_buf), +";kvm.Rdmsr;kvm.Wrmsr"); } put_packet(gdb_ctx->s, gdb_ctx->str_buf); @@ -2196,6 +2197,26 @@ static void handle_query_kvm_read_msr(GdbCmdContext *gdb_ctx, void *user_ctx) put_packet(gdb_ctx->s, gdb_ctx->str_buf); } +static void handle_set_kvm_write_msr(GdbCmdContext *gdb_ctx, void *user_ctx) +{ +if (!kvm_enabled()) { +return; +} + +if (gdb_ctx->num_params < 2) { +put_packet(gdb_ctx->s, "E22"); +return; +} + +if (kvm_arch_write_msr(gdbserver_state->c_cpu, gdb_ctx->params[0].val_ul, + gdb_ctx->params[1].val_ull)) { +put_packet(gdb_ctx->s, "E00"); +return; +} + +put_packet(gdb_ctx->s, "OK"); +} + static GdbCmdParseEntry gdb_gen_query_set_common_table[] = { /* Order is important if has same prefix */ { @@ -2302,6 +2323,12 @@ static GdbCmdParseEntry gdb_gen_set_table[] = { .cmd_startswith = 1, .schema = "l0" }, +{ +.handler = handle_set_kvm_write_msr, +.cmd = "qemu.kvm.Wrmsr:", +.cmd_startswith = 1, +.schema = "l,L0" +}, }; static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx) -- 2.20.1
Re: [PATCH v4 0/1] hw/hyperv/vmbus: Is it maintained?
On 13/11/2021, Maciej S. Szmigiero wrote: On 12.11.2021 21:39, Roman Kagan wrote: On Fri, Nov 12, 2021 at 09:32:31PM +0300, Vladimir Sementsov-Ogievskiy wrote: Add Den and Roman (his new address) Thanks, I missed it on the list indeed. 06.11.2021 16:41, Philippe Mathieu-Daudé wrote: This is the 4th time I send this patch. Is the VMBus infrastructure used / maintained? Should we deprecate & remove? I think it's fair to say it's not maintained. The whole hw/hyperv/vmbus.c was submitted as a part of the work by Jon to enable some obscure windows debugging feature which only worked in presence of VMBus. It was mostly taken from the respective branch of the (now effectively abandoned) downstream tree with an implementation of the core VMBus infrastructure and the devices using it; however, none of the actual VMBus devices ever made it into the mainline tree. The VMBus code works fine, is mostly self-contained and by being a part of the upstream QEMU it does benefit from any improvements done there and so it is much less likely to bit-rot with time. I am still committed to upstreaming a Hyper-V Dynamic Memory Protocol driver (which uses VMBus), however had been preempted by higher-priority work for now. Thanks, Maciej Hi guys, Sorry for the late reply, like Roman I also never got to submit the RFC for the Synth debugger device which requires the VMBus, I do hope to get to it at some point and VMBus is a required part for it. In the last year or so I have not had much time to spend on this but I do hope to get back to finishing what I have started. I'm not really sure I have the time or knowledge to maintain VMBus :( but I'll do my best to answer any questions as well. -- Jon.
Re: [PATCH v2 4/4] hw: hyperv: Initial commit for Synthetic Debugging device
ping On Wed, Feb 16, 2022, 12:25 Jon Doron wrote: > Signed-off-by: Jon Doron > --- > hw/hyperv/Kconfig | 5 + > hw/hyperv/meson.build | 1 + > hw/hyperv/syndbg.c| 402 ++ > 3 files changed, 408 insertions(+) > create mode 100644 hw/hyperv/syndbg.c > > diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig > index 3fbfe41c9e..fcf65903bd 100644 > --- a/hw/hyperv/Kconfig > +++ b/hw/hyperv/Kconfig > @@ -11,3 +11,8 @@ config VMBUS > bool > default y > depends on HYPERV > + > +config SYNDBG > +bool > +default y > +depends on VMBUS > diff --git a/hw/hyperv/meson.build b/hw/hyperv/meson.build > index 1367e2994f..b43f119ea5 100644 > --- a/hw/hyperv/meson.build > +++ b/hw/hyperv/meson.build > @@ -1,3 +1,4 @@ > specific_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c')) > specific_ss.add(when: 'CONFIG_HYPERV_TESTDEV', if_true: > files('hyperv_testdev.c')) > specific_ss.add(when: 'CONFIG_VMBUS', if_true: files('vmbus.c')) > +specific_ss.add(when: 'CONFIG_SYNDBG', if_true: files('syndbg.c')) > diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c > new file mode 100644 > index 00..8816bc4082 > --- /dev/null > +++ b/hw/hyperv/syndbg.c > @@ -0,0 +1,402 @@ > +/* > + * QEMU Hyper-V Synthetic Debugging device > + * > + * This work is licensed under the terms of the GNU GPL, version 2 or > later. > + * See the COPYING file in the top-level directory. > + */ > + > +#include "qemu/ctype.h" > +#include "qemu/osdep.h" > +#include "qemu/error-report.h" > +#include "qemu/main-loop.h" > +#include "qemu/sockets.h" > +#include "qemu-common.h" > +#include "qapi/error.h" > +#include "migration/vmstate.h" > +#include "hw/qdev-properties.h" > +#include "hw/loader.h" > +#include "cpu.h" > +#include "hw/hyperv/hyperv.h" > +#include "hw/hyperv/vmbus-bridge.h" > +#include "hw/hyperv/hyperv-proto.h" > +#include "net/net.h" > +#include "net/eth.h" > +#include "net/checksum.h" > +#include "trace.h" > + > +#define TYPE_HV_SYNDBG "hv-syndbg" > + > +typedef struct HvSynDbg { > +DeviceState parent_obj; > + > +char *host_ip; > +uint16_t host_port; > +bool use_hcalls; > + > +uint32_t target_ip; > +struct sockaddr_in servaddr; > +int socket; > +bool has_data_pending; > +uint64_t pending_page_gpa; > +} HvSynDbg; > + > +#define HVSYNDBG(obj) OBJECT_CHECK(HvSynDbg, (obj), TYPE_HV_SYNDBG) > + > +/* returns NULL unless there is exactly one HV Synth debug device */ > +static HvSynDbg *hv_syndbg_find(void) > +{ > +/* Returns NULL unless there is exactly one hvsd device */ > +return HVSYNDBG(object_resolve_path_type("", TYPE_HV_SYNDBG, NULL)); > +} > + > +static void set_pending_state(HvSynDbg *syndbg, bool has_pending) > +{ > +hwaddr out_len; > +void *out_data; > + > +syndbg->has_data_pending = has_pending; > + > +if (!syndbg->pending_page_gpa) { > +return; > +} > + > +out_len = 1; > +out_data = cpu_physical_memory_map(syndbg->pending_page_gpa, > &out_len, 1); > +if (out_data) { > +*(uint8_t *)out_data = !!has_pending; > +cpu_physical_memory_unmap(out_data, out_len, 1, out_len); > +} > +} > + > +static bool get_udb_pkt_data(void *p, uint32_t len, uint32_t *data_ofs, > + uint32_t *src_ip) > +{ > +uint32_t offset, curr_len = len; > + > +if (curr_len < sizeof(struct eth_header) || > +(be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto) != ETH_P_IP)) { > +return false; > +} > +offset = sizeof(struct eth_header); > +curr_len -= sizeof(struct eth_header); > + > +if (curr_len < sizeof(struct ip_header) || > +PKT_GET_IP_HDR(p)->ip_p != IP_PROTO_UDP) { > +return false; > +} > +offset += PKT_GET_IP_HDR_LEN(p); > +curr_len -= PKT_GET_IP_HDR_LEN(p); > + > +if (curr_len < sizeof(struct udp_header)) { > +return false; > +} > + > +offset += sizeof(struct udp_header); > +*data_ofs = offset; > +*src_ip = PKT_GET_IP_HDR(p)->ip_src; > +return true; > +} > + > +static uint16_t handle_send_msg(HvSynDbg *syndbg, uint64_t ingpa, > +uint32_t count, bool is_raw, > +uint32_t *pend
[PATCH v1 0/4] HyperV: Synthetic Debugging device
This patchset adds support for the synthetic debugging device. HyperV supports a special transport layer for the kernel debugger when running in HyperV. This patchset add supports for this device so you could have a setup fast windows kernel debugging. At this point of time, DHCP is not implmeneted so to set this up few things need to be noted. The scenario I used to test is having 2 VMs in the same virtual network i.e a Debugger VM with the NIC: -nic tap,model=virtio,mac=02:ca:01:01:01:01,script=/etc/qemu-ifup And it's IP is going to be static 192.168.53.12 And the VM we want to debug, to which we need to have the englightments and vmbus configured: -cpu host,hv-relaxed,hv_spinlocks=0x1fff,hv_time,+vmx,invtsc,hv-vapic,hv-vpindex,hv-synic,hv-syndbg \ -device vmbus-bridge \ -device hv-syndbg,host_ip=192.168.53.12,host_port=5,use_hcalls=false \ -nic tap,model=virtio,mac=02:ca:01:01:01:02,script=/etc/qemu-ifup \ Then in the debuggee VM we would setup the kernel debugging in the following way: If the VM is older than Win8: * Copy the proper platform kdvm.dll (make sure it's called kdvm.dll even if platform is 32bit) bcdedit /set {GUID} dbgtransport kdvm.dll bcdedit /set {GUID} loadoptions host_ip="1.2.3.4",host_port="5",nodhcp bcdedit /set {GUID} debug on bcdedit /set {GUID} halbreakpoint on Win8 and late: bcdedit /dbgsettings net hostip:7.7.7.7 port:5 nodhcp This is all the setup that is required to get the synthetic debugger configured correctly. Jon Doron (4): hyperv: SControl is optional to enable SynIc hyperv: Add definitions for syndbg hyperv: Add support to process syndbg commands hw: hyperv: Initial commit for Synthetic Debugging device docs/hyperv.txt | 15 + hw/hyperv/Kconfig| 5 + hw/hyperv/hyperv.c | 475 +-- hw/hyperv/meson.build| 1 + hw/hyperv/syndbg.c | 407 ++ include/hw/hyperv/hyperv-proto.h | 52 include/hw/hyperv/hyperv.h | 60 target/i386/cpu.c| 2 + target/i386/cpu.h| 7 + target/i386/kvm/hyperv-proto.h | 37 +++ target/i386/kvm/hyperv-stub.c| 6 + target/i386/kvm/hyperv.c | 52 +++- target/i386/kvm/kvm.c| 76 - 13 files changed, 1105 insertions(+), 90 deletions(-) create mode 100644 hw/hyperv/syndbg.c -- 2.34.1
[PATCH v1 3/4] hyperv: Add support to process syndbg commands
SynDbg commands can come from two different flows: 1. Hypercalls, in this mode the data being sent is fully encapsulated network packets. 2. SynDbg specific MSRs, in this mode only the data that needs to be transfered is passed. Signed-off-by: Jon Doron --- docs/hyperv.txt | 15 +++ hw/hyperv/hyperv.c| 242 ++ include/hw/hyperv/hyperv.h| 58 target/i386/cpu.c | 2 + target/i386/cpu.h | 7 + target/i386/kvm/hyperv-stub.c | 6 + target/i386/kvm/hyperv.c | 52 +++- target/i386/kvm/kvm.c | 76 ++- 8 files changed, 450 insertions(+), 8 deletions(-) diff --git a/docs/hyperv.txt b/docs/hyperv.txt index 0417c183a3..7abc1b2d89 100644 --- a/docs/hyperv.txt +++ b/docs/hyperv.txt @@ -225,6 +225,21 @@ default (WS2016). Note: hv-version-id-* are not enlightenments and thus don't enable Hyper-V identification when specified without any other enlightenments. +3.21. hv-syndbg +=== +Enables Hyper-V synthetic debugger interface, this is a special interface used +by Windows Kernel debugger to send the packets through, rather than sending +them via serial/network . +Whe enabled, this enlightenment provides additional communication facilities +to the guest: SynDbg messages. +This new communication is used by Windows Kernel debugger rather than sending +packets via serial/network, adding significant performance boost over the other +comm channels. +This enlightenment requires a VMBus device (-device vmbus-bridge,irq=15) +and the follow enlightenments to work: +hv-relaxed,hv_time,hv-vapic,hv-vpindex,hv-synic,hv-runtime,hv-stimer + + 4. Supplementary features = diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 88c9cc1334..c86e2aa02e 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -730,3 +730,245 @@ uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast) } return HV_STATUS_INVALID_CONNECTION_ID; } + +static HvSynDbgHandler hv_syndbg_handler; +static void *hv_syndbg_context; +void hyperv_set_syndbg_handler(HvSynDbgHandler handler, void *context) +{ +assert(!hv_syndbg_handler); +hv_syndbg_handler = handler; +hv_syndbg_context = context; +} + +uint16_t hyperv_hcall_reset_dbg_session(uint64_t outgpa) +{ +uint16_t ret; +HvSynDbgMsg msg; +struct hyperv_reset_debug_session_output *reset_dbg_session = NULL; +hwaddr len; + +if (!hv_syndbg_handler) { +ret = HV_STATUS_INVALID_HYPERCALL_CODE; +goto cleanup; +} + +len = sizeof(*reset_dbg_session); +reset_dbg_session = cpu_physical_memory_map(outgpa, &len, 1); +if (!reset_dbg_session || len < sizeof(*reset_dbg_session)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +msg.type = HV_SYNDBG_MSG_CONNECTION_INFO; +ret = hv_syndbg_handler(hv_syndbg_context, &msg); +if (ret) { +goto cleanup; +} + +reset_dbg_session->host_ip = msg.u.connection_info.host_ip; +reset_dbg_session->host_port = msg.u.connection_info.host_port; +/* The following fields are only used as validation for KDVM */ +memset(&reset_dbg_session->host_mac, 0, + sizeof(reset_dbg_session->host_mac)); +reset_dbg_session->target_ip = msg.u.connection_info.host_ip; +reset_dbg_session->target_port = msg.u.connection_info.host_port; +memset(&reset_dbg_session->target_mac, 0, + sizeof(reset_dbg_session->target_mac)); +cleanup: +if (reset_dbg_session) { +cpu_physical_memory_unmap(reset_dbg_session, + sizeof(*reset_dbg_session), 1, len); +} + +return ret; +} + +uint16_t hyperv_hcall_retreive_dbg_data(uint64_t ingpa, uint64_t outgpa, +bool fast) +{ +uint16_t ret; +struct hyperv_retrieve_debug_data_input *debug_data_in = NULL; +struct hyperv_retrieve_debug_data_output *debug_data_out = NULL; +hwaddr in_len, out_len; +HvSynDbgMsg msg; + +if (fast || !hv_syndbg_handler) { +ret = HV_STATUS_INVALID_HYPERCALL_CODE; +goto cleanup; +} + +in_len = sizeof(*debug_data_in); +debug_data_in = cpu_physical_memory_map(ingpa, &in_len, 0); +if (!debug_data_in || in_len < sizeof(*debug_data_in)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +out_len = sizeof(*debug_data_out); +debug_data_out = cpu_physical_memory_map(outgpa, &out_len, 1); +if (!debug_data_out || out_len < sizeof(*debug_data_out)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +msg.type = HV_SYNDBG_MSG_RECV; +msg.u.recv.buf_gpa = outgpa + sizeof(*debug_data_out); +msg.u.recv.count = TARGET_PAGE_SIZE - sizeof(*debug_data_out); +msg.u.recv.options = debug_data_in->options; +msg.u.recv.timeout
[PATCH v1 1/4] hyperv: SControl is optional to enable SynIc
SynIc can be enabled regardless of the SControl mechanisim which can register a GSI for a given SintRoute. This behaviour can achived by setting enabling SIMP and then the guest will poll on the message slot. Once there is another message pending the host will set the message slot with the pending flag. When the guest polls from the message slot, incase the pending flag is set it will write to the HV_X64_MSR_EOM indicating it has cleared the slow and we can try and push our message again. Signed-off-by: Jon Doron --- hw/hyperv/hyperv.c | 233 - include/hw/hyperv/hyperv.h | 2 + 2 files changed, 153 insertions(+), 82 deletions(-) diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index cb1074f234..88c9cc1334 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -27,18 +27,70 @@ struct SynICState { CPUState *cs; -bool enabled; +bool sctl_enabled; hwaddr msg_page_addr; hwaddr event_page_addr; MemoryRegion msg_page_mr; MemoryRegion event_page_mr; struct hyperv_message_page *msg_page; struct hyperv_event_flags_page *event_page; + +QemuMutex sint_routes_mutex; +QLIST_HEAD(, HvSintRoute) sint_routes; }; #define TYPE_SYNIC "hyperv-synic" OBJECT_DECLARE_SIMPLE_TYPE(SynICState, SYNIC) +/* + * KVM has its own message producers (SynIC timers). To guarantee + * serialization with both KVM vcpu and the guest cpu, the messages are first + * staged in an intermediate area and then posted to the SynIC message page in + * the vcpu thread. + */ +typedef struct HvSintStagedMessage { +/* message content staged by hyperv_post_msg */ +struct hyperv_message msg; +/* callback + data (r/o) to complete the processing in a BH */ +HvSintMsgCb cb; +void *cb_data; +/* message posting status filled by cpu_post_msg */ +int status; +/* passing the buck: */ +enum { +/* initial state */ +HV_STAGED_MSG_FREE, +/* + * hyperv_post_msg (e.g. in main loop) grabs the staged area (FREE -> + * BUSY), copies msg, and schedules cpu_post_msg on the assigned cpu + */ +HV_STAGED_MSG_BUSY, +/* + * cpu_post_msg (vcpu thread) tries to copy staged msg to msg slot, + * notify the guest, records the status, marks the posting done (BUSY + * -> POSTED), and schedules sint_msg_bh BH + */ +HV_STAGED_MSG_POSTED, +/* + * sint_msg_bh (BH) verifies that the posting is done, runs the + * callback, and starts over (POSTED -> FREE) + */ +} state; +} HvSintStagedMessage; + +struct HvSintRoute { +uint32_t sint; +SynICState *synic; +int gsi; +EventNotifier sint_set_notifier; +EventNotifier sint_ack_notifier; + +HvSintStagedMessage *staged_msg; + +unsigned refcount; +QLIST_ENTRY(HvSintRoute) link; +}; + static bool synic_enabled; bool hyperv_is_synic_enabled(void) @@ -51,11 +103,11 @@ static SynICState *get_synic(CPUState *cs) return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); } -static void synic_update(SynICState *synic, bool enable, +static void synic_update(SynICState *synic, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { -synic->enabled = enable; +synic->sctl_enabled = sctl_enable; if (synic->msg_page_addr != msg_page_addr) { if (synic->msg_page_addr) { memory_region_del_subregion(get_system_memory(), @@ -80,7 +132,7 @@ static void synic_update(SynICState *synic, bool enable, } } -void hyperv_synic_update(CPUState *cs, bool enable, +void hyperv_synic_update(CPUState *cs, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { SynICState *synic = get_synic(cs); @@ -89,7 +141,7 @@ void hyperv_synic_update(CPUState *cs, bool enable, return; } -synic_update(synic, enable, msg_page_addr, event_page_addr); +synic_update(synic, sctl_enable, msg_page_addr, event_page_addr); } static void synic_realize(DeviceState *dev, Error **errp) @@ -110,16 +162,20 @@ static void synic_realize(DeviceState *dev, Error **errp) sizeof(*synic->event_page), &error_abort); synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr); synic->event_page = memory_region_get_ram_ptr(&synic->event_page_mr); +qemu_mutex_init(&synic->sint_routes_mutex); +QLIST_INIT(&synic->sint_routes); g_free(msgp_name); g_free(eventp_name); } + static void synic_reset(DeviceState *dev) { SynICState *synic = SYNIC(dev); memset(synic->msg_page, 0, sizeof(*synic->msg_page)); memset(synic->event_page, 0, sizeof(*synic->event_page)); synic_update(synic, false, 0, 0); +assert(QLIST_EMPTY(&synic->sint_routes)); } static void s
[PATCH v1 2/4] hyperv: Add definitions for syndbg
Add all required definitions for hyperv synthetic debugger interface. Signed-off-by: Jon Doron --- include/hw/hyperv/hyperv-proto.h | 52 target/i386/kvm/hyperv-proto.h | 37 +++ 2 files changed, 89 insertions(+) diff --git a/include/hw/hyperv/hyperv-proto.h b/include/hw/hyperv/hyperv-proto.h index 21dc28aee9..94c9658eb0 100644 --- a/include/hw/hyperv/hyperv-proto.h +++ b/include/hw/hyperv/hyperv-proto.h @@ -24,12 +24,17 @@ #define HV_STATUS_INVALID_PORT_ID 17 #define HV_STATUS_INVALID_CONNECTION_ID 18 #define HV_STATUS_INSUFFICIENT_BUFFERS19 +#define HV_STATUS_NOT_ACKNOWLEDGED20 +#define HV_STATUS_NO_DATA 27 /* * Hypercall numbers */ #define HV_POST_MESSAGE 0x005c #define HV_SIGNAL_EVENT 0x005d +#define HV_POST_DEBUG_DATA0x0069 +#define HV_RETREIVE_DEBUG_DATA0x006a +#define HV_RESET_DEBUG_SESSION0x006b #define HV_HYPERCALL_FAST (1u << 16) /* @@ -127,4 +132,51 @@ struct hyperv_event_flags_page { struct hyperv_event_flags slot[HV_SINT_COUNT]; }; +/* + * Kernel debugger structures + */ + +/* Options flags for hyperv_reset_debug_session */ +#define HV_DEBUG_PURGE_INCOMING_DATA0x0001 +#define HV_DEBUG_PURGE_OUTGOING_DATA0x0002 +struct hyperv_reset_debug_session_input { +uint32_t options; +} __attribute__ ((__packed__)); + +struct hyperv_reset_debug_session_output { +uint32_t host_ip; +uint32_t target_ip; +uint16_t host_port; +uint16_t target_port; +uint8_t host_mac[6]; +uint8_t target_mac[6]; +} __attribute__ ((__packed__)); + +/* Options for hyperv_post_debug_data */ +#define HV_DEBUG_POST_LOOP 0x0001 + +struct hyperv_post_debug_data_input { +uint32_t count; +uint32_t options; +/*uint8_t data[HV_HYP_PAGE_SIZE - 2 * sizeof(uint32_t)];*/ +} __attribute__ ((__packed__)); + +struct hyperv_post_debug_data_output { +uint32_t pending_count; +} __attribute__ ((__packed__)); + +/* Options for hyperv_retrieve_debug_data */ +#define HV_DEBUG_RETRIEVE_LOOP 0x0001 +#define HV_DEBUG_RETRIEVE_TEST_ACTIVITY 0x0002 + +struct hyperv_retrieve_debug_data_input { +uint32_t count; +uint32_t options; +uint64_t timeout; +} __attribute__ ((__packed__)); + +struct hyperv_retrieve_debug_data_output { +uint32_t retrieved_count; +uint32_t remaining_count; +} __attribute__ ((__packed__)); #endif diff --git a/target/i386/kvm/hyperv-proto.h b/target/i386/kvm/hyperv-proto.h index 89f81afda7..9480bcdf04 100644 --- a/target/i386/kvm/hyperv-proto.h +++ b/target/i386/kvm/hyperv-proto.h @@ -19,6 +19,9 @@ #define HV_CPUID_ENLIGHTMENT_INFO 0x4004 #define HV_CPUID_IMPLEMENT_LIMITS 0x4005 #define HV_CPUID_NESTED_FEATURES 0x400A +#define HV_CPUID_SYNDBG_VENDOR_AND_MAX_FUNCTIONS0x4080 +#define HV_CPUID_SYNDBG_INTERFACE 0x4081 +#define HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES 0x4082 #define HV_CPUID_MIN 0x4005 #define HV_CPUID_MAX 0x4000 #define HV_HYPERVISOR_PRESENT_BIT 0x8000 @@ -55,8 +58,14 @@ #define HV_GUEST_IDLE_STATE_AVAILABLE (1u << 5) #define HV_FREQUENCY_MSRS_AVAILABLE (1u << 8) #define HV_GUEST_CRASH_MSR_AVAILABLE(1u << 10) +#define HV_FEATURE_DEBUG_MSRS_AVAILABLE (1u << 11) #define HV_STIMER_DIRECT_MODE_AVAILABLE (1u << 19) +/* + * HV_CPUID_FEATURES.EBX bits + */ +#define HV_PARTITION_DEUBGGING_ALLOWED (1u << 12) + /* * HV_CPUID_ENLIGHTMENT_INFO.EAX bits */ @@ -72,6 +81,11 @@ #define HV_ENLIGHTENED_VMCS_RECOMMENDED (1u << 14) #define HV_NO_NONARCH_CORESHARING (1u << 18) +/* + * HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES.EAX bits + */ +#define HV_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING(1u << 1) + /* * Basic virtualized MSRs */ @@ -130,6 +144,18 @@ #define HV_X64_MSR_STIMER3_CONFIG 0x40B6 #define HV_X64_MSR_STIMER3_COUNT0x40B7 +/* + * Hyper-V Synthetic debug options MSR + */ +#define HV_X64_MSR_SYNDBG_CONTROL 0x40F1 +#define HV_X64_MSR_SYNDBG_STATUS0x40F2 +#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x40F3 +#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x40F4 +#define HV_X64_MSR_SYNDBG_PENDING_BUFFER0x40F5 +#define HV_X64_MSR_SYNDBG_OPTIONS 0x40FF + +#define HV_X64_SYNDBG_OPTION_USE_HCALLS BIT(2) + /* * Guest crash notification MSRs */ @@ -168,5 +194,16 @@ #define HV_STIMER_COUNT 4 +/* + * Synthetic debugger control definitions + */ +#define HV_SYNDBG_CONTROL_SEND (1u <<
[PATCH v1 4/4] hw: hyperv: Initial commit for Synthetic Debugging device
Signed-off-by: Jon Doron --- hw/hyperv/Kconfig | 5 + hw/hyperv/meson.build | 1 + hw/hyperv/syndbg.c| 407 ++ 3 files changed, 413 insertions(+) create mode 100644 hw/hyperv/syndbg.c diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig index 3fbfe41c9e..fcf65903bd 100644 --- a/hw/hyperv/Kconfig +++ b/hw/hyperv/Kconfig @@ -11,3 +11,8 @@ config VMBUS bool default y depends on HYPERV + +config SYNDBG +bool +default y +depends on VMBUS diff --git a/hw/hyperv/meson.build b/hw/hyperv/meson.build index 1367e2994f..b43f119ea5 100644 --- a/hw/hyperv/meson.build +++ b/hw/hyperv/meson.build @@ -1,3 +1,4 @@ specific_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c')) specific_ss.add(when: 'CONFIG_HYPERV_TESTDEV', if_true: files('hyperv_testdev.c')) specific_ss.add(when: 'CONFIG_VMBUS', if_true: files('vmbus.c')) +specific_ss.add(when: 'CONFIG_SYNDBG', if_true: files('syndbg.c')) diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c new file mode 100644 index 00..837eb33458 --- /dev/null +++ b/hw/hyperv/syndbg.c @@ -0,0 +1,407 @@ +/* + * QEMU Hyper-V Synthetic Debugging device + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/ctype.h" +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/sockets.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/loader.h" +#include "cpu.h" +#include "hw/hyperv/hyperv.h" +#include "hw/hyperv/vmbus-bridge.h" +#include "hw/hyperv/hyperv-proto.h" +#include "net/net.h" +#include "net/eth.h" +#include "net/checksum.h" +#include "trace.h" + +#define TYPE_HV_SYNDBG "hv-syndbg" + +typedef struct HvSynDbg { +DeviceState parent_obj; + +char *host_ip; +uint16_t host_port; +bool use_hcalls; + +uint32_t target_ip; +struct sockaddr_in servaddr; +int socket; +bool has_data_pending; +uint64_t pending_page_gpa; +} HvSynDbg; + +#define HVSYNDBG(obj) OBJECT_CHECK(HvSynDbg, (obj), TYPE_HV_SYNDBG) + +/* returns NULL unless there is exactly one HV Synth debug device */ +static HvSynDbg *hv_syndbg_find(void) +{ +/* Returns NULL unless there is exactly one hvsd device */ +return HVSYNDBG(object_resolve_path_type("", TYPE_HV_SYNDBG, NULL)); +} + +static void set_pending_state(HvSynDbg *syndbg, bool has_pending) +{ +hwaddr out_len; +void *out_data; + +syndbg->has_data_pending = has_pending; + +if (!syndbg->pending_page_gpa) { +return; +} + +out_len = 1; +out_data = cpu_physical_memory_map(syndbg->pending_page_gpa, &out_len, 1); +if (out_data) { +*(uint8_t *)out_data = !!has_pending; +cpu_physical_memory_unmap(out_data, out_len, 1, out_len); +} +} + +static bool get_udb_pkt_data(void *p, uint32_t len, uint32_t *data_ofs, + uint32_t *src_ip) +{ +uint32_t offset, curr_len = len; + +if (curr_len < sizeof(struct eth_header) || +(be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto) != ETH_P_IP)) { +return false; +} +offset = sizeof(struct eth_header); +curr_len -= sizeof(struct eth_header); + +if (curr_len < sizeof(struct ip_header) || +PKT_GET_IP_HDR(p)->ip_p != IP_PROTO_UDP) { +return false; +} +offset += PKT_GET_IP_HDR_LEN(p); +curr_len -= PKT_GET_IP_HDR_LEN(p); + +if (curr_len < sizeof(struct udp_header)) { +return false; +} + +offset += sizeof(struct udp_header); +*data_ofs = offset; +*src_ip = PKT_GET_IP_HDR(p)->ip_src; +return true; +} + +static uint16_t handle_send_msg(HvSynDbg *syndbg, uint64_t ingpa, +uint32_t count, bool is_raw, +uint32_t *pending_count) +{ +uint16_t ret; +hwaddr data_len; +void *debug_data = NULL; +uint32_t udp_data_ofs = 0; +const void *pkt_data; +int sent_count; + +data_len = count; +debug_data = cpu_physical_memory_map(ingpa, &data_len, 0); +if (!debug_data || data_len < count) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +if (is_raw && +!get_udb_pkt_data(debug_data, count, &udp_data_ofs, + &syndbg->target_ip)) { +ret = HV_STATUS_SUCCESS; +goto cleanup; +} + +pkt_data = (const void *)((uintptr_t)debug_data + udp_data_ofs); +sent_count = qemu_sendto(syndbg->socket, pkt_data, count - udp_data_ofs, +
Re: [PATCH v1 0/4] HyperV: Synthetic Debugging device
On 04/02/2022, Jon Doron wrote: Ping This patchset adds support for the synthetic debugging device. HyperV supports a special transport layer for the kernel debugger when running in HyperV. This patchset add supports for this device so you could have a setup fast windows kernel debugging. At this point of time, DHCP is not implmeneted so to set this up few things need to be noted. The scenario I used to test is having 2 VMs in the same virtual network i.e a Debugger VM with the NIC: -nic tap,model=virtio,mac=02:ca:01:01:01:01,script=/etc/qemu-ifup And it's IP is going to be static 192.168.53.12 And the VM we want to debug, to which we need to have the englightments and vmbus configured: -cpu host,hv-relaxed,hv_spinlocks=0x1fff,hv_time,+vmx,invtsc,hv-vapic,hv-vpindex,hv-synic,hv-syndbg \ -device vmbus-bridge \ -device hv-syndbg,host_ip=192.168.53.12,host_port=5,use_hcalls=false \ -nic tap,model=virtio,mac=02:ca:01:01:01:02,script=/etc/qemu-ifup \ Then in the debuggee VM we would setup the kernel debugging in the following way: If the VM is older than Win8: * Copy the proper platform kdvm.dll (make sure it's called kdvm.dll even if platform is 32bit) bcdedit /set {GUID} dbgtransport kdvm.dll bcdedit /set {GUID} loadoptions host_ip="1.2.3.4",host_port="5",nodhcp bcdedit /set {GUID} debug on bcdedit /set {GUID} halbreakpoint on Win8 and late: bcdedit /dbgsettings net hostip:7.7.7.7 port:5 nodhcp This is all the setup that is required to get the synthetic debugger configured correctly. Jon Doron (4): hyperv: SControl is optional to enable SynIc hyperv: Add definitions for syndbg hyperv: Add support to process syndbg commands hw: hyperv: Initial commit for Synthetic Debugging device docs/hyperv.txt | 15 + hw/hyperv/Kconfig| 5 + hw/hyperv/hyperv.c | 475 +-- hw/hyperv/meson.build| 1 + hw/hyperv/syndbg.c | 407 ++ include/hw/hyperv/hyperv-proto.h | 52 include/hw/hyperv/hyperv.h | 60 target/i386/cpu.c| 2 + target/i386/cpu.h| 7 + target/i386/kvm/hyperv-proto.h | 37 +++ target/i386/kvm/hyperv-stub.c| 6 + target/i386/kvm/hyperv.c | 52 +++- target/i386/kvm/kvm.c| 76 - 13 files changed, 1105 insertions(+), 90 deletions(-) create mode 100644 hw/hyperv/syndbg.c -- 2.34.1
[PATCH v2 2/4] hyperv: Add definitions for syndbg
Add all required definitions for hyperv synthetic debugger interface. Signed-off-by: Jon Doron --- include/hw/hyperv/hyperv-proto.h | 52 target/i386/kvm/hyperv-proto.h | 37 +++ 2 files changed, 89 insertions(+) diff --git a/include/hw/hyperv/hyperv-proto.h b/include/hw/hyperv/hyperv-proto.h index 21dc28aee9..4a2297307b 100644 --- a/include/hw/hyperv/hyperv-proto.h +++ b/include/hw/hyperv/hyperv-proto.h @@ -24,12 +24,17 @@ #define HV_STATUS_INVALID_PORT_ID 17 #define HV_STATUS_INVALID_CONNECTION_ID 18 #define HV_STATUS_INSUFFICIENT_BUFFERS19 +#define HV_STATUS_NOT_ACKNOWLEDGED20 +#define HV_STATUS_NO_DATA 27 /* * Hypercall numbers */ #define HV_POST_MESSAGE 0x005c #define HV_SIGNAL_EVENT 0x005d +#define HV_POST_DEBUG_DATA0x0069 +#define HV_RETRIEVE_DEBUG_DATA0x006a +#define HV_RESET_DEBUG_SESSION0x006b #define HV_HYPERCALL_FAST (1u << 16) /* @@ -127,4 +132,51 @@ struct hyperv_event_flags_page { struct hyperv_event_flags slot[HV_SINT_COUNT]; }; +/* + * Kernel debugger structures + */ + +/* Options flags for hyperv_reset_debug_session */ +#define HV_DEBUG_PURGE_INCOMING_DATA0x0001 +#define HV_DEBUG_PURGE_OUTGOING_DATA0x0002 +struct hyperv_reset_debug_session_input { +uint32_t options; +} __attribute__ ((__packed__)); + +struct hyperv_reset_debug_session_output { +uint32_t host_ip; +uint32_t target_ip; +uint16_t host_port; +uint16_t target_port; +uint8_t host_mac[6]; +uint8_t target_mac[6]; +} __attribute__ ((__packed__)); + +/* Options for hyperv_post_debug_data */ +#define HV_DEBUG_POST_LOOP 0x0001 + +struct hyperv_post_debug_data_input { +uint32_t count; +uint32_t options; +/*uint8_t data[HV_HYP_PAGE_SIZE - 2 * sizeof(uint32_t)];*/ +} __attribute__ ((__packed__)); + +struct hyperv_post_debug_data_output { +uint32_t pending_count; +} __attribute__ ((__packed__)); + +/* Options for hyperv_retrieve_debug_data */ +#define HV_DEBUG_RETRIEVE_LOOP 0x0001 +#define HV_DEBUG_RETRIEVE_TEST_ACTIVITY 0x0002 + +struct hyperv_retrieve_debug_data_input { +uint32_t count; +uint32_t options; +uint64_t timeout; +} __attribute__ ((__packed__)); + +struct hyperv_retrieve_debug_data_output { +uint32_t retrieved_count; +uint32_t remaining_count; +} __attribute__ ((__packed__)); #endif diff --git a/target/i386/kvm/hyperv-proto.h b/target/i386/kvm/hyperv-proto.h index 89f81afda7..e40e59411c 100644 --- a/target/i386/kvm/hyperv-proto.h +++ b/target/i386/kvm/hyperv-proto.h @@ -19,6 +19,9 @@ #define HV_CPUID_ENLIGHTMENT_INFO 0x4004 #define HV_CPUID_IMPLEMENT_LIMITS 0x4005 #define HV_CPUID_NESTED_FEATURES 0x400A +#define HV_CPUID_SYNDBG_VENDOR_AND_MAX_FUNCTIONS0x4080 +#define HV_CPUID_SYNDBG_INTERFACE 0x4081 +#define HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES 0x4082 #define HV_CPUID_MIN 0x4005 #define HV_CPUID_MAX 0x4000 #define HV_HYPERVISOR_PRESENT_BIT 0x8000 @@ -55,8 +58,14 @@ #define HV_GUEST_IDLE_STATE_AVAILABLE (1u << 5) #define HV_FREQUENCY_MSRS_AVAILABLE (1u << 8) #define HV_GUEST_CRASH_MSR_AVAILABLE(1u << 10) +#define HV_FEATURE_DEBUG_MSRS_AVAILABLE (1u << 11) #define HV_STIMER_DIRECT_MODE_AVAILABLE (1u << 19) +/* + * HV_CPUID_FEATURES.EBX bits + */ +#define HV_PARTITION_DEBUGGING_ALLOWED (1u << 12) + /* * HV_CPUID_ENLIGHTMENT_INFO.EAX bits */ @@ -72,6 +81,11 @@ #define HV_ENLIGHTENED_VMCS_RECOMMENDED (1u << 14) #define HV_NO_NONARCH_CORESHARING (1u << 18) +/* + * HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES.EAX bits + */ +#define HV_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING(1u << 1) + /* * Basic virtualized MSRs */ @@ -130,6 +144,18 @@ #define HV_X64_MSR_STIMER3_CONFIG 0x40B6 #define HV_X64_MSR_STIMER3_COUNT0x40B7 +/* + * Hyper-V Synthetic debug options MSR + */ +#define HV_X64_MSR_SYNDBG_CONTROL 0x40F1 +#define HV_X64_MSR_SYNDBG_STATUS0x40F2 +#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x40F3 +#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x40F4 +#define HV_X64_MSR_SYNDBG_PENDING_BUFFER0x40F5 +#define HV_X64_MSR_SYNDBG_OPTIONS 0x40FF + +#define HV_X64_SYNDBG_OPTION_USE_HCALLS BIT(2) + /* * Guest crash notification MSRs */ @@ -168,5 +194,16 @@ #define HV_STIMER_COUNT 4 +/* + * Synthetic debugger control definitions + */ +#define HV_SYNDBG_CONTROL_SEND (1u <<
[PATCH v2 4/4] hw: hyperv: Initial commit for Synthetic Debugging device
Signed-off-by: Jon Doron --- hw/hyperv/Kconfig | 5 + hw/hyperv/meson.build | 1 + hw/hyperv/syndbg.c| 402 ++ 3 files changed, 408 insertions(+) create mode 100644 hw/hyperv/syndbg.c diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig index 3fbfe41c9e..fcf65903bd 100644 --- a/hw/hyperv/Kconfig +++ b/hw/hyperv/Kconfig @@ -11,3 +11,8 @@ config VMBUS bool default y depends on HYPERV + +config SYNDBG +bool +default y +depends on VMBUS diff --git a/hw/hyperv/meson.build b/hw/hyperv/meson.build index 1367e2994f..b43f119ea5 100644 --- a/hw/hyperv/meson.build +++ b/hw/hyperv/meson.build @@ -1,3 +1,4 @@ specific_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c')) specific_ss.add(when: 'CONFIG_HYPERV_TESTDEV', if_true: files('hyperv_testdev.c')) specific_ss.add(when: 'CONFIG_VMBUS', if_true: files('vmbus.c')) +specific_ss.add(when: 'CONFIG_SYNDBG', if_true: files('syndbg.c')) diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c new file mode 100644 index 00..8816bc4082 --- /dev/null +++ b/hw/hyperv/syndbg.c @@ -0,0 +1,402 @@ +/* + * QEMU Hyper-V Synthetic Debugging device + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/ctype.h" +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/sockets.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/loader.h" +#include "cpu.h" +#include "hw/hyperv/hyperv.h" +#include "hw/hyperv/vmbus-bridge.h" +#include "hw/hyperv/hyperv-proto.h" +#include "net/net.h" +#include "net/eth.h" +#include "net/checksum.h" +#include "trace.h" + +#define TYPE_HV_SYNDBG "hv-syndbg" + +typedef struct HvSynDbg { +DeviceState parent_obj; + +char *host_ip; +uint16_t host_port; +bool use_hcalls; + +uint32_t target_ip; +struct sockaddr_in servaddr; +int socket; +bool has_data_pending; +uint64_t pending_page_gpa; +} HvSynDbg; + +#define HVSYNDBG(obj) OBJECT_CHECK(HvSynDbg, (obj), TYPE_HV_SYNDBG) + +/* returns NULL unless there is exactly one HV Synth debug device */ +static HvSynDbg *hv_syndbg_find(void) +{ +/* Returns NULL unless there is exactly one hvsd device */ +return HVSYNDBG(object_resolve_path_type("", TYPE_HV_SYNDBG, NULL)); +} + +static void set_pending_state(HvSynDbg *syndbg, bool has_pending) +{ +hwaddr out_len; +void *out_data; + +syndbg->has_data_pending = has_pending; + +if (!syndbg->pending_page_gpa) { +return; +} + +out_len = 1; +out_data = cpu_physical_memory_map(syndbg->pending_page_gpa, &out_len, 1); +if (out_data) { +*(uint8_t *)out_data = !!has_pending; +cpu_physical_memory_unmap(out_data, out_len, 1, out_len); +} +} + +static bool get_udb_pkt_data(void *p, uint32_t len, uint32_t *data_ofs, + uint32_t *src_ip) +{ +uint32_t offset, curr_len = len; + +if (curr_len < sizeof(struct eth_header) || +(be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto) != ETH_P_IP)) { +return false; +} +offset = sizeof(struct eth_header); +curr_len -= sizeof(struct eth_header); + +if (curr_len < sizeof(struct ip_header) || +PKT_GET_IP_HDR(p)->ip_p != IP_PROTO_UDP) { +return false; +} +offset += PKT_GET_IP_HDR_LEN(p); +curr_len -= PKT_GET_IP_HDR_LEN(p); + +if (curr_len < sizeof(struct udp_header)) { +return false; +} + +offset += sizeof(struct udp_header); +*data_ofs = offset; +*src_ip = PKT_GET_IP_HDR(p)->ip_src; +return true; +} + +static uint16_t handle_send_msg(HvSynDbg *syndbg, uint64_t ingpa, +uint32_t count, bool is_raw, +uint32_t *pending_count) +{ +uint16_t ret; +hwaddr data_len; +void *debug_data = NULL; +uint32_t udp_data_ofs = 0; +const void *pkt_data; +int sent_count; + +data_len = count; +debug_data = cpu_physical_memory_map(ingpa, &data_len, 0); +if (!debug_data || data_len < count) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +if (is_raw && +!get_udb_pkt_data(debug_data, count, &udp_data_ofs, + &syndbg->target_ip)) { +ret = HV_STATUS_SUCCESS; +goto cleanup; +} + +pkt_data = (const void *)((uintptr_t)debug_data + udp_data_ofs); +sent_count = qemu_sendto(syndbg->socket, pkt_data, count - udp_data_ofs, +
[PATCH v2 1/4] hyperv: SControl is optional to enable SynIc
SynIc can be enabled regardless of the SControl mechanisim which can register a GSI for a given SintRoute. This behaviour can achived by setting enabling SIMP and then the guest will poll on the message slot. Once there is another message pending the host will set the message slot with the pending flag. When the guest polls from the message slot, in case the pending flag is set it will write to the HV_X64_MSR_EOM indicating it has cleared the slot and we can try and push our message again. Signed-off-by: Jon Doron --- hw/hyperv/hyperv.c | 109 +++-- 1 file changed, 76 insertions(+), 33 deletions(-) diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index cb1074f234..aaba6b4901 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -27,13 +27,16 @@ struct SynICState { CPUState *cs; -bool enabled; +bool sctl_enabled; hwaddr msg_page_addr; hwaddr event_page_addr; MemoryRegion msg_page_mr; MemoryRegion event_page_mr; struct hyperv_message_page *msg_page; struct hyperv_event_flags_page *event_page; + +QemuMutex sint_routes_mutex; +QLIST_HEAD(, HvSintRoute) sint_routes; }; #define TYPE_SYNIC "hyperv-synic" @@ -51,11 +54,11 @@ static SynICState *get_synic(CPUState *cs) return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); } -static void synic_update(SynICState *synic, bool enable, +static void synic_update(SynICState *synic, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { -synic->enabled = enable; +synic->sctl_enabled = sctl_enable; if (synic->msg_page_addr != msg_page_addr) { if (synic->msg_page_addr) { memory_region_del_subregion(get_system_memory(), @@ -80,7 +83,7 @@ static void synic_update(SynICState *synic, bool enable, } } -void hyperv_synic_update(CPUState *cs, bool enable, +void hyperv_synic_update(CPUState *cs, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { SynICState *synic = get_synic(cs); @@ -89,7 +92,7 @@ void hyperv_synic_update(CPUState *cs, bool enable, return; } -synic_update(synic, enable, msg_page_addr, event_page_addr); +synic_update(synic, sctl_enable, msg_page_addr, event_page_addr); } static void synic_realize(DeviceState *dev, Error **errp) @@ -110,16 +113,20 @@ static void synic_realize(DeviceState *dev, Error **errp) sizeof(*synic->event_page), &error_abort); synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr); synic->event_page = memory_region_get_ram_ptr(&synic->event_page_mr); +qemu_mutex_init(&synic->sint_routes_mutex); +QLIST_INIT(&synic->sint_routes); g_free(msgp_name); g_free(eventp_name); } + static void synic_reset(DeviceState *dev) { SynICState *synic = SYNIC(dev); memset(synic->msg_page, 0, sizeof(*synic->msg_page)); memset(synic->event_page, 0, sizeof(*synic->event_page)); synic_update(synic, false, 0, 0); +assert(QLIST_EMPTY(&synic->sint_routes)); } static void synic_class_init(ObjectClass *klass, void *data) @@ -214,6 +221,7 @@ struct HvSintRoute { HvSintStagedMessage *staged_msg; unsigned refcount; +QLIST_ENTRY(HvSintRoute) link; }; static CPUState *hyperv_find_vcpu(uint32_t vp_index) @@ -259,7 +267,7 @@ static void cpu_post_msg(CPUState *cs, run_on_cpu_data data) assert(staged_msg->state == HV_STAGED_MSG_BUSY); -if (!synic->enabled || !synic->msg_page_addr) { +if (!synic->msg_page_addr) { staged_msg->status = -ENXIO; goto posted; } @@ -343,7 +351,7 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno) if (eventno > HV_EVENT_FLAGS_COUNT) { return -EINVAL; } -if (!synic->enabled || !synic->event_page_addr) { +if (!synic->sctl_enabled || !synic->event_page_addr) { return -ENXIO; } @@ -364,11 +372,12 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno) HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, HvSintMsgCb cb, void *cb_data) { -HvSintRoute *sint_route; -EventNotifier *ack_notifier; +HvSintRoute *sint_route = NULL; +EventNotifier *ack_notifier = NULL; int r, gsi; CPUState *cs; SynICState *synic; +bool ack_event_initialized = false; cs = hyperv_find_vcpu(vp_index); if (!cs) { @@ -381,57 +390,77 @@ HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, } sint_route = g_new0(HvSintRoute, 1); -r = event_notifier_init(&sint_route->sint_set_notifier, false); -if (r) { -goto err; +if (!sint_route) { +return NULL; } +sint_route->synic =
[PATCH v2 0/4] HyperV: Synthetic Debugging device
This patchset adds support for the synthetic debugging device. HyperV supports a special transport layer for the kernel debugger when running in HyperV. This patchset add supports for this device so you could have a setup fast windows kernel debugging. At this point of time, DHCP is not implmeneted so to set this up few things need to be noted. The scenario I used to test is having 2 VMs in the same virtual network i.e a Debugger VM with the NIC: -nic tap,model=virtio,mac=02:ca:01:01:01:01,script=/etc/qemu-ifup And it's IP is going to be static 192.168.53.12 And the VM we want to debug, to which we need to have the englightments and vmbus configured: -cpu host,hv-relaxed,hv_spinlocks=0x1fff,hv_time,+vmx,invtsc,hv-vapic,hv-vpindex,hv-synic,hv-syndbg \ -device vmbus-bridge \ -device hv-syndbg,host_ip=192.168.53.12,host_port=5,use_hcalls=false \ -nic tap,model=virtio,mac=02:ca:01:01:01:02,script=/etc/qemu-ifup \ Then in the debuggee VM we would setup the kernel debugging in the following way: If the VM is older than Win8: * Copy the proper platform kdvm.dll (make sure it's called kdvm.dll even if platform is 32bit) bcdedit /set {GUID} dbgtransport kdvm.dll bcdedit /set {GUID} loadoptions host_ip="1.2.3.4",host_port="5",nodhcp bcdedit /set {GUID} debug on bcdedit /set {GUID} halbreakpoint on Win8 and late: bcdedit /dbgsettings net hostip:7.7.7.7 port:5 nodhcp This is all the setup that is required to get the synthetic debugger configured correctly. Jon Doron (4): hyperv: SControl is optional to enable SynIc hyperv: Add definitions for syndbg hyperv: Add support to process syndbg commands hw: hyperv: Initial commit for Synthetic Debugging device docs/hyperv.txt | 15 ++ hw/hyperv/Kconfig| 5 + hw/hyperv/hyperv.c | 352 --- hw/hyperv/meson.build| 1 + hw/hyperv/syndbg.c | 402 +++ include/hw/hyperv/hyperv-proto.h | 52 include/hw/hyperv/hyperv.h | 58 + target/i386/cpu.c| 2 + target/i386/cpu.h| 7 + target/i386/kvm/hyperv-proto.h | 37 +++ target/i386/kvm/hyperv-stub.c| 6 + target/i386/kvm/hyperv.c | 52 +++- target/i386/kvm/kvm.c| 76 +- 13 files changed, 1024 insertions(+), 41 deletions(-) create mode 100644 hw/hyperv/syndbg.c -- 2.35.1
[PATCH v2 3/4] hyperv: Add support to process syndbg commands
SynDbg commands can come from two different flows: 1. Hypercalls, in this mode the data being sent is fully encapsulated network packets. 2. SynDbg specific MSRs, in this mode only the data that needs to be transfered is passed. Signed-off-by: Jon Doron --- docs/hyperv.txt | 15 +++ hw/hyperv/hyperv.c| 243 ++ include/hw/hyperv/hyperv.h| 58 target/i386/cpu.c | 2 + target/i386/cpu.h | 7 + target/i386/kvm/hyperv-stub.c | 6 + target/i386/kvm/hyperv.c | 52 +++- target/i386/kvm/kvm.c | 76 ++- 8 files changed, 451 insertions(+), 8 deletions(-) diff --git a/docs/hyperv.txt b/docs/hyperv.txt index 0417c183a3..33588a0396 100644 --- a/docs/hyperv.txt +++ b/docs/hyperv.txt @@ -225,6 +225,21 @@ default (WS2016). Note: hv-version-id-* are not enlightenments and thus don't enable Hyper-V identification when specified without any other enlightenments. +3.21. hv-syndbg +=== +Enables Hyper-V synthetic debugger interface, this is a special interface used +by Windows Kernel debugger to send the packets through, rather than sending +them via serial/network . +When enabled, this enlightenment provides additional communication facilities +to the guest: SynDbg messages. +This new communication is used by Windows Kernel debugger rather than sending +packets via serial/network, adding significant performance boost over the other +comm channels. +This enlightenment requires a VMBus device (-device vmbus-bridge,irq=15) +and the follow enlightenments to work: +hv-relaxed,hv_time,hv-vapic,hv-vpindex,hv-synic,hv-runtime,hv-stimer + + 4. Supplementary features = diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index aaba6b4901..86d295395e 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -704,3 +704,246 @@ uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast) } return HV_STATUS_INVALID_CONNECTION_ID; } + +static HvSynDbgHandler hv_syndbg_handler; +static void *hv_syndbg_context; + +void hyperv_set_syndbg_handler(HvSynDbgHandler handler, void *context) +{ +assert(!hv_syndbg_handler); +hv_syndbg_handler = handler; +hv_syndbg_context = context; +} + +uint16_t hyperv_hcall_reset_dbg_session(uint64_t outgpa) +{ +uint16_t ret; +HvSynDbgMsg msg; +struct hyperv_reset_debug_session_output *reset_dbg_session = NULL; +hwaddr len; + +if (!hv_syndbg_handler) { +ret = HV_STATUS_INVALID_HYPERCALL_CODE; +goto cleanup; +} + +len = sizeof(*reset_dbg_session); +reset_dbg_session = cpu_physical_memory_map(outgpa, &len, 1); +if (!reset_dbg_session || len < sizeof(*reset_dbg_session)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +msg.type = HV_SYNDBG_MSG_CONNECTION_INFO; +ret = hv_syndbg_handler(hv_syndbg_context, &msg); +if (ret) { +goto cleanup; +} + +reset_dbg_session->host_ip = msg.u.connection_info.host_ip; +reset_dbg_session->host_port = msg.u.connection_info.host_port; +/* The following fields are only used as validation for KDVM */ +memset(&reset_dbg_session->host_mac, 0, + sizeof(reset_dbg_session->host_mac)); +reset_dbg_session->target_ip = msg.u.connection_info.host_ip; +reset_dbg_session->target_port = msg.u.connection_info.host_port; +memset(&reset_dbg_session->target_mac, 0, + sizeof(reset_dbg_session->target_mac)); +cleanup: +if (reset_dbg_session) { +cpu_physical_memory_unmap(reset_dbg_session, + sizeof(*reset_dbg_session), 1, len); +} + +return ret; +} + +uint16_t hyperv_hcall_retreive_dbg_data(uint64_t ingpa, uint64_t outgpa, +bool fast) +{ +uint16_t ret; +struct hyperv_retrieve_debug_data_input *debug_data_in = NULL; +struct hyperv_retrieve_debug_data_output *debug_data_out = NULL; +hwaddr in_len, out_len; +HvSynDbgMsg msg; + +if (fast || !hv_syndbg_handler) { +ret = HV_STATUS_INVALID_HYPERCALL_CODE; +goto cleanup; +} + +in_len = sizeof(*debug_data_in); +debug_data_in = cpu_physical_memory_map(ingpa, &in_len, 0); +if (!debug_data_in || in_len < sizeof(*debug_data_in)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +out_len = sizeof(*debug_data_out); +debug_data_out = cpu_physical_memory_map(outgpa, &out_len, 1); +if (!debug_data_out || out_len < sizeof(*debug_data_out)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +msg.type = HV_SYNDBG_MSG_RECV; +msg.u.recv.buf_gpa = outgpa + sizeof(*debug_data_out); +msg.u.recv.count = TARGET_PAGE_SIZE - sizeof(*debug_data_out); +msg.u.recv.options = debug_data_in->options; +msg.u.recv.timeout
Re: [PATCH v1 3/4] hyperv: Add support to process syndbg commands
On 16/02/2022, Emanuele Giuseppe Esposito wrote: On 04/02/2022 11:07, Jon Doron wrote: SynDbg commands can come from two different flows: 1. Hypercalls, in this mode the data being sent is fully encapsulated network packets. 2. SynDbg specific MSRs, in this mode only the data that needs to be transfered is passed. Signed-off-by: Jon Doron --- docs/hyperv.txt | 15 +++ hw/hyperv/hyperv.c| 242 ++ include/hw/hyperv/hyperv.h| 58 target/i386/cpu.c | 2 + target/i386/cpu.h | 7 + target/i386/kvm/hyperv-stub.c | 6 + target/i386/kvm/hyperv.c | 52 +++- target/i386/kvm/kvm.c | 76 ++- 8 files changed, 450 insertions(+), 8 deletions(-) diff --git a/docs/hyperv.txt b/docs/hyperv.txt index 0417c183a3..7abc1b2d89 100644 --- a/docs/hyperv.txt +++ b/docs/hyperv.txt @@ -225,6 +225,21 @@ default (WS2016). Note: hv-version-id-* are not enlightenments and thus don't enable Hyper-V identification when specified without any other enlightenments. +3.21. hv-syndbg +=== +Enables Hyper-V synthetic debugger interface, this is a special interface used +by Windows Kernel debugger to send the packets through, rather than sending +them via serial/network . +Whe enabled, this enlightenment provides additional communication facilities When Done +to the guest: SynDbg messages. +This new communication is used by Windows Kernel debugger rather than sending +packets via serial/network, adding significant performance boost over the other +comm channels. +This enlightenment requires a VMBus device (-device vmbus-bridge,irq=15) +and the follow enlightenments to work: +hv-relaxed,hv_time,hv-vapic,hv-vpindex,hv-synic,hv-runtime,hv-stimer + + 4. Supplementary features = diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 88c9cc1334..c86e2aa02e 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -730,3 +730,245 @@ uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast) } return HV_STATUS_INVALID_CONNECTION_ID; } + +static HvSynDbgHandler hv_syndbg_handler; +static void *hv_syndbg_context; Add a line here between field and function definition. Done +void hyperv_set_syndbg_handler(HvSynDbgHandler handler, void *context) +{ +assert(!hv_syndbg_handler); +hv_syndbg_handler = handler; +hv_syndbg_context = context; +} + +uint16_t hyperv_hcall_reset_dbg_session(uint64_t outgpa) +{ +uint16_t ret; +HvSynDbgMsg msg; +struct hyperv_reset_debug_session_output *reset_dbg_session = NULL; +hwaddr len; + +if (!hv_syndbg_handler) { +ret = HV_STATUS_INVALID_HYPERCALL_CODE; +goto cleanup; +} + +len = sizeof(*reset_dbg_session); +reset_dbg_session = cpu_physical_memory_map(outgpa, &len, 1); +if (!reset_dbg_session || len < sizeof(*reset_dbg_session)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +msg.type = HV_SYNDBG_MSG_CONNECTION_INFO; +ret = hv_syndbg_handler(hv_syndbg_context, &msg); +if (ret) { +goto cleanup; +} + +reset_dbg_session->host_ip = msg.u.connection_info.host_ip; +reset_dbg_session->host_port = msg.u.connection_info.host_port; +/* The following fields are only used as validation for KDVM */ +memset(&reset_dbg_session->host_mac, 0, + sizeof(reset_dbg_session->host_mac)); +reset_dbg_session->target_ip = msg.u.connection_info.host_ip; +reset_dbg_session->target_port = msg.u.connection_info.host_port; +memset(&reset_dbg_session->target_mac, 0, + sizeof(reset_dbg_session->target_mac)); +cleanup: +if (reset_dbg_session) { +cpu_physical_memory_unmap(reset_dbg_session, + sizeof(*reset_dbg_session), 1, len); +} + +return ret; +} + +uint16_t hyperv_hcall_retreive_dbg_data(uint64_t ingpa, uint64_t outgpa, +bool fast) +{ +uint16_t ret; +struct hyperv_retrieve_debug_data_input *debug_data_in = NULL; +struct hyperv_retrieve_debug_data_output *debug_data_out = NULL; +hwaddr in_len, out_len; +HvSynDbgMsg msg; + +if (fast || !hv_syndbg_handler) { +ret = HV_STATUS_INVALID_HYPERCALL_CODE; +goto cleanup; +} + +in_len = sizeof(*debug_data_in); +debug_data_in = cpu_physical_memory_map(ingpa, &in_len, 0); +if (!debug_data_in || in_len < sizeof(*debug_data_in)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +out_len = sizeof(*debug_data_out); +debug_data_out = cpu_physical_memory_map(outgpa, &out_len, 1); +if (!debug_data_out || out_len < sizeof(*debug_data_out)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +msg.type = HV_SYNDBG_MSG_RECV; +msg.u.recv.buf_gpa = outgpa + sizeof(*debu
Re: [PATCH v1 1/4] hyperv: SControl is optional to enable SynIc
On 16/02/2022, Emanuele Giuseppe Esposito wrote: On 04/02/2022 11:07, Jon Doron wrote: SynIc can be enabled regardless of the SControl mechanisim which can register a GSI for a given SintRoute. This behaviour can achived by setting enabling SIMP and then the guest will poll on the message slot. Once there is another message pending the host will set the message slot with the pending flag. When the guest polls from the message slot, incase the pending flag is s/incase/in case Done set it will write to the HV_X64_MSR_EOM indicating it has cleared the slow and we can try and push our message again. what do you mean by "the slow"? Just a typo to slot :) fixed Signed-off-by: Jon Doron --- hw/hyperv/hyperv.c | 233 - include/hw/hyperv/hyperv.h | 2 + 2 files changed, 153 insertions(+), 82 deletions(-) diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index cb1074f234..88c9cc1334 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -27,18 +27,70 @@ struct SynICState { CPUState *cs; -bool enabled; +bool sctl_enabled; hwaddr msg_page_addr; hwaddr event_page_addr; MemoryRegion msg_page_mr; MemoryRegion event_page_mr; struct hyperv_message_page *msg_page; struct hyperv_event_flags_page *event_page; + +QemuMutex sint_routes_mutex; +QLIST_HEAD(, HvSintRoute) sint_routes; }; #define TYPE_SYNIC "hyperv-synic" OBJECT_DECLARE_SIMPLE_TYPE(SynICState, SYNIC) +/* + * KVM has its own message producers (SynIC timers). To guarantee + * serialization with both KVM vcpu and the guest cpu, the messages are first + * staged in an intermediate area and then posted to the SynIC message page in + * the vcpu thread. + */ +typedef struct HvSintStagedMessage { +/* message content staged by hyperv_post_msg */ +struct hyperv_message msg; +/* callback + data (r/o) to complete the processing in a BH */ +HvSintMsgCb cb; +void *cb_data; +/* message posting status filled by cpu_post_msg */ +int status; +/* passing the buck: */ +enum { +/* initial state */ +HV_STAGED_MSG_FREE, +/* + * hyperv_post_msg (e.g. in main loop) grabs the staged area (FREE -> + * BUSY), copies msg, and schedules cpu_post_msg on the assigned cpu + */ +HV_STAGED_MSG_BUSY, +/* + * cpu_post_msg (vcpu thread) tries to copy staged msg to msg slot, + * notify the guest, records the status, marks the posting done (BUSY + * -> POSTED), and schedules sint_msg_bh BH + */ +HV_STAGED_MSG_POSTED, +/* + * sint_msg_bh (BH) verifies that the posting is done, runs the + * callback, and starts over (POSTED -> FREE) + */ +} state; +} HvSintStagedMessage; + +struct HvSintRoute { +uint32_t sint; +SynICState *synic; +int gsi; +EventNotifier sint_set_notifier; +EventNotifier sint_ack_notifier; + +HvSintStagedMessage *staged_msg; + +unsigned refcount; +QLIST_ENTRY(HvSintRoute) link; +}; + static bool synic_enabled; Why did you move this struct above? I think it was done purposefully to separate synic_* functions from the others below (sint_*). Done bool hyperv_is_synic_enabled(void) @@ -51,11 +103,11 @@ static SynICState *get_synic(CPUState *cs) return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); } -static void synic_update(SynICState *synic, bool enable, +static void synic_update(SynICState *synic, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { -synic->enabled = enable; +synic->sctl_enabled = sctl_enable; if (synic->msg_page_addr != msg_page_addr) { if (synic->msg_page_addr) { memory_region_del_subregion(get_system_memory(), @@ -80,7 +132,7 @@ static void synic_update(SynICState *synic, bool enable, } } -void hyperv_synic_update(CPUState *cs, bool enable, +void hyperv_synic_update(CPUState *cs, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { SynICState *synic = get_synic(cs); @@ -89,7 +141,7 @@ void hyperv_synic_update(CPUState *cs, bool enable, return; } -synic_update(synic, enable, msg_page_addr, event_page_addr); +synic_update(synic, sctl_enable, msg_page_addr, event_page_addr); } static void synic_realize(DeviceState *dev, Error **errp) @@ -110,16 +162,20 @@ static void synic_realize(DeviceState *dev, Error **errp) sizeof(*synic->event_page), &error_abort); synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr); synic->event_page = memory_region_get_ram_ptr(&synic->event_page_mr); +qemu_mutex_init(&synic->sint_routes_mutex); +QLIST_INIT(&synic->sint_routes); g_free(msgp_name); g_free(eventp_name); } + s
Re: [PATCH v1 4/4] hw: hyperv: Initial commit for Synthetic Debugging device
On 16/02/2022, Emanuele Giuseppe Esposito wrote: + +static uint16_t handle_recv_msg(HvSynDbg *syndbg, uint64_t outgpa, +uint32_t count, bool is_raw, uint32_t options, +uint64_t timeout, uint32_t *retrieved_count) +{ +uint16_t ret; +uint8_t data_buf[TARGET_PAGE_SIZE - UDP_PKT_HEADER_SIZE]; +hwaddr out_len; +void *out_data = NULL; +ssize_t recv_byte_count; + +/* TODO: Handle options and timeout */ +(void)options; +(void)timeout; + +if (!syndbg->has_data_pending) { +recv_byte_count = 0; +} else { +recv_byte_count = qemu_recv(syndbg->socket, data_buf, +MIN(sizeof(data_buf), count), MSG_WAITALL); +if (recv_byte_count == -1) { +ret = HV_STATUS_INVALID_PARAMETER; +goto cleanup; +} +} + +if (!recv_byte_count) { +*retrieved_count = 0; +ret = HV_STATUS_NO_DATA; +goto cleanup; +} + +set_pending_state(syndbg, false); + +out_len = recv_byte_count; +if (is_raw) { +out_len += UDP_PKT_HEADER_SIZE; +} +out_data = cpu_physical_memory_map(outgpa, &out_len, 1); +if (!out_data) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +if (is_raw && +!create_udp_pkt(syndbg, out_data, +recv_byte_count + UDP_PKT_HEADER_SIZE, +data_buf, recv_byte_count)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} else if (!is_raw) { +memcpy(out_data, data_buf, recv_byte_count); +} + +*retrieved_count = recv_byte_count; +if (is_raw) { +*retrieved_count += UDP_PKT_HEADER_SIZE; +} +ret = HV_STATUS_SUCCESS; +cleanup: +if (out_data) { +cpu_physical_memory_unmap(out_data, out_len, 1, out_len); +} Same nitpick as done in patch 1, I think you can use more gotos labels instead of adding if statements. Done + +return ret; +} +
Re: [PATCH v1 2/4] hyperv: Add definitions for syndbg
On 16/02/2022, Emanuele Giuseppe Esposito wrote: On 04/02/2022 11:07, Jon Doron wrote: Add all required definitions for hyperv synthetic debugger interface. Signed-off-by: Jon Doron --- include/hw/hyperv/hyperv-proto.h | 52 target/i386/kvm/hyperv-proto.h | 37 +++ 2 files changed, 89 insertions(+) diff --git a/include/hw/hyperv/hyperv-proto.h b/include/hw/hyperv/hyperv-proto.h index 21dc28aee9..94c9658eb0 100644 --- a/include/hw/hyperv/hyperv-proto.h +++ b/include/hw/hyperv/hyperv-proto.h @@ -24,12 +24,17 @@ #define HV_STATUS_INVALID_PORT_ID 17 #define HV_STATUS_INVALID_CONNECTION_ID 18 #define HV_STATUS_INSUFFICIENT_BUFFERS19 +#define HV_STATUS_NOT_ACKNOWLEDGED20 +#define HV_STATUS_NO_DATA 27 /* * Hypercall numbers */ #define HV_POST_MESSAGE 0x005c #define HV_SIGNAL_EVENT 0x005d +#define HV_POST_DEBUG_DATA0x0069 +#define HV_RETREIVE_DEBUG_DATA0x006a s/RETREIVE/RETRIEVE? Done +#define HV_RESET_DEBUG_SESSION0x006b #define HV_HYPERCALL_FAST (1u << 16) /* @@ -127,4 +132,51 @@ struct hyperv_event_flags_page { struct hyperv_event_flags slot[HV_SINT_COUNT]; }; +/* + * Kernel debugger structures + */ + +/* Options flags for hyperv_reset_debug_session */ +#define HV_DEBUG_PURGE_INCOMING_DATA0x0001 +#define HV_DEBUG_PURGE_OUTGOING_DATA0x0002 +struct hyperv_reset_debug_session_input { +uint32_t options; +} __attribute__ ((__packed__)); + +struct hyperv_reset_debug_session_output { +uint32_t host_ip; +uint32_t target_ip; +uint16_t host_port; +uint16_t target_port; +uint8_t host_mac[6]; +uint8_t target_mac[6]; +} __attribute__ ((__packed__)); + +/* Options for hyperv_post_debug_data */ +#define HV_DEBUG_POST_LOOP 0x0001 + +struct hyperv_post_debug_data_input { +uint32_t count; +uint32_t options; +/*uint8_t data[HV_HYP_PAGE_SIZE - 2 * sizeof(uint32_t)];*/ What is this comment for? It's a reference how the data really looks like. +} __attribute__ ((__packed__)); + +struct hyperv_post_debug_data_output { +uint32_t pending_count; +} __attribute__ ((__packed__)); + +/* Options for hyperv_retrieve_debug_data */ +#define HV_DEBUG_RETRIEVE_LOOP 0x0001 +#define HV_DEBUG_RETRIEVE_TEST_ACTIVITY 0x0002 + +struct hyperv_retrieve_debug_data_input { +uint32_t count; +uint32_t options; +uint64_t timeout; +} __attribute__ ((__packed__)); + +struct hyperv_retrieve_debug_data_output { +uint32_t retrieved_count; +uint32_t remaining_count; +} __attribute__ ((__packed__)); #endif diff --git a/target/i386/kvm/hyperv-proto.h b/target/i386/kvm/hyperv-proto.h index 89f81afda7..9480bcdf04 100644 --- a/target/i386/kvm/hyperv-proto.h +++ b/target/i386/kvm/hyperv-proto.h @@ -19,6 +19,9 @@ #define HV_CPUID_ENLIGHTMENT_INFO 0x4004 #define HV_CPUID_IMPLEMENT_LIMITS 0x4005 #define HV_CPUID_NESTED_FEATURES 0x400A +#define HV_CPUID_SYNDBG_VENDOR_AND_MAX_FUNCTIONS0x4080 +#define HV_CPUID_SYNDBG_INTERFACE 0x4081 +#define HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES 0x4082 #define HV_CPUID_MIN 0x4005 #define HV_CPUID_MAX 0x4000 #define HV_HYPERVISOR_PRESENT_BIT 0x8000 @@ -55,8 +58,14 @@ #define HV_GUEST_IDLE_STATE_AVAILABLE (1u << 5) #define HV_FREQUENCY_MSRS_AVAILABLE (1u << 8) #define HV_GUEST_CRASH_MSR_AVAILABLE(1u << 10) +#define HV_FEATURE_DEBUG_MSRS_AVAILABLE (1u << 11) #define HV_STIMER_DIRECT_MODE_AVAILABLE (1u << 19) +/* + * HV_CPUID_FEATURES.EBX bits + */ +#define HV_PARTITION_DEUBGGING_ALLOWED (1u << 12) s/DEUBGGING/DEBUGGING Done + /* * HV_CPUID_ENLIGHTMENT_INFO.EAX bits */ @@ -72,6 +81,11 @@ #define HV_ENLIGHTENED_VMCS_RECOMMENDED (1u << 14) #define HV_NO_NONARCH_CORESHARING (1u << 18) +/* + * HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES.EAX bits + */ +#define HV_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING(1u << 1) + /* * Basic virtualized MSRs */ @@ -130,6 +144,18 @@ #define HV_X64_MSR_STIMER3_CONFIG 0x40B6 #define HV_X64_MSR_STIMER3_COUNT0x40B7 +/* + * Hyper-V Synthetic debug options MSR + */ +#define HV_X64_MSR_SYNDBG_CONTROL 0x40F1 +#define HV_X64_MSR_SYNDBG_STATUS0x40F2 +#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x40F3 +#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x40F4 +#define HV_X64_MSR_SYNDBG_PENDING_BUFFER0x40F5 +#define HV_X64_MSR_SYNDBG_OPTIONS 0x40FF + +#define HV_X64_SYNDBG_OPTION_USE_HCALLS
Re: [PATCH v2 1/4] hyperv: SControl is optional to enable SynIc
Thanks! is there an estimate when will this patchset be merged? On Thu, Feb 24, 2022, 18:36 Emanuele Giuseppe Esposito wrote: > > > On 16/02/2022 11:24, Jon Doron wrote: > > SynIc can be enabled regardless of the SControl mechanisim which can > > register a GSI for a given SintRoute. > > > > This behaviour can achived by setting enabling SIMP and then the guest > > will poll on the message slot. > > > > Once there is another message pending the host will set the message slot > > with the pending flag. > > When the guest polls from the message slot, in case the pending flag is > > set it will write to the HV_X64_MSR_EOM indicating it has cleared the > > slot and we can try and push our message again. > > > > Signed-off-by: Jon Doron > > --- > > hw/hyperv/hyperv.c | 109 +++-- > > 1 file changed, 76 insertions(+), 33 deletions(-) > > > > diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c > > index cb1074f234..aaba6b4901 100644 > > --- a/hw/hyperv/hyperv.c > > +++ b/hw/hyperv/hyperv.c > > @@ -27,13 +27,16 @@ struct SynICState { > > > > CPUState *cs; > > > > -bool enabled; > > +bool sctl_enabled; > > hwaddr msg_page_addr; > > hwaddr event_page_addr; > > MemoryRegion msg_page_mr; > > MemoryRegion event_page_mr; > > struct hyperv_message_page *msg_page; > > struct hyperv_event_flags_page *event_page; > > + > > +QemuMutex sint_routes_mutex; > > +QLIST_HEAD(, HvSintRoute) sint_routes; > > }; > > > > #define TYPE_SYNIC "hyperv-synic" > > @@ -51,11 +54,11 @@ static SynICState *get_synic(CPUState *cs) > > return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); > > } > > > > -static void synic_update(SynICState *synic, bool enable, > > +static void synic_update(SynICState *synic, bool sctl_enable, > > hwaddr msg_page_addr, hwaddr event_page_addr) > > { > > > > -synic->enabled = enable; > > +synic->sctl_enabled = sctl_enable; > > if (synic->msg_page_addr != msg_page_addr) { > > if (synic->msg_page_addr) { > > memory_region_del_subregion(get_system_memory(), > > @@ -80,7 +83,7 @@ static void synic_update(SynICState *synic, bool > enable, > > } > > } > > > > -void hyperv_synic_update(CPUState *cs, bool enable, > > +void hyperv_synic_update(CPUState *cs, bool sctl_enable, > > hwaddr msg_page_addr, hwaddr event_page_addr) > > { > > SynICState *synic = get_synic(cs); > > @@ -89,7 +92,7 @@ void hyperv_synic_update(CPUState *cs, bool enable, > > return; > > } > > > > -synic_update(synic, enable, msg_page_addr, event_page_addr); > > +synic_update(synic, sctl_enable, msg_page_addr, event_page_addr); > > } > > > > static void synic_realize(DeviceState *dev, Error **errp) > > @@ -110,16 +113,20 @@ static void synic_realize(DeviceState *dev, Error > **errp) > > sizeof(*synic->event_page), &error_abort); > > synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr); > > synic->event_page = > memory_region_get_ram_ptr(&synic->event_page_mr); > > +qemu_mutex_init(&synic->sint_routes_mutex); > > +QLIST_INIT(&synic->sint_routes); > > > > g_free(msgp_name); > > g_free(eventp_name); > > } > > + > > static void synic_reset(DeviceState *dev) > > { > > SynICState *synic = SYNIC(dev); > > memset(synic->msg_page, 0, sizeof(*synic->msg_page)); > > memset(synic->event_page, 0, sizeof(*synic->event_page)); > > synic_update(synic, false, 0, 0); > > +assert(QLIST_EMPTY(&synic->sint_routes)); > > } > > > > static void synic_class_init(ObjectClass *klass, void *data) > > @@ -214,6 +221,7 @@ struct HvSintRoute { > > HvSintStagedMessage *staged_msg; > > > > unsigned refcount; > > +QLIST_ENTRY(HvSintRoute) link; > > }; > > > > static CPUState *hyperv_find_vcpu(uint32_t vp_index) > > @@ -259,7 +267,7 @@ static void cpu_post_msg(CPUState *cs, > run_on_cpu_data data) > > > > assert(staged_msg->state == HV_STAGED_MSG_BUSY); > > > > -if (!synic->enabled || !synic->msg_page_addr) { > > +if (!synic->msg_page_addr) { > > staged_
[PATCH v3 2/4] hyperv: Add definitions for syndbg
Add all required definitions for hyperv synthetic debugger interface. Signed-off-by: Jon Doron --- include/hw/hyperv/hyperv-proto.h | 52 target/i386/kvm/hyperv-proto.h | 37 +++ 2 files changed, 89 insertions(+) diff --git a/include/hw/hyperv/hyperv-proto.h b/include/hw/hyperv/hyperv-proto.h index 21dc28aee9..4a2297307b 100644 --- a/include/hw/hyperv/hyperv-proto.h +++ b/include/hw/hyperv/hyperv-proto.h @@ -24,12 +24,17 @@ #define HV_STATUS_INVALID_PORT_ID 17 #define HV_STATUS_INVALID_CONNECTION_ID 18 #define HV_STATUS_INSUFFICIENT_BUFFERS19 +#define HV_STATUS_NOT_ACKNOWLEDGED20 +#define HV_STATUS_NO_DATA 27 /* * Hypercall numbers */ #define HV_POST_MESSAGE 0x005c #define HV_SIGNAL_EVENT 0x005d +#define HV_POST_DEBUG_DATA0x0069 +#define HV_RETRIEVE_DEBUG_DATA0x006a +#define HV_RESET_DEBUG_SESSION0x006b #define HV_HYPERCALL_FAST (1u << 16) /* @@ -127,4 +132,51 @@ struct hyperv_event_flags_page { struct hyperv_event_flags slot[HV_SINT_COUNT]; }; +/* + * Kernel debugger structures + */ + +/* Options flags for hyperv_reset_debug_session */ +#define HV_DEBUG_PURGE_INCOMING_DATA0x0001 +#define HV_DEBUG_PURGE_OUTGOING_DATA0x0002 +struct hyperv_reset_debug_session_input { +uint32_t options; +} __attribute__ ((__packed__)); + +struct hyperv_reset_debug_session_output { +uint32_t host_ip; +uint32_t target_ip; +uint16_t host_port; +uint16_t target_port; +uint8_t host_mac[6]; +uint8_t target_mac[6]; +} __attribute__ ((__packed__)); + +/* Options for hyperv_post_debug_data */ +#define HV_DEBUG_POST_LOOP 0x0001 + +struct hyperv_post_debug_data_input { +uint32_t count; +uint32_t options; +/*uint8_t data[HV_HYP_PAGE_SIZE - 2 * sizeof(uint32_t)];*/ +} __attribute__ ((__packed__)); + +struct hyperv_post_debug_data_output { +uint32_t pending_count; +} __attribute__ ((__packed__)); + +/* Options for hyperv_retrieve_debug_data */ +#define HV_DEBUG_RETRIEVE_LOOP 0x0001 +#define HV_DEBUG_RETRIEVE_TEST_ACTIVITY 0x0002 + +struct hyperv_retrieve_debug_data_input { +uint32_t count; +uint32_t options; +uint64_t timeout; +} __attribute__ ((__packed__)); + +struct hyperv_retrieve_debug_data_output { +uint32_t retrieved_count; +uint32_t remaining_count; +} __attribute__ ((__packed__)); #endif diff --git a/target/i386/kvm/hyperv-proto.h b/target/i386/kvm/hyperv-proto.h index 89f81afda7..e40e59411c 100644 --- a/target/i386/kvm/hyperv-proto.h +++ b/target/i386/kvm/hyperv-proto.h @@ -19,6 +19,9 @@ #define HV_CPUID_ENLIGHTMENT_INFO 0x4004 #define HV_CPUID_IMPLEMENT_LIMITS 0x4005 #define HV_CPUID_NESTED_FEATURES 0x400A +#define HV_CPUID_SYNDBG_VENDOR_AND_MAX_FUNCTIONS0x4080 +#define HV_CPUID_SYNDBG_INTERFACE 0x4081 +#define HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES 0x4082 #define HV_CPUID_MIN 0x4005 #define HV_CPUID_MAX 0x4000 #define HV_HYPERVISOR_PRESENT_BIT 0x8000 @@ -55,8 +58,14 @@ #define HV_GUEST_IDLE_STATE_AVAILABLE (1u << 5) #define HV_FREQUENCY_MSRS_AVAILABLE (1u << 8) #define HV_GUEST_CRASH_MSR_AVAILABLE(1u << 10) +#define HV_FEATURE_DEBUG_MSRS_AVAILABLE (1u << 11) #define HV_STIMER_DIRECT_MODE_AVAILABLE (1u << 19) +/* + * HV_CPUID_FEATURES.EBX bits + */ +#define HV_PARTITION_DEBUGGING_ALLOWED (1u << 12) + /* * HV_CPUID_ENLIGHTMENT_INFO.EAX bits */ @@ -72,6 +81,11 @@ #define HV_ENLIGHTENED_VMCS_RECOMMENDED (1u << 14) #define HV_NO_NONARCH_CORESHARING (1u << 18) +/* + * HV_CPUID_SYNDBG_PLATFORM_CAPABILITIES.EAX bits + */ +#define HV_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING(1u << 1) + /* * Basic virtualized MSRs */ @@ -130,6 +144,18 @@ #define HV_X64_MSR_STIMER3_CONFIG 0x40B6 #define HV_X64_MSR_STIMER3_COUNT0x40B7 +/* + * Hyper-V Synthetic debug options MSR + */ +#define HV_X64_MSR_SYNDBG_CONTROL 0x40F1 +#define HV_X64_MSR_SYNDBG_STATUS0x40F2 +#define HV_X64_MSR_SYNDBG_SEND_BUFFER 0x40F3 +#define HV_X64_MSR_SYNDBG_RECV_BUFFER 0x40F4 +#define HV_X64_MSR_SYNDBG_PENDING_BUFFER0x40F5 +#define HV_X64_MSR_SYNDBG_OPTIONS 0x40FF + +#define HV_X64_SYNDBG_OPTION_USE_HCALLS BIT(2) + /* * Guest crash notification MSRs */ @@ -168,5 +194,16 @@ #define HV_STIMER_COUNT 4 +/* + * Synthetic debugger control definitions + */ +#define HV_SYNDBG_CONTROL_SEND (1u <<
[PATCH v3 4/4] hw: hyperv: Initial commit for Synthetic Debugging device
Signed-off-by: Jon Doron --- hw/hyperv/Kconfig | 5 + hw/hyperv/meson.build | 1 + hw/hyperv/syndbg.c| 402 ++ 3 files changed, 408 insertions(+) create mode 100644 hw/hyperv/syndbg.c diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig index 3fbfe41c9e..fcf65903bd 100644 --- a/hw/hyperv/Kconfig +++ b/hw/hyperv/Kconfig @@ -11,3 +11,8 @@ config VMBUS bool default y depends on HYPERV + +config SYNDBG +bool +default y +depends on VMBUS diff --git a/hw/hyperv/meson.build b/hw/hyperv/meson.build index 1367e2994f..b43f119ea5 100644 --- a/hw/hyperv/meson.build +++ b/hw/hyperv/meson.build @@ -1,3 +1,4 @@ specific_ss.add(when: 'CONFIG_HYPERV', if_true: files('hyperv.c')) specific_ss.add(when: 'CONFIG_HYPERV_TESTDEV', if_true: files('hyperv_testdev.c')) specific_ss.add(when: 'CONFIG_VMBUS', if_true: files('vmbus.c')) +specific_ss.add(when: 'CONFIG_SYNDBG', if_true: files('syndbg.c')) diff --git a/hw/hyperv/syndbg.c b/hw/hyperv/syndbg.c new file mode 100644 index 00..8816bc4082 --- /dev/null +++ b/hw/hyperv/syndbg.c @@ -0,0 +1,402 @@ +/* + * QEMU Hyper-V Synthetic Debugging device + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/ctype.h" +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qemu/sockets.h" +#include "qemu-common.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/qdev-properties.h" +#include "hw/loader.h" +#include "cpu.h" +#include "hw/hyperv/hyperv.h" +#include "hw/hyperv/vmbus-bridge.h" +#include "hw/hyperv/hyperv-proto.h" +#include "net/net.h" +#include "net/eth.h" +#include "net/checksum.h" +#include "trace.h" + +#define TYPE_HV_SYNDBG "hv-syndbg" + +typedef struct HvSynDbg { +DeviceState parent_obj; + +char *host_ip; +uint16_t host_port; +bool use_hcalls; + +uint32_t target_ip; +struct sockaddr_in servaddr; +int socket; +bool has_data_pending; +uint64_t pending_page_gpa; +} HvSynDbg; + +#define HVSYNDBG(obj) OBJECT_CHECK(HvSynDbg, (obj), TYPE_HV_SYNDBG) + +/* returns NULL unless there is exactly one HV Synth debug device */ +static HvSynDbg *hv_syndbg_find(void) +{ +/* Returns NULL unless there is exactly one hvsd device */ +return HVSYNDBG(object_resolve_path_type("", TYPE_HV_SYNDBG, NULL)); +} + +static void set_pending_state(HvSynDbg *syndbg, bool has_pending) +{ +hwaddr out_len; +void *out_data; + +syndbg->has_data_pending = has_pending; + +if (!syndbg->pending_page_gpa) { +return; +} + +out_len = 1; +out_data = cpu_physical_memory_map(syndbg->pending_page_gpa, &out_len, 1); +if (out_data) { +*(uint8_t *)out_data = !!has_pending; +cpu_physical_memory_unmap(out_data, out_len, 1, out_len); +} +} + +static bool get_udb_pkt_data(void *p, uint32_t len, uint32_t *data_ofs, + uint32_t *src_ip) +{ +uint32_t offset, curr_len = len; + +if (curr_len < sizeof(struct eth_header) || +(be16_to_cpu(PKT_GET_ETH_HDR(p)->h_proto) != ETH_P_IP)) { +return false; +} +offset = sizeof(struct eth_header); +curr_len -= sizeof(struct eth_header); + +if (curr_len < sizeof(struct ip_header) || +PKT_GET_IP_HDR(p)->ip_p != IP_PROTO_UDP) { +return false; +} +offset += PKT_GET_IP_HDR_LEN(p); +curr_len -= PKT_GET_IP_HDR_LEN(p); + +if (curr_len < sizeof(struct udp_header)) { +return false; +} + +offset += sizeof(struct udp_header); +*data_ofs = offset; +*src_ip = PKT_GET_IP_HDR(p)->ip_src; +return true; +} + +static uint16_t handle_send_msg(HvSynDbg *syndbg, uint64_t ingpa, +uint32_t count, bool is_raw, +uint32_t *pending_count) +{ +uint16_t ret; +hwaddr data_len; +void *debug_data = NULL; +uint32_t udp_data_ofs = 0; +const void *pkt_data; +int sent_count; + +data_len = count; +debug_data = cpu_physical_memory_map(ingpa, &data_len, 0); +if (!debug_data || data_len < count) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +if (is_raw && +!get_udb_pkt_data(debug_data, count, &udp_data_ofs, + &syndbg->target_ip)) { +ret = HV_STATUS_SUCCESS; +goto cleanup; +} + +pkt_data = (const void *)((uintptr_t)debug_data + udp_data_ofs); +sent_count = qemu_sendto(syndbg->socket, pkt_data, count - udp_data_ofs, +
[PATCH v3 0/4] HyperV: Synthetic Debugging device
This patchset adds support for the synthetic debugging device. HyperV supports a special transport layer for the kernel debugger when running in HyperV. This patchset add supports for this device so you could have a setup fast windows kernel debugging. At this point of time, DHCP is not implmeneted so to set this up few things need to be noted. The scenario I used to test is having 2 VMs in the same virtual network i.e a Debugger VM with the NIC: -nic tap,model=virtio,mac=02:ca:01:01:01:01,script=/etc/qemu-ifup And it's IP is going to be static 192.168.53.12 And the VM we want to debug, to which we need to have the englightments and vmbus configured: -cpu host,hv-relaxed,hv_spinlocks=0x1fff,hv_time,+vmx,invtsc,hv-vapic,hv-vpindex,hv-synic,hv-syndbg \ -device vmbus-bridge \ -device hv-syndbg,host_ip=192.168.53.12,host_port=5,use_hcalls=false \ -nic tap,model=virtio,mac=02:ca:01:01:01:02,script=/etc/qemu-ifup \ Then in the debuggee VM we would setup the kernel debugging in the following way: If the VM is older than Win8: * Copy the proper platform kdvm.dll (make sure it's called kdvm.dll even if platform is 32bit) bcdedit /set {GUID} dbgtransport kdvm.dll bcdedit /set {GUID} loadoptions host_ip="1.2.3.4",host_port="5",nodhcp bcdedit /set {GUID} debug on bcdedit /set {GUID} halbreakpoint on Win8 and late: bcdedit /dbgsettings net hostip:7.7.7.7 port:5 nodhcp This is all the setup that is required to get the synthetic debugger configured correctly. v3: Fixed review from Paolo changes from QLIST*RCU to non RCU Jon Doron (4): hyperv: SControl is optional to enable SynIc hyperv: Add definitions for syndbg hyperv: Add support to process syndbg commands hw: hyperv: Initial commit for Synthetic Debugging device docs/hyperv.txt | 15 ++ hw/hyperv/Kconfig| 5 + hw/hyperv/hyperv.c | 352 --- hw/hyperv/meson.build| 1 + hw/hyperv/syndbg.c | 402 +++ include/hw/hyperv/hyperv-proto.h | 52 include/hw/hyperv/hyperv.h | 58 + target/i386/cpu.c| 2 + target/i386/cpu.h| 7 + target/i386/kvm/hyperv-proto.h | 37 +++ target/i386/kvm/hyperv-stub.c| 6 + target/i386/kvm/hyperv.c | 52 +++- target/i386/kvm/kvm.c| 76 +- 13 files changed, 1024 insertions(+), 41 deletions(-) create mode 100644 hw/hyperv/syndbg.c -- 2.35.1
[PATCH v3 3/4] hyperv: Add support to process syndbg commands
SynDbg commands can come from two different flows: 1. Hypercalls, in this mode the data being sent is fully encapsulated network packets. 2. SynDbg specific MSRs, in this mode only the data that needs to be transfered is passed. Signed-off-by: Jon Doron --- docs/hyperv.txt | 15 +++ hw/hyperv/hyperv.c| 243 ++ include/hw/hyperv/hyperv.h| 58 target/i386/cpu.c | 2 + target/i386/cpu.h | 7 + target/i386/kvm/hyperv-stub.c | 6 + target/i386/kvm/hyperv.c | 52 +++- target/i386/kvm/kvm.c | 76 ++- 8 files changed, 451 insertions(+), 8 deletions(-) diff --git a/docs/hyperv.txt b/docs/hyperv.txt index 0417c183a3..33588a0396 100644 --- a/docs/hyperv.txt +++ b/docs/hyperv.txt @@ -225,6 +225,21 @@ default (WS2016). Note: hv-version-id-* are not enlightenments and thus don't enable Hyper-V identification when specified without any other enlightenments. +3.21. hv-syndbg +=== +Enables Hyper-V synthetic debugger interface, this is a special interface used +by Windows Kernel debugger to send the packets through, rather than sending +them via serial/network . +When enabled, this enlightenment provides additional communication facilities +to the guest: SynDbg messages. +This new communication is used by Windows Kernel debugger rather than sending +packets via serial/network, adding significant performance boost over the other +comm channels. +This enlightenment requires a VMBus device (-device vmbus-bridge,irq=15) +and the follow enlightenments to work: +hv-relaxed,hv_time,hv-vapic,hv-vpindex,hv-synic,hv-runtime,hv-stimer + + 4. Supplementary features = diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 8b832566c1..4a1b59cb9d 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -704,3 +704,246 @@ uint16_t hyperv_hcall_signal_event(uint64_t param, bool fast) } return HV_STATUS_INVALID_CONNECTION_ID; } + +static HvSynDbgHandler hv_syndbg_handler; +static void *hv_syndbg_context; + +void hyperv_set_syndbg_handler(HvSynDbgHandler handler, void *context) +{ +assert(!hv_syndbg_handler); +hv_syndbg_handler = handler; +hv_syndbg_context = context; +} + +uint16_t hyperv_hcall_reset_dbg_session(uint64_t outgpa) +{ +uint16_t ret; +HvSynDbgMsg msg; +struct hyperv_reset_debug_session_output *reset_dbg_session = NULL; +hwaddr len; + +if (!hv_syndbg_handler) { +ret = HV_STATUS_INVALID_HYPERCALL_CODE; +goto cleanup; +} + +len = sizeof(*reset_dbg_session); +reset_dbg_session = cpu_physical_memory_map(outgpa, &len, 1); +if (!reset_dbg_session || len < sizeof(*reset_dbg_session)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +msg.type = HV_SYNDBG_MSG_CONNECTION_INFO; +ret = hv_syndbg_handler(hv_syndbg_context, &msg); +if (ret) { +goto cleanup; +} + +reset_dbg_session->host_ip = msg.u.connection_info.host_ip; +reset_dbg_session->host_port = msg.u.connection_info.host_port; +/* The following fields are only used as validation for KDVM */ +memset(&reset_dbg_session->host_mac, 0, + sizeof(reset_dbg_session->host_mac)); +reset_dbg_session->target_ip = msg.u.connection_info.host_ip; +reset_dbg_session->target_port = msg.u.connection_info.host_port; +memset(&reset_dbg_session->target_mac, 0, + sizeof(reset_dbg_session->target_mac)); +cleanup: +if (reset_dbg_session) { +cpu_physical_memory_unmap(reset_dbg_session, + sizeof(*reset_dbg_session), 1, len); +} + +return ret; +} + +uint16_t hyperv_hcall_retreive_dbg_data(uint64_t ingpa, uint64_t outgpa, +bool fast) +{ +uint16_t ret; +struct hyperv_retrieve_debug_data_input *debug_data_in = NULL; +struct hyperv_retrieve_debug_data_output *debug_data_out = NULL; +hwaddr in_len, out_len; +HvSynDbgMsg msg; + +if (fast || !hv_syndbg_handler) { +ret = HV_STATUS_INVALID_HYPERCALL_CODE; +goto cleanup; +} + +in_len = sizeof(*debug_data_in); +debug_data_in = cpu_physical_memory_map(ingpa, &in_len, 0); +if (!debug_data_in || in_len < sizeof(*debug_data_in)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +out_len = sizeof(*debug_data_out); +debug_data_out = cpu_physical_memory_map(outgpa, &out_len, 1); +if (!debug_data_out || out_len < sizeof(*debug_data_out)) { +ret = HV_STATUS_INSUFFICIENT_MEMORY; +goto cleanup; +} + +msg.type = HV_SYNDBG_MSG_RECV; +msg.u.recv.buf_gpa = outgpa + sizeof(*debug_data_out); +msg.u.recv.count = TARGET_PAGE_SIZE - sizeof(*debug_data_out); +msg.u.recv.options = debug_data_in->options; +msg.u.recv.timeout
[PATCH v3 1/4] hyperv: SControl is optional to enable SynIc
SynIc can be enabled regardless of the SControl mechanisim which can register a GSI for a given SintRoute. This behaviour can achived by setting enabling SIMP and then the guest will poll on the message slot. Once there is another message pending the host will set the message slot with the pending flag. When the guest polls from the message slot, in case the pending flag is set it will write to the HV_X64_MSR_EOM indicating it has cleared the slot and we can try and push our message again. Signed-off-by: Jon Doron --- hw/hyperv/hyperv.c | 109 +++-- 1 file changed, 76 insertions(+), 33 deletions(-) diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index cb1074f234..8b832566c1 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -27,13 +27,16 @@ struct SynICState { CPUState *cs; -bool enabled; +bool sctl_enabled; hwaddr msg_page_addr; hwaddr event_page_addr; MemoryRegion msg_page_mr; MemoryRegion event_page_mr; struct hyperv_message_page *msg_page; struct hyperv_event_flags_page *event_page; + +QemuMutex sint_routes_mutex; +QLIST_HEAD(, HvSintRoute) sint_routes; }; #define TYPE_SYNIC "hyperv-synic" @@ -51,11 +54,11 @@ static SynICState *get_synic(CPUState *cs) return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); } -static void synic_update(SynICState *synic, bool enable, +static void synic_update(SynICState *synic, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { -synic->enabled = enable; +synic->sctl_enabled = sctl_enable; if (synic->msg_page_addr != msg_page_addr) { if (synic->msg_page_addr) { memory_region_del_subregion(get_system_memory(), @@ -80,7 +83,7 @@ static void synic_update(SynICState *synic, bool enable, } } -void hyperv_synic_update(CPUState *cs, bool enable, +void hyperv_synic_update(CPUState *cs, bool sctl_enable, hwaddr msg_page_addr, hwaddr event_page_addr) { SynICState *synic = get_synic(cs); @@ -89,7 +92,7 @@ void hyperv_synic_update(CPUState *cs, bool enable, return; } -synic_update(synic, enable, msg_page_addr, event_page_addr); +synic_update(synic, sctl_enable, msg_page_addr, event_page_addr); } static void synic_realize(DeviceState *dev, Error **errp) @@ -110,16 +113,20 @@ static void synic_realize(DeviceState *dev, Error **errp) sizeof(*synic->event_page), &error_abort); synic->msg_page = memory_region_get_ram_ptr(&synic->msg_page_mr); synic->event_page = memory_region_get_ram_ptr(&synic->event_page_mr); +qemu_mutex_init(&synic->sint_routes_mutex); +QLIST_INIT(&synic->sint_routes); g_free(msgp_name); g_free(eventp_name); } + static void synic_reset(DeviceState *dev) { SynICState *synic = SYNIC(dev); memset(synic->msg_page, 0, sizeof(*synic->msg_page)); memset(synic->event_page, 0, sizeof(*synic->event_page)); synic_update(synic, false, 0, 0); +assert(QLIST_EMPTY(&synic->sint_routes)); } static void synic_class_init(ObjectClass *klass, void *data) @@ -214,6 +221,7 @@ struct HvSintRoute { HvSintStagedMessage *staged_msg; unsigned refcount; +QLIST_ENTRY(HvSintRoute) link; }; static CPUState *hyperv_find_vcpu(uint32_t vp_index) @@ -259,7 +267,7 @@ static void cpu_post_msg(CPUState *cs, run_on_cpu_data data) assert(staged_msg->state == HV_STAGED_MSG_BUSY); -if (!synic->enabled || !synic->msg_page_addr) { +if (!synic->msg_page_addr) { staged_msg->status = -ENXIO; goto posted; } @@ -343,7 +351,7 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno) if (eventno > HV_EVENT_FLAGS_COUNT) { return -EINVAL; } -if (!synic->enabled || !synic->event_page_addr) { +if (!synic->sctl_enabled || !synic->event_page_addr) { return -ENXIO; } @@ -364,11 +372,12 @@ int hyperv_set_event_flag(HvSintRoute *sint_route, unsigned eventno) HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, HvSintMsgCb cb, void *cb_data) { -HvSintRoute *sint_route; -EventNotifier *ack_notifier; +HvSintRoute *sint_route = NULL; +EventNotifier *ack_notifier = NULL; int r, gsi; CPUState *cs; SynICState *synic; +bool ack_event_initialized = false; cs = hyperv_find_vcpu(vp_index); if (!cs) { @@ -381,57 +390,77 @@ HvSintRoute *hyperv_sint_route_new(uint32_t vp_index, uint32_t sint, } sint_route = g_new0(HvSintRoute, 1); -r = event_notifier_init(&sint_route->sint_set_notifier, false); -if (r) { -goto err; +if (!sint_route) { +return NULL; } +sint_route->synic =
Re: Concerns regarding e17bebd049 ("dump: Set correct vaddr for ELF dump")
Hi Stephen, Like you have said the reason is as I wrote in the commit message, without "fixing" the vaddr GDB is messing up mapping and working with the generated core file. This patch is almost 4 years old, perhaps some changes to GDB has been introduced to resolve this, I have not checked since then. As I'm no longer using this feature and have not worked and tested it in a long while, so I have no obligations to this change, but perhaps someone else might be using it... -- Jon. On 19/09/2023, Stephen Brennan wrote: Hello all, I've started working on better support and documentation around hypervisor vmcores in the Drgn debugger[1]. Of course there's quite a lot of different implementations out there, but recently I'm looking at Qemu kdump and ELF vmcores generated via dump-guest-memory, and one thing caught my eye. I generated a ELF vmcore without the paging option enabled, and without the guest note loaded, and the resulting core dump's program header looked like this: $ eu-readelf -l dumpfile2 Program Headers: Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align NOTE 0x000168 0x 0x 0x001980 0x001980 0x0 LOAD 0x001ae8 0x 0x 0x8000 0x8000 0x0 LOAD 0x80001ae8 0xfffc 0xfffc 0x04 0x04 0x0 In particular, the "VirtAddr" field for the loadable segment shows a confusing address - it appears to reuse the segment's physical address, despite the fact that there's no actual corresponding mapping. By comparison, the /proc/kcore and /proc/vmcore ELF vmcores use the VirtAddr in the program header to represent the real virtual memory mappings in use by the kernel. Debuggers can directly use these without needing to walk page tables. If there is no virtual memory mapping information available, I would have expected a placeholder value such as ... or ... to take the place of VirtAddr here so a debugger can detect the lack of virtual mappings and know that it needs to use architecture-specific details (and the vmcoreinfo) to find the page tables and accurately determine memory mappings. As it is, this program header seems to advertise to a debugger, "yes, we have the virtual memory mappings" when in fact, that's not the case. It seems that this behavior was introduced in e17bebd049 ("dump: Set correct vaddr for ELF dump")[2], a small commit I'll reproduce below. The justification seems to be that it fixes an issue reading the vmcore with GDB, but I wonder if that's not a GDB bug which should have been fixed with them? If GDB aims to support ELF kernel core dumps, presumably it should be handling physical addresses separately from virtual addresses. And if GDB doesn't aim for this, but you'd like to con it into reading your core dump, presumably the onus is on you to edit the ELF VirtAddr field to suit your needs? It should be QEMU's primary goal to produce a *correct* vmcore, not work around limitations or bugs in GDB. I'd like to propose reverting this, since it makes it impossible to interpret QEMU ELF vmcores, unless you discard all the virtual addresses in the program headers, and unconditionally do all the page table walks yourself. But I wanted to see if there was some justification for this behavior that I missed. Thanks, Stephen [1]: https://github.com/osandov/drgn [2]: https://lore.kernel.org/qemu-devel/20181225125344.4482-1-ari...@gmail.com/ --- commit e17bebd049d78f489c2cff755e2b66a0536a156e Author: Jon Doron Date: Wed Jan 9 10:22:03 2019 +0200 dump: Set correct vaddr for ELF dump vaddr needs to be equal to the paddr since the dump file represents the physical memory image. Without setting vaddr correctly, GDB would load all the different memory regions on top of each other to vaddr 0, thus making GDB showing the wrong memory data for a given address. Signed-off-by: Jon Doron Message-Id: <20190109082203.27142-1-ari...@gmail.com> Reviewed-by: Marc-André Lureau Tested-by: Marc-André Lureau Acked-by: Laszlo Ersek diff --git a/dump.c b/dump.c index ef1d8025c9..107a67165a 100644 --- a/dump.c +++ b/dump.c @@ -192,7 +192,7 @@ static void write_elf64_load(DumpState *s, MemoryMapping *memory_mapping, phdr.p_paddr = cpu_to_dump64(s, memory_mapping->phys_addr); phdr.p_filesz = cpu_to_dump64(s, filesz); phdr.p_memsz = cpu_to_dump64(s, memory_mapping->length); -phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr); +phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr) ?: phdr.p_paddr; assert(memory_mapping->length >= filesz); @@ -216,7 +216,8 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping, phdr.p_paddr = cpu_to_dump32(s, memory_mapping->phys_addr); p
[Qemu-devel] [PATCH v1] dump: Set correct vaddr for ELF dump
vaddr needs to be equal to the paddr since the dump file represents the physical memory image. Without setting vaddr correctly, GDB would load all the different memory regions on top of each other to vaddr 0, thus making GDB showing the wrong memory data for a given address. Signed-off-by: Jon Doron --- dump.c | 4 ++-- scripts/dump-guest-memory.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dump.c b/dump.c index 4ec94c5e25..bf77a119ea 100644 --- a/dump.c +++ b/dump.c @@ -192,7 +192,7 @@ static void write_elf64_load(DumpState *s, MemoryMapping *memory_mapping, phdr.p_paddr = cpu_to_dump64(s, memory_mapping->phys_addr); phdr.p_filesz = cpu_to_dump64(s, filesz); phdr.p_memsz = cpu_to_dump64(s, memory_mapping->length); -phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr); +phdr.p_vaddr = phdr.p_paddr; assert(memory_mapping->length >= filesz); @@ -216,7 +216,7 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping, phdr.p_paddr = cpu_to_dump32(s, memory_mapping->phys_addr); phdr.p_filesz = cpu_to_dump32(s, filesz); phdr.p_memsz = cpu_to_dump32(s, memory_mapping->length); -phdr.p_vaddr = cpu_to_dump32(s, memory_mapping->virt_addr); +phdr.p_vaddr = phdr.p_paddr; assert(memory_mapping->length >= filesz); diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py index 198cd0fe40..2c587cbefc 100644 --- a/scripts/dump-guest-memory.py +++ b/scripts/dump-guest-memory.py @@ -163,6 +163,7 @@ class ELF(object): phdr = get_arch_phdr(self.endianness, self.elfclass) phdr.p_type = p_type phdr.p_paddr = p_paddr +phdr.p_vaddr = p_paddr phdr.p_filesz = p_size phdr.p_memsz = p_size self.segments.append(phdr) -- 2.19.2
Re: [Qemu-devel] [PATCH v1] dump: Set correct vaddr for ELF dump
Thank you for looking into this, perhaps I could change the patch (at least in the C part not the python script) to something like: -phdr.p_vaddr = cpu_to_dumpXX(s, memory_mapping->virt_addr); +phdr.p_vaddr = cpu_to_dumpXX(s, memory_mapping->virt_addr) ? cpu_to_dumpXX(s, memory_mapping->virt_addr) : phdr.p_paddr; So in the case of paging where virt_addr is available we will use it Thanks, -- Jon. On Mon, Jan 7, 2019 at 8:04 PM Laszlo Ersek wrote: > > On 01/07/19 13:14, Marc-André Lureau wrote: > > Hi > > > > On Tue, Dec 25, 2018 at 5:52 PM Jon Doron wrote: > >> > >> vaddr needs to be equal to the paddr since the dump file represents the > >> physical memory image. > >> > >> Without setting vaddr correctly, GDB would load all the different memory > >> regions on top of each other to vaddr 0, thus making GDB showing the wrong > >> memory data for a given address. > >> > >> Signed-off-by: Jon Doron > > > > This is a non-trivial patch! (qemu-trivial, please ignore). > > > >> --- > >> dump.c | 4 ++-- > >> scripts/dump-guest-memory.py | 1 + > >> 2 files changed, 3 insertions(+), 2 deletions(-) > >> > >> diff --git a/dump.c b/dump.c > >> index 4ec94c5e25..bf77a119ea 100644 > >> --- a/dump.c > >> +++ b/dump.c > >> @@ -192,7 +192,7 @@ static void write_elf64_load(DumpState *s, > >> MemoryMapping *memory_mapping, > >> phdr.p_paddr = cpu_to_dump64(s, memory_mapping->phys_addr); > >> phdr.p_filesz = cpu_to_dump64(s, filesz); > >> phdr.p_memsz = cpu_to_dump64(s, memory_mapping->length); > >> -phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr); > >> +phdr.p_vaddr = phdr.p_paddr; > > > > This is likely breaking paging=true somehow, which sets > > memory_mapping->virt_addr to non-0. > > > > According to doc "If you want to use gdb to process the core, please > > set @paging to true." > > > > Although I am not able to (gdb) x/10bx 0xa for example on a core > > produced with paging. Not sure why, anybody could help? > > > >> assert(memory_mapping->length >= filesz); > >> > >> @@ -216,7 +216,7 @@ static void write_elf32_load(DumpState *s, > >> MemoryMapping *memory_mapping, > >> phdr.p_paddr = cpu_to_dump32(s, memory_mapping->phys_addr); > >> phdr.p_filesz = cpu_to_dump32(s, filesz); > >> phdr.p_memsz = cpu_to_dump32(s, memory_mapping->length); > >> -phdr.p_vaddr = cpu_to_dump32(s, memory_mapping->virt_addr); > >> +phdr.p_vaddr = phdr.p_paddr; > >> > >> assert(memory_mapping->length >= filesz); > >> > >> diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py > >> index 198cd0fe40..2c587cbefc 100644 > >> --- a/scripts/dump-guest-memory.py > >> +++ b/scripts/dump-guest-memory.py > >> @@ -163,6 +163,7 @@ class ELF(object): > >> phdr = get_arch_phdr(self.endianness, self.elfclass) > >> phdr.p_type = p_type > >> phdr.p_paddr = p_paddr > >> +phdr.p_vaddr = p_paddr > > > > With your proposed change though, I can dump memory with gdb... > > > >> phdr.p_filesz = p_size > >> phdr.p_memsz = p_size > >> self.segments.append(phdr) > >> -- > >> 2.19.2 > >> > >> > > > > > > I've never used paging-enabled dumps. First, because doing so requires > QEMU to trust guest memory contents (see original commit 783e9b4826b9; > or more recently/generally, the @dump-guest-memory docs in > "qapi/misc.json"). Second, because whenever I had to deal with guest > memory dumps, I always used "crash" (which needs no paging), and the > subject guests were all Linux. > > I can't comment on paging-enabled patches for dump, except that they > shouldn't regress the paging-disabled functionality. :) If the patches > satisfy that, I'm fine. > > (I *am* surprised that GDB insists on p_vaddr equaling p_paddr; after > all, in the guest, the virtual address is "memory_mapping->virt_addr". > But, I would never claim to understand most of the ELF intricacies, > and/or what GDB requires on top of those.) > > Thanks > Laszlo
[Qemu-devel] [PATCH v2] dump: Set correct vaddr for ELF dump
vaddr needs to be equal to the paddr since the dump file represents the physical memory image. Without setting vaddr correctly, GDB would load all the different memory regions on top of each other to vaddr 0, thus making GDB showing the wrong memory data for a given address. Signed-off-by: Jon Doron --- dump.c | 5 +++-- scripts/dump-guest-memory.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dump.c b/dump.c index 4ec94c5e25..7a56e254d4 100644 --- a/dump.c +++ b/dump.c @@ -192,7 +192,7 @@ static void write_elf64_load(DumpState *s, MemoryMapping *memory_mapping, phdr.p_paddr = cpu_to_dump64(s, memory_mapping->phys_addr); phdr.p_filesz = cpu_to_dump64(s, filesz); phdr.p_memsz = cpu_to_dump64(s, memory_mapping->length); -phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr); +phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr) ? : phdr.p_paddr; assert(memory_mapping->length >= filesz); @@ -216,7 +216,8 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping, phdr.p_paddr = cpu_to_dump32(s, memory_mapping->phys_addr); phdr.p_filesz = cpu_to_dump32(s, filesz); phdr.p_memsz = cpu_to_dump32(s, memory_mapping->length); -phdr.p_vaddr = cpu_to_dump32(s, memory_mapping->virt_addr); +phdr.p_vaddr = + cpu_to_dump32(s, memory_mapping->virt_addr) ? : phdr.p_paddr; assert(memory_mapping->length >= filesz); diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py index 198cd0fe40..2c587cbefc 100644 --- a/scripts/dump-guest-memory.py +++ b/scripts/dump-guest-memory.py @@ -163,6 +163,7 @@ class ELF(object): phdr = get_arch_phdr(self.endianness, self.elfclass) phdr.p_type = p_type phdr.p_paddr = p_paddr +phdr.p_vaddr = p_paddr phdr.p_filesz = p_size phdr.p_memsz = p_size self.segments.append(phdr) -- 2.19.2
[Qemu-devel] [PATCH v3] dump: Set correct vaddr for ELF dump
vaddr needs to be equal to the paddr since the dump file represents the physical memory image. Without setting vaddr correctly, GDB would load all the different memory regions on top of each other to vaddr 0, thus making GDB showing the wrong memory data for a given address. Signed-off-by: Jon Doron --- dump.c | 5 +++-- scripts/dump-guest-memory.py | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/dump.c b/dump.c index 4ec94c5e25..de7f70f099 100644 --- a/dump.c +++ b/dump.c @@ -192,7 +192,7 @@ static void write_elf64_load(DumpState *s, MemoryMapping *memory_mapping, phdr.p_paddr = cpu_to_dump64(s, memory_mapping->phys_addr); phdr.p_filesz = cpu_to_dump64(s, filesz); phdr.p_memsz = cpu_to_dump64(s, memory_mapping->length); -phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr); +phdr.p_vaddr = cpu_to_dump64(s, memory_mapping->virt_addr) ? : phdr.p_paddr; assert(memory_mapping->length >= filesz); @@ -216,7 +216,8 @@ static void write_elf32_load(DumpState *s, MemoryMapping *memory_mapping, phdr.p_paddr = cpu_to_dump32(s, memory_mapping->phys_addr); phdr.p_filesz = cpu_to_dump32(s, filesz); phdr.p_memsz = cpu_to_dump32(s, memory_mapping->length); -phdr.p_vaddr = cpu_to_dump32(s, memory_mapping->virt_addr); +phdr.p_vaddr = +cpu_to_dump32(s, memory_mapping->virt_addr) ? : phdr.p_paddr; assert(memory_mapping->length >= filesz); diff --git a/scripts/dump-guest-memory.py b/scripts/dump-guest-memory.py index 198cd0fe40..2c587cbefc 100644 --- a/scripts/dump-guest-memory.py +++ b/scripts/dump-guest-memory.py @@ -163,6 +163,7 @@ class ELF(object): phdr = get_arch_phdr(self.endianness, self.elfclass) phdr.p_type = p_type phdr.p_paddr = p_paddr +phdr.p_vaddr = p_paddr phdr.p_filesz = p_size phdr.p_memsz = p_size self.segments.append(phdr) -- 2.19.2
[PATCH v4 1/6] hyperv: expose API to determine if synic is enabled
Signed-off-by: Jon Doron --- hw/hyperv/hyperv.c | 8 include/hw/hyperv/hyperv.h | 1 + 2 files changed, 9 insertions(+) diff --git a/hw/hyperv/hyperv.c b/hw/hyperv/hyperv.c index 8ca3706f5b..ddf4f32c60 100644 --- a/hw/hyperv/hyperv.c +++ b/hw/hyperv/hyperv.c @@ -37,6 +37,13 @@ typedef struct SynICState { #define TYPE_SYNIC "hyperv-synic" #define SYNIC(obj) OBJECT_CHECK(SynICState, (obj), TYPE_SYNIC) +static bool synic_enabled; + +bool hyperv_is_synic_enabled(void) +{ +return synic_enabled; +} + static SynICState *get_synic(CPUState *cs) { return SYNIC(object_resolve_path_component(OBJECT(cs), "synic")); @@ -133,6 +140,7 @@ void hyperv_synic_add(CPUState *cs) object_property_add_child(OBJECT(cs), "synic", obj, &error_abort); object_unref(obj); object_property_set_bool(obj, true, "realized", &error_abort); +synic_enabled = true; } void hyperv_synic_reset(CPUState *cs) diff --git a/include/hw/hyperv/hyperv.h b/include/hw/hyperv/hyperv.h index 597381cb01..a63ee0003c 100644 --- a/include/hw/hyperv/hyperv.h +++ b/include/hw/hyperv/hyperv.h @@ -79,5 +79,6 @@ void hyperv_synic_add(CPUState *cs); void hyperv_synic_reset(CPUState *cs); void hyperv_synic_update(CPUState *cs, bool enable, hwaddr msg_page_addr, hwaddr event_page_addr); +bool hyperv_is_synic_enabled(void); #endif -- 2.24.1
[PATCH v4 5/6] i386: Hyper-V VMBus ACPI DSDT entry
Guest OS uses ACPI to discover VMBus presence. Add a corresponding entry to DSDT in case VMBus has been enabled. Experimentally Windows guests were found to require this entry to include two IRQ resources. They seem to never be used but they still have to be there. Make IRQ numbers user-configurable via corresponding properties; use 7 and 13 by default. Signed-off-by: Evgeny Yakovlev Signed-off-by: Roman Kagan Signed-off-by: Maciej S. Szmigiero Signed-off-by: Jon Doron --- hw/hyperv/vmbus.c| 7 ++ hw/i386/acpi-build.c | 43 include/hw/hyperv/vmbus-bridge.h | 3 +++ 3 files changed, 53 insertions(+) diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 1f5873ab60..0df7afe0ca 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2641,6 +2641,12 @@ static const VMStateDescription vmstate_vmbus_bridge = { }, }; +static Property vmbus_bridge_props[] = { +DEFINE_PROP_UINT8("irq0", VMBusBridge, irq0, 7), +DEFINE_PROP_UINT8("irq1", VMBusBridge, irq1, 13), +DEFINE_PROP_END_OF_LIST() +}; + static void vmbus_bridge_class_init(ObjectClass *klass, void *data) { DeviceClass *k = DEVICE_CLASS(klass); @@ -2651,6 +2657,7 @@ static void vmbus_bridge_class_init(ObjectClass *klass, void *data) sk->explicit_ofw_unit_address = vmbus_bridge_ofw_unit_address; set_bit(DEVICE_CATEGORY_BRIDGE, k->categories); k->vmsd = &vmstate_vmbus_bridge; +device_class_set_props(k, vmbus_bridge_props); /* override SysBusDevice's default */ k->user_creatable = true; } diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 2a7e55bae7..d235074fb8 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -50,6 +50,7 @@ #include "hw/mem/nvdimm.h" #include "sysemu/numa.h" #include "sysemu/reset.h" +#include "hw/hyperv/vmbus-bridge.h" /* Supported chipsets: */ #include "hw/southbridge/piix.h" @@ -1270,9 +1271,47 @@ static Aml *build_com_device_aml(uint8_t uid) return dev; } +static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) +{ +Aml *dev; +Aml *method; +Aml *crs; + +dev = aml_device("VMBS"); +aml_append(dev, aml_name_decl("STA", aml_int(0xF))); +aml_append(dev, aml_name_decl("_HID", aml_string("VMBus"))); +aml_append(dev, aml_name_decl("_UID", aml_int(0x0))); +aml_append(dev, aml_name_decl("_DDN", aml_string("VMBUS"))); + +method = aml_method("_DIS", 0, AML_NOTSERIALIZED); +aml_append(method, aml_store(aml_and(aml_name("STA"), aml_int(0xD), NULL), + aml_name("STA"))); +aml_append(dev, method); + +method = aml_method("_PS0", 0, AML_NOTSERIALIZED); +aml_append(method, aml_store(aml_or(aml_name("STA"), aml_int(0xF), NULL), + aml_name("STA"))); +aml_append(dev, method); + +method = aml_method("_STA", 0, AML_NOTSERIALIZED); +aml_append(method, aml_return(aml_name("STA"))); +aml_append(dev, method); + +aml_append(dev, aml_name_decl("_PS3", aml_int(0x0))); + +crs = aml_resource_template(); +aml_append(crs, aml_irq_no_flags(vmbus_bridge->irq0)); +/* FIXME: newer HyperV gets by with only one IRQ */ +aml_append(crs, aml_irq_no_flags(vmbus_bridge->irq1)); +aml_append(dev, aml_name_decl("_CRS", crs)); + +return dev; +} + static void build_isa_devices_aml(Aml *table) { ISADevice *fdc = pc_find_fdc0(); +VMBusBridge *vmbus_bridge = vmbus_bridge_find(); bool ambiguous; Aml *scope = aml_scope("_SB.PCI0.ISA"); @@ -1296,6 +1335,10 @@ static void build_isa_devices_aml(Aml *table) build_acpi_ipmi_devices(scope, BUS(obj), "\\_SB.PCI0.ISA"); } +if (vmbus_bridge) { +aml_append(scope, build_vmbus_device_aml(vmbus_bridge)); +} + aml_append(table, scope); } diff --git a/include/hw/hyperv/vmbus-bridge.h b/include/hw/hyperv/vmbus-bridge.h index 9cc8f780de..c0a06d832c 100644 --- a/include/hw/hyperv/vmbus-bridge.h +++ b/include/hw/hyperv/vmbus-bridge.h @@ -19,6 +19,9 @@ typedef struct VMBus VMBus; typedef struct VMBusBridge { SysBusDevice parent_obj; +uint8_t irq0; +uint8_t irq1; + VMBus *bus; } VMBusBridge; -- 2.24.1
[PATCH v4 0/6] hyperv: VMBus implementation
This is a rebase of the old patchset from Roman for HyperV VMBus implementation. How to use: -device vmbus-bridge Later on new paravirtualized devices can be implemented on top of it (Network/SCSI/etc.) VMBus is a collection of technologies. At its lowest layer, it's a message passing and signaling mechanism, allowing efficient passing of messages to and from guest VMs. A layer higher, it's a mechanism for defining channels of communication, where each channel is tagged with a type (which implies a protocol) and a instance ID. A layer higher than that, it's a bus driver, serving as the basis of device enumeration within a VM, where a channel can optionally be exposed as a paravirtual device. When a server-side (paravirtual back-end) component wishes to offer a channel to a guest VM, it does so by specifying a channel type, a mode, and an instance ID. VMBus then exposes this in the guest. More information about VMBus can be found in the file vmbuskernelmodeclientlibapi.h in Microsoft's WDK. v4: Decided to ditch the patch that envolves handling of EOM as there is still a discussion going on with it in the KVM mailing list. v3: Fixed an error asan v2: Rebased on top of latest patchset from Roman and Maciej Jon Doron (6): hyperv: expose API to determine if synic is enabled vmbus: add vmbus protocol definitions vmbus: vmbus implementation i386:pc: whitelist dynamic vmbus-bridge i386: Hyper-V VMBus ACPI DSDT entry vmbus: add infrastructure to save/load vmbus requests Makefile.objs|1 + hw/hyperv/Kconfig|5 + hw/hyperv/Makefile.objs |1 + hw/hyperv/hyperv.c |8 + hw/hyperv/trace-events | 18 + hw/hyperv/vmbus.c| 2778 ++ hw/i386/acpi-build.c | 43 + hw/i386/pc_piix.c|2 + hw/i386/pc_q35.c |2 + include/hw/hyperv/hyperv.h |1 + include/hw/hyperv/vmbus-bridge.h | 35 + include/hw/hyperv/vmbus-proto.h | 222 +++ include/hw/hyperv/vmbus.h| 230 +++ 13 files changed, 3346 insertions(+) create mode 100644 hw/hyperv/trace-events create mode 100644 hw/hyperv/vmbus.c create mode 100644 include/hw/hyperv/vmbus-bridge.h create mode 100644 include/hw/hyperv/vmbus-proto.h create mode 100644 include/hw/hyperv/vmbus.h -- 2.24.1
[PATCH v4 4/6] i386:pc: whitelist dynamic vmbus-bridge
As vmbus-bridge is derived from sysbus device, it has to be whitelisted to be allowed to be created with -device. Signed-off-by: Roman Kagan Signed-off-by: Maciej S. Szmigiero Signed-off-by: Jon Doron --- hw/i386/pc_piix.c | 2 ++ hw/i386/pc_q35.c | 2 ++ 2 files changed, 4 insertions(+) diff --git a/hw/i386/pc_piix.c b/hw/i386/pc_piix.c index 9cceae3e2c..6daa0770fa 100644 --- a/hw/i386/pc_piix.c +++ b/hw/i386/pc_piix.c @@ -60,6 +60,7 @@ #include "migration/global_state.h" #include "migration/misc.h" #include "sysemu/numa.h" +#include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" #define MAX_IDE_BUS 2 @@ -417,6 +418,7 @@ static void pc_i440fx_machine_options(MachineClass *m) m->default_machine_opts = "firmware=bios-256k.bin"; m->default_display = "std"; machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); +machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); } static void pc_i440fx_5_0_machine_options(MachineClass *m) diff --git a/hw/i386/pc_q35.c b/hw/i386/pc_q35.c index d37c425e22..faaa39ced2 100644 --- a/hw/i386/pc_q35.c +++ b/hw/i386/pc_q35.c @@ -53,6 +53,7 @@ #include "qapi/error.h" #include "qemu/error-report.h" #include "sysemu/numa.h" +#include "hw/hyperv/vmbus-bridge.h" #include "hw/mem/nvdimm.h" /* ICH9 AHCI has 6 ports */ @@ -346,6 +347,7 @@ static void pc_q35_machine_options(MachineClass *m) machine_class_allow_dynamic_sysbus_dev(m, TYPE_AMD_IOMMU_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_INTEL_IOMMU_DEVICE); machine_class_allow_dynamic_sysbus_dev(m, TYPE_RAMFB_DEVICE); +machine_class_allow_dynamic_sysbus_dev(m, TYPE_VMBUS_BRIDGE); m->max_cpus = 288; } -- 2.24.1
[PATCH v4 3/6] vmbus: vmbus implementation
Add the VMBus infrastructure -- bus, devices, root bridge, vmbus state machine, vmbus channel interactions, etc. VMBus is a collection of technologies. At its lowest layer, it's a message passing and signaling mechanism, allowing efficient passing of messages to and from guest VMs. A layer higher, it's a mechanism for defining channels of communication, where each channel is tagged with a type (which implies a protocol) and a instance ID. A layer higher than that, it's a bus driver, serving as the basis of device enumeration within a VM, where a channel can optionally be exposed as a paravirtual device. When a server-side (paravirtual back-end) component wishes to offer a channel to a guest VM, it does so by specifying a channel type, a mode, and an instance ID. VMBus then exposes this in the guest. More information about VMBus can be found in the file vmbuskernelmodeclientlibapi.h in Microsoft's WDK. TODO: - split into smaller palatable pieces - more comments - check and handle corner cases Kudos to Evgeny Yakovlev (formerly eyakov...@virtuozzo.com) and Andrey Smetatin (formerly asmeta...@virtuozzo.com) for research and prototyping. Signed-off-by: Roman Kagan Signed-off-by: Maciej S. Szmigiero Signed-off-by: Jon Doron --- Makefile.objs|1 + hw/hyperv/Kconfig|5 + hw/hyperv/Makefile.objs |1 + hw/hyperv/trace-events | 18 + hw/hyperv/vmbus.c| 2672 ++ include/hw/hyperv/vmbus-bridge.h | 32 + include/hw/hyperv/vmbus.h| 227 +++ 7 files changed, 2956 insertions(+) create mode 100644 hw/hyperv/trace-events create mode 100644 hw/hyperv/vmbus.c create mode 100644 include/hw/hyperv/vmbus-bridge.h create mode 100644 include/hw/hyperv/vmbus.h diff --git a/Makefile.objs b/Makefile.objs index a7c967633a..1ef80ce58f 100644 --- a/Makefile.objs +++ b/Makefile.objs @@ -151,6 +151,7 @@ trace-events-subdirs += hw/block/dataplane trace-events-subdirs += hw/char trace-events-subdirs += hw/dma trace-events-subdirs += hw/hppa +trace-events-subdirs += hw/hyperv trace-events-subdirs += hw/i2c trace-events-subdirs += hw/i386 trace-events-subdirs += hw/i386/xen diff --git a/hw/hyperv/Kconfig b/hw/hyperv/Kconfig index a1fa8ff9be..3fbfe41c9e 100644 --- a/hw/hyperv/Kconfig +++ b/hw/hyperv/Kconfig @@ -6,3 +6,8 @@ config HYPERV_TESTDEV bool default y if TEST_DEVICES depends on HYPERV + +config VMBUS +bool +default y +depends on HYPERV diff --git a/hw/hyperv/Makefile.objs b/hw/hyperv/Makefile.objs index edaca2f763..5b614e040c 100644 --- a/hw/hyperv/Makefile.objs +++ b/hw/hyperv/Makefile.objs @@ -1,2 +1,3 @@ obj-y += hyperv.o obj-$(CONFIG_HYPERV_TESTDEV) += hyperv_testdev.o +obj-$(CONFIG_VMBUS) += vmbus.o diff --git a/hw/hyperv/trace-events b/hw/hyperv/trace-events new file mode 100644 index 00..ba5bd62d61 --- /dev/null +++ b/hw/hyperv/trace-events @@ -0,0 +1,18 @@ +# vmbus +vmbus_recv_message(uint32_t type, uint32_t size) "type %d size %d" +vmbus_signal_event(void) "" +vmbus_channel_notify_guest(uint32_t chan_id) "channel #%d" +vmbus_post_msg(uint32_t type, uint32_t size) "type %d size %d" +vmbus_msg_cb(int status) "message status %d" +vmbus_process_incoming_message(uint32_t message_type) "type %d" +vmbus_initiate_contact(uint16_t major, uint16_t minor, uint32_t vcpu, uint64_t monitor_page1, uint64_t monitor_page2, uint64_t interrupt_page) "version %d.%d target vp %d mon pages 0x%"PRIx64",0x%"PRIx64" int page 0x%"PRIx64 +vmbus_send_offer(uint32_t chan_id, void *dev) "channel #%d dev %p" +vmbus_terminate_offers(void) "" +vmbus_gpadl_header(uint32_t gpadl_id, uint16_t num_gfns) "gpadl #%d gfns %d" +vmbus_gpadl_body(uint32_t gpadl_id) "gpadl #%d" +vmbus_gpadl_created(uint32_t gpadl_id) "gpadl #%d" +vmbus_gpadl_teardown(uint32_t gpadl_id) "gpadl #%d" +vmbus_gpadl_torndown(uint32_t gpadl_id) "gpadl #%d" +vmbus_open_channel(uint32_t chan_id, uint32_t gpadl_id, uint32_t target_vp) "channel #%d gpadl #%d target vp %d" +vmbus_channel_open(uint32_t chan_id, uint32_t status) "channel #%d status %d" +vmbus_close_channel(uint32_t chan_id) "channel #%d" diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c new file mode 100644 index 00..1f5873ab60 --- /dev/null +++ b/hw/hyperv/vmbus.c @@ -0,0 +1,2672 @@ +/* + * QEMU Hyper-V VMBus + * + * Copyright (c) 2017-2018 Virtuozzo International GmbH. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qemu/error-report.h" +#include "qemu/main-loop.h" +#include "qapi/error.h" +#include "migration/vmstate.h" +#include "hw/qdev-prope
[PATCH v4 2/6] vmbus: add vmbus protocol definitions
Add a header with data structures and constants used in Hyper-V VMBus hypervisor <-> guest interactions. Based on the respective stuff from Linux kernel. Signed-off-by: Roman Kagan Signed-off-by: Maciej S. Szmigiero Signed-off-by: Jon Doron --- include/hw/hyperv/vmbus-proto.h | 222 1 file changed, 222 insertions(+) create mode 100644 include/hw/hyperv/vmbus-proto.h diff --git a/include/hw/hyperv/vmbus-proto.h b/include/hw/hyperv/vmbus-proto.h new file mode 100644 index 00..4628d3b323 --- /dev/null +++ b/include/hw/hyperv/vmbus-proto.h @@ -0,0 +1,222 @@ +/* + * QEMU Hyper-V VMBus support + * + * Copyright (c) 2017-2018 Virtuozzo International GmbH. + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#ifndef HW_HYPERV_VMBUS_PROTO_H +#define HW_HYPERV_VMBUS_PROTO_H + +#define VMBUS_VERSION_WS2008((0 << 16) | (13)) +#define VMBUS_VERSION_WIN7 ((1 << 16) | (1)) +#define VMBUS_VERSION_WIN8 ((2 << 16) | (4)) +#define VMBUS_VERSION_WIN8_1((3 << 16) | (0)) +#define VMBUS_VERSION_WIN10 ((4 << 16) | (0)) +#define VMBUS_VERSION_INVAL -1 +#define VMBUS_VERSION_CURRENT VMBUS_VERSION_WIN10 + +#define VMBUS_MESSAGE_CONNECTION_ID 1 +#define VMBUS_EVENT_CONNECTION_ID 2 +#define VMBUS_MONITOR_CONNECTION_ID 3 +#define VMBUS_SINT 2 + +#define VMBUS_MSG_INVALID 0 +#define VMBUS_MSG_OFFERCHANNEL 1 +#define VMBUS_MSG_RESCIND_CHANNELOFFER 2 +#define VMBUS_MSG_REQUESTOFFERS 3 +#define VMBUS_MSG_ALLOFFERS_DELIVERED 4 +#define VMBUS_MSG_OPENCHANNEL 5 +#define VMBUS_MSG_OPENCHANNEL_RESULT6 +#define VMBUS_MSG_CLOSECHANNEL 7 +#define VMBUS_MSG_GPADL_HEADER 8 +#define VMBUS_MSG_GPADL_BODY9 +#define VMBUS_MSG_GPADL_CREATED 10 +#define VMBUS_MSG_GPADL_TEARDOWN11 +#define VMBUS_MSG_GPADL_TORNDOWN12 +#define VMBUS_MSG_RELID_RELEASED13 +#define VMBUS_MSG_INITIATE_CONTACT 14 +#define VMBUS_MSG_VERSION_RESPONSE 15 +#define VMBUS_MSG_UNLOAD16 +#define VMBUS_MSG_UNLOAD_RESPONSE 17 +#define VMBUS_MSG_COUNT 18 + +#define VMBUS_MESSAGE_SIZE_ALIGNsizeof(uint64_t) + +#define VMBUS_PACKET_INVALID0x0 +#define VMBUS_PACKET_SYNCH 0x1 +#define VMBUS_PACKET_ADD_XFER_PAGESET 0x2 +#define VMBUS_PACKET_RM_XFER_PAGESET0x3 +#define VMBUS_PACKET_ESTABLISH_GPADL0x4 +#define VMBUS_PACKET_TEARDOWN_GPADL 0x5 +#define VMBUS_PACKET_DATA_INBAND0x6 +#define VMBUS_PACKET_DATA_USING_XFER_PAGES 0x7 +#define VMBUS_PACKET_DATA_USING_GPADL 0x8 +#define VMBUS_PACKET_DATA_USING_GPA_DIRECT 0x9 +#define VMBUS_PACKET_CANCEL_REQUEST 0xa +#define VMBUS_PACKET_COMP 0xb +#define VMBUS_PACKET_DATA_USING_ADDITIONAL_PKT 0xc +#define VMBUS_PACKET_ADDITIONAL_DATA0xd + +#define VMBUS_CHANNEL_USER_DATA_SIZE120 + +#define VMBUS_OFFER_MONITOR_ALLOCATED 0x1 +#define VMBUS_OFFER_INTERRUPT_DEDICATED 0x1 + +#define VMBUS_RING_BUFFER_FEAT_PENDING_SZ (1ul << 0) + +#define VMBUS_CHANNEL_ENUMERATE_DEVICE_INTERFACE 0x1 +#define VMBUS_CHANNEL_SERVER_SUPPORTS_TRANSFER_PAGES 0x2 +#define VMBUS_CHANNEL_SERVER_SUPPORTS_GPADLS 0x4 +#define VMBUS_CHANNEL_NAMED_PIPE_MODE 0x10 +#define VMBUS_CHANNEL_LOOPBACK_OFFER 0x100 +#define VMBUS_CHANNEL_PARENT_OFFER0x200 +#define VMBUS_CHANNEL_REQUEST_MONITORED_NOTIFICATION 0x400 +#define VMBUS_CHANNEL_TLNPI_PROVIDER_OFFER0x2000 + +#define VMBUS_PACKET_FLAG_REQUEST_COMPLETION1 + +typedef struct vmbus_message_header { +uint32_t message_type; +uint32_t _padding; +} vmbus_message_header; + +typedef struct vmbus_message_initiate_contact { +vmbus_message_header header; +uint32_t version_requested; +uint32_t target_vcpu; +uint64_t interrupt_page; +uint64_t monitor_page1; +uint64_t monitor_page2; +} vmbus_message_initiate_contact; + +typedef struct vmbus_message_version_response { +vmbus_message_header header; +uint8_t version_supported; +uint8_t status; +} vmbus_message_version_response; + +typedef struct vmbus_message_offer_channel { +vmbus_message_header header; +uint8_t type_uuid[16]; +uint8_t instance_uuid[16]; +uint64_t _reserved1; +uint64_t _reserved2; +uint16_t channel_flags; +uint16_t mmio_size_mb; +uint8_t user_data[VMBUS_CHANNEL_USER_DATA_SIZE]; +uint16_t sub_channel_index; +uint16_t _reserved3; +uint32_t child_relid; +uint8_t monitor_id;
[PATCH v4 6/6] vmbus: add infrastructure to save/load vmbus requests
This can be allow to include controller-specific data while saving/loading in-flight scsi requests of the vmbus scsi controller. Signed-off-by: Roman Kagan Signed-off-by: Maciej S. Szmigiero Signed-off-by: Jon Doron --- hw/hyperv/vmbus.c | 99 +++ include/hw/hyperv/vmbus.h | 3 ++ 2 files changed, 102 insertions(+) diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index 0df7afe0ca..ab72a59a4a 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -1272,6 +1272,105 @@ void vmbus_free_req(void *req) g_free(req); } +static const VMStateDescription vmstate_sgent = { +.name = "vmbus/sgentry", +.version_id = 0, +.minimum_version_id = 0, +.fields = (VMStateField[]) { +VMSTATE_UINT64(base, ScatterGatherEntry), +VMSTATE_UINT64(len, ScatterGatherEntry), +VMSTATE_END_OF_LIST() +} +}; + +typedef struct VMBusChanReqSave { +uint16_t chan_idx; +uint16_t pkt_type; +uint32_t msglen; +void *msg; +uint64_t transaction_id; +bool need_comp; +uint32_t num; +ScatterGatherEntry *sgl; +} VMBusChanReqSave; + +static const VMStateDescription vmstate_vmbus_chan_req = { +.name = "vmbus/vmbus_chan_req", +.version_id = 0, +.minimum_version_id = 0, +.fields = (VMStateField[]) { +VMSTATE_UINT16(chan_idx, VMBusChanReqSave), +VMSTATE_UINT16(pkt_type, VMBusChanReqSave), +VMSTATE_UINT32(msglen, VMBusChanReqSave), +VMSTATE_VBUFFER_ALLOC_UINT32(msg, VMBusChanReqSave, 0, NULL, msglen), +VMSTATE_UINT64(transaction_id, VMBusChanReqSave), +VMSTATE_BOOL(need_comp, VMBusChanReqSave), +VMSTATE_UINT32(num, VMBusChanReqSave), +VMSTATE_STRUCT_VARRAY_POINTER_UINT32(sgl, VMBusChanReqSave, num, + vmstate_sgent, ScatterGatherEntry), +VMSTATE_END_OF_LIST() +} +}; + +void vmbus_save_req(QEMUFile *f, VMBusChanReq *req) +{ +VMBusChanReqSave req_save; + +req_save.chan_idx = req->chan->subchan_idx; +req_save.pkt_type = req->pkt_type; +req_save.msglen = req->msglen; +req_save.msg = req->msg; +req_save.transaction_id = req->transaction_id; +req_save.need_comp = req->need_comp; +req_save.num = req->sgl.nsg; +req_save.sgl = g_memdup(req->sgl.sg, +req_save.num * sizeof(ScatterGatherEntry)); + +vmstate_save_state(f, &vmstate_vmbus_chan_req, &req_save, NULL); + +g_free(req_save.sgl); +} + +void *vmbus_load_req(QEMUFile *f, VMBusDevice *dev, uint32_t size) +{ +VMBusChanReqSave req_save; +VMBusChanReq *req = NULL; +VMBusChannel *chan = NULL; +uint32_t i; + +vmstate_load_state(f, &vmstate_vmbus_chan_req, &req_save, 0); + +if (req_save.chan_idx >= dev->num_channels) { +error_report("%s: %u(chan_idx) > %u(num_channels)", __func__, + req_save.chan_idx, dev->num_channels); +goto out; +} +chan = &dev->channels[req_save.chan_idx]; + +if (vmbus_channel_reserve(chan, 0, req_save.msglen)) { +goto out; +} + +req = vmbus_alloc_req(chan, size, req_save.pkt_type, req_save.msglen, + req_save.transaction_id, req_save.need_comp); +if (req_save.msglen) { +memcpy(req->msg, req_save.msg, req_save.msglen); +} + +for (i = 0; i < req_save.num; i++) { +qemu_sglist_add(&req->sgl, req_save.sgl[i].base, req_save.sgl[i].len); +} + +out: +if (req_save.msglen) { +g_free(req_save.msg); +} +if (req_save.num) { +g_free(req_save.sgl); +} +return req; +} + static void channel_event_cb(EventNotifier *e) { VMBusChannel *chan = container_of(e, VMBusChannel, notifier); diff --git a/include/hw/hyperv/vmbus.h b/include/hw/hyperv/vmbus.h index 63a5b807b6..9219f34d6b 100644 --- a/include/hw/hyperv/vmbus.h +++ b/include/hw/hyperv/vmbus.h @@ -224,4 +224,7 @@ int vmbus_map_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *iov, void vmbus_unmap_sgl(VMBusChanReq *req, DMADirection dir, struct iovec *iov, unsigned iov_cnt, size_t accessed); +void vmbus_save_req(QEMUFile *f, VMBusChanReq *req); +void *vmbus_load_req(QEMUFile *f, VMBusDevice *dev, uint32_t size); + #endif -- 2.24.1
Re: [PATCH v3 2/3] i386: acpi: vmbus: Add _ADR definition
On 23/06/2020, Igor Mammedov wrote: On Thu, 18 Jun 2020 06:00:26 +0300 Jon Doron wrote: It seems like latest HyperV sets _ADR to 0 in the ACPI for the VMBS that's a hardly a good reason to add this. To me looks like a pointless addition, _ADR mostly is used when device resides on a bus with standard ennumeration algorithm (i.e. PCI, ...). Value is also wrong for the bus it's placed currently, and with the next patch it won't make a sense altogether. Pls, drop this patch unless Windows refuses to work without it. Windows seems to handle fine without this. Signed-off-by: Jon Doron --- hw/i386/acpi-build.c | 1 + 1 file changed, 1 insertion(+) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 91af0d2d0d..6d9df38e31 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1061,6 +1061,7 @@ static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) dev = aml_device("VMBS"); aml_append(dev, aml_name_decl("STA", aml_int(0xF))); +aml_append(dev, aml_name_decl("_ADR", aml_int(0x0))); aml_append(dev, aml_name_decl("_HID", aml_string("VMBus"))); aml_append(dev, aml_name_decl("_UID", aml_int(0x0))); aml_append(dev, aml_name_decl("_DDN", aml_string("VMBUS")));
Re: [PATCH v3 3/3] acpi: i386: Move VMBus DSDT entry to SB
On 23/06/2020, Igor Mammedov wrote: On Thu, 18 Jun 2020 06:00:27 +0300 Jon Doron wrote: Signed-off-by: Jon Doron --- hw/i386/acpi-build.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 6d9df38e31..38be9e5a58 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1092,7 +1092,6 @@ static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) static void build_isa_devices_aml(Aml *table) { ISADevice *fdc = pc_find_fdc0(); -VMBusBridge *vmbus_bridge = vmbus_bridge_find(); bool ambiguous; Aml *scope = aml_scope("_SB.PCI0.ISA"); @@ -1113,10 +1112,6 @@ static void build_isa_devices_aml(Aml *table) isa_build_aml(ISA_BUS(obj), scope); } -if (vmbus_bridge) { -aml_append(scope, build_vmbus_device_aml(vmbus_bridge)); -} - aml_append(table, scope); } @@ -1661,6 +1656,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, PCIBus *bus = NULL; TPMIf *tpm = tpm_find(); int i; +VMBusBridge *vmbus_bridge = vmbus_bridge_find(); dsdt = init_aml_allocator(); @@ -1675,6 +1671,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_ADR", aml_int(0))); aml_append(dev, aml_name_decl("_UID", aml_int(1))); aml_append(sb_scope, dev); +if (vmbus_bridge) { +aml_append(sb_scope, build_vmbus_device_aml(vmbus_bridge)); +} aml_append(dsdt, sb_scope); build_hpet_aml(dsdt); @@ -1692,6 +1691,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_UID", aml_int(1))); aml_append(dev, build_q35_osc_method()); aml_append(sb_scope, dev); +if (vmbus_bridge) { +aml_append(sb_scope, build_vmbus_device_aml(vmbus_bridge)); +} aml_append(dsdt, sb_scope); build_hpet_aml(dsdt); why are you duplicating instead of putting one if () block after if (misc->is_piix4) { } else { } ? Well it seems like Windows is very "picky" about where you declare the VMBS not sure why if i had moved it to the suggested location as such if (misc->is_piix4) { } else { } if (vmbus_bridge) { aml_append(sb_scope, build_vmbus_device_aml(vmbus_bridge)); aml_append(dsdt, sb_scope); } Windows would BSOD right away with ACPI error. Same goes for declaring it before PCI0 device... -- Jon.
Re: [PATCH v3 3/3] acpi: i386: Move VMBus DSDT entry to SB
On 25/06/2020, Jon Doron wrote: On 23/06/2020, Igor Mammedov wrote: On Thu, 18 Jun 2020 06:00:27 +0300 Jon Doron wrote: Signed-off-by: Jon Doron --- hw/i386/acpi-build.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 6d9df38e31..38be9e5a58 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1092,7 +1092,6 @@ static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) static void build_isa_devices_aml(Aml *table) { ISADevice *fdc = pc_find_fdc0(); -VMBusBridge *vmbus_bridge = vmbus_bridge_find(); bool ambiguous; Aml *scope = aml_scope("_SB.PCI0.ISA"); @@ -1113,10 +1112,6 @@ static void build_isa_devices_aml(Aml *table) isa_build_aml(ISA_BUS(obj), scope); } -if (vmbus_bridge) { -aml_append(scope, build_vmbus_device_aml(vmbus_bridge)); -} - aml_append(table, scope); } @@ -1661,6 +1656,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, PCIBus *bus = NULL; TPMIf *tpm = tpm_find(); int i; +VMBusBridge *vmbus_bridge = vmbus_bridge_find(); dsdt = init_aml_allocator(); @@ -1675,6 +1671,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_ADR", aml_int(0))); aml_append(dev, aml_name_decl("_UID", aml_int(1))); aml_append(sb_scope, dev); +if (vmbus_bridge) { +aml_append(sb_scope, build_vmbus_device_aml(vmbus_bridge)); +} aml_append(dsdt, sb_scope); build_hpet_aml(dsdt); @@ -1692,6 +1691,9 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, aml_append(dev, aml_name_decl("_UID", aml_int(1))); aml_append(dev, build_q35_osc_method()); aml_append(sb_scope, dev); +if (vmbus_bridge) { +aml_append(sb_scope, build_vmbus_device_aml(vmbus_bridge)); +} aml_append(dsdt, sb_scope); build_hpet_aml(dsdt); why are you duplicating instead of putting one if () block after if (misc->is_piix4) { } else { } ? Well it seems like Windows is very "picky" about where you declare the VMBS not sure why if i had moved it to the suggested location as such if (misc->is_piix4) { } else { } if (vmbus_bridge) { aml_append(sb_scope, build_vmbus_device_aml(vmbus_bridge)); aml_append(dsdt, sb_scope); } Windows would BSOD right away with ACPI error. Same goes for declaring it before PCI0 device... -- Jon. Never mind did a silly mistake ill send a new set shortly
[PATCH v4 0/2] hyperv: vmbus: ACPI various corrections
After doing further tests and looking at the latest HyperV ACPI DSDT. Do minor fix to our VMBus ACPI entry. v4: * Removed the patch which adds _ADR definition to the VMBus * Correct the change which moves the VMBus under the SB v3: Removed accidental change for the dct submodule head v2: Renamed irq0 to irq now that there is a single IRQ required Jon Doron (2): hyperv: vmbus: Remove the 2nd IRQ acpi: i386: Move VMBus DSDT entry to SB hw/hyperv/vmbus.c| 3 +-- hw/i386/acpi-build.c | 16 include/hw/hyperv/vmbus-bridge.h | 3 +-- 3 files changed, 10 insertions(+), 12 deletions(-) -- 2.24.1
[PATCH v4 1/2] hyperv: vmbus: Remove the 2nd IRQ
It seems like Windows does not really require 2 IRQs to have a functioning VMBus. Signed-off-by: Jon Doron Reviewed-by: Igor Mammedov --- hw/hyperv/vmbus.c| 3 +-- hw/i386/acpi-build.c | 4 +--- include/hw/hyperv/vmbus-bridge.h | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/hw/hyperv/vmbus.c b/hw/hyperv/vmbus.c index f371240176..a8bcb41026 100644 --- a/hw/hyperv/vmbus.c +++ b/hw/hyperv/vmbus.c @@ -2741,8 +2741,7 @@ static const VMStateDescription vmstate_vmbus_bridge = { }; static Property vmbus_bridge_props[] = { -DEFINE_PROP_UINT8("irq0", VMBusBridge, irq0, 7), -DEFINE_PROP_UINT8("irq1", VMBusBridge, irq1, 13), +DEFINE_PROP_UINT8("irq", VMBusBridge, irq, 7), DEFINE_PROP_END_OF_LIST() }; diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 900f786d08..91af0d2d0d 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1082,9 +1082,7 @@ static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) aml_append(dev, aml_name_decl("_PS3", aml_int(0x0))); crs = aml_resource_template(); -aml_append(crs, aml_irq_no_flags(vmbus_bridge->irq0)); -/* FIXME: newer HyperV gets by with only one IRQ */ -aml_append(crs, aml_irq_no_flags(vmbus_bridge->irq1)); +aml_append(crs, aml_irq_no_flags(vmbus_bridge->irq)); aml_append(dev, aml_name_decl("_CRS", crs)); return dev; diff --git a/include/hw/hyperv/vmbus-bridge.h b/include/hw/hyperv/vmbus-bridge.h index c0a06d832c..33f93de64d 100644 --- a/include/hw/hyperv/vmbus-bridge.h +++ b/include/hw/hyperv/vmbus-bridge.h @@ -19,8 +19,7 @@ typedef struct VMBus VMBus; typedef struct VMBusBridge { SysBusDevice parent_obj; -uint8_t irq0; -uint8_t irq1; +uint8_t irq; VMBus *bus; } VMBusBridge; -- 2.24.1
[PATCH v4 2/2] acpi: i386: Move VMBus DSDT entry to SB
Signed-off-by: Jon Doron --- hw/i386/acpi-build.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index 91af0d2d0d..1f938a53b2 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -1091,7 +1091,6 @@ static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) static void build_isa_devices_aml(Aml *table) { ISADevice *fdc = pc_find_fdc0(); -VMBusBridge *vmbus_bridge = vmbus_bridge_find(); bool ambiguous; Aml *scope = aml_scope("_SB.PCI0.ISA"); @@ -1112,10 +,6 @@ static void build_isa_devices_aml(Aml *table) isa_build_aml(ISA_BUS(obj), scope); } -if (vmbus_bridge) { -aml_append(scope, build_vmbus_device_aml(vmbus_bridge)); -} - aml_append(table, scope); } @@ -1660,6 +1655,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, PCIBus *bus = NULL; TPMIf *tpm = tpm_find(); int i; +VMBusBridge *vmbus_bridge = vmbus_bridge_find(); dsdt = init_aml_allocator(); @@ -1702,6 +1698,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, } } +if (vmbus_bridge) { +sb_scope = aml_scope("_SB"); +aml_append(sb_scope, build_vmbus_device_aml(vmbus_bridge)); +aml_append(dsdt, sb_scope); +} + if (pcmc->legacy_cpu_hotplug) { build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base); } else { -- 2.24.1
Re: [PATCH v4 0/2] hyperv: vmbus: ACPI various corrections
Sure thing I'll try to take a look at it this weekend. Jon. On Mon, Jul 13, 2020, 11:44 Igor Mammedov wrote: > On Thu, 25 Jun 2020 07:50:09 +0300 > Jon Doron wrote: > > > After doing further tests and looking at the latest HyperV ACPI DSDT. > > Do minor fix to our VMBus ACPI entry. > > Jon, > vmbus feature needs a testcase, could you look into it please? > (see tests/qtest/bios-tables-test.c for an example. > also look into comment blob at the top for the propper process > for acpi patches/tests) > > > > v4: > > * Removed the patch which adds _ADR definition to the VMBus > > * Correct the change which moves the VMBus under the SB > > > > v3: > > Removed accidental change for the dct submodule head > > > > v2: > > Renamed irq0 to irq now that there is a single IRQ required > > > > Jon Doron (2): > > hyperv: vmbus: Remove the 2nd IRQ > > acpi: i386: Move VMBus DSDT entry to SB > > > > hw/hyperv/vmbus.c| 3 +-- > > hw/i386/acpi-build.c | 16 > > include/hw/hyperv/vmbus-bridge.h | 3 +-- > > 3 files changed, 10 insertions(+), 12 deletions(-) > > > >
[PATCH v5 1/2] acpi: i386: Move VMBus DSDT entry to SB
Signed-off-by: Jon Doron --- hw/i386/acpi-build.c | 12 +++- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/hw/i386/acpi-build.c b/hw/i386/acpi-build.c index b7bc2a..7708a27f70 100644 --- a/hw/i386/acpi-build.c +++ b/hw/i386/acpi-build.c @@ -975,7 +975,6 @@ static Aml *build_vmbus_device_aml(VMBusBridge *vmbus_bridge) static void build_isa_devices_aml(Aml *table) { -VMBusBridge *vmbus_bridge = vmbus_bridge_find(); bool ambiguous; Object *obj = object_resolve_path_type("", TYPE_ISA_BUS, &ambiguous); Aml *scope; @@ -986,10 +985,6 @@ static void build_isa_devices_aml(Aml *table) build_acpi_ipmi_devices(scope, BUS(obj), "\\_SB.PCI0.ISA"); isa_build_aml(ISA_BUS(obj), scope); -if (vmbus_bridge) { -aml_append(scope, build_vmbus_device_aml(vmbus_bridge)); -} - aml_append(table, scope); } @@ -1485,6 +1480,7 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, PCIBus *bus = NULL; TPMIf *tpm = tpm_find(); int i; +VMBusBridge *vmbus_bridge = vmbus_bridge_find(); dsdt = init_aml_allocator(); @@ -1526,6 +1522,12 @@ build_dsdt(GArray *table_data, BIOSLinker *linker, } } +if (vmbus_bridge) { +sb_scope = aml_scope("_SB"); +aml_append(sb_scope, build_vmbus_device_aml(vmbus_bridge)); +aml_append(dsdt, sb_scope); +} + if (pcmc->legacy_cpu_hotplug) { build_legacy_cpu_hotplug_aml(dsdt, machine, pm->cpu_hp_io_base); } else { -- 2.24.1
[PATCH v5 0/2] hyperv: vmbus: ACPI various corrections
After doing further tests and looking at the latest HyperV ACPI DSDT. Do minor fix to our VMBus ACPI entry. v5: * Rebased on latest master * Added a patch to fix 32bit compliation on VMBus v4: * Removed the patch which adds _ADR definition to the VMBus * Correct the change which moves the VMBus under the SB v3: Removed accidental change for the dct submodule head v2: Renamed irq0 to irq now that there is a single IRQ required Jon Doron (2): acpi: i386: Move VMBus DSDT entry to SB hw: hyperv: vmbus: Fix 32bit compilation hw/hyperv/vmbus.c| 3 ++- hw/i386/acpi-build.c | 12 +++- 2 files changed, 9 insertions(+), 6 deletions(-) -- 2.24.1