Patchwork load_elf: replace the address addend by a translation function

login
register
mail settings
Submitter Aurelien Jarno
Date March 14, 2010, 8:57 p.m.
Message ID <1268600220-29293-1-git-send-email-aurelien@aurel32.net>
Download mbox | patch
Permalink /patch/47733/
State New
Headers show

Comments

Aurelien Jarno - March 14, 2010, 8:57 p.m.
A few machines need to translate the ELF header addresses into physical
addresses. Currently the only possibility is to add a value to the
addresses.

This patch replaces the addend argument by and a translation function
and an opaque passed to the function. A NULL function does not translate
the address.

The patch also convert all machines that have an addend, simplify the
PowerPC kernel loading and fix the MIPS kernel loading using this new
feature. Other machines may benefit from this feature.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 hw/an5206.c                   |    4 ++--
 hw/arm_boot.c                 |    4 ++--
 hw/armv7m.c                   |    4 ++--
 hw/axis_dev88.c               |    7 ++++++-
 hw/dummy_m68k.c               |    4 ++--
 hw/elf_ops.h                  |   10 ++++++++--
 hw/etraxfs.c                  |    7 ++++++-
 hw/loader.c                   |   14 +++++++-------
 hw/loader.h                   |    7 ++++---
 hw/mcf5208.c                  |    4 ++--
 hw/mips_malta.c               |   34 ++++++++++++++++++----------------
 hw/mips_mipssim.c             |   21 +++++++++------------
 hw/mips_r4k.c                 |   26 ++++++++++++++++----------
 hw/multiboot.c                |    4 ++--
 hw/petalogix_s3adsp1800_mmu.c |   11 ++++++++---
 hw/ppc440_bamboo.c            |    4 ++--
 hw/ppc_newworld.c             |   19 +++++++++----------
 hw/ppc_oldworld.c             |   21 ++++++++++-----------
 hw/ppce500_mpc8544ds.c        |    4 ++--
 hw/sun4m.c                    |   19 +++++++++++++++----
 hw/sun4u.c                    |   14 ++++++++++----
 21 files changed, 142 insertions(+), 100 deletions(-)
Paul Brook - March 14, 2010, 9:59 p.m.
> The patch also convert all machines that have an addend, simplify the
> PowerPC kernel loading 

The new PPC code looks like it may break images > 16M in size.

> and fix the MIPS kernel loading using this new
> feature.

I'm fairly sure the MIPS routines are not board specific, so it'd be nice if 
we could avoid the duplication.  This would also reduce pain if we implement 
loading to other segments (in particular kseg1 and xkphys).

Paul
Aurelien Jarno - March 14, 2010, 10:17 p.m.
Thanks for the review.

On Sun, Mar 14, 2010 at 09:59:44PM +0000, Paul Brook wrote:
> > The patch also convert all machines that have an addend, simplify the
> > PowerPC kernel loading 
> 
> The new PPC code looks like it may break images > 16M in size.

The idea was to get rid of the most significant byte, as it changes
depending on the kernel version, while we want the kernel to be loaded
at a fixed address. Looks like I have to shift the mask by four bits.

> > and fix the MIPS kernel loading using this new
> > feature.
> 
> I'm fairly sure the MIPS routines are not board specific, so it'd be nice if 
> we could avoid the duplication.  This would also reduce pain if we implement 
> loading to other segments (in particular kseg1 and xkphys).
> 

Good idea.
Paul Brook - March 14, 2010, 10:55 p.m.
> Thanks for the review.
> 
> On Sun, Mar 14, 2010 at 09:59:44PM +0000, Paul Brook wrote:
> > > The patch also convert all machines that have an addend, simplify the
> > > PowerPC kernel loading
> >
> > The new PPC code looks like it may break images > 16M in size.
> 
> The idea was to get rid of the most significant byte, as it changes
> depending on the kernel version, while we want the kernel to be loaded
> at a fixed address. Looks like I have to shift the mask by four bits.

I thought that problem was that some kernels are (incorrectly) linked with 
LMA==VMA, whereas they should have a base load address of zero.

