Directly map MCD register groups to GDB features lists

Signed-off-by: Mario Fleischmann <mario.fleischm...@lauterbach.com>
---
 mcd/libmcd_qapi.c         |  48 +++++++
 mcd/libmcd_qapi.h         |   8 ++
 mcd/mcdserver.c           | 237 ++++++++++++++++++++++++++++--
 mcd/mcdstub_qapi.c        |  93 ++++++++++++
 qapi/mcd.json             | 296 ++++++++++++++++++++++++++++++++++++++
 tests/qtest/libmcd-test.c |  40 ++++++
 tests/qtest/libmcd-test.h |   6 +
 tests/qtest/mcd-test.c    |  85 +++++++++++
 8 files changed, 803 insertions(+), 10 deletions(-)

diff --git a/mcd/libmcd_qapi.c b/mcd/libmcd_qapi.c
index 0421152705..99177b2fd2 100644
--- a/mcd/libmcd_qapi.c
+++ b/mcd/libmcd_qapi.c
@@ -147,3 +147,51 @@ MCDMemspace *marshal_mcd_memspace(const mcd_memspace_st 
*mem_space)
 
     return marshal;
 }
+
+MCDRegisterGroup *marshal_mcd_register_group(
+    const mcd_register_group_st *reg_group)
+{
+    MCDRegisterGroup *marshal = g_malloc0(sizeof(*marshal));
+
+    *marshal = (MCDRegisterGroup) {
+        .reg_group_id = reg_group->reg_group_id,
+        .reg_group_name = g_strdup(reg_group->reg_group_name),
+        .n_registers = reg_group->n_registers,
+    };
+
+    return marshal;
+}
+
+MCDAddr *marshal_mcd_addr(const mcd_addr_st *addr)
+{
+    MCDAddr *marshal = g_malloc0(sizeof(*marshal));
+
+    *marshal = (MCDAddr) {
+        .address = addr->address,
+        .mem_space_id = addr->mem_space_id,
+        .addr_space_id = addr->addr_space_id,
+        .addr_space_type = addr->addr_space_type,
+    };
+
+    return marshal;
+}
+
+MCDRegisterInfo *marshal_mcd_register_info(const mcd_register_info_st 
*reg_info)
+{
+    MCDRegisterInfo *marshal = g_malloc0(sizeof(*marshal));
+
+    *marshal = (MCDRegisterInfo) {
+        .addr = marshal_mcd_addr(&reg_info->addr),
+        .reg_group_id = reg_info->reg_group_id,
+        .regname = g_strdup(reg_info->regname),
+        .regsize = reg_info->regsize,
+        .core_mode_mask_read = reg_info->core_mode_mask_read,
+        .core_mode_mask_write = reg_info->core_mode_mask_write,
+        .side_effects_read = reg_info->has_side_effects_read,
+        .side_effects_write = reg_info->has_side_effects_write,
+        .reg_type = reg_info->reg_type,
+        .hw_thread_id = reg_info->hw_thread_id,
+    };
+
+    return marshal;
+}
diff --git a/mcd/libmcd_qapi.h b/mcd/libmcd_qapi.h
index 7e874dec25..edcc6d0b7c 100644
--- a/mcd/libmcd_qapi.h
+++ b/mcd/libmcd_qapi.h
@@ -27,6 +27,14 @@ MCDCoreConInfo *marshal_mcd_core_con_info(const 
mcd_core_con_info_st *con_info);
 
 MCDMemspace *marshal_mcd_memspace(const mcd_memspace_st *mem_space);
 
+MCDRegisterGroup *marshal_mcd_register_group(
+    const mcd_register_group_st *reg_group);
+
+MCDAddr *marshal_mcd_addr(const mcd_addr_st *addr);
+
+MCDRegisterInfo *marshal_mcd_register_info(
+    const mcd_register_info_st *reg_info);
+
 mcd_api_version_st unmarshal_mcd_api_version(MCDAPIVersion *api_version);
 
 mcd_core_con_info_st unmarshal_mcd_core_con_info(MCDCoreConInfo *con_info);
diff --git a/mcd/mcdserver.c b/mcd/mcdserver.c
index f924bf3799..f0cda23981 100644
--- a/mcd/mcdserver.c
+++ b/mcd/mcdserver.c
@@ -13,6 +13,7 @@
 #include "mcd_api.h"
 #include "hw/boards.h"
 #include "exec/tswap.h"
+#include "exec/gdbstub.h"
 
 /* Custom memory space type */
 static const mcd_mem_type_et MCD_MEM_SPACE_IS_SECURE = 0x00010000;
