QEMU中GDB远程串行协议

2024-08-23 16:04
文章标签 协议 远程 gdb 串行 qemu

本文主要是介绍QEMU中GDB远程串行协议,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

QEMU中GDB远程串行协议

  • 1 GDB远程串行协议介绍
  • 2 QEMU中“g”命令数据包
    • 2.1 获取x0~x31与pc寄存器
    • 2.2 获取f0 ~ f31、模式、CSR寄存器
      • 2.2.1 为何使用CPUState.gdb_num_regs
      • 2.2.2 如何修改
      • 2.2.3 数据包中寄存器布局

本文属于 《RISC-V指令集差分测试(DiffTest)系列教程》之一,欢迎查看其它文章。

1 GDB远程串行协议介绍

GDB(GNU Debugger)是一个强大的代码调试工具,它提供了一种使用串行通信协议进行远程调试的方法。

在GDB中,可以通过GDB服务器和GDB客户端进行远程调试。其中,GDB远程串行协议允许GDB客户端通过串行连接(如USB或TCP/IP)与GDB服务器通信。

GDB远程串行协议的主要作用是:允许开发者在一个系统(通常是开发主机)上,调试另一个系统(通常是目标设备)上运行的程序。这在调试嵌入式系统或需要物理访问硬件的情况下特别有用。

协议具体内容,参考官网:https://sourceware.org/gdb/current/onlinedocs/gdb.html/Remote-Protocol.html
中文含义介绍:https://www.cnblogs.com/linucos/archive/2013/03/01/2938836.html

2 QEMU中“g”命令数据包

NEMU的DiffTest中,就是基于上述GDB远程串行协议,来获取QEMU中模拟的内存,以及寄存器等信息的。

2.1 获取x0~x31与pc寄存器

“g”命令,表示获取QEMU中通用寄存器(x0~x31与pc)。

当NEMU通过调用gdb_getregs函数,向QEMU发送“g”命令时,QEMU会将当前模拟的CPU中,通用寄存器值,回传给NEMU。

回传的数据包,长度为528字节,内容假设为如下:

"1234567823456789…”
  • 每个字节必须为:“0” ~ “9"或"a” ~ “f"之间的任一字符
  • 每2个字节,表示1字节数据的十六进制,比如"12"表示0x12
  • 所表示数据,低位在前,高位在后

假设寄存器数据长度为8字节,那么"1234567823456789",低位在前,高位在后,即表示寄存器值为0x8967452378563412。

x0~x31与pc,一共33个寄存器,寄存器数据长度为33 * 8=264字节,因此需要使用264 * 2=528个字符来表示。

数据包中,寄存器排列顺序,如下所示:
在这里插入图片描述

2.2 获取f0 ~ f31、模式、CSR寄存器

原版QEMU6.2.0中,虽然通过g命令,能获取上述寄存器;但是,并不能获取到f0 ~ f31、当前模式、CSR这些寄存器。

首先,需要明白QEMU中,以下几个寄存器数变量,的含义:

  • CPUState.gdb_num_g_regs:表示gdb下’g’命令可获取的寄存器数量,此值为33
  • CPUClass.gdb_num_core_regs:表示gdb可访问的core寄存器数量,此值为33
  • CPUState.gdb_num_regs:表示gdb可访问的所有寄存器数,此值为4166。这4166个寄存器,是由如下组成的:
    • x0 ~ x31 + pc,共33个;
    • f0 ~ f31 + 4个浮点相关CSR寄存器(fflags、frm、fcsr、其他),共36个;
    • mode模式,共1个;
    • csr寄存器,12位地址,共4096个。

2.2.1 为何使用CPUState.gdb_num_regs

在qemu6.2.0/target/riscv/gdbstub.c的riscv_cpu_register_gdb_regs_for_features函数中,会为浮点以及CSR寄存器注册访问函数,如下:

