On 5/15/24 19:31, Gustavo Romero wrote:
+static int aarch64_gdb_set_tag_ctl_reg(CPUState *cs, uint8_t *buf, int reg)
+{
+ ARMCPU *cpu = ARM_CPU(cs);
+ CPUARMState *env = &cpu->env;
+
+ assert(reg == 0);
+
+ /* Sanitize TCF0 bits. */
+ *buf &= 0x03;
+
+ if (!isar_feature_aa64_mte3(&cpu->isar) && *buf == 3) {
cpu_isar_feature(aa64_mte3, cpu)
+ /*
+ * If FEAT_MTE3 is not implemented, the value 0b11 is reserved, hence
+ * ignore setting it.
+ */
+ return 0;
That said, we always implement the mte3 behaviour, so perhaps drop this
entirely?
+ }
+
+ /*
+ * 'tag_ctl' register is actually a "pseudo-register" provided by GDB to
+ * expose options that can be controlled at runtime and has the same effect
+ * of prctl() with option PR_SET_TAGGED_ADDR_CTRL,
+ * i.e. prctl(PR_SET_TAGGED_ADDR_CTRL, tcf, 0, 0, 0), hence it controls
+ * the effect of Tag Check Faults (TCF) due to Loads and Stores in EL0.
+ */
+ env->cp15.sctlr_el[1] = deposit64(env->cp15.sctlr_el[1], 38, 2, *buf);
+
+ return 1;
+}
+
+static void handle_q_memtag(GArray *params, G_GNUC_UNUSED void *user_ctx)
+{
+ uint64_t addr = get_param(params, 0)->val_ull;
+ uint64_t len = get_param(params, 1)->val_ul;
+ int type = get_param(params, 2)->val_ul;
+
+ uint64_t clean_addr;
+ uint8_t *tags;
+ int granules_index;
+ int granule_index;
+ uint8_t addr_tag;
+
+ g_autoptr(GString) str_buf = g_string_new(NULL);
+
+ /*
+ * GDB does not query 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("E02");
+ }
+
+ /* Remove any non-addressing bits. */
+ clean_addr = useronly_clean_ptr(addr);
+
+ /*
+ * Get pointer to all tags in the page where the address is. Note that tags
+ * are packed, so there are 2 tags packed in one byte.
+ */
+ tags = page_get_target_data(clean_addr);
While you expect gdb will have called isaddresstagged first, you can't
guarantee that:
you should verify that the address is valid and tagged first.
+static void handle_q_isaddresstagged(GArray *params, G_GNUC_UNUSED void
*user_ctx)
+{
+ uint64_t addr = get_param(params, 0)->val_ull;
+
+ uint64_t clean_addr;
+ int mflags;
+
+ g_autoptr(GString) str_buf = g_string_new(NULL);
+
+ /* Remove any non-addressing bits. */
+ clean_addr = useronly_clean_ptr(addr);
+
+ mflags = page_get_flags(clean_addr);
+ if (mflags & PAGE_ANON && mflags & PAGE_MTE) {
+ /* Address is tagged. */
+ g_string_printf(str_buf, "%.2x", 1 /* true */);
+ } else {
+ /* Address is not tagged. */
+ g_string_printf(str_buf, "%.2x", 0 /* false */);
+ }
+
+ gdb_put_packet(str_buf->str);
Overkill with GString?
const char *result = (mflags & PAGE_ANON && mflags & PAGE_MTE ? "1" : "0");
gdb_put_packet(result);
?
+}
+
+static void handle_Q_memtag(GArray *params, G_GNUC_UNUSED void *user_ctx)
+{
+ uint64_t addr = get_param(params, 0)->val_ull;
+ uint64_t len = get_param(params, 1)->val_ul;
+ int type = get_param(params, 2)->val_ul;
+ char const *new_tags = get_param(params, 3)->data;
+
+ uint64_t clean_addr;
+ int last_addr_index;
+
+ uint64_t start_addr_page;
+ uint64_t end_addr_page;
+
+ uint32_t first_tag_index;
+ uint32_t last_tag_index;
+
+ uint8_t *tags; /* Pointer to the current tags in a page. */
+ int num_new_tags;
+
+ g_autoptr(GString) str_buf = g_string_new(NULL);
+
+ /*
+ * Only the allocation tag (type 1) can be set at the stub side.
+ */
+ if (type != 1) {
+ gdb_put_packet("E02");
+ return;
+ }
+
+ /*
+ * 'len' is always >= 1 and refers to the size of the memory range about to
+ * have its tags updated. However, it's convenient to obtain the index for
+ * the last byte of the memory range for page boundary checks and for
+ * obtaining the indexes for the tags in the page.
+ */
+ last_addr_index = len - 1;
+
+ /* Remove any non-addressing bits. */
+ clean_addr = useronly_clean_ptr(addr);
+
+ start_addr_page = extract64(clean_addr, TARGET_PAGE_BITS,
+ 64 - TARGET_PAGE_BITS);
+ end_addr_page = extract64(clean_addr + last_addr_index, TARGET_PAGE_BITS,
+ 64 - TARGET_PAGE_BITS);
+
+ /*
+ * Check if memory range is within page boundaries.
+ */
+ if (start_addr_page != end_addr_page) {
+ gdb_put_packet("E03");
+ return;
+ }
+
+ /*
+ * Get pointer to all tags in the page where the address is. Note that here
+ * tags are packed, so there are 2 tags packed in one byte.
+ */
+ tags = page_get_target_data(clean_addr);
Likewise.
+
+ /* Tag indices below refer to unpacked tags. */
+ first_tag_index = extract32(clean_addr, LOG2_TAG_GRANULE,
+ TARGET_PAGE_BITS - LOG2_TAG_GRANULE);
+ last_tag_index = extract32(clean_addr + last_addr_index, LOG2_TAG_GRANULE,
+ TARGET_PAGE_BITS - LOG2_TAG_GRANULE);
+
+ /*
+ * GDB sends 2 hex digits per tag number, i.e. tags are not represented in
+ * a packed way.
+ */
+ num_new_tags = strlen(new_tags) / 2;
+
+ /*
+ * If the number of tags provided is greater than the number of tags
+ * in the provided memory range, the exceeding tags are ignored. If the
+ * number of tags is less than the number of tags in the provided memory
+ * range, then the provided tags are used as a repeating pattern to fill
+ * the tags in the provided memory range.
+ */
+ for (int i = first_tag_index, j = 0; i <= last_tag_index; i++, j++) {
+ int new_tag_value;
+ int packed_granules_index;
+ int nibble_index;
+
+ sscanf(new_tags + 2 * (j % num_new_tags), "%2x", &new_tag_value);
Overkill?
While gdb may send 2 digits, only 0[0-9a-fA-F] is valid.
+ /*
+ * Find packed tag index from unpacked tag index. There are two tags
+ * packed in one packed index. One tag per nibble.
+ */
+ packed_granules_index = i / 2;
+ /* Find nibble index in the packed tag from unpacked tag index. */
+ nibble_index = i % 2;
+
+ if (nibble_index == 0) { /* Update low nibble */
+ *(tags + packed_granules_index) &= 0xF0;
+ *(tags + packed_granules_index) |= (new_tag_value & 0x0F);
+ } else { /* Update high nibble */
+ *(tags + packed_granules_index) &= 0x0F;
+ *(tags + packed_granules_index) |= ((new_tag_value & 0x0F) << 4);
+ }
How many tags will gdb typically send with this?
If 1 or 2, it might be worth using memset.
If even, it might be worth pre-computing and using memcpy.
r~