@@ -55,12 +56,14 @@ static const mcd_error_info_st MCD_ERROR_NONE = {
 /**
  * struct mcdcore_state - State of a core.
  *
- * @last_error:    Error info of most recent executed core-related function.
- * @custom_error:  Reserves memory for custom MCD errors.
- * @info:          Core connection information.
- * @open_core:     Open core instance as allocated in mcd_open_core_f().
- * @cpu:           QEMU's internal CPU handle.
- * @memory_spaces: Memory spaces as queried by mcd_qry_mem_spaces_f().
+ * @last_error:      Error info of most recent executed core-related function.
+ * @custom_error:    Reserves memory for custom MCD errors.
+ * @info:            Core connection information.
+ * @open_core:       Open core instance as allocated in mcd_open_core_f().
+ * @cpu:             QEMU's internal CPU handle.
+ * @memory_spaces:   Memory spaces as queried by mcd_qry_mem_spaces_f().
+ * @register_groups: Register groups as queried by mcd_qry_reg_groups_f().
+ * @registers:       Registers as queried by mcd_qry_reg_map_f().
  *
  * MCD is mainly being used on the core level:
  * After the initial query functions, a core connection is opened in
@@ -77,6 +80,8 @@ typedef struct mcdcore_state {
     mcd_core_st *open_core;
     CPUState *cpu;
     GArray *memory_spaces;
+    GArray *register_groups;
+    GArray *registers;
 } mcdcore_state;
 
 /**
@@ -267,6 +272,10 @@ mcd_return_et mcd_open_server_f(const char *system_key,
             .open_core = NULL,
             .cpu = cpu,
             .memory_spaces = g_array_new(false, true, sizeof(mcd_memspace_st)),
+            .register_groups = g_array_new(false, true,
+                                           sizeof(mcd_register_group_st)),
+            .registers = g_array_new(false, true,
+                                     sizeof(mcd_register_info_st)),
         };
         pstrcpy(c.info.core, MCD_UNIQUE_NAME_LEN, cpu_model);
         g_array_append_val(g_server_state.cores, c);
@@ -306,6 +315,10 @@ mcd_return_et mcd_close_server_f(const mcd_server_st 
*server)
         if (c->open_core) {
             mcd_close_core_f(c->open_core);
         }
+
+        g_array_free(c->memory_spaces, true);
+        g_array_free(c->register_groups, true);
+        g_array_free(c->registers, true);
     }
 
     g_array_free(g_server_state.cores, true);
@@ -538,6 +551,59 @@ static mcd_return_et query_memspaces(mcdcore_state 
*core_state)
     return MCD_RET_ACT_NONE;
 }
 
+static mcd_return_et query_registers(mcdcore_state *core_state)
+{
+    GArray *gdb_regs = core_state->cpu->gdb_regs;
+    assert(gdb_regs);
+
+    g_array_set_size(core_state->register_groups, 0);
+    g_array_set_size(core_state->registers, 0);
+
+    for (int feature_id = 0; feature_id < gdb_regs->len; feature_id++) {
+        GDBRegisterState *f = &g_array_index(gdb_regs, GDBRegisterState,
+                                             feature_id);
+        /* register group ID 0 is reserved */
+        uint32_t group_id = feature_id + 1;
+        uint32_t num_regs = 0;
+
+        GByteArray *mem_buf = g_byte_array_new();
+        for (int i = 0; i < f->feature->num_regs; i++) {
+            const char *name = f->feature->regs[i];
+            if (name) {
+                int reg_id = f->base_reg + i;
+                int bitsize = gdb_read_register(core_state->cpu,
+                                                mem_buf, reg_id) * 8;
+                mcd_register_info_st r = {
+                    .addr = {
+                        .address = (uint64_t) reg_id,
+                        /* memory space "GDB Registers" */
+                        .mem_space_id = core_state->memory_spaces->len,
+                        .addr_space_type = MCD_NOTUSED_ID,
+                    },
+                    .reg_group_id = group_id,
+                    .regsize = (uint32_t) bitsize,
+                    .reg_type = MCD_REG_TYPE_SIMPLE,
+                    /* ID 0 reserved */
+                    .hw_thread_id = core_state->info.core_id + 1,
+                };
+                strncpy(r.regname, name, MCD_REG_NAME_LEN - 1);
+                g_array_append_val(core_state->registers, r);
+                num_regs++;
+            }
+        }
+        g_byte_array_free(mem_buf, true);
+
+        mcd_register_group_st rg = {
+            .reg_group_id = group_id,
+            .n_registers = num_regs,
+        };
+        strncpy(rg.reg_group_name, f->feature->name, MCD_REG_NAME_LEN - 1);
+        g_array_append_val(core_state->register_groups, rg);
+    }
+
+    return MCD_RET_ACT_NONE;
+}
+
 mcd_return_et mcd_open_core_f(const mcd_core_con_info_st *core_con_info,
                               mcd_core_st **core)
 {
@@ -583,6 +649,10 @@ mcd_return_et mcd_open_core_f(const mcd_core_con_info_st 
*core_con_info,
         return g_server_state.last_error->return_status;
     }
 
+    if (query_registers(core_state) != MCD_RET_ACT_NONE) {
+        return g_server_state.last_error->return_status;
+    }
+
     *core = g_malloc(sizeof(mcd_core_st));
     info = g_malloc(sizeof(mcd_core_con_info_st));
     *info = *core_con_info;
@@ -626,6 +696,8 @@ mcd_return_et mcd_close_core_f(const mcd_core_st *core)
     core_state->open_core = NULL;
     core_state->cpu = NULL;
     g_array_set_size(core_state->memory_spaces, 0);
+    g_array_set_size(core_state->register_groups, 0);
+    g_array_set_size(core_state->registers, 0);
 
     g_server_state.last_error = &MCD_ERROR_NONE;
     return g_server_state.last_error->return_status;
@@ -771,16 +843,161 @@ mcd_return_et mcd_qry_reg_groups_f(const mcd_core_st 
*core,
                                    uint32_t *num_reg_groups,
                                    mcd_register_group_st *reg_groups)
 {
-    g_server_state.last_error = &MCD_ERROR_NOT_IMPLEMENTED;
-    return g_server_state.last_error->return_status;
+    uint32_t i;
+    mcdcore_state *core_state;
+
+    if (!core || !num_reg_groups) {
+        g_server_state.last_error = &MCD_ERROR_INVALID_NULL_PARAM;
+        return g_server_state.last_error->return_status;
+    }
+
+    core_state = find_core(core->core_con_info);
+    if (!core_state || core_state->open_core != core) {
+        g_server_state.last_error = &MCD_ERROR_UNKNOWN_CORE;
+        return g_server_state.last_error->return_status;
+    }
+
+    g_assert(core_state->register_groups);
+
+    if (core_state->register_groups->len == 0) {
+        core_state->custom_error = (mcd_error_info_st) {
+            .return_status = MCD_RET_ACT_HANDLE_ERROR,
+            .error_code = MCD_ERR_NO_REG_GROUPS,
+            .error_events = MCD_ERR_EVT_NONE,
+            .error_str = "",
+        };
+        core_state->last_error = &core_state->custom_error;
+        return core_state->last_error->return_status;
+    }
+
+    if (*num_reg_groups == 0) {
+        *num_reg_groups = core_state->register_groups->len;
+        core_state->last_error = &MCD_ERROR_NONE;
+        return core_state->last_error->return_status;
+    }
+
+    if (start_index >= core_state->register_groups->len) {
+        core_state->custom_error = (mcd_error_info_st) {
+            .return_status = MCD_RET_ACT_HANDLE_ERROR,
+            .error_code = MCD_ERR_PARAM,
+            .error_events = MCD_ERR_EVT_NONE,
+            .error_str = "start_index exceeds the number of register groups",
+        };
+        core_state->last_error = &core_state->custom_error;
+        return core_state->last_error->return_status;
+    }
+
+    if (!reg_groups) {
+        core_state->last_error = &MCD_ERROR_INVALID_NULL_PARAM;
+        return core_state->last_error->return_status;
+    }
+
+    for (i = 0; i < *num_reg_groups &&
+         start_index + i < core_state->register_groups->len; i++) {
+
+        reg_groups[i] = g_array_index(core_state->register_groups,
+                                      mcd_register_group_st, start_index + i);
+    }
+
+    *num_reg_groups = i;
+
+    core_state->last_error = &MCD_ERROR_NONE;
+    return core_state->last_error->return_status;
 }
 
 mcd_return_et mcd_qry_reg_map_f(const mcd_core_st *core, uint32_t reg_group_id,
                                 uint32_t start_index, uint32_t *num_regs,
                                 mcd_register_info_st *reg_info)
 {
-    g_server_state.last_error = &MCD_ERROR_NOT_IMPLEMENTED;
-    return g_server_state.last_error->return_status;
+    mcdcore_state *core_state;
+    bool query_all_regs = reg_group_id == 0;
+    bool query_num_only;
+
+    if (!core || !num_regs) {
+        g_server_state.last_error = &MCD_ERROR_INVALID_NULL_PARAM;
+        return g_server_state.last_error->return_status;
+    }
+
+    query_num_only = *num_regs == 0;
+
+    core_state = find_core(core->core_con_info);
+    if (!core_state || core_state->open_core != core) {
+        g_server_state.last_error = &MCD_ERROR_UNKNOWN_CORE;
+        return g_server_state.last_error->return_status;
+    }
+
+    if (core_state->register_groups->len == 0 ||
+        reg_group_id > core_state->register_groups->len) {
+
+        core_state->custom_error = (mcd_error_info_st) {
+            .return_status = MCD_RET_ACT_HANDLE_ERROR,
+            .error_code = MCD_ERR_REG_GROUP_ID,
+            .error_events = MCD_ERR_EVT_NONE,
+            .error_str = "",
+        };
+        core_state->last_error = &core_state->custom_error;
+        return core_state->last_error->return_status;
+    }
+
+    /*
+     * Depending on reg_group_id, start_index refers either to the total list 
of
+     * register or a single register group.
+     */
+
+    if (query_all_regs) {
+        if (start_index >= core_state->registers->len) {
+            core_state->custom_error = (mcd_error_info_st) {
+                .return_status = MCD_RET_ACT_HANDLE_ERROR,
+                .error_code = MCD_ERR_PARAM,
+                .error_events = MCD_ERR_EVT_NONE,
+                .error_str = "start_index exceeds the number of registers",
+            };
+            core_state->last_error = &core_state->custom_error;
+            return core_state->last_error->return_status;
+        };
+
+        if (*num_regs == 0) {
+            *num_regs = core_state->registers->len;
+        } else if (*num_regs > core_state->registers->len - start_index) {
+            *num_regs = core_state->registers->len - start_index;
+        }
+    } else {
+        mcd_register_group_st *rg = &g_array_index(core_state->register_groups,
+            mcd_register_group_st, reg_group_id - 1);
+
+        if (start_index > rg->n_registers) {
+            core_state->custom_error = (mcd_error_info_st) {
+                .return_status = MCD_RET_ACT_HANDLE_ERROR,
+                .error_code = MCD_ERR_PARAM,
+                .error_events = MCD_ERR_EVT_NONE,
+                .error_str = "start_index exceeds the number of registers",
+            };
+            core_state->last_error = &core_state->custom_error;
+            return core_state->last_error->return_status;
+        }
+
+        if (*num_regs == 0) {
+            *num_regs = rg->n_registers;
+        } else if (*num_regs > rg->n_registers - start_index) {
+            *num_regs = rg->n_registers - start_index;
+        }
+
+        for (uint32_t rg_id = 0; rg_id < reg_group_id - 1; rg_id++) {
+            mcd_register_group_st *prev_rg = &g_array_index(
+                core_state->register_groups, mcd_register_group_st, rg_id);
+            start_index += prev_rg->n_registers;
+        }
+    }
+
+    if (!query_num_only) {
+        for (uint32_t i = 0; i < *num_regs; i++) {
+            reg_info[i] = g_array_index(
+                core_state->registers, mcd_register_info_st, start_index + i);
+        }
+    }
+
+    core_state->last_error = &MCD_ERROR_NONE;
+    return core_state->last_error->return_status;
 }
 
 mcd_return_et mcd_qry_reg_compound_f(const mcd_core_st *core,
diff --git a/mcd/mcdstub_qapi.c b/mcd/mcdstub_qapi.c
index 437d2c2e3e..6aa72b025c 100644
--- a/mcd/mcdstub_qapi.c
+++ b/mcd/mcdstub_qapi.c
@@ -412,3 +412,96 @@ MCDQryMemSpacesResult *qmp_mcd_qry_mem_spaces(uint32_t 
core_uid,
     g_stub_state.on_error_ask_server = true;
     return result;
 }
+
+MCDQryRegGroupsResult *qmp_mcd_qry_reg_groups(uint32_t core_uid,
+                                              uint32_t start_index,
+                                              uint32_t num_reg_groups,
+                                              Error **errp)
+{
+    MCDRegisterGroupList **tailp;
+    MCDRegisterGroup *rg;
+    mcd_register_group_st *reg_groups = NULL;
+    bool query_num_only = num_reg_groups == 0;
+    MCDQryRegGroupsResult *result = g_malloc0(sizeof(*result));
+    mcd_core_st *core = NULL;
+
+    result->return_status = retrieve_open_core(core_uid, &core);
+    if (result->return_status != MCD_RET_ACT_NONE) {
+        g_stub_state.on_error_ask_server = false;
+        return result;
+    }
+
+    if (!query_num_only) {
+        reg_groups = g_malloc0(num_reg_groups * sizeof(*reg_groups));
+    }
+
+    result->return_status = mcd_qry_reg_groups_f(core, start_index,
+                                                 &num_reg_groups, reg_groups);
+
+    if (result->return_status == MCD_RET_ACT_NONE) {
+        result->has_num_reg_groups = true;
+        result->num_reg_groups = num_reg_groups;
+        if (!query_num_only) {
+            result->has_reg_groups = true;
+            tailp = &(result->reg_groups);
+            for (uint32_t i = 0; i < num_reg_groups; i++) {
+                rg = marshal_mcd_register_group(reg_groups + i);
+                QAPI_LIST_APPEND(tailp, rg);
+            }
+        }
+    }
+
+    if (!query_num_only) {
+        g_free(reg_groups);
+    }
+
+    g_stub_state.on_error_ask_server = true;
+    return result;
+}
+
+MCDQryRegMapResult *qmp_mcd_qry_reg_map(uint32_t core_uid,
+                                        uint32_t reg_group_id,
+                                        uint32_t start_index, uint32_t 
num_regs,
+                                        Error **errp)
+{
+    MCDRegisterInfoList **tailp;
+    MCDRegisterInfo *r;
+    mcd_register_info_st *regs = NULL;
+    bool query_num_only = num_regs == 0;
+    MCDQryRegMapResult *result = g_malloc0(sizeof(*result));
+    mcd_core_st *core = NULL;
+
+    result->return_status = retrieve_open_core(core_uid, &core);
+    if (result->return_status != MCD_RET_ACT_NONE) {
+        g_stub_state.on_error_ask_server = false;
+        return result;
+    }
+
+    if (!query_num_only) {
+        regs = g_malloc0(num_regs * sizeof(*regs));
+    }
+
+    result->return_status = mcd_qry_reg_map_f(core, reg_group_id,
+                                              start_index, &num_regs,
+                                              regs);
+
+    if (result->return_status == MCD_RET_ACT_NONE) {
+        result->has_num_regs = true;
+        result->num_regs = num_regs;
+        if (!query_num_only) {
+            result->has_reg_info = true;
+            tailp = &(result->reg_info);
+            for (uint32_t i = 0; i < num_regs; i++) {
+                r = marshal_mcd_register_info(regs + i);
+                QAPI_LIST_APPEND(tailp, r);
+            }
+        }
+    }
+
+    if (!query_num_only) {
+        g_free(regs);
+    }
+
+    g_stub_state.on_error_ask_server = true;
+    return result;
+}
diff --git a/qapi/mcd.json b/qapi/mcd.json
index 214933e279..936016de45 100644
--- a/qapi/mcd.json
+++ b/qapi/mcd.json
@@ -215,6 +215,98 @@
     'core-mode-mask-write'    : 'uint32' } }
 
 
