我正在研究裸机。没有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的回答,我需要做以下两件事之一...
更改启动代码以进行设置并发出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
答案 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
的后两位来确定它们各自的内核编号。