Linux 内核模块作弊 - Qemu Baremetal Xilinx Zynq A9 [英] Linux Kernel Module Cheat - Qemu Baremetal Xilinx Zynq A9

查看:23
本文介绍了Linux 内核模块作弊 - Qemu Baremetal Xilinx Zynq A9的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的目标是模拟 Zynq-7000 上的 ARM A9 处理器,运行裸机软件.我为此尝试了 2 种不同的方法,但都遇到了障碍.任何有关如何进行的建议将不胜感激.

My goal is to emulate the ARM A9 processor as found on the Zynq-7000, running baremetal software.  I have tried 2 different approaches to this and run into road blocks on both.  Any suggestions on how to proceed would be appreciated.

链接到
Linux 内核模块作弊 (LKMC, 使用 v3.0)
使用 ./build --arch arm qemu-baremetal

which links to
Linux Kernel Module Cheat (LKMC, using v3.0)
built using ./build --arch arm qemu-baremetal

网站上使用 ARM 虚拟机的示例(-virt 标志)工作正常.尝试修改它以适用于我的设置是导致问题的原因(详情如下).

The examples on the site of using the ARM virtual machine (-virt flag) work fine. Trying to modify this to work with my setup is what's causing problems (details below).

我尝试复制示例命令行调用,但使用了 -cpu cortex-a9 选项:
qemu-system-arm: mach-virt: CPU cortex-a9 不支持
然后我将整个调用更改为
qemu-system-arm -M xilinx-zynq-a9 -cpu cortex-a9 -nographic -kernel hello.elf -m 512M -serial mon:stdio -s -S
它因错误而崩溃
qemu:fatal: 试图在 0x40000000 处的 RAM 或 ROM 之外执行代码
这是有道理的,因为应用程序是用 LKMC 构建的,而我试图在该框架之外运行它.

I tried to copy the example command line invocation, but with the -cpu cortex-a9 option instead:
qemu-system-arm: mach-virt: CPU cortex-a9 not supported
Then I changed the whole invocation to be
qemu-system-arm -M xilinx-zynq-a9 -cpu cortex-a9 -nographic -kernel hello.elf -m 512M -serial mon:stdio -s -S
And it crashed with the error
qemu: fatal: Trying to execute code outside RAM or ROM at 0x40000000
Which makes sense, because the application was built with the LKMC, and I was trying to run it outside of that framework.

然后我尝试运行我自己的应用程序,该应用程序是使用 Xilinx 工具链的修改版本编译的.我确定它不会立即起作用,因为我必须更改启动顺序的某些部分.但我试图弄清楚那些是什么并改变它们.与
一起跑步qemu-system-arm -M xilinx-zynq-a9 -cpu cortex-a9 -nographic -kernel helloworld.elf -m 512M -serial mon:stdio -s -S
允许 GDB 连接成功,但无法正确读取符号表.使用
arm-none-eabi-objdump -D helloworld.elf
告诉我 main0x001004fc,但 GDB 认为它在 0x40000324(使用命令 info address main).

So then I tried running my own application, which was compiled using a modified version of the Xilinx toolchain. I'm sure it won't work right away, as there will be some parts of the bootup sequence that I have to change. But I'm trying to figure out what those are and change them. Running with
qemu-system-arm -M xilinx-zynq-a9 -cpu cortex-a9 -nographic -kernel helloworld.elf -m 512M -serial mon:stdio -s -S
allows GDB to connect successfully, but it can't read the symbol table properly. Using
arm-none-eabi-objdump -D helloworld.elf
tells me that main is at 0x001004fc, but GDB thinks it's at 0x40000324 (using the command info address main).

PYNQ-Z1(网页数据表) 具有 32 位 ARM Cortex-A9 处理器,这就是我使用 qemu-system-arm 而不是 qemu-system-aarch64 的原因.如果错了,有人可以纠正我.

The PYNQ-Z1 (webpage, datasheet) has a 32-bit ARM Cortex-A9 processor, so that's why I'm using qemu-system-arm instead of qemu-system-aarch64. Someone can correct me if that's wrong.

请注意,我不能简单地切换到不同的架构;我使用的代码不能容忍更改,只能进行小幅调整以使板级支持包 (BSP) 与模拟器兼容,同时又不损害我研究的有效性.

As a note, I cannot simply switch to a different architecture; the code that I am using cannot tolerate changes other than small tweaks to make the Board Support Package (BSP) compatible with the simulator, without harming the validity of my research.