+##
+# @MCDRegisterGroup:
+#
+# Structure type containing register group information.
+#
+# @reg-group-id:   Contains the ID of this register group. A register group ID
+#                  must be unique within the scope of a target core. ID '0' is
+#                  reserved.
+# @reg-group-name: The name of a register group. A register group name cannot
+#                  be longer than MCD_REG_NAME_LEN characters (use
+#                  representative names).
+# @n-registers:    Number of registers part of this group.
+#
+# Since: 9.1
+##
+{ 'struct': 'MCDRegisterGroup',
+  'data': {
+    'reg-group-id'  : 'uint32',
+    'reg-group-name': 'str',
+    'n-registers'   : 'uint32' } }
+
+
+##
+# @MCDAddr:
+#
+# Structure type containing a completely resolved logical or physical memory
+# address.
+#
+# @address:         Address value within a memory space, expressed in bytes.
+# @mem-space-id:    ID of the memory space associated with this address, e.g. a
+#                   program memory, a data memory or registers .
+# @addr-space-id:   ID of the address space in which this address is valid.
+# @addr-space-type: Type of the address space in which this address is valid.
+#
+# Since: 9.1
+##
+{ 'struct': 'MCDAddr',
+  'data': {
+    'address'        : 'uint64',
+    'mem-space-id'   : 'uint32',
+    'addr-space-id'  : 'uint32',
+    'addr-space-type': 'uint32' } }
+
+
+##
+# @MCDRegisterInfo:
+#
+# Structure type containing register information for a single register.
+#
+# @addr:                   Either the address of a memory mapped register or
+#                          the register address in a dedicated
+#                          "register memory space"
+# @reg-group-id:           ID of the group this register belongs to.
+# @regname:                The name of a register. A register name cannot be
+#                          longer than MCD_REG_NAME_LEN characters (use
+#                          representative names).
+# @regsize:                Register size in bits.
+# @core-mode-mask-read:    Mask of core modes for which read accesses are
+#                          impossible. A set bit indicates that read accesses
+#                          are denied in this mode. Bit 0 represents core mode
+#                          '1', bit 31 represents core mode 32. Overrides
+#                          @core-mode-mask-read of the corresponding
+#                          mcd_memspace_st.
+# @core-mode-mask-write:   Mask of core modes for which write accesses are
+#                          impossible. A set bit indicates that write accesses
+#                          are denied in this mode. Bit 0 represents core mode
+#                          '1', bit 31 represents core mode '32'. Overrides
+#                          @core-mode-mask-write of the corresponding
+#                          mcd_memspace_st.
+# @side-effects-read:      Reading this register can trigger side effects.
+# @side-effects-write:     Writing this register can trigger side effects.
+# @reg-type:               Register type (simple, compound or partial)
+# @hw-thread-id:           Hardware thread ID this register belongs to. The ID
+#                          must be set to '0' if the register is not assigned
+#                          to a hardware thread.
+#
+# Since: 9.1
+##
+{ 'struct': 'MCDRegisterInfo',
+  'data': {
+    'addr'                : 'MCDAddr',
+    'reg-group-id'        : 'uint32',
+    'regname'             : 'str',
+    'regsize'             : 'uint32',
+    'core-mode-mask-read' : 'uint32',
+    'core-mode-mask-write': 'uint32',
+    'side-effects-read'   : 'bool',
+    'side-effects-write'  : 'bool',
+    'reg-type'            : 'uint32',
+    'hw-thread-id'        : 'uint32' } }
+
+
 ##
 # == Target Initialization API
 ##
