ipl.c 13.8 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13
/*
 * bootloader support
 *
 * Copyright IBM, Corp. 2012
 *
 * Authors:
 *  Christian Borntraeger <borntraeger@de.ibm.com>
 *
 * This work is licensed under the terms of the GNU GPL, version 2 or (at your
 * option) any later version.  See the COPYING file in the top-level directory.
 *
 */

P
Peter Maydell 已提交
14
#include "qemu/osdep.h"
15
#include "qapi/error.h"
16 17 18 19
#include "sysemu/sysemu.h"
#include "cpu.h"
#include "elf.h"
#include "hw/loader.h"
20
#include "hw/boards.h"
D
Dominik Dingel 已提交
21 22
#include "hw/s390x/virtio-ccw.h"
#include "hw/s390x/css.h"
23
#include "hw/s390x/ebcdic.h"
24
#include "ipl.h"
F
Farhan Ali 已提交
25
#include "qemu/error-report.h"
26 27 28 29 30 31 32 33 34 35

#define KERN_IMAGE_START                0x010000UL
#define KERN_PARM_AREA                  0x010480UL
#define INITRD_START                    0x800000UL
#define INITRD_PARM_START               0x010408UL
#define INITRD_PARM_SIZE                0x010410UL
#define PARMFILE_START                  0x001000UL
#define ZIPL_IMAGE_START                0x009000UL
#define IPL_PSW_MASK                    (PSW_MASK_32 | PSW_MASK_64)

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
static bool iplb_extended_needed(void *opaque)
{
    S390IPLState *ipl = S390_IPL(object_resolve_path(TYPE_S390_IPL, NULL));

    return ipl->iplbext_migration;
}

static const VMStateDescription vmstate_iplb_extended = {
    .name = "ipl/iplb_extended",
    .version_id = 0,
    .minimum_version_id = 0,
    .needed = iplb_extended_needed,
    .fields = (VMStateField[]) {
        VMSTATE_UINT8_ARRAY(reserved_ext, IplParameterBlock, 4096 - 200),
        VMSTATE_END_OF_LIST()
    }
};

54 55 56 57 58 59 60 61 62
static const VMStateDescription vmstate_iplb = {
    .name = "ipl/iplb",
    .version_id = 0,
    .minimum_version_id = 0,
    .fields = (VMStateField[]) {
        VMSTATE_UINT8_ARRAY(reserved1, IplParameterBlock, 110),
        VMSTATE_UINT16(devno, IplParameterBlock),
        VMSTATE_UINT8_ARRAY(reserved2, IplParameterBlock, 88),
        VMSTATE_END_OF_LIST()
63 64 65 66
    },
    .subsections = (const VMStateDescription*[]) {
        &vmstate_iplb_extended,
        NULL
67 68 69 70 71 72 73 74
    }
};

static const VMStateDescription vmstate_ipl = {
    .name = "ipl",
    .version_id = 0,
    .minimum_version_id = 0,
    .fields = (VMStateField[]) {
75 76
        VMSTATE_UINT64(compat_start_addr, S390IPLState),
        VMSTATE_UINT64(compat_bios_start_addr, S390IPLState),
77 78 79 80 81 82 83 84
        VMSTATE_STRUCT(iplb, S390IPLState, 0, vmstate_iplb, IplParameterBlock),
        VMSTATE_BOOL(iplb_valid, S390IPLState),
        VMSTATE_UINT8(cssid, S390IPLState),
        VMSTATE_UINT8(ssid, S390IPLState),
        VMSTATE_UINT16(devno, S390IPLState),
        VMSTATE_END_OF_LIST()
     }
};
85

86 87 88 89 90
static S390IPLState *get_ipl_device(void)
{
    return S390_IPL(object_resolve_path_type("", TYPE_S390_IPL, NULL));
}

91 92 93 94 95 96 97 98 99 100
static uint64_t bios_translate_addr(void *opaque, uint64_t srcaddr)
{
    uint64_t dstaddr = *(uint64_t *) opaque;
    /*
     * Assuming that our s390-ccw.img was linked for starting at address 0,
     * we can simply add the destination address for the final location
     */
    return srcaddr + dstaddr;
}

