diff options
author | Mark Kettenis <kettenis@cvs.openbsd.org> | 2009-12-31 11:58:42 +0000 |
---|---|---|
committer | Mark Kettenis <kettenis@cvs.openbsd.org> | 2009-12-31 11:58:42 +0000 |
commit | c48e4ae3ee447184c63263c049f6a66b6114da52 (patch) | |
tree | 98e210696795cfe72b3cef15aa31ba9e2564791f /sys/arch/sparc64/dev | |
parent | 9c7eefdd05232a7b6317a4f205cfd812a5f2a68b (diff) |
Add vds(4) and vdsp(4), implementing the vDisk server side protocol. This
makes it possible to run an OpenBSD guest domain on top of a virtiual disk
backed by a disk image on a filesystem on OpenBSD control or service domain.
Probably doesn't support running Solaris or Linux yet. Haven't tried those
yet because booting their installers in a guest domain is way too painful!
Diffstat (limited to 'sys/arch/sparc64/dev')
-rw-r--r-- | sys/arch/sparc64/dev/vds.c | 137 | ||||
-rw-r--r-- | sys/arch/sparc64/dev/vdsp.c | 1110 |
2 files changed, 1247 insertions, 0 deletions
diff --git a/sys/arch/sparc64/dev/vds.c b/sys/arch/sparc64/dev/vds.c new file mode 100644 index 00000000000..b02d7473a06 --- /dev/null +++ b/sys/arch/sparc64/dev/vds.c @@ -0,0 +1,137 @@ +/* $OpenBSD: vds.c,v 1.1 2009/12/31 11:58:41 kettenis Exp $ */ +/* + * Copyright (c) 2009 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/systm.h> + +#include <machine/autoconf.h> +#include <machine/hypervisor.h> +#include <machine/mdesc.h> + +#include <sparc64/dev/cbusvar.h> + +#ifdef VDS_DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +struct vds_softc { + struct device sc_dv; + bus_space_tag_t sc_bustag; + bus_dma_tag_t sc_dmatag; +}; + +int vds_match(struct device *, void *, void *); +void vds_attach(struct device *, struct device *, void *); + +struct cfattach vds_ca = { + sizeof(struct vds_softc), vds_match, vds_attach +}; + +struct cfdriver vds_cd = { + NULL, "vds", DV_DULL +}; + +void vds_get_channel_endpoint(int, struct cbus_attach_args *); + +int +vds_match(struct device *parent, void *match, void *aux) +{ + struct cbus_attach_args *ca = aux; + + if (strcmp(ca->ca_name, "virtual-disk-server") == 0) + return (1); + + return (0); +} + +void +vds_attach(struct device *parent, struct device *self, void *aux) +{ + struct cbus_attach_args *ca = aux; + struct cbus_attach_args nca; + struct md_header *hdr; + struct md_element *elem; + const char *name_blk; + const char *str; + int idx; + int arc; + + printf("\n"); + + hdr = (struct md_header *)mdesc; + elem = (struct md_element *)(mdesc + sizeof(struct md_header)); + name_blk = mdesc + sizeof(struct md_header) + hdr->node_blk_sz; + + idx = ca->ca_idx; + for (; elem[idx].tag != 'E'; idx++) { + str = name_blk + elem[idx].name_offset; + if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) + continue; + + arc = elem[idx].d.val; + str = name_blk + elem[arc].name_offset; + if (strcmp(str, "virtual-device-port") == 0) { + bzero(&nca, sizeof(nca)); + nca.ca_name = "vds-port"; + nca.ca_node = ca->ca_node; + nca.ca_bustag = ca->ca_bustag; + nca.ca_dmatag = ca->ca_dmatag; + vds_get_channel_endpoint(arc, &nca); + config_found(self, &nca, cbus_print); + } + } +} + +void +vds_get_channel_endpoint(int idx, struct cbus_attach_args *ca) +{ + struct md_header *hdr; + struct md_element *elem; + const char *name_blk; + const char *str; + int arc; + + hdr = (struct md_header *)mdesc; + elem = (struct md_element *)(mdesc + sizeof(struct md_header)); + name_blk = mdesc + sizeof(struct md_header) + hdr->node_blk_sz; + + ca->ca_idx = idx; + + ca->ca_id = -1; + ca->ca_tx_ino = -1; + ca->ca_rx_ino = -1; + + for (; elem[idx].tag != 'E'; idx++) { + str = name_blk + elem[idx].name_offset; + if (elem[idx].tag != 'a' || strcmp(str, "fwd") != 0) + continue; + + arc = elem[idx].d.val; + str = name_blk + elem[arc].name_offset; + if (strcmp(str, "channel-endpoint") == 0) { + ca->ca_id = mdesc_get_prop_val(arc, "id"); + ca->ca_tx_ino = mdesc_get_prop_val(arc, "tx-ino"); + ca->ca_rx_ino = mdesc_get_prop_val(arc, "rx-ino"); + return; + } + } +} diff --git a/sys/arch/sparc64/dev/vdsp.c b/sys/arch/sparc64/dev/vdsp.c new file mode 100644 index 00000000000..6c0d286934a --- /dev/null +++ b/sys/arch/sparc64/dev/vdsp.c @@ -0,0 +1,1110 @@ +/* $OpenBSD: vdsp.c,v 1.1 2009/12/31 11:58:41 kettenis Exp $ */ +/* + * Copyright (c) 2009 Mark Kettenis + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/buf.h> +#include <sys/device.h> +#include <sys/fcntl.h> +#include <sys/malloc.h> +#include <sys/namei.h> +#include <sys/systm.h> +#include <sys/vnode.h> +#include <sys/workq.h> + +#include <machine/autoconf.h> +#include <machine/hypervisor.h> +#include <machine/mdesc.h> + +#include <uvm/uvm.h> + +#include <scsi/scsi_all.h> +#include <scsi/scsi_disk.h> +#include <scsi/scsiconf.h> + +#include <sparc64/dev/cbusvar.h> +#include <sparc64/dev/ldcvar.h> +#include <sparc64/dev/viovar.h> + +#ifdef VDSP_DEBUG +#define DPRINTF(x) printf x +#else +#define DPRINTF(x) +#endif + +#define VDSK_TX_ENTRIES 64 +#define VDSK_RX_ENTRIES 64 + +#define VDSK_MAX_DESCRIPTORS 1024 + +struct vd_attr_info { + struct vio_msg_tag tag; + uint8_t xfer_mode; + uint8_t vd_type; + uint8_t vd_mtype; + uint8_t _reserved1; + uint32_t vdisk_block_size; + uint64_t operations; + uint64_t vdisk_size; + uint64_t max_xfer_sz; + uint64_t _reserved2[2]; +}; + +#define VD_DISK_TYPE_SLICE 0x01 +#define VD_DISK_TYPE_DISK 0x02 + +#define VD_MEDIA_TYPE_FIXED 0x01 +#define VD_MEDIA_TYPE_CD 0x02 +#define VD_MEDIA_TYPE_DVD 0x03 + +/* vDisk version 1.0. */ +#define VD_OP_BREAD 0x01 +#define VD_OP_BWRITE 0x02 +#define VD_OP_FLUSH 0x03 +#define VD_OP_GET_WCE 0x04 +#define VD_OP_SET_WCE 0x05 +#define VD_OP_GET_VTOC 0x06 +#define VD_OP_SET_VTOC 0x07 +#define VD_OP_GET_DISKGEOM 0x08 +#define VD_OP_SET_DISKGEOM 0x09 +#define VD_OP_GET_DEVID 0x0b +#define VD_OP_GET_EFI 0x0c +#define VD_OP_SET_EFI 0x0d + +/* vDisk version 1.1 */ +#define VD_OP_SCSICMD 0x0a +#define VD_OP_RESET 0x0e +#define VD_OP_GET_ACCESS 0x0f +#define VD_OP_SET_ACCESS 0x10 +#define VD_OP_GET_CAPACITY 0x11 + +struct vd_desc { + struct vio_dring_hdr hdr; + uint64_t req_id; + uint8_t operation; + uint8_t slice; + uint16_t _reserved1; + uint32_t status; + uint64_t offset; + uint64_t size; + uint32_t ncookies; + uint32_t _reserved2; + struct ldc_cookie cookie[MAXPHYS / PAGE_SIZE]; +}; + +#define VD_SLICE_NONE 0xff + +struct vdsk_desc_msg { + struct vio_msg_tag tag; + uint64_t seq_no; + uint64_t desc_handle; + uint64_t req_id; + uint8_t operation; + uint8_t slice; + uint16_t _reserved1; + uint32_t status; + uint64_t offset; + uint64_t size; + uint32_t ncookies; + uint32_t _reserved2; + struct ldc_cookie cookie[1]; +}; + +/* + * For now, we only support vDisk 1.0. + */ +#define VDSK_MAJOR 1 +#define VDSK_MINOR 0 + +struct vdsp_softc { + struct device sc_dv; + int sc_idx; + bus_space_tag_t sc_bustag; + bus_dma_tag_t sc_dmatag; + + uint64_t sc_tx_sysino; + uint64_t sc_rx_sysino; + void *sc_tx_ih; + void *sc_rx_ih; + + struct ldc_conn sc_lc; + + uint16_t sc_vio_state; +#define VIO_SND_VER_INFO 0x0001 +#define VIO_ACK_VER_INFO 0x0002 +#define VIO_RCV_VER_INFO 0x0004 +#define VIO_SND_ATTR_INFO 0x0008 +#define VIO_ACK_ATTR_INFO 0x0010 +#define VIO_RCV_ATTR_INFO 0x0020 +#define VIO_SND_DRING_REG 0x0040 +#define VIO_ACK_DRING_REG 0x0080 +#define VIO_RCV_DRING_REG 0x0100 +#define VIO_SND_RDX 0x0200 +#define VIO_ACK_RDX 0x0400 +#define VIO_RCV_RDX 0x0800 + + uint16_t sc_major; + uint16_t sc_minor; + + uint8_t sc_xfer_mode; + + uint32_t sc_local_sid; + uint64_t sc_seq_no; + + uint64_t sc_dring_ident; + uint32_t sc_num_descriptors; + uint32_t sc_descriptor_size; + struct ldc_cookie sc_dring_cookie; + + struct vd_desc *sc_vd; + + int sc_tx_cnt; + int sc_tx_prod; + int sc_tx_cons; + + uint32_t sc_vdisk_block_size; + uint64_t sc_vdisk_size; + + struct vnode *sc_vp; +}; + +int vdsp_match(struct device *, void *, void *); +void vdsp_attach(struct device *, struct device *, void *); + +struct cfattach vdsp_ca = { + sizeof(struct vdsp_softc), vdsp_match, vdsp_attach +}; + +struct cfdriver vdsp_cd = { + NULL, "vdsp", DV_DULL +}; + +int vdsp_tx_intr(void *); +int vdsp_rx_intr(void *); + +void vdsp_rx_data(struct ldc_conn *, struct ldc_pkt *); +void vdsp_rx_vio_ctrl(struct vdsp_softc *, struct vio_msg *); +void vdsp_rx_vio_ver_info(struct vdsp_softc *, struct vio_msg_tag *); +void vdsp_rx_vio_attr_info(struct vdsp_softc *, struct vio_msg_tag *); +void vdsp_rx_vio_dring_reg(struct vdsp_softc *, struct vio_msg_tag *); +void vdsp_rx_vio_rdx(struct vdsp_softc *sc, struct vio_msg_tag *); +void vdsp_rx_vio_data(struct vdsp_softc *sc, struct vio_msg *); +void vdsp_rx_vio_dring_data(struct vdsp_softc *sc, + struct vio_msg_tag *); +void vdsp_rx_vio_desc_data(struct vdsp_softc *sc, struct vio_msg_tag *); + +void vdsp_ldc_reset(struct ldc_conn *); +void vdsp_ldc_start(struct ldc_conn *); + +void vdsp_sendmsg(struct vdsp_softc *, void *, size_t); + +void vdsp_mountroot(void *); +void vdsp_open(void *, void *); +void vdsp_alloc(void *, void *); +void vdsp_read(void *, void *); +void vdsp_read_dring(void *, void *); +void vdsp_write_dring(void *, void *); +void vdsp_flush_dring(void *, void *); + +int +vdsp_match(struct device *parent, void *match, void *aux) +{ + struct cbus_attach_args *ca = aux; + + if (strcmp(ca->ca_name, "vds-port") == 0) + return (1); + + return (0); +} + +void +vdsp_attach(struct device *parent, struct device *self, void *aux) +{ + struct vdsp_softc *sc = (struct vdsp_softc *)self; + struct cbus_attach_args *ca = aux; + struct ldc_conn *lc; + + sc->sc_idx = ca->ca_idx; + sc->sc_bustag = ca->ca_bustag; + sc->sc_dmatag = ca->ca_dmatag; + + if (cbus_intr_map(ca->ca_node, ca->ca_tx_ino, &sc->sc_tx_sysino) || + cbus_intr_map(ca->ca_node, ca->ca_rx_ino, &sc->sc_rx_sysino)) { + printf(": can't map interrupt\n"); + return; + } + printf(": ivec 0x%lx, 0x%lx", sc->sc_tx_sysino, sc->sc_tx_sysino); + + /* + * Un-configure queues before registering interrupt handlers, + * such that we dont get any stale LDC packets or events. + */ + hv_ldc_tx_qconf(ca->ca_id, 0, 0); + hv_ldc_rx_qconf(ca->ca_id, 0, 0); + + sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_sysino, + IPL_BIO, 0, vdsp_tx_intr, sc, sc->sc_dv.dv_xname); + sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_sysino, + IPL_BIO, 0, vdsp_rx_intr, sc, sc->sc_dv.dv_xname); + if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) { + printf(", can't establish interrupt\n"); + return; + } + + /* + * Disable interrupts while we have no queues allocated. + * Otherwise we may end up with an interrupt storm as soon as + * our peer places a packet in their transmit queue. + */ + cbus_intr_setenabled(sc->sc_tx_sysino, INTR_DISABLED); + cbus_intr_setenabled(sc->sc_rx_sysino, INTR_DISABLED); + + lc = &sc->sc_lc; + lc->lc_id = ca->ca_id; + lc->lc_sc = sc; + lc->lc_reset = vdsp_ldc_reset; + lc->lc_start = vdsp_ldc_start; + lc->lc_rx_data = vdsp_rx_data; + + lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VDSK_TX_ENTRIES); + if (lc->lc_txq == NULL) { + printf(", can't allocate tx queue\n"); + return; + } + + lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VDSK_RX_ENTRIES); + if (lc->lc_rxq == NULL) { + printf(", can't allocate rx queue\n"); + goto free_txqueue; + } + + printf("\n"); + + mountroothook_establish(vdsp_mountroot, sc); + return; + +#if 0 +free_rxqueue: + ldc_queue_free(sc->sc_dmatag, lc->lc_rxq); +#endif +free_txqueue: + ldc_queue_free(sc->sc_dmatag, lc->lc_txq); +} + +int +vdsp_tx_intr(void *arg) +{ + struct vdsp_softc *sc = arg; + struct ldc_conn *lc = &sc->sc_lc; + uint64_t tx_head, tx_tail, tx_state; + + hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); + if (tx_state != lc->lc_tx_state) { + switch (tx_state) { + case LDC_CHANNEL_DOWN: + DPRINTF(("Tx link down\n")); + break; + case LDC_CHANNEL_UP: + DPRINTF(("Tx link up\n")); + break; + case LDC_CHANNEL_RESET: + DPRINTF(("Tx link reset\n")); + break; + } + lc->lc_tx_state = tx_state; + } + + return (1); +} + +int +vdsp_rx_intr(void *arg) +{ + struct vdsp_softc *sc = arg; + struct ldc_conn *lc = &sc->sc_lc; + uint64_t rx_head, rx_tail, rx_state; + struct ldc_pkt *lp; + int err; + + err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state); + if (err == H_EINVAL) + return (0); + if (err != H_EOK) { + printf("hv_ldc_rx_get_state %d\n", err); + return (0); + } + + if (rx_state != lc->lc_rx_state) { + switch (rx_state) { + case LDC_CHANNEL_DOWN: + DPRINTF(("Rx link down\n")); + lc->lc_tx_seqid = 0; + lc->lc_state = 0; + lc->lc_reset(lc); + break; + case LDC_CHANNEL_UP: + DPRINTF(("Rx link up\n")); + break; + case LDC_CHANNEL_RESET: + DPRINTF(("Rx link reset\n")); + lc->lc_tx_seqid = 0; + lc->lc_state = 0; + lc->lc_reset(lc); + break; + } + lc->lc_rx_state = rx_state; + return (1); + } + + if (lc->lc_rx_state == LDC_CHANNEL_DOWN) + return (1); + + lp = (struct ldc_pkt *)(lc->lc_rxq->lq_va + rx_head); + switch (lp->type) { + case LDC_CTRL: + ldc_rx_ctrl(lc, lp); + break; + + case LDC_DATA: + ldc_rx_data(lc, lp); + break; + + default: + DPRINTF(("0x%02x/0x%02x/0x%02x\n", lp->type, lp->stype, + lp->ctrl)); + ldc_reset(lc); + break; + } + + rx_head += sizeof(*lp); + rx_head &= ((lc->lc_rxq->lq_nentries * sizeof(*lp)) - 1); + err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head); + if (err != H_EOK) + printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err); + + return (1); +} + +void +vdsp_rx_data(struct ldc_conn *lc, struct ldc_pkt *lp) +{ + struct vio_msg *vm = (struct vio_msg *)lp; + + switch (vm->type) { + case VIO_TYPE_CTRL: + if ((lp->env & LDC_FRAG_START) == 0 && + (lp->env & LDC_FRAG_STOP) == 0) + return; + vdsp_rx_vio_ctrl(lc->lc_sc, vm); + break; + + case VIO_TYPE_DATA: + if((lp->env & LDC_FRAG_START) == 0) + return; + vdsp_rx_vio_data(lc->lc_sc, vm); + break; + + default: + DPRINTF(("Unhandled packet type 0x%02x\n", vm->type)); + ldc_reset(lc); + break; + } +} + +void +vdsp_rx_vio_ctrl(struct vdsp_softc *sc, struct vio_msg *vm) +{ + struct vio_msg_tag *tag = (struct vio_msg_tag *)&vm->type; + + switch (tag->stype_env) { + case VIO_VER_INFO: + vdsp_rx_vio_ver_info(sc, tag); + break; + case VIO_ATTR_INFO: + vdsp_rx_vio_attr_info(sc, tag); + break; + case VIO_DRING_REG: + vdsp_rx_vio_dring_reg(sc, tag); + break; + case VIO_RDX: + vdsp_rx_vio_rdx(sc, tag); + break; + default: + DPRINTF(("CTRL/0x%02x/0x%04x\n", tag->stype, tag->stype_env)); + break; + } +} + +void +vdsp_rx_vio_ver_info(struct vdsp_softc *sc, struct vio_msg_tag *tag) +{ + struct vio_ver_info *vi = (struct vio_ver_info *)tag; + + switch (vi->tag.stype) { + case VIO_SUBTYPE_INFO: + DPRINTF(("CTRL/INFO/VER_INFO\n")); + + /* Make sure we're talking to a virtual disk. */ + if (vi->dev_class != VDEV_DISK) { + /* Huh, we're not talking to a disk device? */ + printf("%s: peer is not a disk device\n", + sc->sc_dv.dv_xname); + vi->tag.stype = VIO_SUBTYPE_NACK; + vi->major = 0; + vdsp_sendmsg(sc, vi, sizeof(*vi)); + return; + } + + if (vi->major != VDSK_MAJOR) { + vi->tag.stype = VIO_SUBTYPE_NACK; + vi->major = VDSK_MAJOR; + vi->minor = VDSK_MINOR; + vdsp_sendmsg(sc, vi, sizeof(*vi)); + return; + } + + vi->tag.stype = VIO_SUBTYPE_ACK; + vi->tag.sid = sc->sc_local_sid; + vi->minor = VDSK_MINOR; + vdsp_sendmsg(sc, vi, sizeof(*vi)); + sc->sc_vio_state |= VIO_RCV_VER_INFO; + break; + + case VIO_SUBTYPE_ACK: + DPRINTF(("CTRL/ACK/VER_INFO\n")); + break; + + default: + DPRINTF(("CTRL/0x%02x/VER_INFO\n", vi->tag.stype)); + break; + } +} + +void +vdsp_rx_vio_attr_info(struct vdsp_softc *sc, struct vio_msg_tag *tag) +{ + struct vd_attr_info *ai = (struct vd_attr_info *)tag; + + switch (ai->tag.stype) { + case VIO_SUBTYPE_INFO: + DPRINTF(("CTRL/INFO/ATTR_INFO\n")); + + if (ai->xfer_mode != VIO_DESC_MODE && + ai->xfer_mode != VIO_DRING_MODE) { + printf("%s: peer uses unsupported xfer mode 0x%02x\n", + sc->sc_dv.dv_xname, ai->xfer_mode); + ai->tag.stype = VIO_SUBTYPE_NACK; + vdsp_sendmsg(sc, ai, sizeof(*ai)); + return; + } + sc->sc_xfer_mode = ai->xfer_mode; + sc->sc_vio_state |= VIO_RCV_ATTR_INFO; + + workq_add_task(NULL, 0, vdsp_open, sc, NULL); + break; + + case VIO_SUBTYPE_ACK: + DPRINTF(("CTRL/ACK/ATTR_INFO\n")); + break; + + default: + DPRINTF(("CTRL/0x%02x/ATTR_INFO\n", ai->tag.stype)); + break; + } +} + +void +vdsp_rx_vio_dring_reg(struct vdsp_softc *sc, struct vio_msg_tag *tag) +{ + struct vio_dring_reg *dr = (struct vio_dring_reg *)tag; + + switch (dr->tag.stype) { + case VIO_SUBTYPE_INFO: + DPRINTF(("CTRL/INFO/DRING_REG\n")); + + if (dr->num_descriptors > VDSK_MAX_DESCRIPTORS || + dr->descriptor_size > sizeof(struct vd_desc) || + dr->ncookies > 1) { + dr->tag.stype = VIO_SUBTYPE_NACK; + vdsp_sendmsg(sc, dr, sizeof(*dr)); + return; + } + sc->sc_num_descriptors = dr->num_descriptors; + sc->sc_descriptor_size = dr->descriptor_size; + sc->sc_dring_cookie = dr->cookie[0]; + sc->sc_vio_state |= VIO_RCV_DRING_REG; + + workq_add_task(NULL, 0, vdsp_alloc, sc, NULL); + break; + + case VIO_SUBTYPE_ACK: + DPRINTF(("CTRL/ACK/DRING_REG\n")); + break; + + default: + DPRINTF(("CTRL/0x%02x/DRING_REG\n", dr->tag.stype)); + break; + } +} + +void +vdsp_rx_vio_rdx(struct vdsp_softc *sc, struct vio_msg_tag *tag) +{ + switch(tag->stype) { + case VIO_SUBTYPE_INFO: + DPRINTF(("CTRL/INFO/RDX\n")); + + tag->stype = VIO_SUBTYPE_ACK; + tag->sid = sc->sc_local_sid; + vdsp_sendmsg(sc, tag, sizeof(*tag)); + sc->sc_vio_state |= VIO_RCV_RDX; + break; + + case VIO_SUBTYPE_ACK: + DPRINTF(("CTRL/ACK/RDX\n")); + break; + + default: + DPRINTF(("CTRL/0x%02x/RDX (VIO)\n", tag->stype)); + break; + } +} + +void +vdsp_rx_vio_data(struct vdsp_softc *sc, struct vio_msg *vm) +{ + struct vio_msg_tag *tag = (struct vio_msg_tag *)&vm->type; + + if (!ISSET(sc->sc_vio_state, VIO_RCV_RDX)) { + DPRINTF(("Spurious DATA/0x%02x/0x%04x\n", tag->stype, + tag->stype_env)); + return; + } + + switch(tag->stype_env) { + case VIO_DESC_DATA: + vdsp_rx_vio_desc_data(sc, tag); + break; + + case VIO_DRING_DATA: + vdsp_rx_vio_dring_data(sc, tag); + break; + + default: + DPRINTF(("DATA/0x%02x/0x%04x\n", tag->stype, tag->stype_env)); + break; + } +} + +void +vdsp_rx_vio_dring_data(struct vdsp_softc *sc, struct vio_msg_tag *tag) +{ + struct vio_dring_msg *dm = (struct vio_dring_msg *)tag; + struct vd_desc *vd; + paddr_t pa, offset; + psize_t nbytes; + int err; + + switch(tag->stype) { + case VIO_SUBTYPE_INFO: + DPRINTF(("DATA/INFO/DRING_DATA\n")); + + if (dm->dring_ident != sc->sc_dring_ident || + dm->start_idx >= sc->sc_num_descriptors) { + dm->tag.stype = VIO_SUBTYPE_NACK; + vdsp_sendmsg(sc, dm, sizeof(*dm)); + return; + } + + vd = &sc->sc_vd[dm->start_idx]; + pmap_extract(pmap_kernel(), (vaddr_t)vd, &pa); + offset = dm->start_idx * sc->sc_descriptor_size; + err = hv_ldc_copy(sc->sc_lc.lc_id, LDC_COPY_IN, + sc->sc_dring_cookie.addr | offset, pa, + sc->sc_descriptor_size, &nbytes); + if (err != H_EOK) { + printf("hv_ldc_copy %d\n", err); + return; + } + + switch (vd->operation) { + case VD_OP_BREAD: + workq_add_task(NULL, 0, vdsp_read_dring, sc, vd); + break; + case VD_OP_BWRITE: + workq_add_task(NULL, 0, vdsp_write_dring, sc, vd); + break; + case VD_OP_FLUSH: + workq_add_task(NULL, 0, vdsp_flush_dring, sc, vd); + break; + default: + printf("%s: unsupported operation 0x%02x\n", + sc->sc_dv.dv_xname, vd->operation); + break; + } + break; + + case VIO_SUBTYPE_ACK: + DPRINTF(("DATA/ACK/DRING_DATA\n")); + break; + + case VIO_SUBTYPE_NACK: + DPRINTF(("DATA/NACK/DRING_DATA\n")); + break; + + default: + DPRINTF(("DATA/0x%02x/DRING_DATA\n", tag->stype)); + break; + } +} + +void +vdsp_rx_vio_desc_data(struct vdsp_softc *sc, struct vio_msg_tag *tag) +{ + struct vdsk_desc_msg *dm = (struct vdsk_desc_msg *)tag; + + switch(tag->stype) { + case VIO_SUBTYPE_INFO: + DPRINTF(("DATA/INFO/DESC_DATA\n")); + + switch (dm->operation) { + case VD_OP_BREAD: + workq_add_task(NULL, 0, vdsp_read, sc, dm); + break; + default: + printf("%s: unsupported operation 0x%02x\n", + sc->sc_dv.dv_xname, dm->operation); + break; + } + break; + + case VIO_SUBTYPE_ACK: + DPRINTF(("DATA/ACK/DESC_DATA\n")); + break; + + case VIO_SUBTYPE_NACK: + DPRINTF(("DATA/NACK/DESC_DATA\n")); + break; + + default: + DPRINTF(("DATA/0x%02x/DESC_DATA\n", tag->stype)); + break; + } +} + +void +vdsp_ldc_reset(struct ldc_conn *lc) +{ + struct vdsp_softc *sc = lc->lc_sc; + + sc->sc_vio_state = 0; + if (sc->sc_vd) { + free(sc->sc_vd, M_DEVBUF); + sc->sc_vd = NULL; + } + +} + +void +vdsp_ldc_start(struct ldc_conn *lc) +{ + /* The vDisk client is supposed to initiate the handshake. */ +} + +void +vdsp_sendmsg(struct vdsp_softc *sc, void *msg, size_t len) +{ + struct ldc_conn *lc = &sc->sc_lc; + struct ldc_pkt *lp; + uint64_t tx_head, tx_tail, tx_state; + uint8_t *p = msg; + int err; + + err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state); + if (err != H_EOK) + return; + + while (len > 0) { + lp = (struct ldc_pkt *)(lc->lc_txq->lq_va + tx_tail); + bzero(lp, sizeof(struct ldc_pkt)); + lp->type = LDC_DATA; + lp->stype = LDC_INFO; + lp->env = min(len, LDC_PKT_PAYLOAD); + if (p == msg) + lp->env |= LDC_FRAG_START; + if (len <= LDC_PKT_PAYLOAD) + lp->env |= LDC_FRAG_STOP; + lp->seqid = lc->lc_tx_seqid++; + bcopy(p, &lp->major, min(len, LDC_PKT_PAYLOAD)); + + tx_tail += sizeof(*lp); + tx_tail &= ((lc->lc_txq->lq_nentries * sizeof(*lp)) - 1); + err = hv_ldc_tx_set_qtail(lc->lc_id, tx_tail); + if (err != H_EOK) + printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err); + p += min(len, LDC_PKT_PAYLOAD); + len -= min(len, LDC_PKT_PAYLOAD); + } +} + +void +vdsp_mountroot(void *arg) +{ + struct vdsp_softc *sc = arg; + struct ldc_conn *lc = &sc->sc_lc; + int err; + + err = hv_ldc_tx_qconf(lc->lc_id, + lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries); + if (err != H_EOK) + printf("hv_ldc_tx_qconf %d\n", err); + + err = hv_ldc_rx_qconf(lc->lc_id, + lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries); + if (err != H_EOK) + printf("hv_ldc_rx_qconf %d\n", err); + + cbus_intr_setenabled(sc->sc_tx_sysino, INTR_ENABLED); + cbus_intr_setenabled(sc->sc_rx_sysino, INTR_ENABLED); +} + +void +vdsp_open(void *arg1, void *arg2) +{ + struct vdsp_softc *sc = arg1; + struct proc *p = curproc; + struct vd_attr_info ai; + + if (sc->sc_vp == NULL) { + struct nameidata nd; + struct vattr va; + const char *name; + int error; + + name = mdesc_get_prop_str(sc->sc_idx, "vds-block-device"); + if (name == NULL) + return; + + NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, name, p); + error = vn_open(&nd, FREAD, 0); + if (error) { + printf("VOP_OPEN: %s, %d\n", name, error); + return; + } + + error = VOP_GETATTR(nd.ni_vp, &va, p->p_ucred, p); + if (error) + printf("VOP_GETATTR: %s, %d\n", name, error); + sc->sc_vdisk_block_size = DEV_BSIZE; + sc->sc_vdisk_size = va.va_size / DEV_BSIZE; + + VOP_UNLOCK(nd.ni_vp, 0, p); + sc->sc_vp = nd.ni_vp; + } + + bzero(&ai, sizeof(ai)); + ai.tag.type = VIO_TYPE_CTRL; + ai.tag.stype = VIO_SUBTYPE_ACK; + ai.tag.stype_env = VIO_ATTR_INFO; + ai.tag.sid = sc->sc_local_sid; + ai.xfer_mode = sc->sc_xfer_mode; + ai.vdisk_block_size = sc->sc_vdisk_block_size; + ai.vdisk_size = sc->sc_vdisk_size; + vdsp_sendmsg(sc, &ai, sizeof(ai)); +} + +void +vdsp_alloc(void *arg1, void *arg2) +{ + struct vdsp_softc *sc = arg1; + struct vio_dring_reg dr; + + KASSERT(sc->sc_num_descriptors <= 1024); + sc->sc_vd = malloc(sc->sc_num_descriptors * sizeof(struct vd_desc), + M_WAITOK, M_DEVBUF); + + bzero(&dr, sizeof(dr)); + dr.tag.type = VIO_TYPE_CTRL; + dr.tag.stype = VIO_SUBTYPE_ACK; + dr.tag.stype_env = VIO_DRING_REG; + dr.tag.sid = sc->sc_local_sid; + dr.dring_ident = ++sc->sc_dring_ident; + vdsp_sendmsg(sc, &dr, sizeof(dr)); +} + +void +vdsp_read(void *arg1, void *arg2) +{ + struct vdsp_softc *sc = arg1; + struct ldc_conn *lc = &sc->sc_lc; + struct vdsk_desc_msg *dm = arg2; + struct proc *p = curproc; + struct iovec iov; + struct uio uio; + caddr_t buf; + vaddr_t va; + paddr_t pa; + uint64_t size, off; + psize_t nbytes; + int err, i; + + if (sc->sc_vp == NULL) + return; + + buf = malloc(dm->size, M_DEVBUF, M_WAITOK); + + iov.iov_base = buf; + iov.iov_len = dm->size; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = dm->offset * DEV_BSIZE; + uio.uio_resid = dm->size; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_procp = p; + + vn_lock(sc->sc_vp, LK_EXCLUSIVE | LK_RETRY, p); + dm->status = VOP_READ(sc->sc_vp, &uio, 0, p->p_ucred); + VOP_UNLOCK(sc->sc_vp, 0, p); + + if (dm->status == 0) { + i = 0; + va = (vaddr_t)buf; + size = dm->size; + off = 0; + while (size > 0 && i < dm->ncookies) { + pmap_extract(pmap_kernel(), va, &pa); + nbytes = min(size, dm->cookie[i].size - off); + nbytes = min(nbytes, PAGE_SIZE - (off & PAGE_MASK)); + err = hv_ldc_copy(lc->lc_id, LDC_COPY_OUT, + dm->cookie[i].addr + off, pa, nbytes, &nbytes); + if (err != H_EOK) + printf("hv_ldc_copy: %d\n", err); + va += nbytes; + size -= nbytes; + off += nbytes; + if (off >= dm->cookie[i].size) { + off = 0; + i++; + } + } + } + + free(buf, M_DEVBUF); + + /* ACK the descriptor. */ + dm->tag.stype = VIO_SUBTYPE_ACK; + dm->tag.sid = sc->sc_local_sid; + vdsp_sendmsg(sc, dm, sizeof(*dm) + + (dm->ncookies - 1) * sizeof(struct ldc_cookie)); +} + +void +vdsp_read_dring(void *arg1, void *arg2) +{ + struct vdsp_softc *sc = arg1; + struct ldc_conn *lc = &sc->sc_lc; + struct vd_desc *vd = arg2; + struct vio_dring_msg dm; + struct proc *p = curproc; + struct iovec iov; + struct uio uio; + caddr_t buf; + vaddr_t va; + paddr_t pa, offset; + uint64_t size, off; + psize_t nbytes; + int err, i; + + if (sc->sc_vp == NULL) + return; + + buf = malloc(vd->size, M_DEVBUF, M_WAITOK); + + iov.iov_base = buf; + iov.iov_len = vd->size; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = vd->offset * DEV_BSIZE; + uio.uio_resid = vd->size; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_READ; + uio.uio_procp = p; + + vn_lock(sc->sc_vp, LK_EXCLUSIVE | LK_RETRY, p); + vd->status = VOP_READ(sc->sc_vp, &uio, 0, p->p_ucred); + VOP_UNLOCK(sc->sc_vp, 0, p); + + if (vd->status == 0) { + i = 0; + va = (vaddr_t)buf; + size = vd->size; + off = 0; + while (size > 0 && i < vd->ncookies) { + pmap_extract(pmap_kernel(), va, &pa); + nbytes = min(size, vd->cookie[i].size - off); + nbytes = min(nbytes, PAGE_SIZE - (off & PAGE_MASK)); + err = hv_ldc_copy(lc->lc_id, LDC_COPY_OUT, + vd->cookie[i].addr + off, pa, nbytes, &nbytes); + if (err != H_EOK) + printf("hv_ldc_copy: %d\n", err); + va += nbytes; + size -= nbytes; + off += nbytes; + if (off >= vd->cookie[i].size) { + off = 0; + i++; + } + } + } + + free(buf, M_DEVBUF); + + vd->hdr.dstate = VIO_DESC_DONE; + pmap_extract(pmap_kernel(), (vaddr_t)vd, &pa); + offset = (vd - sc->sc_vd) * sc->sc_descriptor_size; + err = hv_ldc_copy(sc->sc_lc.lc_id, LDC_COPY_OUT, + sc->sc_dring_cookie.addr | offset, pa, + sc->sc_descriptor_size, &nbytes); + if (err != H_EOK) { + printf("hv_ldc_copy %d\n", err); + return; + } + + /* ACK the descriptor. */ + bzero(&dm, sizeof(dm)); + dm.tag.type = VIO_TYPE_DATA; + dm.tag.stype = VIO_SUBTYPE_ACK; + dm.tag.stype_env = VIO_DRING_DATA; + dm.tag.sid = sc->sc_local_sid; + dm.dring_ident = sc->sc_dring_ident; + vdsp_sendmsg(sc, &dm, sizeof(dm)); +} + +void +vdsp_write_dring(void *arg1, void *arg2) +{ + struct vdsp_softc *sc = arg1; + struct ldc_conn *lc = &sc->sc_lc; + struct vd_desc *vd = arg2; + struct vio_dring_msg dm; + struct proc *p = curproc; + struct iovec iov; + struct uio uio; + caddr_t buf; + vaddr_t va; + paddr_t pa, offset; + uint64_t size, off; + psize_t nbytes; + int err, i; + + if (sc->sc_vp == NULL) + return; + + buf = malloc(vd->size, M_DEVBUF, M_WAITOK); + + i = 0; + va = (vaddr_t)buf; + size = vd->size; + off = 0; + while (size > 0 && i < vd->ncookies) { + pmap_extract(pmap_kernel(), va, &pa); + nbytes = min(size, vd->cookie[i].size - off); + nbytes = min(nbytes, PAGE_SIZE - (off & PAGE_MASK)); + err = hv_ldc_copy(lc->lc_id, LDC_COPY_IN, + vd->cookie[i].addr + off, pa, nbytes, &nbytes); + if (err != H_EOK) + printf("hv_ldc_copy: %d\n", err); + va += nbytes; + size -= nbytes; + off += nbytes; + if (off >= vd->cookie[i].size) { + off = 0; + i++; + } + } + + iov.iov_base = buf; + iov.iov_len = vd->size; + uio.uio_iov = &iov; + uio.uio_iovcnt = 1; + uio.uio_offset = vd->offset * DEV_BSIZE; + uio.uio_resid = vd->size; + uio.uio_segflg = UIO_SYSSPACE; + uio.uio_rw = UIO_WRITE; + uio.uio_procp = p; + + vn_lock(sc->sc_vp, LK_EXCLUSIVE | LK_RETRY, p); + vd->status = VOP_WRITE(sc->sc_vp, &uio, 0, p->p_ucred); + VOP_UNLOCK(sc->sc_vp, 0, p); + + free(buf, M_DEVBUF); + + vd->hdr.dstate = VIO_DESC_DONE; + pmap_extract(pmap_kernel(), (vaddr_t)vd, &pa); + offset = (vd - sc->sc_vd) * sc->sc_descriptor_size; + err = hv_ldc_copy(sc->sc_lc.lc_id, LDC_COPY_OUT, + sc->sc_dring_cookie.addr | offset, pa, + sc->sc_descriptor_size, &nbytes); + if (err != H_EOK) { + printf("hv_ldc_copy %d\n", err); + return; + } + + /* ACK the descriptor. */ + bzero(&dm, sizeof(dm)); + dm.tag.type = VIO_TYPE_DATA; + dm.tag.stype = VIO_SUBTYPE_ACK; + dm.tag.stype_env = VIO_DRING_DATA; + dm.tag.sid = sc->sc_local_sid; + dm.dring_ident = sc->sc_dring_ident; + vdsp_sendmsg(sc, &dm, sizeof(dm)); +} + +void +vdsp_flush_dring(void *arg1, void *arg2) +{ + struct vdsp_softc *sc = arg1; + struct vd_desc *vd = arg2; + struct vio_dring_msg dm; + paddr_t pa, offset; + psize_t nbytes; + int err; + + if (sc->sc_vp == NULL) + return; + + vd->status = 0; + vd->hdr.dstate = VIO_DESC_DONE; + pmap_extract(pmap_kernel(), (vaddr_t)vd, &pa); + offset = (vd - sc->sc_vd) * sc->sc_descriptor_size; + err = hv_ldc_copy(sc->sc_lc.lc_id, LDC_COPY_OUT, + sc->sc_dring_cookie.addr | offset, pa, + sc->sc_descriptor_size, &nbytes); + if (err != H_EOK) { + printf("hv_ldc_copy %d\n", err); + return; + } + + /* ACK the descriptor. */ + bzero(&dm, sizeof(dm)); + dm.tag.type = VIO_TYPE_DATA; + dm.tag.stype = VIO_SUBTYPE_ACK; + dm.tag.stype_env = VIO_DRING_DATA; + dm.tag.sid = sc->sc_local_sid; + dm.dring_ident = sc->sc_dring_ident; + vdsp_sendmsg(sc, &dm, sizeof(dm)); +} |