qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH 12/13] Add zipl bootloader interpreter


From: Alexander Graf
Subject: [Qemu-devel] [PATCH 12/13] Add zipl bootloader interpreter
Date: Tue, 24 Nov 2009 18:29:40 +0100

The default bootloader on S390 is zipl. Because we don't emulate normal S390
hardware we need to write our own parser for the bootloader configuration,
so we can boot off real hard disks.

This patch adds a pretty simple implementation of such an interpreter. It only
supports 512 bytes sector sizes, always boots the default entry and doesn't
work with reboots yet. But it's better than nothing.

Signed-off-by: Alexander Graf <address@hidden>
---
 Makefile.target    |    2 +-
 hw/s390-virtio.c   |   11 ++-
 hw/s390-zipl.c     |  233 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 target-s390x/cpu.h |    1 +
 4 files changed, 243 insertions(+), 4 deletions(-)
 create mode 100644 hw/s390-zipl.c

diff --git a/Makefile.target b/Makefile.target
index d47d879..e147e09 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -297,7 +297,7 @@ obj-sh4-y += ide/core.o ide/mmio.o
 obj-m68k-y = an5206.o mcf5206.o mcf_uart.o mcf_intc.o mcf5208.o mcf_fec.o
 obj-m68k-y += m68k-semi.o dummy_m68k.o
 
-obj-s390x-y = s390-virtio-bus.o s390-virtio.o 
+obj-s390x-y = s390-virtio-bus.o s390-virtio.o s390-zipl.o
 
 main.o vl.o: QEMU_CFLAGS+=$(GPROF_CFLAGS)
 
diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
index c843e0d..a008c45 100644
--- a/hw/s390-virtio.c
+++ b/hw/s390-virtio.c
@@ -29,6 +29,7 @@
 #include "hw/virtio-console.h"
 #include "hw/sysbus.h"
 #include "kvm.h"
+#include "s390-zipl.h"
 
 #include "hw/s390-virtio-bus.h"
 