101
static void s390_ipl_realize(DeviceState *dev, Error **errp)
102 103
{
    S390IPLState *ipl = S390_IPL(dev);
104
    uint64_t pentry = KERN_IMAGE_START;
105
    int kernel_size;
106
    Error *err = NULL;
107

108 109
    int bios_size;
    char *bios_filename;
110

111 112 113 114 115
    /*
     * Always load the bios if it was enforced,
     * even if an external kernel has been defined.
     */
    if (!ipl->kernel || ipl->enforce_bios) {
116 117
        uint64_t fwbase = (MIN(ram_size, 0x80000000U) - 0x200000) & ~0xffffUL;

118
        if (bios_name == NULL) {
119
            bios_name = ipl->firmware;
120 121 122
        }

        bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
D
Dominik Dingel 已提交
123
        if (bios_filename == NULL) {
124
            error_setg(&err, "could not find stage1 bootloader");
125
            goto error;
D
Dominik Dingel 已提交
126 127
        }

128 129
        bios_size = load_elf(bios_filename, bios_translate_addr, &fwbase,
                             &ipl->bios_start_addr, NULL, NULL, 1,
130
                             EM_S390, 0, 0);
131 132 133 134
        if (bios_size > 0) {
            /* Adjust ELF start address to final location */
            ipl->bios_start_addr += fwbase;
        } else {
135
            /* Try to load non-ELF file */
A
Alexander Graf 已提交
136 137
            bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START,
                                            4096);
138
            ipl->bios_start_addr = ZIPL_IMAGE_START;
A
Alexander Graf 已提交
139
        }
140 141
        g_free(bios_filename);

142
        if (bios_size == -1) {
143
            error_setg(&err, "could not load bootloader '%s'", bios_name);
144
            goto error;
145
        }
146

147 148
        /* default boot target is the bios */
        ipl->start_addr = ipl->bios_start_addr;
149
    }
150

151 152
    if (ipl->kernel) {
        kernel_size = load_elf(ipl->kernel, NULL, NULL, &pentry, NULL,
153
                               NULL, 1, EM_S390, 0, 0);
154 155 156 157
        if (kernel_size < 0) {
            kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
        }
        if (kernel_size < 0) {
158
            error_setg(&err, "could not load kernel '%s'", ipl->kernel);
159
            goto error;
160
        }
161 162 163 164 165 166 167 168 169 170 171 172
        /*
         * Is it a Linux kernel (starting at 0x10000)? If yes, we fill in the
         * kernel parameters here as well. Note: For old kernels (up to 3.2)
         * we can not rely on the ELF entry point - it was 0x800 (the SALIPL
         * loader) and it won't work. For this case we force it to 0x10000, too.
         */
        if (pentry == KERN_IMAGE_START || pentry == 0x800) {
            ipl->start_addr = KERN_IMAGE_START;
            /* Overwrite parameters in the kernel image, which are "rom" */
            strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);
        } else {
            ipl->start_addr = pentry;
173 174
        }

175 176 177 178 179 180 181 182 183 184 185
        if (ipl->initrd) {
            ram_addr_t initrd_offset;
            int initrd_size;

            initrd_offset = INITRD_START;
            while (kernel_size + 0x100000 > initrd_offset) {
                initrd_offset += 0x100000;
            }
            initrd_size = load_image_targphys(ipl->initrd, initrd_offset,
                                              ram_size - initrd_offset);
            if (initrd_size == -1) {
186
                error_setg(&err, "could not load initrd '%s'", ipl->initrd);
187
                goto error;
188
            }
189

190 191 192 193 194 195 196 197
            /*
             * we have to overwrite values in the kernel image,
             * which are "rom"
             */
            stq_p(rom_ptr(INITRD_PARM_START), initrd_offset);
            stq_p(rom_ptr(INITRD_PARM_SIZE), initrd_size);
        }
    }
198 199 200 201 202 203 204
    /*
     * Don't ever use the migrated values, they could come from a different
     * BIOS and therefore don't work. But still migrate the values, so
     * QEMUs relying on it don't break.
     */
    ipl->compat_start_addr = ipl->start_addr;
    ipl->compat_bios_start_addr = ipl->bios_start_addr;
205
    qemu_register_reset(qdev_reset_all_fn, dev);
206
error:
207
    error_propagate(errp, err);
208 209 210 211 212 213
}

static Property s390_ipl_properties[] = {
    DEFINE_PROP_STRING("kernel", S390IPLState, kernel),
    DEFINE_PROP_STRING("initrd", S390IPLState, initrd),
    DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
214
    DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
215
    DEFINE_PROP_STRING("netboot_fw", S390IPLState, netboot_fw),
216
    DEFINE_PROP_BOOL("enforce_bios", S390IPLState, enforce_bios, false),
217 218
    DEFINE_PROP_BOOL("iplbext_migration", S390IPLState, iplbext_migration,
                     true),
219 220 221
    DEFINE_PROP_END_OF_LIST(),
};

