qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH 07/12] S390: ccw firmware: Add virtio device dri


From: Cornelia Huck
Subject: Re: [Qemu-devel] [PATCH 07/12] S390: ccw firmware: Add virtio device drivers
Date: Tue, 23 Apr 2013 13:24:29 +0200

On Mon, 22 Apr 2013 21:18:13 +0200
Alexander Graf <address@hidden> wrote:

> 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 <address@hidden>
> ---
>  pc-bios/s390-ccw/cio.h    |  322 
> +++++++++++++++++++++++++++++++++++++++++++++
>  pc-bios/s390-ccw/virtio.c |  274 ++++++++++++++++++++++++++++++++++++++
>  pc-bios/s390-ccw/virtio.h |  158 ++++++++++++++++++++++
>  3 files changed, 754 insertions(+), 0 deletions(-)
>  create mode 100644 pc-bios/s390-ccw/cio.h
>  create mode 100644 pc-bios/s390-ccw/virtio.c
>  create mode 100644 pc-bios/s390-ccw/virtio.h
> 
> diff --git a/pc-bios/s390-ccw/cio.h b/pc-bios/s390-ccw/cio.h
> new file mode 100644
> index 0000000..cb5815a
> --- /dev/null
> +++ b/pc-bios/s390-ccw/cio.h
> @@ -0,0 +1,322 @@
> +/*
> + * Channel IO definitions
> + *
> + * Copyright (c) 2013 Alexander Graf <address@hidden>
> + *
> + * 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));

I'd be surprised if you needed that structure.

> +
> +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)));

Just make this orb. I doubt we want to emulate transport mode :)

> +
> +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)

You'll probably won't need chsc, unless you want to support ipling from
subchannel sets > 0.

> +{
> +    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 */
> diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
> new file mode 100644
> index 0000000..79e2941
> --- /dev/null
> +++ b/pc-bios/s390-ccw/virtio.c
> @@ -0,0 +1,274 @@
> +/*
> + * Virtio driver bits
> + *
> + * Copyright (c) 2013 Alexander Graf <address@hidden>
> + *
> + * 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;
> +        }
> +    }
> +}

Please convert this to a tpi-loop with tsch on interrupt, and
actually return an error if the irb has an error condition (see the
guest virtio_ccw driver for an example).

> +
> +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;

Not needed.

> +    msch(schid, &schib);

Hmm... where do you set pmcw.ena? If you can do I/O on disabled
subchannels, this is a bug in the virtual css :)

> +
> +    /* start subchannel command */
> +    orb.fmt = 1;
> +    orb.cpa = (u32)(long)&ccw;

Is this guaranteed to always work?

> +    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
> +     */

And that's why you should use a tpi-loop + tsch :)

> +    drain_irqs(schid);

You don't need to collect status if ssch() returned an error, but you
should return errors indicated in the irb as well.

> +    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));

It might be a good idea to do virtio_panic() on error (also some other
callers of run_ccw()).

> +}
> +
> +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);

Collect error from irb?

> +
> +    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 */

Please ;)

> +    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));

No host->guest notifications, right? Otherwise you'd be dead without
indicators. (Mental note to self: make sure virtio-ccw handles this
fine.)

> +    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;
> +}
> +
> diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
> new file mode 100644
> index 0000000..a33199d
> --- /dev/null
> +++ b/pc-bios/s390-ccw/virtio.h
> @@ -0,0 +1,158 @@
> +/*
> + * Virtio driver bits
> + *
> + * Copyright (c) 2013 Alexander Graf <address@hidden>
> + *
> + * 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 */




reply via email to

[Prev in Thread] Current Thread [Next in Thread]