[PATCH v3 05/20] mcdstub: tcp packet processing added

Nicolas Eder posted 20 patches 1 year ago
Maintainers: Nicolas Eder <nicolas.eder@lauterbach.com>, "Alex Bennée" <alex.bennee@linaro.org>, "Philippe Mathieu-Daudé" <philmd@linaro.org>, Richard Henderson <richard.henderson@linaro.org>, Paolo Bonzini <pbonzini@redhat.com>, Peter Xu <peterx@redhat.com>, David Hildenbrand <david@redhat.com>, Eduardo Habkost <eduardo@habkost.net>, Marcel Apfelbaum <marcel.apfelbaum@gmail.com>, Yanan Wang <wangyanan55@huawei.com>, "Marc-André Lureau" <marcandre.lureau@redhat.com>, "Daniel P. Berrangé" <berrange@redhat.com>, Thomas Huth <thuth@redhat.com>, Peter Maydell <peter.maydell@linaro.org>
There is a newer version of this series
[PATCH v3 05/20] mcdstub: tcp packet processing added
Posted by Nicolas Eder 1 year ago
---
 include/mcdstub/mcdstub.h | 144 ++++++++++++++++++++
 mcdstub/mcdstub.c         | 277 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 421 insertions(+)

diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
index 36058157ae..1461d0e1cb 100644
--- a/include/mcdstub/mcdstub.h
+++ b/include/mcdstub/mcdstub.h
@@ -25,6 +25,21 @@ typedef struct MCDProcess {
     char target_xml[1024];
 } MCDProcess;
 
+typedef void (*MCDCmdHandler)(GArray *params, void *user_ctx);
+typedef struct MCDCmdParseEntry {
+    MCDCmdHandler handler;
+    const char *cmd;
+    char schema[CMD_SCHEMA_LENGTH];
+} MCDCmdParseEntry;
+
+typedef union MCDCmdVariant {
+    const char *data;
+    uint32_t data_uint32_t;
+    uint64_t data_uint64_t;
+    uint32_t query_handle;
+    uint32_t cpu_id;
+} MCDCmdVariant;
+
 #define get_param(p, i)    (&g_array_index(p, MCDCmdVariant, i))
 
 enum RSState {
@@ -176,6 +191,35 @@ bool mcd_supports_guest_debug(void);
  * @state: The new (and active) VM run state.
  */
 void mcd_vm_state_change(void *opaque, bool running, RunState state);
+
+/**
+ * mcd_put_packet() - Calls :c:func:`mcd_put_packet_binary` with buf and length
+ * of buf.
+ *
+ * @buf: TCP packet data.
+ */
+int mcd_put_packet(const char *buf);
+
+/**
+ * mcd_put_packet_binary() - Adds footer and header to the TCP packet data in
+ * buf.
+ *
+ * Besides adding header and footer, this function also stores the complete TCP
+ * packet in the last_packet member of the mcdserver_state. Then the packet
+ * gets send with the :c:func:`mcd_put_buffer` function.
+ * @buf: TCP packet data.
+ * @len: TCP packet length.
+ */
+int mcd_put_packet_binary(const char *buf, int len);
+
+/**
+ * mcd_put_buffer() - Sends the buf as TCP packet with qemu_chr_fe_write_all.
+ *
+ * @buf: TCP packet data.
+ * @len: TCP packet length.
+ */
+void mcd_put_buffer(const uint8_t *buf, int len);
+
 /**
  * mcd_get_cpu_process() - Returns the process of the provided CPU.
  *
@@ -218,6 +262,82 @@ CPUState *mcd_first_attached_cpu(void);
  */
 CPUState *mcd_next_attached_cpu(CPUState *cpu);
 
+/**
+ * mcd_read_byte() - Resends the last packet if not acknowledged and extracts
+ * the data from a received TCP packet.
+ *
+ * In case the last sent packet was not acknowledged from the mcdstub,
+ * this function resends it.
+ * If it was acknowledged this function parses the incoming packet
+ * byte by byte.
+ * It extracts the data in the packet and sends an
+ * acknowledging response when finished. Then :c:func:`mcd_handle_packet` gets
+ * called.
+ * @ch: Character of the received TCP packet, which should be parsed.
+ */
+void mcd_read_byte(uint8_t ch);
+
+/**
+ * mcd_handle_packet() - Evaluates the type of received packet and chooses the
+ * correct handler.
+ *
+ * This function takes the first character of the line_buf to determine the
+ * type of packet. Then it selects the correct handler function and parameter
+ * schema. With this info it calls :c:func:`run_cmd_parser`.
+ * @line_buf: TCP packet data.
+ */
+int mcd_handle_packet(const char *line_buf);
+
+/**
+ * mcd_put_strbuf() - Calls :c:func:`mcd_put_packet` with the str_buf of the
+ * mcdserver_state.
+ */
+void mcd_put_strbuf(void);
+
+/**
+ * run_cmd_parser() - Prepares the mcdserver_state before executing TCP packet
+ * functions.
+ *
+ * This function empties the str_buf and mem_buf of the mcdserver_state and
+ * then calls :c:func:`process_string_cmd`. In case this function fails, an
+ * empty TCP packet is sent back the MCD Shared Library.
+ * @data: TCP packet data.
+ * @cmd: Handler function (can be an array of functions).
+ */
+void run_cmd_parser(const char *data, const MCDCmdParseEntry *cmd);
+
+/**
+ * process_string_cmd() - Collects all parameters from the data and calls the
+ * correct handler.
+ *
+ * The parameters are extracted with the :c:func:`cmd_parse_params function.
+ * This function selects the command in the cmds array, which fits the start of
+ * the data string. This way the correct commands is selected.
+ * @data: TCP packet data.
+ * @cmds: Array of possible commands.
+ * @num_cmds: Number of commands in the cmds array.
+ */
+int process_string_cmd(void *user_ctx, const char *data,
+    const MCDCmdParseEntry *cmds, int num_cmds);
+
+/**
+ * cmd_parse_params() - Extracts all parameters from a TCP packet.
+ *
+ * This function uses the schema parameter to determine which type of parameter
+ * to expect. It then extracts that parameter from the data and stores it in
+ * the params GArray.
+ * @data: TCP packet data.
+ * @schema: List of expected parameters for the packet.
+ * @params: GArray with all extracted parameters.
+ */
+int cmd_parse_params(const char *data, const char *schema, GArray *params);
+/**
+ * mcd_get_cpu_index() - Returns the internal CPU index plus one.
+ *
+ * @cpu: CPU of interest.
+ */
+int mcd_get_cpu_index(CPUState *cpu);
+
 /**
  * mcd_get_cpu() - Returns the CPU the index i_cpu_index.
  *
@@ -237,3 +357,27 @@ CPUState *get_first_cpu_in_process(MCDProcess *process);
  * @thread_id: ID of the desired CPU.
  */
 CPUState *find_cpu(uint32_t thread_id);
+/* helpers */
+
+/**
+ * int_cmp() - Compares a and b and returns zero if they are equal.
+ *
+ * @a: Pointer to integer a.
+ * @b: Pointer to integer b.
+ */
+int int_cmp(gconstpointer a, gconstpointer b);
+/**
+ * atouint64_t() - Converts a string into a unsigned 64 bit integer.
+ *
+ * @in: Pointer to input string.
+ */
+uint64_t atouint64_t(const char *in);
+
+/**
+ * atouint32_t() - Converts a string into a unsigned 32 bit integer.
+ *
+ * @in: Pointer to input string.
+ */
+uint32_t atouint32_t(const char *in);
+
+#endif /* MCDSTUB_INTERNALS_H */
diff --git a/mcdstub/mcdstub.c b/mcdstub/mcdstub.c
index 4cdf2e42ed..6900dcd0ea 100644
--- a/mcdstub/mcdstub.c
+++ b/mcdstub/mcdstub.c
@@ -80,7 +80,14 @@ void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table)
 {
     /* initalizes a list of all query commands */
     int cmd_number = 0;
+
+void reset_mcdserver_state(void)
+{
+    g_free(mcdserver_state.processes);
+    mcdserver_state.processes = NULL;
+    mcdserver_state.process_num = 0;
 }
+
 void create_processes(MCDState *s)
 {
     object_child_foreach(object_get_root(), find_cpu_clusters, s);
@@ -243,6 +250,228 @@ void mcd_chr_receive(void *opaque, const uint8_t *buf, int size)
     }
 }
 