我最近一直在关注的是
./run --arch arm -m 512M --baremetal pynq/helloworld --wait-gdb
./run-gdb --arch arm --baremetal pynq/helloworld --no-continue -- main
然后我逐步使用 GDB 查找数据中止的位置,并找出 Qemu 不支持哪种硬件.

What I have been going off of for a while now is
./run --arch arm -m 512M --baremetal pynq/helloworld --wait-gdb
./run-gdb --arch arm --baremetal pynq/helloworld --no-continue -- main
and I step through using GDB to find where there are data aborts and figure out what kind of hardware is not supported by Qemu.

我运行的软件是使用修改后的 Xilinx 工具链构建的,因此包含许多 Xilinx 标准库函数.在修改代码以使用虚拟机时,我发现了一些更改,例如更改 UART 设备的地址并禁用一些启动任务,例如使 SCU 无效或更改缓存控制器配置,大概是因为这些东西不是 Qemu 模仿的.

The software that I am running is built using a modified Xilinx toolchain, and so includes many of the Xilinx standard library functions.  In modifying the code to work with the virtual machine, I have discovered a few changes so far, such as changing the address of the UART device and disabling some boot-up tasks such as invalidating the SCU or changing the cache controller configuration, presumably because these things are not emulated by Qemu.

在调试启动时,我在启动时遇到的下一个问题是 XTime 函数 (xtime_l.c).这些函数是读取全局系统计时器的包装器.Qemu 界面中命令 info mtree 的结果似乎表明没有可与之交互的全局计时器设备.有没有办法给ARM虚拟机添加定时器设备?基地址是什么并不重要,只要能像在Zynq上一样使用,使用寄存器读写即可.

When debugging bootup, the next problem I have run into booting up is the XTime functions (xtime_l.c).  These functions are wrappers around reading the global system timer.  The results of the command info mtree in the Qemu interface seem to indicate that there is no global timer device with which to interact.  Is there a way to add a timer device to the ARM virtual machine? It doesn't matter what the base address is, as long as it can be used in the same way as on the Zynq, using register reads and writes.

然后我尝试使用特定的机器标志xilinx-zynq-a9.LKMC 生成以下命令:

Then I tried to use the specific machine flag xilinx-zynq-a9.  LKMC generates the following command:

+ /home/$USER/$INSTALL_DIR/out/qemu/default/arm-softmmu/qemu-system-arm \
  -machine xilinx-zynq-a9 \
  -gdb tcp::45457 \
  -kernel /home/$USER/$INSTALL_DIR/out/baremetal/arm/qemu/xilinx-zynq-a9/hello.elf \
  -m 512M \
  -monitor telnet::45454,server,nowait \
  -netdev user,hostfwd=tcp::45455-:45455,hostfwd=tcp::45456-:22,id=net0 \
  -no-reboot \
  -smp 1 \
  -virtfs local,path=/home/$USER/$INSTALL_DIR/data/9p,mount_tag=host_data,security_model=mapped,id=host_data \
  -virtfs local,path=/home/$USER/$INSTALL_DIR/out,mount_tag=host_out,security_model=mapped,id=host_out \
  -virtfs local,path=/home/$USER/$INSTALL_DIR/rootfs_overlay,mount_tag=host_rootfs_overlay,security_model=mapped,id=host_rootfs_overlay \
  -serial mon:stdio \
  -trace enable=load_file,file=/home/$USER/$INSTALL_DIR/out/run/qemu/arm/0/trace.bin \
  -cpu cortex-a9 \
  -device virtio-gpu-pci \
  -nographic \
  -serial tcp::45458,server,nowait \
  -semihosting \
;

这与通用虚拟机之间的唯一区别是指定机器和 cpu 的行,以前是 -machine virt -machine highmem=off-cpu cortex-a15 (我实际上不得不修改 LKMC 代码才能让它为机器输出正确的 cpu 名称).

The only differences between this and the generic virtual machine are the lines that specify the machine and the cpu, which used to be -machine virt  -machine highmem=off and -cpu cortex-a15 respectively (I actually had to modify the LKMC code to get it to output the correct cpu name for the machine).

然而,这失败并出现错误
qemu-system-arm: -device rtl8139,netdev=net0: 未找到设备 'rtl8139' 的 'PCI' 总线
这是有道理的,因为并非所有 Zynq 部件都有 PCI 总线.所以我主要想知道为什么 LKMC 会在目标是裸机的情况下生成这样的命令序列.

