2007-09-17 01:08:06 +04:00
|
|
|
/*
|
2006-04-28 03:15:07 +04:00
|
|
|
* ARM kernel loader.
|
|
|
|
*
|
2007-11-11 03:04:49 +03:00
|
|
|
* Copyright (c) 2006-2007 CodeSourcery.
|
2006-04-28 03:15:07 +04:00
|
|
|
* Written by Paul Brook
|
|
|
|
*
|
2011-06-26 06:21:35 +04:00
|
|
|
* This code is licensed under the GPL.
|
2006-04-28 03:15:07 +04:00
|
|
|
*/
|
|
|
|
|
2015-12-07 19:23:45 +03:00
|
|
|
#include "qemu/osdep.h"
|
2020-10-28 14:36:57 +03:00
|
|
|
#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"
|
include/qemu/osdep.h: Don't include qapi/error.h
Commit 57cb38b included qapi/error.h into qemu/osdep.h to get the
Error typedef. Since then, we've moved to include qemu/osdep.h
everywhere. Its file comment explains: "To avoid getting into
possible circular include dependencies, this file should not include
any other QEMU headers, with the exceptions of config-host.h,
compiler.h, os-posix.h and os-win32.h, all of which are doing a
similar job to this file and are under similar constraints."
qapi/error.h doesn't do a similar job, and it doesn't adhere to
similar constraints: it includes qapi-types.h. That's in excess of
100KiB of crap most .c files don't actually need.
Add the typedef to qemu/typedefs.h, and include that instead of
qapi/error.h. Include qapi/error.h in .c files that need it and don't
get it now. Include qapi-types.h in qom/object.h for uint16List.
Update scripts/clean-includes accordingly. Update it further to match
reality: replace config.h by config-target.h, add sysemu/os-posix.h,
sysemu/os-win32.h. Update the list of includes in the qemu/osdep.h
comment quoted above similarly.
This reduces the number of objects depending on qapi/error.h from "all
of them" to less than a third. Unfortunately, the number depending on
qapi-types.h shrinks only a little. More work is needed for that one.
Signed-off-by: Markus Armbruster <armbru@redhat.com>
[Fix compilation without the spice devel packages. - Paolo]
Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
2016-03-14 11:01:28 +03:00
|
|
|
#include "qapi/error.h"
|
2016-11-17 04:30:21 +03:00
|
|
|
#include <libfdt.h>
|
2019-05-23 16:47:43 +03:00
|
|
|
#include "hw/arm/boot.h"
|
2015-09-08 19:38:43 +03:00
|
|
|
#include "hw/arm/linux-boot-if.h"
|
2015-11-10 16:37:33 +03:00
|
|
|
#include "sysemu/kvm.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/sysemu.h"
|
2016-05-12 15:22:27 +03:00
|
|
|
#include "sysemu/numa.h"
|
2019-08-09 09:57:21 +03:00
|
|
|
#include "hw/boards.h"
|
2019-08-12 08:23:38 +03:00
|
|
|
#include "sysemu/reset.h"
|
2013-02-04 18:40:22 +04:00
|
|
|
#include "hw/loader.h"
|
2009-09-20 18:58:02 +04:00
|
|
|
#include "elf.h"
|
2012-12-17 21:20:04 +04:00
|
|
|
#include "sysemu/device_tree.h"
|
2012-12-17 21:20:00 +04:00
|
|
|
#include "qemu/config-file.h"
|
2018-02-01 14:18:46 +03:00
|
|
|
#include "qemu/option.h"
|
2018-10-24 09:50:18 +03:00
|
|
|
#include "qemu/units.h"
|
2006-04-28 03:15:07 +04:00
|
|
|
|
2013-12-17 23:42:30 +04:00
|
|
|
/* 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.
|
|
|
|
*/
|
2019-01-07 11:31:50 +03:00
|
|
|
#define KERNEL_ARGS_ADDR 0x100
|
|
|
|
#define KERNEL_NOLOAD_ADDR 0x02000000
|
|
|
|
#define KERNEL_LOAD_ADDR 0x00010000
|
2013-12-17 23:42:30 +04:00
|
|
|
#define KERNEL64_LOAD_ADDR 0x00080000
|
2006-04-28 03:15:07 +04:00
|
|
|
|
2017-04-20 19:32:28 +03:00
|
|
|
#define ARM64_TEXT_OFFSET_OFFSET 8
|
|
|
|
#define ARM64_MAGIC_OFFSET 56
|
|
|
|
|
2018-10-24 09:50:18 +03:00
|
|
|
#define BOOTLOADER_MAX_SIZE (4 * KiB)
|
|
|
|
|
2018-05-10 20:10:56 +03:00
|
|
|
AddressSpace *arm_boot_address_space(ARMCPU *cpu,
|
|
|
|
const struct arm_boot_info *info)
|
2018-03-02 13:45:36 +03:00
|
|
|
{
|
|
|
|
/* 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);
|
|
|
|
}
|
|
|
|
|
2013-12-17 23:42:30 +04:00
|
|
|
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 */
|
2018-12-13 16:47:56 +03:00
|
|
|
{ 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 */
|
2013-12-17 23:42:30 +04:00
|
|
|
{ 0, FIXUP_TERMINATOR }
|
|
|
|
};
|
|
|
|
|
2015-11-03 16:49:41 +03:00
|
|
|
/* 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.
|
|
|
|
*/
|
|
|
|
|
2013-12-17 23:42:30 +04:00
|
|
|
static const ARMInsnFixup bootloader[] = {
|
2016-04-21 00:35:28 +03:00
|
|
|
{ 0xe28fe004 }, /* add lr, pc, #4 */
|
2015-11-03 16:49:41 +03:00
|
|
|
{ 0xe51ff004 }, /* ldr pc, [pc, #-4] */
|
|
|
|
{ 0, FIXUP_BOARD_SETUP },
|
|
|
|
#define BOOTLOADER_NO_BOARD_SETUP_OFFSET 3
|
2013-12-17 23:42:30 +04:00
|
|
|
{ 0xe3a00000 }, /* mov r0, #0 */
|
|
|
|
{ 0xe59f1004 }, /* ldr r1, [pc, #4] */
|
|
|
|
{ 0xe59f2004 }, /* ldr r2, [pc, #4] */
|
|
|
|
{ 0xe59ff004 }, /* ldr pc, [pc, #4] */
|
|
|
|
{ 0, FIXUP_BOARDID },
|
2018-12-13 16:47:56 +03:00
|
|
|
{ 0, FIXUP_ARGPTR_LO },
|
|
|
|
{ 0, FIXUP_ENTRYPOINT_LO },
|
2013-12-17 23:42:30 +04:00
|
|
|
{ 0, FIXUP_TERMINATOR }
|
2006-04-28 03:15:07 +04:00
|
|
|
};
|
|
|
|
|
2012-01-26 15:43:48 +04:00
|
|
|
/* 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.
|
|
|
|
*/
|
2012-12-11 15:30:37 +04:00
|
|
|
#define DSB_INSN 0xf57ff04f
|
|
|
|
#define CP15_DSB_INSN 0xee070f9a /* mcr cp15, 0, r0, c7, c10, 4 */
|
|
|
|
|
2013-12-17 23:42:30 +04:00
|
|
|
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 }
|
2007-11-11 03:04:49 +03:00
|
|
|
};
|
|
|
|
|
2023-04-24 18:27:15 +03:00
|
|
|
void arm_write_bootloader(const char *name,
|
|
|
|
AddressSpace *as, hwaddr addr,
|
|
|
|
const ARMInsnFixup *insns,
|
|
|
|
const uint32_t *fixupcontext)
|
2013-12-17 23:42:30 +04:00
|
|
|
{
|
|
|
|
/* 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:
|
2015-11-03 16:49:41 +03:00
|
|
|
case FIXUP_BOARD_SETUP:
|
2018-12-13 16:47:56 +03:00
|
|
|
case FIXUP_ARGPTR_LO:
|
|
|
|
case FIXUP_ARGPTR_HI:
|
|
|
|
case FIXUP_ENTRYPOINT_LO:
|
|
|
|
case FIXUP_ENTRYPOINT_HI:
|
2013-12-17 23:42:30 +04:00
|
|
|
case FIXUP_GIC_CPU_IF:
|
|
|
|
case FIXUP_BOOTREG:
|
|
|
|
case FIXUP_DSB:
|
|
|
|
insn = fixupcontext[fixup];
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
code[i] = tswap32(insn);
|
|
|
|
}
|
|
|
|
|
2018-10-24 09:50:18 +03:00
|
|
|
assert((len * sizeof(uint32_t)) < BOOTLOADER_MAX_SIZE);
|
|
|
|
|
2018-03-02 13:45:36 +03:00
|
|
|
rom_add_blob_fixed_as(name, code, len * sizeof(uint32_t), addr, as);
|
2013-12-17 23:42:30 +04:00
|
|
|
|
|
|
|
g_free(code);
|
|
|
|
}
|
|
|
|
|
2012-05-14 02:08:10 +04:00
|
|
|
static void default_write_secondary(ARMCPU *cpu,
|
2012-01-26 15:43:48 +04:00
|
|
|
const struct arm_boot_info *info)
|
|
|
|
{
|
2013-12-17 23:42:30 +04:00
|
|
|
uint32_t fixupcontext[FIXUP_MAX];
|
2018-03-02 13:45:36 +03:00
|
|
|
AddressSpace *as = arm_boot_address_space(cpu, info);
|
2013-12-17 23:42:30 +04:00
|
|
|
|
|
|
|
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;
|
2012-01-26 15:43:48 +04:00
|
|
|
}
|
2013-12-17 23:42:30 +04:00
|
|
|
|
2023-04-24 18:27:15 +03:00
|
|
|
arm_write_bootloader("smpboot", as, info->smp_loader_start,
|
|
|
|
smpboot, fixupcontext);
|
2012-01-26 15:43:48 +04:00
|
|
|
}
|
|
|
|
|
2016-01-30 01:50:43 +03:00
|
|
|
void arm_write_secure_board_setup_dummy_smc(ARMCPU *cpu,
|
|
|
|
const struct arm_boot_info *info,
|
|
|
|
hwaddr mvbar_addr)
|
|
|
|
{
|
2018-03-02 13:45:36 +03:00
|
|
|
AddressSpace *as = arm_boot_address_space(cpu, info);
|
2016-01-30 01:50:43 +03:00
|
|
|
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 */
|
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 */
|
2016-01-30 01:50:43 +03:00
|
|
|
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]);
|
|
|
|
}
|
2018-03-02 13:45:36 +03:00
|
|
|
rom_add_blob_fixed_as("board-setup-mvbar", mvbar_blob, sizeof(mvbar_blob),
|
|
|
|
mvbar_addr, as);
|
2016-01-30 01:50:43 +03:00
|
|
|
|
|
|
|
for (n = 0; n < ARRAY_SIZE(board_setup_blob); n++) {
|
|
|
|
board_setup_blob[n] = tswap32(board_setup_blob[n]);
|
|
|
|
}
|
2018-03-02 13:45:36 +03:00
|
|
|
rom_add_blob_fixed_as("board-setup", board_setup_blob,
|
|
|
|
sizeof(board_setup_blob), info->board_setup_addr, as);
|
2016-01-30 01:50:43 +03:00
|
|
|
}
|
|
|
|
|
2012-05-14 03:05:40 +04:00
|
|
|
static void default_reset_secondary(ARMCPU *cpu,
|
2012-01-26 15:43:48 +04:00
|
|
|
const struct arm_boot_info *info)
|
|
|
|
{
|
2018-03-02 13:45:36 +03:00
|
|
|
AddressSpace *as = arm_boot_address_space(cpu, info);
|
2015-06-24 06:19:22 +03:00
|
|
|
CPUState *cs = CPU(cpu);
|
2012-05-14 03:05:40 +04:00
|
|
|
|
2018-03-02 13:45:36 +03:00
|
|
|
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);
|
2015-06-24 06:19:22 +03:00
|
|
|
cpu_set_pc(cs, info->smp_loader_start);
|
2012-01-26 15:43:48 +04:00
|
|
|
}
|
|
|
|
|
2014-01-31 18:47:32 +04:00
|
|
|
static inline bool have_dtb(const struct arm_boot_info *info)
|
|
|
|
{
|
|
|
|
return info->dtb_filename || info->get_dtb;
|
|
|
|
}
|
|
|
|
|
2009-04-09 21:19:47 +04:00
|
|
|
#define WRITE_WORD(p, value) do { \
|
2018-03-02 13:45:36 +03:00
|
|
|
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); \
|
2009-04-09 21:19:47 +04:00
|
|
|
p += 4; \
|
|
|
|
} while (0)
|
|
|
|
|
2018-03-02 13:45:36 +03:00
|
|
|
static void set_kernel_args(const struct arm_boot_info *info, AddressSpace *as)
|
2006-04-28 03:15:07 +04:00
|
|
|
{
|
2012-01-29 11:52:15 +04:00
|
|
|
int initrd_size = info->initrd_size;
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr base = info->loader_start;
|
|
|
|
hwaddr p;
|
2006-04-28 03:15:07 +04:00
|
|
|
|
2009-04-09 21:19:47 +04:00
|
|
|
p = base + KERNEL_ARGS_ADDR;
|
2006-04-28 03:15:07 +04:00
|
|
|
/* ATAG_CORE */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 5);
|
|
|
|
WRITE_WORD(p, 0x54410001);
|
|
|
|
WRITE_WORD(p, 1);
|
|
|
|
WRITE_WORD(p, 0x1000);
|
|
|
|
WRITE_WORD(p, 0);
|
2006-04-28 03:15:07 +04:00
|
|
|
/* ATAG_MEM */
|
2008-04-15 00:27:51 +04:00
|
|
|
/* TODO: handle multiple chips on one ATAG list */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 4);
|
|
|
|
WRITE_WORD(p, 0x54410002);
|
|
|
|
WRITE_WORD(p, info->ram_size);
|
|
|
|
WRITE_WORD(p, info->loader_start);
|
2006-04-28 03:15:07 +04:00
|
|
|
if (initrd_size) {
|
|
|
|
/* ATAG_INITRD2 */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 4);
|
|
|
|
WRITE_WORD(p, 0x54420005);
|
2012-10-26 19:29:38 +04:00
|
|
|
WRITE_WORD(p, info->initrd_start);
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, initrd_size);
|
2006-04-28 03:15:07 +04:00
|
|
|
}
|
2008-04-15 00:27:51 +04:00
|
|
|
if (info->kernel_cmdline && *info->kernel_cmdline) {
|
2006-04-28 03:15:07 +04:00
|
|
|
/* ATAG_CMDLINE */
|
|
|
|
int cmdline_size;
|
|
|
|
|
2008-04-15 00:27:51 +04:00
|
|
|
cmdline_size = strlen(info->kernel_cmdline);
|
2018-03-02 13:45:36 +03:00
|
|
|
address_space_write(as, p + 8, MEMTXATTRS_UNSPECIFIED,
|
2020-02-19 22:28:22 +03:00
|
|
|
info->kernel_cmdline, cmdline_size + 1);
|
2006-04-28 03:15:07 +04:00
|
|
|
cmdline_size = (cmdline_size >> 2) + 1;
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, cmdline_size + 2);
|
|
|
|
WRITE_WORD(p, 0x54410009);
|
|
|
|
p += cmdline_size * 4;
|
2006-04-28 03:15:07 +04:00
|
|
|
}
|
2008-04-15 00:27:51 +04:00
|
|
|
if (info->atag_board) {
|
|
|
|
/* ATAG_BOARD */
|
|
|
|
int atag_board_len;
|
2009-04-09 21:19:47 +04:00
|
|
|
uint8_t atag_board_buf[0x1000];
|
2008-04-15 00:27:51 +04:00
|
|
|
|
2009-04-09 21:19:47 +04:00
|
|
|
atag_board_len = (info->atag_board(info, atag_board_buf) + 3) & ~3;
|
|
|
|
WRITE_WORD(p, (atag_board_len + 8) >> 2);
|
|
|
|
WRITE_WORD(p, 0x414f4d50);
|
2018-03-02 13:45:36 +03:00
|
|
|
address_space_write(as, p, MEMTXATTRS_UNSPECIFIED,
|
|
|
|
atag_board_buf, atag_board_len);
|
2008-04-15 00:27:51 +04:00
|
|
|
p += atag_board_len;
|
|
|
|
}
|
2006-04-28 03:15:07 +04:00
|
|
|
/* ATAG_END */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
|
|
|
WRITE_WORD(p, 0);
|
2006-04-28 03:15:07 +04:00
|
|
|
}
|
|
|
|
|
2018-03-02 13:45:36 +03:00
|
|
|
static void set_kernel_args_old(const struct arm_boot_info *info,
|
|
|
|
AddressSpace *as)
|
2007-07-28 02:08:46 +04:00
|
|
|
{
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr p;
|
2009-04-09 21:19:47 +04:00
|
|
|
const char *s;
|
2012-01-29 11:52:15 +04:00
|
|
|
int initrd_size = info->initrd_size;
|
2012-10-23 14:30:10 +04:00
|
|
|
hwaddr base = info->loader_start;
|
2007-07-28 02:08:46 +04:00
|
|
|
|
|
|
|
/* see linux/include/asm-arm/setup.h */
|
2009-04-09 21:19:47 +04:00
|
|
|
p = base + KERNEL_ARGS_ADDR;
|
2007-07-28 02:08:46 +04:00
|
|
|
/* page_size */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 4096);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* nr_pages */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, info->ram_size / 4096);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* ramdisk_size */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
#define FLAG_READONLY 1
|
|
|
|
#define FLAG_RDLOAD 4
|
|
|
|
#define FLAG_RDPROMPT 8
|
|
|
|
/* flags */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, FLAG_READONLY | FLAG_RDLOAD | FLAG_RDPROMPT);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* rootdev */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, (31 << 8) | 0); /* /dev/mtdblock0 */
|
2007-07-28 02:08:46 +04:00
|
|
|
/* video_num_cols */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* video_num_rows */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* video_x */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* video_y */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* memc_control_reg */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* unsigned char sounddefault */
|
|
|
|
/* unsigned char adfsdrives */
|
|
|
|
/* unsigned char bytes_per_char_h */
|
|
|
|
/* unsigned char bytes_per_char_v */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* pages_in_bank[4] */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
|
|
|
WRITE_WORD(p, 0);
|
|
|
|
WRITE_WORD(p, 0);
|
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* pages_in_vram */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* initrd_start */
|
2012-10-26 19:29:38 +04:00
|
|
|
if (initrd_size) {
|
|
|
|
WRITE_WORD(p, info->initrd_start);
|
|
|
|
} else {
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2012-10-26 19:29:38 +04:00
|
|
|
}
|
2007-07-28 02:08:46 +04:00
|
|
|
/* initrd_size */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, initrd_size);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* rd_start */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* system_rev */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* system_serial_low */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* system_serial_high */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* mem_fclk_21285 */
|
2009-04-09 21:19:47 +04:00
|
|
|
WRITE_WORD(p, 0);
|
2007-07-28 02:08:46 +04:00
|
|
|
/* zero unused fields */
|
2009-04-09 21:19:47 +04:00
|
|
|
while (p < base + KERNEL_ARGS_ADDR + 256 + 1024) {
|
|
|
|
WRITE_WORD(p, 0);
|
|
|
|
}
|
|
|
|
s = info->kernel_cmdline;
|
|
|
|
if (s) {
|
2020-02-19 22:28:22 +03:00
|
|
|
address_space_write(as, p, MEMTXATTRS_UNSPECIFIED, s, strlen(s) + 1);
|
2009-04-09 21:19:47 +04:00
|
|
|
} else {
|
|
|
|
WRITE_WORD(p, 0);
|
|
|
|
}
|
2007-07-28 02:08:46 +04:00
|
|
|
}
|
|
|
|
|
2019-03-04 13:13:30 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-02-09 13:40:30 +03:00
|
|
|
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;
|
2018-04-10 15:02:24 +03:00
|
|
|
int rc;
|
2018-02-09 13:40:30 +03:00
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2018-04-10 15:02:24 +03:00
|
|
|
/*
|
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.
|
2018-04-10 15:02:24 +03:00
|
|
|
*/
|
|
|
|
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");
|
2018-04-10 15:02:24 +03:00
|
|
|
}
|
|
|
|
|
2018-02-09 13:40:30 +03:00
|
|
|
qemu_fdt_add_subnode(fdt, "/psci");
|
2022-02-24 16:46:54 +03:00
|
|
|
if (armcpu->psci_version >= QEMU_PSCI_VERSION_0_2) {
|
|
|
|
if (armcpu->psci_version < QEMU_PSCI_VERSION_1_0) {
|
2022-02-13 06:57:53 +03:00
|
|
|
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));
|
|
|
|
}
|
2018-02-09 13:40:30 +03:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2018-05-10 20:10:56 +03:00
|
|
|
int arm_load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
|
2019-08-09 09:57:21 +03:00
|
|
|
hwaddr addr_limit, AddressSpace *as, MachineState *ms)
|
2012-03-02 15:56:38 +04:00
|
|
|
{
|
|
|
|
void *fdt = NULL;
|
2018-06-29 17:11:01 +03:00
|
|
|
int size, rc, n = 0;
|
2013-07-16 16:25:06 +04:00
|
|
|
uint32_t acells, scells;
|
2016-05-12 15:22:27 +03:00
|
|
|
unsigned int i;
|
|
|
|
hwaddr mem_base, mem_len;
|
2018-06-29 17:11:01 +03:00
|
|
|
char **node_path;
|
|
|
|
Error *err = NULL;
|
2012-03-02 15:56:38 +04:00
|
|
|
|
2013-11-22 21:17:10 +04:00
|
|
|
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;
|
|
|
|
}
|
2012-03-02 15:56:38 +04:00
|
|
|
|
2013-11-22 21:17:10 +04:00
|
|
|
fdt = load_device_tree(filename, &size);
|
|
|
|
if (!fdt) {
|
|
|
|
fprintf(stderr, "Couldn't open dtb file %s\n", filename);
|
|
|
|
g_free(filename);
|
|
|
|
goto fail;
|
|
|
|
}
|
2012-03-02 15:56:38 +04:00
|
|
|
g_free(filename);
|
2014-12-11 15:07:53 +03:00
|
|
|
} else {
|
2013-11-22 21:17:10 +04:00
|
|
|
fdt = binfo->get_dtb(binfo, &size);
|
|
|
|
if (!fdt) {
|
|
|
|
fprintf(stderr, "Board was unable to create a dtb blob\n");
|
|
|
|
goto fail;
|
|
|
|
}
|
2012-03-02 15:56:38 +04:00
|
|
|
}
|
|
|
|
|
2014-09-12 17:06:50 +04:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-02-19 19:42:30 +03:00
|
|
|
acells = qemu_fdt_getprop_cell(fdt, "/", "#address-cells",
|
|
|
|
NULL, &error_fatal);
|
|
|
|
scells = qemu_fdt_getprop_cell(fdt, "/", "#size-cells",
|
|
|
|
NULL, &error_fatal);
|
2012-07-20 16:34:50 +04:00
|
|
|
if (acells == 0 || scells == 0) {
|
|
|
|
fprintf(stderr, "dtb file invalid (#address-cells or #size-cells 0)\n");
|
2013-06-25 21:34:13 +04:00
|
|
|
goto fail;
|
2012-07-20 16:34:50 +04:00
|
|
|
}
|
|
|
|
|
2019-09-23 16:11:08 +03:00
|
|
|
if (scells < 2 && binfo->ram_size >= 4 * GiB) {
|
2013-07-16 16:25:06 +04:00
|
|
|
/* This is user error so deserves a friendlier error message
|
|
|
|
* than the failure of setprop_sized_cells would provide
|
|
|
|
*/
|
2012-07-20 16:34:50 +04:00
|
|
|
fprintf(stderr, "qemu: dtb file not compatible with "
|
|
|
|
"RAM size > 4GB\n");
|
2013-06-25 21:34:13 +04:00
|
|
|
goto fail;
|
2012-07-20 16:34:50 +04:00
|
|
|
}
|
|
|
|
|
2018-06-29 17:11:01 +03:00
|
|
|
/* 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.
|
|
|
|
*/
|
2019-08-09 09:57:22 +03:00
|
|
|
if (ms->numa_state != NULL && ms->numa_state->num_nodes > 0) {
|
2016-05-12 15:22:27 +03:00
|
|
|
mem_base = binfo->loader_start;
|
2019-08-09 09:57:22 +03:00
|
|
|
for (i = 0; i < ms->numa_state->num_nodes; i++) {
|
2019-08-09 09:57:24 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2019-03-04 13:13:30 +03:00
|
|
|
rc = fdt_add_memory_node(fdt, acells, mem_base,
|
|
|
|
scells, mem_len, i);
|
2016-05-12 15:22:27 +03:00
|
|
|
if (rc < 0) {
|
2019-03-04 13:13:30 +03:00
|
|
|
fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n",
|
|
|
|
mem_base);
|
2016-05-12 15:22:27 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
mem_base += mem_len;
|
|
|
|
}
|
|
|
|
} else {
|
2019-03-04 13:13:30 +03:00
|
|
|
rc = fdt_add_memory_node(fdt, acells, binfo->loader_start,
|
|
|
|
scells, binfo->ram_size, -1);
|
2016-05-12 15:22:27 +03:00
|
|
|
if (rc < 0) {
|
2019-03-04 13:13:30 +03:00
|
|
|
fprintf(stderr, "couldn't add /memory@%"PRIx64" node\n",
|
|
|
|
binfo->loader_start);
|
2016-05-12 15:22:27 +03:00
|
|
|
goto fail;
|
|
|
|
}
|
2012-03-02 15:56:38 +04:00
|
|
|
}
|
|
|
|
|
2016-11-17 04:30:21 +03:00
|
|
|
rc = fdt_path_offset(fdt, "/chosen");
|
|
|
|
if (rc < 0) {
|
|
|
|
qemu_fdt_add_subnode(fdt, "/chosen");
|
|
|
|
}
|
|
|
|
|
2019-08-09 09:57:21 +03:00
|
|
|
if (ms->kernel_cmdline && *ms->kernel_cmdline) {
|
2013-11-11 12:14:41 +04:00
|
|
|
rc = qemu_fdt_setprop_string(fdt, "/chosen", "bootargs",
|
2019-08-09 09:57:21 +03:00
|
|
|
ms->kernel_cmdline);
|
2012-06-17 19:35:36 +04:00
|
|
|
if (rc < 0) {
|
|
|
|
fprintf(stderr, "couldn't set /chosen/bootargs\n");
|
2013-06-25 21:34:13 +04:00
|
|
|
goto fail;
|
2012-06-17 19:35:36 +04:00
|
|
|
}
|
2012-03-02 15:56:38 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (binfo->initrd_size) {
|
2013-11-11 12:14:41 +04:00
|
|
|
rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start",
|
|
|
|
binfo->initrd_start);
|
2012-03-02 15:56:38 +04:00
|
|
|
if (rc < 0) {
|
|
|
|
fprintf(stderr, "couldn't set /chosen/linux,initrd-start\n");
|
2013-06-25 21:34:13 +04:00
|
|
|
goto fail;
|
2012-03-02 15:56:38 +04:00
|
|
|
}
|
|
|
|
|
2013-11-11 12:14:41 +04:00
|
|
|
rc = qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end",
|
|
|
|
binfo->initrd_start + binfo->initrd_size);
|
2012-03-02 15:56:38 +04:00
|
|
|
if (rc < 0) {
|
|
|
|
fprintf(stderr, "couldn't set /chosen/linux,initrd-end\n");
|
2013-06-25 21:34:13 +04:00
|
|
|
goto fail;
|
2012-03-02 15:56:38 +04:00
|
|
|
}
|
|
|
|
}
|
2013-07-16 16:25:10 +04:00
|
|
|
|
2018-02-09 13:40:30 +03:00
|
|
|
fdt_add_psci_node(fdt);
|
|
|
|
|
2013-07-16 16:25:10 +04:00
|
|
|
if (binfo->modify_dtb) {
|
|
|
|
binfo->modify_dtb(binfo, fdt);
|
|
|
|
}
|
|
|
|
|
2013-11-11 12:14:41 +04:00
|
|
|
qemu_fdt_dumpdtb(fdt, size);
|
2012-03-02 15:56:38 +04:00
|
|
|
|
2014-09-12 17:06:50 +04:00
|
|
|
/* 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.
|
|
|
|
*/
|
2018-03-02 13:45:36 +03:00
|
|
|
rom_add_blob_fixed_as("dtb", fdt, size, addr, as);
|
2022-10-25 03:43:20 +03:00
|
|
|
qemu_register_reset_nosnapshotload(qemu_fdt_randomize_seeds,
|
|
|
|
rom_ptr_for_as(as, addr, size));
|
2012-03-02 15:56:38 +04:00
|
|
|
|
2023-04-03 18:12:30 +03:00
|
|
|
if (fdt != ms->fdt) {
|
|
|
|
g_free(ms->fdt);
|
|
|
|
ms->fdt = fdt;
|
|
|
|
}
|
2013-06-25 21:34:13 +04:00
|
|
|
|
2014-09-12 17:06:50 +04:00
|
|
|
return size;
|
2013-06-25 21:34:13 +04:00
|
|
|
|
|
|
|
fail:
|
|
|
|
g_free(fdt);
|
|
|
|
return -1;
|
2012-03-02 15:56:38 +04:00
|
|
|
}
|
|
|
|
|
2011-03-05 15:51:45 +03:00
|
|
|
static void do_cpu_reset(void *opaque)
|
2009-11-11 21:07:53 +03:00
|
|
|
{
|
2012-05-05 14:40:39 +04:00
|
|
|
ARMCPU *cpu = opaque;
|
2015-06-24 06:19:22 +03:00
|
|
|
CPUState *cs = CPU(cpu);
|
2012-05-05 14:40:39 +04:00
|
|
|
CPUARMState *env = &cpu->env;
|
2011-06-23 19:53:48 +04:00
|
|
|
const struct arm_boot_info *info = env->boot_info;
|
2009-11-11 21:07:53 +03:00
|
|
|
|
2015-06-24 06:19:22 +03:00
|
|
|
cpu_reset(cs);
|
2009-11-11 21:07:53 +03:00
|
|
|
if (info) {
|
|
|
|
if (!info->is_linux) {
|
2016-03-04 14:30:21 +03:00
|
|
|
int i;
|
2009-11-11 21:07:53 +03:00
|
|
|
/* Jump to the entry point. */
|
2015-06-24 06:19:22 +03:00
|
|
|
uint64_t entry = info->entry;
|
|
|
|
|
2016-03-04 14:30:21 +03:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2015-06-24 06:19:22 +03:00
|
|
|
cpu_set_pc(cs, entry);
|
2009-11-11 21:07:53 +03:00
|
|
|
} else {
|
2014-12-16 02:09:47 +03:00
|
|
|
/* 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.
|
|
|
|
*/
|
2015-02-05 16:37:22 +03:00
|
|
|
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) {
|
2016-02-03 16:46:33 +03:00
|
|
|
env->cp15.scr_el3 |= SCR_RW;
|
2015-02-05 16:37:22 +03:00
|
|
|
if (arm_feature(env, ARM_FEATURE_EL2)) {
|
2016-02-03 16:46:33 +03:00
|
|
|
env->cp15.hcr_el2 |= HCR_RW;
|
2015-02-05 16:37:22 +03:00
|
|
|
env->pstate = PSTATE_MODE_EL2h;
|
|
|
|
} else {
|
|
|
|
env->pstate = PSTATE_MODE_EL1h;
|
|
|
|
}
|
2020-07-27 18:12:10 +03:00
|
|
|
if (cpu_isar_feature(aa64_pauth, cpu)) {
|
|
|
|
env->cp15.scr_el3 |= SCR_API | SCR_APK;
|
|
|
|
}
|
2020-07-27 18:12:10 +03:00
|
|
|
if (cpu_isar_feature(aa64_mte, cpu)) {
|
|
|
|
env->cp15.scr_el3 |= SCR_ATA;
|
|
|
|
}
|
2020-11-02 19:52:16 +03:00
|
|
|
if (cpu_isar_feature(aa64_sve, cpu)) {
|
2022-05-17 08:48:45 +03:00
|
|
|
env->cp15.cptr_el[3] |= R_CPTR_EL3_EZ_MASK;
|
2022-11-03 16:10:40 +03:00
|
|
|
env->vfp.zcr_el[3] = 0xf;
|
2020-11-02 19:52:16 +03:00
|
|
|
}
|
2022-10-03 17:56:41 +03:00
|
|
|
if (cpu_isar_feature(aa64_sme, cpu)) {
|
|
|
|
env->cp15.cptr_el[3] |= R_CPTR_EL3_ESM_MASK;
|
|
|
|
env->cp15.scr_el3 |= SCR_ENTP2;
|
2022-11-03 16:10:40 +03:00
|
|
|
env->vfp.smcr_el[3] = 0xf;
|
2022-10-03 17:56:41 +03:00
|
|
|
}
|
2022-11-03 16:10:40 +03:00
|
|
|
if (cpu_isar_feature(aa64_hcx, cpu)) {
|
|
|
|
env->cp15.scr_el3 |= SCR_HXEN;
|
|
|
|
}
|
2018-03-13 18:34:51 +03:00
|
|
|
/* 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);
|
2015-02-05 16:37:22 +03:00
|
|
|
}
|
|
|
|
|
2018-03-13 18:34:52 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2015-02-05 16:37:22 +03:00
|
|
|
/* Set to non-secure if not a secure boot */
|
2015-11-10 16:37:33 +03:00
|
|
|
if (!info->secure_boot &&
|
|
|
|
(cs != first_cpu || !info->secure_board_setup)) {
|
2015-02-05 16:37:22 +03:00
|
|
|
/* Linux expects non-secure state */
|
|
|
|
env->cp15.scr_el3 |= SCR_NS;
|
2019-09-20 20:40:39 +03:00
|
|
|
/* Set NSACR.{CP11,CP10} so NS can access the FPU */
|
|
|
|
env->cp15.nsacr |= 3 << 10;
|
2015-02-05 16:37:22 +03:00
|
|
|
}
|
2014-12-16 02:09:47 +03:00
|
|
|
}
|
|
|
|
|
2018-08-24 15:17:39 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2015-06-24 06:19:22 +03:00
|
|
|
if (cs == first_cpu) {
|
2018-03-02 13:45:36 +03:00
|
|
|
AddressSpace *as = arm_boot_address_space(cpu, info);
|
|
|
|
|
2015-06-24 06:19:22 +03:00
|
|
|
cpu_set_pc(cs, info->loader_start);
|
2013-12-17 23:42:30 +04:00
|
|
|
|
2014-01-31 18:47:32 +04:00
|
|
|
if (!have_dtb(info)) {
|
2012-03-02 15:56:38 +04:00
|
|
|
if (old_param) {
|
2018-03-02 13:45:36 +03:00
|
|
|
set_kernel_args_old(info, as);
|
2012-03-02 15:56:38 +04:00
|
|
|
} else {
|
2018-03-02 13:45:36 +03:00
|
|
|
set_kernel_args(info, as);
|
2012-03-02 15:56:38 +04:00
|
|
|
}
|
2011-03-05 15:51:45 +03:00
|
|
|
}
|
2022-01-27 18:46:36 +03:00
|
|
|
} else if (info->secondary_cpu_reset_hook) {
|
2012-05-14 03:05:40 +04:00
|
|
|
info->secondary_cpu_reset_hook(cpu, info);
|
2009-11-11 21:07:53 +03:00
|
|
|
}
|
|
|
|
}
|
2019-10-31 07:08:30 +03:00
|
|
|
arm_rebuild_hflags(env);
|
2009-11-11 21:07:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-09-08 19:38:43 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2021-11-11 17:11:40 +03:00
|
|
|
static ssize_t arm_load_elf(struct arm_boot_info *info, uint64_t *pentry,
|
2018-08-14 19:17:21 +03:00
|
|
|
uint64_t *lowaddr, uint64_t *highaddr,
|
|
|
|
int elf_machine, AddressSpace *as)
|
2016-03-04 14:30:21 +03:00
|
|
|
{
|
|
|
|
bool elf_is64;
|
|
|
|
union {
|
|
|
|
Elf32_Ehdr h32;
|
|
|
|
Elf64_Ehdr h64;
|
|
|
|
} elf_header;
|
|
|
|
int data_swab = 0;
|
|
|
|
bool big_endian;
|
2021-11-11 17:11:40 +03:00
|
|
|
ssize_t ret = -1;
|
2016-03-04 14:30:21 +03:00
|
|
|
Error *err = NULL;
|
|
|
|
|
|
|
|
|
|
|
|
load_elf_hdr(info->kernel_filename, &elf_header, &elf_is64, &err);
|
|
|
|
if (err) {
|
2018-03-09 20:09:44 +03:00
|
|
|
error_free(err);
|
2016-03-04 14:30:21 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-01-15 15:18:03 +03:00
|
|
|
ret = load_elf_as(info->kernel_filename, NULL, NULL, NULL,
|
2020-01-27 01:55:04 +03:00
|
|
|
pentry, lowaddr, highaddr, NULL, big_endian, elf_machine,
|
2018-03-02 13:45:36 +03:00
|
|
|
1, data_swab, as);
|
2016-03-04 14:30:21 +03:00
|
|
|
if (ret <= 0) {
|
|
|
|
/* The header loaded but the image didn't */
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2017-04-20 19:32:28 +03:00
|
|
|
static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
|
2018-03-02 13:45:36 +03:00
|
|
|
hwaddr *entry, AddressSpace *as)
|
2017-04-20 19:32:28 +03:00
|
|
|
{
|
|
|
|
hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR;
|
2019-05-16 17:47:33 +03:00
|
|
|
uint64_t kernel_size = 0;
|
2017-04-20 19:32:28 +03:00
|
|
|
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 */
|
2018-03-09 20:09:44 +03:00
|
|
|
if (size > ARM64_MAGIC_OFFSET + 4 &&
|
|
|
|
memcmp(buffer + ARM64_MAGIC_OFFSET, "ARM\x64", 4) == 0) {
|
2017-04-20 19:32:28 +03:00
|
|
|
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));
|
2019-05-16 17:47:33 +03:00
|
|
|
|
|
|
|
kernel_size = le64_to_cpu(hdrvals[1]);
|
|
|
|
|
|
|
|
if (kernel_size != 0) {
|
2017-04-20 19:32:28 +03:00
|
|
|
kernel_load_offset = le64_to_cpu(hdrvals[0]);
|
2018-10-24 09:50:18 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
2017-04-20 19:32:28 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-16 17:47:33 +03:00
|
|
|
/*
|
|
|
|
* 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;
|
|
|
|
}
|
|
|
|
|
2017-04-20 19:32:28 +03:00
|
|
|
*entry = mem_base + kernel_load_offset;
|
2018-03-02 13:45:36 +03:00
|
|
|
rom_add_blob_fixed_as(filename, buffer, size, *entry, as);
|
2017-04-20 19:32:28 +03:00
|
|
|
|
|
|
|
g_free(buffer);
|
|
|
|
|
2019-05-16 17:47:33 +03:00
|
|
|
return kernel_size;
|
2017-04-20 19:32:28 +03:00
|
|
|
}
|
|
|
|
|
2019-02-05 19:52:41 +03:00
|
|
|
static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
|
|
|
|
struct arm_boot_info *info)
|
2006-04-28 03:15:07 +04:00
|
|
|
{
|
2019-02-05 19:52:41 +03:00
|
|
|
/* Set up for a direct boot of a kernel image file. */
|
2014-10-24 15:19:11 +04:00
|
|
|
CPUState *cs;
|
2019-02-05 19:52:41 +03:00
|
|
|
AddressSpace *as = arm_boot_address_space(cpu, info);
|
2021-11-11 17:11:40 +03:00
|
|
|
ssize_t kernel_size;
|
2006-04-28 03:15:07 +04:00
|
|
|
int initrd_size;
|
2007-03-07 02:52:01 +03:00
|
|
|
int is_linux = 0;
|
2019-07-22 18:18:03 +03:00
|
|
|
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;
|
2014-03-21 22:44:36 +04:00
|
|
|
int elf_machine;
|
2017-04-20 19:32:28 +03:00
|
|
|
hwaddr entry;
|
2013-12-17 23:42:30 +04:00
|
|
|
static const ARMInsnFixup *primary_loader;
|
2019-05-16 17:47:30 +03:00
|
|
|
uint64_t ram_end = info->loader_start + info->ram_size;
|
2007-01-16 21:54:31 +03:00
|
|
|
|
2013-12-17 23:42:30 +04:00
|
|
|
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
|
|
|
|
primary_loader = bootloader_aarch64;
|
2014-03-21 22:44:36 +04:00
|
|
|
elf_machine = EM_AARCH64;
|
2013-12-17 23:42:30 +04:00
|
|
|
} else {
|
|
|
|
primary_loader = bootloader;
|
2015-11-03 16:49:41 +03:00
|
|
|
if (!info->write_board_setup) {
|
|
|
|
primary_loader += BOOTLOADER_NO_BOARD_SETUP_OFFSET;
|
|
|
|
}
|
2014-03-21 22:44:36 +04:00
|
|
|
elf_machine = EM_ARM;
|
2013-12-17 23:42:30 +04:00
|
|
|
}
|
|
|
|
|
2007-03-07 02:52:01 +03:00
|
|
|
/* Assume that raw images are linux kernels, and ELF images are not. */
|
2019-07-22 18:18:03 +03:00
|
|
|
kernel_size = arm_load_elf(info, &elf_entry, &image_low_addr,
|
|
|
|
&image_high_addr, elf_machine, as);
|
2014-09-12 17:06:50 +04:00
|
|
|
if (kernel_size > 0 && have_dtb(info)) {
|
2019-02-05 19:52:41 +03:00
|
|
|
/*
|
|
|
|
* If there is still some room left at the base of RAM, try and put
|
2014-09-12 17:06:50 +04:00
|
|
|
* the DTB there like we do for images loaded with -bios or -pflash.
|
|
|
|
*/
|
2019-07-22 18:18:03 +03:00
|
|
|
if (image_low_addr > info->loader_start
|
|
|
|
|| image_high_addr < info->loader_start) {
|
2019-02-05 19:52:41 +03:00
|
|
|
/*
|
2019-07-22 18:18:03 +03:00
|
|
|
* Set image_low_addr as address limit for arm_load_dtb if it may be
|
2014-09-12 17:06:50 +04:00
|
|
|
* pointing into RAM, otherwise pass '0' (no limit)
|
|
|
|
*/
|
2019-07-22 18:18:03 +03:00
|
|
|
if (image_low_addr < info->loader_start) {
|
|
|
|
image_low_addr = 0;
|
2014-09-12 17:06:50 +04:00
|
|
|
}
|
2018-05-10 20:10:56 +03:00
|
|
|
info->dtb_start = info->loader_start;
|
2019-07-22 18:18:03 +03:00
|
|
|
info->dtb_limit = image_low_addr;
|
2014-09-12 17:06:50 +04:00
|
|
|
}
|
|
|
|
}
|
2007-03-07 02:52:01 +03:00
|
|
|
entry = elf_entry;
|
|
|
|
if (kernel_size < 0) {
|
2019-01-07 11:31:50 +03:00
|
|
|
uint64_t loadaddr = info->loader_start + KERNEL_NOLOAD_ADDR;
|
|
|
|
kernel_size = load_uimage_as(info->kernel_filename, &entry, &loadaddr,
|
2018-03-02 13:45:36 +03:00
|
|
|
&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;
|
|
|
|
}
|
2007-03-07 02:52:01 +03:00
|
|
|
}
|
2014-08-19 21:56:28 +04:00
|
|
|
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {
|
2017-04-20 19:32:28 +03:00
|
|
|
kernel_size = load_aarch64_image(info->kernel_filename,
|
2018-03-02 13:45:36 +03:00
|
|
|
info->loader_start, &entry, as);
|
2014-08-19 21:56:28 +04:00
|
|
|
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;
|
|
|
|
}
|
2017-04-20 19:32:28 +03:00
|
|
|
} else if (kernel_size < 0) {
|
|
|
|
/* 32-bit ARM */
|
|
|
|
entry = info->loader_start + KERNEL_LOAD_ADDR;
|
2018-03-02 13:45:36 +03:00
|
|
|
kernel_size = load_image_targphys_as(info->kernel_filename, entry,
|
2019-05-16 17:47:30 +03:00
|
|
|
ram_end - KERNEL_LOAD_ADDR, as);
|
2007-03-07 02:52:01 +03:00
|
|
|
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;
|
|
|
|
}
|
2007-03-07 02:52:01 +03:00
|
|
|
}
|
|
|
|
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);
|
2007-03-07 02:52:01 +03:00
|
|
|
exit(1);
|
|
|
|
}
|
2019-05-16 17:47:31 +03:00
|
|
|
|
|
|
|
if (kernel_size > info->ram_size) {
|
|
|
|
error_report("kernel '%s' is too large to fit in RAM "
|
2021-11-11 17:11:40 +03:00
|
|
|
"(kernel size %zd, RAM size %" PRId64 ")",
|
2019-05-16 17:47:31 +03:00
|
|
|
info->kernel_filename, kernel_size, info->ram_size);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2009-11-11 21:07:53 +03:00
|
|
|
info->entry = entry;
|
2019-05-16 17:47:32 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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 +
|
2019-09-23 16:11:08 +03:00
|
|
|
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);
|
|
|
|
}
|
2019-05-16 17:47:32 +03:00
|
|
|
info->initrd_start = TARGET_PAGE_ALIGN(info->initrd_start);
|
|
|
|
|
2009-11-11 21:07:53 +03:00
|
|
|
if (is_linux) {
|
2013-12-17 23:42:30 +04:00
|
|
|
uint32_t fixupcontext[FIXUP_MAX];
|
|
|
|
|
2008-04-15 00:27:51 +04:00
|
|
|
if (info->initrd_filename) {
|
2019-05-16 17:47:31 +03:00
|
|
|
|
|
|
|
if (info->initrd_start >= ram_end) {
|
|
|
|
error_report("not enough space after kernel to load initrd");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2018-03-02 13:45:36 +03:00
|
|
|
initrd_size = load_ramdisk_as(info->initrd_filename,
|
|
|
|
info->initrd_start,
|
2019-05-16 17:47:30 +03:00
|
|
|
ram_end - info->initrd_start, as);
|
2013-07-09 02:40:02 +04:00
|
|
|
if (initrd_size < 0) {
|
2018-03-02 13:45:36 +03:00
|
|
|
initrd_size = load_image_targphys_as(info->initrd_filename,
|
|
|
|
info->initrd_start,
|
2019-05-16 17:47:30 +03:00
|
|
|
ram_end -
|
2018-03-02 13:45:36 +03:00
|
|
|
info->initrd_start,
|
|
|
|
as);
|
2013-07-09 02:40:02 +04:00
|
|
|
}
|
2007-01-16 21:54:31 +03:00
|
|
|
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);
|
2007-01-16 21:54:31 +03:00
|
|
|
exit(1);
|
|
|
|
}
|
2019-07-01 19:26:14 +03:00
|
|
|
if (info->initrd_start + initrd_size > ram_end) {
|
2019-05-16 17:47:31 +03:00
|
|
|
error_report("could not load initrd '%s': "
|
|
|
|
"too big to fit into RAM after the kernel",
|
|
|
|
info->initrd_filename);
|
2019-07-01 19:26:14 +03:00
|
|
|
exit(1);
|
2019-05-16 17:47:31 +03:00
|
|
|
}
|
2007-01-16 21:54:31 +03:00
|
|
|
} else {
|
|
|
|
initrd_size = 0;
|
|
|
|
}
|
2012-03-02 15:56:38 +04:00
|
|
|
info->initrd_size = initrd_size;
|
|
|
|
|
2013-12-17 23:42:30 +04:00
|
|
|
fixupcontext[FIXUP_BOARDID] = info->board_id;
|
2015-11-03 16:49:41 +03:00
|
|
|
fixupcontext[FIXUP_BOARD_SETUP] = info->board_setup_addr;
|
2012-03-02 15:56:38 +04:00
|
|
|
|
2019-02-05 19:52:41 +03:00
|
|
|
/*
|
|
|
|
* for device tree boot, we pass the DTB directly in r2. Otherwise
|
2012-03-02 15:56:38 +04:00
|
|
|
* we point to the kernel args.
|
|
|
|
*/
|
2014-01-31 18:47:32 +04:00
|
|
|
if (have_dtb(info)) {
|
2015-07-15 19:16:26 +03:00
|
|
|
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.
|
|
|
|
*/
|
2019-09-23 16:11:08 +03:00
|
|
|
align = 2 * MiB;
|
2015-07-15 19:16:26 +03:00
|
|
|
} 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.
|
|
|
|
*/
|
2019-09-23 16:11:08 +03:00
|
|
|
align = 4 * KiB;
|
2015-07-15 19:16:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Place the DTB after the initrd in memory with alignment. */
|
2018-05-10 20:10:56 +03:00
|
|
|
info->dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size,
|
|
|
|
align);
|
2019-05-16 17:47:31 +03:00
|
|
|
if (info->dtb_start >= ram_end) {
|
|
|
|
error_report("Not enough space for DTB after kernel/initrd");
|
|
|
|
exit(1);
|
|
|
|
}
|
2018-12-13 16:47:56 +03:00
|
|
|
fixupcontext[FIXUP_ARGPTR_LO] = info->dtb_start;
|
|
|
|
fixupcontext[FIXUP_ARGPTR_HI] = info->dtb_start >> 32;
|
2012-03-02 15:56:38 +04:00
|
|
|
} else {
|
2018-12-13 16:47:56 +03:00
|
|
|
fixupcontext[FIXUP_ARGPTR_LO] =
|
|
|
|
info->loader_start + KERNEL_ARGS_ADDR;
|
|
|
|
fixupcontext[FIXUP_ARGPTR_HI] =
|
|
|
|
(info->loader_start + KERNEL_ARGS_ADDR) >> 32;
|
2019-09-23 16:11:08 +03:00
|
|
|
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)");
|
2012-07-20 16:34:50 +04:00
|
|
|
exit(1);
|
|
|
|
}
|
2012-03-02 15:56:38 +04:00
|
|
|
}
|
2018-12-13 16:47:56 +03:00
|
|
|
fixupcontext[FIXUP_ENTRYPOINT_LO] = entry;
|
|
|
|
fixupcontext[FIXUP_ENTRYPOINT_HI] = entry >> 32;
|
2013-12-17 23:42:30 +04:00
|
|
|
|
2023-04-24 18:27:15 +03:00
|
|
|
arm_write_bootloader("bootloader", as, info->loader_start,
|
|
|
|
primary_loader, fixupcontext);
|
2013-12-17 23:42:30 +04:00
|
|
|
|
2015-11-03 16:49:41 +03:00
|
|
|
if (info->write_board_setup) {
|
|
|
|
info->write_board_setup(cpu, info);
|
|
|
|
}
|
2015-09-08 19:38:43 +03:00
|
|
|
|
2019-02-05 19:52:41 +03:00
|
|
|
/*
|
|
|
|
* Notify devices which need to fake up firmware initialization
|
2015-09-08 19:38:43 +03:00
|
|
|
* that we're doing a direct kernel boot.
|
|
|
|
*/
|
|
|
|
object_child_foreach_recursive(object_get_root(),
|
|
|
|
do_arm_linux_init, info);
|
2006-04-28 03:15:07 +04:00
|
|
|
}
|
2009-11-11 21:07:53 +03:00
|
|
|
info->is_linux = is_linux;
|
2011-03-05 15:51:45 +03:00
|
|
|
|
2018-05-04 20:05:51 +03:00
|
|
|
for (cs = first_cpu; cs; cs = CPU_NEXT(cs)) {
|
2014-10-24 15:19:11 +04:00
|
|
|
ARM_CPU(cs)->env.boot_info = info;
|
2011-03-05 15:51:45 +03:00
|
|
|
}
|
2019-02-05 19:52:41 +03:00
|
|
|
}
|
|
|
|
|
2019-02-05 19:52:42 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-02-05 19:52:42 +03:00
|
|
|
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
|
2019-02-05 19:52:42 +03:00
|
|
|
* 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.
|
2019-02-05 19:52:42 +03:00
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
2019-08-09 09:57:21 +03:00
|
|
|
void arm_load_kernel(ARMCPU *cpu, MachineState *ms, struct arm_boot_info *info)
|
2019-02-05 19:52:41 +03:00
|
|
|
{
|
|
|
|
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;
|
2022-01-27 18:46:38 +03:00
|
|
|
int nb_cpus = 0;
|
2019-02-05 19:52:41 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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));
|
2022-01-27 18:46:38 +03:00
|
|
|
nb_cpus++;
|
2019-02-05 19:52:41 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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()));
|
2019-08-09 09:57:21 +03:00
|
|
|
info->kernel_filename = ms->kernel_filename;
|
|
|
|
info->kernel_cmdline = ms->kernel_cmdline;
|
|
|
|
info->initrd_filename = ms->initrd_filename;
|
2020-11-02 17:44:36 +03:00
|
|
|
info->dtb_filename = ms->dtb;
|
2019-02-05 19:52:41 +03:00
|
|
|
info->dtb_limit = 0;
|
|
|
|
|
|
|
|
/* Load the kernel. */
|
|
|
|
if (!info->kernel_filename || info->firmware_loaded) {
|
2019-02-05 19:52:42 +03:00
|
|
|
arm_setup_firmware_boot(cpu, info);
|
2019-02-05 19:52:41 +03:00
|
|
|
} else {
|
|
|
|
arm_setup_direct_kernel_boot(cpu, info);
|
|
|
|
}
|
2015-06-15 20:06:11 +03:00
|
|
|
|
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.
|
|
|
|
*/
|
|
|
|
|
2022-01-27 18:46:35 +03:00
|
|
|
/*
|
|
|
|
* 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-27 18:46:36 +03:00
|
|
|
if (info->psci_conduit == QEMU_PSCI_CONDUIT_DISABLED &&
|
2022-01-27 18:46:38 +03:00
|
|
|
info->is_linux && nb_cpus > 1) {
|
2022-01-27 18:46:36 +03:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2018-05-10 20:10:56 +03:00
|
|
|
if (!info->skip_dtb_autoload && have_dtb(info)) {
|
2019-08-09 09:57:21 +03:00
|
|
|
if (arm_load_dtb(info->dtb_start, info, info->dtb_limit, as, ms) < 0) {
|
2018-05-10 20:10:56 +03:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
}
|
2015-06-02 14:29:12 +03:00
|
|
|
}
|
2015-09-08 19:38:43 +03:00
|
|
|
|
|
|
|
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)
|