@@ -166,6 +167,7 @@ static void s390_init(ram_addr_t _ram_size,
 
     env->halted = 0;
     env->exception_index = 0;
+    cpu_synchronize_state(env);
 
     if (kernel_filename) {
         kernel_size = load_image(kernel_filename, qemu_get_ram_ptr(0));
@@ -175,9 +177,7 @@ static void s390_init(ram_addr_t _ram_size,
             exit(1);
         }
 
-        cpu_synchronize_state(env);
-        env->psw.addr = KERN_IMAGE_START;
-        env->psw.mask = 0x0000000180000000UL;
+        env->reset_addr = KERN_IMAGE_START;
     }
 
     if (initrd_filename) {
@@ -223,7 +223,12 @@ static void s390_init(ram_addr_t _ram_size,
         dev = qdev_create((BusState *)s390_bus, "virtio-blk-s390");
         qdev_prop_set_drive(dev, "drive", dinfo);
         qdev_init_nofail(dev);
+
+        zipl_load(env, dinfo->bdrv);
     }
+
+    env->psw.mask = 0x0000000180000000UL;
+    env->psw.addr = env->reset_addr;
 }
 
 static QEMUMachine s390_machine = {
diff --git a/hw/s390-zipl.c b/hw/s390-zipl.c
new file mode 100644
index 0000000..44c08c2
--- /dev/null
+++ b/hw/s390-zipl.c
@@ -0,0 +1,233 @@
+/*
+ * QEMU S390 zipl interpreter
+ *
+ * Copyright (c) 2009 Alexander Graf <address@hidden>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "hw.h"
+#include "block.h"
+#include "sysemu.h"
+#include "net.h"
+#include "boards.h"
+#include "monitor.h"
+#include "loader.h"
+#include "elf.h"
+#include "hw/virtio.h"
+#include "hw/virtio-console.h"
+#include "hw/sysbus.h"
+#include "kvm.h"
+#include "s390-zipl.h"
+
+#include "hw/s390-virtio-bus.h"
+
+//#define DEBUG_S390
+
+#ifdef DEBUG_S390
+#define dprintf(fmt, ...) \
+    do { fprintf(stderr, "zipl: " fmt, ## __VA_ARGS__); } while (0)
+#else
+#define dprintf(fmt, ...) \
+    do { } while (0)
+#endif
+
+struct scsi_blockptr {
+    uint64_t blockno;
+    uint16_t size;
+    uint16_t blockct;
+    uint8_t reserved[4];
+} __attribute__ ((packed));
+
+struct component_entry {
+    struct scsi_blockptr data;
+    uint8_t pad[7];
+    uint8_t component_type;
+    uint64_t load_address;
+} __attribute((packed));
+
+struct component_header {
+    uint8_t magic[4];
+    uint8_t type;
+    uint8_t reserved[27];
+} __attribute((packed));
+
+struct mbr {
+    uint8_t magic[4];
+    uint32_t version_id;
+    uint8_t reserved[8];
+    struct scsi_blockptr blockptr;
+} __attribute__ ((packed));
+
+#define ZIPL_MAGIC                     "zIPL"
+#define SECTOR_SIZE                    512
+
+#define ZIPL_COMP_HEADER_IPL           0x00
+#define ZIPL_COMP_HEADER_DUMP          0x01
+
+#define ZIPL_COMP_ENTRY_LOAD           0x02
+#define ZIPL_COMP_ENTRY_EXEC           0x01
+
+/* Check for ZIPL magic. Returns 0 if not matched. */
+static int zipl_magic(uint8_t *ptr)
+{
+    int r;
+
+    r = !memcmp(ptr, ZIPL_MAGIC, 4);
+    if (!r)
+        dprintf("invalid magic: %#hhx %#hhx %#hhx %#hhx\n",
+                ptr[0], ptr[1], ptr[2], ptr[3]);
+
+    return r;
+}
+
+static int zipl_load_segment(BlockDriverState *bdrv,
+                             struct component_entry *entry)
+{
+    int max_entries = SECTOR_SIZE / sizeof(struct scsi_blockptr);
+    struct scsi_blockptr bprs[max_entries + 1];
+    uint64_t blockno, address;
+    int i, len;
+    uint8_t *tmp;
+
+    blockno = be64_to_cpu(entry->data.blockno);
+    address = be64_to_cpu(entry->load_address);
+
+    dprintf("loading segment at %#lx : %#lx\n", blockno, address);
+
+    do {
+        if (bdrv_read(bdrv, blockno, (uint8_t *)&bprs, 1) == -1) {
+            dprintf("failed reading bprs at %#lx\n", blockno);
+            goto fail;
+        }
+
+        for (i = 0; i < max_entries; i++) {
+            blockno = be64_to_cpu(bprs[i].blockno);
+            if (!blockno)
+                break;
+
+            len = be16_to_cpu(bprs[i].size) *
+                  (be16_to_cpu(bprs[i].blockct) + 1);
+            tmp = qemu_malloc(len);
+            if (bdrv_pread(bdrv, blockno * SECTOR_SIZE, tmp, len) == -1) {
+                dprintf("failed reading %#x b segment at %#lx\n",
+                        len, blockno * SECTOR_SIZE);
+                qemu_free(tmp);
+                goto fail;
+            }
+
+            cpu_physical_memory_write(address, tmp, len);
+            qemu_free(tmp);
+            address += len;
+        }
+    } while (blockno);
+
+    return 0;
+
+fail:
+    dprintf("failed loading segment\n");
+    return -1;
+}
+
+/* Run a zipl program */
+static int zipl_run(CPUState *env, BlockDriverState *bdrv, struct 
scsi_blockptr *pte)
+{
+    struct component_header *header;
+    struct component_entry *entry;
+    uint8_t sec[SECTOR_SIZE];
+
+    bdrv_read(bdrv, be64_to_cpu(pte->blockno), sec, 1);
+    header = (struct component_header *)sec;
+
+    if (!zipl_magic(sec))
+        goto fail;
+
+    if (header->type != ZIPL_COMP_HEADER_IPL)
+        goto fail;
+
+    dprintf("start loading images\n");
+
+    /* Load image(s) into RAM */
+    entry = (struct component_entry *)(&header[1]);
+    while (entry->component_type == ZIPL_COMP_ENTRY_LOAD) {
+        if (zipl_load_segment(bdrv, entry) < 0)
+            goto fail;
+
+        entry++;
+
+        if ((uint8_t*)(&entry[1]) > (sec + SECTOR_SIZE))
+            goto fail;
+    }
+
+    /* And set the reset vector, so we know where to start */
+    if (entry->component_type != ZIPL_COMP_ENTRY_EXEC)
+        goto fail;
+
+    env->reset_addr = be64_to_cpu(entry->load_address) & 0x7fffffff;
+    dprintf("set reset addr to: %#lx\n", env->reset_addr);
+
+    return 0;
+
+fail:
+    dprintf("failed running zipl\n");
+    return -1;
+}
+
+int zipl_load(CPUState *env, BlockDriverState *bdrv)
+{
+    struct mbr mbr;
+    uint8_t sec[SECTOR_SIZE], *ns, *ns_end;
+    int program_table_entries = 0;
+    int pte_len = sizeof(struct scsi_blockptr);
+    struct scsi_blockptr *prog_table_entry;
+
+    /* Grab the MBR */
+
+    if (bdrv_pread(bdrv, 0, &mbr, sizeof(mbr)) == -1)
+        goto fail;
+
+    if (!zipl_magic(mbr.magic))
+        goto fail;
+
+    /* Parse the program table */
+    if (bdrv_read(bdrv, be64_to_cpu(mbr.blockptr.blockno), sec, 1) == -1)
+        goto fail;
+
+    if (!zipl_magic(sec))
+        goto fail;
+
+    ns_end = sec + SECTOR_SIZE;
+    for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns++) {
+        prog_table_entry = (struct scsi_blockptr *)ns;
+        if (!prog_table_entry->blockno)
+            break;
+
+        program_table_entries++;
+    }
+
+    dprintf("Found %d program table entries\n", program_table_entries);
+
+    if (!program_table_entries)
+        goto fail;
+
+    /* Run the default entry */
+
+    prog_table_entry = (struct scsi_blockptr *)(sec + pte_len);
+
+    return zipl_run(env, bdrv, prog_table_entry);
+
+fail:
+    dprintf("failed loading zipl\n");
+    return -1;
+}
diff --git a/target-s390x/cpu.h b/target-s390x/cpu.h
index a74745c..2e286c7 100644
--- a/target-s390x/cpu.h
+++ b/target-s390x/cpu.h
@@ -63,6 +63,7 @@ typedef struct CPUS390XState {
     int cc; /* condition code (0-3) */
     
     uint64_t __excp_addr;
+    uint64_t reset_addr;
     
     CPU_COMMON
 } CPUS390XState;
-- 
1.6.0.2





reply via email to

[Prev in Thread] Current Thread [Next in Thread]