+void mcd_read_byte(uint8_t ch)
+{
+    uint8_t reply;
+
+    if (mcdserver_state.last_packet->len) {
+        if (ch == TCP_NOT_ACKNOWLEDGED) {
+            /* the previous packet was not akcnowledged */
+            mcd_put_buffer(mcdserver_state.last_packet->data,
+                mcdserver_state.last_packet->len);
+        } else if (ch == TCP_ACKNOWLEDGED) {
+            /* the previous packet was acknowledged */
+        }
+
+        if (ch == TCP_ACKNOWLEDGED || ch == TCP_COMMAND_START) {
+            /*
+             * either acknowledged or a new communication starts
+             * -> discard previous packet
+             */
+            g_byte_array_set_size(mcdserver_state.last_packet, 0);
+        }
+        if (ch != TCP_COMMAND_START) {
+            /* skip to the next char */
+            return;
+        }
+    }
+
+    switch (mcdserver_state.state) {
+    case RS_IDLE:
+        if (ch == TCP_COMMAND_START) {
+            /* start of command packet */
+            mcdserver_state.line_buf_index = 0;
+            mcdserver_state.line_sum = 0;
+            mcdserver_state.state = RS_GETLINE;
+        }
+        break;
+    case RS_GETLINE:
+        if (ch == TCP_COMMAND_END) {
+            /* end of command */
+            mcdserver_state.line_buf[mcdserver_state.line_buf_index++] = 0;
+            mcdserver_state.state = RS_DATAEND;
+        } else if (mcdserver_state.line_buf_index >=
+            sizeof(mcdserver_state.line_buf) - 1) {
+            /* the input string is too long for the linebuffer! */
+            mcdserver_state.state = RS_IDLE;
+        } else {
+            /* copy the content to the line_buf */
+            mcdserver_state.line_buf[mcdserver_state.line_buf_index++] = ch;
+            mcdserver_state.line_sum += ch;
+        }
+        break;
+    case RS_DATAEND:
+        if (ch == TCP_WAS_NOT_LAST) {
+            reply = TCP_ACKNOWLEDGED;
+            mcd_put_buffer(&reply, 1);
+            mcdserver_state.state = mcd_handle_packet(mcdserver_state.line_buf);
+        } else if (ch == TCP_WAS_LAST) {
+            reply = TCP_ACKNOWLEDGED;
+            mcd_put_buffer(&reply, 1);
+            mcdserver_state.state = mcd_handle_packet(mcdserver_state.line_buf);
+        } else {
+            /* not acknowledged! */
+            reply = TCP_NOT_ACKNOWLEDGED;
+            mcd_put_buffer(&reply, 1);
+            /* waiting for package to get resent */
+            mcdserver_state.state = RS_IDLE;
+        }
+        break;
+    default:
+        abort();
+    }
+}
+
+int mcd_handle_packet(const char *line_buf)
+{
+    /*
+     * decides what function (handler) to call depending on
+     * the first character in the line_buf
+     */
+    const MCDCmdParseEntry *cmd_parser = NULL;
+
+    switch (line_buf[0]) {
+    default:
+        /* command not supported */
+        mcd_put_packet("");
+        break;
+    }
+
+    if (cmd_parser) {
+        /* parse commands and run the selected handler function */
+        run_cmd_parser(line_buf, cmd_parser);
+    }
+
+    return RS_IDLE;
+}
+void run_cmd_parser(const char *data, const MCDCmdParseEntry *cmd)
+{
+    if (!data) {
+        return;
+    }
+
+    g_string_set_size(mcdserver_state.str_buf, 0);
+    g_byte_array_set_size(mcdserver_state.mem_buf, 0);
+
+    if (process_string_cmd(NULL, data, cmd, 1)) {
+        mcd_put_packet("");
+    }
+}
+
+uint64_t atouint64_t(const char *in)
+{
+    uint64_t res = 0;
+    for (int i = 0; i < strlen(in); ++i) {
+        const char c = in[i];
+        res *= 10;
+        res += c - '0';
+    }
+
+    return res;
+}
+
+uint32_t atouint32_t(const char *in)
+{
+    uint32_t res = 0;
+    for (int i = 0; i < strlen(in); ++i) {
+        const char c = in[i];
+        res *= 10;
+        res += c - '0';
+    }
+
+    return res;
+}
+
+int cmd_parse_params(const char *data, const char *schema, GArray *params)
+{
+
+    char data_buffer[64] = {0};
+    const char *remaining_data = data;
+
+    for (int i = 0; i < strlen(schema); i++) {
+        /* get correct part of data */
+        char *separator = strchr(remaining_data, ARGUMENT_SEPARATOR);
+
+        if (separator) {
+            /* multiple arguments */
+            int seperator_index = (int)(separator - remaining_data);
+            strncpy(data_buffer, remaining_data, seperator_index);
+            data_buffer[seperator_index] = 0;
+            /* update remaining data for the next run */
+            remaining_data = &(remaining_data[seperator_index + 1]);
+        } else {
+            strncpy(data_buffer, remaining_data, strlen(remaining_data));
+            data_buffer[strlen(remaining_data)] = 0;
+        }
+
+        /* store right data */
+        MCDCmdVariant this_param;
+        switch (schema[i]) {
+        case ARG_SCHEMA_STRING:
+            /* this has to be the last argument */
+            this_param.data = remaining_data;
+            g_array_append_val(params, this_param);
+            break;
+        case ARG_SCHEMA_HEXDATA:
+            g_string_printf(mcdserver_state.str_buf, "%s", data_buffer);
+            break;
+        case ARG_SCHEMA_INT:
+            this_param.data_uint32_t = atouint32_t(data_buffer);
+            g_array_append_val(params, this_param);
+            break;
+        case ARG_SCHEMA_UINT64_T:
+            this_param.data_uint64_t = atouint64_t(data_buffer);
+            g_array_append_val(params, this_param);
+            break;
+        case ARG_SCHEMA_QRYHANDLE:
+            this_param.query_handle = atouint32_t(data_buffer);
+            g_array_append_val(params, this_param);
+            break;
+        case ARG_SCHEMA_CORENUM:
+            this_param.cpu_id = atouint32_t(data_buffer);
+            g_array_append_val(params, this_param);
+            break;
+        default:
+            return -1;
+        }
+    }
+    return 0;
+}
+
+int process_string_cmd(void *user_ctx, const char *data,
+    const MCDCmdParseEntry *cmds, int num_cmds)
+{
+    int i;
+    g_autoptr(GArray) params = g_array_new(false, true, sizeof(MCDCmdVariant));
+
+    if (!cmds) {
+        return -1;
+    }
+
+    for (i = 0; i < num_cmds; i++) {
+        const MCDCmdParseEntry *cmd = &cmds[i];
+        g_assert(cmd->handler && cmd->cmd);
+
+        /* continue if data and command are different */
+        if (strncmp(data, cmd->cmd, strlen(cmd->cmd))) {
+            continue;
+        }
+
+        if (strlen(cmd->schema)) {
+            /* extract data for parameters */
+            if (cmd_parse_params(&data[strlen(cmd->cmd)], cmd->schema, params))
+            {
+                return -1;
+            }
+        }
+
+        /* call handler */
+        cmd->handler(params, user_ctx);
+        return 0;
+    }
+
+    return -1;
+}
 
 void mcd_chr_event(void *opaque, QEMUChrEvent event)
 {
@@ -281,6 +510,43 @@ void mcd_sigterm_handler(int signal)
 }
 #endif
 
