diff mbox series

[qemu,v7,5/5] spapr/vof: Add basic support for MBR/GPT/GRUB

Message ID 20200220061622.15064-6-aik@ozlabs.ru (mailing list archive)
State New, archived
Headers show
Series spapr: Kill SLOF | expand

Commit Message

Alexey Kardashevskiy Feb. 20, 2020, 6:16 a.m. UTC
This hooks up disks to block backends so vof.bin can read MBR/GPT,
find a bootloader and run it. This bypasses the device drivers
and goes straight to the backend.

This implements basic support for 32bit big endian bootloader;
tested on GRUB.

Signed-off-by: Alexey Kardashevskiy <aik@ozlabs.ru>
---
 pc-bios/vof/Makefile     |   2 +-
 pc-bios/vof/vof.h        |  15 +++
 hw/ppc/spapr_of_client.c |  64 +++++++++
 pc-bios/vof/bootblock.c  | 242 ++++++++++++++++++++++++++++++++++
 pc-bios/vof/ci.c         |  11 ++
 pc-bios/vof/elf32.c      | 273 +++++++++++++++++++++++++++++++++++++++
 pc-bios/vof/main.c       |   1 +
 hw/ppc/trace-events      |   4 +
 pc-bios/vof.bin          | Bin 4272 -> 9180 bytes
 9 files changed, 611 insertions(+), 1 deletion(-)
 create mode 100644 pc-bios/vof/bootblock.c
 create mode 100644 pc-bios/vof/elf32.c

