On Mon, 22 Apr 2013 21:18:13 +0200 Alexander Graf <ag...@suse.de> 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 <ag...@suse.de> > --- > 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 <ag...@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)); 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 <ag...@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; > + } > + } > +} 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 <ag...@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 */