qemu/hw/s390x/s390-virtio.c
David Hildenbrand 75973bfe41 s390x/kvm: introduce proper states for s390 cpus
Until now, when a s390 cpu was stopped or halted, the number of running
CPUs was tracked in a global variable. This was problematic for migration,
so Jason came up with a per-cpu running state.
As it turns out, we want to track the full logical state of a target vcpu,
so we need real s390 cpu states.

This patch is based on an initial patch by Jason Herne, but was heavily
rewritten when adding the cpu states STOPPED and OPERATING. On the way we
move add_del_running to cpu.c (the declaration is already in cpu.h) and
modify the users where appropriate.

Please note that the cpu is still set to be stopped when it is
halted, which is wrong. This will be fixed in the next patch. The LOAD and
CHECK-STOP state will not be used in the first step.

Signed-off-by: David Hildenbrand <dahi@linux.vnet.ibm.com>
[folded Jason's patch into David's patch to avoid add/remove same lines]
Signed-off-by: Jens Freimann <jfrei@linux.vnet.ibm.com>
Reviewed-by: Cornelia Huck <cornelia.huck@de.ibm.com>
Reviewed-by: Christian Borntraeger <borntraeger@de.ibm.com>
CC: Andreas Faerber <afaerber@suse.de>
Tested-by: Christian Borntraeger <borntraeger@de.ibm.com>
Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
2014-10-10 10:37:47 +02:00

300 lines
8.0 KiB
C

