diff mbox series

[v3,01/20] mcdstub: initial file structure for new mcdstub created. -mcd QEMU startup option added. Functions for initializing the mcdstub added. Basic helper functions for processes/cpus in the mcdstub added

Message ID 20231107130323.4126-2-nicolas.eder@lauterbach.com
State New
Headers show
Series first version of mcdstub | expand

Commit Message

nicolas.eder@lauterbach.com Nov. 7, 2023, 1:03 p.m. UTC
---
 include/mcdstub/arm_mcdstub.h    |  10 +
 include/mcdstub/mcdstub.h        | 239 +++++++++++++++++++
 include/mcdstub/mcdstub_common.h |   7 +
 mcdstub/mcdstub.c                | 383 +++++++++++++++++++++++++++++++
 qemu-options.hx                  |  18 ++
 system/vl.c                      |  13 ++
 6 files changed, 670 insertions(+)
 create mode 100644 include/mcdstub/arm_mcdstub.h
 create mode 100644 include/mcdstub/mcdstub.h
 create mode 100644 include/mcdstub/mcdstub_common.h
 create mode 100644 mcdstub/mcdstub.c

Comments

Alex Bennée Nov. 29, 2023, 3:23 p.m. UTC | #1
Nicolas Eder <nicolas.eder@lauterbach.com> writes:

I think you need to ensure an extra line in you git commit messages
because currently the whole commit runs into your subject.

  foo/bar: summary of change made

  longer description of the problem and how the patch solves it. This can
  continue to be multiple lines of text.

  Signed-off-by: Name <email>

> ---
>  include/mcdstub/arm_mcdstub.h    |  10 +
>  include/mcdstub/mcdstub.h        | 239 +++++++++++++++++++
>  include/mcdstub/mcdstub_common.h |   7 +
>  mcdstub/mcdstub.c                | 383 +++++++++++++++++++++++++++++++
>  qemu-options.hx                  |  18 ++
>  system/vl.c                      |  13 ++
>  6 files changed, 670 insertions(+)
>  create mode 100644 include/mcdstub/arm_mcdstub.h
>  create mode 100644 include/mcdstub/mcdstub.h
>  create mode 100644 include/mcdstub/mcdstub_common.h
>  create mode 100644 mcdstub/mcdstub.c
>
> diff --git a/include/mcdstub/arm_mcdstub.h b/include/mcdstub/arm_mcdstub.h
> new file mode 100644
> index 0000000000..a57aa8e9f2
> --- /dev/null
> +++ b/include/mcdstub/arm_mcdstub.h
> @@ -0,0 +1,10 @@
> +#ifndef ARM_MCDSTUB_H
> +#define ARM_MCDSTUB_H
> +
> +#include "hw/core/cpu.h"
> +#include "mcdstub_common.h"
> +/* just used for the register xml files */
> +#include "exec/gdbstub.h"
> +
> +
> +#endif /* ARM_MCDSTUB_H */
> diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
> new file mode 100644
> index 0000000000..36058157ae
> --- /dev/null
> +++ b/include/mcdstub/mcdstub.h
> @@ -0,0 +1,239 @@
> +#ifndef MCDSTUB_INTERNALS_H
> +#define MCDSTUB_INTERNALS_H
> +
> +#include "exec/cpu-common.h"
> +#include "chardev/char.h"
> +#include "hw/core/cpu.h"
> +#include "mcdstub_common.h"
> +
> +#define MAX_PACKET_LENGTH 1024
> +/* misc */
> +#define QUERY_TOTAL_NUMBER 12
> +#define CMD_SCHEMA_LENGTH 6
> +#define MCD_SYSTEM_NAME "qemu-system"
> +
> +/* supported architectures */
> +#define MCDSTUB_ARCH_ARM "arm"
> +
> +/* tcp query packet values templates */
> +#define DEVICE_NAME_TEMPLATE(s) "qemu-" #s "-device"
> +
> +typedef struct MCDProcess {
> +    uint32_t pid;
> +    bool attached;
> +
> +    char target_xml[1024];

Is this even used?

> +} MCDProcess;
> +
> +#define get_param(p, i)    (&g_array_index(p, MCDCmdVariant, i))
> +
> +enum RSState {
> +    RS_INACTIVE,
> +    RS_IDLE,
> +    RS_GETLINE,
> +    RS_DATAEND,
> +};
> +
> +typedef struct MCDState {
> +    bool init;       /* have we been initialised? */
> +    CPUState *c_cpu; /* current CPU for everything */
> +    enum RSState state; /* parsing state */
> +    char line_buf[MAX_PACKET_LENGTH];
> +    int line_buf_index;
> +    int line_sum; /* running checksum */
> +    int line_csum; /* checksum at the end of the packet */
> +    GByteArray *last_packet;
> +    int signal;
> +
> +    MCDProcess *processes;
> +    int process_num;
> +    GString *str_buf;
> +    GByteArray *mem_buf;
> +    int sstep_flags;
> +    int supported_sstep_flags;
> +
> +    uint32_t query_cpu_id;
> +    GList *all_memspaces;
> +    GList *all_reggroups;
> +    GList *all_registers;
> +    GList *all_breakpoints;
> +    GArray *resets;
> +    mcd_trigger_into_st trigger;
> +    mcd_cpu_state_st cpu_state;
> +    MCDCmdParseEntry mcd_query_cmds_table[QUERY_TOTAL_NUMBER];
> +} MCDState;

Hmm this doesn't even compile:

  cc -m64 -mcx16 -Ilibcommon.fa.p -I../../common-user/host/x86_64 -I../../linux-user/include/host/x86_64 -I../../linux-user/include -Iui -I../../ui -I/usr/include/capstone -I/usr/include/p11-kit-1 -I/usr/include/pixman-1 -I/usr/include/libpng16 -I/usr/include/spice-server -I/usr/include/spice-1 -I/usr/include/libusb-1.0 -I/usr/include/SDL2 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include -I/usr/include/libmount -I/usr/include/blkid -I/usr/include/gio-unix-2.0 -I/usr/include/slirp -I/usr/include/gtk-3.0 -I/usr/include/pango-1.0 -I/usr/include/harfbuzz -I/usr/include/freetype2 -I/usr/include/fribidi -I/usr/include/cairo -I/usr/include/gdk-pixbuf-2.0 -I/usr/include/x86_64-linux-gnu -I/usr/include/atk-1.0 -I/usr/include/at-spi2-atk/2.0 -I/usr/include/at-spi-2.0 -I/usr/include/dbus-1.0 -I/usr/lib/x86_64-linux-gnu/dbus-1.0/include -I/usr/include/vte-2.91 -I/usr/include/virgl -I/usr/include/cacard -I/usr/include/nss -I/usr/include/nspr -I/usr/include/PCSC -fdiagnostics-color=auto -Wall -Winvalid-pch -Werror -std=gnu11 -O2 -g -fstack-protector-strong -Wundef -Wwrite-strings -Wmissing-prototypes -Wstrict-prototypes -Wredundant-decls -Wold-style-declaration -Wold-style-definition -Wtype-limits -Wformat-security -Wformat-y2k -Winit-self -Wignored-qualifiers -Wempty-body -Wnested-externs -Wendif-labels -Wexpansion-to-defined -Wimplicit-fallthrough=2 -Wmissing-format-attribute -Wno-missing-include-dirs -Wno-shift-negative-value -Wno-psabi -Wshadow=local -isystem /home/alex/lsrc/qemu.git/linux-headers -isystem linux-headers -iquote . -iquote /home/alex/lsrc/qemu.git -iquote /home/alex/lsrc/qemu.git/include -iquote /home/alex/lsrc/qemu.git/host/include/x86_64 -iquote /home/alex/lsrc/qemu.git/host/include/generic -iquote /home/alex/lsrc/qemu.git/tcg/i386 -pthread -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -fno-strict-aliasing -fno-common -fwrapv -fPIE -D_DEFAULT_SOURCE -D_XOPEN_SOURCE=600 -DNCURSES_WIDECHAR=1 -D_REENTRANT -DSTRUCT_IOVEC_DEFINED -MD -MQ libcommon.fa.p/system_vl.c.o -MF libcommon.fa.p/system_vl.c.o.d -o libcommon.fa.p/system_vl.c.o -c ../../system/vl.c
  In file included from ../../system/vl.c:71:
  /home/alex/lsrc/qemu.git/include/mcdstub/mcdstub.h:61:5: error: unknown type name ‘mcd_trigger_into_st’
     61 |     mcd_trigger_into_st trigger;
        |     ^~~~~~~~~~~~~~~~~~~
  /home/alex/lsrc/qemu.git/include/mcdstub/mcdstub.h:62:5: error: unknown type name ‘mcd_cpu_state_st’
     62 |     mcd_cpu_state_st cpu_state;
        |     ^~~~~~~~~~~~~~~~
  /home/alex/lsrc/qemu.git/include/mcdstub/mcdstub.h:63:5: error: unknown type name ‘MCDCmdParseEntry’
     63 |     MCDCmdParseEntry mcd_query_cmds_table[QUERY_TOTAL_NUMBER];
        |     ^~~~~~~~~~~~~~~~
  /home/alex/lsrc/qemu.git/include/mcdstub/mcdstub.h:96:28: error: unknown type name ‘MCDCmdParseEntry’
     96 | void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table);
        |                            ^~~~~~~~~~~~~~~~
  /home/alex/lsrc/qemu.git/include/mcdstub/mcdstub.h:1: error: unterminated #ifndef
      1 | #ifndef MCDSTUB_INTERNALS_H
        | 

Each patch in the series should be able to compile cleanly for the
purposes of bisection.

If we need external headers you'll need to tweak configure/meson to make
sure they are available and gate the feature if its not.

> +
> +/* lives in main mcdstub.c */
> +extern MCDState mcdserver_state;
> +
> +#ifndef _WIN32
> +void mcd_sigterm_handler(int signal);
> +#endif
> +
> +/**
> + * mcdserver_start() - initializes the mcdstub and opens a TCP port
> + * @device: TCP port (e.g. tcp::1235)
> + */
> +int mcdserver_start(const char *device);
> +
> +/**
> + * mcd_init_mcdserver_state() - Initializes the mcdserver_state struct.
> + *
> + * This function allocates memory for the mcdserver_state struct and sets
> + * all of its members to their inital values. This includes setting the
> + * cpu_state to halted and initializing the query functions with
> + * :c:func:`init_query_cmds_table`.
> + */
> +void mcd_init_mcdserver_state(void);
> +
> +/**
> + * init_query_cmds_table() - Initializes all query functions.
> + *
> + * This function adds all query functions to the mcd_query_cmds_table. This
> + * includes their command string, handler function and parameter schema.
> + * @mcd_query_cmds_table: Lookup table with all query commands.
> + */
> +void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table);

No MCDCmdParseEntry defined. However this doesn't seem to be an exported
function. If it was it should be prefixed.

> +/**
> + * create_processes() - Sorts all processes and calls
> + * :c:func:`mcd_create_default_process`.
> + *
> + * This function sorts all connected processes with the qsort function.
> + * Afterwards, it creates a new process with
> + * :c:func:`mcd_create_default_process`.
> + * @s: A MCDState object.
> + */
> +void create_processes(MCDState *s);

this is definitely internal.