However, this fails with the error
qemu-system-arm: -device rtl8139,netdev=net0: No 'PCI' bus found for device 'rtl8139'
This makes sense, because not all Zynq parts have PCI buses.  So mostly I am wondering why LKMC would generate such a sequence of commands when the target is baremetal anyway.

我认为第一个选项最有可能起作用,因为看起来 -virt 机器比某些特定目标具有更好的支持.有趣的是,Xilinx SDK 附带的 Qemu 版本不支持带有 Zynq 的裸机(在 Xilinx 文档).

The first option I think is the most likely to work, since it seems like the -virt machine has better support than some of the specific targets.  It is interesting that the version of Qemu that ships with the Xilinx SDK does not support baremetal with the Zynq (referred to as "standalone" in the Xilinx Docs).

有没有办法给ARM虚拟机添加定时器设备?
有没有人在 Qemu Xilinx ARM A9 上运行过裸机代码?

Is there a way to add a timer device to the ARM virtual machine?
Has anyone run baremetal code on Qemu Xilinx ARM A9?

我已尝试尽可能具体,但请随时提出澄清问题.

I've tried to be as specific as possible, but feel free to ask clarifying questions.

推荐答案

  1. 这可能比添加对计时器的支持更重要设备到现有的 QEMU 机器.更具体地说,这可能不会需要,因为其中相当多的要么支持 ARM架构定时器或特定的定时器硬件.

    在 xilinx-zynq-a9 的特定情况下,似乎 Global定时器计数器来自的第1448页描述Zynq-7000 技术支持参考手册.
  2. 在多次阅读您的帖子后,我得出的结论是,您使用的工具集(KMC、工具链、QEMU)可能会出现很多问题.因此,我创建了一个与 QEMU xilinx 一起使用的裸机应用程序的最小的、可重现的示例-zynq-a9 机器使用我信任的 arm 工具链,以及最新版本的 QEMU 4.2.0,使用我编写的脚本从头开始构建.
  1. This is probably a non-trivial task than to add support for a timer device to an existing QEMU machine. More specifically, this may not be needed since a fair amount of them either support an ARM architectural timer or a specific timer hardware.

    In the specific case of the xilinx-zynq-a9, it seem the Global Timer Counter described from page 1448 of the Zynq-7000 Technical Reference Manual is supported.
  2. After having reading your post a couple of times, I reached the conclusion that a lot of things may go wrong with the set of tools you are using (KMC, toolchain, QEMU). I therefore created what I hope is a Minimal, Reproducible Example of a bare-metal application working with a QEMU xilinx-zynq-a9 machine using an arm toolchain I do trust, and the latest version of QEMU, 4.2.0, built from scratch using a script I wrote.

请注意,为了回答您的问题,我改编了一个现有的个人项目,我已经可用,并且我知道正在工作.

Please note that I adapted an existing personal project I already had available, and I know is working, for the purpose of answering your question.

构建 QEMU:执行 build-qemu.sh - 此脚本适用于 64 位 Ubuntu 18.04 和 19.10,您必须将 PERL_MODULES_VERSION 设置为 5.28.

Building QEMU: execute build-qemu.sh - this script works on 64 bits Ubuntu 18.04 and 19.10, you will have to set PERL_MODULES_VERSION to 5.28.

build-qemu.sh:

#!/bin/bash

set -e 

QEMU_VERSION=4.2.0
# xenial
PERL_MODULES_VERSION=5.22
# eoan
PERL_MODULES_VERSION=5.28

# bionic
PERL_MODULES_VERSION=5.26

PREFIX=/opt/qemu-${QEMU_VERSION}

do_install_prerequisites()
{
  sudo apt-get install libglib2.0-dev libfdt-dev libpixman-1-dev zlib1g-dev libaio-dev libbluetooth-dev libbrlapi-dev libbz2-dev  libcap-dev libcap-ng-dev libcurl4-gnutls-dev libgtk-3-dev libibverbs-dev \
  libjpeg8-dev libncurses5-dev libnuma-dev librbd-dev librdmacm-dev libsasl2-dev libsdl2-dev libseccomp-dev libsnappy-dev libssh2-1-dev libvde-dev libvdeplug-dev libvte-2.91-dev libxen-dev liblzo2-dev \
  valgrind xfslibs-dev liblzma-dev flex bison texinfo perl perl-modules-${PERL_MODULES_VERSION}  python-sphinx gettext
}

