S390: ccw firmware: Add virtio device drivers
In order to boot, we need to be able to access a virtio-blk device through the CCW bus. Implement support for this. Signed-off-by: Alexander Graf <agraf@suse.de>
This commit is contained in:
parent
0369b2eb07
commit
1e17c2c15b
322
pc-bios/s390-ccw/cio.h
Normal file
322
pc-bios/s390-ccw/cio.h
Normal file
@ -0,0 +1,322 @@
|
||||
/*
|
||||
* Channel IO definitions
|
||||
*
|
||||
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* Inspired by various s390 headers in Linux 3.9.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef CIO_H
|
||||
#define CIO_H
|
||||
|
||||
/*
|
||||
* path management control word
|
||||
*/
|
||||
struct pmcw {
|
||||
__u32 intparm; /* interruption parameter */
|
||||
__u32 qf : 1; /* qdio facility */
|
||||
__u32 w : 1;
|
||||
__u32 isc : 3; /* interruption sublass */
|
||||
__u32 res5 : 3; /* reserved zeros */
|
||||
__u32 ena : 1; /* enabled */
|
||||
__u32 lm : 2; /* limit mode */
|
||||
__u32 mme : 2; /* measurement-mode enable */
|
||||
__u32 mp : 1; /* multipath mode */
|
||||
__u32 tf : 1; /* timing facility */
|
||||
__u32 dnv : 1; /* device number valid */
|
||||
__u32 dev : 16; /* device number */
|
||||
__u8 lpm; /* logical path mask */
|
||||
__u8 pnom; /* path not operational mask */
|
||||
__u8 lpum; /* last path used mask */
|
||||
__u8 pim; /* path installed mask */
|
||||
__u16 mbi; /* measurement-block index */
|
||||
__u8 pom; /* path operational mask */
|
||||
__u8 pam; /* path available mask */
|
||||
__u8 chpid[8]; /* CHPID 0-7 (if available) */
|
||||
__u32 unused1 : 8; /* reserved zeros */
|
||||
__u32 st : 3; /* subchannel type */
|
||||
__u32 unused2 : 18; /* reserved zeros */
|
||||
__u32 mbfc : 1; /* measurement block format control */
|
||||
__u32 xmwme : 1; /* extended measurement word mode enable */
|
||||
__u32 csense : 1; /* concurrent sense; can be enabled ...*/
|
||||
/* ... per MSCH, however, if facility */
|
||||
/* ... is not installed, this results */
|
||||
/* ... in an operand exception. */
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* Target SCHIB configuration. */
|
||||
struct schib_config {
|
||||
__u64 mba;
|
||||
__u32 intparm;
|
||||
__u16 mbi;
|
||||
__u32 isc:3;
|
||||
__u32 ena:1;
|
||||
__u32 mme:2;
|
||||
__u32 mp:1;
|
||||
__u32 csense:1;
|
||||
__u32 mbfc:1;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct scsw {
|
||||
__u16 flags;
|
||||
__u16 ctrl;
|
||||
__u32 cpa;
|
||||
__u8 dstat;
|
||||
__u8 cstat;
|
||||
__u16 count;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define SCSW_FCTL_CLEAR_FUNC 0x1000
|
||||
#define SCSW_FCTL_HALT_FUNC 0x2000
|
||||
#define SCSW_FCTL_START_FUNC 0x4000
|
||||
|
||||
/*
|
||||
* subchannel information block
|
||||
*/
|
||||
struct schib {
|
||||
struct pmcw pmcw; /* path management control word */
|
||||
struct scsw scsw; /* subchannel status word */
|
||||
__u64 mba; /* measurement block address */
|
||||
__u8 mda[4]; /* model dependent area */
|
||||
} __attribute__ ((packed,aligned(4)));
|
||||
|
||||
struct subchannel_id {
|
||||
__u32 cssid : 8;
|
||||
__u32 : 4;
|
||||
__u32 m : 1;
|
||||
__u32 ssid : 2;
|
||||
__u32 one : 1;
|
||||
__u32 sch_no : 16;
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
|
||||
/*
|
||||
* TPI info structure
|
||||
*/
|
||||
struct tpi_info {
|
||||
struct subchannel_id schid;
|
||||
__u32 intparm; /* interruption parameter */
|
||||
__u32 adapter_IO : 1;
|
||||
__u32 reserved2 : 1;
|
||||
__u32 isc : 3;
|
||||
__u32 reserved3 : 12;
|
||||
__u32 int_type : 3;
|
||||
__u32 reserved4 : 12;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
/* channel command word (type 1) */
|
||||
struct ccw1 {
|
||||
__u8 cmd_code;
|
||||
__u8 flags;
|
||||
__u16 count;
|
||||
__u32 cda;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define CCW_FLAG_DC 0x80
|
||||
#define CCW_FLAG_CC 0x40
|
||||
#define CCW_FLAG_SLI 0x20
|
||||
#define CCW_FLAG_SKIP 0x10
|
||||
#define CCW_FLAG_PCI 0x08
|
||||
#define CCW_FLAG_IDA 0x04
|
||||
#define CCW_FLAG_SUSPEND 0x02
|
||||
|
||||
#define CCW_CMD_NOOP 0x03
|
||||
#define CCW_CMD_BASIC_SENSE 0x04
|
||||
#define CCW_CMD_TIC 0x08
|
||||
#define CCW_CMD_SENSE_ID 0xe4
|
||||
|
||||
#define CCW_CMD_SET_VQ 0x13
|
||||
#define CCW_CMD_VDEV_RESET 0x33
|
||||
#define CCW_CMD_READ_FEAT 0x12
|
||||
#define CCW_CMD_WRITE_FEAT 0x11
|
||||
#define CCW_CMD_READ_CONF 0x22
|
||||
#define CCW_CMD_WRITE_CONF 0x21
|
||||
#define CCW_CMD_WRITE_STATUS 0x31
|
||||
#define CCW_CMD_SET_IND 0x43
|
||||
#define CCW_CMD_SET_CONF_IND 0x53
|
||||
#define CCW_CMD_READ_VQ_CONF 0x32
|
||||
|
||||
/*
|
||||
* Command-mode operation request block
|
||||
*/
|
||||
struct cmd_orb {
|
||||
__u32 intparm; /* interruption parameter */
|
||||
__u32 key:4; /* flags, like key, suspend control, etc. */
|
||||
__u32 spnd:1; /* suspend control */
|
||||
__u32 res1:1; /* reserved */
|
||||
__u32 mod:1; /* modification control */
|
||||
__u32 sync:1; /* synchronize control */
|
||||
__u32 fmt:1; /* format control */
|
||||
__u32 pfch:1; /* prefetch control */
|
||||
__u32 isic:1; /* initial-status-interruption control */
|
||||
__u32 alcc:1; /* address-limit-checking control */
|
||||
__u32 ssic:1; /* suppress-suspended-interr. control */
|
||||
__u32 res2:1; /* reserved */
|
||||
__u32 c64:1; /* IDAW/QDIO 64 bit control */
|
||||
__u32 i2k:1; /* IDAW 2/4kB block size control */
|
||||
__u32 lpm:8; /* logical path mask */
|
||||
__u32 ils:1; /* incorrect length */
|
||||
__u32 zero:6; /* reserved zeros */
|
||||
__u32 orbx:1; /* ORB extension control */
|
||||
__u32 cpa; /* channel program address */
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
|
||||
struct ciw {
|
||||
__u8 type;
|
||||
__u8 command;
|
||||
__u16 count;
|
||||
};
|
||||
|
||||
/*
|
||||
* sense-id response buffer layout
|
||||
*/
|
||||
struct senseid {
|
||||
/* common part */
|
||||
__u8 reserved; /* always 0x'FF' */
|
||||
__u16 cu_type; /* control unit type */
|
||||
__u8 cu_model; /* control unit model */
|
||||
__u16 dev_type; /* device type */
|
||||
__u8 dev_model; /* device model */
|
||||
__u8 unused; /* padding byte */
|
||||
/* extended part */
|
||||
struct ciw ciw[62];
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
|
||||
/* interruption response block */
|
||||
struct irb {
|
||||
struct scsw scsw;
|
||||
__u32 esw[5];
|
||||
__u32 ecw[8];
|
||||
__u32 emw[8];
|
||||
} __attribute__ ((packed, aligned(4)));
|
||||
|
||||
/*
|
||||
* Some S390 specific IO instructions as inline
|
||||
*/
|
||||
|
||||
static inline int stsch_err(struct subchannel_id schid, struct schib *addr)
|
||||
{
|
||||
register struct subchannel_id reg1 asm ("1") = schid;
|
||||
int ccode = -EIO;
|
||||
|
||||
asm volatile(
|
||||
" stsch 0(%3)\n"
|
||||
"0: ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
"1:\n"
|
||||
: "+d" (ccode), "=m" (*addr)
|
||||
: "d" (reg1), "a" (addr)
|
||||
: "cc");
|
||||
return ccode;
|
||||
}
|
||||
|
||||
static inline int msch(struct subchannel_id schid, struct schib *addr)
|
||||
{
|
||||
register struct subchannel_id reg1 asm ("1") = schid;
|
||||
int ccode;
|
||||
|
||||
asm volatile(
|
||||
" msch 0(%2)\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28"
|
||||
: "=d" (ccode)
|
||||
: "d" (reg1), "a" (addr), "m" (*addr)
|
||||
: "cc");
|
||||
return ccode;
|
||||
}
|
||||
|
||||
static inline int msch_err(struct subchannel_id schid, struct schib *addr)
|
||||
{
|
||||
register struct subchannel_id reg1 asm ("1") = schid;
|
||||
int ccode = -EIO;
|
||||
|
||||
asm volatile(
|
||||
" msch 0(%2)\n"
|
||||
"0: ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
"1:\n"
|
||||
: "+d" (ccode)
|
||||
: "d" (reg1), "a" (addr), "m" (*addr)
|
||||
: "cc");
|
||||
return ccode;
|
||||
}
|
||||
|
||||
static inline int tsch(struct subchannel_id schid, struct irb *addr)
|
||||
{
|
||||
register struct subchannel_id reg1 asm ("1") = schid;
|
||||
int ccode;
|
||||
|
||||
asm volatile(
|
||||
" tsch 0(%3)\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28"
|
||||
: "=d" (ccode), "=m" (*addr)
|
||||
: "d" (reg1), "a" (addr)
|
||||
: "cc");
|
||||
return ccode;
|
||||
}
|
||||
|
||||
static inline int ssch(struct subchannel_id schid, struct cmd_orb *addr)
|
||||
{
|
||||
register struct subchannel_id reg1 asm("1") = schid;
|
||||
int ccode = -EIO;
|
||||
|
||||
asm volatile(
|
||||
" ssch 0(%2)\n"
|
||||
"0: ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
"1:\n"
|
||||
: "+d" (ccode)
|
||||
: "d" (reg1), "a" (addr), "m" (*addr)
|
||||
: "cc", "memory");
|
||||
return ccode;
|
||||
}
|
||||
|
||||
static inline int csch(struct subchannel_id schid)
|
||||
{
|
||||
register struct subchannel_id reg1 asm("1") = schid;
|
||||
int ccode;
|
||||
|
||||
asm volatile(
|
||||
" csch\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28"
|
||||
: "=d" (ccode)
|
||||
: "d" (reg1)
|
||||
: "cc");
|
||||
return ccode;
|
||||
}
|
||||
|
||||
static inline int tpi(struct tpi_info *addr)
|
||||
{
|
||||
int ccode;
|
||||
|
||||
asm volatile(
|
||||
" tpi 0(%2)\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28"
|
||||
: "=d" (ccode), "=m" (*addr)
|
||||
: "a" (addr)
|
||||
: "cc");
|
||||
return ccode;
|
||||
}
|
||||
|
||||
static inline int chsc(void *chsc_area)
|
||||
{
|
||||
typedef struct { char _[4096]; } addr_type;
|
||||
int cc;
|
||||
|
||||
asm volatile(
|
||||
" .insn rre,0xb25f0000,%2,0\n"
|
||||
" ipm %0\n"
|
||||
" srl %0,28\n"
|
||||
: "=d" (cc), "=m" (*(addr_type *) chsc_area)
|
||||
: "d" (chsc_area), "m" (*(addr_type *) chsc_area)
|
||||
: "cc");
|
||||
return cc;
|
||||
}
|
||||
|
||||
#endif /* CIO_H */
|
274
pc-bios/s390-ccw/virtio.c
Normal file
274
pc-bios/s390-ccw/virtio.c
Normal file
@ -0,0 +1,274 @@
|
||||
/*
|
||||
* Virtio driver bits
|
||||
*
|
||||
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* 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 "s390-ccw.h"
|
||||
#include "virtio.h"
|
||||
|
||||
struct vring block;
|
||||
|
||||
static long kvm_hypercall(unsigned long nr, unsigned long param1,
|
||||
unsigned long param2)
|
||||
{
|
||||
register ulong r_nr asm("1") = nr;
|
||||
register ulong r_param1 asm("2") = param1;
|
||||
register ulong r_param2 asm("3") = param2;
|
||||
register long retval asm("2");
|
||||
|
||||
asm volatile ("diag 2,4,0x500"
|
||||
: "=d" (retval)
|
||||
: "d" (r_nr), "0" (r_param1), "r"(r_param2)
|
||||
: "memory", "cc");
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void virtio_notify(struct subchannel_id schid)
|
||||
{
|
||||
kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32*)&schid, 0);
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* Virtio functions *
|
||||
***********************************************/
|
||||
|
||||
static void drain_irqs(struct subchannel_id schid)
|
||||
{
|
||||
struct irb irb = {};
|
||||
while (1) {
|
||||
if (tsch(schid, &irb)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int run_ccw(struct subchannel_id schid, int cmd, void *ptr, int len)
|
||||
{
|
||||
struct ccw1 ccw = {};
|
||||
struct cmd_orb orb = {};
|
||||
struct schib schib;
|
||||
int r;
|
||||
|
||||
/* start command processing */
|
||||
stsch_err(schid, &schib);
|
||||
schib.scsw.ctrl = SCSW_FCTL_START_FUNC;
|
||||
msch(schid, &schib);
|
||||
|
||||
/* start subchannel command */
|
||||
orb.fmt = 1;
|
||||
orb.cpa = (u32)(long)&ccw;
|
||||
orb.lpm = 0x80;
|
||||
|
||||
ccw.cmd_code = cmd;
|
||||
ccw.cda = (long)ptr;
|
||||
ccw.count = len;
|
||||
|
||||
r = ssch(schid, &orb);
|
||||
/*
|
||||
* XXX Wait until device is done processing the CCW. For now we can
|
||||
* assume that a simple tsch will have finished the CCW processing,
|
||||
* but the architecture allows for asynchronous operation
|
||||
*/
|
||||
drain_irqs(schid);
|
||||
return r;
|
||||
}
|
||||
|
||||
static void virtio_set_status(struct subchannel_id schid,
|
||||
unsigned long dev_addr)
|
||||
{
|
||||
unsigned char status = dev_addr;
|
||||
run_ccw(schid, CCW_CMD_WRITE_STATUS, &status, sizeof(status));
|
||||
}
|
||||
|
||||
static void virtio_reset(struct subchannel_id schid)
|
||||
{
|
||||
run_ccw(schid, CCW_CMD_VDEV_RESET, NULL, 0);
|
||||
}
|
||||
|
||||
static void vring_init(struct vring *vr, unsigned int num, void *p,
|
||||
unsigned long align)
|
||||
{
|
||||
debug_print_addr("init p", p);
|
||||
vr->num = num;
|
||||
vr->desc = p;
|
||||
vr->avail = p + num*sizeof(struct vring_desc);
|
||||
vr->used = (void *)(((unsigned long)&vr->avail->ring[num] + align-1)
|
||||
& ~(align - 1));
|
||||
|
||||
/* We're running with interrupts off anyways, so don't bother */
|
||||
vr->used->flags = VRING_USED_F_NO_NOTIFY;
|
||||
|
||||
debug_print_addr("init vr", vr);
|
||||
}
|
||||
|
||||
static void vring_notify(struct subchannel_id schid)
|
||||
{
|
||||
virtio_notify(schid);
|
||||
}
|
||||
|
||||
static void vring_send_buf(struct vring *vr, void *p, int len, int flags)
|
||||
{
|
||||
/* For follow-up chains we need to keep the first entry point */
|
||||
if (!(flags & VRING_HIDDEN_IS_CHAIN)) {
|
||||
vr->avail->ring[vr->avail->idx % vr->num] = vr->next_idx;
|
||||
}
|
||||
|
||||
vr->desc[vr->next_idx].addr = (ulong)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;
|
||||
|
||||
/* Chains only have a single ID */
|
||||
if (!(flags & VRING_DESC_F_NEXT)) {
|
||||
vr->avail->idx++;
|
||||
}
|
||||
|
||||
vr->used->idx = vr->next_idx;
|
||||
}
|
||||
|
||||
static u64 get_clock(void)
|
||||
{
|
||||
u64 r;
|
||||
|
||||
asm volatile("stck %0" : "=Q" (r) : : "cc");
|
||||
return r;
|
||||
}
|
||||
|
||||
static ulong get_second(void)
|
||||
{
|
||||
return (get_clock() >> 12) / 1000000;
|
||||
}
|
||||
|
||||
/*
|
||||
* Wait for the host to reply.
|
||||
*
|
||||
* timeout is in seconds if > 0.
|
||||
*
|
||||
* Returns 0 on success, 1 on timeout.
|
||||
*/
|
||||
static int vring_wait_reply(struct vring *vr, int timeout)
|
||||
{
|
||||
ulong target_second = get_second() + timeout;
|
||||
struct subchannel_id schid = vr->schid;
|
||||
int r = 0;
|
||||
|
||||
while (vr->used->idx == vr->next_idx) {
|
||||
vring_notify(schid);
|
||||
if (timeout && (get_second() >= target_second)) {
|
||||
r = 1;
|
||||
break;
|
||||
}
|
||||
yield();
|
||||
}
|
||||
|
||||
vr->next_idx = 0;
|
||||
vr->desc[0].len = 0;
|
||||
vr->desc[0].flags = 0;
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
* Virtio block *
|
||||
***********************************************/
|
||||
|
||||
static int virtio_read_many(ulong sector, void *load_addr, int sec_num)
|
||||
{
|
||||
struct virtio_blk_outhdr out_hdr;
|
||||
u8 status;
|
||||
|
||||
/* Tell the host we want to read */
|
||||
out_hdr.type = VIRTIO_BLK_T_IN;
|
||||
out_hdr.ioprio = 99;
|
||||
out_hdr.sector = sector;
|
||||
|
||||
vring_send_buf(&block, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT);
|
||||
|
||||
/* This is where we want to receive data */
|
||||
vring_send_buf(&block, load_addr, SECTOR_SIZE * sec_num,
|
||||
VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
|
||||
VRING_DESC_F_NEXT);
|
||||
|
||||
/* status field */
|
||||
vring_send_buf(&block, &status, sizeof(u8), VRING_DESC_F_WRITE |
|
||||
VRING_HIDDEN_IS_CHAIN);
|
||||
|
||||
/* Now we can tell the host to read */
|
||||
vring_wait_reply(&block, 0);
|
||||
|
||||
drain_irqs(block.schid);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
|
||||
ulong subchan_id, void *load_addr)
|
||||
{
|
||||
u8 status;
|
||||
int sec = rec_list1;
|
||||
int sec_num = (((rec_list2 >> 32)+ 1) & 0xffff);
|
||||
int sec_len = rec_list2 >> 48;
|
||||
ulong addr = (ulong)load_addr;
|
||||
|
||||
if (sec_len != SECTOR_SIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
sclp_print(".");
|
||||
status = virtio_read_many(sec, (void*)addr, sec_num);
|
||||
if (status) {
|
||||
virtio_panic("I/O Error");
|
||||
}
|
||||
addr += sec_num * SECTOR_SIZE;
|
||||
|
||||
return addr;
|
||||
}
|
||||
|
||||
int virtio_read(ulong sector, void *load_addr)
|
||||
{
|
||||
return virtio_read_many(sector, load_addr, 1);
|
||||
}
|
||||
|
||||
void virtio_setup_block(struct subchannel_id schid)
|
||||
{
|
||||
struct vq_info_block info;
|
||||
|
||||
virtio_reset(schid);
|
||||
|
||||
/* XXX need to fetch the 128 from host */
|
||||
vring_init(&block, 128, (void*)(100 * 1024 * 1024),
|
||||
KVM_S390_VIRTIO_RING_ALIGN);
|
||||
|
||||
info.queue = (100ULL * 1024ULL* 1024ULL);
|
||||
info.align = KVM_S390_VIRTIO_RING_ALIGN;
|
||||
info.index = 0;
|
||||
info.num = 128;
|
||||
block.schid = schid;
|
||||
|
||||
run_ccw(schid, CCW_CMD_SET_VQ, &info, sizeof(info));
|
||||
virtio_set_status(schid, VIRTIO_CONFIG_S_DRIVER_OK);
|
||||
}
|
||||
|
||||
bool virtio_is_blk(struct subchannel_id schid)
|
||||
{
|
||||
int r;
|
||||
struct senseid senseid = {};
|
||||
|
||||
/* run sense id command */
|
||||
r = run_ccw(schid, CCW_CMD_SENSE_ID, &senseid, sizeof(senseid));
|
||||
if (r) {
|
||||
return false;
|
||||
}
|
||||
if ((senseid.cu_type != 0x3832) || (senseid.cu_model != VIRTIO_ID_BLOCK)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
158
pc-bios/s390-ccw/virtio.h
Normal file
158
pc-bios/s390-ccw/virtio.h
Normal file
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Virtio driver bits
|
||||
*
|
||||
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef VIRTIO_H
|
||||
#define VIRTIO_H
|
||||
|
||||
#include "s390-ccw.h"
|
||||
|
||||
/* Status byte for guest to report progress, and synchronize features. */
|
||||
/* We have seen device and processed generic fields (VIRTIO_CONFIG_F_VIRTIO) */
|
||||
#define VIRTIO_CONFIG_S_ACKNOWLEDGE 1
|
||||
/* We have found a driver for the device. */
|
||||
#define VIRTIO_CONFIG_S_DRIVER 2
|
||||
/* Driver has used its parts of the config, and is happy */
|
||||
#define VIRTIO_CONFIG_S_DRIVER_OK 4
|
||||
/* We've given up on this device. */
|
||||
#define VIRTIO_CONFIG_S_FAILED 0x80
|
||||
|
||||
enum virtio_dev_type {
|
||||
VIRTIO_ID_NET = 1,
|
||||
VIRTIO_ID_BLOCK = 2,
|
||||
VIRTIO_ID_CONSOLE = 3,
|
||||
VIRTIO_ID_BALLOON = 5,
|
||||
};
|
||||
|
||||
struct virtio_dev_header {
|
||||
enum virtio_dev_type type : 8;
|
||||
u8 num_vq;
|
||||
u8 feature_len;
|
||||
u8 config_len;
|
||||
u8 status;
|
||||
u8 vqconfig[];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct virtio_vqconfig {
|
||||
u64 token;
|
||||
u64 address;
|
||||
u16 num;
|
||||
u8 pad[6];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct vq_info_block {
|
||||
u64 queue;
|
||||
u32 align;
|
||||
u16 index;
|
||||
u16 num;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct virtio_dev {
|
||||
struct virtio_dev_header *header;
|
||||
struct virtio_vqconfig *vqconfig;
|
||||
char *host_features;
|
||||
char *guest_features;
|
||||
char *config;
|
||||
};
|
||||
|
||||
#define KVM_S390_VIRTIO_RING_ALIGN 4096
|
||||
|
||||
#define VRING_USED_F_NO_NOTIFY 1
|
||||
|
||||
/* This marks a buffer as continuing via the next field. */
|
||||
#define VRING_DESC_F_NEXT 1
|
||||
/* This marks a buffer as write-only (otherwise read-only). */
|
||||
#define VRING_DESC_F_WRITE 2
|
||||
/* This means the buffer contains a list of buffer descriptors. */
|
||||
#define VRING_DESC_F_INDIRECT 4
|
||||
|
||||
/* Internal flag to mark follow-up segments as such */
|
||||
#define VRING_HIDDEN_IS_CHAIN 256
|
||||
|
||||
/* Virtio ring descriptors: 16 bytes. These can chain together via "next". */
|
||||
struct vring_desc {
|
||||
/* Address (guest-physical). */
|
||||
u64 addr;
|
||||
/* Length. */
|
||||
u32 len;
|
||||
/* The flags as indicated above. */
|
||||
u16 flags;
|
||||
/* We chain unused descriptors via this, too */
|
||||
u16 next;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct vring_avail {
|
||||
u16 flags;
|
||||
u16 idx;
|
||||
u16 ring[];
|
||||
} __attribute__((packed));
|
||||
|
||||
/* u32 is used here for ids for padding reasons. */
|
||||
struct vring_used_elem {
|
||||
/* Index of start of used descriptor chain. */
|
||||
u32 id;
|
||||
/* Total length of the descriptor chain which was used (written to) */
|
||||
u32 len;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct vring_used {
|
||||
u16 flags;
|
||||
u16 idx;
|
||||
struct vring_used_elem ring[];
|
||||
} __attribute__((packed));
|
||||
|
||||
struct vring {
|
||||
unsigned int num;
|
||||
int next_idx;
|
||||
struct vring_desc *desc;
|
||||
struct vring_avail *avail;
|
||||
struct vring_used *used;
|
||||
struct subchannel_id schid;
|
||||
};
|
||||
|
||||
|
||||
/***********************************************
|
||||
* Virtio block *
|
||||
***********************************************/
|
||||
|
||||
/*
|
||||
* Command types
|
||||
*
|
||||
* Usage is a bit tricky as some bits are used as flags and some are not.
|
||||
*
|
||||
* Rules:
|
||||
* VIRTIO_BLK_T_OUT may be combined with VIRTIO_BLK_T_SCSI_CMD or
|
||||
* VIRTIO_BLK_T_BARRIER. VIRTIO_BLK_T_FLUSH is a command of its own
|
||||
* and may not be combined with any of the other flags.
|
||||
*/
|
||||
|
||||
/* These two define direction. */
|
||||
#define VIRTIO_BLK_T_IN 0
|
||||
#define VIRTIO_BLK_T_OUT 1
|
||||
|
||||
/* This bit says it's a scsi command, not an actual read or write. */
|
||||
#define VIRTIO_BLK_T_SCSI_CMD 2
|
||||
|
||||
/* Cache flush command */
|
||||
#define VIRTIO_BLK_T_FLUSH 4
|
||||
|
||||
/* Barrier before this op. */
|
||||
#define VIRTIO_BLK_T_BARRIER 0x80000000
|
||||
|
||||
/* This is the first element of the read scatter-gather list. */
|
||||
struct virtio_blk_outhdr {
|
||||
/* VIRTIO_BLK_T* */
|
||||
u32 type;
|
||||
/* io priority. */
|
||||
u32 ioprio;
|
||||
/* Sector (ie. 512 byte offset) */
|
||||
u64 sector;
|
||||
};
|
||||
|
||||
#endif /* VIRTIO_H */
|
Loading…
Reference in New Issue
Block a user