222
static bool s390_gen_initial_iplb(S390IPLState *ipl)
223 224 225 226 227
{
    DeviceState *dev_st;

    dev_st = get_boot_device(0);
    if (dev_st) {
228 229
        VirtioCcwDevice *virtio_ccw_dev = (VirtioCcwDevice *)
            object_dynamic_cast(OBJECT(qdev_get_parent_bus(dev_st)->parent),
230
                TYPE_VIRTIO_CCW_DEVICE);
231 232
        SCSIDevice *sd = (SCSIDevice *) object_dynamic_cast(OBJECT(dev_st),
                                                            TYPE_SCSI_DEVICE);
F
Farhan Ali 已提交
233 234 235 236 237 238
        VirtIONet *vn = (VirtIONet *) object_dynamic_cast(OBJECT(dev_st),
                                                          TYPE_VIRTIO_NET);

        if (vn) {
            ipl->netboot = true;
        }
239 240 241
        if (virtio_ccw_dev) {
            CcwDevice *ccw_dev = CCW_DEVICE(virtio_ccw_dev);

242 243 244 245 246
            ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_CCW_LEN);
            ipl->iplb.blk0_len =
                cpu_to_be32(S390_IPLB_MIN_CCW_LEN - S390_IPLB_HEADER_LEN);
            ipl->iplb.pbt = S390_IPL_TYPE_CCW;
            ipl->iplb.ccw.devno = cpu_to_be16(ccw_dev->sch->devno);
247
            ipl->iplb.ccw.ssid = ccw_dev->sch->ssid & 3;
248 249 250 251
        } else if (sd) {
            SCSIBus *bus = scsi_bus_from_device(sd);
            VirtIOSCSI *vdev = container_of(bus, VirtIOSCSI, bus);
            VirtIOSCSICcw *scsi_ccw = container_of(vdev, VirtIOSCSICcw, vdev);
252 253 254 255 256 257 258
            CcwDevice *ccw_dev;

            ccw_dev = (CcwDevice *)object_dynamic_cast(OBJECT(scsi_ccw),
                                                       TYPE_CCW_DEVICE);
            if (!ccw_dev) {       /* It might be a PCI device instead */
                return false;
            }
259 260 261 262 263 264 265 266

            ipl->iplb.len = cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN);
            ipl->iplb.blk0_len =
                cpu_to_be32(S390_IPLB_MIN_QEMU_SCSI_LEN - S390_IPLB_HEADER_LEN);
            ipl->iplb.pbt = S390_IPL_TYPE_QEMU_SCSI;
            ipl->iplb.scsi.lun = cpu_to_be32(sd->lun);
            ipl->iplb.scsi.target = cpu_to_be16(sd->id);
            ipl->iplb.scsi.channel = cpu_to_be16(sd->channel);
267 268
            ipl->iplb.scsi.devno = cpu_to_be16(ccw_dev->sch->devno);
            ipl->iplb.scsi.ssid = ccw_dev->sch->ssid & 3;
269 270 271 272 273 274
        } else {
            return false; /* unknown device */
        }

        if (!s390_ipl_set_loadparm(ipl->iplb.loadparm)) {
            ipl->iplb.flags |= DIAG308_FLAGS_LP_VALID;
275
        }
276
        return true;
277 278
    }

279
    return false;
280 281
}

282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
int s390_ipl_set_loadparm(uint8_t *loadparm)
{
    MachineState *machine = MACHINE(qdev_get_machine());
    char *lp = object_property_get_str(OBJECT(machine), "loadparm", NULL);

    if (lp) {
        int i;

        /* lp is an uppercase string without leading/embedded spaces */
        for (i = 0; i < 8 && lp[i]; i++) {
            loadparm[i] = ascii2ebcdic[(uint8_t) lp[i]];
        }

        g_free(lp);
        return 0;
    }

    return -1;
}

F
Farhan Ali 已提交
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374
static int load_netboot_image(Error **errp)
{
    S390IPLState *ipl = get_ipl_device();
    char *netboot_filename;
    MemoryRegion *sysmem =  get_system_memory();
    MemoryRegion *mr = NULL;
    void *ram_ptr = NULL;
    int img_size = -1;

    mr = memory_region_find(sysmem, 0, 1).mr;
    if (!mr) {
        error_setg(errp, "Failed to find memory region at address 0");
        return -1;
    }

    ram_ptr = memory_region_get_ram_ptr(mr);
    if (!ram_ptr) {
        error_setg(errp, "No RAM found");
        goto unref_mr;
    }

    netboot_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, ipl->netboot_fw);
    if (netboot_filename == NULL) {
        error_setg(errp, "Could not find network bootloader");
        goto unref_mr;
    }

    img_size = load_elf_ram(netboot_filename, NULL, NULL, &ipl->start_addr,
                            NULL, NULL, 1, EM_S390, 0, 0, NULL, false);

    if (img_size < 0) {
        img_size = load_image_size(netboot_filename, ram_ptr, ram_size);
        ipl->start_addr = KERN_IMAGE_START;
    }

    if (img_size < 0) {
        error_setg(errp, "Failed to load network bootloader");
    }

    g_free(netboot_filename);