do_download_qemu()
{
    if [ ! -f qemu-${QEMU_VERSION}.tar.xz ]
  then
    wget https://download.qemu.org/qemu-${QEMU_VERSION}.tar.xz
  fi
}

do_extract_qemu()
{
  echo "extracting..."
  rm -rf qemu-${QEMU_VERSION}
  tar Jxf qemu-${QEMU_VERSION}.tar.xz
}

do_configure_qemu()
{
  local TARGET_LIST="arm-softmmu"
  pushd qemu-${QEMU_VERSION}
  ./configure --target-list="${TARGET_LIST}" --prefix=${PREFIX} --extra-cflags="-I$(pwd)/packages/include" --extra-ldflags="-L$(pwd)/packages/lib" 
  popd
}


do_build_qemu()
{
  echo "building..."
  pushd qemu-${QEMU_VERSION}
  make all
  popd
}

do_install_qemu()
{
  echo "installing..."
  pushd qemu-${QEMU_VERSION}
  sudo make install
  popd
}


do_build()
{
  do_download_qemu
  do_extract_qemu
  do_configure_qemu
  do_build_qemu
  do_install_qemu
}

# main

do_install_prerequisites
do_build

脚本完成后,您应该已经安装了 qemu-system-user 4.2.0:

Upon the script completion, you should have qemu-system-user 4.2.0 installed:

ls -gG /opt/qemu-4.2.0/bin/
total 22992
-rwxr-xr-x 1    22520 Feb 21 23:57 elf2dmp
-rwxr-xr-x 1    18424 Feb 21 23:57 ivshmem-client
-rwxr-xr-x 1   218264 Feb 21 23:57 ivshmem-server
-rwxr-xr-x 1    30864 Feb 21 23:57 qemu-edid
-rwxr-xr-x 1   374328 Feb 21 23:57 qemu-ga
-rwxr-xr-x 1  1767744 Feb 21 23:57 qemu-img
-rwxr-xr-x 1  1719104 Feb 21 23:57 qemu-io
-rwxr-xr-x 1   505016 Feb 21 23:57 qemu-keymap
-rwxr-xr-x 1  1727744 Feb 21 23:57 qemu-nbd
-rwxr-xr-x 1   599848 Feb 21 23:57 qemu-pr-helper
-rwxr-xr-x 1 16510840 Feb 21 23:57 qemu-system-arm
-rwxr-xr-x 1    26856 Feb 21 23:57 virtfs-proxy-helper

我们现在需要创建以下文件:

We now need to create the following files:

gcc_arm32_ram.ld(改编自标准 GCC CMSIS 5.60 链接脚本):

gcc_arm32_ram.ld (adapted from the standard GCC CMSIS 5.60 linker script):

/******************************************************************************
 * @file     gcc_arm32.ld
 * @brief    GNU Linker Script for Cortex-M based device
 * @version  V2.0.0
 * @date     21. May 2019
 ******************************************************************************/
/*
 * Copyright (c) 2009-2019 Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

MEMORY
{
  RAM   (rwx) : ORIGIN = __RAM_BASE, LENGTH = __RAM_SIZE
}

/* Linker script to place sections and symbol values. Should be used together
 * with other linker script that defines memory regions FLASH and RAM.
 * It references following symbols, which must be defined in code:
 *   Reset_Handler : Entry of reset handler
 *
 * It defines following symbols, which code can use without definition:
 *   __exidx_start
 *   __exidx_end
 *   __copy_table_start__
 *   __copy_table_end__
 *   __zero_table_start__
 *   __zero_table_end__
 *   __etext
 *   __data_start__
 *   __preinit_array_start
 *   __preinit_array_end
 *   __init_array_start
 *   __init_array_end
 *   __fini_array_start
 *   __fini_array_end
 *   __data_end__
 *   __bss_start__
 *   __bss_end__
 *   __end__
 *   end
 *   __HeapLimit
 *   __StackLimit
 *   __StackTop
 *   __stack
 */
ENTRY(Reset_Handler)

