summary refs log tree commit diff stats
path: root/hw/arm/boot.c
diff options
context:
space:
mode:
authorPeter Maydell <peter.maydell@linaro.org>2019-06-17 15:35:21 +0100
committerPeter Maydell <peter.maydell@linaro.org>2019-06-17 15:35:21 +0100
commit144ecc7f1a12504e8a134d1cb6d88764f75ae36c (patch)
tree35f39f957b3609984733a4daf3450af639eb2aba /hw/arm/boot.c
parent5d0e5694470d2952b4f257bc985cac8c89b4fd92 (diff)
parent1120827fa182f0e76226df7ffe7a86598d1df54f (diff)
downloadfocaccia-qemu-144ecc7f1a12504e8a134d1cb6d88764f75ae36c.tar.gz
focaccia-qemu-144ecc7f1a12504e8a134d1cb6d88764f75ae36c.zip
Merge remote-tracking branch 'remotes/pmaydell/tags/pull-target-arm-20190617' into staging
target-arm queue:
 * support large kernel images in bootloader (by avoiding
   putting the initrd over the top of them)
 * correctly disable FPU/DSP in the CPU for the mps2-an521, musca-a boards
 * arm_gicv3: Fix decoding of ID register range
 * arm_gicv3: GICD_TYPER.SecurityExtn is RAZ if GICD_CTLR.DS == 1
 * some code cleanups following on from the VFP decodetree conversion
 * Only implement doubles if the FPU supports them
   (so we now correctly model Cortex-M4, -M33 as single precision only)

# gpg: Signature made Mon 17 Jun 2019 15:33:01 BST
# gpg:                using RSA key E1A5C593CD419DE28E8315CF3C2525ED14360CDE
# gpg:                issuer "peter.maydell@linaro.org"
# gpg: Good signature from "Peter Maydell <peter.maydell@linaro.org>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@gmail.com>" [ultimate]
# gpg:                 aka "Peter Maydell <pmaydell@chiark.greenend.org.uk>" [ultimate]
# Primary key fingerprint: E1A5 C593 CD41 9DE2 8E83  15CF 3C25 25ED 1436 0CDE

