mirror of https://github.com/xemu-project/xemu.git
gdbstub: Add support for MTE in user mode
This commit implements the stubs to handle the qIsAddressTagged, qMemTag, and QMemTag GDB packets, allowing all GDB 'memory-tag' subcommands to work with QEMU gdbstub on aarch64 user mode. It also implements the get/set functions for the special GDB MTE register 'tag_ctl', used to control the MTE fault type at runtime. Signed-off-by: Gustavo Romero <gustavo.romero@linaro.org> Message-Id: <20240628050850.536447-11-gustavo.romero@linaro.org> Signed-off-by: Alex Bennée <alex.bennee@linaro.org> Message-Id: <20240705084047.857176-40-alex.bennee@linaro.org>
This commit is contained in:
parent
3b6c27d8f2
commit
f81198cefa
|
@ -1,6 +1,6 @@
|
|||
TARGET_ARCH=aarch64
|
||||
TARGET_BASE_ARCH=arm
|
||||
TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.xml
|
||||
TARGET_XML_FILES= gdb-xml/aarch64-core.xml gdb-xml/aarch64-fpu.xml gdb-xml/aarch64-pauth.xml gdb-xml/aarch64-mte.xml
|
||||
TARGET_HAS_BFLT=y
|
||||
CONFIG_SEMIHOSTING=y
|
||||
CONFIG_ARM_COMPATIBLE_SEMIHOSTING=y
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0"?>
|
||||
<!-- Copyright (C) 2021-2023 Free Software Foundation, Inc.
|
||||
|
||||
Copying and distribution of this file, with or without modification,
|
||||
are permitted in any medium without royalty provided the copyright
|
||||
notice and this notice are preserved. -->
|
||||
|
||||
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
|
||||
<feature name="org.gnu.gdb.aarch64.mte">
|
||||
<reg name="tag_ctl" bitsize="64" type="uint64" group="system" save-restore="no"/>
|
||||
</feature>
|
|
@ -2518,6 +2518,7 @@ static void arm_cpu_realizefn(DeviceState *dev, Error **errp)
|
|||
|
||||
register_cp_regs_for_features(cpu);
|
||||
arm_cpu_register_gdb_regs_for_features(cpu);
|
||||
arm_cpu_register_gdb_commands(cpu);
|
||||
|
||||
init_cpreg_list(cpu);
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "cpu.h"
|
||||
#include "exec/gdbstub.h"
|
||||
#include "gdbstub/helpers.h"
|
||||
#include "gdbstub/commands.h"
|
||||
#include "sysemu/tcg.h"
|
||||
#include "internals.h"
|
||||
#include "cpu-features.h"
|
||||
|
@ -474,6 +475,41 @@ static GDBFeature *arm_gen_dynamic_m_secextreg_feature(CPUState *cs,
|
|||
#endif
|
||||
#endif /* CONFIG_TCG */
|
||||
|
||||
void arm_cpu_register_gdb_commands(ARMCPU *cpu)
|
||||
{
|
||||
GArray *query_table =
|
||||
g_array_new(FALSE, FALSE, sizeof(GdbCmdParseEntry));
|
||||
GArray *set_table =
|
||||
g_array_new(FALSE, FALSE, sizeof(GdbCmdParseEntry));
|
||||
GString *qsupported_features = g_string_new(NULL);
|
||||
|
||||
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
||||
#ifdef TARGET_AARCH64
|
||||
aarch64_cpu_register_gdb_commands(cpu, qsupported_features, query_table,
|
||||
set_table);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Set arch-specific handlers for 'q' commands. */
|
||||
if (query_table->len) {
|
||||
gdb_extend_query_table(&g_array_index(query_table,
|
||||
GdbCmdParseEntry, 0),
|
||||
query_table->len);
|
||||
}
|
||||
|
||||
/* Set arch-specific handlers for 'Q' commands. */
|
||||
if (set_table->len) {
|
||||
gdb_extend_set_table(&g_array_index(set_table,
|
||||
GdbCmdParseEntry, 0),
|
||||
set_table->len);
|
||||
}
|
||||
|
||||
/* Set arch-specific qSupported feature. */
|
||||
if (qsupported_features->len) {
|
||||
gdb_extend_qsupported_features(qsupported_features->str);
|
||||
}
|
||||
}
|
||||
|
||||
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
|
||||
{
|
||||
CPUState *cs = CPU(cpu);
|
||||
|
@ -507,6 +543,16 @@ void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu)
|
|||
gdb_find_static_feature("aarch64-pauth.xml"),
|
||||
0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_USER_ONLY
|
||||
/* Memory Tagging Extension (MTE) 'tag_ctl' pseudo-register. */
|
||||
if (cpu_isar_feature(aa64_mte, cpu)) {
|
||||
gdb_register_coprocessor(cs, aarch64_gdb_get_tag_ctl_reg,
|
||||
aarch64_gdb_set_tag_ctl_reg,
|
||||
gdb_find_static_feature("aarch64-mte.xml"),
|
||||
0);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
} else {
|
||||
if (arm_feature(env, ARM_FEATURE_NEON)) {
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
#include "cpu.h"
|
||||
#include "internals.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)
|
||||
{
|
||||
|
@ -381,3 +387,220 @@ GDBFeature *arm_gen_dynamic_svereg_feature(CPUState *cs, int base_reg)
|
|||
|
||||
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
|
||||
}
|
||||
|
|
|
@ -358,6 +358,10 @@ void init_cpreg_list(ARMCPU *cpu);
|
|||
void arm_cpu_register_gdb_regs_for_features(ARMCPU *cpu);
|
||||
void arm_translate_init(void);
|
||||
|
||||
void arm_cpu_register_gdb_commands(ARMCPU *cpu);
|
||||
void aarch64_cpu_register_gdb_commands(ARMCPU *cpu, GString *, GArray *,
|
||||
GArray *);
|
||||
|
||||
void arm_restore_state_to_opc(CPUState *cs,
|
||||
const TranslationBlock *tb,
|
||||
const uint64_t *data);
|
||||
|
@ -1640,6 +1644,8 @@ int aarch64_gdb_get_fpu_reg(CPUState *cs, GByteArray *buf, int reg);
|
|||
int aarch64_gdb_set_fpu_reg(CPUState *cs, uint8_t *buf, int reg);
|
||||
int aarch64_gdb_get_pauth_reg(CPUState *cs, GByteArray *buf, int reg);
|
||||
int aarch64_gdb_set_pauth_reg(CPUState *cs, uint8_t *buf, int reg);
|
||||
int aarch64_gdb_get_tag_ctl_reg(CPUState *cs, GByteArray *buf, int reg);
|
||||
int aarch64_gdb_set_tag_ctl_reg(CPUState *cs, uint8_t *buf, int reg);
|
||||
void arm_cpu_sve_finalize(ARMCPU *cpu, Error **errp);
|
||||
void arm_cpu_sme_finalize(ARMCPU *cpu, Error **errp);
|
||||
void arm_cpu_pauth_finalize(ARMCPU *cpu, Error **errp);
|
||||
|
|
Loading…
Reference in New Issue