Почему этот код зависает при работе на реальном BCM2837 (pi 3), но работает нормально с qemu

Рассмотрим следующую функцию:

_atomic_raw_lock:
.global _atomic_raw_lock
.type _atomic_raw_lock, %function
1:  ldxr        x9, [x0]                //atomic load from memory pointed to by x0
cbz         x9, 2f                  //branch if zero (unlocked)
wfe                                 //sleep if locked
b           1b
2:  mov         x9, #0x01               //set x9 to be LOCKED (1).
stxr        w10, x9, [x0]
dsb         sy
cbz         w10, 3f                 // atomic store success?
b           1b
3:  ret

Функция вызывается из кода c и сохраняет адрес в 64-битное целое число в x0 в качестве первого параметра. Программа работает как положено в QEMU. Я определил, что это проблемная область, установив аппаратные контакты непосредственно перед и после этой строки, которая вызывает эту функцию. Эта функция никогда не возвращается. x9 и w10 сохранены вызывающим абонентом (я предполагаю, что код вызова c сохранит их автоматически).

подробности:
работает на пи 3 б +
компиляция с помощью перекрестной цепочки aarch64-buildroot-linux-gnu (glibc static link) из toolchains.bootlin.com Все работает на реальном оборудовании, пока эта функция не будет вызвана.
команда построения:

aarch64-linux-g++ -g -std=gnu++17 -O0 -Wall -fno-exceptions -Wextra -Wno-attributes -fno-asynchronous-unwind-tables -Wno-sign-compare -c start.S -o
aarch64-linux-ld start.o main.o -T link.ld -o kernel8.elf -l:libc.a -l:libstdc++.a -l:libc.so -l:libgcc_s.so

LD-файл:

