diff mbox series

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

Message ID 20231107130323.4126-6-nicolas.eder@lauterbach.com (mailing list archive)
State New, archived
Headers show
Series first version of mcdstub | expand

Commit Message

nicolas.eder@lauterbach.com Nov. 7, 2023, 1:03 p.m. UTC
---
 include/mcdstub/mcdstub.h | 144 ++++++++++++++++++++
 mcdstub/mcdstub.c         | 277 ++++++++++++++++++++++++++++++++++++++
 2 files changed, 421 insertions(+)

Comments

Philippe Mathieu-Daudé Nov. 8, 2023, 2:33 p.m. UTC | #1
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.
nicolas.eder@lauterbach.com Nov. 13, 2023, 9:18 a.m. UTC | #2
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.
Alex Bennée Nov. 29, 2023, 4:25 p.m. UTC | #3
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;
> +    }
> +}
diff mbox series

Patch

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;
+    }
+}