void riscv_cpu_register_gdb_regs_for_features(CPUState *cs)
{RISCVCPU *cpu = RISCV_CPU(cs);CPURISCVState *env = &cpu->env;if (env->misa_ext & RVD) {gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu,36, "riscv-64bit-fpu.xml", 0);} else if (env->misa_ext & RVF) {gdb_register_coprocessor(cs, riscv_gdb_get_fpu, riscv_gdb_set_fpu,36, "riscv-32bit-fpu.xml", 0);}if (env->misa_ext & RVV) {gdb_register_coprocessor(cs, riscv_gdb_get_vector, riscv_gdb_set_vector,ricsv_gen_dynamic_vector_xml(cs,cs->gdb_num_regs),"riscv-vector.xml", 0);}switch (env->misa_mxl_max) {case MXL_RV32:gdb_register_coprocessor(cs, riscv_gdb_get_virtual,riscv_gdb_set_virtual,1, "riscv-32bit-virtual.xml", 0);break;case MXL_RV64:case MXL_RV128:gdb_register_coprocessor(cs, riscv_gdb_get_virtual,riscv_gdb_set_virtual,1, "riscv-64bit-virtual.xml", 0);break;default:g_assert_not_reached();}gdb_register_coprocessor(cs, riscv_gdb_get_csr, riscv_gdb_set_csr,riscv_gen_dynamic_csr_xml(cs, cs->gdb_num_regs),"riscv-csr.xml", 0);
}

在gdb_register_coprocessor函数中,会对已注册的寄存器,进行数量累加,因此CPUState.gdb_num_regs才是所有寄存器总数

2.2.2 如何修改

如何修改QEMU源码,才能使得g命令,可以获取到所有寄存器的值呢?

非常简单。

将qemu6.2.0/gdbstub.c的handle_read_all_regs函数中gdb_num_g_regs改为gdb_num_regs,如下:

static void handle_read_all_regs(GArray *params, void *user_ctx)
{...for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_regs; addr++) {...}...
}

如此,便可获取到所有寄存器。

需要注意的是,这4166个寄存器,并不是每个寄存器都会在数据包中传输,若QEMU未实现的寄存器,就不会放入buf中,也就不会传输。

2.2.3 数据包中寄存器布局

经过调试发现,响应g命令时,实际能获取到的寄存器,只有209个,具体为以下这些:

  • x0 ~ x31 + pc
  • f0 ~ f31 + fflags + frm + fcsr
  • mode
  • 140个CSR寄存器,QEMU 6.2.0只支持140个CSR寄存器。

数据包中(总长度为3344字节),寄存器排列顺序,如下所示:
在这里插入图片描述

在数据包中,从前往后,这些CSR寄存器的地址,如下所示:

int csrAddr[140] = { 
0x1, 0x2, 0x3, 0x100, 0x104, 0x105, 0x106, 0x140, 0x141, 0x142, 0x143, 0x144, 0x180, 0x300, 0x301, 0x302, 0x303, 0x304, 0x305, 0x306, 
0x323, 0x324, 0x325, 0x326, 0x327, 0x328, 0x329, 0x32a, 0x32b, 0x32c, 0x32d, 0x32e, 0x32f, 0x330, 0x331, 0x332, 0x333, 0x334, 0x335, 0x336, 
0x337, 0x338, 0x339, 0x33a, 0x33b, 0x33c, 0x33d, 0x33e, 0x33f, 0x340, 0x341, 0x342, 0x343, 0x344, 0x3a0, 0x3a1, 0x3a2, 0x3a3, 0x3b0, 0x3b1, 
0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7, 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bc, 0x3bd, 0x3be, 0x3bf, 0xb00, 0xb02, 0xb03, 0xb04, 0xb05, 0xb06, 
0xb07, 0xb08, 0xb09, 0xb0a, 0xb0b, 0xb0c, 0xb0d, 0xb0e, 0xb0f, 0xb10, 0xb11, 0xb12, 0xb13, 0xb14, 0xb15, 0xb16, 0xb17, 0xb18, 0xb19, 0xb1a, 
0xb1b, 0xb1c, 0xb1d, 0xb1e, 0xb1f, 0xc00, 0xc02, 0xc03, 0xc04, 0xc05, 0xc06, 0xc07, 0xc08, 0xc09, 0xc0a, 0xc0b, 0xc0c, 0xc0d, 0xc0e, 0xc0f, 
0xc10, 0xc11, 0xc12, 0xc13, 0xc14, 0xc15, 0xc16, 0xc17, 0xc18, 0xc19, 0xc1a, 0xc1b, 0xc1c, 0xc1d, 0xc1e, 0xc1f, 0xf11, 0xf12, 0xf13, 0xf14, };

即:fflags地址为0x1,frm地址为0x2,fcsr地址为0x3,sstatus地址为0x100,以此类推。

  • 后来测试QEMU 7.1.0,发现g命令,获取的CSR数量,比QEMU6.2.0时多了3个,为143个。
  • 看来,在不同QEMU版本中,实现的CSR数量,还是有区别的。

