Qemu baremetal эмуляция - как просмотреть вывод UART? - PullRequest
0 голосов
/ 05 марта 2020

Вопрос:

Как получить выходные данные UART из программы, работающей с неизолированными металлами, с Qemu?

Фон

Вот вызов командной строки, который я использовал:

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 МБ ОЗУ
  • -s - это ярлык для -gdb tcp::1234
  • -S означает зависание ЦП при запуске

Используемый мной файл ELF (mm.elf) выполняет простую операцию умножения матриц, а затем печатает, успешно ли она выполнена или нет, и сколько времени потребовалось для ее запуска. ELF был скомпилирован с использованием набора инструментов Xilinx ARM. Я использую это для программного обеспечения ошибка инъекции . В настоящее время я использую GDB для запроса значений переменных, которые должны быть напечатаны. Тем не менее, поскольку есть много вещей, которые могут go не работать с печатью в контексте инъекции ошибок, было бы неплохо увидеть, что на самом деле отправляется через UART.

Связанные ответы:

перенаправить вывод окна QEMU на терминал, на котором запущен qemu

Это несколько советов, которые я пробовал, но они не применимы, потому что вопрос был о получении загрузочных сообщений Linux в окне хост-терминала .

Как запустить программу без операционной системы?

Это не очень связано, так как предполагает, что у пользователя есть какой-то загрузчик , В то время как технически должен существовать загрузчик для запуска приложения, Xilinx предоставляет этот системный код в файлах, таких как boot.S , которые затем компилируются в файл ELF в виде кода, который выполняется до main.

То, что я пробовал:

Я попытался добавить каждый из них в конец моей текущей команды Qemu. Результаты соответствуют опробованным параметрам.

  • -serial mon:stdio
    • Ничего
  • -serial null -serial mon:stdio (потому что Cortex-A9 имеет два UART)
    • ничего
  • вышеупомянутые два с добавлением -semihosting добавлено
    • ничего
  • -serial stdio
    • не может использовать stdio несколькими символьными устройствами
    • не может подключить последовательное устройство к символьному бэкэнду 'stdio'
  • -console=/dev/tty
    • недопустимая опция
  • -curses
    • черный экран без вывода

Расследование

Я посмотрел на разборку файла ELF и убедился, что адрес, на который пишутся сообщения UART, совпадает с ожидаемым при настройке Qemu (info mtree). Базовый адрес 0xe0000000, одинаковый в обоих местах.

Цель

Я хочу иметь возможность фиксировать вывод сообщений, отправленных в UART. Если это делается путем перенаправления на стандартный вывод, это нормально. Если он проходит через сокет TCP, это тоже нормально. В настройке ввода ошибок используется Python, а Qemu работает как подпроцесс, поэтому было бы легко получить выходные данные из любого из этих источников.

Примечание: при запуске в настройке ввода ошибок Вызов Qemu:

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 осуществляется с помощью соединения te lnet через сокет так что им можно управлять с помощью скрипта Python.

1 Ответ

0 голосов
/ 06 марта 2020

Вам нужно инициализировать UART до попытки вывода каких-либо символов. Эмуляция UART0 работает нормально, например, с использованием слегка измененной версии этой программы :

/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 после внесения изменений был:

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, была:

make ARMGNU=/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-arm-none-eabi/bin/arm-none-eabi clean all 

Как говорится, последний комментарий из вашего предыдущего поста заставил меня подумать, что вы можете просто захотеть увидеть вывод вашей программы, но не обязательно с помощью UART0.

Если это так, то с помощью интерфейса Angel / Semihosting все будет в порядке - я так понимаю, вы могли попытаться go таким образом.

Пример:

// hello.c:

#include <stdlib.h>

int main(int argc, char** argv)
{
    printf("Hello, World!\n");
    return EXIT_SUCCESS;
}

g cc команда:

/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!

Использование полухостинга Интерфейс позволит вам читать / записывать файлы, читать пользовательский ввод и использовать некоторые из xUnit платформ тестирования, доступных для C или C ++ - например, я успешно использую CppUnit с QEMU и интерфейсом Semihosting. несколько раз.

Я надеюсь, что эта помощь.

...