* remotes/pmaydell/tags/pull-target-arm-20190617: (24 commits)
  target/arm: Only implement doubles if the FPU supports them
  target/arm: Fix typos in trans function prototypes
  target/arm: Remove unused cpu_F0s, cpu_F0d, cpu_F1s, cpu_F1d
  target/arm: Stop using deprecated functions in NEON_2RM_VCVT_F32_F16
  target/arm: stop using deprecated functions in NEON_2RM_VCVT_F16_F32
  target/arm: Stop using cpu_F0s in Neon VCVT fixed-point ops
  target/arm: Stop using cpu_F0s for Neon f32/s32 VCVT
  target/arm: Stop using cpu_F0s for NEON_2RM_VRECPE_F and NEON_2RM_VRSQRTE_F
  target/arm: Stop using cpu_F0s for NEON_2RM_VCVT[ANPM][US]
  target/arm: Stop using cpu_F0s for NEON_2RM_VRINT*
  target/arm: Stop using cpu_F0s for NEON_2RM_VNEG_F
  target/arm: Stop using cpu_F0s for NEON_2RM_VABS_F
  target/arm: Use vfp_expand_imm() for AArch32 VFP VMOV_imm
  target/arm: Move vfp_expand_imm() to translate.[ch]
  hw/intc/arm_gicv3: GICD_TYPER.SecurityExtn is RAZ if GICD_CTLR.DS == 1
  hw/intc/arm_gicv3: Fix decoding of ID register range
  hw/arm: Correctly disable FPU/DSP for some ARMSSE-based boards
  hw/arm/armv7m: Forward "vfp" and "dsp" properties to CPU
  target/arm: Allow M-profile CPUs to disable the DSP extension via CPU property
  target/arm: Allow VFP and Neon to be disabled via a CPU property
  ...

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Diffstat (limited to 'hw/arm/boot.c')
-rw-r--r--hw/arm/boot.c83
1 files changed, 62 insertions, 21 deletions
diff --git a/hw/arm/boot.c b/hw/arm/boot.c
index 0261fdabab..b2f93f6bef 100644
--- a/hw/arm/boot.c
+++ b/hw/arm/boot.c
@@ -911,6 +911,7 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
                                    hwaddr *entry, AddressSpace *as)
 {
     hwaddr kernel_load_offset = KERNEL64_LOAD_ADDR;
+    uint64_t kernel_size = 0;
     uint8_t *buffer;
     int size;
 
@@ -938,7 +939,10 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
          * is only valid if the image_size is non-zero.
          */
         memcpy(&hdrvals, buffer + ARM64_TEXT_OFFSET_OFFSET, sizeof(hdrvals));
-        if (hdrvals[1] != 0) {
+
+        kernel_size = le64_to_cpu(hdrvals[1]);
+
+        if (kernel_size != 0) {
             kernel_load_offset = le64_to_cpu(hdrvals[0]);
 
             /*
@@ -956,12 +960,21 @@ static uint64_t load_aarch64_image(const char *filename, hwaddr mem_base,
         }
     }
 
+    /*
+     * Kernels before v3.17 don't populate the image_size field, and
+     * raw images have no header. For those our best guess at the size
+     * is the size of the Image file itself.
+     */
+    if (kernel_size == 0) {
+        kernel_size = size;
+    }
+
     *entry = mem_base + kernel_load_offset;
     rom_add_blob_fixed_as(filename, buffer, size, *entry, as);
 
     g_free(buffer);
 
-    return size;
+    return kernel_size;
 }
 
 static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
@@ -977,6 +990,7 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
     int elf_machine;
     hwaddr entry;
     static const ARMInsnFixup *primary_loader;
+    uint64_t ram_end = info->loader_start + info->ram_size;
 
     if (arm_feature(&cpu->env, ARM_FEATURE_AARCH64)) {
         primary_loader = bootloader_aarch64;
@@ -999,20 +1013,6 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
     if (info->nb_cpus == 0)
         info->nb_cpus = 1;
 
-    /*
-     * 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.
-     */
-    info->initrd_start = info->loader_start +
-        MIN(info->ram_size / 2, 128 * 1024 * 1024);
-
     /* Assume that raw images are linux kernels, and ELF images are not.  */
     kernel_size = arm_load_elf(info, &elf_entry, &elf_low_addr,
                                &elf_high_addr, elf_machine, as);
@@ -1048,27 +1048,59 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
         /* 32-bit ARM */
         entry = info->loader_start + KERNEL_LOAD_ADDR;
         kernel_size = load_image_targphys_as(info->kernel_filename, entry,
-                                             info->ram_size - KERNEL_LOAD_ADDR,
-                                             as);
+                                             ram_end - KERNEL_LOAD_ADDR, as);
         is_linux = 1;
     }
     if (kernel_size < 0) {
         error_report("could not load kernel '%s'", info->kernel_filename);
         exit(1);
     }
+
+    if (kernel_size > info->ram_size) {
+        error_report("kernel '%s' is too large to fit in RAM "
+                     "(kernel size %d, RAM size %" PRId64 ")",
+                     info->kernel_filename, kernel_size, info->ram_size);
+        exit(1);
+    }
+
     info->entry = entry;
+
+    /*
+     * We want to put the initrd far enough into RAM that when the
+     * kernel is uncompressed it will not clobber the initrd. However
+     * on boards without much RAM we must ensure that we still leave
+     * enough room for a decent sized initrd, and on boards with large
+     * amounts of RAM we must avoid the initrd being so far up in RAM
+     * that it is outside lowmem and inaccessible to the kernel.
+     * So for boards with less  than 256MB of RAM we put the initrd
+     * halfway into RAM, and for boards with 256MB of RAM or more we put
+     * the initrd at 128MB.
+     * We also refuse to put the initrd somewhere that will definitely
+     * overlay the kernel we just loaded, though for kernel formats which
+     * don't tell us their exact size (eg self-decompressing 32-bit kernels)
+     * we might still make a bad choice here.
+     */
+    info->initrd_start = info->loader_start +
+        MAX(MIN(info->ram_size / 2, 128 * 1024 * 1024), kernel_size);
+    info->initrd_start = TARGET_PAGE_ALIGN(info->initrd_start);
+
     if (is_linux) {
         uint32_t fixupcontext[FIXUP_MAX];
 
         if (info->initrd_filename) {
+
+            if (info->initrd_start >= ram_end) {
+                error_report("not enough space after kernel to load initrd");
+                exit(1);
+            }
+
             initrd_size = load_ramdisk_as(info->initrd_filename,
                                           info->initrd_start,
-                                          info->ram_size - info->initrd_start,
-                                          as);
+                                          ram_end - info->initrd_start, as);
             if (initrd_size < 0) {
                 initrd_size = load_image_targphys_as(info->initrd_filename,
                                                      info->initrd_start,
-                                                     info->ram_size -
+                                                     ram_end -
                                                      info->initrd_start,
                                                      as);
             }
@@ -1077,6 +1109,11 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
                              info->initrd_filename);
                 exit(1);
             }
+            if (info->initrd_start + initrd_size > info->ram_size) {
+                error_report("could not load initrd '%s': "
+                             "too big to fit into RAM after the kernel",
+                             info->initrd_filename);
+            }
         } else {
             initrd_size = 0;
         }
@@ -1112,6 +1149,10 @@ static void arm_setup_direct_kernel_boot(ARMCPU *cpu,
             /* Place the DTB after the initrd in memory with alignment. */
             info->dtb_start = QEMU_ALIGN_UP(info->initrd_start + initrd_size,
                                            align);
+            if (info->dtb_start >= ram_end) {
+                error_report("Not enough space for DTB after kernel/initrd");
+                exit(1);
+            }
             fixupcontext[FIXUP_ARGPTR_LO] = info->dtb_start;
             fixupcontext[FIXUP_ARGPTR_HI] = info->dtb_start >> 32;
         } else {