SECTIONS
{
  .text :
  {
    KEEP(*(.vectors))
    *(.text*)

    KEEP(*(.init))
    KEEP(*(.fini))

    /* .ctors */
    *crtbegin.o(.ctors)
    *crtbegin?.o(.ctors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
    *(SORT(.ctors.*))
    *(.ctors)

    /* .dtors */
    *crtbegin.o(.dtors)
    *crtbegin?.o(.dtors)
    *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
    *(SORT(.dtors.*))
    *(.dtors)

    *(.rodata*)

    KEEP(*(.eh_frame*))
  } > RAM

  /*
   * SG veneers:
   * All SG veneers are placed in the special output section .gnu.sgstubs. Its start address
   * must be set, either with the command line option �--section-start� or in a linker script,
   * to indicate where to place these veneers in memory.
   */
/*
  .gnu.sgstubs :
  {
    . = ALIGN(32);
  } > RAM
*/
  .ARM.extab :
  {
    *(.ARM.extab* .gnu.linkonce.armextab.*)
  } > RAM

  __exidx_start = .;
  .ARM.exidx :
  {
    *(.ARM.exidx* .gnu.linkonce.armexidx.*)
  } > RAM
  __exidx_end = .;

  .copy.table :
  {
    . = ALIGN(4);
    __copy_table_start__ = .;
    LONG (__etext)
    LONG (__data_start__)
    LONG (__data_end__ - __data_start__)
    /* Add each additional data section here */
/*
    LONG (__etext2)
    LONG (__data2_start__)
    LONG (__data2_end__ - __data2_start__)
*/
    __copy_table_end__ = .;
  } > RAM

  .zero.table :
  {
    . = ALIGN(4);
    __zero_table_start__ = .;
    /* Add each additional bss section here */
/*
    LONG (__bss2_start__)
    LONG (__bss2_end__ - __bss2_start__)
*/
    __zero_table_end__ = .;
  } > RAM

  /**
   * Location counter can end up 2byte aligned with narrow Thumb code but
   * __etext is assumed by startup code to be the LMA of a section in RAM
   * which must be 4byte aligned 
   */
  __etext = ALIGN (4);

  .data : AT (__etext)
  {
    __data_start__ = .;
    *(vtable)
    *(.data)
    *(.data.*)

    . = ALIGN(4);
    /* preinit data */
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP(*(.preinit_array))
    PROVIDE_HIDDEN (__preinit_array_end = .);

    . = ALIGN(4);
    /* init data */
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP(*(SORT(.init_array.*)))
    KEEP(*(.init_array))
    PROVIDE_HIDDEN (__init_array_end = .);


    . = ALIGN(4);
    /* finit data */
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP(*(SORT(.fini_array.*)))
    KEEP(*(.fini_array))
    PROVIDE_HIDDEN (__fini_array_end = .);

    KEEP(*(.jcr*))
    . = ALIGN(4);
    /* All data end */
    __data_end__ = .;

  } > RAM

  /*
   * Secondary data section, optional
   *
   * Remember to add each additional data section
   * to the .copy.table above to asure proper
   * initialization during startup.
   */
/*
  __etext2 = ALIGN (4);

  .data2 : AT (__etext2)
  {
    . = ALIGN(4);
    __data2_start__ = .;
    *(.data2)
    *(.data2.*)
    . = ALIGN(4);
    __data2_end__ = .;

  } > RAM2
*/

  .bss :
  {
    . = ALIGN(4);
    __bss_start__ = .;
    *(.bss)
    *(.bss.*)
    *(COMMON)
    . = ALIGN(4);
    __bss_end__ = .;
  } > RAM AT > RAM

  /*
   * Secondary bss section, optional
   *
   * Remember to add each additional bss section
   * to the .zero.table above to asure proper
   * initialization during startup.
   */
/*
  .bss2 :
  {
    . = ALIGN(4);
    __bss2_start__ = .;
    *(.bss2)
    *(.bss2.*)
    . = ALIGN(4);
    __bss2_end__ = .;
  } > RAM2 AT > RAM2
*/

  .heap (COPY) :
  {
    . = ALIGN(8);
    __end__ = .;
    PROVIDE(end = .);
    . = . + __HEAP_SIZE;
    . = ALIGN(8);
    __HeapLimit = .;
  } > RAM

  .stack (ORIGIN(RAM) + LENGTH(RAM) - __STACK_SIZE) (COPY) :
  {
    . = ALIGN(8);
    __StackLimit = .;
    . = . + __STACK_SIZE;
    . = ALIGN(8);
    __StackTop = .;
  } > RAM
  PROVIDE(__stack = __StackTop);

  /* Check if data + heap + stack exceeds RAM limit */
  ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
}

Makefile.inc

# Shared Makefile 

.PHONY:         clean
all:            $(MACHINE).elf

$(MACHINE).elf: $(SOURCES) $(MACHINE).c
                $(CC) $(CFLAGS) $(LDFLAGS) -o $(MACHINE).elf $(MACHINE).c $(SOURCES)
                $(OBJDUMP) -d $(MACHINE).elf > $(MACHINE).lst

qemu:           $(MACHINE).elf
                $(QEMU_SYSTEM) -m 513M -nographic -machine $(MACHINE) $(QEMU_DEBUG_OPTIONS) -cpu $(CPU) -kernel $(MACHINE).elf

gdb:            $(MACHINE).elf
                $(GDB) --quiet --command=$(GDB_COMMANDS) $(MACHINE).elf

clean:
                rm -f $(MACHINE).elf $(MACHINE).lst

startup-aarch32.s:

                .title startup-aarch32.s
                .arch armv7-a
                .text
                .section .text.startup,"ax"    
                .globl Reset_Handler   
Reset_Handler:
                ldr r0, =__StackTop
                mov sp, r0
                bl start
wait:           wfe
                b wait
               .end

xilinx-zynq-a9.c:

#include <stdint.h>

/* Reference: https://www.xilinx.com/support/documentation/user_guides/ug585-Zynq-7000-TRM.pdf - page 1449.

 1. Read the upper 32-bit timer counter register
 2. Read the lower 32-bit timer counter register
 3. Read the upper 32-bit timer counter register again.
    If the value is different to the32-bit upper value read previously, go back to step 2.
    Otherwise the 64-bit timercounter value is correct.
*/

static const uintptr_t Global_Timer_Counter_Register0 = 0xF8F00200;
static const uintptr_t Global_Timer_Counter_Register1 = 0xF8F00204;

void start()
{
  uint64_t global_timer_counter = 0;
  uint32_t upper = 0;
  uint32_t upper2 = 0;
  uint32_t lower = 0;

  for (;;) {
    upper = *(volatile uint32_t*) Global_Timer_Counter_Register1;
    lower = *(volatile uint32_t*) Global_Timer_Counter_Register0;

    upper2 = *(volatile uint32_t*) Global_Timer_Counter_Register1;
    if (upper != upper2) {
        lower = *(volatile uint32_t*) Global_Timer_Counter_Register0;
    }

    global_timer_counter = (uint64_t) upper << 32 | lower;

  }
}

xilinx-zynq-a9.gdb:

target remote localhost:1234
monitor reset halt
load
break Reset_Handler
break start

xilinx-zynq-a9.ld:

/*
 *-------- <<< Use Configuration Wizard in Context Menu >>> -------------------
 */

/*--------------------- Embedded RAM Configuration ----------------------------
  <h> RAM Configuration
    <o0> RAM Base Address    <0x0-0xFFFFFFFF:8>
    <o1> RAM Size (in Bytes) <0x0-0xFFFFFFFF:8>
  </h>
 -----------------------------------------------------------------------------*/
__RAM_BASE = 0x00100000;
__RAM_SIZE = 0x20000000;

/*--------------------- Stack / Heap Configuration ----------------------------
  <h> Stack / Heap Configuration
    <o0> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
    <o1> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
  </h>
  -----------------------------------------------------------------------------*/
__STACK_SIZE = 0x00020000;
__HEAP_SIZE  = 0x00080000;

/*
 *-------------------- <<< end of configuration section >>> -------------------
 */

INCLUDE gcc_arm32_ram.ld

我指定了一个 512MiB 的 DDR RAM 区域,从 0x20000000 开始 - 有关 Zynq-7000 内存映射.

I specified a 512MiB of DDR RAM area starting from 0x20000000 - see here for more information on the Zynq-7000 memory map.

xilinx-zynq-a9.mak:

# Toolchain
CROSS_COMPILE=/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-arm-none-eabi/bin/arm-none-eabi-
CC=$(CROSS_COMPILE)gcc
OBJDUMP=$(CROSS_COMPILE)objdump
OBJCOPY=$(CROSS_COMPILE)objcopy

# Target
CPU=cortex-a9
MACHINE=xilinx-zynq-a9
CFLAGS=-O0 -ggdb -mtune=$(CPU) -nostdlib -nostartfiles -ffreestanding 
LDFLAGS=-L. -Wl,-T,$(MACHINE).ld
SOURCES=startup-aarch32.s 

# qemu
QEMU_DEBUG_OPTIONS=-S -gdb tcp::1234,ipv4
QEMU_SYSTEM=/opt/qemu-4.2.0/bin/qemu-system-arm

# GDB
GDB=$(CROSS_COMPILE)gdb
GDB_COMMANDS=${MACHINE}.gdb

include Makefile.inc

您现在需要安装

You now need yo install the latest GCC toolchain provided by arm:

wget "https://developer.arm.com/-/media/Files/downloads/gnu-a/9.2-2019.12/binrel/gcc-arm-9.2-2019.12-x86_64-arm-none-eabi.tar.xz?revision=64186c5d-b471-4c97-a8f5-b1b300d6594a&la=en&hash=5E9204DA5AF0B055B5B0F50C53E185FAA10FF625" -o gcc-arm-9.2-2019.12-x86_64-arm-none-eabi.tar.xz
mkdir -p /opt/arm/9
tar Jxf gcc-arm-9.2-2019.12-x86_64-arm-none-eabi.tar.xz -C /opt/arm/9

您现在已准备好编译/执行/调试示例:

You are now ready to compile/execute/debug the example:

make -f xilinx-zynq-a9.mak clean all

您应该得到以下输出:

rm -f xilinx-zynq-a9.elf xilinx-zynq-a9.lst
/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-arm-none-eabi/bin/arm-none-eabi-gcc -O0 -ggdb -mtune=cortex-a9 -nostdlib -nostartfiles -ffreestanding  -L. -Wl,-T,xilinx-zynq-a9.ld -o xilinx-zynq-a9.elf xilinx-zynq-a9.c startup-aarch32.s 
/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-arm-none-eabi/bin/arm-none-eabi-objdump -d xilinx-zynq-a9.elf > xilinx-zynq-a9.lst

您可以启动 QEMU:

You can start QEMU:

make -f xilinx-zynq-a9.mak qemu

应该显示已执行的 QEMU 命令:

The QEMU command that was executed should be displayed:

/opt/qemu-4.2.0/bin/qemu-system-arm -m 513M -nographic -machine xilinx-zynq-a9 -S -gdb tcp::1234,ipv4 -cpu cortex-a9 -kernel xilinx-zynq-a9.elf

在另一个 shell 中,启动 GDB:

In an other shell, start GDB:

make -f xilinx-zynq-a9.mak gdb

您应该看到以下输出:

/opt/arm/9/gcc-arm-9.2-2019.12-x86_64-arm-none-eabi/bin/arm-none-eabi-gdb --quiet --command=xilinx-zynq-a9.gdb xilinx-zynq-a9.elf
Reading symbols from xilinx-zynq-a9.elf...
Reset_Handler () at startup-aarch32.s:7
7                       ldr r0, =__StackTop
unknown command: 'reset'
Loading section .text, size 0xd8 lma 0x100000
Loading section .copy.table, size 0xc lma 0x1000d8
Start address 0x1000b8, load size 228
Transfer rate: 222 KB/sec, 114 bytes/write.
Breakpoint 1 at 0x1000b8: file startup-aarch32.s, line 7.
Breakpoint 2 at 0x10000c: file xilinx-zynq-a9.c, line 17.
(gdb) 

执行继续:

(gdb) continue
Continuing.

Breakpoint 2, start () at xilinx-zynq-a9.c:17
17        uint64_t global_timer_counter = 0;

现在,执行几个step命令,并在每一行显示global_timer_counter变量:

Now, execute several step commands, and display the global_timer_counter variable everytime line:

31          global_timer_counter = (uint64_t) upper << 32 | lower;

被执行:

(gdb) p/x global_timer_counter
$2 = 0xa6f2a0a

(gdb) p/x global_timer_counter
$9 = 0xa84315b

(gdb)  p/x global_timer_counter
$10 = 0xabe77cf

64 位变量不断递增 ¸ 这与 QEMU 对 Zynq 全局定时器计数器的工作仿真一致,我们现在有一个可以调试的裸机 Zynq-7000 示例使用 GDB.

The 64 bits variable keeps incrementing ¸which is consistant with a working emulation of the Zynq Global Timer Counter by QEMU, and we now have a working bare-metal Zynq-7000 example that can be debugged using GDB.

我希望我回答了你提出的两个问题.

I hope I answered to the two questions you asked.

这篇关于Linux 内核模块作弊 - Qemu Baremetal Xilinx Zynq A9的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