[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH 1/1] s390: IPL device for s390
From: |
Christian Borntraeger |
Subject: |
[Qemu-devel] [PATCH 1/1] s390: IPL device for s390 |
Date: |
Fri, 4 May 2012 15:44:00 +0200 |
An IPL (booting) on s390 of SCSI disks is done by a firmware component.
Lets implement this scheme as an qemu device that also allows to
configure the IPL like the HMC. We have a parameter iplid that
refers to a disk device and a load parm that specifies the entry
on the disk to be ipled. We also provide a default device
if no -device s390-ipl statement is given.
Signed-off-by: Christian Borntraeger <address@hidden>
---
Makefile.target | 2 +-
hw/s390-loader.c | 463 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
hw/s390-loader.h | 81 ++++++++++
hw/s390-virtio.c | 36 +----
vl.c | 7 +
5 files changed, 554 insertions(+), 35 deletions(-)
create mode 100644 hw/s390-loader.c
create mode 100644 hw/s390-loader.h
diff --git a/Makefile.target b/Makefile.target
index 1582904..7b8cd84 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -374,7 +374,7 @@ obj-sh4-y += 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-loader.o
obj-alpha-y = mc146818rtc.o
obj-alpha-y += alpha_pci.o alpha_dp264.o alpha_typhoon.o
diff --git a/hw/s390-loader.c b/hw/s390-loader.c
new file mode 100644
index 0000000..2d63ecf
--- /dev/null
+++ b/hw/s390-loader.c
@@ -0,0 +1,463 @@
+/*
+ * bootloader support
+ * Copyright IBM Corp. 2007,2012
+ * Author: Christian Borntraeger <address@hidden>
+ *
+ * This file is licensed under the terms of the GNU General Public License(GPL)
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <linux/fs.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "cpu.h"
+#include "hw/loader.h"
+#include "hw/s390-loader.h"
+#include "hw/s390-virtio-bus.h"
+#include "hw/sysbus.h"
+
+#define KERN_IMAGE_START 0x010000UL
+
+typedef struct {
+ BlockDriverState *bs;
+ uint64_t (*blockno)(BlockPtr *blockptr);
+ uint64_t (*offset)(BlockPtr *blockptr);
+ uint64_t (*size)(BlockPtr *blockptr);
+ bool (*empty)(BlockPtr *blockptr);
+ BlockPtr *(*element)(BlockPtr *blockptr, int num);
+ uint32_t (*entries)(void);
+ uint32_t loadparm;
+ uint8_t heads;
+ uint8_t secs;
+ uint16_t blk_size;
+} Loader;
+
+/*
+ * We have one structure that is setup with the right callbacks for the
+ * detected type of boot loader
+ */
+static Loader loader;
+
+/* here are the FCP Callbacks */
+static uint64_t getblockno_fcp(BlockPtr *entry)
+{
+ return be64_to_cpu(entry->u.fcp.blockno);
+}
+
+static uint64_t getoffset_fcp(BlockPtr *entry)
+{
+ return getblockno_fcp(entry) * be16_to_cpu(entry->u.fcp.size);
+}
+
+static uint64_t getsize_fcp(BlockPtr *entry)
+{
+ return loader.blk_size * (be16_to_cpu(entry->u.fcp.blockct) + 1);
+}
+
+static bool getempty_fcp(BlockPtr *entry)
+{
+ return getblockno_fcp(entry) == 0UL;
+}
+
+static BlockPtr *getelement_fcp(BlockPtr *blockptr, int num)
+{
+ FCPBlockPtr *fcp = (FCPBlockPtr *) blockptr;
+
+ return (BlockPtr *) &fcp[num];
+}
+
+static uint32_t entries_fcp(void)
+{
+ return loader.blk_size / sizeof(FCPBlockPtr);
+};
+
+/* and here the callbacks for the new and old eckd map */
+static uint64_t getblockno_eckd(BlockPtr *entry)
+{
+ return 1UL * loader.secs * loader.heads * entry->u.eckd.cyls +
+ 1UL * loader.secs * entry->u.eckd.heads +
+ 1UL * entry->u.eckd.secs - 1UL;
+}
+
+static uint64_t getoffset_eckd(BlockPtr *entry)
+{
+ return getblockno_eckd(entry) * entry->u.eckd.block_size;
+}
+
+static uint64_t getsize_eckd(BlockPtr *entry)
+{
+ return loader.blk_size * (entry->u.eckd.count + 1);
+}
+
+static bool getempty_eckd(BlockPtr *entry)
+{
+ return getblockno_eckd(entry) == -1UL;
+}
+
+static BlockPtr *getelement_eckd(BlockPtr *blockptr, int num)
+{
+ ECKDBlockPtr *eckd = (ECKDBlockPtr *) blockptr;
+
+ return (BlockPtr *) &eckd[num];
+}
+
+static BlockPtr *getelement_neckd(BlockPtr *blockptr, int num)
+{
+ NECKDBlockPtr *neckd = (NECKDBlockPtr *) blockptr;
+
+ return (BlockPtr *) &neckd[num];
+}
+
+
+static uint32_t entries_eckd(void)
+{
+ return loader.blk_size / sizeof(ECKDBlockPtr);
+};
+
+static uint32_t entries_neckd(void)
+{
+ return loader.blk_size / sizeof(NECKDBlockPtr);
+};
+
+static int magic_ok(void *tmp)
+{
+ return memcmp(tmp, "zIPL", 4) == 0 ? 1 : 0;
+}
+
+static uint64_t parse_segment_elements(BlockPtr *bprs,
+ uint64_t *address,
+ Loader *loader)
+{
+ unsigned d;
+ int len;
+
+ for (d = 0; d < loader->entries() - 1; d++) {
+ if (*address > ram_size) {
+ error_report("s390-ipl: bootmap points to illegal address");
+ exit(1);
+ }
+ if (loader->empty(loader->element(bprs, d))) {
+ return 0;
+ }
+ len = bdrv_pread(loader->bs,
+ loader->offset(loader->element(bprs, d)),
+ (void *) (*address + qemu_get_ram_ptr(0)),
+ loader->size(loader->element(bprs, d)));
+ if (len != loader->size(loader->element(bprs, d))) {
+ error_report("s390-ipl: error while parsing bootmap\n");
+ exit(1);
+ }
+ *address += len;
+ }
+ return loader->blockno(loader->element(bprs, loader->entries() - 1));
+}
+
+static void parse_segment_table(uint64_t blockno, uint64_t address,
+ Loader *loader)
+{
+ BlockPtr bprs[loader->entries() + 1];
+
+ do {
+ bdrv_pread(loader->bs, blockno * loader->blk_size, bprs, sizeof(bprs));
+ blockno = parse_segment_elements(bprs, &address, loader);
+ } while (blockno);
+}
+
+static uint64_t parse_program(BlockPtr *blockptr, Loader *loader)
+{
+ ComponentHeader header;
+ ComponentEntry entry;
+ uint64_t offset = loader->offset(blockptr);
+ int ret;
+
+ ret = bdrv_pread(loader->bs, offset, &header, sizeof(header));
+ if (ret != sizeof(header)) {
+ return -1;
+ }
+ if (!magic_ok(&header.magic)) {
+ return -1;
+ }
+
+ if (header.type != component_header_ipl) {
+ error_report("s390-ipl: no IPL header on bootdevice\n");
+ exit(1);
+ }
+
+ offset += sizeof(header);
+ ret = bdrv_pread(loader->bs, offset, &entry, sizeof(entry));
+ if (ret != sizeof(header)) {
+ return -1;
+ }
+
+ while (entry.component_type == component_load) {
+ parse_segment_table(loader->blockno(&entry.blkptr),
+ entry.address.load_address, loader);
+ offset += sizeof(entry);
+ ret = bdrv_pread(loader->bs, offset, &entry, sizeof(entry));
+ if (ret != sizeof(header)) {
+ return -1;
+ }
+ }
+ if (entry.component_type == component_execute) {
+ return entry.address.load_address;
+ } else {
+ error_report("s390-ipl: no IPL address on bootmap\n");
+ exit(1);
+ }
+}
+
+static uint64_t parse_program_table(BlockPtr *blockptr,
+ Loader *loader)
+{
+ BlockPtr entries[loader->entries()];
+ uint32_t n;
+
+ if (bdrv_pread(loader->bs, loader->offset(blockptr),
+ entries, loader->blk_size) != loader->blk_size) {
+ return -1;
+ }
+
+ /* entry 0, holds the magic */
+ if (!magic_ok(&entries[0])) {
+ return -1;
+ }
+
+ /* Get the number of entries */
+ for (n = 1; n < loader->entries(); n++) {
+ if (loader->empty(loader->element(entries, n))) {
+ break;
+ }
+ }
+
+ /*
+ * on disk: 0 = magic, 1 = default, 2..n = entries
+ * on HMC: 0 = default, 1..m = entries
+ */
+ if (loader->loadparm >= n - 1) {
+ error_report("s390-ipl: Loadparm entry %d does not exists",
+ loader->loadparm);
+ exit(1);
+ }
+
+ return parse_program(loader->element(entries, loader->loadparm + 1),
+ loader);
+}
+
+static uint64_t parse_mbr(BlockDriverState *bs, uint8_t loadparm,
+ uint16_t blk_size)
+{
+ FCPMbr fmbr;
+ NewECKDMbr nembr;
+ ECKDMbr embr;
+
+ int ret;
+
+ loader.bs = bs;
+ loader.blk_size = be16_to_cpu(fmbr.blockptr.u.fcp.size);
+ loader.loadparm = loadparm;
+ loader.blk_size = blk_size;
+
+
+ /* is this a scsi bootmap ? */
+ bdrv_pread(bs, 0, &fmbr, sizeof(fmbr));
+ if (magic_ok(&fmbr.magic)) {
+ loader.blockno = getblockno_fcp;
+ loader.offset = getoffset_fcp;
+ loader.size = getsize_fcp;
+ loader.empty = getempty_fcp;
+ loader.element = getelement_fcp;
+ loader.entries = entries_fcp;
+ fprintf(stderr, "s390-ipl: detected FCP bootmap\n");
+ return parse_program_table(&fmbr.blockptr, &loader);
+ }
+
+ /* lets try several ECKD bootloader types */
+ loader.heads = bs->heads;
+ loader.secs = bs->secs;
+ loader.blockno = getblockno_eckd;
+ loader.offset = getoffset_eckd;
+ loader.size = getsize_eckd;
+ loader.empty = getempty_eckd;
+ loader.element = getelement_neckd;
+ loader.entries = entries_neckd;
+
+ /* new dasd bootmap for CDL*/
+ bdrv_pread(bs, blk_size + 92, &nembr, sizeof(nembr));
+ if (magic_ok(&nembr.magic)) {
+ if (nembr.dev_type != DEV_TYPE_ECKD) {
+ return -1;
+ }
+ fprintf(stderr, "s390-ipl: detected new CDL bootmap\n");
+ return parse_program_table(&nembr.blockptr, &loader);
+ }
+ /* new dasd bootmap for LDL */
+ bdrv_pread(bs, 112, &nembr, sizeof(nembr));
+ if (magic_ok(&nembr.magic)) {
+ if (nembr.dev_type != DEV_TYPE_ECKD) {
+ return -1;
+ }
+ fprintf(stderr, "s390-ipl: detected new LDL bootmap\n");
+ return parse_program_table(&nembr.blockptr, &loader);
+ }
+
+ /* classic cdl dasd bootmap, unfortunately there is no magic available */
+ loader.element = getelement_eckd;
+ loader.entries = entries_eckd;
+
+ bdrv_pread(bs, blk_size + 4, &embr, sizeof(embr));
+ fprintf(stderr, "s390-ipl: trying old CDL bootmap\n");
+ ret = parse_program_table((BlockPtr *) &embr.blockptr, &loader);
+ if (ret == -1) {
+ /* last chance, classic ldl dasd bootmap */
+ bdrv_pread(bs, blk_size, &embr, sizeof(embr));
+ fprintf(stderr, "s390-ipl: CDL failed. trying old LDL bootmap\n");
+ ret = parse_program_table((BlockPtr *) &embr.blockptr, &loader);
+ }
+ return ret;
+}
+
+/*
+ * looks at the program tables written by the boot loader to load
+ * everything which is specified in the bootmap
+ */
+static unsigned long load_from_disk(BlockDriverState *bs, uint32_t loadparm,
+ uint32_t blk_size)
+{
+ uint64_t address;
+
+ address = parse_mbr(bs, loadparm, blk_size);
+ if (address == -1) {
+ return -1;
+ }
+ return address & 0x7fffffff;
+}
+
+static void s390_ipl_disk(const char *id, uint32_t loadparm)
+{
+ uint64_t addr = -1UL;
+ DeviceState *dev;
+ VirtIOS390Device *vdev;
+ DriveInfo *drive;
+ CPUS390XState *env;
+
+ /* If no disk is specified, use the first one */
+ if (!id) {
+ /*
+ * libvirt and friends use if=none to create the device itself,
+ * standard command line without an if= will result in virtio.
+ * Lets search both types for a device
+ */
+ drive = drive_get_by_index(IF_NONE, 0);
+ if (!drive) {
+ drive = drive_get_by_index(IF_VIRTIO, 0);
+ }
+ if (drive) {
+ dev = bdrv_get_attached_dev(drive->bdrv);
+ if (!dev) {
+ error_report("s390-ipl: First drive has no attached device");
+ return;
+ }
+ } else {
+ error_report("s390-ipl: No bootable disk found");
+ return;
+ }
+ } else {
+ dev = qdev_find_recursive(sysbus_get_default(), id);
+ if (!dev) {
+ error_report("s390-ipl: Unable to find '%s'", id);
+ return;
+ }
+ }
+
+ vdev = DO_UPCAST(VirtIOS390Device, qdev, dev);
+ if (!vdev->block.bs) {
+ error_report("s390-ipl: '%s' is not a block device", id);
+ return;
+ }
+
+ addr = load_from_disk(vdev->block.bs, loadparm,
+ vdev->block.logical_block_size);
+ if (addr == -1) {
+ error_report("s390-ipl: %s id \"%s\" does not contain a valid bootmap",
+ qdev_fw_name(dev), id ? id : "");
+ return;
+ }
+
+ env = qemu_get_cpu(0);
+ env->psw.addr = addr;
+ env->psw.mask = 0x0000000180000000ULL;
+ s390_add_running_cpu(env);
+}
+
+static void s390_ipl_kernel(void)
+{
+ CPUS390XState *env;
+ env = qemu_get_cpu(0);
+ /*
+ * we can not rely on the ELF entry point, since up to 3.2 this
+ * value was 0x800 (the SALIPL loader) and it wont work. For
+ * all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine.
+ */
+ env->psw.addr = KERN_IMAGE_START;
+ env->psw.mask = 0x0000000180000000ULL;
+ s390_add_running_cpu(env);
+}
+
+typedef struct {
+ SysBusDevice busdev;
+ uint32_t loadparm;
+ char *iplid;
+} S390IPLState;
+
+static int s390_ipl_init(SysBusDevice *dev)
+{
+ return 0;
+}
+
+static Property s390_ipl_properties[] = {
+ DEFINE_PROP_UINT32("loadparm", S390IPLState, loadparm, 0),
+ DEFINE_PROP_STRING("iplid", S390IPLState, iplid),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
+static void s390_ipl_reset(DeviceState *s390ipl)
+{
+ S390IPLState *iplstate = container_of(s390ipl, S390IPLState, busdev.qdev);
+
+ /* if the user provides a kernel, we dont load from disk */
+ if (rom_ptr(KERN_IMAGE_START)) {
+ s390_ipl_kernel();
+ } else {
+ s390_ipl_disk(iplstate->iplid, iplstate->loadparm);
+ }
+}
+
+static void s390_ipl_class_init(ObjectClass *klass, void *data)
+{
+ DeviceClass *dc = DEVICE_CLASS(klass);
+ SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
+
+ k->init = s390_ipl_init;
+ dc->props = s390_ipl_properties;
+ dc->reset = s390_ipl_reset;
+}
+
+static TypeInfo s390_ipl_info = {
+ .class_init = s390_ipl_class_init,
+ .parent = TYPE_SYS_BUS_DEVICE,
+ .name = "s390-ipl",
+ .instance_size = sizeof(S390IPLState),
+};
+
+static void s390_register_ipl(void)
+{
+ type_register_static(&s390_ipl_info);
+}
+
+type_init(s390_register_ipl)
diff --git a/hw/s390-loader.h b/hw/s390-loader.h
new file mode 100644
index 0000000..347c07f
--- /dev/null
+++ b/hw/s390-loader.h
@@ -0,0 +1,81 @@
+#include "blockdev.h"
+#include "block_int.h"
+#include "cpu.h"
+
+typedef struct {
+ uint64_t blockno;
+ uint16_t size;
+ uint16_t blockct;
+ uint8_t reserved[4];
+} __attribute__ ((packed)) FCPBlockPtr;
+
+typedef struct {
+ uint16_t cyls;
+ uint16_t heads;
+ uint8_t secs;
+ uint16_t block_size;
+ uint8_t count;
+ uint8_t reserved[8];
+} __attribute__ ((packed)) NECKDBlockPtr;
+
+typedef struct {
+ uint16_t cyls;
+ uint16_t heads;
+ uint8_t secs;
+ uint16_t block_size;
+ uint8_t count;
+} __attribute__ ((packed)) ECKDBlockPtr;
+
+
+typedef struct {
+ union {
+ NECKDBlockPtr neckd;
+ ECKDBlockPtr eckd;
+ FCPBlockPtr fcp;
+ } u;
+} __attribute__ ((packed)) BlockPtr;
+
+typedef struct {
+ BlockPtr blkptr;
+ uint8_t pad[7];
+ uint8_t component_type;
+ union {
+ uint64_t load_address;
+ uint64_t load_psw;
+ } address;
+} __attribute((packed)) ComponentEntry;
+
+typedef struct {
+ uint8_t magic[4];
+ uint8_t type;
+ uint8_t reserved[27];
+} __attribute((packed)) ComponentHeader;
+
+typedef struct {
+ ECKDBlockPtr blockptr;
+} __attribute__ ((packed)) ECKDMbr;
+
+typedef struct {
+ char magic[4];
+ uint8_t version;
+ uint8_t bp_type;
+#define DEV_TYPE_ECKD 0x00
+#define DEV_TYPE_FBA 0x01
+ uint8_t dev_type;
+ uint8_t flags;
+ BlockPtr blockptr;
+ uint8_t reserved[8];
+} __attribute__ ((packed)) NewECKDMbr;
+
+typedef struct {
+ char magic[4];
+ uint32_t version_id;
+ uint8_t reserved[8];
+ BlockPtr blockptr;
+} __attribute__ ((packed)) FCPMbr;
+
+#define component_execute 0x01
+#define component_load 0x02
+
+#define component_header_ipl 0x00
+#define component_header_dump 0x01
diff --git a/hw/s390-virtio.c b/hw/s390-virtio.c
index 2c5820f..d68e25f 100644
--- a/hw/s390-virtio.c
+++ b/hw/s390-virtio.c
@@ -20,6 +20,7 @@
#include "hw.h"
#include "block.h"
#include "blockdev.h"
+#include "block_int.h"
#include "sysemu.h"
#include "net.h"
#include "boards.h"
@@ -33,6 +34,7 @@
#include "hw/s390-virtio-bus.h"
+#include "hw/s390-loader.h"
//#define DEBUG_S390
#ifdef DEBUG_S390
@@ -221,11 +223,7 @@ static void s390_init(ram_addr_t my_ram_size,
tmp_env->storage_keys = storage_keys;
}
- /* One CPU has to run */
- s390_add_running_cpu(env);
-
if (kernel_filename) {
-
kernel_size = load_elf(kernel_filename, NULL, NULL, NULL, NULL,
NULL, 1, ELF_MACHINE, 0);
if (kernel_size == -1UL) {
@@ -236,36 +234,6 @@ static void s390_init(ram_addr_t my_ram_size,
kernel_filename);
exit(1);
}
- /*
- * we can not rely on the ELF entry point, since up to 3.2 this
- * value was 0x800 (the SALIPL loader) and it wont work. For
- * all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine.
- */
- env->psw.addr = KERN_IMAGE_START;
- env->psw.mask = 0x0000000180000000ULL;
- } else {
- ram_addr_t bios_size = 0;
- char *bios_filename;
-
- /* Load zipl bootloader */
- if (bios_name == NULL) {
- bios_name = ZIPL_FILENAME;
- }
-
- bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
- bios_size = load_image_targphys(bios_filename, ZIPL_LOAD_ADDR, 4096);
- g_free(bios_filename);
-
- if ((long)bios_size < 0) {
- hw_error("could not load bootloader '%s'\n", bios_name);
- }
-
- if (bios_size > 4096) {
- hw_error("stage1 bootloader is > 4k\n");
- }
-
- env->psw.addr = ZIPL_START;
- env->psw.mask = 0x0000000180000000ULL;
}
if (initrd_filename) {
diff --git a/vl.c b/vl.c
index e724084..f8341c9 100644
--- a/vl.c
+++ b/vl.c
@@ -273,6 +273,7 @@ static int default_monitor = 1;
static int default_floppy = 1;
static int default_cdrom = 1;
static int default_sdcard = 1;
+static int default_loader = 1;
static struct {
const char *driver;
@@ -288,6 +289,7 @@ static struct {
{ .driver = "virtio-serial-pci", .flag = &default_virtcon },
{ .driver = "virtio-serial-s390", .flag = &default_virtcon },
{ .driver = "virtio-serial", .flag = &default_virtcon },
+ { .driver = "s390-ipl", .flag = &default_loader },
};
static void res_free(void)
@@ -3114,6 +3116,7 @@ int main(int argc, char **argv, char **envp)
default_floppy = 0;
default_cdrom = 0;
default_sdcard = 0;
+ default_loader = 0;
vga_model = "none";
break;
case QEMU_OPTION_xen_domid:
@@ -3268,6 +3271,10 @@ int main(int argc, char **argv, char **envp)
qemu_opts_foreach(qemu_find_opts("device"), default_driver_check, NULL, 0);
qemu_opts_foreach(qemu_find_opts("global"), default_driver_check, NULL, 0);
+ if (default_loader && strcmp(machine->alias, "s390") == 0) {
+ qdev_init_nofail(qdev_create(NULL, "s390-ipl"));
+ }
+
if (machine->no_serial) {
default_serial = 0;
}
--
1.7.9.6
- [Qemu-devel] [PATCH 0/1] RFC: ipl device for s390, Christian Borntraeger, 2012/05/04
- [Qemu-devel] [PATCH 1/1] s390: IPL device for s390,
Christian Borntraeger <=
- Re: [Qemu-devel] [PATCH 1/1] s390: IPL device for s390, Alexander Graf, 2012/05/04
- Re: [Qemu-devel] [PATCH 1/1] s390: IPL device for s390, Christian Borntraeger, 2012/05/04
- Re: [Qemu-devel] [PATCH 1/1] s390: IPL device for s390, Christian Borntraeger, 2012/05/04
- Re: [Qemu-devel] [PATCH 1/1] s390: IPL device for s390, Alexander Graf, 2012/05/04
- Re: [Qemu-devel] [PATCH 1/1] s390: IPL device for s390, Christian Borntraeger, 2012/05/08
- Re: [Qemu-devel] [PATCH 1/1] s390: IPL device for s390, Alexander Graf, 2012/05/08
- Re: [Qemu-devel] [PATCH 1/1] s390: IPL device for s390, Christian Borntraeger, 2012/05/08
- Re: [Qemu-devel] [PATCH 1/1] s390: IPL device for s390, Alexander Graf, 2012/05/08
- Re: [Qemu-devel] [PATCH 1/1] s390: IPL device for s390, Christian Borntraeger, 2012/05/08
- Re: [Qemu-devel] [PATCH 1/1] s390: IPL device for s390, Alexander Graf, 2012/05/08