OUTPUT_ARCH(aarch64)
ENTRY(_start)
MEMORY
{
RAM (xrw)   : ORIGIN = 0x80000, LENGTH = 0x40000000
}
SECTIONS
{
PROVIDE(__start_prog_mem = .); /*I may want to use this in c somehow*/
.text : /*The place where code goes*/
{
KEEP(*(.text.boot)) /*Keep this even if it is not used*/
*(.text .text.* .gnu.linkonce.t*) /*all variations of text and
the gnu generated (for the c ability) text sections*/
} > RAM /*These sections now belong in .text*/
.bss : /*Uninitialized data goes here, will not load at runtime.*/
{
. = ALIGN(4); /*allign the current location to 4 bytes*/
__bss_start = .; /*define __bss_start to be at the current location.*/
*(.bss .bss.* .gnu.linkonce.b.*)
*(.rela.bss .rela.bss.* .rela.gnu.linkonce.b.*)
*(COMMON)
. = ALIGN(4); /*allign the current location to 4 bytes*/
__bss_end = .; /*Define __bss_end to be at the current location.*/
} > RAM
.data : /*Initialized global and static data*/
{
. = ALIGN(4); /*allign the current location to 4 bytes*/
__data_begin = .;
*(.data .data.* .gnu.linkonce.d*) /*Put all data sections here.*/
*(.rel.data .rel.data.* .rel.gnu.linkonce.d.*)
. = ALIGN(4);
__data_end = .;
} > RAM
.rodata : /*const data*/
{
. = ALIGN(4); /*allign the current location to 4 bytes*/
__rodata_begin = .;
*(.rodata .rodata.* .gnu.linkonce.r*) /*All const data sections,
including the gnu leftovers*/
*(.rel.rodata .rel.rodata.* .rel.gnu.linkonce.r.*)
. = ALIGN(4);
__rodata_end = .;
} > RAM
.stack_core0 :
{
. = ALIGN(16); /*stack must be 16 byte aligned*/
__stack_start_core0 = .;
. = . + 1024;  /*el0 size*/
__el0_stack_core0 = .; /*el0 stack*/
. = . + 1048576; /*el1 size*/
__el1_stack_core0 = .; /*el1 stack*/
PROVIDE(__el1_stack_core0 = .);
. = . + 16384; /*el2 stack size*/
__el2_stack_core0 = .;
. = ALIGN(16);
__stack_end_core0 = .;
} > RAM
.stack_core1 :
{
. = ALIGN(16); /*stack must be 16 byte aligned*/
__stack_start_core1 = .;
. = . + 1024;  /*el0 size*/
__el0_stack_core1 = .; /*el0 stack*/
. = . + 1048576; /*el1 size*/
__el1_stack_core1 = .; /*el1 stack*/
PROVIDE(__el1_stack_core1 = .);
. = . + 16384; /*el2 stack size*/
__el2_stack_core1 = .;
. = ALIGN(16);
__stack_end_core1 = .;
} > RAM
.stack_core2 :
{
. = ALIGN(16); /*stack must be 16 byte aligned*/
__stack_start_core2 = .;
. = . + 1024;  /*el0 size*/
__el0_stack_core2 = .; /*el0 stack*/
. = . + 1048576; /*el1 size*/
__el1_stack_core2 = .; /*el1 stack*/
PROVIDE(__el1_stack_core2 = .);
. = . + 16384; /*el2 stack size*/
__el2_stack_core2 = .;
. = ALIGN(16);
__stack_end_core2 = .;
} > RAM
.stack_core3 :
{
. = ALIGN(16); /*stack must be 16 byte aligned*/
__stack_start_core3 = .;
. = . + 1024;  /*el0 size*/
__el0_stack_core3 = .; /*el0 stack*/
. = . + 1048576; /*el1 size*/
__el1_stack_core3 = .; /*el1 stack*/
PROVIDE(__el1_stack_core3 = .);
. = . + 16384; /*el2 stack size*/
__el2_stack_core3 = .;
. = ALIGN(16);
__stack_end_core3 = .;
} > RAM
_end = .; /*Define _end to be at the current location*/
PROVIDE(__end_prog_mem = .); /*I may want to use this in c somehow*/
.heap :
{
. = ALIGN(4);
__heap_start = .;
} > RAM
/DISCARD/ : /*Any sections listed here will not be included*/
{
/* *(.comment*) /*Exclude any comments made by the compiler*/
/* *(.gnu*) /*Exclude any version numbers included by the compiler*/
/* *(.note*) /*Exclude any notes made by the compiler.*/
/* *(.eh_frame*) /*This sections is ecluded because it contains asyncronous
unwind tbles we dont need.*/
}
}

__bss_size = (__bss_end - __bss_start) >> 3; /*Define the symbol to hold the
size of the .bss sections.  This size will be in single units of 8 bytes
(due to shift >> 3)*/
__prog_mem_size = (__end_prog_mem - __start_prog_mem);

Удаленный механизм атомарной блокировки (список кодов в самом верху) позволяет программе запускаться, но вывод перемешивается из-за не поточно-безопасного кода. Даже простой флаг bool не будет работать, потому что обычное чтение-изменение-запись не является многоядерным.

Повторяемый вопрос: почему это работает на эмуляторе (qemu), а не на реальном оборудовании. Приведенный выше список функций _atomic_raw_lock подтвержден как зависший при вызове на реальном оборудовании, но не на qemu.

РЕДАКТИРОВАТЬ: код работает нормально в QEMU, что приводит меня к мысли, что это не тупик. Я подтвердил это, выполнив тест, в котором все, что делает программа, — блокировка один раз и вращение навсегда. Проблема сохраняется в этих обстоятельствах.

0

Решение

Это может произойти сбой (ошибочно), если предоставленная память не является НОРМАЛЬНЫЙ.

Qemu вряд ли будет моделировать атрибуты памяти и имитировать такие сбои.

(извините за stxr, мой разум был в cswap).

Вы можете подумать о том, чтобы сбросить счетчик итераций в цикле, чтобы вы могли спасти отладчик / аварийный дамп, если он там висит.

1

Другие решения

Других решений пока нет …