mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:17 +00:00
pc-bios/s390-ccw: Split virtio-ccw and generic virtio
Separate the CCW specific virtio routines and create generic wrappers for easier reuse of existing virtio functions with non-CCW devices. Reviewed-by: Thomas Huth <thuth@redhat.com> Reviewed-by: Eric Farman <farman@linux.ibm.com> Signed-off-by: Jared Rossi <jrossi@linux.ibm.com> Message-ID: <20260309003601.242634-7-jrossi@linux.ibm.com> [thuth: Use SPDX license identifier in virtio-ccw.c] Signed-off-by: Thomas Huth <thuth@redhat.com>
This commit is contained in:
@@ -34,7 +34,8 @@ QEMU_DGFLAGS = -MMD -MP -MT $@ -MF $(@D)/$(*F).d
|
||||
.PHONY : all clean build-all distclean
|
||||
|
||||
OBJECTS = start.o main.o bootmap.o jump2ipl.o sclp.o menu.o netmain.o \
|
||||
virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o
|
||||
virtio.o virtio-net.o virtio-scsi.o virtio-blkdev.o cio.o dasd-ipl.o \
|
||||
virtio-ccw.o
|
||||
|
||||
SLOF_DIR := $(SRC_PATH)/../../roms/SLOF
|
||||
|
||||
|
||||
@@ -71,6 +71,7 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no)
|
||||
bool is_virtio;
|
||||
Schib schib;
|
||||
int r;
|
||||
VDev *vdev = virtio_get_device();
|
||||
|
||||
blk_schid.sch_no = sch_no;
|
||||
r = stsch_err(blk_schid, &schib);
|
||||
@@ -91,7 +92,8 @@ static int is_dev_possibly_bootable(int dev_no, int sch_no)
|
||||
* Note: we always have to run virtio_is_supported() here to make
|
||||
* sure that the vdev.senseid data gets pre-initialized correctly
|
||||
*/
|
||||
is_virtio = virtio_is_supported(blk_schid);
|
||||
vdev->schid = blk_schid;
|
||||
is_virtio = virtio_is_supported(vdev);
|
||||
|
||||
/* No specific devno given, just return whether the device is possibly bootable */
|
||||
if (dev_no < 0) {
|
||||
@@ -256,10 +258,10 @@ static int virtio_setup(void)
|
||||
puts("Network boot device detected");
|
||||
return 0;
|
||||
case VIRTIO_ID_BLOCK:
|
||||
ret = virtio_blk_setup_device(blk_schid);
|
||||
ret = virtio_blk_setup_device(vdev);
|
||||
break;
|
||||
case VIRTIO_ID_SCSI:
|
||||
ret = virtio_scsi_setup_device(blk_schid);
|
||||
ret = virtio_scsi_setup_device(vdev);
|
||||
break;
|
||||
default:
|
||||
puts("\n! No IPL device available !\n");
|
||||
|
||||
@@ -500,7 +500,7 @@ static bool find_net_dev(Schib *schib, int dev_no)
|
||||
continue;
|
||||
}
|
||||
enable_subchannel(net_schid);
|
||||
if (!virtio_is_supported(net_schid)) {
|
||||
if (!virtio_is_supported(virtio_get_device())) {
|
||||
continue;
|
||||
}
|
||||
if (virtio_get_device_type() != VIRTIO_ID_NET) {
|
||||
|
||||
@@ -66,9 +66,6 @@ void sclp_setup(void);
|
||||
void sclp_get_loadparm_ascii(char *loadparm);
|
||||
int sclp_read(char *str, size_t count);
|
||||
|
||||
/* virtio.c */
|
||||
bool virtio_is_supported(SubChannelId schid);
|
||||
|
||||
/* bootmap.c */
|
||||
void zipl_load(void);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "s390-ccw.h"
|
||||
#include "virtio.h"
|
||||
#include "virtio-scsi.h"
|
||||
#include "virtio-ccw.h"
|
||||
|
||||
#define VIRTIO_BLK_F_GEOMETRY (1 << 4)
|
||||
#define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
|
||||
@@ -229,15 +230,17 @@ uint64_t virtio_get_blocks(void)
|
||||
}
|
||||
}
|
||||
|
||||
int virtio_blk_setup_device(SubChannelId schid)
|
||||
int virtio_blk_setup_device(VDev *vdev)
|
||||
{
|
||||
VDev *vdev = virtio_get_device();
|
||||
|
||||
vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE;
|
||||
vdev->schid = schid;
|
||||
virtio_setup_ccw(vdev);
|
||||
|
||||
puts("Using virtio-blk.");
|
||||
|
||||
return 0;
|
||||
switch (vdev->ipl_type) {
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
case S390_IPL_TYPE_CCW:
|
||||
return virtio_ccw_setup(vdev);
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
239
pc-bios/s390-ccw/virtio-ccw.c
Normal file
239
pc-bios/s390-ccw/virtio-ccw.c
Normal file
@@ -0,0 +1,239 @@
|
||||
/*
|
||||
* Virtio functionality for CCW devices
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*
|
||||
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
|
||||
* Copyright 2025 IBM Corp.
|
||||
*
|
||||
* Author(s): Jared Rossi <jrossi@linux.ibm.com>
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "cio.h"
|
||||
#include "virtio.h"
|
||||
#include "virtio-ccw.h"
|
||||
#include "virtio-scsi.h"
|
||||
#include "bswap.h"
|
||||
#include "helper.h"
|
||||
#include "s390-time.h"
|
||||
|
||||
/* virtio spec v1.0 para 4.3.3.2 */
|
||||
static long kvm_hypercall(unsigned long nr, unsigned long param1,
|
||||
unsigned long param2, unsigned long param3)
|
||||
{
|
||||
register unsigned long r_nr asm("1") = nr;
|
||||
register unsigned long r_param1 asm("2") = param1;
|
||||
register unsigned long r_param2 asm("3") = param2;
|
||||
register unsigned long r_param3 asm("4") = param3;
|
||||
register long retval asm("2");
|
||||
|
||||
asm volatile ("diag %%r2,%%r4,0x500"
|
||||
: "=d" (retval)
|
||||
: "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
|
||||
: "memory", "cc");
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
|
||||
{
|
||||
Ccw1 ccw = {};
|
||||
|
||||
ccw.cmd_code = cmd;
|
||||
ccw.cda = (long)ptr;
|
||||
ccw.count = len;
|
||||
|
||||
if (sli) {
|
||||
ccw.flags |= CCW_FLAG_SLI;
|
||||
}
|
||||
|
||||
return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw), CCW_FMT1);
|
||||
}
|
||||
|
||||
bool virtio_ccw_is_supported(VDev *vdev)
|
||||
{
|
||||
memset(&vdev->senseid, 0, sizeof(vdev->senseid));
|
||||
|
||||
/*
|
||||
* Run sense id command.
|
||||
* The size of the senseid data differs between devices (notably,
|
||||
* between virtio devices and dasds), so specify the largest possible
|
||||
* size and suppress the incorrect length indication for smaller sizes.
|
||||
*/
|
||||
if (run_ccw(vdev, CCW_CMD_SENSE_ID, &vdev->senseid, sizeof(vdev->senseid),
|
||||
true)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
vdev->dev_type = vdev->senseid.cu_model;
|
||||
|
||||
if (vdev->senseid.cu_type == 0x3832) {
|
||||
switch (vdev->dev_type) {
|
||||
case VIRTIO_ID_BLOCK:
|
||||
case VIRTIO_ID_SCSI:
|
||||
case VIRTIO_ID_NET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int drain_irqs_ccw(SubChannelId schid)
|
||||
{
|
||||
Irb irb = {};
|
||||
int r = 0;
|
||||
|
||||
while (1) {
|
||||
/* FIXME: make use of TPI, for that enable subchannel and isc */
|
||||
if (tsch(schid, &irb)) {
|
||||
/* Might want to differentiate error codes later on. */
|
||||
if (irb.scsw.cstat) {
|
||||
r = -EIO;
|
||||
} else if (irb.scsw.dstat != 0xc) {
|
||||
r = -EIO;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
long virtio_ccw_notify(SubChannelId schid, int vq_idx, long cookie)
|
||||
{
|
||||
return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid,
|
||||
vq_idx, cookie);
|
||||
}
|
||||
|
||||
int virtio_ccw_run(VDev *vdev, int vqid, VirtioCmd *cmd)
|
||||
{
|
||||
VRing *vr = &vdev->vrings[vqid];
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
vring_send_buf(vr, cmd[i].data, cmd[i].size,
|
||||
cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
|
||||
} while (cmd[i++].flags & VRING_DESC_F_NEXT);
|
||||
|
||||
vring_wait_reply();
|
||||
if (drain_irqs()) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int virtio_ccw_reset(VDev *vdev)
|
||||
{
|
||||
return run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
|
||||
}
|
||||
|
||||
int virtio_ccw_setup(VDev *vdev)
|
||||
{
|
||||
int i, cfg_size = 0;
|
||||
uint8_t status;
|
||||
struct VirtioFeatureDesc {
|
||||
uint32_t features;
|
||||
uint8_t index;
|
||||
} __attribute__((packed)) feats;
|
||||
|
||||
if (!virtio_ccw_is_supported(vdev)) {
|
||||
puts("Virtio unsupported for this device ID");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* device ID has been established now */
|
||||
|
||||
vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
|
||||
vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
|
||||
|
||||
virtio_reset(vdev);
|
||||
|
||||
status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
|
||||
puts("Could not write ACKNOWLEDGE status to host");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch (vdev->dev_type) {
|
||||
case VIRTIO_ID_NET:
|
||||
vdev->nr_vqs = 2;
|
||||
vdev->cmd_vr_idx = 0;
|
||||
cfg_size = sizeof(vdev->config.net);
|
||||
break;
|
||||
case VIRTIO_ID_BLOCK:
|
||||
vdev->nr_vqs = 1;
|
||||
vdev->cmd_vr_idx = 0;
|
||||
cfg_size = sizeof(vdev->config.blk);
|
||||
break;
|
||||
case VIRTIO_ID_SCSI:
|
||||
vdev->nr_vqs = 3;
|
||||
vdev->cmd_vr_idx = VR_REQUEST;
|
||||
cfg_size = sizeof(vdev->config.scsi);
|
||||
break;
|
||||
default:
|
||||
puts("Unsupported virtio device");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
status |= VIRTIO_CONFIG_S_DRIVER;
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
|
||||
puts("Could not write DRIVER status to host");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Feature negotiation */
|
||||
for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
|
||||
feats.features = 0;
|
||||
feats.index = i;
|
||||
if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) {
|
||||
puts("Could not get features bits");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
vdev->guest_features[i] &= bswap32(feats.features);
|
||||
feats.features = bswap32(vdev->guest_features[i]);
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) {
|
||||
puts("Could not set features bits");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) {
|
||||
puts("Could not get virtio device configuration");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (i = 0; i < vdev->nr_vqs; i++) {
|
||||
VqInfo info = {
|
||||
.queue = (unsigned long long) virtio_get_ring_area(i),
|
||||
.align = KVM_S390_VIRTIO_RING_ALIGN,
|
||||
.index = i,
|
||||
.num = 0,
|
||||
};
|
||||
VqConfig config = {
|
||||
.index = i,
|
||||
.num = 0,
|
||||
};
|
||||
|
||||
if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config),
|
||||
false)) {
|
||||
puts("Could not get virtio device VQ config");
|
||||
return -EIO;
|
||||
}
|
||||
info.num = config.num;
|
||||
vring_init(&vdev->vrings[i], &info);
|
||||
if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) {
|
||||
puts("Cannot set VQ info");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
status |= VIRTIO_CONFIG_S_DRIVER_OK;
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
|
||||
puts("Could not write DRIVER_OK status to host");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
24
pc-bios/s390-ccw/virtio-ccw.h
Normal file
24
pc-bios/s390-ccw/virtio-ccw.h
Normal file
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Virtio definitions for CCW devices
|
||||
*
|
||||
* Copyright 2025 IBM Corp.
|
||||
* Author(s): Jared Rossi <jrossi@linux.ibm.com>
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef VIRTIO_CCW_H
|
||||
#define VIRTIO_CCW_H
|
||||
|
||||
/* main.c */
|
||||
extern SubChannelId blk_schid;
|
||||
|
||||
/* virtio-ccw.c */
|
||||
int drain_irqs_ccw(SubChannelId schid);
|
||||
bool virtio_ccw_is_supported(VDev *vdev);
|
||||
int virtio_ccw_run(VDev *vdev, int vqid, VirtioCmd *cmd);
|
||||
long virtio_ccw_notify(SubChannelId schid, int vq_idx, long cookie);
|
||||
int virtio_ccw_setup(VDev *vdev);
|
||||
int virtio_ccw_reset(VDev *vdev);
|
||||
|
||||
#endif
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <ethernet.h>
|
||||
#include "s390-ccw.h"
|
||||
#include "virtio.h"
|
||||
#include "virtio-ccw.h"
|
||||
#include "s390-time.h"
|
||||
#include "helper.h"
|
||||
|
||||
@@ -54,7 +55,7 @@ int virtio_net_init(void *mac_addr)
|
||||
rx_last_idx = 0;
|
||||
|
||||
vdev->guest_features[0] = VIRTIO_NET_F_MAC_BIT;
|
||||
virtio_setup_ccw(vdev);
|
||||
virtio_ccw_setup(vdev);
|
||||
|
||||
if (!(vdev->guest_features[0] & VIRTIO_NET_F_MAC_BIT)) {
|
||||
puts("virtio-net device does not support the MAC address feature");
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "virtio.h"
|
||||
#include "scsi.h"
|
||||
#include "virtio-scsi.h"
|
||||
#include "virtio-ccw.h"
|
||||
#include "s390-time.h"
|
||||
#include "helper.h"
|
||||
|
||||
@@ -476,12 +477,9 @@ static int virtio_scsi_setup(VDev *vdev)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int virtio_scsi_setup_device(SubChannelId schid)
|
||||
int virtio_scsi_setup_device(VDev *vdev)
|
||||
{
|
||||
VDev *vdev = virtio_get_device();
|
||||
|
||||
vdev->schid = schid;
|
||||
virtio_setup_ccw(vdev);
|
||||
virtio_ccw_setup(vdev);
|
||||
|
||||
if (vdev->config.scsi.sense_size != VIRTIO_SCSI_SENSE_SIZE) {
|
||||
puts("Config: sense size mismatch");
|
||||
|
||||
@@ -69,6 +69,6 @@ static inline bool virtio_scsi_response_ok(const VirtioScsiCmdResp *r)
|
||||
|
||||
int virtio_scsi_read_many(VDev *vdev,
|
||||
unsigned long sector, void *load_addr, int sec_num);
|
||||
int virtio_scsi_setup_device(SubChannelId schid);
|
||||
int virtio_scsi_setup_device(VDev *vdev);
|
||||
|
||||
#endif /* VIRTIO_SCSI_H */
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
* Virtio driver bits
|
||||
*
|
||||
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
|
||||
* Copyright 2025 IBM Corp.
|
||||
*
|
||||
* Author(s): Jared Rossi <jrossi@linux.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
|
||||
@@ -13,6 +16,7 @@
|
||||
#include "cio.h"
|
||||
#include "virtio.h"
|
||||
#include "virtio-scsi.h"
|
||||
#include "virtio-ccw.h"
|
||||
#include "bswap.h"
|
||||
#include "helper.h"
|
||||
#include "s390-time.h"
|
||||
@@ -44,28 +48,9 @@ VirtioDevType virtio_get_device_type(void)
|
||||
return vdev.dev_type;
|
||||
}
|
||||
|
||||
/* virtio spec v1.0 para 4.3.3.2 */
|
||||
static long kvm_hypercall(unsigned long nr, unsigned long param1,
|
||||
unsigned long param2, unsigned long param3)
|
||||
char *virtio_get_ring_area(int ring_num)
|
||||
{
|
||||
register unsigned long r_nr asm("1") = nr;
|
||||
register unsigned long r_param1 asm("2") = param1;
|
||||
register unsigned long r_param2 asm("3") = param2;
|
||||
register unsigned long r_param3 asm("4") = param3;
|
||||
register long retval asm("2");
|
||||
|
||||
asm volatile ("diag %%r2,%%r4,0x500"
|
||||
: "=d" (retval)
|
||||
: "d" (r_nr), "0" (r_param1), "r"(r_param2), "d"(r_param3)
|
||||
: "memory", "cc");
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static long virtio_notify(SubChannelId schid, int vq_idx, long cookie)
|
||||
{
|
||||
return kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid,
|
||||
vq_idx, cookie);
|
||||
return ring_area + ring_num * VIRTIO_RING_SIZE;
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
@@ -74,39 +59,27 @@ static long virtio_notify(SubChannelId schid, int vq_idx, long cookie)
|
||||
|
||||
int drain_irqs(void)
|
||||
{
|
||||
Irb irb = {};
|
||||
int r = 0;
|
||||
|
||||
while (1) {
|
||||
/* FIXME: make use of TPI, for that enable subchannel and isc */
|
||||
if (tsch(vdev.schid, &irb)) {
|
||||
/* Might want to differentiate error codes later on. */
|
||||
if (irb.scsw.cstat) {
|
||||
r = -EIO;
|
||||
} else if (irb.scsw.dstat != 0xc) {
|
||||
r = -EIO;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
switch (vdev.ipl_type) {
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
case S390_IPL_TYPE_CCW:
|
||||
return drain_irqs_ccw(vdev.schid);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int run_ccw(VDev *vdev, int cmd, void *ptr, int len, bool sli)
|
||||
int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
|
||||
{
|
||||
Ccw1 ccw = {};
|
||||
|
||||
ccw.cmd_code = cmd;
|
||||
ccw.cda = (long)ptr;
|
||||
ccw.count = len;
|
||||
|
||||
if (sli) {
|
||||
ccw.flags |= CCW_FLAG_SLI;
|
||||
switch (vdev->ipl_type) {
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
case S390_IPL_TYPE_CCW:
|
||||
return virtio_ccw_run(vdev, vqid, cmd);
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
|
||||
return do_cio(vdev->schid, vdev->senseid.cu_type, ptr2u32(&ccw), CCW_FMT1);
|
||||
}
|
||||
|
||||
static void vring_init(VRing *vr, VqInfo *info)
|
||||
void vring_init(VRing *vr, VqInfo *info)
|
||||
{
|
||||
void *p = (void *) info->queue;
|
||||
|
||||
@@ -134,7 +107,15 @@ static void vring_init(VRing *vr, VqInfo *info)
|
||||
|
||||
bool vring_notify(VRing *vr)
|
||||
{
|
||||
vr->cookie = virtio_notify(vdev.schid, vr->id, vr->cookie);
|
||||
switch (vdev.ipl_type) {
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
case S390_IPL_TYPE_CCW:
|
||||
vr->cookie = virtio_ccw_notify(vdev.schid, vr->id, vr->cookie);
|
||||
break;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
|
||||
return vr->cookie >= 0;
|
||||
}
|
||||
|
||||
@@ -200,164 +181,24 @@ int vring_wait_reply(void)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
|
||||
{
|
||||
VRing *vr = &vdev->vrings[vqid];
|
||||
int i = 0;
|
||||
|
||||
do {
|
||||
vring_send_buf(vr, cmd[i].data, cmd[i].size,
|
||||
cmd[i].flags | (i ? VRING_HIDDEN_IS_CHAIN : 0));
|
||||
} while (cmd[i++].flags & VRING_DESC_F_NEXT);
|
||||
|
||||
vring_wait_reply();
|
||||
if (drain_irqs()) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int virtio_reset(VDev *vdev)
|
||||
{
|
||||
return run_ccw(vdev, CCW_CMD_VDEV_RESET, NULL, 0, false);
|
||||
}
|
||||
|
||||
int virtio_setup_ccw(VDev *vdev)
|
||||
{
|
||||
int i, cfg_size = 0;
|
||||
uint8_t status;
|
||||
struct VirtioFeatureDesc {
|
||||
uint32_t features;
|
||||
uint8_t index;
|
||||
} __attribute__((packed)) feats;
|
||||
|
||||
if (!virtio_is_supported(vdev->schid)) {
|
||||
puts("Virtio unsupported for this device ID");
|
||||
return -ENODEV;
|
||||
}
|
||||
/* device ID has been established now */
|
||||
|
||||
vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */
|
||||
vdev->guessed_disk_nature = VIRTIO_GDN_NONE;
|
||||
|
||||
virtio_reset(vdev);
|
||||
|
||||
status = VIRTIO_CONFIG_S_ACKNOWLEDGE;
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
|
||||
puts("Could not write ACKNOWLEDGE status to host");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch (vdev->dev_type) {
|
||||
case VIRTIO_ID_NET:
|
||||
vdev->nr_vqs = 2;
|
||||
vdev->cmd_vr_idx = 0;
|
||||
cfg_size = sizeof(vdev->config.net);
|
||||
break;
|
||||
case VIRTIO_ID_BLOCK:
|
||||
vdev->nr_vqs = 1;
|
||||
vdev->cmd_vr_idx = 0;
|
||||
cfg_size = sizeof(vdev->config.blk);
|
||||
break;
|
||||
case VIRTIO_ID_SCSI:
|
||||
vdev->nr_vqs = 3;
|
||||
vdev->cmd_vr_idx = VR_REQUEST;
|
||||
cfg_size = sizeof(vdev->config.scsi);
|
||||
break;
|
||||
switch (vdev->ipl_type) {
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
case S390_IPL_TYPE_CCW:
|
||||
return virtio_ccw_reset(vdev);
|
||||
default:
|
||||
puts("Unsupported virtio device");
|
||||
return -ENODEV;
|
||||
return -1;
|
||||
}
|
||||
|
||||
status |= VIRTIO_CONFIG_S_DRIVER;
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
|
||||
puts("Could not write DRIVER status to host");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* Feature negotiation */
|
||||
for (i = 0; i < ARRAY_SIZE(vdev->guest_features); i++) {
|
||||
feats.features = 0;
|
||||
feats.index = i;
|
||||
if (run_ccw(vdev, CCW_CMD_READ_FEAT, &feats, sizeof(feats), false)) {
|
||||
puts("Could not get features bits");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
vdev->guest_features[i] &= bswap32(feats.features);
|
||||
feats.features = bswap32(vdev->guest_features[i]);
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_FEAT, &feats, sizeof(feats), false)) {
|
||||
puts("Could not set features bits");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
if (run_ccw(vdev, CCW_CMD_READ_CONF, &vdev->config, cfg_size, false)) {
|
||||
puts("Could not get virtio device configuration");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
for (i = 0; i < vdev->nr_vqs; i++) {
|
||||
VqInfo info = {
|
||||
.queue = (unsigned long long) ring_area + (i * VIRTIO_RING_SIZE),
|
||||
.align = KVM_S390_VIRTIO_RING_ALIGN,
|
||||
.index = i,
|
||||
.num = 0,
|
||||
};
|
||||
VqConfig config = {
|
||||
.index = i,
|
||||
.num = 0,
|
||||
};
|
||||
|
||||
if (run_ccw(vdev, CCW_CMD_READ_VQ_CONF, &config, sizeof(config),
|
||||
false)) {
|
||||
puts("Could not get virtio device VQ config");
|
||||
return -EIO;
|
||||
}
|
||||
info.num = config.num;
|
||||
vring_init(&vdev->vrings[i], &info);
|
||||
if (run_ccw(vdev, CCW_CMD_SET_VQ, &info, sizeof(info), false)) {
|
||||
puts("Cannot set VQ info");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
status |= VIRTIO_CONFIG_S_DRIVER_OK;
|
||||
if (run_ccw(vdev, CCW_CMD_WRITE_STATUS, &status, sizeof(status), false)) {
|
||||
puts("Could not write DRIVER_OK status to host");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool virtio_is_supported(SubChannelId schid)
|
||||
bool virtio_is_supported(VDev *vdev)
|
||||
{
|
||||
vdev.schid = schid;
|
||||
memset(&vdev.senseid, 0, sizeof(vdev.senseid));
|
||||
|
||||
/*
|
||||
* Run sense id command.
|
||||
* The size of the senseid data differs between devices (notably,
|
||||
* between virtio devices and dasds), so specify the largest possible
|
||||
* size and suppress the incorrect length indication for smaller sizes.
|
||||
*/
|
||||
if (run_ccw(&vdev, CCW_CMD_SENSE_ID, &vdev.senseid, sizeof(vdev.senseid),
|
||||
true)) {
|
||||
switch (vdev->ipl_type) {
|
||||
case S390_IPL_TYPE_QEMU_SCSI:
|
||||
case S390_IPL_TYPE_CCW:
|
||||
return virtio_ccw_is_supported(vdev);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
vdev.dev_type = vdev.senseid.cu_model;
|
||||
|
||||
if (vdev.senseid.cu_type == 0x3832) {
|
||||
switch (vdev.dev_type) {
|
||||
case VIRTIO_ID_BLOCK:
|
||||
case VIRTIO_ID_SCSI:
|
||||
case VIRTIO_ID_NET:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -108,6 +108,7 @@ struct VRing {
|
||||
};
|
||||
typedef struct VRing VRing;
|
||||
|
||||
char *virtio_get_ring_area(int ring_num);
|
||||
|
||||
/***********************************************
|
||||
* Virtio block *
|
||||
@@ -269,6 +270,8 @@ struct VirtioCmd {
|
||||
};
|
||||
typedef struct VirtioCmd VirtioCmd;
|
||||
|
||||
void vring_init(VRing *vr, VqInfo *info);
|
||||
bool virtio_is_supported(VDev *vdev);
|
||||
bool vring_notify(VRing *vr);
|
||||
int drain_irqs(void);
|
||||
void vring_send_buf(VRing *vr, void *p, int len, int flags);
|
||||
@@ -283,7 +286,7 @@ int virtio_net_init(void *mac_addr);
|
||||
void virtio_net_deinit(void);
|
||||
|
||||
/* virtio-blkdev.c */
|
||||
int virtio_blk_setup_device(SubChannelId schid);
|
||||
int virtio_blk_setup_device(VDev *vdev);
|
||||
int virtio_read(unsigned long sector, void *load_addr);
|
||||
unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
|
||||
void *load_addr);
|
||||
|
||||
Reference in New Issue
Block a user