
In an ideal world, machines can be built by wiring devices together with configuration, not code. Unfortunately, that's not the world we live in right now. We still have quite a few devices that need to be wired up by code. If you try to device_add such a device, it'll fail in sometimes mysterious ways. If you're lucky, you get an unmysterious immediate crash. To protect users from such badness, DeviceClass member no_user used to make device models unavailable with -device / device_add, but that regressed in commit 18b6dad. The device model is still omitted from help, but is available anyway. Attempts to fix the regression have been rejected with the argument that the purpose of no_user isn't clear, and it's prone to misuse. This commit clarifies no_user's purpose. Anthony suggested to rename it cannot_instantiate_with_device_add_yet_due_to_internal_bugs, which I shorten somewhat to keep checkpatch happy. While there, make it bool. Every use of cannot_instantiate_with_device_add_yet gets a FIXME comment asking for rationale. The next few commits will clean them all up, either by providing a rationale, or by getting rid of the use. With that done, the regression fix is hopefully acceptable. Signed-off-by: Markus Armbruster <armbru@redhat.com> Reviewed-by: Marcel Apfelbaum <marcel.a@redhat.com> Signed-off-by: Andreas Färber <afaerber@suse.de>
201 lines
5.9 KiB
C
201 lines
5.9 KiB
C
/*
|
|
* 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.
|
|
*
|
|
*/
|
|
|
|
#include "sysemu/sysemu.h"
|
|
#include "cpu.h"
|
|
#include "elf.h"
|
|
#include "hw/loader.h"
|
|
#include "hw/sysbus.h"
|
|
#include "hw/s390x/virtio-ccw.h"
|
|
#include "hw/s390x/css.h"
|
|
|
|
#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)
|
|
|
|
#define TYPE_S390_IPL "s390-ipl"
|
|
#define S390_IPL(obj) \
|
|
OBJECT_CHECK(S390IPLState, (obj), TYPE_S390_IPL)
|
|
#if 0
|
|
#define S390_IPL_CLASS(klass) \
|
|
OBJECT_CLASS_CHECK(S390IPLState, (klass), TYPE_S390_IPL)
|
|
#define S390_IPL_GET_CLASS(obj) \
|
|
OBJECT_GET_CLASS(S390IPLState, (obj), TYPE_S390_IPL)
|
|
#endif
|
|
|
|
typedef struct S390IPLClass {
|
|
/*< private >*/
|
|
SysBusDeviceClass parent_class;
|
|
/*< public >*/
|
|
|
|
void (*parent_reset) (SysBusDevice *dev);
|
|
} S390IPLClass;
|
|
|
|
typedef struct S390IPLState {
|
|
/*< private >*/
|
|
SysBusDevice parent_obj;
|
|
uint64_t start_addr;
|
|
|
|
/*< public >*/
|
|
char *kernel;
|
|
char *initrd;
|
|
char *cmdline;
|
|
char *firmware;
|
|
} S390IPLState;
|
|
|
|
|
|
static int s390_ipl_init(SysBusDevice *dev)
|
|
{
|
|
S390IPLState *ipl = S390_IPL(dev);
|
|
int kernel_size;
|
|
|
|
if (!ipl->kernel) {
|
|
int bios_size;
|
|
char *bios_filename;
|
|
|
|
/* Load zipl bootloader */
|
|
if (bios_name == NULL) {
|
|
bios_name = ipl->firmware;
|
|
}
|
|
|
|
bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
|
|
if (bios_filename == NULL) {
|
|
hw_error("could not find stage1 bootloader\n");
|
|
}
|
|
|
|
bios_size = load_elf(bios_filename, NULL, NULL, &ipl->start_addr, NULL,
|
|
NULL, 1, ELF_MACHINE, 0);
|
|
if (bios_size == -1) {
|
|
bios_size = load_image_targphys(bios_filename, ZIPL_IMAGE_START,
|
|
4096);
|
|
ipl->start_addr = ZIPL_IMAGE_START;
|
|
if (bios_size > 4096) {
|
|
hw_error("stage1 bootloader is > 4k\n");
|
|
}
|
|
}
|
|
g_free(bios_filename);
|
|
|
|
if (bios_size == -1) {
|
|
hw_error("could not load bootloader '%s'\n", bios_name);
|
|
}
|
|
return 0;
|
|
} else {
|
|
kernel_size = load_elf(ipl->kernel, NULL, NULL, NULL, NULL,
|
|
NULL, 1, ELF_MACHINE, 0);
|
|
if (kernel_size == -1) {
|
|
kernel_size = load_image_targphys(ipl->kernel, 0, ram_size);
|
|
}
|
|
if (kernel_size == -1) {
|
|
fprintf(stderr, "could not load kernel '%s'\n", ipl->kernel);
|
|
return -1;
|
|
}
|
|
/* we have to overwrite values in the kernel image, which are "rom" */
|
|
strcpy(rom_ptr(KERN_PARM_AREA), ipl->cmdline);
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
ipl->start_addr = KERN_IMAGE_START;
|
|
}
|
|
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) {
|
|
fprintf(stderr, "qemu: could not load initrd '%s'\n", ipl->initrd);
|
|
exit(1);
|
|
}
|
|
|
|
/* 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);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static Property s390_ipl_properties[] = {
|
|
DEFINE_PROP_STRING("kernel", S390IPLState, kernel),
|
|
DEFINE_PROP_STRING("initrd", S390IPLState, initrd),
|
|
DEFINE_PROP_STRING("cmdline", S390IPLState, cmdline),
|
|
DEFINE_PROP_STRING("firmware", S390IPLState, firmware),
|
|
DEFINE_PROP_END_OF_LIST(),
|
|
};
|
|
|
|
static void s390_ipl_reset(DeviceState *dev)
|
|
{
|
|
S390IPLState *ipl = S390_IPL(dev);
|
|
S390CPU *cpu = S390_CPU(qemu_get_cpu(0));
|
|
CPUS390XState *env = &cpu->env;
|
|
|
|
env->psw.addr = ipl->start_addr;
|
|
env->psw.mask = IPL_PSW_MASK;
|
|
|
|
if (!ipl->kernel) {
|
|
/* Tell firmware, if there is a preferred boot device */
|
|
env->regs[7] = -1;
|
|
DeviceState *dev_st = get_boot_device(0);
|
|
if (dev_st) {
|
|
VirtioCcwDevice *ccw_dev = (VirtioCcwDevice *) object_dynamic_cast(
|
|
OBJECT(qdev_get_parent_bus(dev_st)->parent),
|
|
TYPE_VIRTIO_CCW_DEVICE);
|
|
|
|
if (ccw_dev) {
|
|
env->regs[7] = ccw_dev->sch->cssid << 24 |
|
|
ccw_dev->sch->ssid << 16 |
|
|
ccw_dev->sch->devno;
|
|
}
|
|
}
|
|
}
|
|
|
|
s390_add_running_cpu(cpu);
|
|
}
|
|
|
|
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;
|
|
dc->cannot_instantiate_with_device_add_yet = true; /* FIXME explain why */
|
|
}
|
|
|
|
static const 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_ipl_register_types(void)
|
|
{
|
|
type_register_static(&s390_ipl_info);
|
|
}
|
|
|
|
type_init(s390_ipl_register_types)
|