Paul
Aurelien Jarno - March 14, 2010, 11:13 p.m.
On Sun, Mar 14, 2010 at 10:55:48PM +0000, Paul Brook wrote:
> > Thanks for the review.
> > 
> > On Sun, Mar 14, 2010 at 09:59:44PM +0000, Paul Brook wrote:
> > > > The patch also convert all machines that have an addend, simplify the
> > > > PowerPC kernel loading
> > >
> > > The new PPC code looks like it may break images > 16M in size.
> > 
> > The idea was to get rid of the most significant byte, as it changes
> > depending on the kernel version, while we want the kernel to be loaded
> > at a fixed address. Looks like I have to shift the mask by four bits.
> 
> I thought that problem was that some kernels are (incorrectly) linked with 
> LMA==VMA, whereas they should have a base load address of zero.
> 

Yes, this is the problem. But the way to workaround it is to get rid of
the MSB.

Patch

diff --git a/hw/an5206.c b/hw/an5206.c
index a4b83b0..f584d88 100644
--- a/hw/an5206.c
+++ b/hw/an5206.c
@@ -68,8 +68,8 @@  static void an5206_init(ram_addr_t ram_size,
         exit(1);
     }
 
-    kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL,
-                           1, ELF_MACHINE, 0);
+    kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+                           NULL, NULL, 1, ELF_MACHINE, 0);
     entry = elf_entry;
     if (kernel_size < 0) {
         kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
diff --git a/hw/arm_boot.c b/hw/arm_boot.c
index 30a76a5..df031a5 100644
--- a/hw/arm_boot.c
+++ b/hw/arm_boot.c
@@ -225,8 +225,8 @@  void arm_load_kernel(CPUState *env, struct arm_boot_info *info)
 #endif
 
     /* Assume that raw images are linux kernels, and ELF images are not.  */
-    kernel_size = load_elf(info->kernel_filename, 0, &elf_entry, NULL, NULL,
-                           big_endian, ELF_MACHINE, 1);
+    kernel_size = load_elf(info->kernel_filename, NULL, NULL, &elf_entry,
+                           NULL, NULL, big_endian, ELF_MACHINE, 1);
     entry = elf_entry;
     if (kernel_size < 0) {
         kernel_size = load_uimage(info->kernel_filename, &entry, NULL,
diff --git a/hw/armv7m.c b/hw/armv7m.c
index 034323d..35f7573 100644
--- a/hw/armv7m.c
+++ b/hw/armv7m.c
@@ -215,8 +215,8 @@  qemu_irq *armv7m_init(int flash_size, int sram_size,
     big_endian = 0;
 #endif
 
-    image_size = load_elf(kernel_filename, 0, &entry, &lowaddr, NULL,
-                          big_endian, ELF_MACHINE, 1);
+    image_size = load_elf(kernel_filename, NULL, NULL, &entry, &lowaddr,
+                          NULL, big_endian, ELF_MACHINE, 1);
     if (image_size < 0) {
         image_size = load_image_targphys(kernel_filename, 0, flash_size);
 	lowaddr = 0;
diff --git a/hw/axis_dev88.c b/hw/axis_dev88.c
index 1a66649..5516e42 100644
--- a/hw/axis_dev88.c
+++ b/hw/axis_dev88.c
@@ -249,6 +249,11 @@  static void main_cpu_reset(void *opaque)
     env->pc = bootstrap_pc;
 }
 
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+    return addr - 0x80000000LL;
+}
+
 static
 void axisdev88_init (ram_addr_t ram_size,
                      const char *boot_device,
@@ -345,7 +350,7 @@  void axisdev88_init (ram_addr_t ram_size,
 
         /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis 
            devboard SDK.  */
-        kernel_size = load_elf(kernel_filename, -0x80000000LL,
+        kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
                                &entry, NULL, &high, 0, ELF_MACHINE, 0);
         bootstrap_pc = entry;
         if (kernel_size < 0) {
diff --git a/hw/dummy_m68k.c b/hw/dummy_m68k.c
index ce45a59..9c9e6ff 100644
--- a/hw/dummy_m68k.c
+++ b/hw/dummy_m68k.c
@@ -43,8 +43,8 @@  static void dummy_m68k_init(ram_addr_t ram_size,
 
     /* Load kernel.  */
     if (kernel_filename) {
-        kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL,
-                               1, ELF_MACHINE, 0);
+        kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+                               NULL, NULL, 1, ELF_MACHINE, 0);
         entry = elf_entry;
         if (kernel_size < 0) {
             kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
diff --git a/hw/elf_ops.h b/hw/elf_ops.h
index 14b9ec0..69c0757 100644
--- a/hw/elf_ops.h
+++ b/hw/elf_ops.h
@@ -184,7 +184,9 @@  static int glue(load_symbols, SZ)(struct elfhdr *ehdr, int fd, int must_swab,
     return -1;
 }
 
-static int glue(load_elf, SZ)(const char *name, int fd, int64_t address_offset,
+static int glue(load_elf, SZ)(const char *name, int fd,
+                              uint64_t (*translate_fn)(void *, uint64_t),
+                              void *translate_opaque,
                               int must_swab, uint64_t *pentry,
                               uint64_t *lowaddr, uint64_t *highaddr,
                               int elf_machine, int clear_lsb)
@@ -253,7 +255,11 @@  static int glue(load_elf, SZ)(const char *name, int fd, int64_t address_offset,
             }
             /* address_offset is hack for kernel images that are
                linked at the wrong physical address.  */
-            addr = ph->p_paddr + address_offset;
+            if (translate_fn) {
+                addr = translate_fn(translate_opaque, ph->p_paddr);
+            } else {
+                addr = ph->p_paddr;
+            }
 
             snprintf(label, sizeof(label), "phdr #%d: %s", i, name);
             rom_add_blob_fixed(label, data, mem_size, addr);
diff --git a/hw/etraxfs.c b/hw/etraxfs.c
index 2f7f369..7405db4 100644
--- a/hw/etraxfs.c
+++ b/hw/etraxfs.c
@@ -44,6 +44,11 @@  static void main_cpu_reset(void *opaque)
     env->pc = bootstrap_pc;
 }
 
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+    return addr - 0x80000000LL;
+}
+
 static
 void bareetraxfs_init (ram_addr_t ram_size,
                        const char *boot_device,
@@ -137,7 +142,7 @@  void bareetraxfs_init (ram_addr_t ram_size,
 
         /* Boots a kernel elf binary, os/linux-2.6/vmlinux from the axis 
            devboard SDK.  */
-        kernel_size = load_elf(kernel_filename, -0x80000000LL,
+        kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
                                &entry, NULL, &high, 0, ELF_MACHINE, 0);
         bootstrap_pc = entry;
         if (kernel_size < 0) {
diff --git a/hw/loader.c b/hw/loader.c
index 1448887..79a6f95 100644
--- a/hw/loader.c
+++ b/hw/loader.c
@@ -276,9 +276,9 @@  static void *load_at(int fd, int offset, int size)
 #include "elf_ops.h"
 
 /* return < 0 if error, otherwise the number of bytes loaded in memory */
-int load_elf(const char *filename, int64_t address_offset,
-             uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr,
-             int big_endian, int elf_machine, int clear_lsb)
+int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
+             void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
+             uint64_t *highaddr, int big_endian, int elf_machine, int clear_lsb)
 {
     int fd, data_order, target_data_order, must_swab, ret;
     uint8_t e_ident[EI_NIDENT];
@@ -312,11 +312,11 @@  int load_elf(const char *filename, int64_t address_offset,
 
     lseek(fd, 0, SEEK_SET);
     if (e_ident[EI_CLASS] == ELFCLASS64) {
-        ret = load_elf64(filename, fd, address_offset, must_swab, pentry,
-                         lowaddr, highaddr, elf_machine, clear_lsb);
+        ret = load_elf64(filename, fd, translate_fn, translate_opaque, must_swab,
+                         pentry, lowaddr, highaddr, elf_machine, clear_lsb);
     } else {
-        ret = load_elf32(filename, fd, address_offset, must_swab, pentry,
-                         lowaddr, highaddr, elf_machine, clear_lsb);
+        ret = load_elf32(filename, fd, translate_fn, translate_opaque, must_swab,
+                         pentry, lowaddr, highaddr, elf_machine, clear_lsb);
     }
 
     close(fd);
diff --git a/hw/loader.h b/hw/loader.h
index 56676e1..1f82fc5 100644
--- a/hw/loader.h
+++ b/hw/loader.h
@@ -5,9 +5,10 @@ 
 int get_image_size(const char *filename);
 int load_image(const char *filename, uint8_t *addr); /* deprecated */
 int load_image_targphys(const char *filename, target_phys_addr_t, int max_sz);
-int load_elf(const char *filename, int64_t address_offset,
-             uint64_t *pentry, uint64_t *lowaddr, uint64_t *highaddr,
-             int big_endian, int elf_machine, int clear_lsb);
+int load_elf(const char *filename, uint64_t (*translate_fn)(void *, uint64_t),
+             void *translate_opaque, uint64_t *pentry, uint64_t *lowaddr,
+             uint64_t *highaddr, int big_endian, int elf_machine,
+             int clear_lsb);
 int load_aout(const char *filename, target_phys_addr_t addr, int max_sz,
               int bswap_needed, target_phys_addr_t target_page_size);
 int load_uimage(const char *filename, target_phys_addr_t *ep,
diff --git a/hw/mcf5208.c b/hw/mcf5208.c
index 5598611..5b686c6 100644
--- a/hw/mcf5208.c
+++ b/hw/mcf5208.c
@@ -270,8 +270,8 @@  static void mcf5208evb_init(ram_addr_t ram_size,
         exit(1);
     }
 
-    kernel_size = load_elf(kernel_filename, 0, &elf_entry, NULL, NULL,
-                           1, ELF_MACHINE, 0);
+    kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+                           NULL, NULL, 1, ELF_MACHINE, 0);
     entry = elf_entry;
     if (kernel_size < 0) {
         kernel_size = load_uimage(kernel_filename, &entry, NULL, NULL);
diff --git a/hw/mips_malta.c b/hw/mips_malta.c
index 2d02a10..274513a 100644
--- a/hw/mips_malta.c
+++ b/hw/mips_malta.c
@@ -46,15 +46,7 @@ 
 
 //#define DEBUG_BOARD_INIT
 
-#ifdef TARGET_MIPS64
-#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffULL)
-#else
-#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffU)
-#endif
-
-#define ENVP_ADDR (int32_t)0x80002000
-#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000))
-
+#define ENVP_ADDR		0x80002000l
 #define ENVP_NB_ENTRIES	 	16
 #define ENVP_ENTRY_SIZE	 	256
 
@@ -82,6 +74,16 @@  static struct _loaderparams {
     const char *initrd_filename;
 } loaderparams;
 
+static uint64_t kseg0_to_phys(void *opaque, uint64_t addr)
+{
+    return addr & 0x7fffffffll;
+}
+
+static uint64_t phys_to_kseg0(void *opaque, uint64_t addr)
+{
+    return addr | ~0x7fffffffll;
+}
+
 /* Malta FPGA */
 static void malta_fpga_update_display(void *opaque)
 {
@@ -681,7 +683,7 @@  static void prom_set(uint32_t* prom_buf, int index, const char *string, ...)
 /* Kernel */
 static int64_t load_kernel (void)
 {
-    int64_t kernel_entry, kernel_low, kernel_high;
+    int64_t kernel_entry, kernel_high;
     long initrd_size;
     ram_addr_t initrd_offset;
     int big_endian;
@@ -695,9 +697,9 @@  static int64_t load_kernel (void)
     big_endian = 0;
 #endif
 
-    if (load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND,
-                 (uint64_t *)&kernel_entry, (uint64_t *)&kernel_low,
-                 (uint64_t *)&kernel_high, big_endian, ELF_MACHINE, 1) < 0) {
+    if (load_elf(loaderparams.kernel_filename, kseg0_to_phys, NULL,
+                 (uint64_t *)&kernel_entry, NULL, (uint64_t *)&kernel_high,
+                 big_endian, ELF_MACHINE, 1) < 0) {
         fprintf(stderr, "qemu: could not load kernel '%s'\n",
                 loaderparams.kernel_filename);
         exit(1);
@@ -733,8 +735,8 @@  static int64_t load_kernel (void)
 
     prom_set(prom_buf, prom_index++, loaderparams.kernel_filename);
     if (initrd_size > 0) {
-        prom_set(prom_buf, prom_index++, "rd_start=0x" TARGET_FMT_lx " rd_size=%li %s",
-                 PHYS_TO_VIRT(initrd_offset), initrd_size,
+        prom_set(prom_buf, prom_index++, "rd_start=0x%" PRIx64 " rd_size=%li %s",
+                 phys_to_kseg0(NULL, initrd_offset), initrd_size,
                  loaderparams.kernel_cmdline);
     } else {
         prom_set(prom_buf, prom_index++, loaderparams.kernel_cmdline);
@@ -747,7 +749,7 @@  static int64_t load_kernel (void)
     prom_set(prom_buf, prom_index++, NULL);
 
     rom_add_blob_fixed("prom", prom_buf, prom_size,
-                       ENVP_ADDR + VIRT_TO_PHYS_ADDEND);
+                       kseg0_to_phys(NULL, ENVP_ADDR));
 
     return kernel_entry;
 }
diff --git a/hw/mips_mipssim.c b/hw/mips_mipssim.c
index aa90116..8646011 100644
--- a/hw/mips_mipssim.c
+++ b/hw/mips_mipssim.c
@@ -35,14 +35,6 @@ 
 #include "loader.h"
 #include "elf.h"
 
-#ifdef TARGET_MIPS64
-#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffULL)
-#else
-#define PHYS_TO_VIRT(x) ((x) | ~0x7fffffffU)
-#endif
-
-#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000))
-
 static struct _loaderparams {
     int ram_size;
     const char *kernel_filename;
@@ -55,9 +47,14 @@  typedef struct ResetData {
     uint64_t vector;
 } ResetData;
 
+static uint64_t kseg0_to_phys(void *opaque, uint64_t addr)
+{
+    return addr & 0x7fffffffll;
+}
+
 static int64_t load_kernel(void)
 {
-    int64_t entry, kernel_low, kernel_high;
+    int64_t entry, kernel_high;
     long kernel_size;
     long initrd_size;
     ram_addr_t initrd_offset;
@@ -69,9 +66,9 @@  static int64_t load_kernel(void)
     big_endian = 0;
 #endif
 
-    kernel_size = load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND,
-                           (uint64_t *)&entry, (uint64_t *)&kernel_low,
-                           (uint64_t *)&kernel_high, big_endian, ELF_MACHINE, 1);
+    kernel_size = load_elf(loaderparams.kernel_filename, kseg0_to_phys, NULL,
+                           (uint64_t *)&entry, NULL, (uint64_t *)&kernel_high,
+                           big_endian, ELF_MACHINE, 1);
     if (kernel_size >= 0) {
         if ((entry & ~0x7fffffffULL) == 0x80000000)
             entry = (int32_t)entry;
diff --git a/hw/mips_r4k.c b/hw/mips_r4k.c
index b69d7c3..31af82e 100644
--- a/hw/mips_r4k.c
+++ b/hw/mips_r4k.c
@@ -21,10 +21,6 @@ 
 #include "loader.h"
 #include "elf.h"
 
-#define PHYS_TO_VIRT(x) ((x) | ~(target_ulong)0x7fffffff)
-
-#define VIRT_TO_PHYS_ADDEND (-((int64_t)(int32_t)0x80000000))
-
 #define MAX_IDE_BUS 2
 
 static const int ide_iobase[2] = { 0x1f0, 0x170 };
@@ -42,6 +38,16 @@  static struct _loaderparams {
     const char *initrd_filename;
 } loaderparams;
 
+static uint64_t kseg0_to_phys(void *opaque, uint64_t addr)
+{
+    return addr & 0x7fffffffll;
+}
+
+static uint64_t phys_to_kseg0(void *opaque, uint64_t addr)
+{
+    return addr | ~0x7fffffffll;
+}
+
 static void mips_qemu_writel (void *opaque, target_phys_addr_t addr,
 			      uint32_t val)
 {
@@ -77,7 +83,7 @@  typedef struct ResetData {
 
 static int64_t load_kernel(void)
 {
-    int64_t entry, kernel_low, kernel_high;
+    int64_t entry, kernel_high;
     long kernel_size, initrd_size, params_size;
     ram_addr_t initrd_offset;
     uint32_t *params_buf;
@@ -88,9 +94,9 @@  static int64_t load_kernel(void)
 #else
     big_endian = 0;
 #endif
-    kernel_size = load_elf(loaderparams.kernel_filename, VIRT_TO_PHYS_ADDEND,
-                           (uint64_t *)&entry, (uint64_t *)&kernel_low,
-                           (uint64_t *)&kernel_high, big_endian, ELF_MACHINE, 1);
+    kernel_size = load_elf(loaderparams.kernel_filename, kseg0_to_phys, NULL,
+                           (uint64_t *)&entry, NULL, (uint64_t *)&kernel_high,
+                           big_endian, ELF_MACHINE, 1);
     if (kernel_size >= 0) {
         if ((entry & ~0x7fffffffULL) == 0x80000000)
             entry = (int32_t)entry;
@@ -132,8 +138,8 @@  static int64_t load_kernel(void)
     params_buf[1] = tswap32(0x12345678);
 
     if (initrd_size > 0) {
-        snprintf((char *)params_buf + 8, 256, "rd_start=0x" TARGET_FMT_lx " rd_size=%li %s",
-                 PHYS_TO_VIRT((uint32_t)initrd_offset),
+        snprintf((char *)params_buf + 8, 256, "rd_start=0x%" PRIx64 " rd_size=%li %s",
+                 phys_to_kseg0(NULL, initrd_offset),
                  initrd_size, loaderparams.kernel_cmdline);
     } else {
         snprintf((char *)params_buf + 8, 256, "%s", loaderparams.kernel_cmdline);
diff --git a/hw/multiboot.c b/hw/multiboot.c
index a25fbf6..a1b665c 100644
--- a/hw/multiboot.c
+++ b/hw/multiboot.c
@@ -170,8 +170,8 @@  int load_multiboot(void *fw_cfg,
         uint64_t elf_low, elf_high;
         int kernel_size;
         fclose(f);
-        kernel_size = load_elf(kernel_filename, 0, &elf_entry, &elf_low, &elf_high,
-                               0, ELF_MACHINE, 0);
+        kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+                               &elf_low, &elf_high, 0, ELF_MACHINE, 0);
         if (kernel_size < 0) {
             fprintf(stderr, "Error while loading elf kernel\n");
             exit(1);
diff --git a/hw/petalogix_s3adsp1800_mmu.c b/hw/petalogix_s3adsp1800_mmu.c
index 8743636..ea91d65 100644
--- a/hw/petalogix_s3adsp1800_mmu.c
+++ b/hw/petalogix_s3adsp1800_mmu.c
@@ -104,6 +104,11 @@  static int petalogix_load_device_tree(target_phys_addr_t addr,
     return fdt_size;
 }
 
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+    return addr - 0x30000000LL;
+}
+
 static void
 petalogix_s3adsp1800_init(ram_addr_t ram_size,
                           const char *boot_device,
@@ -163,13 +168,13 @@  petalogix_s3adsp1800_init(ram_addr_t ram_size,
         uint32_t base32;
 
         /* Boots a kernel elf binary.  */
-        kernel_size = load_elf(kernel_filename, 0,
+        kernel_size = load_elf(kernel_filename, NULL, NULL,
                                &entry, &low, &high,
                                1, ELF_MACHINE, 0);
         base32 = entry;
         if (base32 == 0xc0000000) {
-            kernel_size = load_elf(kernel_filename, -0x30000000LL,
-                                   &entry, NULL, NULL,
+            kernel_size = load_elf(kernel_filename, translate_kernel_address,
+                                   NULL, &entry, NULL, NULL,
                                    1, ELF_MACHINE, 0);
         }
         /* Always boot into physical ram.  */
diff --git a/hw/ppc440_bamboo.c b/hw/ppc440_bamboo.c
index efe5a77..6ca873e 100644
--- a/hw/ppc440_bamboo.c
+++ b/hw/ppc440_bamboo.c
@@ -120,8 +120,8 @@  static void bamboo_init(ram_addr_t ram_size,
     if (kernel_filename) {
         kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
         if (kernel_size < 0) {
-            kernel_size = load_elf(kernel_filename, 0, &elf_entry, &elf_lowaddr,
-                                   NULL, 1, ELF_MACHINE, 0);
+            kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+                                   &elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
             entry = elf_entry;
             loadaddr = elf_lowaddr;
         }
diff --git a/hw/ppc_newworld.c b/hw/ppc_newworld.c
index d4f9013..7f4c0b4 100644
--- a/hw/ppc_newworld.c
+++ b/hw/ppc_newworld.c
@@ -115,6 +115,11 @@  static int fw_cfg_boot_set(void *opaque, const char *boot_device)
     return 0;
 }
 
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+    return (addr & 0x00ffffff) + KERNEL_LOAD_ADDR;
+}
+
 /* PowerPC Mac99 hardware initialisation */
 static void ppc_core99_init (ram_addr_t ram_size,
                              const char *boot_device,
@@ -180,7 +185,8 @@  static void ppc_core99_init (ram_addr_t ram_size,
 
     /* Load OpenBIOS (ELF) */
     if (filename) {
-        bios_size = load_elf(filename, 0, NULL, NULL, NULL, 1, ELF_MACHINE, 0);
+        bios_size = load_elf(filename, NULL, NULL, NULL,
+                             NULL, NULL, 1, ELF_MACHINE, 0);
 
         qemu_free(filename);
     } else {
@@ -232,15 +238,8 @@  static void ppc_core99_init (ram_addr_t ram_size,
 #endif
         kernel_base = KERNEL_LOAD_ADDR;
 
-        /* Now we can load the kernel. The first step tries to load the kernel
-           supposing PhysAddr = 0x00000000. If that was wrong the kernel is
-           loaded again, the new PhysAddr being computed from lowaddr. */
-        kernel_size = load_elf(kernel_filename, kernel_base, NULL, &lowaddr, NULL,
-                               1, ELF_MACHINE, 0);
-        if (kernel_size > 0 && lowaddr != KERNEL_LOAD_ADDR) {
-            kernel_size = load_elf(kernel_filename, (2 * kernel_base) - lowaddr,
-                                   NULL, NULL, NULL, 1, ELF_MACHINE, 0);
-        }
+        kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
+                               NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
         if (kernel_size < 0)
             kernel_size = load_aout(kernel_filename, kernel_base,
                                     ram_size - kernel_base, bswap_needed,
diff --git a/hw/ppc_oldworld.c b/hw/ppc_oldworld.c
index 93c95ba..45eea7d 100644
--- a/hw/ppc_oldworld.c
+++ b/hw/ppc_oldworld.c
@@ -122,6 +122,12 @@  static int fw_cfg_boot_set(void *opaque, const char *boot_device)
     return 0;
 }
 
+
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+    return (addr & 0x00ffffff) + KERNEL_LOAD_ADDR;
+}
+
 static void ppc_heathrow_init (ram_addr_t ram_size,
                                const char *boot_device,
                                const char *kernel_filename,
@@ -185,8 +191,8 @@  static void ppc_heathrow_init (ram_addr_t ram_size,
 
     /* Load OpenBIOS (ELF) */
     if (filename) {
-        bios_size = load_elf(filename, 0, NULL, NULL, NULL,
-                               1, ELF_MACHINE, 0);
+        bios_size = load_elf(filename, 0, NULL, NULL, NULL, NULL,
+                             1, ELF_MACHINE, 0);
         qemu_free(filename);
     } else {
         bios_size = -1;
@@ -236,15 +242,8 @@  static void ppc_heathrow_init (ram_addr_t ram_size,
         bswap_needed = 0;
 #endif
         kernel_base = KERNEL_LOAD_ADDR;
-        /* Now we can load the kernel. The first step tries to load the kernel
-           supposing PhysAddr = 0x00000000. If that was wrong the kernel is
-           loaded again, the new PhysAddr being computed from lowaddr. */
-        kernel_size = load_elf(kernel_filename, kernel_base, NULL, &lowaddr, NULL,
-                               1, ELF_MACHINE, 0);
-        if (kernel_size > 0 && lowaddr != KERNEL_LOAD_ADDR) {
-            kernel_size = load_elf(kernel_filename, (2 * kernel_base) - lowaddr,
-                                   NULL, NULL, NULL, 1, ELF_MACHINE, 0);
-        }
+        kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
+                               NULL, &lowaddr, NULL, 1, ELF_MACHINE, 0);
         if (kernel_size < 0)
             kernel_size = load_aout(kernel_filename, kernel_base,
                                     ram_size - kernel_base, bswap_needed,
diff --git a/hw/ppce500_mpc8544ds.c b/hw/ppce500_mpc8544ds.c
index 90b4c56..a83dba4 100644
--- a/hw/ppce500_mpc8544ds.c
+++ b/hw/ppce500_mpc8544ds.c
@@ -231,8 +231,8 @@  static void mpc8544ds_init(ram_addr_t ram_size,
     if (kernel_filename) {
         kernel_size = load_uimage(kernel_filename, &entry, &loadaddr, NULL);
         if (kernel_size < 0) {
-            kernel_size = load_elf(kernel_filename, 0, &elf_entry, &elf_lowaddr,
-                                   NULL, 1, ELF_MACHINE, 0);
+            kernel_size = load_elf(kernel_filename, NULL, NULL, &elf_entry,
+                                   &elf_lowaddr, NULL, 1, ELF_MACHINE, 0);
             entry = elf_entry;
             loadaddr = elf_lowaddr;
         }
diff --git a/hw/sun4m.c b/hw/sun4m.c
index 7e26282..90e661d 100644
--- a/hw/sun4m.c
+++ b/hw/sun4m.c
@@ -292,6 +292,11 @@  static void cpu_halt_signal(void *opaque, int irq, int level)
         cpu_interrupt(cpu_single_env, CPU_INTERRUPT_HALT);
 }
 
+static uint64_t translate_kernel_address(void *opaque, uint64_t addr)
+{
+    return addr - 0xf0000000ULL;
+}
+
 static unsigned long sun4m_load_kernel(const char *kernel_filename,
                                        const char *initrd_filename,
                                        ram_addr_t RAM_size)
@@ -312,8 +317,8 @@  static unsigned long sun4m_load_kernel(const char *kernel_filename,
 #else
         bswap_needed = 0;
 #endif
-        kernel_size = load_elf(kernel_filename, -0xf0000000ULL, NULL, NULL,
-                               NULL, 1, ELF_MACHINE, 0);
+        kernel_size = load_elf(kernel_filename, translate_kernel_address, NULL,
+                               NULL, NULL, NULL, 1, ELF_MACHINE, 0);
         if (kernel_size < 0)
             kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR,
                                     RAM_size - KERNEL_LOAD_ADDR, bswap_needed,
@@ -636,6 +641,12 @@  static void afx_register_devices(void)
 device_init(afx_register_devices);
 
 /* Boot PROM (OpenBIOS) */
+static uint64_t translate_prom_address(void *opaque, uint64_t addr)
+{
+    target_phys_addr_t *base_addr = (target_phys_addr_t *)opaque;
+    return addr + *base_addr - PROM_VADDR;
+}
+
 static void prom_init(target_phys_addr_t addr, const char *bios_name)
 {
     DeviceState *dev;
@@ -655,8 +666,8 @@  static void prom_init(target_phys_addr_t addr, const char *bios_name)
     }
     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
     if (filename) {
-        ret = load_elf(filename, addr - PROM_VADDR, NULL, NULL, NULL,
-                       1, ELF_MACHINE, 0);
+        ret = load_elf(filename, translate_prom_address, &addr, NULL,
+                       NULL, NULL, 1, ELF_MACHINE, 0);
         if (ret < 0 || ret > PROM_SIZE_MAX) {
             ret = load_image_targphys(filename, addr, PROM_SIZE_MAX);
         }
diff --git a/hw/sun4u.c b/hw/sun4u.c
index f339df3..6663836 100644
--- a/hw/sun4u.c
+++ b/hw/sun4u.c
@@ -189,8 +189,8 @@  static unsigned long sun4u_load_kernel(const char *kernel_filename,
 #else
         bswap_needed = 0;
 #endif
-        kernel_size = load_elf(kernel_filename, 0, NULL, NULL, NULL,
-                               1, ELF_MACHINE, 0);
+        kernel_size = load_elf(kernel_filename, NULL, NULL, NULL,
+                               NULL, NULL, 1, ELF_MACHINE, 0);
         if (kernel_size < 0)
             kernel_size = load_aout(kernel_filename, KERNEL_LOAD_ADDR,
                                     RAM_size - KERNEL_LOAD_ADDR, bswap_needed,
@@ -580,6 +580,12 @@  static void pci_ebus_register(void)
 
 device_init(pci_ebus_register);
 
+static uint64_t translate_prom_address(void *opaque, uint64_t addr)
+{
+    target_phys_addr_t *base_addr = (target_phys_addr_t *)opaque;
+    return addr + *base_addr - PROM_VADDR;
+}
+
 /* Boot PROM (OpenBIOS) */
 static void prom_init(target_phys_addr_t addr, const char *bios_name)
 {
@@ -600,8 +606,8 @@  static void prom_init(target_phys_addr_t addr, const char *bios_name)
     }
     filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
     if (filename) {
-        ret = load_elf(filename, addr - PROM_VADDR, NULL, NULL, NULL,
-                       1, ELF_MACHINE, 0);
+        ret = load_elf(filename, translate_prom_address, &addr,
+                       NULL, NULL, NULL, 1, ELF_MACHINE, 0);
         if (ret < 0 || ret > PROM_SIZE_MAX) {
             ret = load_image_targphys(filename, addr, PROM_SIZE_MAX);
         }