QEMU AARCH64 "virt" 机器 SMP CPU 开始于 "running" 与 "halted" 状态
QEMU AARCH64 "virt" Machine SMP CPUs Starting in "running" vs. "halted" State
我正在研究裸机。没有 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
我希望其他三个处理器以 "running" 状态启动,而不是 "halted"。怎么样?
请注意,我的 DTS 包含此部分:
psci {
migrate = < 0xc4000005 >;
cpu_on = < 0xc4000003 >;
cpu_off = < 0x84000002 >;
cpu_suspend = < 0xc4000001 >;
method = "smc";
compatible = "arm,psci-0.2[=15=]arm,psci";
};
但是,我不确定该怎么做。添加许多这种形式的不同行,似乎没有帮助:
-device loader,addr=0xc4000003,data=0x80000000,data-len=4
我不确定我在ARM PSCI这件事上是否走在正确的轨道上? ARM 的规范似乎定义了 "interface",而不是系统 "implementation"。但是,我没有将 PSCI 视为 "virt" documentation/source 中提到的 "real" 寄存器。 DTS 中没有提到 "SMC" 设备。
QEMU 如何决定 SMP 处理器在启动时是 "running" 还是 "halted",我如何影响它?
根据下面@Peter-Maydell 的回答,我需要做以下两件事之一...
- 将“-kernel”切换为“-bios”。我这样做了,但我的代码没有按预期加载。我的 *.elf 文件有几个部分;一些在 FLASH 中,一些在 DDR 中(高于 0x40000000)。也许这就是问题所在?
更改我的引导代码以设置并发出 SMC 指令,使 ARM PSCI "CPU_ON" 调用 QEMU 将识别并启动其他处理器。 像这样的代码运行但似乎"do"什么都没有...
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
这取决于电路板型号——通常我们遵循硬件的功能,有些电路板从通电开始所有 CPUs,有些则不然。对于 'virt' 板(特定于 QEMU),我们通常使用 PSCI,这是 Arm 标准固件接口,用于为 SMP CPU 上下供电(除其他外;您也可以例如,将其用于 'power down entire machine')。在启动时只有主 CPU 是 运行ning,来宾代码的工作是使用 PSCI API 启动辅助。这就是 DTS 中的 psci 节点告诉来宾的内容——它告诉来宾 PSCI ABI QEMU 实现的具体形式,特别是来宾是否应该使用 'hvc' 或 'smc' 指令来调用 PSCI 函数。 QEMU 在这里做的是模拟一个 "hardware + firmware" 组合——来宾执行一个 'smc' 指令,QEMU 执行在真实硬件上将由一些固件代码执行的操作 运行ning在 EL3.
virt 板也有另一种操作模式,当您想要 运行 本身就是 EL3 固件的来宾时(例如,如果您想要 运行 OVMF/UEFI 在 EL3)。如果您使用 -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
使用 Peter Maydell 提供的回复,我在这里为可能感兴趣的人提供了一个最小的、可重现的示例。
Downloading/installing 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)
核心 #1、#2 和 #3 在被 CTRL+C
停止之前正在各自的 Core1、Core2、Core3 标签上执行代码。
MPIDR_EL1
寄存器的描述可用here:MPIDR_EL1.Aff0
的最后两位被所有四个内核用来确定它们各自的内核编号。
我正在研究裸机。没有 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
我希望其他三个处理器以 "running" 状态启动,而不是 "halted"。怎么样?
请注意,我的 DTS 包含此部分:
psci {
migrate = < 0xc4000005 >;
cpu_on = < 0xc4000003 >;
cpu_off = < 0x84000002 >;
cpu_suspend = < 0xc4000001 >;
method = "smc";
compatible = "arm,psci-0.2[=15=]arm,psci";
};
但是,我不确定该怎么做。添加许多这种形式的不同行,似乎没有帮助:
-device loader,addr=0xc4000003,data=0x80000000,data-len=4
我不确定我在ARM PSCI这件事上是否走在正确的轨道上? ARM 的规范似乎定义了 "interface",而不是系统 "implementation"。但是,我没有将 PSCI 视为 "virt" documentation/source 中提到的 "real" 寄存器。 DTS 中没有提到 "SMC" 设备。
QEMU 如何决定 SMP 处理器在启动时是 "running" 还是 "halted",我如何影响它?
根据下面@Peter-Maydell 的回答,我需要做以下两件事之一...
- 将“-kernel”切换为“-bios”。我这样做了,但我的代码没有按预期加载。我的 *.elf 文件有几个部分;一些在 FLASH 中,一些在 DDR 中(高于 0x40000000)。也许这就是问题所在?
更改我的引导代码以设置并发出 SMC 指令,使 ARM PSCI "CPU_ON" 调用 QEMU 将识别并启动其他处理器。 像这样的代码运行但似乎"do"什么都没有...
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
这取决于电路板型号——通常我们遵循硬件的功能,有些电路板从通电开始所有 CPUs,有些则不然。对于 'virt' 板(特定于 QEMU),我们通常使用 PSCI,这是 Arm 标准固件接口,用于为 SMP CPU 上下供电(除其他外;您也可以例如,将其用于 'power down entire machine')。在启动时只有主 CPU 是 运行ning,来宾代码的工作是使用 PSCI API 启动辅助。这就是 DTS 中的 psci 节点告诉来宾的内容——它告诉来宾 PSCI ABI QEMU 实现的具体形式,特别是来宾是否应该使用 'hvc' 或 'smc' 指令来调用 PSCI 函数。 QEMU 在这里做的是模拟一个 "hardware + firmware" 组合——来宾执行一个 'smc' 指令,QEMU 执行在真实硬件上将由一些固件代码执行的操作 运行ning在 EL3.
virt 板也有另一种操作模式,当您想要 运行 本身就是 EL3 固件的来宾时(例如,如果您想要 运行 OVMF/UEFI 在 EL3)。如果您使用 -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
使用 Peter Maydell 提供的回复,我在这里为可能感兴趣的人提供了一个最小的、可重现的示例。
Downloading/installing 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)
核心 #1、#2 和 #3 在被 CTRL+C
停止之前正在各自的 Core1、Core2、Core3 标签上执行代码。
MPIDR_EL1
寄存器的描述可用here:MPIDR_EL1.Aff0
的最后两位被所有四个内核用来确定它们各自的内核编号。