qemu/hw/s390x/ipl.c
<<
>>
Prefs
   1/*
   2 * bootloader support
   3 *
   4 * Copyright IBM, Corp. 2012
   5 *
   6 * Authors:
   7 *  Christian Borntraeger <borntraeger@de.ibm.com>
   8 *
   9 * This work is licensed under the terms of the GNU GPL, version 2 or (at your
  10 * option) any later version.  See the COPYING file in the top-level directory.
  11 *
  12 */
  13
  14#include "sysemu/sysemu.h"
  15#include "cpu.h"
  16#include "elf.h"
  17#include "hw/loader.h"
  18#include "hw/sysbus.h"
  19#include "hw/s390x/virtio-ccw.h"
  20#include "hw/s390x/css.h"
  21
  22#define KERN_IMAGE_START                0x010000UL
  23#define KERN_PARM_AREA                  0x010480UL
  24#define INITRD_START                    0x800000UL
  25#define INITRD_PARM_START               0x010408UL
  26#define INITRD_PARM_SIZE                0x010410UL
  27#define PARMFILE_START                  0x001000UL
  28#define ZIPL_IMAGE_START                0x009000UL
  29#define IPL_PSW_MASK                    (PSW_MASK_32 | PSW_MASK_64)
  30
  31#define TYPE_S390_IPL "s390-ipl"
  32#define S390_IPL(obj) \
  33    OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
  34#if 0
  35#define S390_IPL_CLASS(klass) \
  36    OBJECT_CLASS_CHECK(S390IPLState, (klass), TYPE_S390_IPL)
  37#define S390_IPL_GET_CLASS(obj) \
  38    OBJECT_GET_CLASS(S390IPLState, (obj), TYPE_S390_IPL)
  39#endif
  40
  41typedef struct S390IPLClass {
  42    /*< private >*/
  43    SysBusDeviceClass parent_class;
  44    /*< public >*/
  45
  46    void (*parent_reset) (SysBusDevice *dev);
  47} S390IPLClass;
  48
  49typedef struct S390IPLState {
  50    /*< private >*/
  51    SysBusDevice parent_obj;
  52    uint64_t start_addr;
  53
  54    /*< public >*/
  55    char *kernel;
  56    char *initrd;
  57    char *cmdline;
  58    char *firmware;
  59} S390IPLState;
  60
  61
  62static int s390_ipl_init(SysBusDevice *dev)
  63{
  64    S390IPLState *ipl = S390_IPL(dev);
  65    int kernel_size;
  66
  67    if (!ipl->kernel) {
  68        int bios_size;
  69        char *bios_filename;
  70
  71        /* Load zipl bootloader */
  72        if (bios_name == NULL) {
  73            bios_name = ipl->firmware;
  74        }
  75
  76        bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
  77        if (bios_filename == NULL) {
  78            hw_error("could not find stage1 bootloader\n");
  79        }
  80
  81        bios_size = load_elf(bios_filename, NULL, NULL, &ipl->start_addr, NULL,
  82                             NULL, 1, ELF_MACHINE, 0);
  83        if (bios_size == -1) {
  84            bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START,
  85                                            4096);
  86            ipl->start_addr = ZIPL_IMAGE_START;
  87            if (bios_size > 4096) {
  88                hw_error("stage1 bootloader is > 4k\n");
  89            }
  90        }
  91        g_free(bios_filename);
  92
  93        if (bios_size == -1) {
  94            hw_error("could not load bootloader '%s'\n", bios_name);
  95        }
  96        return 0;
  97    } else {
  98        kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, NULL,
  99                               NULL, 1, ELF_MACHINE, 0);
 100        if (kernel_size == -1) {
 101            kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
 102        }
 103        if (kernel_size == -1) {
 104            fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
 105            return -1;
 106        }
 107        /* we have to overwrite values in the kernel image, which are "rom" */
 108        strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);
 109
 110        /*
 111         * we can not rely on the ELF entry point, since up to 3.2 this
 112         * value was 0x800 (the SALIPL loader) and it wont work. For
 113         * all (Linux) cases 0x10000 (KERN_IMAGE_START) should be fine.
 114         */
 115        ipl->start_addr = KERN_IMAGE_START;
 116    }
 117    if (ipl->initrd) {
 118        ram_addr_t initrd_offset;
 119        int initrd_size;
 120
 121        initrd_offset = INITRD_START;
 122        while (kernel_size + 0x100000 > initrd_offset) {
 123            initrd_offset += 0x100000;
 124        }
 125        initrd_size = load_image_targphys(ipl->initrd, initrd_offset,
 126                                          ram_size - initrd_offset);
 127        if (initrd_size == -1) {
 128            fprintf(stderr, "qemu: could not load initrd '%s'\n", ipl->initrd);
 129            exit(1);
 130        }
 131
 132        /* we have to overwrite values in the kernel image, which are "rom" */
 133        stq_p(rom_ptr(INITRD_PARM_START), initrd_offset);
 134        stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size);
 135    }
 136
 137    return 0;
 138}
 139
 140static Property s390_ipl_properties[] = {
 141    DEFINE_PROP_STRING("kernel", S390IPLState, kernel),
 142    DEFINE_PROP_STRING("initrd", S390IPLState, initrd),
 143    DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
 144    DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
 145    DEFINE_PROP_END_OF_LIST(),
 146};
 147
 148static void s390_ipl_reset(DeviceState *dev)
 149{
 150    S390IPLState *ipl = S390_IPL(dev);
 151    S390CPU *cpu = S390_CPU(qemu_get_cpu(0));
 152    CPUS390XState *env = &cpu->env;
 153
 154    env->psw.addr = ipl->start_addr;
 155    env->psw.mask = IPL_PSW_MASK;
 156
 157    if (!ipl->kernel) {
 158        /* Tell firmware, if there is a preferred boot device */
 159        env->regs[7] = -1;
 160        DeviceState *dev_st = get_boot_device(0);
 161        if (dev_st) {
 162            VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast(
 163                OBJECT(qdev_get_parent_bus(dev_st)->parent),
 164                TYPE_VIRTIO_CCW_DEVICE);
 165
 166            if (ccw_dev) {
 167                env->regs[7] = ccw_dev->sch->cssid << 24 |
 168                               ccw_dev->sch->ssid << 16 |
 169                               ccw_dev->sch->devno;
 170            }
 171        }
 172    }
 173
 174    s390_add_running_cpu(cpu);
 175}
 176
 177static void s390_ipl_class_init(ObjectClass *klass, void *data)
 178{
 179    DeviceClass *dc = DEVICE_CLASS(klass);
 180    SysBusDeviceClass *k = SYS_BUS_DEVICE_CLASS(klass);
 181
 182    k->init = s390_ipl_init;
 183    dc->props = s390_ipl_properties;
 184    dc->reset = s390_ipl_reset;
 185    dc->no_user = 1;
 186}
 187
 188static const TypeInfo s390_ipl_info = {
 189    .class_init = s390_ipl_class_init,
 190    .parent = TYPE_SYS_BUS_DEVICE,
 191    .name  = "s390-ipl",
 192    .instance_size  = sizeof(S390IPLState),
 193};
 194
 195static void s390_ipl_register_types(void)
 196{
 197    type_register_static(&s390_ipl_info);
 198}
 199
 200type_init(s390_ipl_register_types)
 201