@@ -1077,3 +1169,207 @@
      'start-index'   : 'uint32',
      'num-mem-spaces': 'uint32' },
   'returns': 'MCDQryMemSpacesResult' }
+
+
+##
+# @MCDQryRegGroupsResult:
+#
+# Return value of @mcd-qry-reg-groups.
+#
+# @return-status:  Return code.
+# @num-reg-groups: Number of returned register groups. In case the input value
+#                  of @num-reg-groups is '0', this is the number of all
+#                  available register groups for the selected core.
+# @reg-groups:     Register group information.
+#
+# Since: 9.1
+##
+{ 'struct': 'MCDQryRegGroupsResult',
+  'data': {
+    'return-status'  : 'uint32',
+    '*num-reg-groups': 'uint32',
+    '*reg-groups'    : [ 'MCDRegisterGroup' ] }}
+
+
+##
+# @mcd-qry-reg-groups:
+#
+# Function querying the register groups defined for a particular component.
+#
+# @core-uid:       Unique ID of the core the calling function addresses.
+# @start-index:    Start index of the requested register groups. This refers
+#                  to an internal list of the target side implementation.
+# @num-reg-groups: Number of register groups, information is requested of. If
+#                  it is set to '0', no register groups information is returned
+#                  but the number of all available register groups for the
+#                  selected core.
+#
+# Returns: @MCDQryRegGroupsResult
+#
+# Since: 9.1
+##
+{ 'command': 'mcd-qry-reg-groups',
+  'data': {
+    'core-uid'      : 'uint32',
+    'start-index'   : 'uint32',
+    'num-reg-groups': 'uint32' },
+  'returns': 'MCDQryRegGroupsResult' }
+
+
+##
+# @MCDQryRegMapResult:
+#
+# Return value of @mcd-qry-reg-map.
+#
+# @return-status: Return code.
+# @num-regs:      Number of returned registers. In case the input value of
+#                 @num-regs is '0', this is the number of all available 
register
+#                 for the selected register group.
+# @reg-info:      Register information.
+#
+# Since: 9.1
+##
+{ 'struct': 'MCDQryRegMapResult',
+  'data': {
+    'return-status': 'uint32',
+    '*num-regs'    : 'uint32',
+    '*reg-info'    : [ 'MCDRegisterInfo' ] }}
+
+##
+# @mcd-qry-reg-map:
+#
+# Function querying the register information of a particular register group.
+#
+# @core-uid:     Unique ID of the core the calling function addresses.
+# @reg-group-id: ID of the register group detailed register information is
+#                requested for.
+# @start-index:  Start index of the requested registers. This refers to an
+#                internal list of the target side implementation.
+# @num-regs:     Number of registers, information is requested of. If it
+#                is set to '0', no register information is returned but the
+#                number of all available registers within for the selected
+#                register group.
+#
+# Returns: @MCDQryRegMapResult
+#
+# Since: 9.1
+#
+# .. qmp-example::
+#
+#     -> { "execute": "mcd-qry-reg-groups",
+#          "arguments": { "core-uid": 1,
+#                         "start-index": 0,
+#                         "num-reg-groups": 3 } }
+#     <- {
+#            "return": {
+#                "reg-groups": [
+#                    {
+#                        "reg-group-id": 1,
+#                        "reg-group-name": "org.gnu.gdb.aarch64.core",
+#                        "n-registers": 34
+#                    },
+#                    {
+#                        "reg-group-id": 2,
+#                        "reg-group-name": "org.gnu.gdb.aarch64.fpu",
+#                        "n-registers": 34
+#                    },
+#                    {
+#                        "reg-group-id": 3,
+#                        "reg-group-name": "org.qemu.gdb.arm.sys.regs",
+#                        "n-registers": 210
+#                    }
+#                ],
+#                "return-status": 0,
+#                "num-reg-groups": 3
+#            }
+#        }
+#     -> { "execute": "mcd-qry-reg-map",
+#          "arguments": { "core-uid": 1,
+#                         "reg-group-id": 1,
+#                         "start-index": 0,
+#                         "num-regs": 34 } }
+#     <- {
+#            "return": {
+#                "reg-info": [
+#                    {
+#                        "reg-group-id": 1,
+#                        "regname": "x0",
+#                        "side-effects-read": false,
+#                        "addr": {
+#                            "mem-space-id": 5,
+#                            "addr-space-id": 0,
+#                            "address": 0,
+#                            "addr-space-type": 0
+#                        },
+#                        "reg-type": 0,
+#                        "core-mode-mask-write": 0,
+#                        "core-mode-mask-read": 0,
+#                        "regsize": 64,
+#                        "hw-thread-id": 0,
+#                        "side-effects-write": false
+#                    },
+#                    {
+#                        "reg-group-id": 1,
+#                        "regname": "x1",
+#                        "side-effects-read": false,
+#                        "addr": {
+#                            "mem-space-id": 5,
+#                            "addr-space-id": 0,
+#                            "address": 1,
+#                            "addr-space-type": 0
+#                        },
+#                        "reg-type": 0,
+#                        "core-mode-mask-write": 0,
+#                        "core-mode-mask-read": 0,
+#                        "regsize": 64,
+#                        "hw-thread-id": 0,
+#                        "side-effects-write": false
+#                    },
+#                    {
+#                        "reg-group-id": 1,
+#                        "regname": "x2",
+#                        "side-effects-read": false,
+#                        "addr": {
+#                            "mem-space-id": 5,
+#                            "addr-space-id": 0,
+#                            "address": 2,
+#                            "addr-space-type": 0
+#                        },
+#                        "reg-type": 0,
+#                        "core-mode-mask-write": 0,
+#                        "core-mode-mask-read": 0,
+#                        "regsize": 64,
+#                        "hw-thread-id": 0,
+#                        "side-effects-write": false
+#                    },
+#                    {
+#                        "reg-group-id": 1,
+#                        "regname": "x3",
+#                        "side-effects-read": false,
+#                        "addr": {
+#                            "mem-space-id": 5,
+#                            "addr-space-id": 0,
+#                            "address": 3,
+#                            "addr-space-type": 0
+#                        },
+#                        "reg-type": 0,
+#                        "core-mode-mask-write": 0,
+#                        "core-mode-mask-read": 0,
+#                        "regsize": 64,
+#                        "hw-thread-id": 0,
+#                        "side-effects-write": false
+#                    },
+#                    ...
+#                ],
+#                "return-status": 0,
+#                "num-regs": 34
+#            }
+#        }
+##
+{ 'command': 'mcd-qry-reg-map',
+  'data': {
+    'core-uid': 'uint32',
+    'reg-group-id': 'uint32',
+    'start-index': 'uint32',
+    'num-regs': 'uint32' },
+  'returns': 'MCDQryRegMapResult' }
diff --git a/tests/qtest/libmcd-test.c b/tests/qtest/libmcd-test.c
index 9e41a0218f..82a39366b2 100644
--- a/tests/qtest/libmcd-test.c
+++ b/tests/qtest/libmcd-test.c
@@ -278,3 +278,43 @@ MCDQryMemSpacesResult *qtest_mcd_qry_mem_spaces(
 
     return unmarshal;
 }