+int mcd_put_packet(const char *buf)
+{
+    return mcd_put_packet_binary(buf, strlen(buf));
+}
+
+void mcd_put_strbuf(void)
+{
+    mcd_put_packet(mcdserver_state.str_buf->str);
+}
+
+int mcd_put_packet_binary(const char *buf, int len)
+{
+    g_byte_array_set_size(mcdserver_state.last_packet, 0);
+    g_byte_array_append(mcdserver_state.last_packet,
+        (const uint8_t *) (char[2]) { TCP_COMMAND_START, '\0' }, 1);
+    g_byte_array_append(mcdserver_state.last_packet,
+        (const uint8_t *) buf, len);
+    g_byte_array_append(mcdserver_state.last_packet,
+        (const uint8_t *) (char[2]) { TCP_COMMAND_END, '\0' }, 1);
+    g_byte_array_append(mcdserver_state.last_packet,
+        (const uint8_t *) (char[2]) { TCP_WAS_LAST, '\0' }, 1);
+
+    mcd_put_buffer(mcdserver_state.last_packet->data,
+        mcdserver_state.last_packet->len);
+    return 0;
+}
+
+void mcd_put_buffer(const uint8_t *buf, int len)
+{
+    qemu_chr_fe_write_all(&mcdserver_system_state.chr, buf, len);
+}
+
+MCDProcess *mcd_get_cpu_process(CPUState *cpu)
+{
+    return mcd_get_process(mcd_get_cpu_pid(cpu));
+}
+
 uint32_t mcd_get_cpu_pid(CPUState *cpu)
 {
     if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
@@ -381,3 +647,14 @@ CPUState *find_cpu(uint32_t thread_id)
     return NULL;
 }
 