这篇关于QEMU中GDB远程串行协议的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1099850

相关文章

VScode连接远程Linux服务器环境配置图文教程

《VScode连接远程Linux服务器环境配置图文教程》:本文主要介绍如何安装和配置VSCode,包括安装步骤、环境配置(如汉化包、远程SSH连接)、语言包安装(如C/C++插件)等,文中给出了详... 目录一、安装vscode二、环境配置1.中文汉化包2.安装remote-ssh,用于远程连接2.1安装2

Xshell远程连接失败以及解决方案

《Xshell远程连接失败以及解决方案》本文介绍了在Windows11家庭版和CentOS系统中解决Xshell无法连接远程服务器问题的步骤,在Windows11家庭版中,需要通过设置添加SSH功能并... 目录一.问题描述二.原因分析及解决办法2.1添加ssh功能2.2 在Windows中开启ssh服务2

Java如何接收并解析HL7协议数据

《Java如何接收并解析HL7协议数据》文章主要介绍了HL7协议及其在医疗行业中的应用,详细描述了如何配置环境、接收和解析数据,以及与前端进行交互的实现方法,文章还分享了使用7Edit工具进行调试的经... 目录一、前言二、正文1、环境配置2、数据接收:HL7Monitor3、数据解析:HL7Busines

Python实现局域网远程控制电脑

《Python实现局域网远程控制电脑》这篇文章主要为大家详细介绍了如何利用Python编写一个工具,可以实现远程控制局域网电脑关机,重启,注销等功能,感兴趣的小伙伴可以参考一下... 目录1.简介2. 运行效果3. 1.0版本相关源码服务端server.py客户端client.py4. 2.0版本相关源码1

【Linux】应用层http协议

一、HTTP协议 1.1 简要介绍一下HTTP        我们在网络的应用层中可以自己定义协议,但是,已经有大佬定义了一些现成的,非常好用的应用层协议,供我们直接使用,HTTP(超文本传输协议)就是其中之一。        在互联网世界中,HTTP(超文本传输协议)是一个至关重要的协议,他定义了客户端(如浏览器)与服务器之间如何进行通信,以交换或者传输超文本(比如HTML文档)。

【Go】go连接clickhouse使用TCP协议

离开你是傻是对是错 是看破是软弱 这结果是爱是恨或者是什么 如果是种解脱 怎么会还有眷恋在我心窝 那么爱你为什么                      🎵 黄品源/莫文蔚《那么爱你为什么》 package mainimport ("context""fmt""log""time""github.com/ClickHouse/clickhouse-go/v2")func main(

2024.9.8 TCP/IP协议学习笔记

1.所谓的层就是数据交换的深度,电脑点对点就是单层,物理层,加上集线器还是物理层,加上交换机就变成链路层了,有地址表,路由器就到了第三层网络层,每个端口都有一个mac地址 2.A 给 C 发数据包,怎么知道是否要通过路由器转发呢?答案:子网 3.将源 IP 与目的 IP 分别同这个子网掩码进行与运算****,相等则是在一个子网,不相等就是在不同子网 4.A 如何知道,哪个设备是路由器?答案:在 A

Modbus-RTU协议

一、协议概述 Modbus-RTU(Remote Terminal Unit)是一种基于主从架构的通信协议,采用二进制数据表示,消息中的每个8位字节含有两个4位十六进制字符。它主要通过RS-485、RS-232、RS-422等物理接口实现数据的传输,传输距离远、抗干扰能力强、通信效率高。 二、报文结构 一个标准的Modbus-RTU报文通常包含以下部分: 地址域:单个字节,表示从站设备

远程工具-SecureCRT/SecureFX

下载地址: https://www.portablesoft.org/securecrt-securefx-integrated/

【微服务】Ribbon(负载均衡,服务调用)+ OpenFeign(服务发现,远程调用)【详解】

文章目录 1.Ribbon(负载均衡,服务调用)1.1问题引出1.2 Ribbon负载均衡1.3 RestTemplate整合Ribbon1.4 指定Ribbon负载均衡策略1.4.1 配置文件1.4.2 配置类1.4.3 定义Ribbon客户端配置1.4.4 自定义负载均衡策略 2.OpenFeign面向接口的服务调用(服务发现,远程调用)2.1 OpenFeign的使用2.1 .1创建