mirror of https://github.com/xemu-project/xemu.git
arm: boot: Support big-endian elfs
Support ARM big-endian ELF files in system-mode emulation. When loading an elf, determine the endianness mode expected by the elf, and set the relevant CPU state accordingly. With this, big-endian modes are now fully supported via system-mode LE, so there is no need to restrict the elf loading to the TARGET endianness so the ifdeffery on TARGET_WORDS_BIGENDIAN goes away. Signed-off-by: Peter Crosthwaite <crosthwaite.peter@gmail.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [PMM: fix typo in comments] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
parent
7ef295ea5b
commit
9776f63645
|
@ -518,9 +518,34 @@ static void do_cpu_reset(void *opaque)
|
|||
cpu_reset(cs);
|
||||
if (info) {
|
||||
if (!info->is_linux) {
|
||||
int i;
|
||||
/* Jump to the entry point. */
|
||||
uint64_t entry = info->entry;
|
||||
|
||||
switch (info->endianness) {
|
||||
case ARM_ENDIANNESS_LE:
|
||||
env->cp15.sctlr_el[1] &= ~SCTLR_E0E;
|
||||
for (i = 1; i < 4; ++i) {
|
||||
env->cp15.sctlr_el[i] &= ~SCTLR_EE;
|
||||
}
|
||||
env->uncached_cpsr &= ~CPSR_E;
|
||||
break;
|
||||
case ARM_ENDIANNESS_BE8:
|
||||
env->cp15.sctlr_el[1] |= SCTLR_E0E;
|
||||
for (i = 1; i < 4; ++i) {
|
||||
env->cp15.sctlr_el[i] |= SCTLR_EE;
|
||||
}
|
||||
env->uncached_cpsr |= CPSR_E;
|
||||
break;
|
||||
case ARM_ENDIANNESS_BE32:
|
||||
env->cp15.sctlr_el[1] |= SCTLR_B;
|
||||
break;
|
||||
case ARM_ENDIANNESS_UNKNOWN:
|
||||
break; /* Board's decision */
|
||||
default:
|
||||
g_assert_not_reached();
|
||||
}
|
||||
|
||||
if (!env->aarch64) {
|
||||
env->thumb = info->entry & 1;
|
||||
entry &= 0xfffffffe;
|
||||
|
@ -638,6 +663,62 @@ static int do_arm_linux_init(Object *obj, void *opaque)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static uint64_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
|
||||
uint64_t *lowaddr, uint64_t *highaddr,
|
||||
int elf_machine)
|
||||
{
|
||||
bool elf_is64;
|
||||
union {
|
||||
Elf32_Ehdr h32;
|
||||
Elf64_Ehdr h64;
|
||||
} elf_header;
|
||||
int data_swab = 0;
|
||||
bool big_endian;
|
||||
uint64_t ret = -1;
|
||||
Error *err = NULL;
|
||||
|
||||
|
||||
load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
|
||||
if (err) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (elf_is64) {
|
||||
big_endian = elf_header.h64.e_ident[EI_DATA] == ELFDATA2MSB;
|
||||
info->endianness = big_endian ? ARM_ENDIANNESS_BE8
|
||||
: ARM_ENDIANNESS_LE;
|
||||
} else {
|
||||
big_endian = elf_header.h32.e_ident[EI_DATA] == ELFDATA2MSB;
|
||||
if (big_endian) {
|
||||
if (bswap32(elf_header.h32.e_flags) & EF_ARM_BE8) {
|
||||
info->endianness = ARM_ENDIANNESS_BE8;
|
||||
} else {
|
||||
info->endianness = ARM_ENDIANNESS_BE32;
|
||||
/* In BE32, the CPU has a different view of the per-byte
|
||||
* address map than the rest of the system. BE32 ELF files
|
||||
* are organised such that they can be programmed through
|
||||
* the CPU's per-word byte-reversed view of the world. QEMU
|
||||
* however loads ELF files independently of the CPU. So
|
||||
* tell the ELF loader to byte reverse the data for us.
|
||||
*/
|
||||
data_swab = 2;
|
||||
}
|
||||
} else {
|
||||
info->endianness = ARM_ENDIANNESS_LE;
|
||||
}
|
||||
}
|
||||
|
||||
ret = load_elf(info->kernel_filename, NULL, NULL,
|
||||
pentry, lowaddr, highaddr, big_endian, elf_machine,
|
||||
1, data_swab);
|
||||
if (ret <= 0) {
|
||||
/* The header loaded but the image didn't */
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
||||
{
|
||||
CPUState *cs;
|
||||
|
@ -647,7 +728,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||
uint64_t elf_entry, elf_low_addr, elf_high_addr;
|
||||
int elf_machine;
|
||||
hwaddr entry, kernel_load_offset;
|
||||
int big_endian;
|
||||
static const ARMInsnFixup *primary_loader;
|
||||
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
|
||||
notifier, notifier);
|
||||
|
@ -733,12 +813,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||
if (info->nb_cpus == 0)
|
||||
info->nb_cpus = 1;
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
big_endian = 1;
|
||||
#else
|
||||
big_endian = 0;
|
||||
#endif
|
||||
|
||||
/* We want to put the initrd far enough into RAM that when the
|
||||
* kernel is uncompressed it will not clobber the initrd. However
|
||||
* on boards without much RAM we must ensure that we still leave
|
||||
|
@ -753,9 +827,8 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||
MIN(info->ram_size / 2, 128 * 1024 * 1024);
|
||||
|
||||
/* Assume that raw images are linux kernels, and ELF images are not. */
|
||||
kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry,
|
||||
&elf_low_addr, &elf_high_addr, big_endian,
|
||||
elf_machine, 1, 0);
|
||||
kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr,
|
||||
&elf_high_addr, elf_machine);
|
||||
if (kernel_size > 0 && have_dtb(info)) {
|
||||
/* If there is still some room left at the base of RAM, try and put
|
||||
* the DTB there like we do for images loaded with -bios or -pflash.
|
||||
|
|
|
@ -16,6 +16,13 @@
|
|||
#include "qemu/notify.h"
|
||||
#include "cpu.h"
|
||||
|
||||
typedef enum {
|
||||
ARM_ENDIANNESS_UNKNOWN = 0,
|
||||
ARM_ENDIANNESS_LE,
|
||||
ARM_ENDIANNESS_BE8,
|
||||
ARM_ENDIANNESS_BE32,
|
||||
} arm_endianness;
|
||||
|
||||
/* armv7m.c */
|
||||
DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
|
||||
const char *kernel_filename, const char *cpu_model);
|
||||
|
@ -103,6 +110,8 @@ struct arm_boot_info {
|
|||
* changing to non-secure state if implementing a non-secure boot
|
||||
*/
|
||||
bool secure_board_setup;
|
||||
|
||||
arm_endianness endianness;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue