qemu/hw/s390x/ipl.c
Markus Armbruster efec3dd631 qdev: Replace no_user by cannot_instantiate_with_device_add_yet
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>
2013-12-23 00:27:22 +01:00

200 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)