mirror of
https://github.com/qemu/qemu.git
synced 2026-07-08 17:46:17 +00:00
The virtio specification requires that each PCI device has a vendor ID of 0x1af4. Verify this value before continuing with boot process. Signed-off-by: Jared Rossi <jrossi@linux.ibm.com> Reviewed-by: Eric Farman <farman@linux.ibm.com> Tested-by: Matthew Rosato <mjrosato@linux.ibm.com> Message-ID: <20260630141917.673995-5-jrossi@linux.ibm.com> Signed-off-by: Cornelia Huck <cohuck@redhat.com>
256 lines
5.9 KiB
C
256 lines
5.9 KiB
C
/*
|
|
* 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
|
|
* directory.
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include "s390-ccw.h"
|
|
#include "cio.h"
|
|
#include "virtio.h"
|
|
#include "virtio-scsi.h"
|
|
#include "virtio-ccw.h"
|
|
#include "virtio-pci.h"
|
|
#include "bswap.h"
|
|
#include "helper.h"
|
|
#include "s390-time.h"
|
|
|
|
#define VRING_WAIT_REPLY_TIMEOUT 30
|
|
|
|
static VRing block[VIRTIO_MAX_VQS];
|
|
static char ring_area[VIRTIO_RING_SIZE * VIRTIO_MAX_VQS]
|
|
__attribute__((__aligned__(PAGE_SIZE)));
|
|
|
|
static VDev vdev = {
|
|
.nr_vqs = 1,
|
|
.vrings = block,
|
|
.cmd_vr_idx = 0,
|
|
.ring_area = ring_area,
|
|
.wait_reply_timeout = VRING_WAIT_REPLY_TIMEOUT,
|
|
.schid = { .one = 1 },
|
|
.scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE,
|
|
.blk_factor = 1,
|
|
};
|
|
|
|
VDev *virtio_get_device(void)
|
|
{
|
|
return &vdev;
|
|
}
|
|
|
|
VirtioDevType virtio_get_device_type(void)
|
|
{
|
|
return vdev.dev_type;
|
|
}
|
|
|
|
char *virtio_get_ring_area(int ring_num)
|
|
{
|
|
return ring_area + ring_num * VIRTIO_RING_SIZE;
|
|
}
|
|
|
|
/***********************************************
|
|
* Virtio functions *
|
|
***********************************************/
|
|
|
|
int drain_irqs(void)
|
|
{
|
|
switch (vdev.ipl_type) {
|
|
case S390_IPL_TYPE_QEMU_SCSI:
|
|
case S390_IPL_TYPE_CCW:
|
|
return drain_irqs_ccw(vdev.schid);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int virtio_run(VDev *vdev, int vqid, VirtioCmd *cmd)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
|
|
void vring_init(VRing *vr, VqInfo *info)
|
|
{
|
|
void *p = (void *) info->queue;
|
|
|
|
debug_print_addr("init p", p);
|
|
vr->id = info->index;
|
|
vr->num = info->num;
|
|
vr->desc = p;
|
|
vr->avail = p + info->num * sizeof(VRingDesc);
|
|
vr->used = (void *)(((unsigned long)&vr->avail->ring[info->num]
|
|
+ info->align - 1) & ~(info->align - 1));
|
|
|
|
/* Zero out all relevant field */
|
|
vr->avail->flags = 0;
|
|
vr->avail->idx = 0;
|
|
|
|
/* We're running with interrupts off anyways, so don't bother */
|
|
vr->used->flags = be_ipl() ? VRING_USED_F_NO_NOTIFY : bswap16(VRING_USED_F_NO_NOTIFY);
|
|
vr->used->idx = 0;
|
|
vr->used_idx = 0;
|
|
vr->next_idx = 0;
|
|
vr->cookie = 0;
|
|
|
|
debug_print_addr("init vr", vr);
|
|
}
|
|
|
|
bool vring_notify(VRing *vr)
|
|
{
|
|
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;
|
|
case S390_IPL_TYPE_PCI:
|
|
vr->cookie = virtio_pci_notify(vr);
|
|
break;
|
|
default:
|
|
return 1;
|
|
}
|
|
|
|
return vr->cookie >= 0;
|
|
}
|
|
|
|
/*
|
|
* Get endienness of the IPL type
|
|
* Return true for s390x native big-endian
|
|
*/
|
|
bool be_ipl(void)
|
|
{
|
|
switch (virtio_get_device()->ipl_type) {
|
|
case S390_IPL_TYPE_QEMU_SCSI:
|
|
case S390_IPL_TYPE_CCW:
|
|
return true;
|
|
case S390_IPL_TYPE_PCI:
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Format the virtio ring descriptor endianness
|
|
* Return the available index increment in the appropriate endianness
|
|
*/
|
|
static void vr_bswap_descriptor(VRingDesc *desc)
|
|
{
|
|
desc->addr = bswap64(desc->addr);
|
|
desc->len = bswap32(desc->len);
|
|
desc->flags = bswap16(desc->flags);
|
|
desc->next = bswap16(desc->next);
|
|
}
|
|
|
|
void vring_send_buf(VRing *vr, void *p, int len, int flags)
|
|
{
|
|
uint16_t avail_idx;
|
|
|
|
avail_idx = be_ipl() ? vr->avail->idx : bswap16(vr->avail->idx);
|
|
|
|
/* For follow-up chains we need to keep the first entry point */
|
|
if (!(flags & VRING_HIDDEN_IS_CHAIN)) {
|
|
vr->avail->ring[avail_idx % vr->num] = be_ipl() ? vr->next_idx :
|
|
bswap16(vr->next_idx);
|
|
}
|
|
|
|
vr->desc[vr->next_idx].addr = (unsigned long)p;
|
|
vr->desc[vr->next_idx].len = len;
|
|
vr->desc[vr->next_idx].flags = flags & ~VRING_HIDDEN_IS_CHAIN;
|
|
vr->desc[vr->next_idx].next = vr->next_idx;
|
|
vr->desc[vr->next_idx].next++;
|
|
|
|
if (!be_ipl()) {
|
|
vr_bswap_descriptor(&vr->desc[vr->next_idx]);
|
|
}
|
|
|
|
vr->next_idx++;
|
|
|
|
/* Chains only have a single ID */
|
|
if (!(flags & VRING_DESC_F_NEXT)) {
|
|
avail_idx++;
|
|
vr->avail->idx = be_ipl() ? avail_idx : bswap16(avail_idx);
|
|
}
|
|
}
|
|
|
|
int vr_poll(VRing *vr)
|
|
{
|
|
uint16_t used_idx;
|
|
|
|
used_idx = be_ipl() ? vr->used->idx : bswap16(vr->used->idx);
|
|
if (used_idx == vr->used_idx) {
|
|
vring_notify(vr);
|
|
yield();
|
|
return 0;
|
|
}
|
|
|
|
vr->used_idx = used_idx;
|
|
vr->next_idx = 0;
|
|
vr->desc[0].len = 0;
|
|
vr->desc[0].flags = 0;
|
|
return 1; /* vr has been updated */
|
|
}
|
|
|
|
/*
|
|
* Wait for the host to reply.
|
|
*
|
|
* timeout is in seconds if > 0.
|
|
*
|
|
* Returns 0 on success, 1 on timeout.
|
|
*/
|
|
int vring_wait_reply(void)
|
|
{
|
|
unsigned long target_second = get_time_seconds() + vdev.wait_reply_timeout;
|
|
|
|
/* Wait for any queue to be updated by the host */
|
|
do {
|
|
int i, r = 0;
|
|
|
|
for (i = 0; i < vdev.nr_vqs; i++) {
|
|
r += vr_poll(&vdev.vrings[i]);
|
|
}
|
|
yield();
|
|
if (r) {
|
|
return 0;
|
|
}
|
|
} while (!vdev.wait_reply_timeout || (get_time_seconds() < target_second));
|
|
|
|
return 1;
|
|
}
|
|
|
|
int virtio_reset(VDev *vdev)
|
|
{
|
|
switch (vdev->ipl_type) {
|
|
case S390_IPL_TYPE_QEMU_SCSI:
|
|
case S390_IPL_TYPE_CCW:
|
|
return virtio_ccw_reset(vdev);
|
|
case S390_IPL_TYPE_PCI:
|
|
return virtio_pci_reset(vdev);
|
|
default:
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
bool virtio_is_supported(VDev *vdev)
|
|
{
|
|
switch (vdev->ipl_type) {
|
|
case S390_IPL_TYPE_QEMU_SCSI:
|
|
case S390_IPL_TYPE_CCW:
|
|
return virtio_ccw_is_supported(vdev);
|
|
case S390_IPL_TYPE_PCI:
|
|
return virtio_pci_is_supported(vdev);
|
|
default:
|
|
return false;
|
|
}
|
|
}
|