qemu/hw/arm/boot.c

1429 lines
50 KiB
C
Raw Normal View History

/*
* ARM kernel loader.
*
* Copyright (c) 2006-2007 CodeSourcery.
* Written by Paul Brook
*
* This code is licensed under the GPL.
*/
#include "qemu/osdep.h"
#include "qemu/datadir.h"
hw/arm: Replace fprintf(stderr, "*\n" with error_report() Replace a large number of the fprintf(stderr, "*\n" calls with error_report(). The functions were renamed with these commands and then compiler issues where manually fixed. find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + Some lines where then manually tweaked to pass checkpatch. The 'qemu: ' prefix was manually removed from the hw/arm/boot.c file. Signed-off-by: Alistair Francis <alistair.francis@xilinx.com> Cc: qemu-arm@nongnu.org Conversions that aren't followed by exit() dropped, because they might be inappropriate. Also trim trailing punctuation from error messages. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-Id: <20180203084315.20497-3-armbru@redhat.com>
2018-02-03 11:43:03 +03:00
#include "qemu/error-report.h"
2016-03-14 11:01:28 +03:00
#include "qapi/error.h"
#include <libfdt.h>
#include "hw/arm/boot.h"
#include "hw/arm/linux-boot-if.h"
#include "sysemu/kvm.h"
#include "sysemu/sysemu.h"
#include "sysemu/numa.h"
#include "hw/boards.h"
#include "sysemu/reset.h"
#include "hw/loader.h"
#include "elf.h"
#include "sysemu/device_tree.h"
#include "qemu/config-file.h"
#include "qemu/option.h"
#include "qemu/units.h"
/* Kernel boot protocol is specified in the kernel docs
* Documentation/arm/Booting and Documentation/arm64/booting.txt
* They have different preferred image load offsets from system RAM base.
*/
#define KERNEL_ARGS_ADDR 0x100
#define KERNEL_NOLOAD_ADDR 0x02000000
#define KERNEL_LOAD_ADDR 0x00010000
#define KERNEL64_LOAD_ADDR 0x00080000
#define ARM64_TEXT_OFFSET_OFFSET 8
#define ARM64_MAGIC_OFFSET 56
#define BOOTLOADER_MAX_SIZE (4 * KiB)
arm/boot: split load_dtb() from arm_load_kernel() load_dtb() depends on arm_load_kernel() to figure out place in RAM where it should be loaded, but it's not required for arm_load_kernel() to work. Sometimes it's neccesary for devices added with -device/device_add to be enumerated in DTB as well, which's lead to [1] and surrounding commits to add 2 more machine_done notifiers with non obvious ordering to make dynamic sysbus devices initialization happen in the right order. However instead of moving whole arm_load_kernel() in to machine_done, it's sufficient to move only load_dtb() into virt_machine_done() notifier and remove ArmLoadKernelNotifier/ /PlatformBusFDTNotifierParams notifiers, which saves us ~90LOC and simplifies code flow quite a bit. Later would allow to consolidate DTB generation within one function for 'mach-virt' board and make it reentrant so it could generate updated DTB in device hotplug secenarios. While at it rename load_dtb() to arm_load_dtb() since it's public now. Add additional field skip_dtb_autoload to struct arm_boot_info to allow manual DTB load later in mach-virt and to avoid touching all other boards to explicitly call arm_load_dtb(). 1) (ac9d32e hw/arm/boot: arm_load_kernel implemented as a machine init done notifier) Signed-off-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Andrew Jones <drjones@redhat.com> Message-id: 1525691524-32265-4-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2018-05-10 20:10:56 +03:00
AddressSpace *arm_boot_address_space(ARMCPU *cpu,
const struct arm_boot_info *info)
{
/* Return the address space to use for bootloader reads and writes.
* We prefer the secure address space if the CPU has it and we're
* going to boot the guest into it.
*/
int asidx;
CPUState *cs = CPU(cpu);
if (arm_feature(&cpu->env, ARM_FEATURE_EL3) && info->secure_boot) {
asidx = ARMASIdx_S;
} else {
asidx = ARMASIdx_NS;
}
return cpu_get_address_space(cs, asidx);
}
typedef enum {
FIXUP_NONE = 0, /* do nothing */
FIXUP_TERMINATOR, /* end of insns */
FIXUP_BOARDID, /* overwrite with board ID number */
FIXUP_BOARD_SETUP, /* overwrite with board specific setup code address */
FIXUP_ARGPTR_LO, /* overwrite with pointer to kernel args */
FIXUP_ARGPTR_HI, /* overwrite with pointer to kernel args (high half) */
FIXUP_ENTRYPOINT_LO, /* overwrite with kernel entry point */
FIXUP_ENTRYPOINT_HI, /* overwrite with kernel entry point (high half) */
FIXUP_GIC_CPU_IF, /* overwrite with GIC CPU interface address */
FIXUP_BOOTREG, /* overwrite with boot register address */
FIXUP_DSB, /* overwrite with correct DSB insn for cpu */
FIXUP_MAX,
} FixupType;
typedef struct ARMInsnFixup {
uint32_t insn;
FixupType fixup;
} ARMInsnFixup;
static const ARMInsnFixup bootloader_aarch64[] = {
{ 0x580000c0 }, /* ldr x0, arg ; Load the lower 32-bits of DTB */
{ 0xaa1f03e1 }, /* mov x1, xzr */
{ 0xaa1f03e2 }, /* mov x2, xzr */
{ 0xaa1f03e3 }, /* mov x3, xzr */
{ 0x58000084 }, /* ldr x4, entry ; Load the lower 32-bits of kernel entry */
{ 0xd61f0080 }, /* br x4 ; Jump to the kernel entry point */
{ 0, FIXUP_ARGPTR_LO }, /* arg: .word @DTB Lower 32-bits */
{ 0, FIXUP_ARGPTR_HI}, /* .word @DTB Higher 32-bits */
{ 0, FIXUP_ENTRYPOINT_LO }, /* entry: .word @Kernel Entry Lower 32-bits */
{ 0, FIXUP_ENTRYPOINT_HI }, /* .word @Kernel Entry Higher 32-bits */
{ 0, FIXUP_TERMINATOR }
};
/* A very small bootloader: call the board-setup code (if needed),
* set r0-r2, then jump to the kernel.
* If we're not calling boot setup code then we don't copy across
* the first BOOTLOADER_NO_BOARD_SETUP_OFFSET insns in this array.
*/
static const ARMInsnFixup bootloader[] = {
{ 0xe28fe004 }, /* add lr, pc, #4 */
{ 0xe51ff004 }, /* ldr pc, [pc, #-4] */
{ 0, FIXUP_BOARD_SETUP },
#define BOOTLOADER_NO_BOARD_SETUP_OFFSET 3
{ 0xe3a00000 }, /* mov r0, #0 */
{ 0xe59f1004 }, /* ldr r1, [pc, #4] */
{ 0xe59f2004 }, /* ldr r2, [pc, #4] */
{ 0xe59ff004 }, /* ldr pc, [pc, #4] */
{ 0, FIXUP_BOARDID },
{ 0, FIXUP_ARGPTR_LO },
{ 0, FIXUP_ENTRYPOINT_LO },
{ 0, FIXUP_TERMINATOR }
};
/* Handling for secondary CPU boot in a multicore system.
* Unlike the uniprocessor/primary CPU boot, this is platform
* dependent. The default code here is based on the secondary
* CPU boot protocol used on realview/vexpress boards, with
* some parameterisation to increase its flexibility.
* QEMU platform models for which this code is not appropriate
* should override write_secondary_boot and secondary_cpu_reset_hook
* instead.
*
* This code enables the interrupt controllers for the secondary
* CPUs and then puts all the secondary CPUs into a loop waiting
* for an interprocessor interrupt and polling a configurable
* location for the kernel secondary CPU entry point.
*/
#define DSB_INSN 0xf57ff04f
#define CP15_DSB_INSN 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */
static const ARMInsnFixup smpboot[] = {
{ 0xe59f2028 }, /* ldr r2, gic_cpu_if */
{ 0xe59f0028 }, /* ldr r0, bootreg_addr */
{ 0xe3a01001 }, /* mov r1, #1 */
{ 0xe5821000 }, /* str r1, [r2] - set GICC_CTLR.Enable */
{ 0xe3a010ff }, /* mov r1, #0xff */
{ 0xe5821004 }, /* str r1, [r2, 4] - set GIC_PMR.Priority to 0xff */
{ 0, FIXUP_DSB }, /* dsb */
{ 0xe320f003 }, /* wfi */
{ 0xe5901000 }, /* ldr r1, [r0] */
{ 0xe1110001 }, /* tst r1, r1 */
{ 0x0afffffb }, /* beq <wfi> */
{ 0xe12fff11 }, /* bx r1 */
{ 0, FIXUP_GIC_CPU_IF }, /* gic_cpu_if: .word 0x.... */
{ 0, FIXUP_BOOTREG }, /* bootreg_addr: .word 0x.... */
{ 0, FIXUP_TERMINATOR }
};
static void write_bootloader(const char *name, hwaddr addr,
const ARMInsnFixup *insns, uint32_t *fixupcontext,
AddressSpace *as)
{
/* Fix up the specified bootloader fragment and write it into
* guest memory using rom_add_blob_fixed(). fixupcontext is
* an array giving the values to write in for the fixup types
* which write a value into the code array.
*/
int i, len;
uint32_t *code;
len = 0;
while (insns[len].fixup != FIXUP_TERMINATOR) {
len++;
}
code = g_new0(uint32_t, len);
for (i = 0; i < len; i++) {
uint32_t insn = insns[i].insn;
FixupType fixup = insns[i].fixup;
switch (fixup) {
case FIXUP_NONE:
break;
case FIXUP_BOARDID:
case FIXUP_BOARD_SETUP:
case FIXUP_ARGPTR_LO:
case FIXUP_ARGPTR_HI:
case FIXUP_ENTRYPOINT_LO:
case FIXUP_ENTRYPOINT_HI:
case FIXUP_GIC_CPU_IF:
case FIXUP_BOOTREG:
case FIXUP_DSB:
insn = fixupcontext[fixup];
break;
default:
abort();
}
code[i] = tswap32(insn);
}
assert((len * sizeof(uint32_t)) < BOOTLOADER_MAX_SIZE);
rom_add_blob_fixed_as(name, code, len * sizeof(uint32_t), addr, as);
g_free(code);
}
static void default_write_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
uint32_t fixupcontext[FIXUP_MAX];
AddressSpace *as = arm_boot_address_space(cpu, info);
fixupcontext[FIXUP_GIC_CPU_IF] = info->gic_cpu_if_addr;
fixupcontext[FIXUP_BOOTREG] = info->smp_bootreg_addr;
if (arm_feature(&cpu->env, ARM_FEATURE_V7)) {
fixupcontext[FIXUP_DSB] = DSB_INSN;
} else {
fixupcontext[FIXUP_DSB] = CP15_DSB_INSN;
}
write_bootloader("smpboot", info->smp_loader_start,
smpboot, fixupcontext, as);
}
void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu,
const struct arm_boot_info *info,
hwaddr mvbar_addr)
{
AddressSpace *as = arm_boot_address_space(cpu, info);
int n;
uint32_t mvbar_blob[] = {
/* mvbar_addr: secure monitor vectors
* Default unimplemented and unused vectors to spin. Makes it
* easier to debug (as opposed to the CPU running away).
*/
0xeafffffe, /* (spin) */
0xeafffffe, /* (spin) */
0xe1b0f00e, /* movs pc, lr ;SMC exception return */
0xeafffffe, /* (spin) */
0xeafffffe, /* (spin) */
0xeafffffe, /* (spin) */
0xeafffffe, /* (spin) */
0xeafffffe, /* (spin) */
};
uint32_t board_setup_blob[] = {
/* board setup addr */
hw/arm/boot: Set NSACR.{CP11, CP10} in dummy SMC setup routine The boot.c code usually puts the CPU into NS mode directly when it is booting a kernel. Since fc1120a7f5f2d4b6 this has included a requirement to set NSACR to give NS state access to the FPU; we fixed that for the usual code path in ece628fcf6. However, it is also possible for a board model to request an alternative mode of booting, where its 'board_setup' code hook runs in Secure state and is responsible for doing the S->NS transition after it has done whatever work it must do in Secure state. In this situation the board_setup code now also needs to update NSACR. This affects all boards which set info->secure_board_setup, which is currently the 'raspi' and 'highbank' families. They both use the common arm_write_secure_board_setup_dummy_smc(). Set the NSACR CP11 and CP10 bits in the code written by that function, to allow FPU access in Non-Secure state when using dummy SMC setup routine. Otherwise an AArch32 kernel booted on the highbank or raspi boards will UNDEF as soon as it tries to use the FPU. Update the comment describing secure_board_setup to note the new requirements on users of it. This fixes a kernel panic when booting raspbian on raspi2. Successfully tested with: 2017-01-11-raspbian-jessie-lite.img 2018-11-13-raspbian-stretch-lite.img 2019-07-10-raspbian-buster-lite.img Fixes: fc1120a7f5 Signed-off-by: Clement Deschamps <clement.deschamps@greensocs.com> Tested-by: Laurent Bonnans <laurent.bonnans@here.com> Message-id: 20191104151137.81931-1-clement.deschamps@greensocs.com Reviewed-by: Peter Maydell <peter.maydell@linaro.org> [PMM: updated comment to boot.h to note new requirement on users of secure_board_setup; edited/rewrote commit message] Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2019-11-11 16:44:16 +03:00
0xee110f51, /* mrc p15, 0, r0, c1, c1, 2 ;read NSACR */
0xe3800b03, /* orr r0, #0xc00 ;set CP11, CP10 */
0xee010f51, /* mcr p15, 0, r0, c1, c1, 2 ;write NSACR */
0xe3a00e00 + (mvbar_addr >> 4), /* mov r0, #mvbar_addr */
0xee0c0f30, /* mcr p15, 0, r0, c12, c0, 1 ;set MVBAR */
0xee110f11, /* mrc p15, 0, r0, c1 , c1, 0 ;read SCR */
0xe3800031, /* orr r0, #0x31 ;enable AW, FW, NS */
0xee010f11, /* mcr p15, 0, r0, c1, c1, 0 ;write SCR */
0xe1a0100e, /* mov r1, lr ;save LR across SMC */
0xe1600070, /* smc #0 ;call monitor to flush SCR */
0xe1a0f001, /* mov pc, r1 ;return */
};
/* check that mvbar_addr is correctly aligned and relocatable (using MOV) */
assert((mvbar_addr & 0x1f) == 0 && (mvbar_addr >> 4) < 0x100);
/* check that these blobs don't overlap */
assert((mvbar_addr + sizeof(mvbar_blob) <= info->board_setup_addr)
|| (info->board_setup_addr + sizeof(board_setup_blob) <= mvbar_addr));
for (n = 0; n < ARRAY_SIZE(mvbar_blob); n++) {
mvbar_blob[n] = tswap32(mvbar_blob[n]);
}
rom_add_blob_fixed_as("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob),
mvbar_addr, as);
for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) {
board_setup_blob[n] = tswap32(board_setup_blob[n]);
}
rom_add_blob_fixed_as("board-setup", board_setup_blob,
sizeof(board_setup_blob), info->board_setup_addr, as);
}
static void default_reset_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
AddressSpace *as = arm_boot_address_space(cpu, info);
CPUState *cs = CPU(cpu);
address_space_stl_notdirty(as, info->smp_bootreg_addr,
Switch non-CPU callers from ld/st*_phys to address_space_ld/st* Switch all the uses of ld/st*_phys to address_space_ld/st*, except for those cases where the address space is the CPU's (ie cs->as). This was done with the following script which generates a Coccinelle patch. A few over-80-columns lines in the result were rewrapped by hand where Coccinelle failed to do the wrapping automatically, as well as one location where it didn't put a line-continuation '\' when wrapping lines on a change made to a match inside a macro definition. ===begin=== #!/bin/sh -e # Usage: # ./ldst-phys.spatch.sh > ldst-phys.spatch # spatch -sp_file ldst-phys.spatch -dir . | sed -e '/^+/s/\t/ /g' > out.patch # patch -p1 < out.patch for FN in ub uw_le uw_be l_le l_be q_le q_be uw l q; do cat <<EOF @ cpu_matches_ld_${FN} @ expression E1,E2; identifier as; @@ ld${FN}_phys(E1->as,E2) @ other_matches_ld_${FN} depends on !cpu_matches_ld_${FN} @ expression E1,E2; @@ -ld${FN}_phys(E1,E2) +address_space_ld${FN}(E1,E2, MEMTXATTRS_UNSPECIFIED, NULL) EOF done for FN in b w_le w_be l_le l_be q_le q_be w l q; do cat <<EOF @ cpu_matches_st_${FN} @ expression E1,E2,E3; identifier as; @@ st${FN}_phys(E1->as,E2,E3) @ other_matches_st_${FN} depends on !cpu_matches_st_${FN} @ expression E1,E2,E3; @@ -st${FN}_phys(E1,E2,E3) +address_space_st${FN}(E1,E2,E3, MEMTXATTRS_UNSPECIFIED, NULL) EOF done ===endit=== Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
2015-04-26 18:49:24 +03:00
0, MEMTXATTRS_UNSPECIFIED, NULL);
cpu_set_pc(cs, info->smp_loader_start);
}
static inline bool have_dtb(const struct arm_boot_info *info)
{
return info->dtb_filename || info->get_dtb;
}
#define WRITE_WORD(p, value) do { \
address_space_stl_notdirty(as, p, value, \
Switch non-CPU callers from ld/st*_phys to address_space_ld/st* Switch all the uses of ld/st*_phys to address_space_ld/st*, except for those cases where the address space is the CPU's (ie cs->as). This was done with the following script which generates a Coccinelle patch. A few over-80-columns lines in the result were rewrapped by hand where Coccinelle failed to do the wrapping automatically, as well as one location where it didn't put a line-continuation '\' when wrapping lines on a change made to a match inside a macro definition. ===begin=== #!/bin/sh -e # Usage: # ./ldst-phys.spatch.sh > ldst-phys.spatch # spatch -sp_file ldst-phys.spatch -dir . | sed -e '/^+/s/\t/ /g' > out.patch # patch -p1 < out.patch for FN in ub uw_le uw_be l_le l_be q_le q_be uw l q; do cat <<EOF @ cpu_matches_ld_${FN} @ expression E1,E2; identifier as; @@ ld${FN}_phys(E1->as,E2) @ other_matches_ld_${FN} depends on !cpu_matches_ld_${FN} @ expression E1,E2; @@ -ld${FN}_phys(E1,E2) +address_space_ld${FN}(E1,E2, MEMTXATTRS_UNSPECIFIED, NULL) EOF done for FN in b w_le w_be l_le l_be q_le q_be w l q; do cat <<EOF @ cpu_matches_st_${FN} @ expression E1,E2,E3; identifier as; @@ st${FN}_phys(E1->as,E2,E3) @ other_matches_st_${FN} depends on !cpu_matches_st_${FN} @ expression E1,E2,E3; @@ -st${FN}_phys(E1,E2,E3) +address_space_st${FN}(E1,E2,E3, MEMTXATTRS_UNSPECIFIED, NULL) EOF done ===endit=== Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
2015-04-26 18:49:24 +03:00
MEMTXATTRS_UNSPECIFIED, NULL); \
p += 4; \
} while (0)
static void set_kernel_args(const struct arm_boot_info *info, AddressSpace *as)
{
int initrd_size = info->initrd_size;
hwaddr base = info->loader_start;
hwaddr p;
p = base + KERNEL_ARGS_ADDR;
/* ATAG_CORE */
WRITE_WORD(p, 5);
WRITE_WORD(p, 0x54410001);
WRITE_WORD(p, 1);
WRITE_WORD(p, 0x1000);
WRITE_WORD(p, 0);
/* ATAG_MEM */
/* TODO: handle multiple chips on one ATAG list */
WRITE_WORD(p, 4);
WRITE_WORD(p, 0x54410002);
WRITE_WORD(p, info->ram_size);
WRITE_WORD(p, info->loader_start);
if (initrd_size) {
/* ATAG_INITRD2 */
WRITE_WORD(p, 4);
WRITE_WORD(p, 0x54420005);
WRITE_WORD(p, info->initrd_start);
WRITE_WORD(p, initrd_size);
}
if (info->kernel_cmdline && *info->kernel_cmdline) {
/* ATAG_CMDLINE */
int cmdline_size;
cmdline_size = strlen(info->kernel_cmdline);
address_space_write(as, p + 8, MEMTXATTRS_UNSPECIFIED,
info->kernel_cmdline, cmdline_size + 1);
cmdline_size = (cmdline_size >> 2) + 1;
WRITE_WORD(p, cmdline_size + 2);
WRITE_WORD(p, 0x54410009);
p += cmdline_size * 4;
}
if (info->atag_board) {
/* ATAG_BOARD */
int atag_board_len;
uint8_t atag_board_buf[0x1000];
atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3;
WRITE_WORD(p, (atag_board_len + 8) >> 2);
WRITE_WORD(p, 0x414f4d50);
address_space_write(as, p, MEMTXATTRS_UNSPECIFIED,
atag_board_buf, atag_board_len);
p += atag_board_len;
}
/* ATAG_END */
WRITE_WORD(p, 0);
WRITE_WORD(p, 0);
}
static void set_kernel_args_old(const struct arm_boot_info *info,
AddressSpace *as)
{
hwaddr p;
const char *s;
int initrd_size = info->initrd_size;
hwaddr base = info->loader_start;
/* see linux/include/asm-arm/setup.h */
p = base + KERNEL_ARGS_ADDR;
/* page_size */
WRITE_WORD(p, 4096);
/* nr_pages */
WRITE_WORD(p, info->ram_size / 4096);
/* ramdisk_size */
WRITE_WORD(p, 0);
#define FLAG_READONLY 1
#define FLAG_RDLOAD 4
#define FLAG_RDPROMPT 8
/* flags */
WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT);
/* rootdev */
WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */
/* video_num_cols */
WRITE_WORD(p, 0);
/* video_num_rows */
WRITE_WORD(p, 0);
/* video_x */
WRITE_WORD(p, 0);
/* video_y */
WRITE_WORD(p, 0);
/* memc_control_reg */
WRITE_WORD(p, 0);
/* unsigned char sounddefault */
/* unsigned char adfsdrives */
/* unsigned char bytes_per_char_h */
/* unsigned char bytes_per_char_v */
WRITE_WORD(p, 0);
/* pages_in_bank[4] */
WRITE_WORD(p, 0);
WRITE_WORD(p, 0);
WRITE_WORD(p, 0);
WRITE_WORD(p, 0);
/* pages_in_vram */
WRITE_WORD(p, 0);
/* initrd_start */
if (initrd_size) {
WRITE_WORD(p, info->initrd_start);
} else {
WRITE_WORD(p, 0);
}
/* initrd_size */
WRITE_WORD(p, initrd_size);
/* rd_start */
WRITE_WORD(p, 0);
/* system_rev */
WRITE_WORD(p, 0);
/* system_serial_low */
WRITE_WORD(p, 0);
/* system_serial_high */
WRITE_WORD(p, 0);
/* mem_fclk_21285 */
WRITE_WORD(p, 0);
/* zero unused fields */
while (p < base + KERNEL_ARGS_ADDR + 256 + 1024) {
WRITE_WORD(p, 0);
}
s = info->kernel_cmdline;
if (s) {
address_space_write(as, p, MEMTXATTRS_UNSPECIFIED, s, strlen(s) + 1);
} else {
WRITE_WORD(p, 0);
}
}
static int fdt_add_memory_node(void *fdt, uint32_t acells, hwaddr mem_base,
uint32_t scells, hwaddr mem_len,
int numa_node_id)
{
char *nodename;
int ret;
nodename = g_strdup_printf("/memory@%" PRIx64, mem_base);
qemu_fdt_add_subnode(fdt, nodename);
qemu_fdt_setprop_string(fdt, nodename, "device_type", "memory");
ret = qemu_fdt_setprop_sized_cells(fdt, nodename, "reg", acells, mem_base,
scells, mem_len);
if (ret < 0) {
goto out;
}
/* only set the NUMA ID if it is specified */
if (numa_node_id >= 0) {
ret = qemu_fdt_setprop_cell(fdt, nodename,
"numa-node-id", numa_node_id);
}
out:
g_free(nodename);
return ret;
}
static void fdt_add_psci_node(void *fdt)
{
uint32_t cpu_suspend_fn;
uint32_t cpu_off_fn;
uint32_t cpu_on_fn;
uint32_t migrate_fn;
ARMCPU *armcpu = ARM_CPU(qemu_get_cpu(0));
const char *psci_method;
int64_t psci_conduit;
int rc;
psci_conduit = object_property_get_int(OBJECT(armcpu),
"psci-conduit",
&error_abort);
switch (psci_conduit) {
case QEMU_PSCI_CONDUIT_DISABLED:
return;
case QEMU_PSCI_CONDUIT_HVC:
psci_method = "hvc";
break;
case QEMU_PSCI_CONDUIT_SMC:
psci_method = "smc";
break;
default:
g_assert_not_reached();
}
/*
hw/arm/boot: Drop existing dtb /psci node rather than retaining it If we're using PSCI emulation, we add a /psci node to the device tree we pass to the guest. At the moment, if the dtb already has a /psci node in it, we retain it, rather than replacing it. (This behaviour was added in commit c39770cd637765 in 2018.) This is a problem if the existing node doesn't match our PSCI emulation. In particular, it might specify the wrong method (HVC vs SMC), or wrong function IDs for cpu_suspend/cpu_off/etc, in which case the guest will not get the behaviour it wants when it makes PSCI calls. An example of this is trying to boot the highbank or midway board models using the device tree supplied in the kernel sources: this device tree includes a /psci node that specifies function IDs that don't match the (PSCI 0.2 compliant) IDs that QEMU uses. The dtb cpu_suspend function ID happens to match the PSCI 0.2 cpu_off ID, so the guest hangs after booting when the kernel tries to idle the CPU and instead it gets turned off. Instead of retaining an existing /psci node, delete it entirely and replace it with a node whose properties match QEMU's PSCI emulation behaviour. This matches the way we handle /memory nodes, where we also delete any existing nodes and write in ones that match the way QEMU is going to behave. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com> Tested-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Tested-by: Cédric Le Goater <clg@kaod.org> Tested-by: Niek Linnenbank <nieklinnenbank@gmail.com> Message-id: 20220127154639.2090164-17-peter.maydell@linaro.org
2022-01-27 18:46:39 +03:00
* A pre-existing /psci node might specify function ID values
* that don't match QEMU's PSCI implementation. Delete the whole
* node and put our own in instead.
*/
rc = fdt_path_offset(fdt, "/psci");
if (rc >= 0) {
hw/arm/boot: Drop existing dtb /psci node rather than retaining it If we're using PSCI emulation, we add a /psci node to the device tree we pass to the guest. At the moment, if the dtb already has a /psci node in it, we retain it, rather than replacing it. (This behaviour was added in commit c39770cd637765 in 2018.) This is a problem if the existing node doesn't match our PSCI emulation. In particular, it might specify the wrong method (HVC vs SMC), or wrong function IDs for cpu_suspend/cpu_off/etc, in which case the guest will not get the behaviour it wants when it makes PSCI calls. An example of this is trying to boot the highbank or midway board models using the device tree supplied in the kernel sources: this device tree includes a /psci node that specifies function IDs that don't match the (PSCI 0.2 compliant) IDs that QEMU uses. The dtb cpu_suspend function ID happens to match the PSCI 0.2 cpu_off ID, so the guest hangs after booting when the kernel tries to idle the CPU and instead it gets turned off. Instead of retaining an existing /psci node, delete it entirely and replace it with a node whose properties match QEMU's PSCI emulation behaviour. This matches the way we handle /memory nodes, where we also delete any existing nodes and write in ones that match the way QEMU is going to behave. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Reviewed-by: Niek Linnenbank <nieklinnenbank@gmail.com> Tested-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Tested-by: Cédric Le Goater <clg@kaod.org> Tested-by: Niek Linnenbank <nieklinnenbank@gmail.com> Message-id: 20220127154639.2090164-17-peter.maydell@linaro.org
2022-01-27 18:46:39 +03:00
qemu_fdt_nop_node(fdt, "/psci");
}
qemu_fdt_add_subnode(fdt, "/psci");
if (armcpu->psci_version >= QEMU_PSCI_VERSION_0_2) {
if (armcpu->psci_version < QEMU_PSCI_VERSION_1_0) {
const char comp[] = "arm,psci-0.2\0arm,psci";
qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
} else {
const char comp[] = "arm,psci-1.0\0arm,psci-0.2\0arm,psci";
qemu_fdt_setprop(fdt, "/psci", "compatible", comp, sizeof(comp));
}
cpu_off_fn = QEMU_PSCI_0_2_FN_CPU_OFF;
if (arm_feature(&armcpu->env, ARM_FEATURE_AARCH64)) {
cpu_suspend_fn = QEMU_PSCI_0_2_FN64_CPU_SUSPEND;
cpu_on_fn = QEMU_PSCI_0_2_FN64_CPU_ON;
migrate_fn = QEMU_PSCI_0_2_FN64_MIGRATE;
} else {
cpu_suspend_fn = QEMU_PSCI_0_2_FN_CPU_SUSPEND;
cpu_on_fn = QEMU_PSCI_0_2_FN_CPU_ON;
migrate_fn = QEMU_PSCI_0_2_FN_MIGRATE;
}
} else {
qemu_fdt_setprop_string(fdt, "/psci", "compatible", "arm,psci");
cpu_suspend_fn = QEMU_PSCI_0_1_FN_CPU_SUSPEND;
cpu_off_fn = QEMU_PSCI_0_1_FN_CPU_OFF;
cpu_on_fn = QEMU_PSCI_0_1_FN_CPU_ON;
migrate_fn = QEMU_PSCI_0_1_FN_MIGRATE;
}
/* We adopt the PSCI spec's nomenclature, and use 'conduit' to refer
* to the instruction that should be used to invoke PSCI functions.
* However, the device tree binding uses 'method' instead, so that is
* what we should use here.
*/
qemu_fdt_setprop_string(fdt, "/psci", "method", psci_method);
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_suspend", cpu_suspend_fn);
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_off", cpu_off_fn);
qemu_fdt_setprop_cell(fdt, "/psci", "cpu_on", cpu_on_fn);
qemu_fdt_setprop_cell(fdt, "/psci", "migrate", migrate_fn);
}
arm/boot: split load_dtb() from arm_load_kernel() load_dtb() depends on arm_load_kernel() to figure out place in RAM where it should be loaded, but it's not required for arm_load_kernel() to work. Sometimes it's neccesary for devices added with -device/device_add to be enumerated in DTB as well, which's lead to [1] and surrounding commits to add 2 more machine_done notifiers with non obvious ordering to make dynamic sysbus devices initialization happen in the right order. However instead of moving whole arm_load_kernel() in to machine_done, it's sufficient to move only load_dtb() into virt_machine_done() notifier and remove ArmLoadKernelNotifier/ /PlatformBusFDTNotifierParams notifiers, which saves us ~90LOC and simplifies code flow quite a bit. Later would allow to consolidate DTB generation within one function for 'mach-virt' board and make it reentrant so it could generate updated DTB in device hotplug secenarios. While at it rename load_dtb() to arm_load_dtb() since it's public now. Add additional field skip_dtb_autoload to struct arm_boot_info to allow manual DTB load later in mach-virt and to avoid touching all other boards to explicitly call arm_load_dtb(). 1) (ac9d32e hw/arm/boot: arm_load_kernel implemented as a machine init done notifier) Signed-off-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Andrew Jones <drjones@redhat.com> Message-id: 1525691524-32265-4-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2018-05-10 20:10:56 +03:00
int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
hwaddr addr_limit, AddressSpace *as, MachineState *ms)
{
void *fdt = NULL;
int size, rc, n = 0;
uint32_t acells, scells;
unsigned int i;
hwaddr mem_base, mem_len;
char **node_path;
Error *err = NULL;
if (binfo->dtb_filename) {
char *filename;
filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, binfo->dtb_filename);
if (!filename) {
fprintf(stderr, "Couldn't open dtb file %s\n", binfo->dtb_filename);
goto fail;
}
fdt = load_device_tree(filename, &size);
if (!fdt) {
fprintf(stderr, "Couldn't open dtb file %s\n", filename);
g_free(filename);
goto fail;
}
g_free(filename);
} else {
fdt = binfo->get_dtb(binfo, &size);
if (!fdt) {
fprintf(stderr, "Board was unable to create a dtb blob\n");
goto fail;
}
}
if (addr_limit > addr && size > (addr_limit - addr)) {
/* Installing the device tree blob at addr would exceed addr_limit.
* Whether this constitutes failure is up to the caller to decide,
* so just return 0 as size, i.e., no error.
*/
g_free(fdt);
return 0;
}
acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells",
NULL, &error_fatal);
scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells",
NULL, &error_fatal);
if (acells == 0 || scells == 0) {
fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n");
goto fail;
}
if (scells < 2 && binfo->ram_size >= 4 * GiB) {
/* This is user error so deserves a friendlier error message
* than the failure of setprop_sized_cells would provide
*/
fprintf(stderr, "qemu: dtb file not compatible with "
"RAM size > 4GB\n");
goto fail;
}
/* nop all root nodes matching /memory or /memory@unit-address */
node_path = qemu_fdt_node_unit_path(fdt, "memory", &err);
if (err) {
error_report_err(err);
goto fail;
}
while (node_path[n]) {
if (g_str_has_prefix(node_path[n], "/memory")) {
qemu_fdt_nop_node(fdt, node_path[n]);
}
n++;
}
g_strfreev(node_path);
hw/arm/virt: Don't create device-tree node for empty NUMA node The empty NUMA node, where no memory resides, are allowed. For example, the following command line specifies two empty NUMA nodes. With this, QEMU fails to boot because of the conflicting device-tree node names, as the following error message indicates. /home/gavin/sandbox/qemu.main/build/qemu-system-aarch64 \ -accel kvm -machine virt,gic-version=host \ -cpu host -smp 4,sockets=2,cores=2,threads=1 \ -m 1024M,slots=16,maxmem=64G \ -object memory-backend-ram,id=mem0,size=512M \ -object memory-backend-ram,id=mem1,size=512M \ -numa node,nodeid=0,cpus=0-1,memdev=mem0 \ -numa node,nodeid=1,cpus=2-3,memdev=mem1 \ -numa node,nodeid=2 \ -numa node,nodeid=3 : qemu-system-aarch64: FDT: Failed to create subnode /memory@80000000: FDT_ERR_EXISTS As specified by linux device-tree binding document, the device-tree nodes for these empty NUMA nodes shouldn't be generated. However, the corresponding NUMA node IDs should be included in the distance map. The memory hotplug through device-tree on ARM64 isn't existing so far and it's not necessary to require the user to provide a distance map. Furthermore, the default distance map Linux generates may even be sufficient. So this simply skips populating the device-tree nodes for these empty NUMA nodes to avoid the error, so that QEMU can be started successfully. Signed-off-by: Gavin Shan <gshan@redhat.com> Reviewed-by: Andrew Jones <drjones@redhat.com> Message-Id: <20211015124246.23073-1-gshan@redhat.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2021-10-15 15:42:46 +03:00
/*
* We drop all the memory nodes which correspond to empty NUMA nodes
* from the device tree, because the Linux NUMA binding document
* states they should not be generated. Linux will get the NUMA node
* IDs of the empty NUMA nodes from the distance map if they are needed.
* This means QEMU users may be obliged to provide command lines which
* configure distance maps when the empty NUMA node IDs are needed and
* Linux's default distance map isn't sufficient.
*/
if (ms->numa_state != NULL && ms->numa_state->num_nodes > 0) {
mem_base = binfo->loader_start;
for (i = 0; i < ms->numa_state->num_nodes; i++) {
mem_len = ms->numa_state->nodes[i].node_mem;
hw/arm/virt: Don't create device-tree node for empty NUMA node The empty NUMA node, where no memory resides, are allowed. For example, the following command line specifies two empty NUMA nodes. With this, QEMU fails to boot because of the conflicting device-tree node names, as the following error message indicates. /home/gavin/sandbox/qemu.main/build/qemu-system-aarch64 \ -accel kvm -machine virt,gic-version=host \ -cpu host -smp 4,sockets=2,cores=2,threads=1 \ -m 1024M,slots=16,maxmem=64G \ -object memory-backend-ram,id=mem0,size=512M \ -object memory-backend-ram,id=mem1,size=512M \ -numa node,nodeid=0,cpus=0-1,memdev=mem0 \ -numa node,nodeid=1,cpus=2-3,memdev=mem1 \ -numa node,nodeid=2 \ -numa node,nodeid=3 : qemu-system-aarch64: FDT: Failed to create subnode /memory@80000000: FDT_ERR_EXISTS As specified by linux device-tree binding document, the device-tree nodes for these empty NUMA nodes shouldn't be generated. However, the corresponding NUMA node IDs should be included in the distance map. The memory hotplug through device-tree on ARM64 isn't existing so far and it's not necessary to require the user to provide a distance map. Furthermore, the default distance map Linux generates may even be sufficient. So this simply skips populating the device-tree nodes for these empty NUMA nodes to avoid the error, so that QEMU can be started successfully. Signed-off-by: Gavin Shan <gshan@redhat.com> Reviewed-by: Andrew Jones <drjones@redhat.com> Message-Id: <20211015124246.23073-1-gshan@redhat.com> Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
2021-10-15 15:42:46 +03:00
if (!mem_len) {
continue;
}
rc = fdt_add_memory_node(fdt, acells, mem_base,
scells, mem_len, i);
if (rc < 0) {
fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n",
mem_base);
goto fail;
}
mem_base += mem_len;
}
} else {
rc = fdt_add_memory_node(fdt, acells, binfo->loader_start,
scells, binfo->ram_size, -1);
if (rc < 0) {
fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n",
binfo->loader_start);
goto fail;
}
}
rc = fdt_path_offset(fdt, "/chosen");
if (rc < 0) {
qemu_fdt_add_subnode(fdt, "/chosen");
}
if (ms->kernel_cmdline && *ms->kernel_cmdline) {
rc = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
ms->kernel_cmdline);
if (rc < 0) {
fprintf(stderr, "couldn't set /chosen/bootargs\n");
goto fail;
}
}
if (binfo->initrd_size) {
rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
binfo->initrd_start);
if (rc < 0) {
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
goto fail;
}
rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
binfo->initrd_start + binfo->initrd_size);
if (rc < 0) {
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
goto fail;
}
}
fdt_add_psci_node(fdt);
if (binfo->modify_dtb) {
binfo->modify_dtb(binfo, fdt);
}
qemu_fdt_dumpdtb(fdt, size);
/* Put the DTB into the memory map as a ROM image: this will ensure
* the DTB is copied again upon reset, even if addr points into RAM.
*/
rom_add_blob_fixed_as("dtb", fdt, size, addr, as);
g_free(fdt);
return size;
fail:
g_free(fdt);
return -1;
}
static void do_cpu_reset(void *opaque)
{
ARMCPU *cpu = opaque;
CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
const struct arm_boot_info *info = env->boot_info;
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();
}
cpu_set_pc(cs, entry);
} else {
/* If we are booting Linux then we need to check whether we are
* booting into secure or non-secure state and adjust the state
* accordingly. Out of reset, ARM is defined to be in secure state
* (SCR.NS = 0), we change that here if non-secure boot has been
* requested.
*/
if (arm_feature(env, ARM_FEATURE_EL3)) {
/* AArch64 is defined to come out of reset into EL3 if enabled.
* If we are booting Linux then we need to adjust our EL as
* Linux expects us to be in EL2 or EL1. AArch32 resets into
* SVC, which Linux expects, so no privilege/exception level to
* adjust.
*/
if (env->aarch64) {
env->cp15.scr_el3 |= SCR_RW;
if (arm_feature(env, ARM_FEATURE_EL2)) {
env->cp15.hcr_el2 |= HCR_RW;
env->pstate = PSTATE_MODE_EL2h;
} else {
env->pstate = PSTATE_MODE_EL1h;
}
if (cpu_isar_feature(aa64_pauth, cpu)) {
env->cp15.scr_el3 |= SCR_API | SCR_APK;
}
if (cpu_isar_feature(aa64_mte, cpu)) {
env->cp15.scr_el3 |= SCR_ATA;
}
if (cpu_isar_feature(aa64_sve, cpu)) {
env->cp15.cptr_el[3] |= R_CPTR_EL3_EZ_MASK;
}
/* AArch64 kernels never boot in secure mode */
assert(!info->secure_boot);
/* This hook is only supported for AArch32 currently:
* bootloader_aarch64[] will not call the hook, and
* the code above has already dropped us into EL2 or EL1.
*/
assert(!info->secure_board_setup);
}
if (arm_feature(env, ARM_FEATURE_EL2)) {
/* If we have EL2 then Linux expects the HVC insn to work */
env->cp15.scr_el3 |= SCR_HCE;
}
/* Set to non-secure if not a secure boot */
if (!info->secure_boot &&
(cs != first_cpu || !info->secure_board_setup)) {
/* Linux expects non-secure state */
env->cp15.scr_el3 |= SCR_NS;
/* Set NSACR.{CP11,CP10} so NS can access the FPU */
env->cp15.nsacr |= 3 << 10;
}
}
if (!env->aarch64 && !info->secure_boot &&
arm_feature(env, ARM_FEATURE_EL2)) {
/*
* This is an AArch32 boot not to Secure state, and
* we have Hyp mode available, so boot the kernel into
* Hyp mode. This is not how the CPU comes out of reset,
* so we need to manually put it there.
*/
cpsr_write(env, ARM_CPU_MODE_HYP, CPSR_M, CPSRWriteRaw);
}
if (cs == first_cpu) {
AddressSpace *as = arm_boot_address_space(cpu, info);
cpu_set_pc(cs, info->loader_start);
if (!have_dtb(info)) {
if (old_param) {
set_kernel_args_old(info, as);
} else {
set_kernel_args(info, as);
}
}
} else if (info->secondary_cpu_reset_hook) {
info->secondary_cpu_reset_hook(cpu, info);
}
}
arm_rebuild_hflags(env);
}
}
hw/arm: pass pristine kernel image to guest firmware over fw_cfg Introduce the new boolean field "arm_boot_info.firmware_loaded". When this field is set, it means that the portion of guest DRAM that the VCPU normally starts to execute, or the pflash chip that the VCPU normally starts to execute, has been populated by board-specific code with full-fledged guest firmware code, before the board calls arm_load_kernel(). Simultaneously, "arm_boot_info.firmware_loaded" guarantees that the board code has set up the global firmware config instance, for arm_load_kernel() to find with fw_cfg_find(). Guest kernel (-kernel) and guest firmware (-bios, -pflash) has always been possible to specify independently on the command line. The following cases should be considered: nr -bios -pflash -kernel description unit#0 -- ------- ------- ------- ------------------------------------------- 1 present present absent Board code rejects this case, -bios and present present present -pflash unit#0 are exclusive. Left intact by this patch. 2 absent absent present Traditional kernel loading, with qemu's minimal board firmware. Left intact by this patch. 3 absent present absent Preexistent case for booting guest firmware present absent absent loaded with -bios or -pflash. Left intact by this patch. 4 absent absent absent Preexistent case for not loading any firmware or kernel up-front. Left intact by this patch. 5 present absent present New case introduced by this patch: kernel absent present present image is passed to externally loaded firmware in unmodified form, using fw_cfg. An easy way to see that this patch doesn't interfere with existing cases is to realize that "info->firmware_loaded" is constant zero at this point. Which makes the "outer" condition unchanged, and the "inner" condition (with the fw_cfg-related code) dead. Signed-off-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Message-id: 1419250305-31062-11-git-send-email-pbonzini@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2014-12-22 15:11:44 +03:00
/**
* load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified
* by key.
* @fw_cfg: The firmware config instance to store the data in.
* @size_key: The firmware config key to store the size of the loaded
* data under, with fw_cfg_add_i32().
* @data_key: The firmware config key to store the loaded data under,
* with fw_cfg_add_bytes().
* @image_name: The name of the image file to load. If it is NULL, the
* function returns without doing anything.
* @try_decompress: Whether the image should be decompressed (gunzipped) before
* adding it to fw_cfg. If decompression fails, the image is
* loaded as-is.
*
* In case of failure, the function prints an error message to stderr and the
* process exits with status 1.
*/
static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
uint16_t data_key, const char *image_name,
bool try_decompress)
{
size_t size = -1;
uint8_t *data;
if (image_name == NULL) {
return;
}
if (try_decompress) {
size = load_image_gzipped_buffer(image_name,
LOAD_IMAGE_MAX_GUNZIP_BYTES, &data);
}
if (size == (size_t)-1) {
gchar *contents;
gsize length;
if (!g_file_get_contents(image_name, &contents, &length, NULL)) {
hw/arm: Replace fprintf(stderr, "*\n" with error_report() Replace a large number of the fprintf(stderr, "*\n" calls with error_report(). The functions were renamed with these commands and then compiler issues where manually fixed. find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + Some lines where then manually tweaked to pass checkpatch. The 'qemu: ' prefix was manually removed from the hw/arm/boot.c file. Signed-off-by: Alistair Francis <alistair.francis@xilinx.com> Cc: qemu-arm@nongnu.org Conversions that aren't followed by exit() dropped, because they might be inappropriate. Also trim trailing punctuation from error messages. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-Id: <20180203084315.20497-3-armbru@redhat.com>
2018-02-03 11:43:03 +03:00
error_report("failed to load \"%s\"", image_name);
hw/arm: pass pristine kernel image to guest firmware over fw_cfg Introduce the new boolean field "arm_boot_info.firmware_loaded". When this field is set, it means that the portion of guest DRAM that the VCPU normally starts to execute, or the pflash chip that the VCPU normally starts to execute, has been populated by board-specific code with full-fledged guest firmware code, before the board calls arm_load_kernel(). Simultaneously, "arm_boot_info.firmware_loaded" guarantees that the board code has set up the global firmware config instance, for arm_load_kernel() to find with fw_cfg_find(). Guest kernel (-kernel) and guest firmware (-bios, -pflash) has always been possible to specify independently on the command line. The following cases should be considered: nr -bios -pflash -kernel description unit#0 -- ------- ------- ------- ------------------------------------------- 1 present present absent Board code rejects this case, -bios and present present present -pflash unit#0 are exclusive. Left intact by this patch. 2 absent absent present Traditional kernel loading, with qemu's minimal board firmware. Left intact by this patch. 3 absent present absent Preexistent case for booting guest firmware present absent absent loaded with -bios or -pflash. Left intact by this patch. 4 absent absent absent Preexistent case for not loading any firmware or kernel up-front. Left intact by this patch. 5 present absent present New case introduced by this patch: kernel absent present present image is passed to externally loaded firmware in unmodified form, using fw_cfg. An easy way to see that this patch doesn't interfere with existing cases is to realize that "info->firmware_loaded" is constant zero at this point. Which makes the "outer" condition unchanged, and the "inner" condition (with the fw_cfg-related code) dead. Signed-off-by: Laszlo Ersek <lersek@redhat.com> Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> Message-id: 1419250305-31062-11-git-send-email-pbonzini@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2014-12-22 15:11:44 +03:00
exit(1);
}
size = length;
data = (uint8_t *)contents;
}
fw_cfg_add_i32(fw_cfg, size_key, size);
fw_cfg_add_bytes(fw_cfg, data_key, data, size);
}
static int do_arm_linux_init(Object *obj, void *opaque)
{
if (object_dynamic_cast(obj, TYPE_ARM_LINUX_BOOT_IF)) {
ARMLinuxBootIf *albif = ARM_LINUX_BOOT_IF(obj);
ARMLinuxBootIfClass *albifc = ARM_LINUX_BOOT_IF_GET_CLASS(obj);
struct arm_boot_info *info = opaque;
if (albifc->arm_linux_init) {
albifc->arm_linux_init(albif, info->secure_boot);
}
}
return 0;
}
static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
uint64_t *lowaddr, uint64_t *highaddr,
int elf_machine, AddressSpace *as)
{
bool elf_is64;
union {
Elf32_Ehdr h32;
Elf64_Ehdr h64;
} elf_header;
int data_swab = 0;
bool big_endian;
ssize_t ret = -1;
Error *err = NULL;
load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
if (err) {
error_free(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_as(info->kernel_filename, NULL, NULL, NULL,
hw/core/loader: Let load_elf() populate a field with CPU-specific flags While loading the executable, some platforms (like AVR) need to detect CPU type that executable is built for - and, with this patch, this is enabled by reading the field 'e_flags' of the ELF header of the executable in question. The change expands functionality of the following functions: - load_elf() - load_elf_as() - load_elf_ram() - load_elf_ram_sym() The argument added to these functions is called 'pflags' and is of type 'uint32_t*' (that matches 'pointer to 'elf_word'', 'elf_word' being the type of the field 'e_flags', in both 32-bit and 64-bit variants of ELF header). Callers are allowed to pass NULL as that argument, and in such case no lookup to the field 'e_flags' will happen, and no information will be returned, of course. CC: Richard Henderson <rth@twiddle.net> CC: Peter Maydell <peter.maydell@linaro.org> CC: Edgar E. Iglesias <edgar.iglesias@gmail.com> CC: Michael Walle <michael@walle.cc> CC: Thomas Huth <huth@tuxfamily.org> CC: Laurent Vivier <laurent@vivier.eu> CC: Philippe Mathieu-Daudé <f4bug@amsat.org> CC: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> CC: Aurelien Jarno <aurelien@aurel32.net> CC: Jia Liu <proljc@gmail.com> CC: David Gibson <david@gibson.dropbear.id.au> CC: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> CC: BALATON Zoltan <balaton@eik.bme.hu> CC: Christian Borntraeger <borntraeger@de.ibm.com> CC: Thomas Huth <thuth@redhat.com> CC: Artyom Tarasenko <atar4qemu@gmail.com> CC: Fabien Chouteau <chouteau@adacore.com> CC: KONRAD Frederic <frederic.konrad@adacore.com> CC: Max Filippov <jcmvbkbc@gmail.com> Reviewed-by: Aleksandar Rikalo <aleksandar.rikalo@rt-rk.com> Signed-off-by: Michael Rolnik <mrolnik@gmail.com> Signed-off-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Signed-off-by: Aleksandar Markovic <amarkovic@wavecomp.com> Message-Id: <1580079311-20447-24-git-send-email-aleksandar.markovic@rt-rk.com>
2020-01-27 01:55:04 +03:00
pentry, lowaddr, highaddr, NULL, big_endian, elf_machine,
1, data_swab, as);
if (ret <= 0) {
/* The header loaded but the image didn't */
exit(1);
}
return ret;
}
static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
hwaddr *entry, AddressSpace *as)
{
hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR;
uint64_t kernel_size = 0;
uint8_t *buffer;
int size;
/* On aarch64, it's the bootloader's job to uncompress the kernel. */
size = load_image_gzipped_buffer(filename, LOAD_IMAGE_MAX_GUNZIP_BYTES,
&buffer);
if (size < 0) {
gsize len;
/* Load as raw file otherwise */
if (!g_file_get_contents(filename, (char **)&buffer, &len, NULL)) {
return -1;
}
size = len;
}
/* check the arm64 magic header value -- very old kernels may not have it */
if (size > ARM64_MAGIC_OFFSET + 4 &&
memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
uint64_t hdrvals[2];
/* The arm64 Image header has text_offset and image_size fields at 8 and
* 16 bytes into the Image header, respectively. The text_offset field
* is only valid if the image_size is non-zero.
*/
memcpy(&hdrvals, buffer + ARM64_TEXT_OFFSET_OFFSET, sizeof(hdrvals));
kernel_size = le64_to_cpu(hdrvals[1]);
if (kernel_size != 0) {
kernel_load_offset = le64_to_cpu(hdrvals[0]);
/*
* We write our startup "bootloader" at the very bottom of RAM,
* so that bit can't be used for the image. Luckily the Image
* format specification is that the image requests only an offset
* from a 2MB boundary, not an absolute load address. So if the
* image requests an offset that might mean it overlaps with the
* bootloader, we can just load it starting at 2MB+offset rather
* than 0MB + offset.
*/
if (kernel_load_offset < BOOTLOADER_MAX_SIZE) {
kernel_load_offset += 2 * MiB;
}
}
}
/*
* Kernels before v3.17 don't populate the image_size field, and
* raw images have no header. For those our best guess at the size
* is the size of the Image file itself.
*/
if (kernel_size == 0) {
kernel_size = size;
}
*entry = mem_base + kernel_load_offset;
rom_add_blob_fixed_as(filename, buffer, size, *entry, as);
g_free(buffer);
return kernel_size;
}
static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
struct arm_boot_info *info)
{
/* Set up for a direct boot of a kernel image file. */
CPUState *cs;
AddressSpace *as = arm_boot_address_space(cpu, info);
ssize_t kernel_size;
int initrd_size;
int is_linux = 0;
uint64_t elf_entry;
/* Addresses of first byte used and first byte not used by the image */
2019-07-22 18:18:04 +03:00
uint64_t image_low_addr = 0, image_high_addr = 0;
int elf_machine;
hwaddr entry;
static const ARMInsnFixup *primary_loader;
uint64_t ram_end = info->loader_start + info->ram_size;
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
primary_loader = bootloader_aarch64;
elf_machine = EM_AARCH64;
} else {
primary_loader = bootloader;
if (!info->write_board_setup) {
primary_loader += BOOTLOADER_NO_BOARD_SETUP_OFFSET;
}
elf_machine = EM_ARM;
}
/* Assume that raw images are linux kernels, and ELF images are not. */
kernel_size = arm_load_elf(info, &elf_entry, &image_low_addr,
&image_high_addr, elf_machine, as);
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.
*/
if (image_low_addr > info->loader_start
|| image_high_addr < info->loader_start) {
/*
* Set image_low_addr as address limit for arm_load_dtb if it may be
* pointing into RAM, otherwise pass '0' (no limit)
*/
if (image_low_addr < info->loader_start) {
image_low_addr = 0;
}
arm/boot: split load_dtb() from arm_load_kernel() load_dtb() depends on arm_load_kernel() to figure out place in RAM where it should be loaded, but it's not required for arm_load_kernel() to work. Sometimes it's neccesary for devices added with -device/device_add to be enumerated in DTB as well, which's lead to [1] and surrounding commits to add 2 more machine_done notifiers with non obvious ordering to make dynamic sysbus devices initialization happen in the right order. However instead of moving whole arm_load_kernel() in to machine_done, it's sufficient to move only load_dtb() into virt_machine_done() notifier and remove ArmLoadKernelNotifier/ /PlatformBusFDTNotifierParams notifiers, which saves us ~90LOC and simplifies code flow quite a bit. Later would allow to consolidate DTB generation within one function for 'mach-virt' board and make it reentrant so it could generate updated DTB in device hotplug secenarios. While at it rename load_dtb() to arm_load_dtb() since it's public now. Add additional field skip_dtb_autoload to struct arm_boot_info to allow manual DTB load later in mach-virt and to avoid touching all other boards to explicitly call arm_load_dtb(). 1) (ac9d32e hw/arm/boot: arm_load_kernel implemented as a machine init done notifier) Signed-off-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Andrew Jones <drjones@redhat.com> Message-id: 1525691524-32265-4-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2018-05-10 20:10:56 +03:00
info->dtb_start = info->loader_start;
info->dtb_limit = image_low_addr;
}
}
entry = elf_entry;
if (kernel_size < 0) {
uint64_t loadaddr = info->loader_start + KERNEL_NOLOAD_ADDR;
kernel_size = load_uimage_as(info->kernel_filename, &entry, &loadaddr,
&is_linux, NULL, NULL, as);
2019-07-22 18:18:04 +03:00
if (kernel_size >= 0) {
image_low_addr = loadaddr;
image_high_addr = image_low_addr + kernel_size;
}
}
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {
kernel_size = load_aarch64_image(info->kernel_filename,
info->loader_start, &entry, as);
is_linux = 1;
2019-07-22 18:18:04 +03:00
if (kernel_size >= 0) {
image_low_addr = entry;
image_high_addr = image_low_addr + kernel_size;
}
} else if (kernel_size < 0) {
/* 32-bit ARM */
entry = info->loader_start + KERNEL_LOAD_ADDR;
kernel_size = load_image_targphys_as(info->kernel_filename, entry,
ram_end - KERNEL_LOAD_ADDR, as);
is_linux = 1;
2019-07-22 18:18:04 +03:00
if (kernel_size >= 0) {
image_low_addr = entry;
image_high_addr = image_low_addr + kernel_size;
}
}
if (kernel_size < 0) {
hw/arm: Replace fprintf(stderr, "*\n" with error_report() Replace a large number of the fprintf(stderr, "*\n" calls with error_report(). The functions were renamed with these commands and then compiler issues where manually fixed. find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + Some lines where then manually tweaked to pass checkpatch. The 'qemu: ' prefix was manually removed from the hw/arm/boot.c file. Signed-off-by: Alistair Francis <alistair.francis@xilinx.com> Cc: qemu-arm@nongnu.org Conversions that aren't followed by exit() dropped, because they might be inappropriate. Also trim trailing punctuation from error messages. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-Id: <20180203084315.20497-3-armbru@redhat.com>
2018-02-03 11:43:03 +03:00
error_report("could not load kernel '%s'", info->kernel_filename);
exit(1);
}
if (kernel_size > info->ram_size) {
error_report("kernel '%s' is too large to fit in RAM "
"(kernel size %zd, RAM size %" PRId64 ")",
info->kernel_filename, kernel_size, info->ram_size);
exit(1);
}
info->entry = entry;
/*
* 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
* enough room for a decent sized initrd, and on boards with large
* amounts of RAM we must avoid the initrd being so far up in RAM
* that it is outside lowmem and inaccessible to the kernel.
* So for boards with less than 256MB of RAM we put the initrd
* halfway into RAM, and for boards with 256MB of RAM or more we put
* the initrd at 128MB.
* We also refuse to put the initrd somewhere that will definitely
* overlay the kernel we just loaded, though for kernel formats which
* don't tell us their exact size (eg self-decompressing 32-bit kernels)
* we might still make a bad choice here.
*/
info->initrd_start = info->loader_start +
MIN(info->ram_size / 2, 128 * MiB);
2019-07-22 18:18:04 +03:00
if (image_high_addr) {
info->initrd_start = MAX(info->initrd_start, image_high_addr);
}
info->initrd_start = TARGET_PAGE_ALIGN(info->initrd_start);
if (is_linux) {
uint32_t fixupcontext[FIXUP_MAX];
if (info->initrd_filename) {
if (info->initrd_start >= ram_end) {
error_report("not enough space after kernel to load initrd");
exit(1);
}
initrd_size = load_ramdisk_as(info->initrd_filename,
info->initrd_start,
ram_end - info->initrd_start, as);
if (initrd_size < 0) {
initrd_size = load_image_targphys_as(info->initrd_filename,
info->initrd_start,
ram_end -
info->initrd_start,
as);
}
if (initrd_size < 0) {
hw/arm: Replace fprintf(stderr, "*\n" with error_report() Replace a large number of the fprintf(stderr, "*\n" calls with error_report(). The functions were renamed with these commands and then compiler issues where manually fixed. find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + Some lines where then manually tweaked to pass checkpatch. The 'qemu: ' prefix was manually removed from the hw/arm/boot.c file. Signed-off-by: Alistair Francis <alistair.francis@xilinx.com> Cc: qemu-arm@nongnu.org Conversions that aren't followed by exit() dropped, because they might be inappropriate. Also trim trailing punctuation from error messages. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-Id: <20180203084315.20497-3-armbru@redhat.com>
2018-02-03 11:43:03 +03:00
error_report("could not load initrd '%s'",
info->initrd_filename);
exit(1);
}
if (info->initrd_start + initrd_size > ram_end) {
error_report("could not load initrd '%s': "
"too big to fit into RAM after the kernel",
info->initrd_filename);
exit(1);
}
} else {
initrd_size = 0;
}
info->initrd_size = initrd_size;
fixupcontext[FIXUP_BOARDID] = info->board_id;
fixupcontext[FIXUP_BOARD_SETUP] = info->board_setup_addr;
/*
* for device tree boot, we pass the DTB directly in r2. Otherwise
* we point to the kernel args.
*/
if (have_dtb(info)) {
hwaddr align;
if (elf_machine == EM_AARCH64) {
/*
* Some AArch64 kernels on early bootup map the fdt region as
*
* [ ALIGN_DOWN(fdt, 2MB) ... ALIGN_DOWN(fdt, 2MB) + 2MB ]
*
* Let's play safe and prealign it to 2MB to give us some space.
*/
align = 2 * MiB;
} else {
/*
* Some 32bit kernels will trash anything in the 4K page the
* initrd ends in, so make sure the DTB isn't caught up in that.
*/
align = 4 * KiB;
}
/* Place the DTB after the initrd in memory with alignment. */
arm/boot: split load_dtb() from arm_load_kernel() load_dtb() depends on arm_load_kernel() to figure out place in RAM where it should be loaded, but it's not required for arm_load_kernel() to work. Sometimes it's neccesary for devices added with -device/device_add to be enumerated in DTB as well, which's lead to [1] and surrounding commits to add 2 more machine_done notifiers with non obvious ordering to make dynamic sysbus devices initialization happen in the right order. However instead of moving whole arm_load_kernel() in to machine_done, it's sufficient to move only load_dtb() into virt_machine_done() notifier and remove ArmLoadKernelNotifier/ /PlatformBusFDTNotifierParams notifiers, which saves us ~90LOC and simplifies code flow quite a bit. Later would allow to consolidate DTB generation within one function for 'mach-virt' board and make it reentrant so it could generate updated DTB in device hotplug secenarios. While at it rename load_dtb() to arm_load_dtb() since it's public now. Add additional field skip_dtb_autoload to struct arm_boot_info to allow manual DTB load later in mach-virt and to avoid touching all other boards to explicitly call arm_load_dtb(). 1) (ac9d32e hw/arm/boot: arm_load_kernel implemented as a machine init done notifier) Signed-off-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Andrew Jones <drjones@redhat.com> Message-id: 1525691524-32265-4-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2018-05-10 20:10:56 +03:00
info->dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size,
align);
if (info->dtb_start >= ram_end) {
error_report("Not enough space for DTB after kernel/initrd");
exit(1);
}
fixupcontext[FIXUP_ARGPTR_LO] = info->dtb_start;
fixupcontext[FIXUP_ARGPTR_HI] = info->dtb_start >> 32;
} else {
fixupcontext[FIXUP_ARGPTR_LO] =
info->loader_start + KERNEL_ARGS_ADDR;
fixupcontext[FIXUP_ARGPTR_HI] =
(info->loader_start + KERNEL_ARGS_ADDR) >> 32;
if (info->ram_size >= 4 * GiB) {
hw/arm: Replace fprintf(stderr, "*\n" with error_report() Replace a large number of the fprintf(stderr, "*\n" calls with error_report(). The functions were renamed with these commands and then compiler issues where manually fixed. find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N;N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + find ./* -type f -exec sed -i \ 'N; {s|fprintf(stderr, "\(.*\)\\n"\(.*\));|error_report("\1"\2);|Ig}' \ {} + Some lines where then manually tweaked to pass checkpatch. The 'qemu: ' prefix was manually removed from the hw/arm/boot.c file. Signed-off-by: Alistair Francis <alistair.francis@xilinx.com> Cc: qemu-arm@nongnu.org Conversions that aren't followed by exit() dropped, because they might be inappropriate. Also trim trailing punctuation from error messages. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Eric Blake <eblake@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Message-Id: <20180203084315.20497-3-armbru@redhat.com>
2018-02-03 11:43:03 +03:00
error_report("RAM size must be less than 4GB to boot"
" Linux kernel using ATAGS (try passing a device tree"
" using -dtb)");
exit(1);
}
}
fixupcontext[FIXUP_ENTRYPOINT_LO] = entry;
fixupcontext[FIXUP_ENTRYPOINT_HI] = entry >> 32;
write_bootloader("bootloader", info->loader_start,
primary_loader, fixupcontext, as);
if (info->write_board_setup) {
info->write_board_setup(cpu, info);
}
/*
* Notify devices which need to fake up firmware initialization
* that we're doing a direct kernel boot.
*/
object_child_foreach_recursive(object_get_root(),
do_arm_linux_init, info);
}
info->is_linux = is_linux;
for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) {
ARM_CPU(cs)->env.boot_info = info;
}
}
static void arm_setup_firmware_boot(ARMCPU *cpu, struct arm_boot_info *info)
{
/* Set up for booting firmware (which might load a kernel via fw_cfg) */
if (have_dtb(info)) {
/*
* If we have a device tree blob, but no kernel to supply it to (or
* the kernel is supposed to be loaded by the bootloader), copy the
* DTB to the base of RAM for the bootloader to pick up.
*/
info->dtb_start = info->loader_start;
}
if (info->kernel_filename) {
FWCfgState *fw_cfg;
bool try_decompressing_kernel;
fw_cfg = fw_cfg_find();
2021-07-26 19:33:51 +03:00
if (!fw_cfg) {
error_report("This machine type does not support loading both "
"a guest firmware/BIOS image and a guest kernel at "
"the same time. You should change your QEMU command "
"line to specify one or the other, but not both.");
exit(1);
}
try_decompressing_kernel = arm_feature(&cpu->env,
ARM_FEATURE_AARCH64);
/*
* Expose the kernel, the command line, and the initrd in fw_cfg.
* We don't process them here at all, it's all left to the
* firmware.
*/
load_image_to_fw_cfg(fw_cfg,
FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA,
info->kernel_filename,
try_decompressing_kernel);
load_image_to_fw_cfg(fw_cfg,
FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
info->initrd_filename, false);
if (info->kernel_cmdline) {
fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
strlen(info->kernel_cmdline) + 1);
fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
info->kernel_cmdline);
}
}
/*
* We will start from address 0 (typically a boot ROM image) in the
* same way as hardware. Leave env->boot_info NULL, so that
* do_cpu_reset() knows it does not need to alter the PC on reset.
*/
}
void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info)
{
CPUState *cs;
AddressSpace *as = arm_boot_address_space(cpu, info);
hw/arm/boot: Support setting psci-conduit based on guest EL Currently we expect board code to set the psci-conduit property on CPUs and ensure that secondary CPUs are created with the start-powered-off property set to false, if the board wishes to use QEMU's builtin PSCI emulation. This worked OK for the virt board where we first wanted to use it, because the virt board directly creates its CPUs and is in a reasonable position to set those properties. For other boards which model real hardware and use a separate SoC object, however, it is more awkward. Most PSCI-using boards just set the psci-conduit board unconditionally. This was never strictly speaking correct (because you would not be able to run EL3 guest firmware that itself provided the PSCI interface, as the QEMU implementation would overrule it), but mostly worked in practice because for non-PSCI SMC calls QEMU would emulate the SMC instruction as normal (by trapping to guest EL3). However, we would like to make our PSCI emulation follow the part of the SMCC specification that mandates that SMC calls with unknown function identifiers return a failure code, which means that all SMC calls will be handled by the PSCI code and the "emulate as normal" path will no longer be taken. We tried to implement that in commit 9fcd15b9193e81 ("arm: tcg: Adhere to SMCCC 1.3 section 5.2"), but this regressed attempts to run EL3 guest code on the affected boards: * mcimx6ul-evk, mcimx7d-sabre, orangepi, xlnx-zcu102 * for the case only of EL3 code loaded via -kernel (and not via -bios or -pflash), virt and xlnx-versal-virt so for the 7.0 release we reverted it (in commit 4825eaae4fdd56f). This commit provides a mechanism that boards can use to arrange that psci-conduit is set if running guest code at a low enough EL but not if it would be running at the same EL that the conduit implies that the QEMU PSCI implementation is using. (Later commits will convert individual board models to use this mechanism.) We do this by moving the setting of the psci-conduit and start-powered-off properties to arm_load_kernel(). Boards which want to potentially use emulated PSCI must set a psci_conduit field in the arm_boot_info struct to the type of conduit they want to use (SMC or HVC); arm_load_kernel() will then set the CPUs up accordingly if it is not going to start the guest code at the same or higher EL as the fake QEMU firmware would be at. Board/SoC code which uses this mechanism should no longer set the CPU psci-conduit property directly. It should only set the start-powered-off property for secondaries if EL3 guest firmware running bare metal expects that rather than the alternative "all CPUs start executing the firmware at once". Note that when calculating whether we are going to run guest code at EL3, we ignore the setting of arm_boot_info::secure_board_setup, which might cause us to run a stub bit of guest code at EL3 which does some board-specific setup before dropping to EL2 or EL1 to run the guest kernel. This is OK because only one board that enables PSCI sets secure_board_setup (the highbank board), and the stub code it writes will behave the same way whether the one SMC call it makes is handled by "emulate the SMC" or by "PSCI default returns an error code". So we can leave that stub code in place until after we've changed the PSCI default behaviour; at that point we will remove it. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Tested-by: Cédric Le Goater <clg@kaod.org> Message-id: 20220127154639.2090164-4-peter.maydell@linaro.org
2022-01-27 18:46:26 +03:00
int boot_el;
CPUARMState *env = &cpu->env;
int nb_cpus = 0;
/*
* CPU objects (unlike devices) are not automatically reset on system
* reset, so we must always register a handler to do so. If we're
* actually loading a kernel, the handler is also responsible for
* arranging that we start it correctly.
*/
for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) {
qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
nb_cpus++;
}
/*
* The board code is not supposed to set secure_board_setup unless
* running its code in secure mode is actually possible, and KVM
* doesn't support secure.
*/
assert(!(info->secure_board_setup && kvm_enabled()));
info->kernel_filename = ms->kernel_filename;
info->kernel_cmdline = ms->kernel_cmdline;
info->initrd_filename = ms->initrd_filename;
info->dtb_filename = ms->dtb;
info->dtb_limit = 0;
/* Load the kernel. */
if (!info->kernel_filename || info->firmware_loaded) {
arm_setup_firmware_boot(cpu, info);
} else {
arm_setup_direct_kernel_boot(cpu, info);
}
hw/arm/boot: Support setting psci-conduit based on guest EL Currently we expect board code to set the psci-conduit property on CPUs and ensure that secondary CPUs are created with the start-powered-off property set to false, if the board wishes to use QEMU's builtin PSCI emulation. This worked OK for the virt board where we first wanted to use it, because the virt board directly creates its CPUs and is in a reasonable position to set those properties. For other boards which model real hardware and use a separate SoC object, however, it is more awkward. Most PSCI-using boards just set the psci-conduit board unconditionally. This was never strictly speaking correct (because you would not be able to run EL3 guest firmware that itself provided the PSCI interface, as the QEMU implementation would overrule it), but mostly worked in practice because for non-PSCI SMC calls QEMU would emulate the SMC instruction as normal (by trapping to guest EL3). However, we would like to make our PSCI emulation follow the part of the SMCC specification that mandates that SMC calls with unknown function identifiers return a failure code, which means that all SMC calls will be handled by the PSCI code and the "emulate as normal" path will no longer be taken. We tried to implement that in commit 9fcd15b9193e81 ("arm: tcg: Adhere to SMCCC 1.3 section 5.2"), but this regressed attempts to run EL3 guest code on the affected boards: * mcimx6ul-evk, mcimx7d-sabre, orangepi, xlnx-zcu102 * for the case only of EL3 code loaded via -kernel (and not via -bios or -pflash), virt and xlnx-versal-virt so for the 7.0 release we reverted it (in commit 4825eaae4fdd56f). This commit provides a mechanism that boards can use to arrange that psci-conduit is set if running guest code at a low enough EL but not if it would be running at the same EL that the conduit implies that the QEMU PSCI implementation is using. (Later commits will convert individual board models to use this mechanism.) We do this by moving the setting of the psci-conduit and start-powered-off properties to arm_load_kernel(). Boards which want to potentially use emulated PSCI must set a psci_conduit field in the arm_boot_info struct to the type of conduit they want to use (SMC or HVC); arm_load_kernel() will then set the CPUs up accordingly if it is not going to start the guest code at the same or higher EL as the fake QEMU firmware would be at. Board/SoC code which uses this mechanism should no longer set the CPU psci-conduit property directly. It should only set the start-powered-off property for secondaries if EL3 guest firmware running bare metal expects that rather than the alternative "all CPUs start executing the firmware at once". Note that when calculating whether we are going to run guest code at EL3, we ignore the setting of arm_boot_info::secure_board_setup, which might cause us to run a stub bit of guest code at EL3 which does some board-specific setup before dropping to EL2 or EL1 to run the guest kernel. This is OK because only one board that enables PSCI sets secure_board_setup (the highbank board), and the stub code it writes will behave the same way whether the one SMC call it makes is handled by "emulate the SMC" or by "PSCI default returns an error code". So we can leave that stub code in place until after we've changed the PSCI default behaviour; at that point we will remove it. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Tested-by: Cédric Le Goater <clg@kaod.org> Message-id: 20220127154639.2090164-4-peter.maydell@linaro.org
2022-01-27 18:46:26 +03:00
/*
* Disable the PSCI conduit if it is set up to target the same
* or a lower EL than the one we're going to start the guest code in.
* This logic needs to agree with the code in do_cpu_reset() which
* decides whether we're going to boot the guest in the highest
* supported exception level or in a lower one.
*/
/*
* If PSCI is enabled, then SMC calls all go to the PSCI handler and
* are never emulated to trap into guest code. It therefore does not
* make sense for the board to have a setup code fragment that runs
* in Secure, because this will probably need to itself issue an SMC of some
* kind as part of its operation.
*/
assert(info->psci_conduit == QEMU_PSCI_CONDUIT_DISABLED ||
!info->secure_board_setup);
hw/arm/boot: Support setting psci-conduit based on guest EL Currently we expect board code to set the psci-conduit property on CPUs and ensure that secondary CPUs are created with the start-powered-off property set to false, if the board wishes to use QEMU's builtin PSCI emulation. This worked OK for the virt board where we first wanted to use it, because the virt board directly creates its CPUs and is in a reasonable position to set those properties. For other boards which model real hardware and use a separate SoC object, however, it is more awkward. Most PSCI-using boards just set the psci-conduit board unconditionally. This was never strictly speaking correct (because you would not be able to run EL3 guest firmware that itself provided the PSCI interface, as the QEMU implementation would overrule it), but mostly worked in practice because for non-PSCI SMC calls QEMU would emulate the SMC instruction as normal (by trapping to guest EL3). However, we would like to make our PSCI emulation follow the part of the SMCC specification that mandates that SMC calls with unknown function identifiers return a failure code, which means that all SMC calls will be handled by the PSCI code and the "emulate as normal" path will no longer be taken. We tried to implement that in commit 9fcd15b9193e81 ("arm: tcg: Adhere to SMCCC 1.3 section 5.2"), but this regressed attempts to run EL3 guest code on the affected boards: * mcimx6ul-evk, mcimx7d-sabre, orangepi, xlnx-zcu102 * for the case only of EL3 code loaded via -kernel (and not via -bios or -pflash), virt and xlnx-versal-virt so for the 7.0 release we reverted it (in commit 4825eaae4fdd56f). This commit provides a mechanism that boards can use to arrange that psci-conduit is set if running guest code at a low enough EL but not if it would be running at the same EL that the conduit implies that the QEMU PSCI implementation is using. (Later commits will convert individual board models to use this mechanism.) We do this by moving the setting of the psci-conduit and start-powered-off properties to arm_load_kernel(). Boards which want to potentially use emulated PSCI must set a psci_conduit field in the arm_boot_info struct to the type of conduit they want to use (SMC or HVC); arm_load_kernel() will then set the CPUs up accordingly if it is not going to start the guest code at the same or higher EL as the fake QEMU firmware would be at. Board/SoC code which uses this mechanism should no longer set the CPU psci-conduit property directly. It should only set the start-powered-off property for secondaries if EL3 guest firmware running bare metal expects that rather than the alternative "all CPUs start executing the firmware at once". Note that when calculating whether we are going to run guest code at EL3, we ignore the setting of arm_boot_info::secure_board_setup, which might cause us to run a stub bit of guest code at EL3 which does some board-specific setup before dropping to EL2 or EL1 to run the guest kernel. This is OK because only one board that enables PSCI sets secure_board_setup (the highbank board), and the stub code it writes will behave the same way whether the one SMC call it makes is handled by "emulate the SMC" or by "PSCI default returns an error code". So we can leave that stub code in place until after we've changed the PSCI default behaviour; at that point we will remove it. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Tested-by: Cédric Le Goater <clg@kaod.org> Message-id: 20220127154639.2090164-4-peter.maydell@linaro.org
2022-01-27 18:46:26 +03:00
/* Boot into highest supported EL ... */
if (arm_feature(env, ARM_FEATURE_EL3)) {
boot_el = 3;
} else if (arm_feature(env, ARM_FEATURE_EL2)) {
boot_el = 2;
} else {
boot_el = 1;
}
/* ...except that if we're booting Linux we adjust the EL we boot into */
if (info->is_linux && !info->secure_boot) {
boot_el = arm_feature(env, ARM_FEATURE_EL2) ? 2 : 1;
}
if ((info->psci_conduit == QEMU_PSCI_CONDUIT_HVC && boot_el >= 2) ||
(info->psci_conduit == QEMU_PSCI_CONDUIT_SMC && boot_el == 3)) {
info->psci_conduit = QEMU_PSCI_CONDUIT_DISABLED;
}
if (info->psci_conduit != QEMU_PSCI_CONDUIT_DISABLED) {
for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) {
Object *cpuobj = OBJECT(cs);
object_property_set_int(cpuobj, "psci-conduit", info->psci_conduit,
&error_abort);
/*
* Secondary CPUs start in PSCI powered-down state. Like the
* code in do_cpu_reset(), we assume first_cpu is the primary
* CPU.
*/
if (cs != first_cpu) {
object_property_set_bool(cpuobj, "start-powered-off", true,
&error_abort);
}
}
}
if (info->psci_conduit == QEMU_PSCI_CONDUIT_DISABLED &&
info->is_linux && nb_cpus > 1) {
/*
* We're booting Linux but not using PSCI, so for SMP we need
* to write a custom secondary CPU boot loader stub, and arrange
* for the secondary CPU reset to make the accompanying initialization.
*/
if (!info->secondary_cpu_reset_hook) {
info->secondary_cpu_reset_hook = default_reset_secondary;
}
if (!info->write_secondary_boot) {
info->write_secondary_boot = default_write_secondary;
}
info->write_secondary_boot(cpu, info);
} else {
/*
* No secondary boot stub; don't use the reset hook that would
* have set the CPU up to call it
*/
info->write_secondary_boot = NULL;
info->secondary_cpu_reset_hook = NULL;
}
hw/arm/boot: Support setting psci-conduit based on guest EL Currently we expect board code to set the psci-conduit property on CPUs and ensure that secondary CPUs are created with the start-powered-off property set to false, if the board wishes to use QEMU's builtin PSCI emulation. This worked OK for the virt board where we first wanted to use it, because the virt board directly creates its CPUs and is in a reasonable position to set those properties. For other boards which model real hardware and use a separate SoC object, however, it is more awkward. Most PSCI-using boards just set the psci-conduit board unconditionally. This was never strictly speaking correct (because you would not be able to run EL3 guest firmware that itself provided the PSCI interface, as the QEMU implementation would overrule it), but mostly worked in practice because for non-PSCI SMC calls QEMU would emulate the SMC instruction as normal (by trapping to guest EL3). However, we would like to make our PSCI emulation follow the part of the SMCC specification that mandates that SMC calls with unknown function identifiers return a failure code, which means that all SMC calls will be handled by the PSCI code and the "emulate as normal" path will no longer be taken. We tried to implement that in commit 9fcd15b9193e81 ("arm: tcg: Adhere to SMCCC 1.3 section 5.2"), but this regressed attempts to run EL3 guest code on the affected boards: * mcimx6ul-evk, mcimx7d-sabre, orangepi, xlnx-zcu102 * for the case only of EL3 code loaded via -kernel (and not via -bios or -pflash), virt and xlnx-versal-virt so for the 7.0 release we reverted it (in commit 4825eaae4fdd56f). This commit provides a mechanism that boards can use to arrange that psci-conduit is set if running guest code at a low enough EL but not if it would be running at the same EL that the conduit implies that the QEMU PSCI implementation is using. (Later commits will convert individual board models to use this mechanism.) We do this by moving the setting of the psci-conduit and start-powered-off properties to arm_load_kernel(). Boards which want to potentially use emulated PSCI must set a psci_conduit field in the arm_boot_info struct to the type of conduit they want to use (SMC or HVC); arm_load_kernel() will then set the CPUs up accordingly if it is not going to start the guest code at the same or higher EL as the fake QEMU firmware would be at. Board/SoC code which uses this mechanism should no longer set the CPU psci-conduit property directly. It should only set the start-powered-off property for secondaries if EL3 guest firmware running bare metal expects that rather than the alternative "all CPUs start executing the firmware at once". Note that when calculating whether we are going to run guest code at EL3, we ignore the setting of arm_boot_info::secure_board_setup, which might cause us to run a stub bit of guest code at EL3 which does some board-specific setup before dropping to EL2 or EL1 to run the guest kernel. This is OK because only one board that enables PSCI sets secure_board_setup (the highbank board), and the stub code it writes will behave the same way whether the one SMC call it makes is handled by "emulate the SMC" or by "PSCI default returns an error code". So we can leave that stub code in place until after we've changed the PSCI default behaviour; at that point we will remove it. Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Tested-by: Edgar E. Iglesias <edgar.iglesias@xilinx.com> Tested-by: Cédric Le Goater <clg@kaod.org> Message-id: 20220127154639.2090164-4-peter.maydell@linaro.org
2022-01-27 18:46:26 +03:00
/*
* arm_load_dtb() may add a PSCI node so it must be called after we have
* decided whether to enable PSCI and set the psci-conduit CPU properties.
*/
arm/boot: split load_dtb() from arm_load_kernel() load_dtb() depends on arm_load_kernel() to figure out place in RAM where it should be loaded, but it's not required for arm_load_kernel() to work. Sometimes it's neccesary for devices added with -device/device_add to be enumerated in DTB as well, which's lead to [1] and surrounding commits to add 2 more machine_done notifiers with non obvious ordering to make dynamic sysbus devices initialization happen in the right order. However instead of moving whole arm_load_kernel() in to machine_done, it's sufficient to move only load_dtb() into virt_machine_done() notifier and remove ArmLoadKernelNotifier/ /PlatformBusFDTNotifierParams notifiers, which saves us ~90LOC and simplifies code flow quite a bit. Later would allow to consolidate DTB generation within one function for 'mach-virt' board and make it reentrant so it could generate updated DTB in device hotplug secenarios. While at it rename load_dtb() to arm_load_dtb() since it's public now. Add additional field skip_dtb_autoload to struct arm_boot_info to allow manual DTB load later in mach-virt and to avoid touching all other boards to explicitly call arm_load_dtb(). 1) (ac9d32e hw/arm/boot: arm_load_kernel implemented as a machine init done notifier) Signed-off-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Andrew Jones <drjones@redhat.com> Message-id: 1525691524-32265-4-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2018-05-10 20:10:56 +03:00
if (!info->skip_dtb_autoload && have_dtb(info)) {
if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as, ms) < 0) {
arm/boot: split load_dtb() from arm_load_kernel() load_dtb() depends on arm_load_kernel() to figure out place in RAM where it should be loaded, but it's not required for arm_load_kernel() to work. Sometimes it's neccesary for devices added with -device/device_add to be enumerated in DTB as well, which's lead to [1] and surrounding commits to add 2 more machine_done notifiers with non obvious ordering to make dynamic sysbus devices initialization happen in the right order. However instead of moving whole arm_load_kernel() in to machine_done, it's sufficient to move only load_dtb() into virt_machine_done() notifier and remove ArmLoadKernelNotifier/ /PlatformBusFDTNotifierParams notifiers, which saves us ~90LOC and simplifies code flow quite a bit. Later would allow to consolidate DTB generation within one function for 'mach-virt' board and make it reentrant so it could generate updated DTB in device hotplug secenarios. While at it rename load_dtb() to arm_load_dtb() since it's public now. Add additional field skip_dtb_autoload to struct arm_boot_info to allow manual DTB load later in mach-virt and to avoid touching all other boards to explicitly call arm_load_dtb(). 1) (ac9d32e hw/arm/boot: arm_load_kernel implemented as a machine init done notifier) Signed-off-by: Igor Mammedov <imammedo@redhat.com> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> Reviewed-by: Andrew Jones <drjones@redhat.com> Message-id: 1525691524-32265-4-git-send-email-imammedo@redhat.com Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
2018-05-10 20:10:56 +03:00
exit(1);
}
}
}
static const TypeInfo arm_linux_boot_if_info = {
.name = TYPE_ARM_LINUX_BOOT_IF,
.parent = TYPE_INTERFACE,
.class_size = sizeof(ARMLinuxBootIfClass),
};
static void arm_linux_boot_register_types(void)
{
type_register_static(&arm_linux_boot_if_info);
}
type_init(arm_linux_boot_register_types)