Qemu裸机仿真-如何查看UART输出? [英] Qemu baremetal emulation - how to view UART output?
问题描述
如何从使用Qemu运行的裸机程序中获取UART输出?
How do I get the UART output from a baremetal program run with Qemu?
这是我一直在使用的命令行调用:
Here is the command line invocation I have been using:
qemu-system-arm -M xilinx-zynq-a9 -cpu cortex-a9 -nographic -kernel $BUILD_DIR/mm.elf -m 512M -s -S
- 使用计算机
xilinx-zynq-a9
- 处理器
cortex-a9
- 因为它是裸机,所以可执行文件是一个自包含的ELF文件
-
-m 512M
表示平台具有512 MiB的RAM -
-s
是-gdb tcp::1234
的快捷方式
-
-S
表示在启动时冻结CPU - using machine
xilinx-zynq-a9
- processor
cortex-a9
- as this is baremetal, the executable is a self contained ELF file
-m 512M
signifies the platform has 512 MiB of RAM-s
is a shortcut for-gdb tcp::1234
-S
means freeze CPU at startup-
-serial mon:stdio
- 没事
- 没事
- 没事
- 不能通过多个字符设备使用stdio
- 无法将串行设备连接到字符后端"stdio"
- 无效的选项
- 黑屏,无输出
我查看了ELF文件的反汇编,并验证了将UART消息写入的地址与Qemu设置所期望的地址相同(
info mtree
).基址是0xe0000000
,两个地方都相同.I looked at the disassembly of the ELF file and verified that the address to which the the UART messages are being written is the same as the Qemu setup expects (
info mtree
). Base address is0xe0000000
, the same in both places.我希望能够捕获发送到UART的消息的输出.如果通过重定向到stdout完成此操作,则可以.如果它通过TCP套接字,那也可以.故障注入设置使用Python,并且Qemu作为子进程运行,因此可以很容易地从任何一个来源获取输出.
I want to be able to capture the output of messages sent to UART. If this is done by redirecting to stdout, that's fine. If it goes through a TCP socket, that's fine too. The fault injection setup uses Python, and Qemu is running as a subprocess, so it would be easy to get the output from either one of those sources.
注意:在故障注入设置中运行时,Qemu调用为
Note: when run in the fault injection setup, the Qemu invocation is
qemu-system-arm -M xilinx-zynq-a9 -cpu cortex-a9 -nographic -kernel $BUILD_DIR/mm.elf -m 512M -gdb tcp::3345 -S -monitor telnet::3347,server,nowait
主要区别在于1)GDB端口号不同(因此可以同时运行多个实例),以及2)Qemu将通过套接字上的telnet连接进行控制,因此可以由Python脚本进行控制./p>
The main differences being 1) the GDB port number is different (so multiple instances can run simultaneously) and 2) Qemu is to be controlled using a telnet connection over a socket, so it can be controlled by the Python script.
推荐答案
在尝试输出任何字符之前,您需要初始化UART. 例如,通过使用UART0仿真可以很好地工作. >此程序:
You need to initialize the UART prior to attempt outputing any characters. The
UART0
emulation is working fine for example by using a slightly modified version of this program:/opt/qemu-4.2.0/bin/qemu-system-arm -semihosting --semihosting-config enable=on,target=native -nographic -serial mon:stdio -machine xilinx-zynq-a9 -m 768M -cpu cortex-a9 -kernel hello05.elf Hello number 1
git diff
命令的修改后的输出为:The output of the
git diff
command after modifications were made was:diff --git a/Hello01/Makefile b/Hello01/Makefile index 4a1b512..8d6d12a 100644 --- a/Hello01/Makefile +++ b/Hello01/Makefile @@ -1,10 +1,10 @@ ARMGNU ?= arm-linux-gnueabihf -COPS = +COPS = -g -O0 ARCH = -mcpu=cortex-a9 -mfpu=vfpv3 gcc : hello01.bin -all : gcc clang +all : gcc clean : rm -f *.o @@ -15,8 +15,6 @@ clean : rm -f *.img rm -f *.bc -clang: hello02.bin - startup.o : startup.s $(ARMGNU)-as $(ARCH) startup.s -o startup.o diff --git a/Hello01/hello01.c b/Hello01/hello01.c index 20cb4a4..14ed2a0 100644 --- a/Hello01/hello01.c +++ b/Hello01/hello01.c @@ -10,16 +10,16 @@ */ -#define UART1_BASE 0xe0001000 -#define UART1_TxRxFIFO0 ((unsigned int *) (UART1_BASE + 0x30)) +#define UART0_BASE 0xe0000000 +#define UART0_TxRxFIFO0 ((unsigned int *) (UART0_BASE + 0x30)) -volatile unsigned int * const TxRxUART1 = UART1_TxRxFIFO0; +volatile unsigned int * const TxRxUART0 = UART0_TxRxFIFO0; void print_uart1(const char *s) { while(*s != '\0') { /* Loop until end of string */ - *TxRxUART1 = (unsigned int)(*s); /* Transmit char */ + *TxRxUART0 = (unsigned int)(*s); /* Transmit char */ s++; /* Next char */ } } @@ -28,4 +28,4 @@ void c_entry() { print_uart1("\r\nHello world!"); while(1) ; /*dont exit the program*/ -} \ No newline at end of file +} diff --git a/Hello05/Makefile b/Hello05/Makefile index 9d3ca23..bc9bb61 100644 --- a/Hello05/Makefile +++ b/Hello05/Makefile @@ -1,5 +1,5 @@ ARMGNU ?= arm-linux-gnueabihf -COPS = +COPS = -g -O0 ARCH = -mcpu=cortex-a9 -mfpu=vfpv3 gcc : hello05.bin diff --git a/Hello05/hello05.c b/Hello05/hello05.c index 1b92dde..01ce7ee 100644 --- a/Hello05/hello05.c +++ b/Hello05/hello05.c @@ -26,7 +26,7 @@ void c_entry() { - init_uart1_RxTx_115200_8N1(); + init_uart0_RxTx_115200_8N1(); printf("\nHello number %d\n",1); while(1) ; /*dont exit the program*/ } diff --git a/Hello05/xuartps.c b/Hello05/xuartps.c index bdf7ad1..74f68bd 100644 --- a/Hello05/xuartps.c +++ b/Hello05/xuartps.c @@ -16,42 +16,42 @@ void putc(int *p ,char c); /* -* Initiate UART1 ( /dev/ttyACM0 on host computer ) +* Initiate UART0 ( /dev/ttyACM0 on host computer ) * 115,200 Baud 8-bit No-Parity 1-stop-bit */ -void init_uart1_RxTx_115200_8N1() +void init_uart0_RxTx_115200_8N1() { /* Disable the transmitter and receiver before writing to the Baud Rate Generator */ - UART1->control_reg0=0; + UART0->control_reg0=0; /* Set Baudrate to 115,200 Baud */ - UART1->baud_rate_divider =XUARTPS_BDIV_CD_115200; - UART1->baud_rate_gen= XUARTPS_BRGR_CD_115200; + UART0->baud_rate_divider =XUARTPS_BDIV_CD_115200; + UART0->baud_rate_gen= XUARTPS_BRGR_CD_115200; /*Set 8-bit NoParity 1-StopBit*/ - UART1->mode_reg0 = XUARTPS_MR_PAR_NONE; + UART0->mode_reg0 = XUARTPS_MR_PAR_NONE; /*Enable Rx & Tx*/ - UART1->control_reg0= XUARTPS_CR_TXEN | XUARTPS_CR_RXEN | XUARTPS_CR_TXRES | XUARTPS_CR_RXRES ; + UART0->control_reg0= XUARTPS_CR_TXEN | XUARTPS_CR_RXEN | XUARTPS_CR_TXRES | XUARTPS_CR_RXRES ; } -void sendUART1char(char s) +void sendUART0char(char s) { /*Make sure that the uart is ready for new char's before continuing*/ - while ((( UART1->channel_sts_reg0 ) & UART_STS_TXFULL) > 0) ; + while ((( UART0->channel_sts_reg0 ) & UART_STS_TXFULL) > 0) ; /* Loop until end of string */ - UART1->tx_rx_fifo= (unsigned int) s; /* Transmit char */ + UART0->tx_rx_fifo= (unsigned int) s; /* Transmit char */ } /* "print.h" uses this function for is's printf implementation */ void putchar(char c) { if(c=='\n') - sendUART1char('\r'); - sendUART1char(c); + sendUART0char('\r'); + sendUART0char(c); } /* <stdio.h>'s printf uses puts to send chars @@ -61,9 +61,9 @@ int puts(const char *s) while(*s != '\0') { if(*s=='\n') - sendUART1char('\r'); + sendUART0char('\r'); - sendUART1char(*s); /*Send char to the UART1*/ + sendUART0char(*s); /*Send char to the UART0*/ s++; /* Next char */ } return 0; diff --git a/Hello05/xuartps.h b/Hello05/xuartps.h index fc5008f..64e3b88 100644 --- a/Hello05/xuartps.h +++ b/Hello05/xuartps.h @@ -13,7 +13,7 @@ #define u32 unsigned int #endif -#define UART1_BASE 0xe0001000 +#define UART0_BASE 0xe0000000 // Register Description as found in // B.33 UART Controller (UART) p.1626 struct XUARTPS{ @@ -34,7 +34,7 @@ struct XUARTPS{ u32 Flow_delay_reg0; /* Flow Control Delay Register def=0*/ u32 Tx_FIFO_trigger_level;}; /* Transmitter FIFO Trigger Level Register */ -static struct XUARTPS *UART1=(struct XUARTPS*) UART1_BASE; +static struct XUARTPS *UART0=(struct XUARTPS*) UART0_BASE; /* Page 496 @@ -87,11 +87,11 @@ static struct XUARTPS *UART1=(struct XUARTPS*) UART1_BASE; #define XUARTPS_MR_CLKS_REF_CLK 0 /* 0: clock source is uart_ref_clk*/ /* -* Initiate UART1 ( /dev/ttyACM0 on host computer ) +* Initiate UART0 ( /dev/ttyACM0 on host computer ) * 115,200 Baud 8-bit No-Parity 1-stop-bit */ -void init_uart1_RxTx_115200_8N1(); -void sendUART1char(char s); +void init_uart0_RxTx_115200_8N1(); +void sendUART0char(char s); int puts(const char *s); //void putc((void*), char);
从
ZedBoard-BareMetal-Examples/Hello05
目录执行的用于构建修改后的Hello05
示例的命令为:The command executed from the
ZedBoard-BareMetal-Examples/Hello05
directory for building the modifiedHello05
example was:make ARMGNU=/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-arm-none-eabi/bin/arm-none-eabi clean all
这就是您的上一篇文章的最后评论使我认为您可能只希望能够看到程序的输出,但不一定必须使用
UART0
.This being said, the last comment from your previous post made me think that you may just want to be able to see the output of your program, but not necessarily by using
UART0
.在这种情况下,使用天使/半主机接口可以工作-我了解您可能尝试过这种方式.
If this is the case, using the Angel/Semihosting interface would do the job - I understand you may have attempted to go this way.
示例:
// hello.c: #include <stdlib.h> int main(int argc, char** argv) { printf("Hello, World!\n"); return EXIT_SUCCESS; }
gcc命令:
/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-arm-none-eabi/bin/arm-none-eabi-gcc -g -O0 --specs=rdimon.specs -o hello.elf hello.c
qemu命令:
/opt/qemu-4.2.0/bin/qemu-system-arm -semihosting --semihosting-config enable=on,target=native -nographic -serial mon:stdio -machine xilinx-zynq-a9 -m 768M -cpu cortex-a9 -kernel hello.elf
结果:
Hello, World!
使用半主机界面将允许您读取/写入文件,读取用户输入以及使用某些 CppUnit 和
QEMU
以及半主机界面..Using the semihosting interface would allow you to read/write files, read user input, and to use some of the xUnit testing frameworks available for either C or C++ - I have been for example successfully be using CppUnit with
QEMU
and the Semihosting interface. at several occasions.我希望有帮助.
这篇关于Qemu裸机仿真-如何查看UART输出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
我正在使用的ELF文件(mm.elf
)执行一个简单的矩阵乘法运算,然后打印它是成功还是失败,以及运行了多长时间. ELF是使用Xilinx ARM工具链编译的.我将其用于软件故障注入.目前,我使用GDB询问应该打印的变量的值.但是,由于在故障注入的情况下打印时可能会出错,因此很高兴看到通过UART实际发送了什么.
The ELF file I am using (mm.elf
) performs a simple matrix multiply operation, and then prints whether it succeeded or failed, and how long it took to run. The ELF was compiled using the Xilinx ARM toolchain. I am using this for software fault injection. Currently I use GDB to ask for the values of the variables which are supposed to be printed. However, as there are many things which could go wrong with printing in the context of fault injection, it would be nice to see what is actually sent over UART.
这是我尝试过的一些建议,但不适用,因为问题是关于在主机终端窗口中获取Linux启动消息.
This has some suggestions I tried, but it isn't applicable because the question was about getting the Linux boot messages in the host terminal window.
这不是很相关,因为它仍然假定用户具有某种引导程序.从技术上说,必须完全有引导加载程序才能运行该应用程序,但Xilinx在
This is one isn't very related because it still assumes that the user has a bootloader of some kind. While there must technically be a bootloader for the application to run at all, Xilinx provides this system code in files like boot.S, which are then compiled into the ELF file as code which runs before main
.
我尝试将所有这些添加到当前Qemu命令的末尾.结果遵循尝试的参数.
I tried adding each of these onto the end of my current Qemu command. The results follow the parameters tried.