@ -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
}