qemu/hw/s390-virtio.c
<<
>>
Prefs
   1/*
   2 * QEMU S390 virtio target
   3 *
   4 * Copyright (c) 2009 Alexander Graf <agraf@suse.de>
   5 *
   6 * This library is free software; you can redistribute it and/or
   7 * modify it under the terms of the GNU Lesser General Public
   8 * License as published by the Free Software Foundation; either
   9 * version 2 of the License, or (at your option) any later version.
  10 *
  11 * This library is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14 * Lesser General Public License for more details.
  15 *
  16 * You should have received a copy of the GNU Lesser General Public
  17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
  18 */
  19
  20#include "hw.h"
  21#include "block.h"
  22#include "blockdev.h"
  23#include "sysemu.h"
  24#include "net.h"
  25#include "boards.h"
  26#include "monitor.h"
  27#include "loader.h"
  28#include "elf.h"
  29#include "hw/virtio.h"
  30#include "hw/sysbus.h"
  31#include "kvm.h"
  32
  33#include "hw/s390-virtio-bus.h"
  34
  35//#define DEBUG_S390
  36
  37#ifdef DEBUG_S390
  38#define dprintf(fmt, ...) \
  39    do { fprintf(stderr, fmt, ## __VA_ARGS__); } while (0)
  40#else
  41#define dprintf(fmt, ...) \
  42    do { } while (0)
  43#endif
  44
  45#define KVM_S390_VIRTIO_NOTIFY          0
  46#define KVM_S390_VIRTIO_RESET           1
  47#define KVM_S390_VIRTIO_SET_STATUS      2
  48
  49#define KERN_IMAGE_START                0x010000UL
  50#define KERN_PARM_AREA                  0x010480UL
  51#define INITRD_START                    0x800000UL
  52#define INITRD_PARM_START               0x010408UL
  53#define INITRD_PARM_SIZE                0x010410UL
  54#define PARMFILE_START                  0x001000UL
  55
  56#define ZIPL_START                      0x009000UL
  57#define ZIPL_LOAD_ADDR                  0x009000UL
  58#define ZIPL_FILENAME                   "s390-zipl.rom"
  59
  60#define MAX_BLK_DEVS                    10
  61
  62static VirtIOS390Bus *s390_bus;
  63static CPUState **ipi_states;
  64
  65void irq_info(Monitor *mon);
  66void pic_info(Monitor *mon);
  67
  68void irq_info(Monitor *mon)
  69{
  70}
  71
  72void pic_info(Monitor *mon)
  73{
  74}
  75
  76CPUState *s390_cpu_addr2state(uint16_t cpu_addr)
  77{
  78    if (cpu_addr >= smp_cpus) {
  79        return NULL;
  80    }
  81
  82    return ipi_states[cpu_addr];
  83}
  84
  85int s390_virtio_hypercall(CPUState *env)
  86{
  87    int r = 0, i;
  88    target_ulong mem = env->regs[2];
  89
  90    dprintf("KVM hypercall: %ld\n", env->regs[1]);
  91    switch (env->regs[1]) {
  92    case KVM_S390_VIRTIO_NOTIFY:
  93        if (mem > ram_size) {
  94            VirtIOS390Device *dev = s390_virtio_bus_find_vring(s390_bus,
  95                                                               mem, &i);
  96            if (dev) {
  97                virtio_queue_notify(dev->vdev, i);
  98            } else {
  99                r = -EINVAL;
 100            }
 101        } else {
 102            /* Early printk */
 103        }
 104        break;
 105    case KVM_S390_VIRTIO_RESET:
 106    {
 107        VirtIOS390Device *dev;
 108
 109        dev = s390_virtio_bus_find_mem(s390_bus, mem);
 110        virtio_reset(dev->vdev);
 111        s390_virtio_device_sync(dev);
 112        break;
 113    }
 114    case KVM_S390_VIRTIO_SET_STATUS:
 115    {
 116        VirtIOS390Device *dev;
 117
 118        dev = s390_virtio_bus_find_mem(s390_bus, mem);
 119        if (dev) {
 120            s390_virtio_device_update_status(dev);
 121        } else {
 122            r = -EINVAL;
 123        }
 124        break;
 125    }
 126    default:
 127        r = -EINVAL;
 128        break;
 129    }
 130
 131    env->regs[2] = r;
 132    return 0;
 133}
 134
 135/* PC hardware initialisation */
 136static void s390_init(ram_addr_t ram_size,
 137                      const char *boot_device,
 138                      const char *kernel_filename,
 139                      const char *kernel_cmdline,
 140                      const char *initrd_filename,
 141                      const char *cpu_model)
 142{
 143    CPUState *env = NULL;
 144    ram_addr_t ram_addr;
 145    ram_addr_t kernel_size = 0;
 146    ram_addr_t initrd_offset;
 147    ram_addr_t initrd_size = 0;
 148    int i;
 149
 150    /* XXX we only work on KVM for now */
 151
 152    if (!kvm_enabled()) {
 153        fprintf(stderr, "The S390 target only works with KVM enabled\n");
 154        exit(1);
 155    }
 156
 157    /* get a BUS */
 158    s390_bus = s390_virtio_bus_init(&ram_size);
 159
 160    /* allocate RAM */
 161    ram_addr = qemu_ram_alloc(NULL, "s390.ram", ram_size);
 162    cpu_register_physical_memory(0, ram_size, ram_addr);
 163
 164    /* init CPUs */
 165    if (cpu_model == NULL) {
 166        cpu_model = "host";
 167    }
 168
 169    ipi_states = qemu_malloc(sizeof(CPUState *) * smp_cpus);
 170
 171    for (i = 0; i < smp_cpus; i++) {
 172        CPUState *tmp_env;
 173
 174        tmp_env = cpu_init(cpu_model);
 175        if (!env) {
 176            env = tmp_env;
 177        }
 178        ipi_states[i] = tmp_env;
 179        tmp_env->halted = 1;
 180        tmp_env->exception_index = EXCP_HLT;
 181    }
 182
 183    env->halted = 0;
 184    env->exception_index = 0;
 185
 186    if (kernel_filename) {
 187        kernel_size = load_image(kernel_filename, qemu_get_ram_ptr(0));
 188
 189        if (lduw_phys(KERN_IMAGE_START) != 0x0dd0) {
 190            fprintf(stderr, "Specified image is not an s390 boot image\n");
 191            exit(1);
 192        }
 193
 194        env->psw.addr = KERN_IMAGE_START;
 195        env->psw.mask = 0x0000000180000000ULL;
 196    } else {
 197        ram_addr_t bios_size = 0;
 198        char *bios_filename;
 199
 200        /* Load zipl bootloader */
 201        if (bios_name == NULL) {
 202            bios_name = ZIPL_FILENAME;
 203        }
 204
 205        bios_filename = qemu_find_file(QEMU_FILE_TYPE_BIOS, bios_name);
 206        bios_size = load_image(bios_filename, qemu_get_ram_ptr(ZIPL_LOAD_ADDR));
 207
 208        if ((long)bios_size < 0) {
 209            hw_error("could not load bootloader '%s'\n", bios_name);
 210        }
 211
 212        if (bios_size > 4096) {
 213            hw_error("stage1 bootloader is > 4k\n");
 214        }
 215
 216        env->psw.addr = ZIPL_START;
 217        env->psw.mask = 0x0000000180000000ULL;
 218    }
 219
 220    if (initrd_filename) {
 221        initrd_offset = INITRD_START;
 222        while (kernel_size + 0x100000 > initrd_offset) {
 223            initrd_offset += 0x100000;
 224        }
 225        initrd_size = load_image(initrd_filename, qemu_get_ram_ptr(initrd_offset));
 226
 227        stq_phys(INITRD_PARM_START, initrd_offset);
 228        stq_phys(INITRD_PARM_SIZE, initrd_size);
 229    }
 230
 231    if (kernel_cmdline) {
 232        cpu_physical_memory_rw(KERN_PARM_AREA, (uint8_t *)kernel_cmdline,
 233                               strlen(kernel_cmdline), 1);
 234    }
 235
 236    /* Create VirtIO network adapters */
 237    for(i = 0; i < nb_nics; i++) {
 238        NICInfo *nd = &nd_table[i];
 239        DeviceState *dev;
 240
 241        if (!nd->model) {
 242            nd->model = qemu_strdup("virtio");
 243        }
 244
 245        if (strcmp(nd->model, "virtio")) {
 246            fprintf(stderr, "S390 only supports VirtIO nics\n");
 247            exit(1);
 248        }
 249
 250        dev = qdev_create((BusState *)s390_bus, "virtio-net-s390");
 251        qdev_set_nic_properties(dev, nd);
 252        qdev_init_nofail(dev);
 253    }
 254
 255    /* Create VirtIO disk drives */
 256    for(i = 0; i < MAX_BLK_DEVS; i++) {
 257        DriveInfo *dinfo;
 258        DeviceState *dev;
 259
 260        dinfo = drive_get(IF_IDE, 0, i);
 261        if (!dinfo) {
 262            continue;
 263        }
 264
 265        dev = qdev_create((BusState *)s390_bus, "virtio-blk-s390");
 266        qdev_prop_set_drive_nofail(dev, "drive", dinfo->bdrv);
 267        qdev_init_nofail(dev);
 268    }
 269}
 270
 271static QEMUMachine s390_machine = {
 272    .name = "s390-virtio",
 273    .alias = "s390",
 274    .desc = "VirtIO based S390 machine",
 275    .init = s390_init,
 276    .no_serial = 1,
 277    .no_parallel = 1,
 278    .use_virtcon = 1,
 279    .no_vga = 1,
 280    .max_cpus = 255,
 281    .is_default = 1,
 282};
 283
 284static void s390_machine_init(void)
 285{
 286    qemu_register_machine(&s390_machine);
 287}
 288
 289machine_init(s390_machine_init);
 290