+
+MCDQryRegGroupsResult *qtest_mcd_qry_reg_groups(
+    QTestState *qts, q_obj_mcd_qry_reg_groups_arg *args)
+{
+    Visitor *v;
+    QObject *marshal;
+    QDict *arg, *resp;
+    QObject *ret;
+    bool ok;
+    MCDQryRegGroupsResult *unmarshal;
+
+    MARSHAL_ARGS(q_obj_mcd_qry_reg_groups_arg);
+
+    resp = qtest_qmp(qts, "{'execute': 'mcd-qry-reg-groups',"
+                          "'arguments': %p}", arg);
+
+    UNMARSHAL_RESULT(MCDQryRegGroupsResult);
+
+    return unmarshal;
+}
+
+MCDQryRegMapResult *qtest_mcd_qry_reg_map(QTestState *qts,
+                                          q_obj_mcd_qry_reg_map_arg *args)
+{
+    Visitor *v;
+    QObject *marshal;
+    QDict *arg, *resp;
+    QObject *ret;
+    bool ok;
+    MCDQryRegMapResult *unmarshal;
+
+    MARSHAL_ARGS(q_obj_mcd_qry_reg_map_arg);
+
+    resp = qtest_qmp(qts, "{'execute': 'mcd-qry-reg-map',"
+                          "'arguments': %p}", arg);
+
+    UNMARSHAL_RESULT(MCDQryRegMapResult);
+
+    return unmarshal;
+}
diff --git a/tests/qtest/libmcd-test.h b/tests/qtest/libmcd-test.h
index 26f35b8931..266b6fe854 100644
--- a/tests/qtest/libmcd-test.h
+++ b/tests/qtest/libmcd-test.h
@@ -50,4 +50,10 @@ MCDCloseCoreResult *qtest_mcd_close_core(QTestState *qts,
 MCDQryMemSpacesResult *qtest_mcd_qry_mem_spaces(QTestState *qts,
     q_obj_mcd_qry_mem_spaces_arg *args);
 
+MCDQryRegGroupsResult *qtest_mcd_qry_reg_groups(QTestState *qts,
+    q_obj_mcd_qry_reg_groups_arg *args);
+
+MCDQryRegMapResult *qtest_mcd_qry_reg_map(QTestState *qts,
+                                          q_obj_mcd_qry_reg_map_arg *args);
+
 #endif /* LIBMCD_TEST_H */
diff --git a/tests/qtest/mcd-test.c b/tests/qtest/mcd-test.c
index 7deca39f93..820408a9a9 100644
--- a/tests/qtest/mcd-test.c
+++ b/tests/qtest/mcd-test.c
@@ -355,8 +355,12 @@ static void test_qry_core_info(void)
     MCDCoreConInfoList *core_head = cores_query->core_con_info;
     for (uint32_t c = 0; c < cores_query->num_cores; c++) {
         q_obj_mcd_qry_mem_spaces_arg qry_mem_spaces_args;
+        q_obj_mcd_qry_reg_groups_arg qry_reg_groups_args;
+        q_obj_mcd_qry_reg_map_arg qry_reg_map_args;
         q_obj_mcd_close_core_arg close_core_args;
         MCDQryMemSpacesResult *qry_mem_spaces_result;
+        MCDQryRegGroupsResult *qry_reg_groups_result;
+        MCDQryRegMapResult *qry_reg_map_result;
         MCDCloseCoreResult *close_core_result;
 
         MCDCoreConInfo *core_con_info = core_head->value;
@@ -374,6 +378,8 @@ static void test_qry_core_info(void)
                                 core_con_info->core_id);
         }
 
