QEMU AARCH64 Процессорные машинные SMP-процессоры "virt" Запуск в состоянии "работает" или "остановлен" - PullRequest
0 голосов
/ 15 октября 2019

Я работаю над голым металлом. Нет 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 как «настоящие» регистры, упомянутые в документации / источнике «virt». В DTS не упомянуто устройство «SMC».

Как QEMU решает, «запущен» ли процессор SMP или «остановлен» при запуске, и как я могу на это повлиять?


Исходя из ответа @ Питера-Мейделла, приведенного ниже, мне нужно сделать одну из двух вещей ...

  1. Переключить "-kernel" на "-bios". Я делаю это, но мой код загружается не так, как я ожидаю. Мой * .elf файл имеет несколько разделов;некоторые во FLASH, а некоторые в DDR (выше 0x40000000). Может быть, в этом проблема?
  2. Измените мой загрузочный код для установки и введите команду SMC, чтобы заставить ARM PSCI вызывать CPU_ON, который QEMU распознает и включит другие процессоры. Код, подобный этому, работает, но, похоже, ничего не делает ...

    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 ]

1 голос
/ 16 октября 2019

Используя ответ, предоставленный Питером Мэйделлом , я приведу здесь минимальный воспроизводимый пример для людей, которые могут быть заинтересованы.

Загрузка / установка набора инструментов 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 и запущены командой continue в loop.gdb. Нажмите 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 выполняли код на соответствующих метках Core1, Core2, Core3 до остановки CTRL+C.

Описание регистра MPIDR_EL1 доступно здесь : два последних бита MPIDR_EL1.Aff0 использовались всеми четырьмя ядрами для определения их соответствующих номеров ядер.

1 голос
/ 15 октября 2019

Это зависит от модели платы - обычно мы следим за тем, что делает аппаратное обеспечение, и некоторые платы запускают все процессоры при включении питания, а некоторые - нет. Для платы «virt» (специфичной для QEMU) мы обычно используем PSCI, который является стандартным интерфейсом прошивки Arm для включения и выключения процессоров SMP (среди прочего, вы также можете использовать его для «полного отключения питания»). машина », например). При запуске работает только основной ЦП, и задача гостевого кода - использовать PSCI API для запуска вторичных серверов. Это то, что этот узел psci в DTS сообщает гостю - он сообщает гостю, какую конкретную форму реализует QEMU PSCI ABI, и, в частности, следует ли гостю использовать инструкцию 'hvc' или 'smc' для вызова функций PSCI. QEMU делает здесь эмуляцию комбинации «аппаратное обеспечение + прошивка» - гость выполняет команду «smc», а QEMU выполняет действия, которые на реальном оборудовании будут выполняться с помощью небольшого количества кода встроенного программного обеспечения, работающего на EL3.

Плата virt также имеет другой режим работы, который предназначен для запуска гостя, который сам является прошивкой EL3 (например, если вы хотите запустить OVMF / UEFI на EL3). Если вы запускаете QEMU с -machine secure = true, чтобы включить эмуляцию EL3, и вы также предоставляете бланк гостевой прошивки через -bios или -drive, если = pflash, ..., тогда QEMU предполагает, что ваша прошивка хочет работать на EL3, и предоставляетPSCI обслуживает сам себя, поэтому он запускается при включении всех процессоров и позволяет встроенному программному обеспечению разобраться с ними.

Простой пример выполнения вызова PSCI для включения другого процессора (в данном случае процессор № 48):

    .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
...