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);
|
cpu_reset(cs);
|
||||||
if (info) {
|
if (info) {
|
||||||
if (!info->is_linux) {
|
if (!info->is_linux) {
|
||||||
|
int i;
|
||||||
/* Jump to the entry point. */
|
/* Jump to the entry point. */
|
||||||
uint64_t entry = info->entry;
|
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) {
|
if (!env->aarch64) {
|
||||||
env->thumb = info->entry & 1;
|
env->thumb = info->entry & 1;
|
||||||
entry &= 0xfffffffe;
|
entry &= 0xfffffffe;
|
||||||
@ -638,6 +663,62 @@ static int do_arm_linux_init(Object *obj, void *opaque)
|
|||||||
return 0;
|
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)
|
static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
||||||
{
|
{
|
||||||
CPUState *cs;
|
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;
|
uint64_t elf_entry, elf_low_addr, elf_high_addr;
|
||||||
int elf_machine;
|
int elf_machine;
|
||||||
hwaddr entry, kernel_load_offset;
|
hwaddr entry, kernel_load_offset;
|
||||||
int big_endian;
|
|
||||||
static const ARMInsnFixup *primary_loader;
|
static const ARMInsnFixup *primary_loader;
|
||||||
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
|
ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
|
||||||
notifier, notifier);
|
notifier, notifier);
|
||||||
@ -733,12 +813,6 @@ static void arm_load_kernel_notify(Notifier *notifier, void *data)
|
|||||||
if (info->nb_cpus == 0)
|
if (info->nb_cpus == 0)
|
||||||
info->nb_cpus = 1;
|
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
|
/* We want to put the initrd far enough into RAM that when the
|
||||||
* kernel is uncompressed it will not clobber the initrd. However
|
* kernel is uncompressed it will not clobber the initrd. However
|
||||||
* on boards without much RAM we must ensure that we still leave
|
* 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);
|
MIN(info->ram_size / 2, 128 * 1024 * 1024);
|
||||||
|
|
||||||
/* Assume that raw images are linux kernels, and ELF images are not. */
|
/* Assume that raw images are linux kernels, and ELF images are not. */
|
||||||
kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry,
|
kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr,
|
||||||
&elf_low_addr, &elf_high_addr, big_endian,
|
&elf_high_addr, elf_machine);
|
||||||
elf_machine, 1, 0);
|
|
||||||
if (kernel_size > 0 && have_dtb(info)) {
|
if (kernel_size > 0 && have_dtb(info)) {
|
||||||
/* If there is still some room left at the base of RAM, try and put
|
/* 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.
|
* the DTB there like we do for images loaded with -bios or -pflash.
|
||||||
|
@ -16,6 +16,13 @@
|
|||||||
#include "qemu/notify.h"
|
#include "qemu/notify.h"
|
||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
ARM_ENDIANNESS_UNKNOWN = 0,
|
||||||
|
ARM_ENDIANNESS_LE,
|
||||||
|
ARM_ENDIANNESS_BE8,
|
||||||
|
ARM_ENDIANNESS_BE32,
|
||||||
|
} arm_endianness;
|
||||||
|
|
||||||
/* armv7m.c */
|
/* armv7m.c */
|
||||||
DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
|
DeviceState *armv7m_init(MemoryRegion *system_memory, int mem_size, int num_irq,
|
||||||
const char *kernel_filename, const char *cpu_model);
|
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
|
* changing to non-secure state if implementing a non-secure boot
|
||||||
*/
|
*/
|
||||||
bool secure_board_setup;
|
bool secure_board_setup;
|
||||||
|
|
||||||
|
arm_endianness endianness;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
Loading…
x
Reference in New Issue
Block a user