+        /* Memory Space Query */
+
         qry_mem_spaces_args = (q_obj_mcd_qry_mem_spaces_arg) {
             .core_uid = open_core_result->core_uid,
             .start_index = 0,
@@ -411,6 +417,85 @@ static void test_qry_core_info(void)
         }
 
         qapi_free_MCDQryMemSpacesResult(qry_mem_spaces_result);
+
+        /* Register Query */
+
+        qry_reg_groups_args = (q_obj_mcd_qry_reg_groups_arg) {
+            .core_uid = open_core_result->core_uid,
+            .start_index = 0,
+            .num_reg_groups = 0, /* query only number of register groups */
+        };
+
+        qry_reg_groups_result = qtest_mcd_qry_reg_groups(qts,
+                                                        &qry_reg_groups_args);
+        g_assert(qry_reg_groups_result->return_status == MCD_RET_ACT_NONE);
+        g_assert(qry_reg_groups_result->has_num_reg_groups);
+
+        if (qry_reg_groups_result->num_reg_groups == 0) {
+            fprintf(stderr, "[WARN]\tTNo register groups!\n");
+        }
+
+        qry_reg_groups_args.num_reg_groups =
+            qry_reg_groups_result->num_reg_groups;
+
+        qapi_free_MCDQryRegGroupsResult(qry_reg_groups_result);
+        qry_reg_groups_result = qtest_mcd_qry_reg_groups(qts,
+                                                         &qry_reg_groups_args);
+        g_assert(qry_reg_groups_result->return_status == MCD_RET_ACT_NONE);
+        g_assert(qry_reg_groups_result->has_num_reg_groups);
+
+        if (verbose) {
+            MCDRegisterGroupList *rg_head = qry_reg_groups_result->reg_groups;
+            for (uint32_t i = 0;
+                 i < qry_reg_groups_result->num_reg_groups; i++) {
+                MCDRegisterGroup *rg = rg_head->value;
+                if (verbose) {
+                    fprintf(stderr, "\tRegister Group: %s (#%d) with "
+                                    "%d registers\n",
+                                    rg->reg_group_name,
+                                    rg->reg_group_id,
+                                    rg->n_registers);
+                }
+                rg_head = rg_head->next;
+            }
+        }
+
+        qapi_free_MCDQryRegGroupsResult(qry_reg_groups_result);
+
+        qry_reg_map_args = (q_obj_mcd_qry_reg_map_arg) {
+            .core_uid = open_core_result->core_uid,
+            .reg_group_id = 0,
+            .start_index = 0,
+            .num_regs = 0, /* query only number of registers */
+        };
+
+        qry_reg_map_result = qtest_mcd_qry_reg_map(qts, &qry_reg_map_args);
+        g_assert(qry_reg_map_result->return_status == MCD_RET_ACT_NONE);
+        g_assert(qry_reg_map_result->has_num_regs);
+
+        if (verbose) {
+            fprintf(stderr, "\t%d registers found\n",
+                qry_reg_map_result->num_regs);
+        }
+
+        qry_reg_map_args.num_regs = qry_reg_map_result->num_regs;
+        qapi_free_MCDQryRegMapResult(qry_reg_map_result);
+
+        qry_reg_map_result = qtest_mcd_qry_reg_map(qts, &qry_reg_map_args);
+        g_assert(qry_reg_map_result->return_status == MCD_RET_ACT_NONE);
+        g_assert(qry_reg_map_result->has_num_regs);
+        if (verbose) {
+            MCDRegisterInfoList *r_head = qry_reg_map_result->reg_info;
+            for (uint32_t i = 0;
+                 i < qry_reg_map_result->num_regs; i++) {
+                MCDRegisterInfo *r = r_head->value;
+                fprintf(stderr, "\tRegister: %s (#%lx)\n",
+                        r->regname, r->addr->address);
+                r_head = r_head->next;
+            }
+        }
+        qapi_free_MCDQryRegMapResult(qry_reg_map_result);
+
         close_core_args.core_uid = open_core_result->core_uid;
         close_core_result = qtest_mcd_close_core(qts, &close_core_args);
         g_assert(close_core_result->return_status == MCD_RET_ACT_NONE);
-- 
2.34.1



Reply via email to