diff mbox

[12/13] Add zipl bootloader interpreter

Message ID 1259083781-14642-13-git-send-email-agraf@suse.de
State New
Headers show

Commit Message

Alexander Graf Nov. 24, 2009, 5:29 p.m. UTC
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 <agraf@suse.de>
---
 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

Comments

Anthony Liguori Nov. 24, 2009, 6:53 p.m. UTC | #1
Alexander Graf wrote:
> 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.
>   

This is a bit unfortunate.  Wouldn't it be better to write a custom 
version of zipl that ran in the guest?

This is like implementing grub in qemu (or pygrub in Xen).  The level of 
security exposure this introduces is really scary.

Regards,

Anthony Liguori
Alexander Graf Nov. 24, 2009, 6:56 p.m. UTC | #2
On 24.11.2009, at 19:53, Anthony Liguori wrote:

> Alexander Graf wrote:
>> 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.
>>  
> 
> This is a bit unfortunate.  Wouldn't it be better to write a custom version of zipl that ran in the guest?

Yeah, I've been struggling quite a bit with this myself. Writing a custom version that runs in the guest means we have to create

1) extboot
2) input backdoor

Or implement virtio in that custom version. That sounds like a pretty huge project.

So I decided to go for the easy way for now and hopefully migrate to an in-guest version later.


Alex
Anthony Liguori Nov. 24, 2009, 7:26 p.m. UTC | #3
Alexander Graf wrote:
> On 24.11.2009, at 19:53, Anthony Liguori wrote:
>
>   
>> Alexander Graf wrote:
>>     
>>> 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.
>>>  
>>>       
>> This is a bit unfortunate.  Wouldn't it be better to write a custom version of zipl that ran in the guest?
>>     
>
> Yeah, I've been struggling quite a bit with this myself. Writing a custom version that runs in the guest means we have to create
>
> 1) extboot
> 2) input backdoor
>
> Or implement virtio in that custom version. That sounds like a pretty huge project.
>
> So I decided to go for the easy way for now and hopefully migrate to an in-guest version later.
>   

Can't you just use kboot?

Use a kernel loader to load the kboot module/initrd, include kboot as 
our firmware, then let kboot do the magic to launch the real linux 
kernel from disk.

Regards,

Anthony Liguori
Alexander Graf Nov. 24, 2009, 7:29 p.m. UTC | #4
On 24.11.2009, at 20:26, Anthony Liguori wrote:

> Alexander Graf wrote:
>> On 24.11.2009, at 19:53, Anthony Liguori wrote:
>> 
>>  
>>> Alexander Graf wrote:
>>>    
>>>> 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.
>>>>       
>>> This is a bit unfortunate.  Wouldn't it be better to write a custom version of zipl that ran in the guest?
>>>    
>> 
>> Yeah, I've been struggling quite a bit with this myself. Writing a custom version that runs in the guest means we have to create
>> 
>> 1) extboot
>> 2) input backdoor
>> 
>> Or implement virtio in that custom version. That sounds like a pretty huge project.
>> 
>> So I decided to go for the easy way for now and hopefully migrate to an in-guest version later.
>>  
> 
> Can't you just use kboot?
> 
> Use a kernel loader to load the kboot module/initrd, include kboot as our firmware, then let kboot do the magic to launch the real linux kernel from disk.

Hm, so we'd have to rely on kexec working properly? I've seen how badly that turned out on the PS3. I guess I'd rather write a virtio implementation :-).

Alex
Mark Williamson Nov. 24, 2009, 8:39 p.m. UTC | #5
> > Can't you just use kboot?
> >
> > Use a kernel loader to load the kboot module/initrd, include kboot as our
> > firmware, then let kboot do the magic to launch the real linux kernel
> > from disk.
> 
> Hm, so we'd have to rely on kexec working properly? I've seen how badly
>  that turned out on the PS3. I guess I'd rather write a virtio
>  implementation :-).

Way back in the mists of time (uh, something like that 2004-05) I had some 
discussions with some of the S390 people about using kboot for more flexible 
boot, since it tallied with their interests.  Although at that point I had the 
impression that zipl was restricted to only one boot option anyhow, so if it 
can now choose from a list then maybe they just enhanced what they had.

Anthony, you might remember these discussions?  I don't know if they went 
anywhere with it.

Cheers,
Mark
Anthony Liguori Nov. 24, 2009, 9:10 p.m. UTC | #6
Hi Mark!

Mark Williamson wrote:
> Way back in the mists of time (uh, something like that 2004-05) I had some 
> discussions with some of the S390 people about using kboot for more flexible 
> boot, since it tallied with their interests.  Although at that point I had the 
> impression that zipl was restricted to only one boot option anyhow, so if it 
> can now choose from a list then maybe they just enhanced what they had.
>
> Anthony, you might remember these discussions?  I don't know if they went 
> anywhere with it.
>   

I do, that's why I brought it up.  AFAICT, there hasn't been a lot of 
progress with kboot.  Carsten or Alex would probably know better if 
anyone is actually using it on s390s.