diff --git a/pc-bios/vof.bin b/pc-bios/vof.bin
index 152d86a094ce376ee8962c3f26afeccc9e7cae58..2c6629d2c72d5d1657c5e6791c15dabbc7aecb48 100755
GIT binary patch
literal 9180
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
Y^MpskV<-9!p2Y=t@b4yz19&R`3jiY<l>h($

delta 501
zcmYk$F-Tis5C`!4PBHJr^u0$>P?1<oAPJ^6LWT%IgA{!bK@cf6ICL?jP)jHjEEMy-
z7)2fQ$t{_ZMZ}>)AdaF=B|`=~6j}(V(4j+zP&%Z9l3q|thaVhw-+lM5&6b8s4~2Ul
zOn_=M2Jm8f9~#50gXRsSGT-sYgsv3|i6rh-C@)^)-V!P3>z$D2V8|=zkr_~A5nLv?
ztg?yjPTW<>sjdZq+b4AD@Q78qcFf?mS@tXQLVTt*^^LZbv)gOTgpRk=P!qT{NQ=%S
zZt{|I9XG=i?M`B@N{8K3RCM=J$)#Z~MHeoQ8l3w$o{hJfpQ!Hi{Tpu7c{nPb|KFV4
zSmQ%|b`b`xNg1{R08e%RVg>NZbs34b{`9wlL@uCHp2@H*$Y?th_g}W((~k!UEqi^R
zHQxI5l@_&Un0-$lG@lx~&GGw&YT5!?4GQT)>eIXC3Z*$*M)wR@RsO)w8G`mXgzPH_
z^KIb#kLgq&z~(#pqL1Ov04au-{3Cg)jLa<qh&}*4&1`nePFRIq^6V8`;=0DZu{vu2
V7{C<9g;8Sshw+H{S%^hg>@Ug1pqT&w
diff mbox series

Patch

diff --git a/pc-bios/vof/Makefile b/pc-bios/vof/Makefile
index 49f7e240eeff..7e4227bb2cc6 100644
--- a/pc-bios/vof/Makefile
+++ b/pc-bios/vof/Makefile
@@ -8,7 +8,7 @@  build-all: vof.bin
 %.o: %.c
 	cc -m32 -mbig-endian -c -fno-stack-protector -Wno-builtin-declaration-mismatch -o $@ $<
 
-vof.elf: entry.o main.o libc.o ci.o bootmem.o
+vof.elf: entry.o main.o libc.o ci.o bootmem.o bootblock.o elf32.o
 	ld -nostdlib -e_start -Tl.lds -EB -o $@ $^
 
 %.bin: %.elf
diff --git a/pc-bios/vof/vof.h b/pc-bios/vof/vof.h
index 738b2539aa19..b16270b289b7 100644
--- a/pc-bios/vof/vof.h
+++ b/pc-bios/vof/vof.h
@@ -37,6 +37,8 @@  phandle ci_finddevice(const char *path);
 uint32_t ci_getprop(phandle ph, const char *propname, void *prop, int len);
 ihandle ci_open(const char *path);
 void ci_close(ihandle ih);
+uint32_t ci_block_size(ihandle ih);
+uint32_t ci_seek(ihandle ih, uint64_t offset);
 uint32_t ci_read(ihandle ih, void *buf, int len);
 uint32_t ci_write(ihandle ih, const void *buf, int len);
 void ci_stdout(const char *buf);
@@ -44,5 +46,18 @@  void ci_stdoutn(const char *buf, int len);
 void *ci_claim(void *virt, uint32_t size, uint32_t align);
 uint32_t ci_release(void *virt, uint32_t size);
 
+/* ELF */
+int elf_load_file(void *file_addr, uint32_t *entry,
+                  int (*pre_load)(void*, long),
+                  void (*post_load)(void*, long));
+
+/* booting from blockdev */
+void boot_block(void);
+
 /* booting from -kernel */
 void boot_from_memory(uint64_t initrd, uint64_t initrdsize);
+
+/* bswap */
+#define le16_to_cpu(x) __builtin_bswap16(x)
+#define le32_to_cpu(x) __builtin_bswap32(x)
+#define le64_to_cpu(x) __builtin_bswap64(x)
diff --git a/hw/ppc/spapr_of_client.c b/hw/ppc/spapr_of_client.c
index 4c476e138e60..a36b32487349 100644
--- a/hw/ppc/spapr_of_client.c
+++ b/hw/ppc/spapr_of_client.c
@@ -43,6 +43,9 @@  typedef struct {
 typedef struct {
     DeviceState *dev;
     CharBackend *cbe;
+    BlockBackend *blk;
+    uint64_t blk_pos;
+    uint16_t blk_physical_block_size;
     char *params;
     char *path; /* the path used to open the instance */
     uint32_t phandle;
@@ -494,6 +497,8 @@  static uint32_t spapr_of_client_open(SpaprMachineState *spapr, const char *path)
     if (inst->dev) {
         const char *cdevstr = object_property_get_str(OBJECT(inst->dev),
                                                       "chardev", NULL);
+        const char *blkstr = object_property_get_str(OBJECT(inst->dev),
+                                                     "drive", NULL);
 
         if (cdevstr) {
             Chardev *cdev = qemu_chr_find(cdevstr);
@@ -501,6 +506,13 @@  static uint32_t spapr_of_client_open(SpaprMachineState *spapr, const char *path)
             if (cdev) {
                 inst->cbe = cdev->be;
             }
+        } else if (blkstr) {
+            BlockConf conf = { 0 };
+
+            inst->blk = blk_by_name(blkstr);
+            conf.blk = inst->blk;
+            blkconf_blocksizes(&conf);
+            inst->blk_physical_block_size = conf.physical_block_size;
         }
     }
 
@@ -602,6 +614,8 @@  static uint32_t of_client_write(SpaprMachineState *spapr, uint32_t ihandle,
             if (inst->cbe) {
                 toprint = qemu_chr_fe_write_all(inst->cbe, (uint8_t *) tmp,
                                                 toprint);
+            } else if (inst->blk) {
+                trace_spapr_of_client_blk_write(ihandle, len);
             }
         } else {
             /* We normally open stdout so this is fallback */
@@ -636,6 +650,17 @@  static uint32_t of_client_read(SpaprMachineState *spapr, uint32_t ihandle,
                 SpaprVioDevice *sdev = VIO_SPAPR_DEVICE(inst->dev);
 
                 ret = vty_getchars(sdev, buf, len); /* qemu_chr_fe_read_all? */
+            } else if (inst->blk) {
+                int rc = blk_pread(inst->blk, inst->blk_pos, buf, len);
+
+                if (rc > 0) {
+                    ret = rc;
+                }
+                trace_spapr_of_client_blk_read(ihandle, inst->blk_pos, len,
+                                               ret);
+                if (rc > 0) {
+                    inst->blk_pos += rc;
+                }
             }
         }
     }
@@ -643,6 +668,25 @@  static uint32_t of_client_read(SpaprMachineState *spapr, uint32_t ihandle,
     return ret;
 }
 
+static uint32_t of_client_seek(SpaprMachineState *spapr, uint32_t ihandle,
+                               uint32_t hi, uint32_t lo)
+{
+    uint32_t ret = -1;
+    uint64_t pos = ((uint64_t) hi << 32) | lo;
+    SpaprOfInstance *inst = (SpaprOfInstance *)
+        g_hash_table_lookup(spapr->of_instances, GINT_TO_POINTER(ihandle));
+
+    if (inst) {
+        if (inst->blk) {
+            inst->blk_pos = pos;
+            ret = 1;
+            trace_spapr_of_client_blk_seek(ihandle, pos, ret);
+        }
+    }
+
+    return ret;
+}
+
 static void of_client_clamed_dump(GArray *claimed)
 {
 #ifdef DEBUG
@@ -797,6 +841,20 @@  static uint32_t of_client_call_method(SpaprMachineState *spapr,
             /* do not bother about colors now */
             ret = 0;
         }
+    } else if (inst->blk) {
+        if (strcmp(method, "block-size") == 0) {
+            ret = 0;
+            *ret2 = inst->blk_physical_block_size;
+        } else if (strcmp(method, "#blocks") == 0) {
+            ret = 0;
+            *ret2 = blk_getlength(inst->blk) / inst->blk_physical_block_size;
+        }
+     } else if (inst->dev) {
+        if (strcmp(method, "vscsi-report-luns") == 0) {
+            /* TODO: Not implemented yet, not clear when it is really needed */
+            ret = -1;
+            *ret2 = 1;
+        }
     } else {
         trace_spapr_of_client_error_unknown_method(method);
     }
@@ -934,6 +992,12 @@  static target_ulong spapr_h_of_client(PowerPCCPU *cpu, SpaprMachineState *spapr,
                            be32_to_cpu(pargs.args[0]),
                            be32_to_cpu(pargs.args[1]),
                            be32_to_cpu(pargs.args[2]));
+    } else if (cmpserv("seek", 3, 1)) {
+        pargs.args[nargs] =
+            of_client_seek(spapr,
+                           be32_to_cpu(pargs.args[0]),
+                           be32_to_cpu(pargs.args[1]),
+                           be32_to_cpu(pargs.args[2]));
     } else if (cmpserv("claim", 3, 1)) {
         pargs.args[nargs] =
             of_client_claim(spapr,
diff --git a/pc-bios/vof/bootblock.c b/pc-bios/vof/bootblock.c
new file mode 100644
index 000000000000..cbf585746734
--- /dev/null
+++ b/pc-bios/vof/bootblock.c
@@ -0,0 +1,242 @@ 
+#include "vof.h"
+
+#define UUID_FMT_LEN 36
+#define UUID_FMT "%02hhx%02hhx%02hhx%02hhx-" \
+                 "%02hhx%02hhx-%02hhx%02hhx-" \
+                 "%02hhx%02hhx-" \
+                 "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
+
+typedef struct {
+    union {
+        unsigned char data[16];
+        struct {
+            /* Generated in BE endian, can be swapped with qemu_uuid_bswap. */
+            uint32_t time_low;
+            uint16_t time_mid;
+            uint16_t time_high_and_version;
+            uint8_t  clock_seq_and_reserved;
+            uint8_t  clock_seq_low;
+	    uint8_t  node[6];
+	} fields;
+    };
+} UUID;
+
+struct gpt_header {
+	char signature[8];
+	char revision[4];
+	uint32_t header_size;
+	uint32_t crc;
+	uint32_t reserved;
+	uint64_t current_lba;
+	uint64_t backup_lba;
+	uint64_t first_usable_lba;
+	uint64_t last_usable_lba;
+	char guid[16];
+	uint64_t partition_entries_lba;
+	uint32_t nr_partition_entries;
+	uint32_t size_partition_entry;
+	uint32_t crc_partitions;
+};
+
+#define GPT_SIGNATURE "EFI PART"
+#define GPT_REVISION "\0\0\1\0" /* revision 1.0 */
+
+struct gpt_entry {
+	char partition_type_guid[16];
+	char unique_guid[16];
+	uint64_t first_lba;
+	uint64_t last_lba;
+	uint64_t attributes;
+	char name[72];                /* UTF-16LE */
+};
+
+#define GPT_MIN_PARTITIONS 128
+#define GPT_PT_ENTRY_SIZE 128
+#define SECTOR_SIZE 512
+
+static int find_prep_partition_on_gpt(ihandle blk, uint8_t *lba01,
+		uint64_t *offset, uint64_t *size)
+{
+	unsigned long i, partnum, partentrysize;
+	int ret;
+	struct gpt_header *hdr = (struct gpt_header *) (lba01 + SECTOR_SIZE);
+	UUID prep_uuid = { .fields =
+			{ 0x9e1a2d38, 0xc612, 0x4316, 0xaa, 0x26,
+			{ 0x8b, 0x49, 0x52, 0x1e, 0x5a, 0x8b} } };
+
+	if (memcmp(hdr, "EFI PART", 8))
+		return -1;
+
+	partnum = le32_to_cpu(hdr->nr_partition_entries);
+	partentrysize = le32_to_cpu(hdr->size_partition_entry);
+
+	if (partentrysize < 128 || partentrysize > 512) {
+		return -1;
+	}
+
+	for (i = 0; i < partnum; ++i) {
+		uint8_t partdata[partentrysize];
+		struct gpt_entry *entry = (struct gpt_entry *) partdata;
+		uint64_t first, last;
+		UUID parttype;
+		char uuid[UUID_FMT_LEN + 1];
+
+		ci_seek(blk, 2 * SECTOR_SIZE + i * partentrysize);
+		ret = ci_read(blk, partdata, sizeof(partdata));
+		if (ret < 0)
+			return ret;
+		else if (!ret)
+			return -1;
+
+		memcpy(parttype.data, entry->partition_type_guid, 16);
+		first = le64_to_cpu(entry->first_lba);
+		last = le64_to_cpu(entry->last_lba);
+
+		if (!memcmp(&parttype, &prep_uuid, sizeof(parttype))) {
+			*offset = first * SECTOR_SIZE;
+			*size = (last - first) * SECTOR_SIZE;
+		}
+	}
+
+	if (*offset)
+		return 0;
+
+	return -1;
+}
+
+struct partition_record {
+	uint8_t bootable;
+	uint8_t start_head;
+	uint32_t start_cylinder;
+	uint8_t start_sector;
+	uint8_t system;
+	uint8_t end_head;
+	uint8_t end_cylinder;
+	uint8_t end_sector;
+	uint32_t start_sector_abs;
+	uint32_t nb_sectors_abs;
+};
+
+static void read_partition(uint8_t *p, struct partition_record *r)
+{
+	r->bootable = p[0];
+	r->start_head = p[1];
+	r->start_cylinder = p[3] | ((p[2] << 2) & 0x0300);
+	r->start_sector = p[2] & 0x3f;
+	r->system = p[4];
+	r->end_head = p[5];
+	r->end_cylinder = p[7] | ((p[6] << 2) & 0x300);
+	r->end_sector = p[6] & 0x3f;
+	r->start_sector_abs = le32_to_cpu(*(uint32_t *)(p + 8));
+	r->nb_sectors_abs   = le32_to_cpu(*(uint32_t *)(p + 12));
+}
+
+static int find_prep_partition(ihandle blk, uint64_t *offset, uint64_t *size)
+{
+	uint8_t lba01[SECTOR_SIZE * 2];
+	int i;
+	int ret = -1;
+
+	ci_seek(blk, 0);
+	ret = ci_read(blk, lba01, sizeof(lba01));
+	if (ret < 0)
+		return ret;
+
+	if (lba01[510] != 0x55 || lba01[511] != 0xaa)
+		return find_prep_partition_on_gpt(blk, lba01, offset, size);
+
+	for (i = 0; i < 4; i++) {
+		struct partition_record part;
+
+		read_partition(&lba01[446 + 16 * i], &part);
+
+		if (!part.system || !part.nb_sectors_abs) {
+			continue;
+		}
+
+		/* 0xEE == GPT */
+		if (part.system == 0xEE) {
+			ret = find_prep_partition_on_gpt(blk, lba01, offset, size);
+		}
+		/* 0x41 == PReP */
+		if (part.system == 0x41) {
+			*offset = part.start_sector_abs;
+			*offset <<= 9;
+			*size = (uint64_t)part.nb_sectors_abs << 9;
+			ret = 0;
+		}
+	}
+
+	return ret;
+}
+
+static int elf_pre_load(void *destaddr, long size)
+{
+	void *ret = ci_claim(destaddr, size, 0);
+
+	return (ret == destaddr) ? 0 : -1;
+}
+
+static void try_boot_block_device(ihandle blk, const char *path)
+{
+	uint32_t rc, elf_addr = 0;
+	uint64_t offset = 0, size = 0;
+	void *grub;
+	int ret;
+
+	if (find_prep_partition(blk, &offset, &size))
+		return;
+
+	grub = ci_claim((void*)0x20000000, size, 0);
+	if (!grub)
+		return;
+
+	ci_seek(blk, offset);
+	rc = ci_read(blk, grub, size);
+	if (rc <= 0) {
+		ci_release(grub, size);
+		return;
+	}
+
+	ret = elf_load_file(grub, &elf_addr, elf_pre_load, NULL);
+	ci_release(grub, size);
+	if (ret < 0)
+		return;
+
+    ci_stdout("** Booting from ");
+    ci_stdout(path);
+    ci_stdout("\n");
+	do_boot(elf_addr, 0, 0);
+}
+
+void boot_block(void)
+{
+	char bootlist[2048], *cur, *next;
+	uint32_t cb, blk;
+	phandle chosen = ci_finddevice("/chosen");
+
+	cb = ci_getprop(chosen, "qemu,boot-list", bootlist,
+			sizeof(bootlist) - 1);
+	bootlist[sizeof(bootlist) - 1] = '\0';
+
+	if (strlen(bootlist) == 0)
+		return;
+
+	for (cur = bootlist; cb > 0; cur = next + 1) {
+		for (next = cur; cb > 0; --cb) {
+			if (*next == '\n') {
+				*next = '\0';
+				++next;
+				--cb;
+				break;
+			}
+		}
+
+		blk = ci_open(cur);
+		if (!blk)
+			continue;
+
+		try_boot_block_device(blk, cur);
+		ci_close(blk);
+	}
+}
diff --git a/pc-bios/vof/ci.c b/pc-bios/vof/ci.c
index 143676421984..584776b2ff5b 100644
--- a/pc-bios/vof/ci.c
+++ b/pc-bios/vof/ci.c
@@ -96,6 +96,17 @@  void ci_close(ihandle ih)
 	call_prom("close", 1, 0, ih);
 }
 
+uint32_t ci_block_size(ihandle ih)
+{
+	return 512;
+}
+
+uint32_t ci_seek(ihandle ih, uint64_t offset)
+{
+	return call_prom("seek", 3, 1, ih, (prom_arg_t)(offset >> 32),
+			(prom_arg_t)(offset & 0xFFFFFFFFUL));
+}
+
 uint32_t ci_read(ihandle ih, void *buf, int len)
 {
 	return call_prom("read", 3, 1, ih, buf, len);
diff --git a/pc-bios/vof/elf32.c b/pc-bios/vof/elf32.c
new file mode 100644
index 000000000000..8e0623557d3f
--- /dev/null
+++ b/pc-bios/vof/elf32.c
@@ -0,0 +1,273 @@ 
+/******************************************************************************
+ * Copyright (c) 2004, 2011 IBM Corporation
+ * All rights reserved.
+ * This program and the accompanying materials
+ * are made available under the terms of the BSD License
+ * which accompanies this distribution, and is available at
+ * http://www.opensource.org/licenses/bsd-license.php
+ *
+ * Contributors:
+ *     IBM Corporation - initial implementation
+ *****************************************************************************/
+
+/*
+ * 32-bit ELF loader
+ */
+#include "vof.h"
+
+/* ELF object file types */
+#define ET_NONE		0	/* No file type */
+#define ET_REL		1	/* Relocatable file */
+#define ET_EXEC		2	/* Executable file */
+#define ET_DYN		3	/* Shared object file */
+#define ET_CORE		4	/* Core file */
+
+/* ELF object endian */
+#define ELFDATA2LSB	1	/* 2's complement, little endian */
+#define ELFDATA2MSB	2	/* 2's complement, big endian */
+
+struct ehdr32 {
+	uint32_t ei_ident;
+	uint8_t ei_class;
+	uint8_t ei_data;
+	uint8_t ei_version;
+	uint8_t ei_pad[9];
+	uint16_t e_type;
+	uint16_t e_machine;
+	uint32_t e_version;
+	uint32_t e_entry;
+	uint32_t e_phoff;
+	uint32_t e_shoff;
+	uint32_t e_flags;
+	uint16_t e_ehsize;
+	uint16_t e_phentsize;
+	uint16_t e_phnum;
+	uint16_t e_shentsize;
+	uint16_t e_shnum;
+	uint16_t e_shstrndx;
+};
+
+struct phdr32 {
+	uint32_t p_type;
+	uint32_t p_offset;
+	uint32_t p_vaddr;
+	uint32_t p_paddr;
+	uint32_t p_filesz;
+	uint32_t p_memsz;
+	uint32_t p_flags;
+	uint32_t p_align;
+};
+
+static inline void bswap_16p (uint16_t *x)
+{
+        *x = __builtin_bswap16(*x);
+}
+
+static inline void bswap_32p (uint32_t *x)
+{
+        *x = __builtin_bswap32(*x);
+}
+
+static inline void bswap_64p (uint64_t *x)
+{
+        *x = __builtin_bswap64(*x);
+}
+
+static struct phdr32*
+get_phdr32(void *file_addr)
+{
+	return (struct phdr32 *) (((unsigned char *)file_addr)
+		+ ((struct ehdr32 *)file_addr)->e_phoff);
+}
+
+static void
+load_segment(void *file_addr, struct phdr32 *phdr, signed long offset,
+             int (*pre_load)(void*, long),
+             void (*post_load)(void*, long))
+{
+	unsigned long src = phdr->p_offset + (unsigned long) file_addr;
+	unsigned long destaddr;
+
+	destaddr = (unsigned long)phdr->p_paddr;
+	destaddr = destaddr + offset;
+
+	/* check if we're allowed to copy */
+	if (pre_load != NULL) {
+		if (pre_load((void*)destaddr, phdr->p_memsz) != 0)
+			return;
+	}
+
+	/* copy into storage */
+	memmove((void *)destaddr, (void *)src, phdr->p_filesz);
+
+	/* clear bss */
+	memset((void *)(destaddr + phdr->p_filesz), 0,
+	       phdr->p_memsz - phdr->p_filesz);
+
+	if (phdr->p_memsz && post_load) {
+		post_load((void*)destaddr, phdr->p_memsz);
+	}
+}
+
+unsigned int
+elf_load_segments32(void *file_addr, signed long offset,
+                    int (*pre_load)(void*, long),
+                    void (*post_load)(void*, long))
+{
+	struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
+	/* Calculate program header address */
+	struct phdr32 *phdr = get_phdr32(file_addr);
+	int i;
+
+	/* loop e_phnum times */
+	for (i = 0; i <= ehdr->e_phnum; i++) {
+		/* PT_LOAD ? */
+		if (phdr->p_type == 1) {
+			if (phdr->p_paddr != phdr->p_vaddr) {
+				return 0;
+			}
+
+			/* copy segment */
+			load_segment(file_addr, phdr, offset, pre_load,
+			             post_load);
+		}
+		/* step to next header */
+		phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
+	}
+
+	/* Entry point is always a virtual address, so translate it
+	 * to physical before returning it */
+	return ehdr->e_entry;
+}
+
+/**
+ * Return the base address for loading (i.e. the address of the first PT_LOAD
+ * segment)
+ * @param  file_addr	pointer to the ELF file in memory
+ * @return		the base address
+ */
+long
+elf_get_base_addr32(void *file_addr)
+{
+	struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
+	struct phdr32 *phdr = get_phdr32(file_addr);
+	int i;
+
+	/* loop e_phnum times */
+	for (i = 0; i <= ehdr->e_phnum; i++) {
+		/* PT_LOAD ? */
+		if (phdr->p_type == 1) {
+			return phdr->p_paddr;
+		}
+		/* step to next header */
+		phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
+	}
+
+	return 0;
+}
+
+uint32_t elf_get_eflags_32(void *file_addr)
+{
+	struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
+
+	return ehdr->e_flags;
+}
+
+void
+elf_byteswap_header32(void *file_addr)
+{
+	struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
+	struct phdr32 *phdr;
+	int i;
+
+	bswap_16p(&ehdr->e_type);
+	bswap_16p(&ehdr->e_machine);
+	bswap_32p(&ehdr->e_version);
+	bswap_32p(&ehdr->e_entry);
+	bswap_32p(&ehdr->e_phoff);
+	bswap_32p(&ehdr->e_shoff);
+	bswap_32p(&ehdr->e_flags);
+	bswap_16p(&ehdr->e_ehsize);
+	bswap_16p(&ehdr->e_phentsize);
+	bswap_16p(&ehdr->e_phnum);
+	bswap_16p(&ehdr->e_shentsize);
+	bswap_16p(&ehdr->e_shnum);
+	bswap_16p(&ehdr->e_shstrndx);
+
+	phdr = get_phdr32(file_addr);
+
+	/* loop e_phnum times */
+	for (i = 0; i <= ehdr->e_phnum; i++) {
+		bswap_32p(&phdr->p_type);
+		bswap_32p(&phdr->p_offset);
+		bswap_32p(&phdr->p_vaddr);
+		bswap_32p(&phdr->p_paddr);
+		bswap_32p(&phdr->p_filesz);
+		bswap_32p(&phdr->p_memsz);
+		bswap_32p(&phdr->p_flags);
+		bswap_32p(&phdr->p_align);
+
+		/* step to next header */
+		phdr = (struct phdr32 *)(((uint8_t *)phdr) + ehdr->e_phentsize);
+	}
+}
+
+/**
+ * elf_check_file tests if the file at file_addr is
+ * a correct endian, ELF PPC executable
+ * @param file_addr  pointer to the start of the ELF file
+ * @return           the class (1 for 32 bit, 2 for 64 bit)
+ *                   -1 if it is not an ELF file
+ *                   -2 if it has the wrong endianness
+ *                   -3 if it is not an ELF executable
+ *                   -4 if it is not for PPC
+ */
+static int
+elf_check_file(unsigned long *file_addr)
+{
+        struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
+
+        /* check if it is an ELF image at all */
+        if (ehdr->ei_ident != 0x7f454c46)
+                return -1;
+
+        if (ELFDATA2MSB != ehdr->ei_data) {
+                switch (ehdr->ei_class) {
+                case 1:
+                        elf_byteswap_header32(file_addr);
+                        break;
+                }
+        }
+
+        /* check if it is an ELF executable ... and also
+         * allow DYN files, since this is specified by ePAPR */
+        if (ehdr->e_type != ET_EXEC && ehdr->e_type != ET_DYN)
+                return -3;
+
+        /* check if it is a PPC ELF executable */
+        if (ehdr->e_machine != 0x14 && ehdr->e_machine != 0x15)
+                return -4;
+
+        return ehdr->ei_class;
+}
+
+int elf_load_file(void *file_addr, uint32_t *entry,
+              int (*pre_load)(void*, long),
+              void (*post_load)(void*, long))
+{
+        int type = elf_check_file(file_addr);
+        struct ehdr32 *ehdr = (struct ehdr32 *) file_addr;
+
+        switch (type) {
+        case 1:
+                *entry = elf_load_segments32(file_addr, 0, pre_load, post_load);
+                if (ehdr->ei_data != ELFDATA2MSB) {
+                        type = 5; /* LE32 ABIv1 */
+                }
+                break;
+        }
+        if (*entry == 0)
+                type = 0;
+
+        return type;
+}
diff --git a/pc-bios/vof/main.c b/pc-bios/vof/main.c
index eb9aa8b78a8f..d0935e78edd8 100644
--- a/pc-bios/vof/main.c
+++ b/pc-bios/vof/main.c
@@ -19,5 +19,6 @@  void entry_c(void)
 
 	ci_stdout("*** Virtual Open Firmware ***\n");
 	boot_from_memory(initrd, initrdsize);
+	boot_block();
 	ci_panic("*** No boot target ***\n");
 }
diff --git a/hw/ppc/trace-events b/hw/ppc/trace-events
index 640d0649394d..9405d7fb2b84 100644
--- a/hw/ppc/trace-events
+++ b/hw/ppc/trace-events
@@ -38,6 +38,10 @@  spapr_of_client_getproplen(uint32_t ph, const char *prop, uint32_t ret) "ph=0x%x
 spapr_of_client_setprop(uint32_t ph, const char *prop, const char *val, uint32_t ret) "ph=0x%x \"%s\" [%s] => len=%d"
 spapr_of_client_open(const char *path, uint32_t ph, uint32_t ih) "%s ph=0x%x => ih=0x%x"
 spapr_of_client_interpret(const char *cmd, uint32_t param1, uint32_t param2, uint32_t ret, uint32_t ret2) "[%s] 0x%x 0x%x => 0x%x 0x%x"
+spapr_of_client_blk_write(uint32_t ih, uint32_t len) "0x%x => len=%d"
+spapr_of_client_blk_read(uint32_t ih, uint64_t pos, uint32_t len, uint32_t ret) "ih=0x%x @0x%"PRIx64" size=%d => %d"
+spapr_of_client_blk_seek(uint32_t ih, uint64_t pos, uint32_t ret) "ih=0x%x 0x%"PRIx64" => %d"
+spapr_of_client_blk_bootloader_read(uint64_t offset, uint64_t size) "0x%"PRIx64" size=0x%"PRIx64
 spapr_of_client_package_to_path(uint32_t ph, const char *tmp, uint32_t ret) "ph=0x%x => %s len=%d"
 spapr_of_client_instance_to_path(uint32_t ih, uint32_t ph, const char *tmp, uint32_t ret) "ih=0x%x ph=0x%x => %s len=%d"
 spapr_of_client_instance_to_package(uint32_t ih, uint32_t ph) "ih=0x%x => ph=0x%x"