|
@@ -21,6 +21,12 @@
|
|
#include "cpu.h"
|
|
#include "cpu.h"
|
|
#include "internals.h"
|
|
#include "internals.h"
|
|
#include "gdbstub/helpers.h"
|
|
#include "gdbstub/helpers.h"
|
|
|
|
+#include "gdbstub/commands.h"
|
|
|
|
+#include "tcg/mte_helper.h"
|
|
|
|
+#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX)
|
|
|
|
+#include <sys/prctl.h>
|
|
|
|
+#include "mte_user_helper.h"
|
|
|
|
+#endif
|
|
|
|
|
|
int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
|
|
int aarch64_cpu_gdb_read_register(CPUState *cs, GByteArray *mem_buf, int n)
|
|
{
|
|
{
|
|
@@ -381,3 +387,220 @@ GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cs, int base_reg)
|
|
|
|
|
|
return &cpu->dyn_svereg_feature.desc;
|
|
return &cpu->dyn_svereg_feature.desc;
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_USER_ONLY
|
|
|
|
+int aarch64_gdb_get_tag_ctl_reg(CPUState *cs, GByteArray *buf, int reg)
|
|
|
|
+{
|
|
|
|
+ ARMCPU *cpu = ARM_CPU(cs);
|
|
|
|
+ CPUARMState *env = &cpu->env;
|
|
|
|
+ uint64_t tcf0;
|
|
|
|
+
|
|
|
|
+ assert(reg == 0);
|
|
|
|
+
|
|
|
|
+ tcf0 = extract64(env->cp15.sctlr_el[1], 38, 2);
|
|
|
|
+
|
|
|
|
+ return gdb_get_reg64(buf, tcf0);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+int aarch64_gdb_set_tag_ctl_reg(CPUState *cs, uint8_t *buf, int reg)
|
|
|
|
+{
|
|
|
|
+ ARMCPU *cpu = ARM_CPU(cs);
|
|
|
|
+ CPUARMState *env = &cpu->env;
|
|
|
|
+
|
|
|
|
+ uint8_t tcf;
|
|
|
|
+
|
|
|
|
+ assert(reg == 0);
|
|
|
|
+
|
|
|
|
+ tcf = *buf << PR_MTE_TCF_SHIFT;
|
|
|
|
+
|
|
|
|
+ if (!tcf) {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * 'tag_ctl' register is actually a "pseudo-register" provided by GDB to
|
|
|
|
+ * expose options regarding the type of MTE fault that can be controlled at
|
|
|
|
+ * runtime.
|
|
|
|
+ */
|
|
|
|
+ arm_set_mte_tcf0(env, tcf);
|
|
|
|
+
|
|
|
|
+ return 1;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void handle_q_memtag(GArray *params, void *user_ctx)
|
|
|
|
+{
|
|
|
|
+ ARMCPU *cpu = ARM_CPU(user_ctx);
|
|
|
|
+ CPUARMState *env = &cpu->env;
|
|
|
|
+
|
|
|
|
+ uint64_t addr = gdb_get_cmd_param(params, 0)->val_ull;
|
|
|
|
+ uint64_t len = gdb_get_cmd_param(params, 1)->val_ul;
|
|
|
|
+ int type = gdb_get_cmd_param(params, 2)->val_ul;
|
|
|
|
+
|
|
|
|
+ uint8_t *tags;
|
|
|
|
+ uint8_t addr_tag;
|
|
|
|
+
|
|
|
|
+ g_autoptr(GString) str_buf = g_string_new(NULL);
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * GDB does not query multiple tags for a memory range on remote targets, so
|
|
|
|
+ * that's not supported either by gdbstub.
|
|
|
|
+ */
|
|
|
|
+ if (len != 1) {
|
|
|
|
+ gdb_put_packet("E02");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* GDB never queries a tag different from an allocation tag (type 1). */
|
|
|
|
+ if (type != 1) {
|
|
|
|
+ gdb_put_packet("E03");
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Note that tags are packed here (2 tags packed in one byte). */
|
|
|
|
+ tags = allocation_tag_mem_probe(env, 0, addr, MMU_DATA_LOAD, 8 /* 64-bit */,
|
|
|
|
+ MMU_DATA_LOAD, true, 0);
|
|
|
|
+ if (!tags) {
|
|
|
|
+ /* Address is not in a tagged region. */
|
|
|
|
+ gdb_put_packet("E04");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Unpack tag from byte. */
|
|
|
|
+ addr_tag = load_tag1(addr, tags);
|
|
|
|
+ g_string_printf(str_buf, "m%.2x", addr_tag);
|
|
|
|
+
|
|
|
|
+ gdb_put_packet(str_buf->str);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void handle_q_isaddresstagged(GArray *params, void *user_ctx)
|
|
|
|
+{
|
|
|
|
+ ARMCPU *cpu = ARM_CPU(user_ctx);
|
|
|
|
+ CPUARMState *env = &cpu->env;
|
|
|
|
+
|
|
|
|
+ uint64_t addr = gdb_get_cmd_param(params, 0)->val_ull;
|
|
|
|
+
|
|
|
|
+ uint8_t *tags;
|
|
|
|
+ const char *reply;
|
|
|
|
+
|
|
|
|
+ tags = allocation_tag_mem_probe(env, 0, addr, MMU_DATA_LOAD, 8 /* 64-bit */,
|
|
|
|
+ MMU_DATA_LOAD, true, 0);
|
|
|
|
+ reply = tags ? "01" : "00";
|
|
|
|
+
|
|
|
|
+ gdb_put_packet(reply);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void handle_Q_memtag(GArray *params, void *user_ctx)
|
|
|
|
+{
|
|
|
|
+ ARMCPU *cpu = ARM_CPU(user_ctx);
|
|
|
|
+ CPUARMState *env = &cpu->env;
|
|
|
|
+
|
|
|
|
+ uint64_t start_addr = gdb_get_cmd_param(params, 0)->val_ull;
|
|
|
|
+ uint64_t len = gdb_get_cmd_param(params, 1)->val_ul;
|
|
|
|
+ int type = gdb_get_cmd_param(params, 2)->val_ul;
|
|
|
|
+ char const *new_tags_str = gdb_get_cmd_param(params, 3)->data;
|
|
|
|
+
|
|
|
|
+ uint64_t end_addr;
|
|
|
|
+
|
|
|
|
+ int num_new_tags;
|
|
|
|
+ uint8_t *tags;
|
|
|
|
+
|
|
|
|
+ g_autoptr(GByteArray) new_tags = g_byte_array_new();
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Only the allocation tag (i.e. type 1) can be set at the stub side.
|
|
|
|
+ */
|
|
|
|
+ if (type != 1) {
|
|
|
|
+ gdb_put_packet("E02");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ end_addr = start_addr + (len - 1); /* 'len' is always >= 1 */
|
|
|
|
+ /* Check if request's memory range does not cross page boundaries. */
|
|
|
|
+ if ((start_addr ^ end_addr) & TARGET_PAGE_MASK) {
|
|
|
|
+ gdb_put_packet("E03");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Get all tags in the page starting from the tag of the start address.
|
|
|
|
+ * Note that there are two tags packed into a single byte here.
|
|
|
|
+ */
|
|
|
|
+ tags = allocation_tag_mem_probe(env, 0, start_addr, MMU_DATA_STORE,
|
|
|
|
+ 8 /* 64-bit */, MMU_DATA_STORE, true, 0);
|
|
|
|
+ if (!tags) {
|
|
|
|
+ /* Address is not in a tagged region. */
|
|
|
|
+ gdb_put_packet("E04");
|
|
|
|
+ return;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Convert tags provided by GDB, 2 hex digits per tag. */
|
|
|
|
+ num_new_tags = strlen(new_tags_str) / 2;
|
|
|
|
+ gdb_hextomem(new_tags, new_tags_str, num_new_tags);
|
|
|
|
+
|
|
|
|
+ uint64_t address = start_addr;
|
|
|
|
+ int new_tag_index = 0;
|
|
|
|
+ while (address <= end_addr) {
|
|
|
|
+ uint8_t new_tag;
|
|
|
|
+ int packed_index;
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * Find packed tag index from unpacked tag index. There are two tags
|
|
|
|
+ * in one packed index (one tag per nibble).
|
|
|
|
+ */
|
|
|
|
+ packed_index = new_tag_index / 2;
|
|
|
|
+
|
|
|
|
+ new_tag = new_tags->data[new_tag_index % num_new_tags];
|
|
|
|
+ store_tag1(address, tags + packed_index, new_tag);
|
|
|
|
+
|
|
|
|
+ address += TAG_GRANULE;
|
|
|
|
+ new_tag_index++;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ gdb_put_packet("OK");
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+enum Command {
|
|
|
|
+ qMemTags,
|
|
|
|
+ qIsAddressTagged,
|
|
|
|
+ QMemTags,
|
|
|
|
+ NUM_CMDS
|
|
|
|
+};
|
|
|
|
+
|
|
|
|
+static GdbCmdParseEntry cmd_handler_table[NUM_CMDS] = {
|
|
|
|
+ [qMemTags] = {
|
|
|
|
+ .handler = handle_q_memtag,
|
|
|
|
+ .cmd_startswith = true,
|
|
|
|
+ .cmd = "MemTags:",
|
|
|
|
+ .schema = "L,l:l0",
|
|
|
|
+ .need_cpu_context = true
|
|
|
|
+ },
|
|
|
|
+ [qIsAddressTagged] = {
|
|
|
|
+ .handler = handle_q_isaddresstagged,
|
|
|
|
+ .cmd_startswith = true,
|
|
|
|
+ .cmd = "IsAddressTagged:",
|
|
|
|
+ .schema = "L0",
|
|
|
|
+ .need_cpu_context = true
|
|
|
|
+ },
|
|
|
|
+ [QMemTags] = {
|
|
|
|
+ .handler = handle_Q_memtag,
|
|
|
|
+ .cmd_startswith = true,
|
|
|
|
+ .cmd = "MemTags:",
|
|
|
|
+ .schema = "L,l:l:s0",
|
|
|
|
+ .need_cpu_context = true
|
|
|
|
+ },
|
|
|
|
+};
|
|
|
|
+#endif /* CONFIG_USER_ONLY */
|
|
|
|
+
|
|
|
|
+void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *qsupported,
|
|
|
|
+ GArray *qtable, GArray *stable)
|
|
|
|
+{
|
|
|
|
+#ifdef CONFIG_USER_ONLY
|
|
|
|
+ /* MTE */
|
|
|
|
+ if (cpu_isar_feature(aa64_mte, cpu)) {
|
|
|
|
+ g_string_append(qsupported, ";memory-tagging+");
|
|
|
|
+
|
|
|
|
+ g_array_append_val(qtable, cmd_handler_table[qMemTags]);
|
|
|
|
+ g_array_append_val(qtable, cmd_handler_table[qIsAddressTagged]);
|
|
|
|
+
|
|
|
|
+ g_array_append_val(stable, cmd_handler_table[QMemTags]);
|
|
|
|
+ }
|
|
|
|
+#endif
|
|
|
|
+}
|