+
+int int_cmp(gconstpointer a, gconstpointer b)
+{
+    int int_a = *(int *)a;
+    int int_b = *(int *)b;
+    if (int_a == int_b) {
+        return 0;
+    } else {
+        return 1;
+    }
+}
-- 
2.34.1
Re: [PATCH v3 05/20] mcdstub: tcp packet processing added
Posted by Alex Bennée 12 months ago
Nicolas Eder <nicolas.eder@lauterbach.com> writes:

> ---
>  include/mcdstub/mcdstub.h | 144 ++++++++++++++++++++
>  mcdstub/mcdstub.c         | 277 ++++++++++++++++++++++++++++++++++++++
>  2 files changed, 421 insertions(+)
>
> diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
> index 36058157ae..1461d0e1cb 100644
> --- a/include/mcdstub/mcdstub.h
> +++ b/include/mcdstub/mcdstub.h
> @@ -25,6 +25,21 @@ typedef struct MCDProcess {
>      char target_xml[1024];
>  } MCDProcess;
>  
> +typedef void (*MCDCmdHandler)(GArray *params, void *user_ctx);
> +typedef struct MCDCmdParseEntry {
> +    MCDCmdHandler handler;
> +    const char *cmd;
> +    char schema[CMD_SCHEMA_LENGTH];
> +} MCDCmdParseEntry;
> +
> +typedef union MCDCmdVariant {
> +    const char *data;
> +    uint32_t data_uint32_t;
> +    uint64_t data_uint64_t;
> +    uint32_t query_handle;
> +    uint32_t cpu_id;
> +} MCDCmdVariant;
> +

These need to be in an earlier patch to keep the build working.

