QEMU AARCH64“虚拟”机器SMP CPU从“运行”状态到“停止”状态开始

时间:2019-10-15 16:55:50

标签: qemu arm64

我正在研究裸机。没有Linux,库等。我正在ASM中编写处理器启动代码,然后跳转到编译的C代码。

我的命令行是:

% qemu-system-aarch64 \
   -s -S \
   -machine virt,secure=on,virtualization=on \
   -cpu cortex-a53 \
   -d int \
   -m 512M \
   -smp 4 \
   -display none \
   -nographic \
   -semihosting \
   -serial mon:stdio \
   -kernel my_file.elf \
   -device loader,addr=0x40004000,cpu-num=0 \
   -device loader,addr=0x40004000,cpu-num=1 \
   -device loader,addr=0x40004000,cpu-num=2 \
   -device loader,addr=0x40004000,cpu-num=3 \
   ;

当我开始连接gcc时,我可以看到:

(gdb) info threads
  Id   Target Id     Frame
 * 1   Thread 1.1 (CPU#0 [running]) _start () at .../start.S:20
   2   Thread 1.2 (CPU#1 [halted ]) _start () at .../start.S:20
   3   Thread 1.3 (CPU#2 [halted ]) _start () at .../start.S:20
   4   Thread 1.4 (CPU#3 [halted ]) _start () at .../start.S:20

我希望其他三个处理器以“运行”状态而不是“暂停”状态启动。怎么样?

请注意,我的DTS包含以下部分:

psci {
    migrate = < 0xc4000005 >;
    cpu_on = < 0xc4000003 >;
    cpu_off = < 0x84000002 >;
    cpu_suspend = < 0xc4000001 >;
    method = "smc";
    compatible = "arm,psci-0.2\0arm,psci";
};

但是,我不确定该怎么办。添加许多这种形式的行似乎没有帮助:

-device loader,addr=0xc4000003,data=0x80000000,data-len=4

我不确定这件事ARM PSCI是否正确? ARM的规范似乎定义了“接口”,而不是系统的“实现”。但是,我不认为PSCI是“虚拟”文档/源中提到的“真实”寄存器。 DTS中没有提到“ SMC”设备。

QEMU如何确定SMP处理器在启动时是“运行”还是“停止”,又如何影响呢?


基于下面@ Peter-Maydell的回答,我需要做以下两件事之一...

  1. 将“ -kernel”切换为“ -bios”。我这样做,但是我的代码没有按预期加载。我的* .elf文件有几个部分;有些在FLASH中,有些在DDR中(0x40000000以上)。也许是问题所在?
  2. 更改启动代码以进行设置并发出SMC指令,以使QEMU将识别并启动其他处理器的ARM PSCI“ CPU_ON”调用。 这样的代码可以运行,但似乎没有“做”任何事情...

    ldr   w0, =0xc4000003   // CPU_ON code from the DTS file
    mov   x1, 1             // CPU #1 in cluster zero (format of MPIDR register?)
    ldr   x2, _boot         // Jump address 0x40006000 (FYI)
    mov   x3, 1             // context ID (meaningful only to caller)
    smc   #0                // GO!
    // result is in x0 -> PSCI_RET_INVALID_PARAMS
    

2 个答案:

答案 0 :(得分:1)

这取决于主板型号-通常我们遵循硬件的功能,有些主板从加电时启动所有CPU,有些则没有。对于“虚拟”板(特定于QEMU),我们通常使用PSCI,这是Arm标准固件接口,用于为SMP CPU供电(包括其他功能);您还可以将其用于“关闭整个电源”。机器”)。在启动时,只有主CPU在运行,而来宾代码的工作是使用PSCI API启动辅助。这就是DTS中的psci节点告诉来宾的内容,它告诉来宾的PSCI ABI QEMU实现了什么特定形式,特别是来宾是否应使用“ hvc”或“ smc”指令来调用PSCI函数。 QEMU在这里所做的是模拟“硬件+固件”组合-来宾执行一条“ smc”指令,QEMU执行在EL3上运行的一些固件代码将在实际硬件上执行的动作。

virt板上还具有另一种操作模式,该模式适用于要运行本身为EL3固件的客户机(例如,如果要在EL3上运行OVMF / UEFI)。如果使用-machine secure = true启动QEMU以启用EL3仿真,并且还通过-bios或-drive if = pflash,...提供来宾固件blob,则QEMU将假定您的固件要在EL3上运行并提供PSCI本身提供服务,因此它将从所有CPU通电开始,并让固件处理它们。

通过一个PSCI调用打开另一个CPU的简单示例(在本例中为cpu,第4个,共8个):

    .equ PSCI_0_2_FN64_CPU_ON, 0xc4000003
    ldr x0, =PSCI_0_2_FN64_CPU_ON
    ldr x1, =4         /* target CPU's MPIDR affinity */
    ldr x2, =0x10000   /* entry point */
    ldr x3, =0         /* context ID: put into target CPU's x0 */
    smc 0

答案 1 :(得分:1)

我使用Peter Maydell提供的答复,在此为可能感兴趣的人提供了一个最小的,可复制的示例。

下载/安装aarch64-elf工具链:

wget "https://developer.arm.com/-/media/Files/downloads/gnu-a/8.3-2019.03/binrel/gcc-arm-8.3-2019.03-x86_64-aarch64-elf.tar.xz?revision=d678fd94-0ac4-485a-8054-1fbc60622a89&la=en"
mkdir -p /opt/arm
tar Jxf gcc-arm-8.3-2019.03-x86_64-aarch64-elf.tar.xz -C /opt/arm

示例文件:

loop.s:

                .title "loop.s"
                .arch armv8-a
                .text
                .global Reset_Handler
Reset_Handler:  mrs x0, mpidr_el1
                and x0,x0, 0b11
                cmp x0, #0
                b.eq Core0
                cmp x0, #1
                b.eq Core1
                cmp x0, #2
                b.eq Core2
                cmp x0, #3
                b.eq Core3
Error:          b .
Core0:          b .
Core1:          b .
Core2:          b .
Core3:          b .
               .end

build.sh:

#!/bin/bash
set -e

CROSS_COMPILE=/opt/arm/gcc-arm-8.3-2019.03-x86_64-aarch64-elf/bin/aarch64-elf-
AS=${CROSS_COMPILE}as
LD=${CROSS_COMPILE}ld
OBJCOPY=${CROSS_COMPILE}objcopy
OBJDUMP=${CROSS_COMPILE}objdump

${AS} -g -o loop.o loop.s 
${LD} -g -gc-sections -g -e Reset_Handler -Ttext-segment=0x40004000 -Map=loop.map -o loop.elf loop.o
${OBJDUMP} -d loop.elf

qemu.sh:

#!/bin/bash
set -e
QEMU_SYSTEM_AARCH64=qemu-system-aarch64
${QEMU_SYSTEM_AARCH64} \
   -s -S \
   -machine virt,secure=on,virtualization=on \
   -cpu cortex-a53 \
   -d int \
   -m 512M \
   -smp 4 \
   -display none \
   -nographic \
   -semihosting \
   -serial mon:stdio \
   -bios loop.elf \
   -device loader,addr=0x40004000,cpu-num=0 \
   -device loader,addr=0x40004000,cpu-num=1 \
   -device loader,addr=0x40004000,cpu-num=2 \
   -device loader,addr=0x40004000,cpu-num=3 \
   ;

loop.gdb:

target remote localhost:1234
file loop.elf
load loop.elf
disassemble Reset_Handler
info threads
continue

debug.sh:

#!/bin/bash
CROSS_COMPILE=/opt/arm/gcc-arm-8.3-2019.03-x86_64-aarch64-elf/bin/aarch64-elf-
GDB=${CROSS_COMPILE}gdb

${GDB} --command=loop.gdb

执行程序-需要两个控制台。

第一个控制台:

./build.sh 

输出应如下所示:

/opt/arm/gcc-arm-8.3-2019.03-x86_64-aarch64-elf/bin/aarch64-elf-ld: warning: address of `text-segment' isn't multiple of maximum page size

loop.elf:     file format elf64-littleaarch64


Disassembly of section .text:

0000000040004000 <Reset_Handler>:
    40004000:   d53800a0        mrs     x0, mpidr_el1
    40004004:   92400400        and     x0, x0, #0x3
    40004008:   f100001f        cmp     x0, #0x0
    4000400c:   54000100        b.eq    4000402c <Core0>  // b.none
    40004010:   f100041f        cmp     x0, #0x1
    40004014:   540000e0        b.eq    40004030 <Core1>  // b.none
    40004018:   f100081f        cmp     x0, #0x2
    4000401c:   540000c0        b.eq    40004034 <Core2>  // b.none
    40004020:   f1000c1f        cmp     x0, #0x3
    40004024:   540000a0        b.eq    40004038 <Core3>  // b.none

0000000040004028 <Error>:
    40004028:   14000000        b       40004028 <Error>

000000004000402c <Core0>:
    4000402c:   14000000        b       4000402c <Core0>

0000000040004030 <Core1>:
    40004030:   14000000        b       40004030 <Core1>

0000000040004034 <Core2>:
    40004034:   14000000        b       40004034 <Core2>

0000000040004038 <Core3>:
    40004038:   14000000        b       40004038 <Core3>

然后:

./qemu.sh

第二个控制台:

./debug.sh

输出应如下所示:

GNU gdb (GNU Toolchain for the A-profile Architecture 8.3-2019.03 (arm-rel-8.36)) 8.2.1.20190227-git
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "--host=x86_64-pc-linux-gnu --target=aarch64-elf".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<https://bugs.linaro.org/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word".
warning: No executable has been specified and target does not support
determining executable automatically.  Try using the "file" command.
0x0000000040004000 in ?? ()
Loading section .text, size 0x3c lma 0x40004000
Start address 0x40004000, load size 60
Transfer rate: 480 bits in <1 sec, 60 bytes/write.
Dump of assembler code for function Reset_Handler:
=> 0x0000000040004000 <+0>:     mrs     x0, mpidr_el1
   0x0000000040004004 <+4>:     and     x0, x0, #0x3
   0x0000000040004008 <+8>:     cmp     x0, #0x0
   0x000000004000400c <+12>:    b.eq    0x4000402c <Core0>  // b.none
   0x0000000040004010 <+16>:    cmp     x0, #0x1
   0x0000000040004014 <+20>:    b.eq    0x40004030 <Core1>  // b.none
   0x0000000040004018 <+24>:    cmp     x0, #0x2
   0x000000004000401c <+28>:    b.eq    0x40004034 <Core2>  // b.none
   0x0000000040004020 <+32>:    cmp     x0, #0x3
   0x0000000040004024 <+36>:    b.eq    0x40004038 <Core3>  // b.none
End of assembler dump.
  Id   Target Id                    Frame 
* 1    Thread 1.1 (CPU#0 [running]) Reset_Handler () at loop.s:5
  2    Thread 1.2 (CPU#1 [running]) Reset_Handler () at loop.s:5
  3    Thread 1.3 (CPU#2 [running]) Reset_Handler () at loop.s:5
  4    Thread 1.4 (CPU#3 [running]) Reset_Handler () at loop.s:5

所有四个内核都在地址0x40004000/Reset_Handler处停止,并由loop.gdb中的continue命令启动。 在第二个控制台中按CTRL+C

^C
Thread 1 received signal SIGINT, Interrupt.
Core0 () at loop.s:16
16      Core0:                  b .
(gdb) 

Core#0正在以Core0标签执行代码。 输入以下命令(仍在第二个控制台中):

(gdb) info threads
  Id   Target Id                    Frame 
* 1    Thread 1.1 (CPU#0 [running]) Core0 () at loop.s:16
  2    Thread 1.2 (CPU#1 [running]) Core1 () at loop.s:17
  3    Thread 1.3 (CPU#2 [running]) Core2 () at loop.s:18
  4    Thread 1.4 (CPU#3 [running]) Core3 () at loop.s:19
(gdb) 

在被CTRL+C停止之前,核心#1,#2和#3在各自的Core1,Core2,Core3标签上执行代码。

MPIDR_EL1寄存器的描述here:所有四个内核都使用MPIDR_EL1.Aff0的后两位来确定它们各自的内核编号。