> +
> +/**
> + * mcd_create_default_process() - Creates a default process for debugging.
> + *
> + * This function creates a new, not yet attached, process with an ID one above
> + * the previous maximum ID.
> + * @s: A MCDState object.
> + */
> +void mcd_create_default_process(MCDState *s);
> +
> +/**
> + * find_cpu_clusters() - Returns the CPU cluster of the child object.
> + *
> + * @param[in] child Object with unknown CPU cluster.
> + * @param[in] opaque Pointer to an MCDState object.
> + */
> +int find_cpu_clusters(Object *child, void *opaque);
> +
> +/**
> + * pid_order() - Compares process IDs.
> + *
> + * This function returns -1 if process "a" has a ower process ID than "b".
> + * If "b" has a lower ID than "a" 1 is returned and if they are qual 0 is
> + * returned.
> + * @a: Process a.
> + * @b: Process b.
> + */
> +int pid_order(const void *a, const void *b);
> +
> +/**
> + * mcd_chr_can_receive() - Returns the maximum packet length of a TCP packet.
> + */
> +int mcd_chr_can_receive(void *opaque);
> +
> +/**
> + * mcd_chr_receive() - Handles receiving a TCP packet.
> + *
> + * This function gets called by QEMU when a TCP packet is received.
> + * It iterates over that packet an calls :c:func:`mcd_read_byte` for each char
> + * of the packet.
> + * @buf: Content of the packet.
> + * @size: Length of the packet.
> + */
> +void mcd_chr_receive(void *opaque, const uint8_t *buf, int size);
> +
> +/**
> + * mcd_chr_event() - Handles a TCP client connect.
> + *
> + * This function gets called by QEMU when a TCP cliet connects to the opened
> + * TCP port. It attaches the first process. From here on TCP packets can be
> + * exchanged.
> + * @event: Type of event.
> + */
> +void mcd_chr_event(void *opaque, QEMUChrEvent event);
> +
> +/**
> + * mcd_supports_guest_debug() - Returns true if debugging the selected
> + * accelerator is supported.
> + */
> +bool mcd_supports_guest_debug(void);
> +
> +/**
> + * mcd_vm_state_change() - Handles a state change of the QEMU VM.
> + *
> + * This function is called when the QEMU VM goes through a state transition.
> + * It stores the runstate the CPU is in to the cpu_state and when in
> + * RUN_STATE_DEBUG it collects additional data on what watchpoint was hit.
> + * This function also resets the singlestep behavior.
> + * @running: True if he VM is running.
> + * @state: The new (and active) VM run state.
> + */
> +void mcd_vm_state_change(void *opaque, bool running, RunState state);
> +/**
> + * mcd_get_cpu_process() - Returns the process of the provided CPU.
> + *
> + * @cpu: The CPU state.
> + */
> +MCDProcess *mcd_get_cpu_process(CPUState *cpu);
> +
> +/**
> + * mcd_set_stop_cpu() - Sets c_cpu to the just stopped CPU.
> + *
> + * @cpu: The CPU state.
> + */
> +void mcd_set_stop_cpu(CPUState *cpu);
> +
> +/**
> + * mcd_get_cpu_pid() - Returns the process ID of the provided CPU.
> + *
> + * @cpu: The CPU state.
> + */
> +uint32_t mcd_get_cpu_pid(CPUState *cpu);
> +
> +/**
> + * mcd_get_process() - Returns the process of the provided pid.
> + *
> + * @pid: The process ID.
> + */
> +MCDProcess *mcd_get_process(uint32_t pid);
> +
> +/**
> + * mcd_first_attached_cpu() - Returns the first CPU with an attached process.
> + */
> +CPUState *mcd_first_attached_cpu(void);
> +
> +/**
> + * mcd_next_attached_cpu() - Returns the first CPU with an attached process
> + * starting after the
> + * provided cpu.
> + *
> + * @cpu: The CPU to start from.
> + */
> +CPUState *mcd_next_attached_cpu(CPUState *cpu);
> +
> +/**
> + * mcd_get_cpu() - Returns the CPU the index i_cpu_index.
> + *
> + * @cpu_index: Index of the desired CPU.
> + */
> +CPUState *mcd_get_cpu(uint32_t cpu_index);
> +/**
> + * get_first_cpu_in_process() - Returns the first CPU in the provided process.
> + *
> + * @process: The process to look in.
> + */
> +CPUState *get_first_cpu_in_process(MCDProcess *process);
> +
> +/**
> + * find_cpu() - Returns the CPU with an index equal to the thread_id.
> + *
> + * @thread_id: ID of the desired CPU.
> + */
> +CPUState *find_cpu(uint32_t thread_id);
> diff --git a/include/mcdstub/mcdstub_common.h b/include/mcdstub/mcdstub_common.h
> new file mode 100644
> index 0000000000..3bae2c3b6f
> --- /dev/null
> +++ b/include/mcdstub/mcdstub_common.h
> @@ -0,0 +1,7 @@
> +#ifndef MCDSTUB_COMMON_H
> +#define MCDSTUB_COMMON_H
> +
> +#define ARGUMENT_STRING_LENGTH 64
> +#define TCP_CONFIG_STRING_LENGTH 128
> +
> +#endif /* MCDSTUB_COMMON_H */
> diff --git a/mcdstub/mcdstub.c b/mcdstub/mcdstub.c
> new file mode 100644
> index 0000000000..4cdf2e42ed
> --- /dev/null
> +++ b/mcdstub/mcdstub.c
> @@ -0,0 +1,383 @@
> +/*
> + * This is the main mcdstub.
> + */

Can we have an author, copyright date and SPDX header for this file
please.

> +
> +#include "qemu/osdep.h"
> +#include "qemu/ctype.h"
> +#include "qemu/cutils.h"
> +#include "qemu/module.h"
> +#include "qemu/error-report.h"
> +#include "qemu/debug.h"
> +#include "hw/cpu/cluster.h"
> +#include "hw/boards.h"
> +#include "sysemu/hw_accel.h"
> +#include "sysemu/runstate.h"
> +#include "exec/replay-core.h"
> +#include "exec/hwaddr.h"
> +#include "qapi/error.h"
> +#include "exec/tb-flush.h"
> +#include "sysemu/cpus.h"
> +#include "sysemu/replay.h"
> +#include "chardev/char.h"
> +#include "chardev/char-fe.h"
> +#include "monitor/monitor.h"
> +#include "cutils.h"

I'm sure half of these includes aren't needed. Try and keep them minimal please.