>  #define get_param(p, i)    (&g_array_index(p, MCDCmdVariant, i))
>  
>  enum RSState {
> @@ -176,6 +191,35 @@ bool mcd_supports_guest_debug(void);
>   * @state: The new (and active) VM run state.
>   */
>  void mcd_vm_state_change(void *opaque, bool running, RunState state);
> +
> +/**
> + * mcd_put_packet() - Calls :c:func:`mcd_put_packet_binary` with buf and length
> + * of buf.
> + *
> + * @buf: TCP packet data.
> + */
> +int mcd_put_packet(const char *buf);
> +
> +/**
> + * mcd_put_packet_binary() - Adds footer and header to the TCP packet data in
> + * buf.
> + *
> + * Besides adding header and footer, this function also stores the complete TCP
> + * packet in the last_packet member of the mcdserver_state. Then the packet
> + * gets send with the :c:func:`mcd_put_buffer` function.
> + * @buf: TCP packet data.
> + * @len: TCP packet length.
> + */
> +int mcd_put_packet_binary(const char *buf, int len);
> +
> +/**
> + * mcd_put_buffer() - Sends the buf as TCP packet with qemu_chr_fe_write_all.
> + *
> + * @buf: TCP packet data.
> + * @len: TCP packet length.
> + */
> +void mcd_put_buffer(const uint8_t *buf, int len);
> +
>  /**
>   * mcd_get_cpu_process() - Returns the process of the provided CPU.
>   *
> @@ -218,6 +262,82 @@ CPUState *mcd_first_attached_cpu(void);
>   */
>  CPUState *mcd_next_attached_cpu(CPUState *cpu);
>  
> +/**
> + * mcd_read_byte() - Resends the last packet if not acknowledged and extracts
> + * the data from a received TCP packet.
> + *
> + * In case the last sent packet was not acknowledged from the mcdstub,
> + * this function resends it.
> + * If it was acknowledged this function parses the incoming packet
> + * byte by byte.
> + * It extracts the data in the packet and sends an
> + * acknowledging response when finished. Then :c:func:`mcd_handle_packet` gets
> + * called.
> + * @ch: Character of the received TCP packet, which should be parsed.
> + */
> +void mcd_read_byte(uint8_t ch);
> +
> +/**
> + * mcd_handle_packet() - Evaluates the type of received packet and chooses the
> + * correct handler.
> + *
> + * This function takes the first character of the line_buf to determine the
> + * type of packet. Then it selects the correct handler function and parameter
> + * schema. With this info it calls :c:func:`run_cmd_parser`.
> + * @line_buf: TCP packet data.
> + */
> +int mcd_handle_packet(const char *line_buf);
> +
> +/**
> + * mcd_put_strbuf() - Calls :c:func:`mcd_put_packet` with the str_buf of the
> + * mcdserver_state.
> + */
> +void mcd_put_strbuf(void);
> +
> +/**
> + * run_cmd_parser() - Prepares the mcdserver_state before executing TCP packet
> + * functions.
> + *
> + * This function empties the str_buf and mem_buf of the mcdserver_state and
> + * then calls :c:func:`process_string_cmd`. In case this function fails, an
> + * empty TCP packet is sent back the MCD Shared Library.
> + * @data: TCP packet data.
> + * @cmd: Handler function (can be an array of functions).
> + */
> +void run_cmd_parser(const char *data, const MCDCmdParseEntry *cmd);
> +
> +/**
> + * process_string_cmd() - Collects all parameters from the data and calls the
> + * correct handler.
> + *
> + * The parameters are extracted with the :c:func:`cmd_parse_params function.
> + * This function selects the command in the cmds array, which fits the start of
> + * the data string. This way the correct commands is selected.
> + * @data: TCP packet data.
> + * @cmds: Array of possible commands.
> + * @num_cmds: Number of commands in the cmds array.
> + */
> +int process_string_cmd(void *user_ctx, const char *data,
> +    const MCDCmdParseEntry *cmds, int num_cmds);
> +
> +/**
> + * cmd_parse_params() - Extracts all parameters from a TCP packet.
> + *
> + * This function uses the schema parameter to determine which type of parameter
> + * to expect. It then extracts that parameter from the data and stores it in
> + * the params GArray.
> + * @data: TCP packet data.
> + * @schema: List of expected parameters for the packet.
> + * @params: GArray with all extracted parameters.
> + */
> +int cmd_parse_params(const char *data, const char *schema, GArray *params);
> +/**
> + * mcd_get_cpu_index() - Returns the internal CPU index plus one.
> + *
> + * @cpu: CPU of interest.
> + */
> +int mcd_get_cpu_index(CPUState *cpu);
> +

Aren't these all internal to mcdstub? We can keep the kdoc comments and
just make the functions static. 

>  /**
>   * mcd_get_cpu() - Returns the CPU the index i_cpu_index.
>   *
> @@ -237,3 +357,27 @@ CPUState *get_first_cpu_in_process(MCDProcess *process);
>   * @thread_id: ID of the desired CPU.
>   */
>  CPUState *find_cpu(uint32_t thread_id);
> +/* helpers */
> +
> +/**
> + * int_cmp() - Compares a and b and returns zero if they are equal.
> + *
> + * @a: Pointer to integer a.
> + * @b: Pointer to integer b.
> + */
> +int int_cmp(gconstpointer a, gconstpointer b);

This especially will clash with local definitions. 

> +/**
> + * atouint64_t() - Converts a string into a unsigned 64 bit integer.
> + *
> + * @in: Pointer to input string.
> + */
> +uint64_t atouint64_t(const char *in);
> +

No _t prefixes for functions please.

> +/**
> + * atouint32_t() - Converts a string into a unsigned 32 bit integer.
> + *
> + * @in: Pointer to input string.
> + */
> +uint32_t atouint32_t(const char *in);
> +
> +#endif /* MCDSTUB_INTERNALS_H */
> diff --git a/mcdstub/mcdstub.c b/mcdstub/mcdstub.c
> index 4cdf2e42ed..6900dcd0ea 100644
> --- a/mcdstub/mcdstub.c
> +++ b/mcdstub/mcdstub.c
> @@ -80,7 +80,14 @@ void init_query_cmds_table(MCDCmdParseEntry *mcd_query_cmds_table)
>  {
>      /* initalizes a list of all query commands */
>      int cmd_number = 0;
> +
> +void reset_mcdserver_state(void)
> +{
> +    g_free(mcdserver_state.processes);
> +    mcdserver_state.processes = NULL;
> +    mcdserver_state.process_num = 0;
>  }
> +
>  void create_processes(MCDState *s)
>  {
>      object_child_foreach(object_get_root(), find_cpu_clusters, s);
> @@ -243,6 +250,228 @@ void mcd_chr_receive(void *opaque, const uint8_t *buf, int size)
>      }
>  }
>  
> +void mcd_read_byte(uint8_t ch)
> +{
> +    uint8_t reply;
> +
> +    if (mcdserver_state.last_packet->len) {
> +        if (ch == TCP_NOT_ACKNOWLEDGED) {
> +            /* the previous packet was not akcnowledged */
> +            mcd_put_buffer(mcdserver_state.last_packet->data,
> +                mcdserver_state.last_packet->len);
> +        } else if (ch == TCP_ACKNOWLEDGED) {
> +            /* the previous packet was acknowledged */
> +        }
> +
> +        if (ch == TCP_ACKNOWLEDGED || ch == TCP_COMMAND_START) {
> +            /*
> +             * either acknowledged or a new communication starts
> +             * -> discard previous packet
> +             */
> +            g_byte_array_set_size(mcdserver_state.last_packet, 0);
> +        }
> +        if (ch != TCP_COMMAND_START) {
> +            /* skip to the next char */
> +            return;
> +        }
> +    }
> +
> +    switch (mcdserver_state.state) {
> +    case RS_IDLE:
> +        if (ch == TCP_COMMAND_START) {
> +            /* start of command packet */
> +            mcdserver_state.line_buf_index = 0;
> +            mcdserver_state.line_sum = 0;
> +            mcdserver_state.state = RS_GETLINE;
> +        }
> +        break;
> +    case RS_GETLINE:
> +        if (ch == TCP_COMMAND_END) {
> +            /* end of command */
> +            mcdserver_state.line_buf[mcdserver_state.line_buf_index++] = 0;
> +            mcdserver_state.state = RS_DATAEND;
> +        } else if (mcdserver_state.line_buf_index >=
> +            sizeof(mcdserver_state.line_buf) - 1) {
> +            /* the input string is too long for the linebuffer! */
> +            mcdserver_state.state = RS_IDLE;
> +        } else {
> +            /* copy the content to the line_buf */
> +            mcdserver_state.line_buf[mcdserver_state.line_buf_index++] = ch;
> +            mcdserver_state.line_sum += ch;
> +        }
> +        break;
> +    case RS_DATAEND:
> +        if (ch == TCP_WAS_NOT_LAST) {
> +            reply = TCP_ACKNOWLEDGED;
> +            mcd_put_buffer(&reply, 1);
> +            mcdserver_state.state = mcd_handle_packet(mcdserver_state.line_buf);
> +        } else if (ch == TCP_WAS_LAST) {
> +            reply = TCP_ACKNOWLEDGED;
> +            mcd_put_buffer(&reply, 1);
> +            mcdserver_state.state = mcd_handle_packet(mcdserver_state.line_buf);
> +        } else {
> +            /* not acknowledged! */
> +            reply = TCP_NOT_ACKNOWLEDGED;
> +            mcd_put_buffer(&reply, 1);
> +            /* waiting for package to get resent */
> +            mcdserver_state.state = RS_IDLE;
> +        }
> +        break;
> +    default:
> +        abort();
> +    }
> +}
> +
> +int mcd_handle_packet(const char *line_buf)
> +{
> +    /*
> +     * decides what function (handler) to call depending on
> +     * the first character in the line_buf
> +     */
> +    const MCDCmdParseEntry *cmd_parser = NULL;
> +
> +    switch (line_buf[0]) {
> +    default:
> +        /* command not supported */
> +        mcd_put_packet("");
> +        break;
> +    }
> +
> +    if (cmd_parser) {
> +        /* parse commands and run the selected handler function */
> +        run_cmd_parser(line_buf, cmd_parser);
> +    }
> +
> +    return RS_IDLE;
> +}
> +void run_cmd_parser(const char *data, const MCDCmdParseEntry *cmd)
> +{
> +    if (!data) {
> +        return;
> +    }
> +
> +    g_string_set_size(mcdserver_state.str_buf, 0);
> +    g_byte_array_set_size(mcdserver_state.mem_buf, 0);
> +
> +    if (process_string_cmd(NULL, data, cmd, 1)) {
> +        mcd_put_packet("");
> +    }
> +}
> +
> +uint64_t atouint64_t(const char *in)
> +{
> +    uint64_t res = 0;
> +    for (int i = 0; i < strlen(in); ++i) {
> +        const char c = in[i];
> +        res *= 10;
> +        res += c - '0';
> +    }
> +
> +    return res;
> +}
> +
> +uint32_t atouint32_t(const char *in)
> +{
> +    uint32_t res = 0;
> +    for (int i = 0; i < strlen(in); ++i) {
> +        const char c = in[i];
> +        res *= 10;
> +        res += c - '0';
> +    }
> +
> +    return res;
> +}

You could use qemu_strtou64 with error checking instead. We could add
qemu_strtou32 if needed.

> +
> +int cmd_parse_params(const char *data, const char *schema, GArray *params)
> +{
> +
> +    char data_buffer[64] = {0};
> +    const char *remaining_data = data;
> +
> +    for (int i = 0; i < strlen(schema); i++) {
> +        /* get correct part of data */
> +        char *separator = strchr(remaining_data, ARGUMENT_SEPARATOR);
> +
> +        if (separator) {
> +            /* multiple arguments */
> +            int seperator_index = (int)(separator - remaining_data);
> +            strncpy(data_buffer, remaining_data, seperator_index);
> +            data_buffer[seperator_index] = 0;
> +            /* update remaining data for the next run */
> +            remaining_data = &(remaining_data[seperator_index + 1]);
> +        } else {
> +            strncpy(data_buffer, remaining_data, strlen(remaining_data));
> +            data_buffer[strlen(remaining_data)] = 0;
> +        }
> +
> +        /* store right data */
> +        MCDCmdVariant this_param;
> +        switch (schema[i]) {
> +        case ARG_SCHEMA_STRING:
> +            /* this has to be the last argument */
> +            this_param.data = remaining_data;
> +            g_array_append_val(params, this_param);
> +            break;
> +        case ARG_SCHEMA_HEXDATA:
> +            g_string_printf(mcdserver_state.str_buf, "%s", data_buffer);
> +            break;
> +        case ARG_SCHEMA_INT:
> +            this_param.data_uint32_t = atouint32_t(data_buffer);
> +            g_array_append_val(params, this_param);
> +            break;
> +        case ARG_SCHEMA_UINT64_T:
> +            this_param.data_uint64_t = atouint64_t(data_buffer);
> +            g_array_append_val(params, this_param);
> +            break;
> +        case ARG_SCHEMA_QRYHANDLE:
> +            this_param.query_handle = atouint32_t(data_buffer);
> +            g_array_append_val(params, this_param);
> +            break;
> +        case ARG_SCHEMA_CORENUM:
> +            this_param.cpu_id = atouint32_t(data_buffer);
> +            g_array_append_val(params, this_param);
> +            break;
> +        default:
> +            return -1;
> +        }
> +    }
> +    return 0;
> +}
> +
> +int process_string_cmd(void *user_ctx, const char *data,
> +    const MCDCmdParseEntry *cmds, int num_cmds)
> +{
> +    int i;
> +    g_autoptr(GArray) params = g_array_new(false, true, sizeof(MCDCmdVariant));
> +
> +    if (!cmds) {
> +        return -1;
> +    }
> +
> +    for (i = 0; i < num_cmds; i++) {
> +        const MCDCmdParseEntry *cmd = &cmds[i];
> +        g_assert(cmd->handler && cmd->cmd);
> +
> +        /* continue if data and command are different */
> +        if (strncmp(data, cmd->cmd, strlen(cmd->cmd))) {
> +            continue;
> +        }
> +
> +        if (strlen(cmd->schema)) {
> +            /* extract data for parameters */
> +            if (cmd_parse_params(&data[strlen(cmd->cmd)], cmd->schema, params))
> +            {
> +                return -1;
> +            }
> +        }
> +
> +        /* call handler */
> +        cmd->handler(params, user_ctx);
> +        return 0;
> +    }
> +
> +    return -1;
> +}
>  
>  void mcd_chr_event(void *opaque, QEMUChrEvent event)
>  {
> @@ -281,6 +510,43 @@ void mcd_sigterm_handler(int signal)
>  }
>  #endif
>  
> +int mcd_put_packet(const char *buf)
> +{
> +    return mcd_put_packet_binary(buf, strlen(buf));
> +}
> +
> +void mcd_put_strbuf(void)
> +{
> +    mcd_put_packet(mcdserver_state.str_buf->str);
> +}
> +
> +int mcd_put_packet_binary(const char *buf, int len)
> +{
> +    g_byte_array_set_size(mcdserver_state.last_packet, 0);
> +    g_byte_array_append(mcdserver_state.last_packet,
> +        (const uint8_t *) (char[2]) { TCP_COMMAND_START, '\0' }, 1);
> +    g_byte_array_append(mcdserver_state.last_packet,
> +        (const uint8_t *) buf, len);
> +    g_byte_array_append(mcdserver_state.last_packet,
> +        (const uint8_t *) (char[2]) { TCP_COMMAND_END, '\0' }, 1);
> +    g_byte_array_append(mcdserver_state.last_packet,
> +        (const uint8_t *) (char[2]) { TCP_WAS_LAST, '\0' }, 1);
> +
> +    mcd_put_buffer(mcdserver_state.last_packet->data,
> +        mcdserver_state.last_packet->len);
> +    return 0;
> +}
> +
> +void mcd_put_buffer(const uint8_t *buf, int len)
> +{
> +    qemu_chr_fe_write_all(&mcdserver_system_state.chr, buf, len);
> +}
> +
> +MCDProcess *mcd_get_cpu_process(CPUState *cpu)
> +{
> +    return mcd_get_process(mcd_get_cpu_pid(cpu));
> +}
> +
>  uint32_t mcd_get_cpu_pid(CPUState *cpu)
>  {
>      if (cpu->cluster_index == UNASSIGNED_CLUSTER_INDEX) {
> @@ -381,3 +647,14 @@ CPUState *find_cpu(uint32_t thread_id)
>      return NULL;
>  }
>  
> +
> +int int_cmp(gconstpointer a, gconstpointer b)
> +{
> +    int int_a = *(int *)a;
> +    int int_b = *(int *)b;
> +    if (int_a == int_b) {
> +        return 0;
> +    } else {
> +        return 1;
> +    }
> +}

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro
Re: [PATCH v3 05/20] mcdstub: tcp packet processing added
Posted by Philippe Mathieu-Daudé 1 year ago
Hi Nicolas,

On 7/11/23 14:03, Nicolas Eder wrote:
> ---
>   include/mcdstub/mcdstub.h | 144 ++++++++++++++++++++
>   mcdstub/mcdstub.c         | 277 ++++++++++++++++++++++++++++++++++++++
>   2 files changed, 421 insertions(+)
> 
> diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
> index 36058157ae..1461d0e1cb 100644
> --- a/include/mcdstub/mcdstub.h
> +++ b/include/mcdstub/mcdstub.h
> @@ -25,6 +25,21 @@ typedef struct MCDProcess {
>       char target_xml[1024];
>   } MCDProcess;
>   
> +typedef void (*MCDCmdHandler)(GArray *params, void *user_ctx);
> +typedef struct MCDCmdParseEntry {
> +    MCDCmdHandler handler;
> +    const char *cmd;
> +    char schema[CMD_SCHEMA_LENGTH];
> +} MCDCmdParseEntry;
> +
> +typedef union MCDCmdVariant {
> +    const char *data;
> +    uint32_t data_uint32_t;
> +    uint64_t data_uint64_t;
> +    uint32_t query_handle;
> +    uint32_t cpu_id;
> +} MCDCmdVariant;

[...]

Can we add an URL to the spec in this file header?

It should be relatively easy to add a qtest for MCB, with raw
packets to:
- read memory
- write memory
- read register
- write register
- register breakpoint
- run vcpu
- stop vcpu

Maybe we can write a tiny code that loop incrementing a static
variable, run for few ms and check the variable got incremented?
That would exercise most of these commands.

Regards,

Phil.
Re: [PATCH v3 05/20] mcdstub: tcp packet processing added
Posted by nicolas.eder@lauterbach.com 1 year ago
Hi Phil,

as of right now, there is no official spec for the TCP packet contents. 
However, in the client code it is rather easy see how the packets are 
constructed. I hope I will get the client code online by the end of the 
week.
I will also look into qtest. We definetely need a test like this and I 
think your example is a good idea.

Best Regards,
Nicolas


On 08/11/2023 15:33, Philippe Mathieu-Daudé wrote:
> Hi Nicolas,
>
> On 7/11/23 14:03, Nicolas Eder wrote:
>> ---
>>   include/mcdstub/mcdstub.h | 144 ++++++++++++++++++++
>>   mcdstub/mcdstub.c         | 277 ++++++++++++++++++++++++++++++++++++++
>>   2 files changed, 421 insertions(+)
>>
>> diff --git a/include/mcdstub/mcdstub.h b/include/mcdstub/mcdstub.h
>> index 36058157ae..1461d0e1cb 100644
>> --- a/include/mcdstub/mcdstub.h
>> +++ b/include/mcdstub/mcdstub.h
>> @@ -25,6 +25,21 @@ typedef struct MCDProcess {
>>       char target_xml[1024];
>>   } MCDProcess;
>>   +typedef void (*MCDCmdHandler)(GArray *params, void *user_ctx);
>> +typedef struct MCDCmdParseEntry {
>> +    MCDCmdHandler handler;
>> +    const char *cmd;
>> +    char schema[CMD_SCHEMA_LENGTH];
>> +} MCDCmdParseEntry;
>> +
>> +typedef union MCDCmdVariant {
>> +    const char *data;
>> +    uint32_t data_uint32_t;
>> +    uint64_t data_uint64_t;
>> +    uint32_t query_handle;
>> +    uint32_t cpu_id;
>> +} MCDCmdVariant;
>
> [...]
>
> Can we add an URL to the spec in this file header?
>
> It should be relatively easy to add a qtest for MCB, with raw
> packets to:
> - read memory
> - write memory
> - read register
> - write register
> - register breakpoint
> - run vcpu
> - stop vcpu
>
> Maybe we can write a tiny code that loop incrementing a static
> variable, run for few ms and check the variable got incremented?
> That would exercise most of these commands.
>
> Regards,
>
> Phil.