static void default_reset_secondary(ARMCPU *cpu,
const struct arm_boot_info *info)
{
- CPUARMState *env = &cpu->env;
+ CPUState *cs = CPU(cpu);
- stl_phys_notdirty(&address_space_memory, info->smp_bootreg_addr, 0);
- env->regs[15] = info->smp_loader_start;
+ address_space_stl_notdirty(&address_space_memory, info->smp_bootreg_addr,
+ 0, MEMTXATTRS_UNSPECIFIED, NULL);
+ cpu_set_pc(cs, info->smp_loader_start);
}
static inline bool have_dtb(const struct arm_boot_info *info)
}
#define WRITE_WORD(p, value) do { \
- stl_phys_notdirty(&address_space_memory, p, value); \
+ address_space_stl_notdirty(&address_space_memory, p, value, \
+ MEMTXATTRS_UNSPECIFIED, NULL); \
p += 4; \
} while (0)
* Returns: the size of the device tree image on success,
* 0 if the image size exceeds the limit,
* -1 on errors.
+ *
+ * Note: Must not be called unless have_dtb(binfo) is true.
*/
static int load_dtb(hwaddr addr, const struct arm_boot_info *binfo,
hwaddr addr_limit)
goto fail;
}
g_free(filename);
- } else if (binfo->get_dtb) {
+ } else {
fdt = binfo->get_dtb(binfo, &size);
if (!fdt) {
fprintf(stderr, "Board was unable to create a dtb blob\n");
static void do_cpu_reset(void *opaque)
{
ARMCPU *cpu = opaque;
+ CPUState *cs = CPU(cpu);
CPUARMState *env = &cpu->env;
const struct arm_boot_info *info = env->boot_info;
- cpu_reset(CPU(cpu));
+ cpu_reset(cs);
if (info) {
if (!info->is_linux) {
/* Jump to the entry point. */
- if (env->aarch64) {
- env->pc = info->entry;
- } else {
- env->regs[15] = info->entry & 0xfffffffe;
+ uint64_t entry = info->entry;
+
+ if (!env->aarch64) {
env->thumb = info->entry & 1;
+ entry &= 0xfffffffe;
}
+ cpu_set_pc(cs, entry);
} else {
- if (CPU(cpu) == first_cpu) {
+ /* If we are booting Linux then we need to check whether we are
+ * booting into secure or non-secure state and adjust the state
+ * accordingly. Out of reset, ARM is defined to be in secure state
+ * (SCR.NS = 0), we change that here if non-secure boot has been
+ * requested.
+ */
+ if (arm_feature(env, ARM_FEATURE_EL3)) {
+ /* AArch64 is defined to come out of reset into EL3 if enabled.
+ * If we are booting Linux then we need to adjust our EL as
+ * Linux expects us to be in EL2 or EL1. AArch32 resets into
+ * SVC, which Linux expects, so no privilege/exception level to
+ * adjust.
+ */
if (env->aarch64) {
- env->pc = info->loader_start;
- } else {
- env->regs[15] = info->loader_start;
+ if (arm_feature(env, ARM_FEATURE_EL2)) {
+ env->pstate = PSTATE_MODE_EL2h;
+ } else {
+ env->pstate = PSTATE_MODE_EL1h;
+ }
}
+ /* Set to non-secure if not a secure boot */
+ if (!info->secure_boot) {
+ /* Linux expects non-secure state */
+ env->cp15.scr_el3 |= SCR_NS;
+ }
+ }
+
+ if (cs == first_cpu) {
+ cpu_set_pc(cs, info->loader_start);
+
if (!have_dtb(info)) {
if (old_param) {
set_kernel_args_old(info);
}
}
-void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
+/**
+ * load_image_to_fw_cfg() - Load an image file into an fw_cfg entry identified
+ * by key.
+ * @fw_cfg: The firmware config instance to store the data in.
+ * @size_key: The firmware config key to store the size of the loaded
+ * data under, with fw_cfg_add_i32().
+ * @data_key: The firmware config key to store the loaded data under,
+ * with fw_cfg_add_bytes().
+ * @image_name: The name of the image file to load. If it is NULL, the
+ * function returns without doing anything.
+ * @try_decompress: Whether the image should be decompressed (gunzipped) before
+ * adding it to fw_cfg. If decompression fails, the image is
+ * loaded as-is.
+ *
+ * In case of failure, the function prints an error message to stderr and the
+ * process exits with status 1.
+ */
+static void load_image_to_fw_cfg(FWCfgState *fw_cfg, uint16_t size_key,
+ uint16_t data_key, const char *image_name,
+ bool try_decompress)
{
- CPUState *cs = CPU(cpu);
+ size_t size = -1;
+ uint8_t *data;
+
+ if (image_name == NULL) {
+ return;
+ }
+
+ if (try_decompress) {
+ size = load_image_gzipped_buffer(image_name,
+ LOAD_IMAGE_MAX_GUNZIP_BYTES, &data);
+ }
+
+ if (size == (size_t)-1) {
+ gchar *contents;
+ gsize length;
+
+ if (!g_file_get_contents(image_name, &contents, &length, NULL)) {
+ fprintf(stderr, "failed to load \"%s\"\n", image_name);
+ exit(1);
+ }
+ size = length;
+ data = (uint8_t *)contents;
+ }
+
+ fw_cfg_add_i32(fw_cfg, size_key, size);
+ fw_cfg_add_bytes(fw_cfg, data_key, data, size);
+}
+
+static void arm_load_kernel_notify(Notifier *notifier, void *data)
+{
+ CPUState *cs;
int kernel_size;
int initrd_size;
int is_linux = 0;
hwaddr entry, kernel_load_offset;
int big_endian;
static const ARMInsnFixup *primary_loader;
+ ArmLoadKernelNotifier *n = DO_UPCAST(ArmLoadKernelNotifier,
+ notifier, notifier);
+ ARMCPU *cpu = n->cpu;
+ struct arm_boot_info *info =
+ container_of(n, struct arm_boot_info, load_kernel_notifier);
/* Load the kernel. */
- if (!info->kernel_filename) {
+ if (!info->kernel_filename || info->firmware_loaded) {
if (have_dtb(info)) {
- /* If we have a device tree blob, but no kernel to supply it to,
- * copy it to the base of RAM for a bootloader to pick up.
+ /* 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.
*/
if (load_dtb(info->loader_start, info, 0) < 0) {
exit(1);
}
}
- /* If no kernel specified, do nothing; we will start from address 0
- * (typically a boot ROM image) in the same way as hardware.
+ if (info->kernel_filename) {
+ FWCfgState *fw_cfg;
+ bool try_decompressing_kernel;
+
+ fw_cfg = fw_cfg_find();
+ try_decompressing_kernel = arm_feature(&cpu->env,
+ ARM_FEATURE_AARCH64);
+
+ /* Expose the kernel, the command line, and the initrd in fw_cfg.
+ * We don't process them here at all, it's all left to the
+ * firmware.
+ */
+ load_image_to_fw_cfg(fw_cfg,
+ FW_CFG_KERNEL_SIZE, FW_CFG_KERNEL_DATA,
+ info->kernel_filename,
+ try_decompressing_kernel);
+ load_image_to_fw_cfg(fw_cfg,
+ FW_CFG_INITRD_SIZE, FW_CFG_INITRD_DATA,
+ info->initrd_filename, false);
+
+ if (info->kernel_cmdline) {
+ fw_cfg_add_i32(fw_cfg, FW_CFG_CMDLINE_SIZE,
+ strlen(info->kernel_cmdline) + 1);
+ fw_cfg_add_string(fw_cfg, FW_CFG_CMDLINE_DATA,
+ info->kernel_cmdline);
+ }
+ }
+
+ /* We will start from address 0 (typically a boot ROM image) in the
+ * same way as hardware.
*/
return;
}
entry = elf_entry;
if (kernel_size < 0) {
kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
- &is_linux);
+ &is_linux, NULL, NULL);
}
/* On aarch64, it's the bootloader's job to uncompress the kernel. */
if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64) && kernel_size < 0) {
* we point to the kernel args.
*/
if (have_dtb(info)) {
- /* Place the DTB after the initrd in memory. Note that some
- * kernels will trash anything in the 4K page the initrd
- * ends in, so make sure the DTB isn't caught up in that.
- */
- hwaddr dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size,
- 4096);
+ hwaddr align;
+ hwaddr dtb_start;
+
+ if (elf_machine == EM_AARCH64) {
+ /*
+ * Some AArch64 kernels on early bootup map the fdt region as
+ *
+ * [ ALIGN_DOWN(fdt, 2MB) ... ALIGN_DOWN(fdt, 2MB) + 2MB ]
+ *
+ * Let's play safe and prealign it to 2MB to give us some space.
+ */
+ align = 2 * 1024 * 1024;
+ } else {
+ /*
+ * Some 32bit kernels will trash anything in the 4K page the
+ * initrd ends in, so make sure the DTB isn't caught up in that.
+ */
+ align = 4096;
+ }
+
+ /* Place the DTB after the initrd in memory with alignment. */
+ dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size, align);
if (load_dtb(dtb_start, info, 0) < 0) {
exit(1);
}
}
info->is_linux = is_linux;
- for (; cs; cs = CPU_NEXT(cs)) {
- cpu = ARM_CPU(cs);
- cpu->env.boot_info = info;
- qemu_register_reset(do_cpu_reset, cpu);
+ for (cs = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
+ ARM_CPU(cs)->env.boot_info = info;
+ }
+}
+
+void arm_load_kernel(ARMCPU *cpu, struct arm_boot_info *info)
+{
+ CPUState *cs;
+
+ info->load_kernel_notifier.cpu = cpu;
+ info->load_kernel_notifier.notifier.notify = arm_load_kernel_notify;
+ qemu_add_machine_init_done_notifier(&info->load_kernel_notifier.notifier);
+
+ /* 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 = CPU(cpu); cs; cs = CPU_NEXT(cs)) {
+ qemu_register_reset(do_cpu_reset, ARM_CPU(cs));
}
}