/*
* QEMU S390 virtio target
*
* Copyright (c) 2009 Alexander Graf <agraf@suse.de>
* Copyright IBM Corp 2012
*
* 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.
*
* Contributions after 2012-10-29 are licensed under the terms of the
* GNU GPL, version 2 or (at your option) any later version.
*
* 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/hw.h"
#include "block/block.h"
#include "sysemu/blockdev.h"
#include "sysemu/sysemu.h"
#include "net/net.h"
#include "hw/boards.h"
#include "monitor/monitor.h"
#include "hw/loader.h"
#include "hw/virtio/virtio.h"
#include "hw/sysbus.h"
#include "sysemu/kvm.h"
#include "exec/address-spaces.h"
#include "hw/s390x/s390-virtio-bus.h"
#include "hw/s390x/sclp.h"
#include "hw/s390x/s390_flic.h"
#include "hw/s390x/s390-virtio.h"
//#define DEBUG_S390
#ifdef DEBUG_S390
#define DPRINTF(fmt, ...) \
do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
#else
#define DPRINTF(fmt, ...) \
do { } while (0)
#endif
#define MAX_BLK_DEVS 10
#define ZIPL_FILENAME "s390-zipl.rom"
#define TYPE_S390_MACHINE "s390-machine"
static VirtIOS390Bus *s390_bus;
static S390CPU **ipi_states;
S390CPU *s390_cpu_addr2state(uint16_t cpu_addr)
{
if (cpu_addr >= smp_cpus) {
return NULL;
}
return ipi_states[cpu_addr];
}
static int s390_virtio_hcall_notify(const uint64_t *args)
{
uint64_t mem = args[0];
int r = 0, i;
if (mem > ram_size) {
VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus, mem, &i);
if (dev) {
virtio_queue_notify(dev->vdev, i);
} else {
r = -EINVAL;
}
} else {
/* Early printk */
}
return r;
}
static int s390_virtio_hcall_reset(const uint64_t *args)
{
uint64_t mem = args[0];
VirtIOS390Device *dev;
dev = s390_virtio_bus_find_mem(s390_bus, mem);
if (dev == NULL) {
return -EINVAL;
}
virtio_reset(dev->vdev);
stb_phys(&address_space_memory, dev->dev_offs + VIRTIO_DEV_OFFS_STATUS, 0);
s390_virtio_device_sync(dev);
s390_virtio_reset_idx(dev);
return 0;
}
static int s390_virtio_hcall_set_status(const uint64_t *args)
{
uint64_t mem = args[0];
int r = 0;
VirtIOS390Device *dev;
dev = s390_virtio_bus_find_mem(s390_bus, mem);
if (dev) {
s390_virtio_device_update_status(dev);
} else {
r = -EINVAL;
}
return r;
}
static void s390_virtio_register_hcalls(void)
{
s390_register_virtio_hypercall(KVM_S390_VIRTIO_NOTIFY,
s390_virtio_hcall_notify);
s390_register_virtio_hypercall(KVM_S390_VIRTIO_RESET,
s390_virtio_hcall_reset);
s390_register_virtio_hypercall(KVM_S390_VIRTIO_SET_STATUS,
s390_virtio_hcall_set_status);
}
void s390_init_ipl_dev(const char *kernel_filename,
const char *kernel_cmdline,
const char *initrd_filename,
const char *firmware)
{
DeviceState *dev;
dev = qdev_create(NULL, "s390-ipl");
if (kernel_filename) {
qdev_prop_set_string(dev, "kernel", kernel_filename);
}
if (initrd_filename) {
qdev_prop_set_string(dev, "initrd", initrd_filename);
}
qdev_prop_set_string(dev, "cmdline", kernel_cmdline);
qdev_prop_set_string(dev, "firmware", firmware);
qdev_init_nofail(dev);
}
void s390_init_cpus(const char *cpu_model, uint8_t *storage_keys)
{
int i;
if (cpu_model == NULL) {
cpu_model = "host";
}
ipi_states = g_malloc(sizeof(S390CPU *) * smp_cpus);
for (i = 0; i < smp_cpus; i++) {
S390CPU *cpu;
CPUState *cs;
cpu = cpu_s390x_init(cpu_model);
cs = CPU(cpu);
ipi_states[i] = cpu;
cs->halted = 1;
cs->exception_index = EXCP_HLT;
cpu->env.storage_keys = storage_keys;
}
}
void s390_create_virtio_net(BusState *bus, const char *name)
{
int i;
for (i = 0; i < nb_nics; i++) {
NICInfo *nd = &nd_table[i];
DeviceState *dev;
if (!nd->model) {
nd->model = g_strdup("virtio");
}
if (strcmp(nd->model, "virtio")) {
fprintf(stderr, "S390 only supports VirtIO nics\n");
exit(1);
}
dev = qdev_create(bus, name);
qdev_set_nic_properties(dev, nd);
qdev_init_nofail(dev);
}
}
/* PC hardware initialisation */
static void s390_init(MachineState *machine)
{
ram_addr_t my_ram_size = machine->ram_size;
MemoryRegion *sysmem = get_system_memory();
MemoryRegion *ram = g_new(MemoryRegion, 1);
int increment_size = 20;
uint8_t *storage_keys;
void *virtio_region;
hwaddr virtio_region_len;
hwaddr virtio_region_start;
/*
* The storage increment size is a multiple of 1M and is a power of 2.
* The number of storage increments must be MAX_STORAGE_INCREMENTS or
* fewer.
*/
while ((my_ram_size >> increment_size) > MAX_STORAGE_INCREMENTS) {
increment_size++;
}
my_ram_size = my_ram_size >> increment_size << increment_size;
/* let's propagate the changed ram size into the global variable. */
ram_size = my_ram_size;
/* get a BUS */
s390_bus = s390_virtio_bus_init(&my_ram_size);
s390_sclp_init();
s390_init_ipl_dev(machine->kernel_filename, machine->kernel_cmdline,
machine->initrd_filename, ZIPL_FILENAME);
s390_flic_init();
/* register hypercalls */
s390_virtio_register_hcalls();
/* allocate RAM */
memory_region_init_ram(ram, NULL, "s390.ram", my_ram_size, &error_abort);
vmstate_register_ram_global(ram);
memory_region_add_subregion(sysmem, 0, ram);
/* clear virtio region */
virtio_region_len = my_ram_size - ram_size;
virtio_region_start = ram_size;
virtio_region = cpu_physical_memory_map(virtio_region_start,
&virtio_region_len, true);
memset(virtio_region, 0, virtio_region_len);
cpu_physical_memory_unmap(virtio_region, virtio_region_len, 1,
virtio_region_len);
/* allocate storage keys */
storage_keys = g_malloc0(my_ram_size / TARGET_PAGE_SIZE);
/* init CPUs */
s390_init_cpus(machine->cpu_model, storage_keys);
/* Create VirtIO network adapters */
s390_create_virtio_net((BusState *)s390_bus, "virtio-net-s390");
}
void s390_nmi(NMIState *n, int cpu_index, Error **errp)
{
CPUState *cs = qemu_get_cpu(cpu_index);
if (s390_cpu_restart(S390_CPU(cs))) {
error_set(errp, QERR_UNSUPPORTED);
}
}
static void s390_machine_class_init(ObjectClass *oc, void *data)
{
MachineClass *mc = MACHINE_CLASS(oc);
NMIClass *nc = NMI_CLASS(oc);
mc->name = "s390-virtio";
mc->alias = "s390";
mc->desc = "VirtIO based S390 machine";
mc->init = s390_init;
mc->block_default_type = IF_VIRTIO;
mc->max_cpus = 255;
mc->no_serial = 1;
mc->no_parallel = 1;
mc->use_virtcon = 1;
mc->no_floppy = 1;
mc->no_cdrom = 1;
mc->no_sdcard = 1;
mc->is_default = 1;
nc->nmi_monitor_handler = s390_nmi;
}
static const TypeInfo s390_machine_info = {
.name = TYPE_S390_MACHINE,
.parent = TYPE_MACHINE,
.class_init = s390_machine_class_init,
.interfaces = (InterfaceInfo[]) {
{ TYPE_NMI },
{ }
},
};
static void s390_machine_register_types(void)
{
type_register_static(&s390_machine_info);
}
type_init(s390_machine_register_types)