Regards,

Anthony Liguori

> Cheers,
> Mark
>
Alexander Graf Nov. 25, 2009, 8:38 a.m. UTC | #7
On 25.11.2009, at 09:35, Hannes Reinecke wrote:

> On Tue, Nov 24, 2009 at 03:10:24PM -0600, Anthony Liguori wrote:
>> Hi Mark!
>> 
>> Mark Williamson wrote:
>>> Way back in the mists of time (uh, something like that 2004-05) I had some 
>>> discussions with some of the S390 people about using kboot for more 
>>> flexible boot, since it tallied with their interests.  Although at that 
>>> point I had the impression that zipl was restricted to only one boot 
>>> option anyhow, so if it can now choose from a list then maybe they just 
>>> enhanced what they had.
>>> 
>>> Anthony, you might remember these discussions?  I don't know if they went 
>>> anywhere with it.
>>> 
>> 
>> I do, that's why I brought it up.  AFAICT, there hasn't been a lot of 
>> progress with kboot.  Carsten or Alex would probably know better if anyone 
>> is actually using it on s390s.
>> 
> No, not to my knowledge.
> 
> There have been some discussion as if kboot would be beneficial here, but
> especially for s390 it doesn't make a lot of sense.

Thanks for clarifying this!

> kboot is okay for scenarios where you have a _lot_ of modules in the default
> kernel, but need only a very small subset to get the system bootstrapped.
> Then you can built a kboot kernel with the driver subset for bootstrapping
> and have that loading the 'normal' kernel which then loads all remaining
> modules.
> 
> If you have (like s390) only about 8 driver modules to care about it's
> pretty much pointless as the kboot kernel will have the same configuration
> than the normal kernel. So you'll end up with loading the same kernel twice.
> 
> And won't change the situation here, as you still have to do the initial
> bootstrap somehow. And as this should be quite close to the original system
> you'll end up having to support zipl anyway.

Yes, you'd basically end up writing a zipl interpreter inside the initrd of kboot. Bleks.

> So back to the zipl question, it might be an idea to support initially
> the SCSI disk layout only. This has the advantage of being far simpler
> as the DASD disk layout and should be pretty straightforward to handle.

That's exactly what my zipl patch does. DASD has 4k sector sizes, so DASD layouts are not supported. Fortunately virtio in qemu only exports 512 bytes sector sizes for now, so when you install the VM onto virtio, you're good.

Alex
Carsten Otte Nov. 25, 2009, 8:59 a.m. UTC | #8
Anthony Liguori wrote:
> This is a bit unfortunate.  Wouldn't it be better to write a custom 
> version of zipl that ran in the guest?
> 
> This is like implementing grub in qemu (or pygrub in Xen).  The level of 
> security exposure this introduces is really scary.
Oh that's really the wrong way to see it. We don't have bios, and 
therefore we need _some_ functionality to start up initial code on the 
disk. The interface that Alex is implementing is also used by the real 
hardware to load from disk in case a scsi/fcp volume is used, and it is 
part of the published machine architecture. That's very unlike grub.
Carsten Otte Nov. 25, 2009, 9:58 a.m. UTC | #9
Anthony Liguori wrote:
> Can't you just use kboot?
> 
> Use a kernel loader to load the kboot module/initrd, include kboot as 
> our firmware, then let kboot do the magic to launch the real linux 
> kernel from disk.
Hehe, and how would you load that initial kernel if you don't have bios?
No matter what, you do need a form of initial loading. And doing it the 
architected way is just plain right.
Carsten Otte Nov. 25, 2009, 10:02 a.m. UTC | #10
Anthony Liguori wrote:
> I do, that's why I brought it up.  AFAICT, there hasn't been a lot of 
> progress with kboot.  Carsten or Alex would probably know better if 
> anyone is actually using it on s390s.
I fail to see how kboot would solve this problem.
Carsten Otte Nov. 25, 2009, 10:09 a.m. UTC | #11
Hannes Reinecke wrote:
> So back to the zipl question, it might be an idea to support initially
> the SCSI disk layout only. This has the advantage of being far simpler
> as the DASD disk layout and should be pretty straightforward to handle.
Issue is, the dasd disk layout is made for dasds. For eckd, the boot 
code assumes variable blocksize, and the loader uses channel programs. 
For both eckd and fba volumes, it assumes IBM disklabels and FICON 
channel programs. Virt-IO disks do have an msdos-style partition table 
as opposed to this.
That said, you'd need proper virtualiztion of a ficon dasd so that the 
guest could possibly run the dasd boot code. Virt-IO disks otoh are 
fixed block, have an msdos-style partition table, no channel programs, 
and thus look just like scsi disks with a different interface. 
Therefore, I think scsi boot is right for virt-IO disks. And zipl does 
already create this layout in the version found in Sles11 and FC12.
diff mbox

Patch

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 <agraf@suse.de>
+ *
+ * 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;