unref_mr:
    memory_region_unref(mr);
    return img_size;
}

static bool is_virtio_net_device(IplParameterBlock *iplb)
{
    uint8_t cssid;
    uint8_t ssid;
    uint16_t devno;
    uint16_t schid;
    SubchDev *sch = NULL;

    if (iplb->pbt != S390_IPL_TYPE_CCW) {
        return false;
    }

    devno = be16_to_cpu(iplb->ccw.devno);
    ssid = iplb->ccw.ssid & 3;

    for (schid = 0; schid < MAX_SCHID; schid++) {
        for (cssid = 0; cssid < MAX_CSSID; cssid++) {
            sch = css_find_subch(1, cssid, ssid, schid);

            if (sch && sch->devno == devno) {
                return sch->id.cu_model == VIRTIO_ID_NET;
            }
        }
    }
    return false;
}

375
void s390_ipl_update_diag308(IplParameterBlock *iplb)
376
{
377
    S390IPLState *ipl = get_ipl_device();
378

379 380
    ipl->iplb = *iplb;
    ipl->iplb_valid = true;
F
Farhan Ali 已提交
381
    ipl->netboot = is_virtio_net_device(iplb);
382 383 384 385
}

IplParameterBlock *s390_ipl_get_iplb(void)
{
386
    S390IPLState *ipl = get_ipl_device();
387

388
    if (!ipl->iplb_valid) {
389 390 391 392 393
        return NULL;
    }
    return &ipl->iplb;
}

394 395
void s390_reipl_request(void)
{
396
    S390IPLState *ipl = get_ipl_device();
397 398

    ipl->reipl_requested = true;
399
    qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET);
400 401
}

402 403 404
void s390_ipl_prepare_cpu(S390CPU *cpu)
{
    S390IPLState *ipl = get_ipl_device();
F
Farhan Ali 已提交
405
    Error *err = NULL;
406 407 408 409 410 411

    cpu->env.psw.addr = ipl->start_addr;
    cpu->env.psw.mask = IPL_PSW_MASK;

    if (!ipl->kernel || ipl->iplb_valid) {
        cpu->env.psw.addr = ipl->bios_start_addr;
412 413 414
        if (!ipl->iplb_valid) {
            ipl->iplb_valid = s390_gen_initial_iplb(ipl);
        }
415
    }
F
Farhan Ali 已提交
416 417 418 419 420
    if (ipl->netboot) {
        if (load_netboot_image(&err) < 0) {
            error_report_err(err);
            vm_stop(RUN_STATE_INTERNAL_ERROR);
        }
421
        ipl->iplb.ccw.netboot_start_addr = cpu_to_be64(ipl->start_addr);
F
Farhan Ali 已提交
422
    }
423 424
}

425 426 427
static void s390_ipl_reset(DeviceState *dev)
{
    S390IPLState *ipl = S390_IPL(dev);
D
Dominik Dingel 已提交
428

429 430
    if (!ipl->reipl_requested) {
        ipl->iplb_valid = false;
431
        memset(&ipl->iplb, 0, sizeof(IplParameterBlock));
432 433
    }
    ipl->reipl_requested = false;
434 435 436 437 438 439
}

static void s390_ipl_class_init(ObjectClass *klass, void *data)
{
    DeviceClass *dc = DEVICE_CLASS(klass);

440
    dc->realize = s390_ipl_realize;
441 442
    dc->props = s390_ipl_properties;
    dc->reset = s390_ipl_reset;
443
    dc->vmsd = &vmstate_ipl;
C
Cornelia Huck 已提交
444
    set_bit(DEVICE_CATEGORY_MISC, dc->categories);
445 446
}

A
Alexander Graf 已提交
447
static const TypeInfo s390_ipl_info = {
448
    .class_init = s390_ipl_class_init,
449 450
    .parent = TYPE_DEVICE,
    .name  = TYPE_S390_IPL,
451 452 453 454 455 456 457 458 459
    .instance_size  = sizeof(S390IPLState),
};

static void s390_ipl_register_types(void)
{
    type_register_static(&s390_ipl_info);
}

type_init(s390_ipl_register_types)