> +
> +/* mcdstub header files */
> +#include "mcdstub/mcd_shared_defines.h"
> +#include "mcdstub/mcdstub.h"
> +
> +/* architecture specific stubs */
> +#include "mcdstub/arm_mcdstub.h"
> +
> +typedef struct {
> +    CharBackend chr;
> +} MCDSystemState;
> +
> +MCDSystemState mcdserver_system_state;
> +
> +MCDState mcdserver_state;
> +
> +void mcd_init_mcdserver_state(void)
> +{
> +    g_assert(!mcdserver_state.init);
> +    memset(&mcdserver_state, 0, sizeof(MCDState));
> +    mcdserver_state.init = true;
> +    mcdserver_state.str_buf = g_string_new(NULL);
> +    mcdserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH);
> +    mcdserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4);
> +
> +    /*
> +     * What single-step modes are supported is accelerator dependent.
> +     * By default try to use no IRQs and no timers while single
> +     * stepping so as to make single stepping like a typical ICE HW step.
> +     */
> +    mcdserver_state.supported_sstep_flags =
> +        accel_supported_gdbstub_sstep_flags();
> +    mcdserver_state.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER;
> +    mcdserver_state.sstep_flags &= mcdserver_state.supported_sstep_flags;
> +
> +    /* init query table */
> +    init_query_cmds_table(mcdserver_state.mcd_query_cmds_table);
> +
> +    /* at this time the cpu hans't been started! -> set cpu_state */
> +    mcd_cpu_state_st cpu_state =  {
> +            .state = CORE_STATE_HALTED,
> +            .info_str = STATE_STR_INIT_HALTED,
> +    };
> +    mcdserver_state.cpu_state = cpu_state;
> +
> +    /* create new debug object */
> +    mcd_init_debug_class();
> + }
> +
> +void mcd_set_stop_cpu(CPUState *cpu)
> +{
> +    mcdserver_state.c_cpu = cpu;
> +}
> +
> +void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table)
> +{
> +    /* initalizes a list of all query commands */
> +    int cmd_number = 0;
> +}
> +void create_processes(MCDState *s)
> +{
> +    object_child_foreach(object_get_root(), find_cpu_clusters, s);
> +
> +    if (mcdserver_state.processes) {
> +        /* Sort by PID */
> +        qsort(mcdserver_state.processes,
> +              mcdserver_state.process_num,
> +              sizeof(mcdserver_state.processes[0]),
> +              pid_order);
> +    }
> +
> +    mcd_create_default_process(s);
> +}
> +
> +void mcd_create_default_process(MCDState *s)
> +{
> +    MCDProcess *process;
> +    int max_pid = 0;
> +
> +    if (mcdserver_state.process_num) {
> +        max_pid = s->processes[s->process_num - 1].pid;
> +    }
> +
> +    s->processes = g_renew(MCDProcess, s->processes, ++s->process_num);
> +    process = &s->processes[s->process_num - 1];
> +
> +    /* We need an available PID slot for this process */
> +    assert(max_pid < UINT32_MAX);
> +
> +    process->pid = max_pid + 1;
> +    process->attached = false;
> +    process->target_xml[0] = '\0';
> +}
> +
> +int find_cpu_clusters(Object *child, void *opaque)
> +{
> +    if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) {
> +        MCDState *s = (MCDState *) opaque;
> +        CPUClusterState *cluster = CPU_CLUSTER(child);
> +        MCDProcess *process;
> +
> +        s->processes = g_renew(MCDProcess, s->processes, ++s->process_num);
> +
> +        process = &s->processes[s->process_num - 1];
> +        assert(cluster->cluster_id != UINT32_MAX);
> +        process->pid = cluster->cluster_id + 1;
> +        process->attached = false;
> +        process->target_xml[0] = '\0';
> +
> +        return 0;
> +    }
> +
> +    return object_child_foreach(child, find_cpu_clusters, opaque);
> +}
> +
> +int pid_order(const void *a, const void *b)
> +{
> +    MCDProcess *pa = (MCDProcess *) a;
> +    MCDProcess *pb = (MCDProcess *) b;
> +
> +    if (pa->pid < pb->pid) {
> +        return -1;
> +    } else if (pa->pid > pb->pid) {
> +        return 1;
> +    } else {
> +        return 0;
> +    }
> +}
> +
> +int mcdserver_start(const char *device)
> +{
> +    char mcd_device_config[TCP_CONFIG_STRING_LENGTH];
> +    char mcd_tcp_port[TCP_CONFIG_STRING_LENGTH];
> +    Chardev *chr = NULL;
> +
> +    if (!first_cpu) {
> +        error_report("mcdstub: meaningless to attach to a "
> +                     "machine without any CPU.");
> +        return -1;
> +    }
> +
> +    if (!mcd_supports_guest_debug()) {
> +        error_report("mcdstub: current accelerator doesn't "
> +                     "support guest debugging");
> +        return -1;
> +    }
> +
> +    if (!device) {
> +        return -1;
> +    }
> +
> +    /* if device == default -> set tcp_port = tcp::<MCD_DEFAULT_TCP_PORT> */
> +    if (strcmp(device, "default") == 0) {
> +        snprintf(mcd_tcp_port, sizeof(mcd_tcp_port), "tcp::%s",
> +            MCD_DEFAULT_TCP_PORT);
> +        device = mcd_tcp_port;
> +    }
> +
> +    if (strcmp(device, "none") != 0) {
> +        if (strstart(device, "tcp:", NULL)) {
> +            /* enforce required TCP attributes */
> +            snprintf(mcd_device_config, sizeof(mcd_device_config),
> +                     "%s,wait=off,nodelay=on,server=on", device);
> +            device = mcd_device_config;
> +        }
> +#ifndef _WIN32
> +        else if (strcmp(device, "stdio") == 0) {
> +            struct sigaction act;
> +
> +            memset(&act, 0, sizeof(act));
> +            act.sa_handler = mcd_sigterm_handler;
> +            sigaction(SIGINT, &act, NULL);
> +            strcpy(mcd_device_config, device);
> +        }
> +#endif
> +        chr = qemu_chr_new_noreplay("mcd", device, true, NULL);
> +        if (!chr) {
> +            return -1;
> +        }
> +    }
> +
> +    if (!mcdserver_state.init) {
> +        mcd_init_mcdserver_state();
> +
> +        qemu_add_vm_change_state_handler(mcd_vm_state_change, NULL);
> +    } else {
> +        qemu_chr_fe_deinit(&mcdserver_system_state.chr, true);
> +        reset_mcdserver_state();
> +    }
> +
> +    create_processes(&mcdserver_state);
> +
> +    if (chr) {
> +        qemu_chr_fe_init(&mcdserver_system_state.chr, chr, &error_abort);
> +        qemu_chr_fe_set_handlers(&mcdserver_system_state.chr,
> +                                 mcd_chr_can_receive,
> +                                 mcd_chr_receive, mcd_chr_event,
> +                                 NULL, &mcdserver_state, NULL, true);
> +    }
> +    mcdserver_state.state = chr ? RS_IDLE : RS_INACTIVE;
> +
> +    return 0;
> +}
> +
> +int mcd_chr_can_receive(void *opaque)
> +{
> +  return MAX_PACKET_LENGTH;
> +}
> +
> +void mcd_chr_receive(void *opaque, const uint8_t *buf, int size)
> +{
> +    int i;
> +
> +    for (i = 0; i < size; i++) {
> +        mcd_read_byte(buf[i]);
> +        if (buf[i] == 0) {
> +            break;
> +        }
> +    }
> +}
> +
> +
> +void mcd_chr_event(void *opaque, QEMUChrEvent event)
> +{
> +    int i;
> +    MCDState *s = (MCDState *) opaque;
> +
> +    switch (event) {
> +    case CHR_EVENT_OPENED:
> +        /* Start with first process attached, others detached */
> +        for (i = 0; i < s->process_num; i++) {
> +            s->processes[i].attached = !i;
> +        }
> +
> +        s->c_cpu = mcd_first_attached_cpu();
> +        break;
> +    default:
> +        break;
> +    }
> +}
> +
> +bool mcd_supports_guest_debug(void)
> +{
> +    const AccelOpsClass *ops = cpus_get_accel();
> +    if (ops->supports_guest_debug) {
> +        return ops->supports_guest_debug();
> +    }
> +    return false;
> +}
> +
> +#ifndef _WIN32
> +void mcd_sigterm_handler(int signal)
> +{
> +    if (runstate_is_running()) {
> +        vm_stop(RUN_STATE_PAUSED);
> +    }
> +}
> +#endif
> +
> +uint32_t mcd_get_cpu_pid(CPUState *cpu)
> +{
> +    if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
> +        /* Return the default process' PID */
> +        int index = mcdserver_state.process_num - 1;
> +        return mcdserver_state.processes[index].pid;
> +    }
> +    return cpu->cluster_index + 1;
> +}
> +
> +MCDProcess *mcd_get_process(uint32_t pid)
> +{
> +    int i;
> +
> +    if (!pid) {
> +        /* 0 means any process, we take the first one */
> +        return &mcdserver_state.processes[0];
> +    }
> +
> +    for (i = 0; i < mcdserver_state.process_num; i++) {
> +        if (mcdserver_state.processes[i].pid == pid) {
> +            return &mcdserver_state.processes[i];
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +CPUState *mcd_get_cpu(uint32_t cpu_index)
> +{
> +    CPUState *cpu = first_cpu;
> +
> +    while (cpu) {
> +        if (cpu->cpu_index == cpu_index) {
> +            return cpu;
> +        }
> +        cpu = mcd_next_attached_cpu(cpu);
> +    }
> +
> +    return cpu;
> +}
> +
> +CPUState *mcd_first_attached_cpu(void)
> +{
> +    CPUState *cpu = first_cpu;
> +    MCDProcess *process = mcd_get_cpu_process(cpu);
> +
> +    if (!process->attached) {
> +        return mcd_next_attached_cpu(cpu);
> +    }
> +
> +    return cpu;
> +}
> +
> +CPUState *mcd_next_attached_cpu(CPUState *cpu)
> +{
> +    cpu = CPU_NEXT(cpu);
> +
> +    while (cpu) {
> +        if (mcd_get_cpu_process(cpu)->attached) {
> +            break;
> +        }
> +
> +        cpu = CPU_NEXT(cpu);
> +    }
> +
> +    return cpu;
> +}
> +
> +int mcd_get_cpu_index(CPUState *cpu)
> +{
> +    return cpu->cpu_index + 1;
> +}
> +
> +CPUState *get_first_cpu_in_process(MCDProcess *process)
> +{
> +    CPUState *cpu;
> +
> +    CPU_FOREACH(cpu) {
> +        if (mcd_get_cpu_pid(cpu) == process->pid) {
> +            return cpu;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> +CPUState *find_cpu(uint32_t thread_id)
> +{
> +    CPUState *cpu;
> +
> +    CPU_FOREACH(cpu) {
> +        if (mcd_get_cpu_index(cpu) == thread_id) {
> +            return cpu;
> +        }
> +    }
> +
> +    return NULL;
> +}
> +
> diff --git a/qemu-options.hx b/qemu-options.hx
> index e26230bac5..accf92642c 100644
> --- a/qemu-options.hx
> +++ b/qemu-options.hx
> @@ -4430,6 +4430,24 @@ SRST
>      (see the :ref:`GDB usage` chapter in the System Emulation Users Guide).
>  ERST
>  
> +DEF("mcd", HAS_ARG, QEMU_OPTION_mcd, \
> +    "-mcd dev        accept mcd connection on 'dev'. (QEMU defaults to starting\n"
> +    "                the guest without waiting for a mcd client to connect; use -S too\n"
> +    "                if you want it to not start execution.)\n"
> +    "                To use the default Port write '-mcd default'\n",
> +    QEMU_ARCH_ALL)
> +SRST
> +``-mcd dev``
> +    Accept a mcd connection on device dev. Note that this option does not pause QEMU
> +    execution -- if you want QEMU to not start the guest until you
> +    connect with mcd and issue a ``run`` command, you will need to
> +    also pass the ``-S`` option to QEMU.
> +
> +    The most usual configuration is to listen on a local TCP socket::
> +
> +        -mcd tcp::1235
> +ERST
> +
>  DEF("d", HAS_ARG, QEMU_OPTION_d, \
>      "-d item1,...    enable logging of specified items (use '-d help' for a list of log items)\n",
>      QEMU_ARCH_ALL)
> diff --git a/system/vl.c b/system/vl.c
> index 92d29bf521..b657c77ea4 100644
> --- a/system/vl.c
> +++ b/system/vl.c
> @@ -68,6 +68,7 @@
>  #include "sysemu/numa.h"
>  #include "sysemu/hostmem.h"
>  #include "exec/gdbstub.h"
> +#include "mcdstub/mcdstub.h"
>  #include "qemu/timer.h"
>  #include "chardev/char.h"
>  #include "qemu/bitmap.h"
> @@ -1266,6 +1267,7 @@ struct device_config {
>          DEV_PARALLEL,  /* -parallel      */
>          DEV_DEBUGCON,  /* -debugcon */
>          DEV_GDB,       /* -gdb, -s */
> +        DEV_MCD,       /* -mcd */
>          DEV_SCLP,      /* s390 sclp */
>      } type;
>      const char *cmdline;
> @@ -2673,6 +2675,14 @@ static void qemu_machine_creation_done(void)
>      if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
>          exit(1);
>      }
> +
> +    if (foreach_device_config(DEV_MCD, mcdserver_start) < 0) {
> +        /*
> +         * starts the mcdserver if the mcd option was set
> +         */
> +        exit(1);
> +    }
> +

More compile failures:

FAILED: qemu-system-alpha 
cc -m64 -mcx16  -o qemu-system-alpha libcommon.fa.p/hw_core_cpu-common.c.o libcommon.fa.p/hw_core_machine-smp.c.o libcommon.fa.p/gdbstub_syscalls.c.o libcommon.fa.p/cpu-common.c.o libcommon.fa.p/page-vary-common.c.o libcommon.fa.p/disas_alpha.c.o libcommon.fa.p/disas_capstone.c.o libcommon.fa.p/disas_disas.c.o libcommon.fa.p/accel_tcg_cpu-exec-common.c.o libcommon.fa.p/trace_trace-hmp-cmds.c.o libcommon.fa.p/qom_qom-hmp-cmds.c.o libcommon.fa.p/crypto_tls-cipher-suites.c.o libcommon.fa.p/ui_clipboard.c.o libcommon.fa.p/ui_console.c.o libcommon.fa.p/ui_cursor.c.o libcommon.fa.p/ui_input-keymap.c.o libcommon.fa.p/ui_input-legacy.c.o libcommon.fa.p/ui_input-barrier.c.o libcommon.fa.p/ui_input.c.o libcommon.fa.p/ui_kbd-state.c.o libcommon.fa.p/ui_keymaps.c.o libcommon.fa.p/ui_qemu-pixman.c.o libcommon.fa.p/ui_ui-hmp-cmds.c.o libcommon.fa.p/ui_ui-qmp-cmds.c.o libcommon.fa.p/ui_util.c.o libcommon.fa.p/ui_console-vc.c.o libcommon.fa.p/ui_dbus-module.c.o libcommon.fa.p/ui_spice-module.c.o libcommon.fa.p/ui_vdagent.c.o libcommon.fa.p/ui_input-linux.c.o libcommon.fa.p/ui_udmabuf.c.o libcommon.fa.p/ui_vnc.c.o libcommon.fa.p/ui_vnc-enc-zlib.c.o libcommon.fa.p/ui_vnc-enc-hextile.c.o libcommon.fa.p/ui_vnc-enc-tight.c.o libcommon.fa.p/ui_vnc-palette.c.o libcommon.fa.p/ui_vnc-enc-zrle.c.o libcommon.fa.p/ui_vnc-auth-vencrypt.c.o libcommon.fa.p/ui_vnc-ws.c.o libcommon.fa.p/ui_vnc-jobs.c.o libcommon.fa.p/ui_vnc-clipboard.c.o libcommon.fa.p/ui_vnc-auth-sasl.c.o libcommon.fa.p/hw_9pfs_9p-local.c.o libcommon.fa.p/hw_9pfs_9p-posix-acl.c.o libcommon.fa.p/hw_9pfs_9p-proxy.c.o libcommon.fa.p/hw_9pfs_9p-synth.c.o libcommon.fa.p/hw_9pfs_9p-xattr-user.c.o libcommon.fa.p/hw_9pfs_9p-xattr.c.o libcommon.fa.p/hw_9pfs_9p.c.o libcommon.fa.p/hw_9pfs_codir.c.o libcommon.fa.p/hw_9pfs_cofile.c.o libcommon.fa.p/hw_9pfs_cofs.c.o libcommon.fa.p/hw_9pfs_coth.c.o libcommon.fa.p/hw_9pfs_coxattr.c.o libcommon.fa.p/hw_9pfs_9p-util-linux.c.o libcommon.fa.p/hw_acpi_acpi-stub.c.o libcommon.fa.p/hw_acpi_aml-build-stub.c.o libcommon.fa.p/hw_acpi_ghes-stub.c.o libcommon.fa.p/hw_acpi_acpi_interface.c.o libcommon.fa.p/hw_acpi_pci-bridge-stub.c.o libcommon.fa.p/hw_acpi_acpi-qmp-cmds.c.o libcommon.fa.p/hw_audio_soundhw.c.o libcommon.fa.p/hw_audio_ac97.c.o libcommon.fa.p/hw_audio_fmopl.c.o libcommon.fa.p/hw_audio_adlib.c.o libcommon.fa.p/hw_audio_cs4231a.c.o libcommon.fa.p/hw_audio_es1370.c.o libcommon.fa.p/hw_audio_gus.c.o libcommon.fa.p/hw_audio_gusemu_hal.c.o libcommon.fa.p/hw_audio_gusemu_mixer.c.o libcommon.fa.p/hw_audio_intel-hda.c.o libcommon.fa.p/hw_audio_hda-codec.c.o libcommon.fa.p/hw_audio_pcspk.c.o libcommon.fa.p/hw_audio_sb16.c.o libcommon.fa.p/hw_audio_virtio-snd.c.o libcommon.fa.p/hw_audio_virtio-snd-pci.c.o libcommon.fa.p/hw_block_block.c.o libcommon.fa.p/hw_block_cdrom.c.o libcommon.fa.p/hw_block_hd-geometry.c.o libcommon.fa.p/hw_block_fdc.c.o libcommon.fa.p/hw_block_fdc-isa.c.o libcommon.fa.p/hw_block_dataplane_virtio-blk.c.o libcommon.fa.p/hw_char_ipoctal232.c.o libcommon.fa.p/hw_char_parallel-isa.c.o libcommon.fa.p/hw_char_parallel.c.o libcommon.fa.p/hw_char_serial.c.o libcommon.fa.p/hw_char_serial-isa.c.o libcommon.fa.p/hw_char_serial-pci.c.o libcommon.fa.p/hw_char_serial-pci-multi.c.o libcommon.fa.p/hw_char_virtio-console.c.o libcommon.fa.p/hw_core_generic-loader.c.o libcommon.fa.p/hw_core_guest-loader.c.o libcommon.fa.p/hw_core_cpu-sysemu.c.o libcommon.fa.p/hw_core_fw-path-provider.c.o libcommon.fa.p/hw_core_gpio.c.o libcommon.fa.p/hw_core_loader.c.o libcommon.fa.p/hw_core_machine-hmp-cmds.c.o libcommon.fa.p/hw_core_machine-qmp-cmds.c.o libcommon.fa.p/hw_core_machine.c.o libcommon.fa.p/hw_core_nmi.c.o libcommon.fa.p/hw_core_null-machine.c.o libcommon.fa.p/hw_core_numa.c.o libcommon.fa.p/hw_core_qdev-fw.c.o libcommon.fa.p/hw_core_qdev-properties-system.c.o libcommon.fa.p/hw_core_sysbus.c.o libcommon.fa.p/hw_core_vm-change-state-handler.c.o libcommon.fa.p/hw_core_clock-vmstate.c.o libcommon.fa.p/hw_cpu_core.c.o libcommon.fa.p/hw_cpu_cluster.c.o libcommon.fa.p/hw_cxl_cxl-host-stubs.c.o libcommon.fa.p/hw_display_i2c-ddc.c.o libcommon.fa.p/hw_display_edid-generate.c.o libcommon.fa.p/hw_display_edid-region.c.o libcommon.fa.p/hw_display_cirrus_vga.c.o libcommon.fa.p/hw_display_vga-pci.c.o libcommon.fa.p/hw_display_bochs-display.c.o libcommon.fa.p/hw_display_vga.c.o libcommon.fa.p/hw_display_virtio-dmabuf.c.o libcommon.fa.p/hw_display_acpi-vga-stub.c.o libcommon.fa.p/hw_display_ati.c.o libcommon.fa.p/hw_display_ati_2d.c.o libcommon.fa.p/hw_display_ati_dbg.c.o libcommon.fa.p/hw_dma_i82374.c.o libcommon.fa.p/hw_dma_i8257.c.o libcommon.fa.p/hw_i2c_core.c.o libcommon.fa.p/hw_i2c_bitbang_i2c.c.o libcommon.fa.p/hw_ide_ahci.c.o libcommon.fa.p/hw_ide_ich.c.o libcommon.fa.p/hw_ide_cmd646.c.o libcommon.fa.p/hw_ide_core.c.o libcommon.fa.p/hw_ide_atapi.c.o libcommon.fa.p/hw_ide_pci.c.o libcommon.fa.p/hw_ide_qdev.c.o libcommon.fa.p/hw_input_hid.c.o libcommon.fa.p/hw_input_pckbd.c.o libcommon.fa.p/hw_input_ps2.c.o libcommon.fa.p/hw_input_virtio-input.c.o libcommon.fa.p/hw_input_virtio-input-hid.c.o libcommon.fa.p/hw_input_virtio-input-host.c.o libcommon.fa.p/hw_input_vhost-user-input.c.o libcommon.fa.p/hw_intc_intc.c.o libcommon.fa.p/hw_intc_i8259_common.c.o libcommon.fa.p/hw_intc_i8259.c.o libcommon.fa.p/hw_intc_kvm_irqcount.c.o libcommon.fa.p/hw_ipack_ipack.c.o libcommon.fa.p/hw_ipack_tpci200.c.o libcommon.fa.p/hw_isa_i82378.c.o libcommon.fa.p/hw_isa_isa-bus.c.o libcommon.fa.p/hw_isa_isa-superio.c.o libcommon.fa.p/hw_isa_smc37c669-superio.c.o libcommon.fa.p/hw_mem_cxl_type3_stubs.c.o libcommon.fa.p/hw_misc_pc-testdev.c.o libcommon.fa.p/hw_misc_pci-testdev.c.o libcommon.fa.p/hw_misc_pvpanic.c.o libcommon.fa.p/hw_misc_pvpanic-pci.c.o libcommon.fa.p/hw_misc_i2c-echo.c.o libcommon.fa.p/hw_net_ne2000.c.o libcommon.fa.p/hw_net_ne2000-pci.c.o libcommon.fa.p/hw_net_eepro100.c.o libcommon.fa.p/hw_net_pcnet-pci.c.o libcommon.fa.p/hw_net_pcnet.c.o libcommon.fa.p/hw_net_e1000.c.o libcommon.fa.p/hw_net_e1000x_common.c.o libcommon.fa.p/hw_net_rtl8139.c.o libcommon.fa.p/hw_net_tulip.c.o libcommon.fa.p/hw_net_net_tx_pkt.c.o libcommon.fa.p/hw_net_net_rx_pkt.c.o libcommon.fa.p/hw_net_vmxnet3.c.o libcommon.fa.p/hw_net_ne2000-isa.c.o libcommon.fa.p/hw_net_vhost_net.c.o libcommon.fa.p/hw_net_rocker_qmp-norocker.c.o libcommon.fa.p/hw_net_rocker_rocker-hmp-cmds.c.o libcommon.fa.p/hw_net_can_can_sja1000.c.o libcommon.fa.p/hw_net_can_can_kvaser_pci.c.o libcommon.fa.p/hw_net_can_can_pcm3680_pci.c.o libcommon.fa.p/hw_net_can_can_mioe3680_pci.c.o libcommon.fa.p/hw_net_can_ctucan_core.c.o libcommon.fa.p/hw_net_can_ctucan_pci.c.o libcommon.fa.p/hw_nvme_ctrl.c.o libcommon.fa.p/hw_nvme_dif.c.o libcommon.fa.p/hw_nvme_ns.c.o libcommon.fa.p/hw_nvme_subsys.c.o libcommon.fa.p/hw_nvram_fw_cfg-interface.c.o libcommon.fa.p/hw_nvram_fw_cfg.c.o libcommon.fa.p/hw_nvram_eeprom93xx.c.o libcommon.fa.p/hw_pci_msi.c.o libcommon.fa.p/hw_pci_msix.c.o libcommon.fa.p/hw_pci_pci.c.o libcommon.fa.p/hw_pci_pci_bridge.c.o libcommon.fa.p/hw_pci_pci_host.c.o libcommon.fa.p/hw_pci_pci-hmp-cmds.c.o libcommon.fa.p/hw_pci_pci-qmp-cmds.c.o libcommon.fa.p/hw_pci_pcie_sriov.c.o libcommon.fa.p/hw_pci_shpc.c.o libcommon.fa.p/hw_pci_slotid_cap.c.o libcommon.fa.p/hw_pci_pcie.c.o libcommon.fa.p/hw_pci_pcie_aer.c.o libcommon.fa.p/hw_pci_pcie_doe.c.o libcommon.fa.p/hw_pci-bridge_pci_bridge_dev.c.o libcommon.fa.p/hw_pci-bridge_pci_expander_bridge_stubs.c.o libcommon.fa.p/hw_rtc_mc146818rtc.c.o libcommon.fa.p/hw_scsi_emulation.c.o libcommon.fa.p/hw_scsi_scsi-bus.c.o libcommon.fa.p/hw_scsi_scsi-disk.c.o libcommon.fa.p/hw_scsi_scsi-generic.c.o libcommon.fa.p/hw_scsi_esp.c.o libcommon.fa.p/hw_scsi_esp-pci.c.o libcommon.fa.p/hw_scsi_lsi53c895a.c.o libcommon.fa.p/hw_scsi_megasas.c.o libcommon.fa.p/hw_scsi_mptsas.c.o libcommon.fa.p/hw_scsi_mptconfig.c.o libcommon.fa.p/hw_scsi_mptendian.c.o libcommon.fa.p/hw_scsi_vmw_pvscsi.c.o libcommon.fa.p/hw_scsi_virtio-scsi-dataplane.c.o libcommon.fa.p/hw_scsi_vhost-scsi.c.o libcommon.fa.p/hw_scsi_vhost-user-scsi.c.o libcommon.fa.p/hw_sd_sd.c.o libcommon.fa.p/hw_sd_core.c.o libcommon.fa.p/hw_sd_sdmmc-internal.c.o libcommon.fa.p/hw_sd_sdhci.c.o libcommon.fa.p/hw_sd_sdhci-pci.c.o libcommon.fa.p/hw_smbios_smbios-stub.c.o libcommon.fa.p/hw_timer_i8254_common.c.o libcommon.fa.p/hw_timer_i8254.c.o libcommon.fa.p/hw_ufs_ufs.c.o libcommon.fa.p/hw_ufs_lu.c.o libcommon.fa.p/hw_usb_bus.c.o libcommon.fa.p/hw_usb_combined-packet.c.o libcommon.fa.p/hw_usb_core.c.o libcommon.fa.p/hw_usb_desc.c.o libcommon.fa.p/hw_usb_desc-msos.c.o libcommon.fa.p/hw_usb_libhw.c.o libcommon.fa.p/hw_usb_pcap.c.o libcommon.fa.p/hw_usb_hcd-uhci.c.o libcommon.fa.p/hw_usb_hcd-ohci.c.o libcommon.fa.p/hw_usb_hcd-ohci-pci.c.o libcommon.fa.p/hw_usb_hcd-ehci.c.o libcommon.fa.p/hw_usb_hcd-ehci-pci.c.o libcommon.fa.p/hw_usb_hcd-xhci.c.o libcommon.fa.p/hw_usb_hcd-xhci-pci.c.o libcommon.fa.p/hw_usb_hcd-xhci-nec.c.o libcommon.fa.p/hw_usb_imx-usb-phy.c.o libcommon.fa.p/hw_usb_dev-hub.c.o libcommon.fa.p/hw_usb_dev-hid.c.o libcommon.fa.p/hw_usb_dev-wacom.c.o libcommon.fa.p/hw_usb_dev-storage.c.o libcommon.fa.p/hw_usb_dev-storage-bot.c.o libcommon.fa.p/hw_usb_dev-storage-classic.c.o libcommon.fa.p/hw_usb_dev-uas.c.o libcommon.fa.p/hw_usb_dev-audio.c.o libcommon.fa.p/hw_usb_dev-serial.c.o libcommon.fa.p/hw_usb_dev-network.c.o libcommon.fa.p/hw_usb_dev-mtp.c.o libcommon.fa.p/hw_usb_dev-smartcard-reader.c.o libcommon.fa.p/hw_usb_u2f.c.o libcommon.fa.p/hw_usb_u2f-passthru.c.o libcommon.fa.p/hw_virtio_virtio-bus.c.o libcommon.fa.p/hw_virtio_virtio-pci.c.o libcommon.fa.p/hw_virtio_virtio-crypto.c.o libcommon.fa.p/hw_virtio_vhost-vsock-common.c.o libcommon.fa.p/hw_virtio_virtio-iommu.c.o libcommon.fa.p/hw_virtio_vdpa-dev.c.o libcommon.fa.p/hw_virtio_vhost.c.o libcommon.fa.p/hw_virtio_vhost-user-device.c.o libcommon.fa.p/hw_virtio_vhost-user-device-pci.c.o libcommon.fa.p/hw_virtio_vhost-vdpa.c.o libcommon.fa.p/hw_virtio_virtio-hmp-cmds.c.o libcommon.fa.p/hw_watchdog_watchdog.c.o libcommon.fa.p/hw_watchdog_wdt_i6300esb.c.o libcommon.fa.p/hw_watchdog_wdt_ib700.c.o libcommon.fa.p/audio_audio.c.o libcommon.fa.p/audio_audio-hmp-cmds.c.o libcommon.fa.p/audio_mixeng.c.o libcommon.fa.p/audio_noaudio.c.o libcommon.fa.p/audio_wavaudio.c.o libcommon.fa.p/audio_wavcapture.c.o libcommon.fa.p/chardev_char-hmp-cmds.c.o libcommon.fa.p/chardev_msmouse.c.o libcommon.fa.p/chardev_wctablet.c.o libcommon.fa.p/chardev_testdev.c.o libcommon.fa.p/fsdev_qemu-fsdev-opts.c.o libcommon.fa.p/fsdev_qemu-fsdev-throttle.c.o libcommon.fa.p/fsdev_9p-iov-marshal.c.o libcommon.fa.p/fsdev_9p-marshal.c.o libcommon.fa.p/fsdev_qemu-fsdev.c.o libcommon.fa.p/dump_dump.c.o libcommon.fa.p/dump_dump-hmp-cmds.c.o libcommon.fa.p/block_blkreplay.c.o libcommon.fa.p/block_block-ram-registrar.c.o libcommon.fa.p/block_qapi-sysemu.c.o libcommon.fa.p/block_monitor_block-hmp-cmds.c.o libcommon.fa.p/system_balloon.c.o libcommon.fa.p/system_bootdevice.c.o libcommon.fa.p/system_cpus.c.o libcommon.fa.p/system_cpu-throttle.c.o libcommon.fa.p/system_cpu-timers.c.o libcommon.fa.p/system_datadir.c.o libcommon.fa.p/system_dirtylimit.c.o libcommon.fa.p/system_dma-helpers.c.o libcommon.fa.p/system_globals.c.o libcommon.fa.p/system_memory_mapping.c.o libcommon.fa.p/system_qdev-monitor.c.o libcommon.fa.p/system_qtest.c.o libcommon.fa.p/system_rtc.c.o libcommon.fa.p/system_runstate-action.c.o libcommon.fa.p/system_runstate-hmp-cmds.c.o libcommon.fa.p/system_runstate.c.o libcommon.fa.p/system_tpm-hmp-cmds.c.o libcommon.fa.p/system_vl.c.o libcommon.fa.p/system_tpm.c.o libcommon.fa.p/system_qemu-seccomp.c.o libcommon.fa.p/system_device_tree.c.o libcommon.fa.p/system_async-teardown.c.o libcommon.fa.p/backends_cryptodev-builtin.c.o libcommon.fa.p/backends_cryptodev-hmp-cmds.c.o libcommon.fa.p/backends_cryptodev.c.o libcommon.fa.p/backends_hostmem-ram.c.o libcommon.fa.p/backends_hostmem.c.o libcommon.fa.p/backends_rng-builtin.c.o libcommon.fa.p/backends_rng-egd.c.o libcommon.fa.p/backends_rng.c.o libcommon.fa.p/backends_confidential-guest-support.c.o libcommon.fa.p/backends_rng-random.c.o libcommon.fa.p/backends_hostmem-file.c.o libcommon.fa.p/backends_hostmem-memfd.c.o libcommon.fa.p/backends_vhost-user.c.o libcommon.fa.p/backends_cryptodev-vhost.c.o libcommon.fa.p/backends_cryptodev-vhost-user.c.o libcommon.fa.p/backends_dbus-vmstate.c.o libcommon.fa.p/backends_tpm_tpm_backend.c.o libcommon.fa.p/backends_tpm_tpm_util.c.o libcommon.fa.p/disas_disas-mon.c.o libcommon.fa.p/migration_block-dirty-bitmap.c.o libcommon.fa.p/migration_channel.c.o libcommon.fa.p/migration_channel-block.c.o libcommon.fa.p/migration_dirtyrate.c.o libcommon.fa.p/migration_exec.c.o libcommon.fa.p/migration_fd.c.o libcommon.fa.p/migration_file.c.o libcommon.fa.p/migration_global_state.c.o libcommon.fa.p/migration_migration-hmp-cmds.c.o libcommon.fa.p/migration_migration.c.o libcommon.fa.p/migration_multifd.c.o libcommon.fa.p/migration_multifd-zlib.c.o libcommon.fa.p/migration_ram-compress.c.o libcommon.fa.p/migration_options.c.o libcommon.fa.p/migration_postcopy-ram.c.o libcommon.fa.p/migration_savevm.c.o libcommon.fa.p/migration_socket.c.o libcommon.fa.p/migration_tls.c.o libcommon.fa.p/migration_threadinfo.c.o libcommon.fa.p/migration_colo-failover.c.o libcommon.fa.p/migration_colo.c.o libcommon.fa.p/migration_rdma.c.o libcommon.fa.p/migration_block.c.o libcommon.fa.p/migration_multifd-zstd.c.o libcommon.fa.p/monitor_fds.c.o libcommon.fa.p/monitor_hmp-cmds.c.o libcommon.fa.p/monitor_hmp.c.o libcommon.fa.p/monitor_qmp-cmds.c.o libcommon.fa.p/net_announce.c.o libcommon.fa.p/net_checksum.c.o libcommon.fa.p/net_dump.c.o libcommon.fa.p/net_eth.c.o libcommon.fa.p/net_filter-buffer.c.o libcommon.fa.p/net_filter-mirror.c.o libcommon.fa.p/net_filter.c.o libcommon.fa.p/net_hub.c.o libcommon.fa.p/net_net-hmp-cmds.c.o libcommon.fa.p/net_net.c.o libcommon.fa.p/net_queue.c.o libcommon.fa.p/net_socket.c.o libcommon.fa.p/net_stream.c.o libcommon.fa.p/net_dgram.c.o libcommon.fa.p/net_util.c.o libcommon.fa.p/net_colo-compare.c.o libcommon.fa.p/net_colo.c.o libcommon.fa.p/net_filter-rewriter.c.o libcommon.fa.p/net_filter-replay.c.o libcommon.fa.p/net_l2tpv3.c.o libcommon.fa.p/net_slirp.c.o libcommon.fa.p/net_vde.c.o libcommon.fa.p/net_vhost-user.c.o libcommon.fa.p/net_tap.c.o libcommon.fa.p/net_tap-linux.c.o libcommon.fa.p/net_vhost-vdpa.c.o libcommon.fa.p/net_can_can_core.c.o libcommon.fa.p/net_can_can_host.c.o libcommon.fa.p/net_can_can_socketcan.c.o libcommon.fa.p/replay_replay.c.o libcommon.fa.p/replay_replay-internal.c.o libcommon.fa.p/replay_replay-events.c.o libcommon.fa.p/replay_replay-time.c.o libcommon.fa.p/replay_replay-input.c.o libcommon.fa.p/replay_replay-char.c.o libcommon.fa.p/replay_replay-snapshot.c.o libcommon.fa.p/replay_replay-net.c.o libcommon.fa.p/replay_replay-audio.c.o libcommon.fa.p/replay_replay-random.c.o libcommon.fa.p/replay_replay-debugging.c.o libcommon.fa.p/stats_stats-hmp-cmds.c.o libcommon.fa.p/stats_stats-qmp-cmds.c.o libcommon.fa.p/accel_accel-system.c.o libcommon.fa.p/accel_accel-blocker.c.o libcommon.fa.p/accel_tcg_icount-common.c.o libcommon.fa.p/accel_tcg_monitor.c.o libcommon.fa.p/accel_dummy-cpus.c.o libcommon.fa.p/ebpf_ebpf_rss.c.o libcommon.fa.p/ui_curses.c.o libcommon.fa.p/ui_shader.c.o libcommon.fa.p/ui_console-gl.c.o libcommon.fa.p/ui_egl-helpers.c.o libcommon.fa.p/ui_egl-context.c.o libcommon.fa.p/ui_egl-headless.c.o libcommon.fa.p/ui_dbus-chardev.c.o libcommon.fa.p/ui_dbus-clipboard.c.o libcommon.fa.p/ui_dbus-console.c.o libcommon.fa.p/ui_dbus-error.c.o libcommon.fa.p/ui_dbus-listener.c.o libcommon.fa.p/ui_dbus.c.o libcommon.fa.p/ui_gtk.c.o libcommon.fa.p/ui_x_keymap.c.o libcommon.fa.p/ui_gtk-gl-area.c.o libcommon.fa.p/ui_gtk-egl.c.o libcommon.fa.p/ui_sdl2-2d.c.o libcommon.fa.p/ui_sdl2-input.c.o libcommon.fa.p/ui_sdl2.c.o libcommon.fa.p/ui_sdl2-gl.c.o libcommon.fa.p/ui_spice-core.c.o libcommon.fa.p/ui_spice-input.c.o libcommon.fa.p/ui_spice-display.c.o libcommon.fa.p/ui_spice-app.c.o libcommon.fa.p/hw_display_virtio-gpu-base.c.o libcommon.fa.p/hw_display_virtio-gpu.c.o libcommon.fa.p/hw_display_virtio-gpu-udmabuf.c.o libcommon.fa.p/hw_display_vhost-user-gpu.c.o libcommon.fa.p/hw_display_virtio-gpu-gl.c.o libcommon.fa.p/hw_display_virtio-gpu-virgl.c.o libcommon.fa.p/hw_display_virtio-gpu-pci.c.o libcommon.fa.p/hw_display_vhost-user-gpu-pci.c.o libcommon.fa.p/hw_display_virtio-gpu-pci-gl.c.o libcommon.fa.p/hw_usb_ccid-card-emulated.c.o libcommon.fa.p/hw_usb_ccid-card-passthru.c.o libcommon.fa.p/hw_usb_redirect.c.o libcommon.fa.p/hw_usb_quirks.c.o libcommon.fa.p/hw_usb_host-libusb.c.o libcommon.fa.p/audio_alsaaudio.c.o libcommon.fa.p/audio_ossaudio.c.o libcommon.fa.p/audio_paaudio.c.o libcommon.fa.p/audio_sdlaudio.c.o libcommon.fa.p/audio_jackaudio.c.o libcommon.fa.p/audio_sndioaudio.c.o libcommon.fa.p/audio_spiceaudio.c.o libcommon.fa.p/audio_dbusaudio.c.o libcommon.fa.p/chardev_baum.c.o libcommon.fa.p/chardev_spice.c.o libqemu-alpha-softmmu.fa.p/target_alpha_machine.c.o libqemu-alpha-softmmu.fa.p/hw_alpha_dp264.c.o libqemu-alpha-softmmu.fa.p/hw_alpha_pci.c.o libqemu-alpha-softmmu.fa.p/hw_alpha_typhoon.c.o libqemu-alpha-softmmu.fa.p/target_alpha_cpu.c.o libqemu-alpha-softmmu.fa.p/target_alpha_fpu_helper.c.o libqemu-alpha-softmmu.fa.p/target_alpha_gdbstub.c.o libqemu-alpha-softmmu.fa.p/target_alpha_helper.c.o libqemu-alpha-softmmu.fa.p/target_alpha_int_helper.c.o libqemu-alpha-softmmu.fa.p/target_alpha_mem_helper.c.o libqemu-alpha-softmmu.fa.p/target_alpha_sys_helper.c.o libqemu-alpha-softmmu.fa.p/target_alpha_translate.c.o libqemu-alpha-softmmu.fa.p/target_alpha_vax_helper.c.o libqemu-alpha-softmmu.fa.p/trace_control-target.c.o libqemu-alpha-softmmu.fa.p/hw_9pfs_virtio-9p-device.c.o libqemu-alpha-softmmu.fa.p/hw_block_virtio-blk.c.o libqemu-alpha-softmmu.fa.p/hw_block_virtio-blk-common.c.o libqemu-alpha-softmmu.fa.p/hw_block_vhost-user-blk.c.o libqemu-alpha-softmmu.fa.p/hw_char_virtio-serial-bus.c.o libqemu-alpha-softmmu.fa.p/hw_hyperv_hv-balloon-stub.c.o libqemu-alpha-softmmu.fa.p/hw_net_virtio-net.c.o libqemu-alpha-softmmu.fa.p/hw_scsi_virtio-scsi.c.o libqemu-alpha-softmmu.fa.p/hw_scsi_vhost-scsi-common.c.o libqemu-alpha-softmmu.fa.p/hw_vfio_helpers.c.o libqemu-alpha-softmmu.fa.p/hw_vfio_common.c.o libqemu-alpha-softmmu.fa.p/hw_vfio_container.c.o libqemu-alpha-softmmu.fa.p/hw_vfio_spapr.c.o libqemu-alpha-softmmu.fa.p/hw_vfio_migration.c.o libqemu-alpha-softmmu.fa.p/hw_vfio_display.c.o libqemu-alpha-softmmu.fa.p/hw_vfio_pci-quirks.c.o libqemu-alpha-softmmu.fa.p/hw_vfio_pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-config-io.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-qmp.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-backend.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-iova-tree.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-shadow-virtqueue.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-balloon.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-fs.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-vsock.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-vsock.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-rng.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-i2c.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-rng.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-gpio.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-gpio-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-scmi.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-scmi-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-vsock-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-vsock-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-blk-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-i2c-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-input-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-rng-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-scsi-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-scsi-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vhost-user-fs-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-crypto-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-input-host-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-input-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-rng-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-balloon-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-9p-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-scsi-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-blk-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-net-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-serial-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_virtio-iommu-pci.c.o libqemu-alpha-softmmu.fa.p/hw_virtio_vdpa-dev-pci.c.o libqemu-alpha-softmmu.fa.p/hw_i386_kvm_xen-stubs.c.o libqemu-alpha-softmmu.fa.p/dump_win_dump.c.o libqemu-alpha-softmmu.fa.p/cpu-target.c.o libqemu-alpha-softmmu.fa.p/system_arch_init.c.o libqemu-alpha-softmmu.fa.p/system_ioport.c.o libqemu-alpha-softmmu.fa.p/system_memory.c.o libqemu-alpha-softmmu.fa.p/system_physmem.c.o libqemu-alpha-softmmu.fa.p/system_watchpoint.c.o libqemu-alpha-softmmu.fa.p/page-vary-target.c.o libqemu-alpha-softmmu.fa.p/migration_ram.c.o libqemu-alpha-softmmu.fa.p/migration_target.c.o libqemu-alpha-softmmu.fa.p/monitor_hmp-cmds-target.c.o libqemu-alpha-softmmu.fa.p/monitor_hmp-target.c.o libqemu-alpha-softmmu.fa.p/fpu_softfloat.c.o libqemu-alpha-softmmu.fa.p/accel_accel-target.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_tcg-all.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_cpu-exec.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_tb-maint.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_tcg-runtime-gvec.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_tcg-runtime.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_translate-all.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_translator.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_plugin-gen.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_perf.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_cputlb.c.o libqemu-alpha-softmmu.fa.p/accel_stubs_xen-stub.c.o libqemu-alpha-softmmu.fa.p/accel_stubs_kvm-stub.c.o libqemu-alpha-softmmu.fa.p/plugins_loader.c.o libqemu-alpha-softmmu.fa.p/plugins_core.c.o libqemu-alpha-softmmu.fa.p/plugins_api.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_tcg-accel-ops.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_tcg-accel-ops-mttcg.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_tcg-accel-ops-icount.c.o libqemu-alpha-softmmu.fa.p/accel_tcg_tcg-accel-ops-rr.c.o libqemu-alpha-softmmu.fa.p/accel_qtest_qtest.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-types-machine-target.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-visit-machine-target.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-events-machine-target.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-commands-machine-target.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-types-misc-target.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-visit-misc-target.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-events-misc-target.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-commands-misc-target.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-introspect.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-types.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-visit.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-commands.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-init-commands.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-events.c.o libqemu-alpha-softmmu.fa.p/meson-generated_.._qapi_qapi-emit-events.c.o qemu-system-alpha.p/system_main.c.o -Wl,--as-needed -Wl,--no-undefined -pie -Wl,--whole-archive libhwcore.fa libqom.fa libevent-loop-base.fa gdbstub/libgdb_system.fa libio.fa libcrypto.fa libauthz.fa libblockdev.fa libblock.fa libchardev.fa libqmp.fa -Wl,--no-whole-archive -fstack-protector-strong -Wl,-z,relro -Wl,-z,now -Wl,--warn-common -Wl,--start-group libqemuutil.a subprojects/libvhost-user/libvhost-user-glib.a subprojects/libvhost-user/libvhost-user.a tcg/libtcg_system.fa ui/libdbus-display1.a libmigration.fa libhwcore.fa libqom.fa libevent-loop-base.fa gdbstub/libgdb_system.fa libio.fa libcrypto.fa libauthz.fa libblockdev.fa subprojects/libvduse/libvduse.a libblock.fa libchardev.fa libqmp.fa @block.syms @qemu.syms /usr/lib/x86_64-linux-gnu/libpixman-1.so /usr/lib/x86_64-linux-gnu/libepoxy.so /usr/lib/x86_64-linux-gnu/libcapstone.so /usr/lib/x86_64-linux-gnu/libspice-server.so -Xlinker --dynamic-list=/home/alex/lsrc/qemu.git/plugins/qemu-plugins.symbols /usr/lib/x86_64-linux-gnu/libgnutls.so /usr/lib/x86_64-linux-gnu/libpng16.so /usr/lib/x86_64-linux-gnu/libz.so /usr/lib/x86_64-linux-gnu/libjpeg.so -lsasl2 -lfdt /usr/lib/x86_64-linux-gnu/libudev.so /usr/lib/x86_64-linux-gnu/libSDL2.so /usr/lib/x86_64-linux-gnu/libpmem.so /usr/lib/x86_64-linux-gnu/libseccomp.so -lnuma /usr/lib/x86_64-linux-gnu/libgio-2.0.so /usr/lib/x86_64-linux-gnu/libgobject-2.0.so /usr/lib/x86_64-linux-gnu/libglib-2.0.so -lrdmacm -libverbs -libumad /usr/lib/gcc/x86_64-linux-gnu/12/../../../x86_64-linux-gnu/libzstd.so /usr/lib/x86_64-linux-gnu/libslirp.so -lvdeplug /usr/lib/x86_64-linux-gnu/libbpf.so /usr/lib/x86_64-linux-gnu/libncursesw.so /usr/lib/x86_64-linux-gnu/libtinfo.so /usr/lib/x86_64-linux-gnu/libgmodule-2.0.so -pthread /usr/lib/x86_64-linux-gnu/libgbm.so /usr/lib/x86_64-linux-gnu/libgtk-3.so /usr/lib/x86_64-linux-gnu/libgdk-3.so /usr/lib/x86_64-linux-gnu/libpangocairo-1.0.so /usr/lib/x86_64-linux-gnu/libpango-1.0.so /usr/lib/x86_64-linux-gnu/libharfbuzz.so /usr/lib/x86_64-linux-gnu/libatk-1.0.so /usr/lib/x86_64-linux-gnu/libcairo-gobject.so /usr/lib/x86_64-linux-gnu/libcairo.so /usr/lib/x86_64-linux-gnu/libgdk_pixbuf-2.0.so /usr/lib/x86_64-linux-gnu/libvte-2.91.so /usr/lib/x86_64-linux-gnu/libX11.so /usr/lib/x86_64-linux-gnu/libvirglrenderer.so /usr/lib/x86_64-linux-gnu/libcacard.so /usr/lib/x86_64-linux-gnu/libusbredirparser.so /usr/lib/x86_64-linux-gnu/libusb-1.0.so /usr/lib/x86_64-linux-gnu/libasound.so /usr/lib/x86_64-linux-gnu/libpulse.so /usr/lib/x86_64-linux-gnu/libjack.so -lpthread /usr/lib/x86_64-linux-gnu/libsndio.so -lbrlapi @block.syms -lnuma /usr/lib/x86_64-linux-gnu/liburing.so -lm /usr/lib/x86_64-linux-gnu/libfuse3.so /usr/lib/x86_64-linux-gnu/libiscsi.so -laio /usr/lib/x86_64-linux-gnu/libcurl.so /usr/lib/x86_64-linux-gnu/libacl.so /usr/lib/x86_64-linux-gnu/libgfapi.so /usr/lib/x86_64-linux-gnu/libglusterfs.so /usr/lib/x86_64-linux-gnu/libgfrpc.so /usr/lib/x86_64-linux-gnu/libgfxdr.so /usr/lib/x86_64-linux-gnu/libuuid.so /usr/lib/x86_64-linux-gnu/libnfs.so /usr/lib/x86_64-linux-gnu/libssh.so -lrbd -lrados -lutil -Wl,--end-group
/usr/bin/ld: libcommon.fa.p/system_vl.c.o: in function `foreach_device_config':
/home/alex/lsrc/qemu.git/builds/all/../../system/vl.c:1306: undefined reference to `mcdserver_start'
collect2: error: ld returned 1 exit status

I think because you are missing the meson integration to add this to the
build. Is it in a later patch?

>      if (!vga_interface_created && !default_vga &&
>          vga_interface_type != VGA_NONE) {
>          warn_report("A -vga option was passed but this machine "
> @@ -3028,6 +3038,9 @@ void qemu_init(int argc, char **argv)
>              case QEMU_OPTION_gdb:
>                  add_device_config(DEV_GDB, optarg);
>                  break;
> +            case QEMU_OPTION_mcd:
> +                add_device_config(DEV_MCD, optarg);
> +                break;
>              case QEMU_OPTION_L:
>                  if (is_help_option(optarg)) {
>                      list_data_dirs = true;
diff mbox series

Patch

diff --git a/include/mcdstub/arm_mcdstub.h b/include/mcdstub/arm_mcdstub.h
new file mode 100644
index 0000000000..a57aa8e9f2
--- /dev/null
+++ b/include/mcdstub/arm_mcdstub.h
@@ -0,0 +1,10 @@ 
+#ifndef ARM_MCDSTUB_H
+#define ARM_MCDSTUB_H
+
+#include "hw/core/cpu.h"
+#include "mcdstub_common.h"
+/* just used for the register xml files */
+#include "exec/gdbstub.h"
+
+
+#endif /* ARM_MCDSTUB_H */
diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
new file mode 100644
index 0000000000..36058157ae
--- /dev/null
+++ b/include/mcdstub/mcdstub.h
@@ -0,0 +1,239 @@ 
+#ifndef MCDSTUB_INTERNALS_H
+#define MCDSTUB_INTERNALS_H
+
+#include "exec/cpu-common.h"
+#include "chardev/char.h"
+#include "hw/core/cpu.h"
+#include "mcdstub_common.h"
+
+#define MAX_PACKET_LENGTH 1024
+/* misc */
+#define QUERY_TOTAL_NUMBER 12
+#define CMD_SCHEMA_LENGTH 6
+#define MCD_SYSTEM_NAME "qemu-system"
+
+/* supported architectures */
+#define MCDSTUB_ARCH_ARM "arm"
+
+/* tcp query packet values templates */
+#define DEVICE_NAME_TEMPLATE(s) "qemu-" #s "-device"
+
+typedef struct MCDProcess {
+    uint32_t pid;
+    bool attached;
+
+    char target_xml[1024];
+} MCDProcess;
+
+#define get_param(p, i)    (&g_array_index(p, MCDCmdVariant, i))
+
+enum RSState {
+    RS_INACTIVE,
+    RS_IDLE,
+    RS_GETLINE,
+    RS_DATAEND,
+};
+
+typedef struct MCDState {
+    bool init;       /* have we been initialised? */
+    CPUState *c_cpu; /* current CPU for everything */
+    enum RSState state; /* parsing state */
+    char line_buf[MAX_PACKET_LENGTH];
+    int line_buf_index;
+    int line_sum; /* running checksum */
+    int line_csum; /* checksum at the end of the packet */
+    GByteArray *last_packet;
+    int signal;
+
+    MCDProcess *processes;
+    int process_num;
+    GString *str_buf;
+    GByteArray *mem_buf;
+    int sstep_flags;
+    int supported_sstep_flags;
+
+    uint32_t query_cpu_id;
+    GList *all_memspaces;
+    GList *all_reggroups;
+    GList *all_registers;
+    GList *all_breakpoints;
+    GArray *resets;
+    mcd_trigger_into_st trigger;
+    mcd_cpu_state_st cpu_state;
+    MCDCmdParseEntry mcd_query_cmds_table[QUERY_TOTAL_NUMBER];
+} MCDState;
+
+/* lives in main mcdstub.c */
+extern MCDState mcdserver_state;
+
+#ifndef _WIN32
+void mcd_sigterm_handler(int signal);
+#endif
+
+/**
+ * mcdserver_start() - initializes the mcdstub and opens a TCP port
+ * @device: TCP port (e.g. tcp::1235)
+ */
+int mcdserver_start(const char *device);
+
+/**
+ * mcd_init_mcdserver_state() - Initializes the mcdserver_state struct.
+ *
+ * This function allocates memory for the mcdserver_state struct and sets
+ * all of its members to their inital values. This includes setting the
+ * cpu_state to halted and initializing the query functions with
+ * :c:func:`init_query_cmds_table`.
+ */
+void mcd_init_mcdserver_state(void);
+
+/**
+ * init_query_cmds_table() - Initializes all query functions.
+ *
+ * This function adds all query functions to the mcd_query_cmds_table. This
+ * includes their command string, handler function and parameter schema.
+ * @mcd_query_cmds_table: Lookup table with all query commands.
+ */
+void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table);
+/**
+ * create_processes() - Sorts all processes and calls
+ * :c:func:`mcd_create_default_process`.
+ *
+ * This function sorts all connected processes with the qsort function.
+ * Afterwards, it creates a new process with
+ * :c:func:`mcd_create_default_process`.
+ * @s: A MCDState object.
+ */
+void create_processes(MCDState *s);
+
+/**
+ * mcd_create_default_process() - Creates a default process for debugging.
+ *
+ * This function creates a new, not yet attached, process with an ID one above
+ * the previous maximum ID.
+ * @s: A MCDState object.
+ */
+void mcd_create_default_process(MCDState *s);
+
+/**
+ * find_cpu_clusters() - Returns the CPU cluster of the child object.
+ *
+ * @param[in] child Object with unknown CPU cluster.
+ * @param[in] opaque Pointer to an MCDState object.
+ */
+int find_cpu_clusters(Object *child, void *opaque);
+
+/**
+ * pid_order() - Compares process IDs.
+ *
+ * This function returns -1 if process "a" has a ower process ID than "b".
+ * If "b" has a lower ID than "a" 1 is returned and if they are qual 0 is
+ * returned.
+ * @a: Process a.
+ * @b: Process b.
+ */
+int pid_order(const void *a, const void *b);
+
+/**
+ * mcd_chr_can_receive() - Returns the maximum packet length of a TCP packet.
+ */
+int mcd_chr_can_receive(void *opaque);
+
+/**
+ * mcd_chr_receive() - Handles receiving a TCP packet.
+ *
+ * This function gets called by QEMU when a TCP packet is received.
+ * It iterates over that packet an calls :c:func:`mcd_read_byte` for each char
+ * of the packet.
+ * @buf: Content of the packet.
+ * @size: Length of the packet.
+ */
+void mcd_chr_receive(void *opaque, const uint8_t *buf, int size);
+
+/**
+ * mcd_chr_event() - Handles a TCP client connect.
+ *
+ * This function gets called by QEMU when a TCP cliet connects to the opened
+ * TCP port. It attaches the first process. From here on TCP packets can be
+ * exchanged.
+ * @event: Type of event.
+ */
+void mcd_chr_event(void *opaque, QEMUChrEvent event);
+
+/**
+ * mcd_supports_guest_debug() - Returns true if debugging the selected
+ * accelerator is supported.
+ */
+bool mcd_supports_guest_debug(void);
+
+/**
+ * mcd_vm_state_change() - Handles a state change of the QEMU VM.
+ *
+ * This function is called when the QEMU VM goes through a state transition.
+ * It stores the runstate the CPU is in to the cpu_state and when in
+ * RUN_STATE_DEBUG it collects additional data on what watchpoint was hit.
+ * This function also resets the singlestep behavior.
+ * @running: True if he VM is running.
+ * @state: The new (and active) VM run state.
+ */
+void mcd_vm_state_change(void *opaque, bool running, RunState state);
+/**
+ * mcd_get_cpu_process() - Returns the process of the provided CPU.
+ *
+ * @cpu: The CPU state.
+ */
+MCDProcess *mcd_get_cpu_process(CPUState *cpu);
+
+/**
+ * mcd_set_stop_cpu() - Sets c_cpu to the just stopped CPU.
+ *
+ * @cpu: The CPU state.
+ */
+void mcd_set_stop_cpu(CPUState *cpu);
+
+/**
+ * mcd_get_cpu_pid() - Returns the process ID of the provided CPU.
+ *
+ * @cpu: The CPU state.
+ */
+uint32_t mcd_get_cpu_pid(CPUState *cpu);
+
+/**
+ * mcd_get_process() - Returns the process of the provided pid.
+ *
+ * @pid: The process ID.
+ */
+MCDProcess *mcd_get_process(uint32_t pid);
+
+/**
+ * mcd_first_attached_cpu() - Returns the first CPU with an attached process.
+ */
+CPUState *mcd_first_attached_cpu(void);
+
+/**
+ * mcd_next_attached_cpu() - Returns the first CPU with an attached process
+ * starting after the
+ * provided cpu.
+ *
+ * @cpu: The CPU to start from.
+ */
+CPUState *mcd_next_attached_cpu(CPUState *cpu);
+
+/**
+ * mcd_get_cpu() - Returns the CPU the index i_cpu_index.
+ *
+ * @cpu_index: Index of the desired CPU.
+ */
+CPUState *mcd_get_cpu(uint32_t cpu_index);
+/**
+ * get_first_cpu_in_process() - Returns the first CPU in the provided process.
+ *
+ * @process: The process to look in.
+ */
+CPUState *get_first_cpu_in_process(MCDProcess *process);
+
+/**
+ * find_cpu() - Returns the CPU with an index equal to the thread_id.
+ *
+ * @thread_id: ID of the desired CPU.
+ */
+CPUState *find_cpu(uint32_t thread_id);
diff --git a/include/mcdstub/mcdstub_common.h b/include/mcdstub/mcdstub_common.h
new file mode 100644
index 0000000000..3bae2c3b6f
--- /dev/null
+++ b/include/mcdstub/mcdstub_common.h
@@ -0,0 +1,7 @@ 
+#ifndef MCDSTUB_COMMON_H
+#define MCDSTUB_COMMON_H
+
+#define ARGUMENT_STRING_LENGTH 64
+#define TCP_CONFIG_STRING_LENGTH 128
+
+#endif /* MCDSTUB_COMMON_H */
diff --git a/mcdstub/mcdstub.c b/mcdstub/mcdstub.c
new file mode 100644
index 0000000000..4cdf2e42ed
--- /dev/null
+++ b/mcdstub/mcdstub.c
@@ -0,0 +1,383 @@ 
+/*
+ * This is the main mcdstub.
+ */
+
+#include "qemu/osdep.h"
+#include "qemu/ctype.h"
+#include "qemu/cutils.h"
+#include "qemu/module.h"
+#include "qemu/error-report.h"
+#include "qemu/debug.h"
+#include "hw/cpu/cluster.h"
+#include "hw/boards.h"
+#include "sysemu/hw_accel.h"
+#include "sysemu/runstate.h"
+#include "exec/replay-core.h"
+#include "exec/hwaddr.h"
+#include "qapi/error.h"
+#include "exec/tb-flush.h"
+#include "sysemu/cpus.h"
+#include "sysemu/replay.h"
+#include "chardev/char.h"
+#include "chardev/char-fe.h"
+#include "monitor/monitor.h"
+#include "cutils.h"
+
+/* mcdstub header files */
+#include "mcdstub/mcd_shared_defines.h"
+#include "mcdstub/mcdstub.h"
+
+/* architecture specific stubs */
+#include "mcdstub/arm_mcdstub.h"
+
+typedef struct {
+    CharBackend chr;
+} MCDSystemState;
+
+MCDSystemState mcdserver_system_state;
+
+MCDState mcdserver_state;
+
+void mcd_init_mcdserver_state(void)
+{
+    g_assert(!mcdserver_state.init);
+    memset(&mcdserver_state, 0, sizeof(MCDState));
+    mcdserver_state.init = true;
+    mcdserver_state.str_buf = g_string_new(NULL);
+    mcdserver_state.mem_buf = g_byte_array_sized_new(MAX_PACKET_LENGTH);
+    mcdserver_state.last_packet = g_byte_array_sized_new(MAX_PACKET_LENGTH + 4);
+
+    /*
+     * What single-step modes are supported is accelerator dependent.
+     * By default try to use no IRQs and no timers while single
+     * stepping so as to make single stepping like a typical ICE HW step.
+     */
+    mcdserver_state.supported_sstep_flags =
+        accel_supported_gdbstub_sstep_flags();
+    mcdserver_state.sstep_flags = SSTEP_ENABLE | SSTEP_NOIRQ | SSTEP_NOTIMER;
+    mcdserver_state.sstep_flags &= mcdserver_state.supported_sstep_flags;
+
+    /* init query table */
+    init_query_cmds_table(mcdserver_state.mcd_query_cmds_table);
+
+    /* at this time the cpu hans't been started! -> set cpu_state */
+    mcd_cpu_state_st cpu_state =  {
+            .state = CORE_STATE_HALTED,
+            .info_str = STATE_STR_INIT_HALTED,
+    };
+    mcdserver_state.cpu_state = cpu_state;
+
+    /* create new debug object */
+    mcd_init_debug_class();
+ }
+
+void mcd_set_stop_cpu(CPUState *cpu)
+{
+    mcdserver_state.c_cpu = cpu;
+}
+
+void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table)
+{
+    /* initalizes a list of all query commands */
+    int cmd_number = 0;
+}
+void create_processes(MCDState *s)
+{
+    object_child_foreach(object_get_root(), find_cpu_clusters, s);
+
+    if (mcdserver_state.processes) {
+        /* Sort by PID */
+        qsort(mcdserver_state.processes,
+              mcdserver_state.process_num,
+              sizeof(mcdserver_state.processes[0]),
+              pid_order);
+    }
+
+    mcd_create_default_process(s);
+}
+
+void mcd_create_default_process(MCDState *s)
+{
+    MCDProcess *process;
+    int max_pid = 0;
+
+    if (mcdserver_state.process_num) {
+        max_pid = s->processes[s->process_num - 1].pid;
+    }
+
+    s->processes = g_renew(MCDProcess, s->processes, ++s->process_num);
+    process = &s->processes[s->process_num - 1];
+
+    /* We need an available PID slot for this process */
+    assert(max_pid < UINT32_MAX);
+
+    process->pid = max_pid + 1;
+    process->attached = false;
+    process->target_xml[0] = '\0';
+}
+
+int find_cpu_clusters(Object *child, void *opaque)
+{
+    if (object_dynamic_cast(child, TYPE_CPU_CLUSTER)) {
+        MCDState *s = (MCDState *) opaque;
+        CPUClusterState *cluster = CPU_CLUSTER(child);
+        MCDProcess *process;
+
+        s->processes = g_renew(MCDProcess, s->processes, ++s->process_num);
+
+        process = &s->processes[s->process_num - 1];
+        assert(cluster->cluster_id != UINT32_MAX);
+        process->pid = cluster->cluster_id + 1;
+        process->attached = false;
+        process->target_xml[0] = '\0';
+
+        return 0;
+    }
+
+    return object_child_foreach(child, find_cpu_clusters, opaque);
+}
+
+int pid_order(const void *a, const void *b)
+{
+    MCDProcess *pa = (MCDProcess *) a;
+    MCDProcess *pb = (MCDProcess *) b;
+
+    if (pa->pid < pb->pid) {
+        return -1;
+    } else if (pa->pid > pb->pid) {
+        return 1;
+    } else {
+        return 0;
+    }
+}
+
+int mcdserver_start(const char *device)
+{
+    char mcd_device_config[TCP_CONFIG_STRING_LENGTH];
+    char mcd_tcp_port[TCP_CONFIG_STRING_LENGTH];
+    Chardev *chr = NULL;
+
+    if (!first_cpu) {
+        error_report("mcdstub: meaningless to attach to a "
+                     "machine without any CPU.");
+        return -1;
+    }
+
+    if (!mcd_supports_guest_debug()) {
+        error_report("mcdstub: current accelerator doesn't "
+                     "support guest debugging");
+        return -1;
+    }
+
+    if (!device) {
+        return -1;
+    }
+
+    /* if device == default -> set tcp_port = tcp::<MCD_DEFAULT_TCP_PORT> */
+    if (strcmp(device, "default") == 0) {
+        snprintf(mcd_tcp_port, sizeof(mcd_tcp_port), "tcp::%s",
+            MCD_DEFAULT_TCP_PORT);
+        device = mcd_tcp_port;
+    }
+
+    if (strcmp(device, "none") != 0) {
+        if (strstart(device, "tcp:", NULL)) {
+            /* enforce required TCP attributes */
+            snprintf(mcd_device_config, sizeof(mcd_device_config),
+                     "%s,wait=off,nodelay=on,server=on", device);
+            device = mcd_device_config;
+        }
+#ifndef _WIN32
+        else if (strcmp(device, "stdio") == 0) {
+            struct sigaction act;
+
+            memset(&act, 0, sizeof(act));
+            act.sa_handler = mcd_sigterm_handler;
+            sigaction(SIGINT, &act, NULL);
+            strcpy(mcd_device_config, device);
+        }
+#endif
+        chr = qemu_chr_new_noreplay("mcd", device, true, NULL);
+        if (!chr) {
+            return -1;
+        }
+    }
+
+    if (!mcdserver_state.init) {
+        mcd_init_mcdserver_state();
+
+        qemu_add_vm_change_state_handler(mcd_vm_state_change, NULL);
+    } else {
+        qemu_chr_fe_deinit(&mcdserver_system_state.chr, true);
+        reset_mcdserver_state();
+    }
+
+    create_processes(&mcdserver_state);
+
+    if (chr) {
+        qemu_chr_fe_init(&mcdserver_system_state.chr, chr, &error_abort);
+        qemu_chr_fe_set_handlers(&mcdserver_system_state.chr,
+                                 mcd_chr_can_receive,
+                                 mcd_chr_receive, mcd_chr_event,
+                                 NULL, &mcdserver_state, NULL, true);
+    }
+    mcdserver_state.state = chr ? RS_IDLE : RS_INACTIVE;
+
+    return 0;
+}
+
+int mcd_chr_can_receive(void *opaque)
+{
+  return MAX_PACKET_LENGTH;
+}
+
+void mcd_chr_receive(void *opaque, const uint8_t *buf, int size)
+{
+    int i;
+
+    for (i = 0; i < size; i++) {
+        mcd_read_byte(buf[i]);
+        if (buf[i] == 0) {
+            break;
+        }
+    }
+}
+
+
+void mcd_chr_event(void *opaque, QEMUChrEvent event)
+{
+    int i;
+    MCDState *s = (MCDState *) opaque;
+
+    switch (event) {
+    case CHR_EVENT_OPENED:
+        /* Start with first process attached, others detached */
+        for (i = 0; i < s->process_num; i++) {
+            s->processes[i].attached = !i;
+        }
+
+        s->c_cpu = mcd_first_attached_cpu();
+        break;
+    default:
+        break;
+    }
+}
+
+bool mcd_supports_guest_debug(void)
+{
+    const AccelOpsClass *ops = cpus_get_accel();
+    if (ops->supports_guest_debug) {
+        return ops->supports_guest_debug();
+    }
+    return false;
+}
+
+#ifndef _WIN32
+void mcd_sigterm_handler(int signal)
+{
+    if (runstate_is_running()) {
+        vm_stop(RUN_STATE_PAUSED);
+    }
+}
+#endif
+
+uint32_t mcd_get_cpu_pid(CPUState *cpu)
+{
+    if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
+        /* Return the default process' PID */
+        int index = mcdserver_state.process_num - 1;
+        return mcdserver_state.processes[index].pid;
+    }
+    return cpu->cluster_index + 1;
+}
+
+MCDProcess *mcd_get_process(uint32_t pid)
+{
+    int i;
+
+    if (!pid) {
+        /* 0 means any process, we take the first one */
+        return &mcdserver_state.processes[0];
+    }
+
+    for (i = 0; i < mcdserver_state.process_num; i++) {
+        if (mcdserver_state.processes[i].pid == pid) {
+            return &mcdserver_state.processes[i];
+        }
+    }
+
+    return NULL;
+}
+
+CPUState *mcd_get_cpu(uint32_t cpu_index)
+{
+    CPUState *cpu = first_cpu;
+
+    while (cpu) {
+        if (cpu->cpu_index == cpu_index) {
+            return cpu;
+        }
+        cpu = mcd_next_attached_cpu(cpu);
+    }
+
+    return cpu;
+}
+
+CPUState *mcd_first_attached_cpu(void)
+{
+    CPUState *cpu = first_cpu;
+    MCDProcess *process = mcd_get_cpu_process(cpu);
+
+    if (!process->attached) {
+        return mcd_next_attached_cpu(cpu);
+    }
+
+    return cpu;
+}
+
+CPUState *mcd_next_attached_cpu(CPUState *cpu)
+{
+    cpu = CPU_NEXT(cpu);
+
+    while (cpu) {
+        if (mcd_get_cpu_process(cpu)->attached) {
+            break;
+        }
+
+        cpu = CPU_NEXT(cpu);
+    }
+
+    return cpu;
+}
+
+int mcd_get_cpu_index(CPUState *cpu)
+{
+    return cpu->cpu_index + 1;
+}
+
+CPUState *get_first_cpu_in_process(MCDProcess *process)
+{
+    CPUState *cpu;
+
+    CPU_FOREACH(cpu) {
+        if (mcd_get_cpu_pid(cpu) == process->pid) {
+            return cpu;
+        }
+    }
+
+    return NULL;
+}
+
+CPUState *find_cpu(uint32_t thread_id)
+{
+    CPUState *cpu;
+
+    CPU_FOREACH(cpu) {
+        if (mcd_get_cpu_index(cpu) == thread_id) {
+            return cpu;
+        }
+    }
+
+    return NULL;
+}
+
diff --git a/qemu-options.hx b/qemu-options.hx
index e26230bac5..accf92642c 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4430,6 +4430,24 @@  SRST
     (see the :ref:`GDB usage` chapter in the System Emulation Users Guide).
 ERST
 
+DEF("mcd", HAS_ARG, QEMU_OPTION_mcd, \
+    "-mcd dev        accept mcd connection on 'dev'. (QEMU defaults to starting\n"
+    "                the guest without waiting for a mcd client to connect; use -S too\n"
+    "                if you want it to not start execution.)\n"
+    "                To use the default Port write '-mcd default'\n",
+    QEMU_ARCH_ALL)
+SRST
+``-mcd dev``
+    Accept a mcd connection on device dev. Note that this option does not pause QEMU
+    execution -- if you want QEMU to not start the guest until you
+    connect with mcd and issue a ``run`` command, you will need to
+    also pass the ``-S`` option to QEMU.
+
+    The most usual configuration is to listen on a local TCP socket::
+
+        -mcd tcp::1235
+ERST
+
 DEF("d", HAS_ARG, QEMU_OPTION_d, \
     "-d item1,...    enable logging of specified items (use '-d help' for a list of log items)\n",
     QEMU_ARCH_ALL)
diff --git a/system/vl.c b/system/vl.c
index 92d29bf521..b657c77ea4 100644
--- a/system/vl.c
+++ b/system/vl.c
@@ -68,6 +68,7 @@ 
 #include "sysemu/numa.h"
 #include "sysemu/hostmem.h"
 #include "exec/gdbstub.h"
+#include "mcdstub/mcdstub.h"
 #include "qemu/timer.h"
 #include "chardev/char.h"
 #include "qemu/bitmap.h"
@@ -1266,6 +1267,7 @@  struct device_config {
         DEV_PARALLEL,  /* -parallel      */
         DEV_DEBUGCON,  /* -debugcon */
         DEV_GDB,       /* -gdb, -s */
+        DEV_MCD,       /* -mcd */
         DEV_SCLP,      /* s390 sclp */
     } type;
     const char *cmdline;
@@ -2673,6 +2675,14 @@  static void qemu_machine_creation_done(void)
     if (foreach_device_config(DEV_GDB, gdbserver_start) < 0) {
         exit(1);
     }
+
+    if (foreach_device_config(DEV_MCD, mcdserver_start) < 0) {
+        /*
+         * starts the mcdserver if the mcd option was set
+         */
+        exit(1);
+    }
+
     if (!vga_interface_created && !default_vga &&
         vga_interface_type != VGA_NONE) {
         warn_report("A -vga option was passed but this machine "
@@ -3028,6 +3038,9 @@  void qemu_init(int argc, char **argv)
             case QEMU_OPTION_gdb:
                 add_device_config(DEV_GDB, optarg);
                 break;
+            case QEMU_OPTION_mcd:
+                add_device_config(DEV_MCD, optarg);
+                break;
             case QEMU_OPTION_L:
                 if (is_help_option(optarg)) {
                     list_data_dirs = true;