virtio_block.c revision 275048
1275048Sbr/*- 2275048Sbr * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 3275048Sbr * All rights reserved. 4275048Sbr * 5275048Sbr * This software was developed by SRI International and the University of 6275048Sbr * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-10-C-0237) 7275048Sbr * ("CTSRD"), as part of the DARPA CRASH research programme. 8275048Sbr * 9275048Sbr * Redistribution and use in source and binary forms, with or without 10275048Sbr * modification, are permitted provided that the following conditions 11275048Sbr * are met: 12275048Sbr * 1. Redistributions of source code must retain the above copyright 13275048Sbr * notice, this list of conditions and the following disclaimer. 14275048Sbr * 2. Redistributions in binary form must reproduce the above copyright 15275048Sbr * notice, this list of conditions and the following disclaimer in the 16275048Sbr * documentation and/or other materials provided with the distribution. 17275048Sbr * 18275048Sbr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19275048Sbr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20275048Sbr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21275048Sbr * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22275048Sbr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23275048Sbr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24275048Sbr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25275048Sbr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26275048Sbr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27275048Sbr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28275048Sbr * SUCH DAMAGE. 29275048Sbr */ 30275048Sbr 31275048Sbr/* 32275048Sbr * BERI virtio block backend driver 33275048Sbr */ 34275048Sbr 35275048Sbr#include <sys/cdefs.h> 36275048Sbr__FBSDID("$FreeBSD: head/sys/dev/beri/virtio/virtio_block.c 275048 2014-11-25 15:58:59Z br $"); 37275048Sbr 38275048Sbr#include <sys/param.h> 39275048Sbr#include <sys/systm.h> 40275048Sbr#include <sys/bus.h> 41275048Sbr#include <sys/kernel.h> 42275048Sbr#include <sys/module.h> 43275048Sbr#include <sys/rman.h> 44275048Sbr#include <sys/conf.h> 45275048Sbr#include <sys/stat.h> 46275048Sbr#include <sys/endian.h> 47275048Sbr#include <sys/disk.h> 48275048Sbr#include <sys/vnode.h> 49275048Sbr#include <sys/fcntl.h> 50275048Sbr#include <sys/kthread.h> 51275048Sbr#include <sys/buf.h> 52275048Sbr#include <sys/mdioctl.h> 53275048Sbr#include <sys/namei.h> 54275048Sbr 55275048Sbr#include <machine/bus.h> 56275048Sbr#include <machine/fdt.h> 57275048Sbr#include <machine/cpu.h> 58275048Sbr#include <machine/intr.h> 59275048Sbr 60275048Sbr#include <dev/fdt/fdt_common.h> 61275048Sbr#include <dev/ofw/openfirm.h> 62275048Sbr#include <dev/ofw/ofw_bus.h> 63275048Sbr#include <dev/ofw/ofw_bus_subr.h> 64275048Sbr 65275048Sbr#include <dev/beri/virtio/virtio.h> 66275048Sbr#include <dev/beri/virtio/virtio_mmio_platform.h> 67275048Sbr#include <dev/altera/pio/pio.h> 68275048Sbr#include <dev/virtio/mmio/virtio_mmio.h> 69275048Sbr#include <dev/virtio/block/virtio_blk.h> 70275048Sbr#include <dev/virtio/virtio.h> 71275048Sbr#include <dev/virtio/virtio_ring.h> 72275048Sbr 73275048Sbr#include "pio_if.h" 74275048Sbr 75275048Sbr#define DPRINTF(fmt, ...) 76275048Sbr 77275048Sbrstruct beri_vtblk_softc { 78275048Sbr struct resource *res[1]; 79275048Sbr bus_space_tag_t bst; 80275048Sbr bus_space_handle_t bsh; 81275048Sbr struct cdev *cdev; 82275048Sbr device_t dev; 83275048Sbr int opened; 84275048Sbr device_t pio_recv; 85275048Sbr device_t pio_send; 86275048Sbr struct vqueue_info vs_queues[NUM_QUEUES]; 87275048Sbr char ident[VTBLK_BLK_ID_BYTES]; 88275048Sbr struct ucred *cred; 89275048Sbr struct vnode *vnode; 90275048Sbr struct thread *vtblk_ktd; 91275048Sbr struct sx sc_mtx; 92275048Sbr int beri_mem_offset; 93275048Sbr struct md_ioctl *mdio; 94275048Sbr struct virtio_blk_config *cfg; 95275048Sbr}; 96275048Sbr 97275048Sbrstatic struct resource_spec beri_spec[] = { 98275048Sbr { SYS_RES_MEMORY, 0, RF_ACTIVE }, 99275048Sbr { -1, 0 } 100275048Sbr}; 101275048Sbr 102275048Sbrstatic int 103275048Sbrvtblk_rdwr(struct beri_vtblk_softc *sc, struct iovec *iov, 104275048Sbr int cnt, int offset, int operation, int iolen) 105275048Sbr{ 106275048Sbr struct vnode *vp; 107275048Sbr struct mount *mp; 108275048Sbr struct uio auio; 109275048Sbr int error; 110275048Sbr 111275048Sbr bzero(&auio, sizeof(auio)); 112275048Sbr 113275048Sbr vp = sc->vnode; 114275048Sbr 115275048Sbr KASSERT(vp != NULL, ("file not opened")); 116275048Sbr 117275048Sbr auio.uio_iov = iov; 118275048Sbr auio.uio_iovcnt = cnt; 119275048Sbr auio.uio_offset = offset; 120275048Sbr auio.uio_segflg = UIO_SYSSPACE; 121275048Sbr auio.uio_rw = operation; 122275048Sbr auio.uio_resid = iolen; 123275048Sbr auio.uio_td = curthread; 124275048Sbr 125275048Sbr if (operation == 0) { 126275048Sbr vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 127275048Sbr error = VOP_READ(vp, &auio, IO_DIRECT, sc->cred); 128275048Sbr VOP_UNLOCK(vp, 0); 129275048Sbr } else { 130275048Sbr (void) vn_start_write(vp, &mp, V_WAIT); 131275048Sbr vn_lock(vp, LK_EXCLUSIVE | LK_RETRY); 132275048Sbr error = VOP_WRITE(vp, &auio, IO_SYNC, sc->cred); 133275048Sbr VOP_UNLOCK(vp, 0); 134275048Sbr vn_finished_write(mp); 135275048Sbr } 136275048Sbr 137275048Sbr return (error); 138275048Sbr} 139275048Sbr 140275048Sbrstatic void 141275048Sbrvtblk_proc(struct beri_vtblk_softc *sc, struct vqueue_info *vq) 142275048Sbr{ 143275048Sbr struct iovec iov[VTBLK_MAXSEGS + 2]; 144275048Sbr uint16_t flags[VTBLK_MAXSEGS + 2]; 145275048Sbr struct virtio_blk_outhdr *vbh; 146275048Sbr uint8_t *status; 147275048Sbr off_t offset; 148275048Sbr int iolen; 149275048Sbr int type; 150275048Sbr int i, n; 151275048Sbr int err; 152275048Sbr 153275048Sbr n = vq_getchain(sc->beri_mem_offset, vq, iov, 154275048Sbr VTBLK_MAXSEGS + 2, flags); 155275048Sbr 156275048Sbr KASSERT(n >= 2 && n <= VTBLK_MAXSEGS + 2, 157275048Sbr ("wrong n value %d", n)); 158275048Sbr 159275048Sbr vbh = iov[0].iov_base; 160275048Sbr 161275048Sbr status = iov[n-1].iov_base; 162275048Sbr KASSERT(iov[n-1].iov_len == 1, 163275048Sbr ("iov_len == %d", iov[n-1].iov_len)); 164275048Sbr 165275048Sbr type = be32toh(vbh->type) & ~VIRTIO_BLK_T_BARRIER; 166275048Sbr offset = be64toh(vbh->sector) * DEV_BSIZE; 167275048Sbr 168275048Sbr iolen = 0; 169275048Sbr for (i = 1; i < (n-1); i++) { 170275048Sbr iolen += iov[i].iov_len; 171275048Sbr } 172275048Sbr 173275048Sbr switch (type) { 174275048Sbr case VIRTIO_BLK_T_OUT: 175275048Sbr case VIRTIO_BLK_T_IN: 176275048Sbr err = vtblk_rdwr(sc, iov + 1, i - 1, 177275048Sbr offset, type, iolen); 178275048Sbr break; 179275048Sbr case VIRTIO_BLK_T_GET_ID: 180275048Sbr /* Assume a single buffer */ 181275048Sbr strlcpy(iov[1].iov_base, sc->ident, 182275048Sbr MIN(iov[1].iov_len, sizeof(sc->ident))); 183275048Sbr err = 0; 184275048Sbr break; 185275048Sbr case VIRTIO_BLK_T_FLUSH: 186275048Sbr /* Possible? */ 187275048Sbr default: 188275048Sbr err = -ENOSYS; 189275048Sbr break; 190275048Sbr } 191275048Sbr 192275048Sbr if (err < 0) { 193275048Sbr if (err == -ENOSYS) { 194275048Sbr *status = VIRTIO_BLK_S_UNSUPP; 195275048Sbr } else 196275048Sbr *status = VIRTIO_BLK_S_IOERR; 197275048Sbr } else 198275048Sbr *status = VIRTIO_BLK_S_OK; 199275048Sbr 200275048Sbr vq_relchain(vq, iov, n, 1); 201275048Sbr} 202275048Sbr 203275048Sbrstatic int 204275048Sbrclose_file(struct beri_vtblk_softc *sc, struct thread *td) 205275048Sbr{ 206275048Sbr int error; 207275048Sbr 208275048Sbr if (sc->vnode != NULL) { 209275048Sbr vn_lock(sc->vnode, LK_EXCLUSIVE | LK_RETRY); 210275048Sbr sc->vnode->v_vflag &= ~VV_MD; 211275048Sbr VOP_UNLOCK(sc->vnode, 0); 212275048Sbr error = vn_close(sc->vnode, (FREAD|FWRITE), 213275048Sbr sc->cred, td); 214275048Sbr if (error != 0) 215275048Sbr return (error); 216275048Sbr sc->vnode = NULL; 217275048Sbr } 218275048Sbr 219275048Sbr if (sc->cred != NULL) 220275048Sbr crfree(sc->cred); 221275048Sbr 222275048Sbr return (0); 223275048Sbr} 224275048Sbr 225275048Sbrstatic int 226275048Sbropen_file(struct beri_vtblk_softc *sc, struct thread *td) 227275048Sbr{ 228275048Sbr struct nameidata nd; 229275048Sbr struct vattr vattr; 230275048Sbr int error; 231275048Sbr int flags; 232275048Sbr 233275048Sbr flags = (FREAD | FWRITE); 234275048Sbr NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, 235275048Sbr sc->mdio->md_file, td); 236275048Sbr error = vn_open(&nd, &flags, 0, NULL); 237275048Sbr if (error != 0) 238275048Sbr return (error); 239275048Sbr NDFREE(&nd, NDF_ONLY_PNBUF); 240275048Sbr 241275048Sbr if (nd.ni_vp->v_type != VREG) { 242275048Sbr return (EINVAL); 243275048Sbr } 244275048Sbr 245275048Sbr error = VOP_GETATTR(nd.ni_vp, &vattr, td->td_ucred); 246275048Sbr if (error != 0) 247275048Sbr return (error); 248275048Sbr 249275048Sbr if (VOP_ISLOCKED(nd.ni_vp) != LK_EXCLUSIVE) { 250275048Sbr vn_lock(nd.ni_vp, LK_UPGRADE | LK_RETRY); 251275048Sbr if (nd.ni_vp->v_iflag & VI_DOOMED) { 252275048Sbr return (1); 253275048Sbr } 254275048Sbr } 255275048Sbr nd.ni_vp->v_vflag |= VV_MD; 256275048Sbr VOP_UNLOCK(nd.ni_vp, 0); 257275048Sbr 258275048Sbr sc->vnode = nd.ni_vp; 259275048Sbr sc->cred = crhold(td->td_ucred); 260275048Sbr 261275048Sbr return (0); 262275048Sbr} 263275048Sbr 264275048Sbrstatic int 265275048Sbrvtblk_notify(struct beri_vtblk_softc *sc) 266275048Sbr{ 267275048Sbr struct vqueue_info *vq; 268275048Sbr int queue; 269275048Sbr int reg; 270275048Sbr 271275048Sbr vq = &sc->vs_queues[0]; 272275048Sbr if (!vq_ring_ready(vq)) 273275048Sbr return (0); 274275048Sbr 275275048Sbr if (!sc->opened) 276275048Sbr return (0); 277275048Sbr 278275048Sbr reg = READ2(sc, VIRTIO_MMIO_QUEUE_NOTIFY); 279275048Sbr queue = be16toh(reg); 280275048Sbr 281275048Sbr KASSERT(queue == 0, ("we support single queue only")); 282275048Sbr 283275048Sbr /* Process new descriptors */ 284275048Sbr vq = &sc->vs_queues[queue]; 285275048Sbr vq->vq_save_used = be16toh(vq->vq_used->idx); 286275048Sbr while (vq_has_descs(vq)) 287275048Sbr vtblk_proc(sc, vq); 288275048Sbr 289275048Sbr /* Interrupt other side */ 290275048Sbr PIO_SET(sc->pio_send, Q_INTR, 1); 291275048Sbr 292275048Sbr return (0); 293275048Sbr} 294275048Sbr 295275048Sbrstatic int 296275048Sbrvq_init(struct beri_vtblk_softc *sc) 297275048Sbr{ 298275048Sbr struct vqueue_info *vq; 299275048Sbr uint8_t *base; 300275048Sbr int size; 301275048Sbr int reg; 302275048Sbr int pfn; 303275048Sbr 304275048Sbr vq = &sc->vs_queues[0]; 305275048Sbr vq->vq_qsize = NUM_QUEUES; 306275048Sbr 307275048Sbr reg = READ4(sc, VIRTIO_MMIO_QUEUE_PFN); 308275048Sbr pfn = be32toh(reg); 309275048Sbr vq->vq_pfn = pfn; 310275048Sbr 311275048Sbr size = vring_size(vq->vq_qsize, VRING_ALIGN); 312275048Sbr base = paddr_map(sc->beri_mem_offset, 313275048Sbr (pfn << PAGE_SHIFT), size); 314275048Sbr 315275048Sbr /* First pages are descriptors */ 316275048Sbr vq->vq_desc = (struct vring_desc *)base; 317275048Sbr base += vq->vq_qsize * sizeof(struct vring_desc); 318275048Sbr 319275048Sbr /* Then avail ring */ 320275048Sbr vq->vq_avail = (struct vring_avail *)base; 321275048Sbr base += (2 + vq->vq_qsize + 1) * sizeof(uint16_t); 322275048Sbr 323275048Sbr /* Then it's rounded up to the next page */ 324275048Sbr base = (uint8_t *)roundup2((uintptr_t)base, VRING_ALIGN); 325275048Sbr 326275048Sbr /* And the last pages are the used ring */ 327275048Sbr vq->vq_used = (struct vring_used *)base; 328275048Sbr 329275048Sbr /* Mark queue as allocated, and start at 0 when we use it. */ 330275048Sbr vq->vq_flags = VQ_ALLOC; 331275048Sbr vq->vq_last_avail = 0; 332275048Sbr 333275048Sbr return (0); 334275048Sbr} 335275048Sbr 336275048Sbr 337275048Sbrstatic void 338275048Sbrvtblk_thread(void *arg) 339275048Sbr{ 340275048Sbr struct beri_vtblk_softc *sc; 341275048Sbr int err; 342275048Sbr 343275048Sbr sc = arg; 344275048Sbr 345275048Sbr sx_xlock(&sc->sc_mtx); 346275048Sbr for (;;) { 347275048Sbr err = msleep(sc, &sc->sc_mtx, PCATCH | PZERO, "prd", hz); 348275048Sbr vtblk_notify(sc); 349275048Sbr } 350275048Sbr sx_xunlock(&sc->sc_mtx); 351275048Sbr 352275048Sbr kthread_exit(); 353275048Sbr} 354275048Sbr 355275048Sbrstatic int 356275048Sbrsetup_pio(struct beri_vtblk_softc *sc, char *name, device_t *dev) 357275048Sbr{ 358275048Sbr phandle_t pio_node; 359275048Sbr struct fdt_ic *ic; 360275048Sbr phandle_t xref; 361275048Sbr phandle_t node; 362275048Sbr 363275048Sbr if ((node = ofw_bus_get_node(sc->dev)) == -1) 364275048Sbr return (ENXIO); 365275048Sbr 366275048Sbr if (OF_searchencprop(node, name, &xref, 367275048Sbr sizeof(xref)) == -1) { 368275048Sbr return (ENXIO); 369275048Sbr } 370275048Sbr 371275048Sbr pio_node = OF_node_from_xref(xref); 372275048Sbr SLIST_FOREACH(ic, &fdt_ic_list_head, fdt_ics) { 373275048Sbr if (ic->iph == pio_node) { 374275048Sbr *dev = ic->dev; 375275048Sbr PIO_CONFIGURE(*dev, PIO_OUT_ALL, 376275048Sbr PIO_UNMASK_ALL); 377275048Sbr return (0); 378275048Sbr } 379275048Sbr } 380275048Sbr 381275048Sbr return (ENXIO); 382275048Sbr} 383275048Sbr 384275048Sbrstatic int 385275048Sbrsetup_offset(struct beri_vtblk_softc *sc) 386275048Sbr{ 387275048Sbr pcell_t dts_value[2]; 388275048Sbr phandle_t mem_node; 389275048Sbr phandle_t xref; 390275048Sbr phandle_t node; 391275048Sbr int len; 392275048Sbr 393275048Sbr if ((node = ofw_bus_get_node(sc->dev)) == -1) 394275048Sbr return (ENXIO); 395275048Sbr 396275048Sbr if (OF_searchencprop(node, "beri-mem", &xref, 397275048Sbr sizeof(xref)) == -1) { 398275048Sbr return (ENXIO); 399275048Sbr } 400275048Sbr 401275048Sbr mem_node = OF_node_from_xref(xref); 402275048Sbr if ((len = OF_getproplen(mem_node, "reg")) <= 0) 403275048Sbr return (ENXIO); 404275048Sbr OF_getencprop(mem_node, "reg", dts_value, len); 405275048Sbr sc->beri_mem_offset = dts_value[0]; 406275048Sbr 407275048Sbr return (0); 408275048Sbr} 409275048Sbr 410275048Sbrstatic int 411275048Sbrbackend_info(struct beri_vtblk_softc *sc) 412275048Sbr{ 413275048Sbr struct virtio_blk_config *cfg; 414275048Sbr uint32_t *s; 415275048Sbr int reg; 416275048Sbr int i; 417275048Sbr 418275048Sbr /* Specify that we provide block device */ 419275048Sbr reg = htobe32(VIRTIO_ID_BLOCK); 420275048Sbr WRITE4(sc, VIRTIO_MMIO_DEVICE_ID, reg); 421275048Sbr 422275048Sbr /* The number of queues we support */ 423275048Sbr reg = htobe16(NUM_QUEUES); 424275048Sbr WRITE2(sc, VIRTIO_MMIO_QUEUE_NUM, reg); 425275048Sbr 426275048Sbr /* Our features */ 427275048Sbr reg = htobe32(VIRTIO_RING_F_INDIRECT_DESC 428275048Sbr | VIRTIO_BLK_F_BLK_SIZE 429275048Sbr | VIRTIO_BLK_F_SEG_MAX); 430275048Sbr WRITE4(sc, VIRTIO_MMIO_HOST_FEATURES, reg); 431275048Sbr 432275048Sbr cfg = sc->cfg; 433275048Sbr cfg->capacity = htobe64(sc->mdio->md_mediasize / DEV_BSIZE); 434275048Sbr cfg->size_max = 0; /* not negotiated */ 435275048Sbr cfg->seg_max = htobe32(VTBLK_MAXSEGS); 436275048Sbr cfg->blk_size = htobe32(DEV_BSIZE); 437275048Sbr 438275048Sbr s = (uint32_t *)cfg; 439275048Sbr 440275048Sbr for (i = 0; i < sizeof(struct virtio_blk_config); i+=4) { 441275048Sbr WRITE4(sc, VIRTIO_MMIO_CONFIG + i, *s); 442275048Sbr s+=1; 443275048Sbr } 444275048Sbr 445275048Sbr sprintf(sc->ident, "Virtio block backend"); 446275048Sbr 447275048Sbr return (0); 448275048Sbr} 449275048Sbr 450275048Sbrstatic void 451275048Sbrvtblk_intr(void *arg) 452275048Sbr{ 453275048Sbr struct beri_vtblk_softc *sc; 454275048Sbr int pending; 455275048Sbr int reg; 456275048Sbr 457275048Sbr sc = arg; 458275048Sbr 459275048Sbr reg = PIO_READ(sc->pio_recv); 460275048Sbr 461275048Sbr /* Ack */ 462275048Sbr PIO_SET(sc->pio_recv, reg, 0); 463275048Sbr 464275048Sbr pending = htobe32(reg); 465275048Sbr 466275048Sbr if (pending & Q_PFN) { 467275048Sbr vq_init(sc); 468275048Sbr } 469275048Sbr 470275048Sbr if (pending & Q_NOTIFY) { 471275048Sbr wakeup(sc); 472275048Sbr } 473275048Sbr} 474275048Sbr 475275048Sbrstatic int 476275048Sbrberi_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, 477275048Sbr int flags, struct thread *td) 478275048Sbr{ 479275048Sbr struct beri_vtblk_softc *sc; 480275048Sbr int err; 481275048Sbr 482275048Sbr sc = dev->si_drv1; 483275048Sbr 484275048Sbr switch (cmd) { 485275048Sbr case MDIOCATTACH: 486275048Sbr /* take file as argument */ 487275048Sbr if (sc->vnode != NULL) { 488275048Sbr /* Already opened */ 489275048Sbr return (1); 490275048Sbr } 491275048Sbr sc->mdio = (struct md_ioctl *)addr; 492275048Sbr backend_info(sc); 493275048Sbr DPRINTF("opening file, td 0x%08x\n", (int)td); 494275048Sbr err = open_file(sc, td); 495275048Sbr if (err) 496275048Sbr return (err); 497275048Sbr PIO_SETUP_IRQ(sc->pio_recv, vtblk_intr, sc); 498275048Sbr sc->opened = 1; 499275048Sbr break; 500275048Sbr case MDIOCDETACH: 501275048Sbr if (sc->vnode == 0) { 502275048Sbr /* File not opened */ 503275048Sbr return (1); 504275048Sbr } 505275048Sbr sc->opened = 0; 506275048Sbr DPRINTF("closing file, td 0x%08x\n", (int)td); 507275048Sbr err = close_file(sc, td); 508275048Sbr if (err) 509275048Sbr return (err); 510275048Sbr PIO_TEARDOWN_IRQ(sc->pio_recv); 511275048Sbr break; 512275048Sbr default: 513275048Sbr break; 514275048Sbr } 515275048Sbr 516275048Sbr return (0); 517275048Sbr} 518275048Sbr 519275048Sbrstatic struct cdevsw beri_cdevsw = { 520275048Sbr .d_version = D_VERSION, 521275048Sbr .d_ioctl = beri_ioctl, 522275048Sbr .d_name = "virtio block backend", 523275048Sbr}; 524275048Sbr 525275048Sbrstatic int 526275048Sbrberi_vtblk_probe(device_t dev) 527275048Sbr{ 528275048Sbr 529275048Sbr if (!ofw_bus_status_okay(dev)) 530275048Sbr return (ENXIO); 531275048Sbr 532275048Sbr if (!ofw_bus_is_compatible(dev, "sri-cambridge,beri-vtblk")) 533275048Sbr return (ENXIO); 534275048Sbr 535275048Sbr device_set_desc(dev, "SRI-Cambridge BERI block"); 536275048Sbr return (BUS_PROBE_DEFAULT); 537275048Sbr} 538275048Sbr 539275048Sbrstatic int 540275048Sbrberi_vtblk_attach(device_t dev) 541275048Sbr{ 542275048Sbr struct beri_vtblk_softc *sc; 543275048Sbr int error; 544275048Sbr 545275048Sbr sc = device_get_softc(dev); 546275048Sbr sc->dev = dev; 547275048Sbr 548275048Sbr if (bus_alloc_resources(dev, beri_spec, sc->res)) { 549275048Sbr device_printf(dev, "could not allocate resources\n"); 550275048Sbr return (ENXIO); 551275048Sbr } 552275048Sbr 553275048Sbr /* Memory interface */ 554275048Sbr sc->bst = rman_get_bustag(sc->res[0]); 555275048Sbr sc->bsh = rman_get_bushandle(sc->res[0]); 556275048Sbr 557275048Sbr sc->cfg = malloc(sizeof(struct virtio_blk_config), 558275048Sbr M_DEVBUF, M_NOWAIT|M_ZERO); 559275048Sbr 560275048Sbr sx_init(&sc->sc_mtx, device_get_nameunit(sc->dev)); 561275048Sbr 562275048Sbr error = kthread_add(vtblk_thread, sc, NULL, &sc->vtblk_ktd, 563275048Sbr 0, 0, "beri_virtio_block"); 564275048Sbr if (error) { 565275048Sbr device_printf(dev, "cannot create kthread\n"); 566275048Sbr return (ENXIO); 567275048Sbr } 568275048Sbr 569275048Sbr if (setup_offset(sc) != 0) 570275048Sbr return (ENXIO); 571275048Sbr if (setup_pio(sc, "pio-send", &sc->pio_send) != 0) 572275048Sbr return (ENXIO); 573275048Sbr if (setup_pio(sc, "pio-recv", &sc->pio_recv) != 0) 574275048Sbr return (ENXIO); 575275048Sbr 576275048Sbr sc->cdev = make_dev(&beri_cdevsw, 0, UID_ROOT, GID_WHEEL, 577275048Sbr S_IRWXU, "beri_vtblk"); 578275048Sbr if (sc->cdev == NULL) { 579275048Sbr device_printf(dev, "Failed to create character device.\n"); 580275048Sbr return (ENXIO); 581275048Sbr } 582275048Sbr 583275048Sbr sc->cdev->si_drv1 = sc; 584275048Sbr return (0); 585275048Sbr} 586275048Sbr 587275048Sbrstatic device_method_t beri_vtblk_methods[] = { 588275048Sbr DEVMETHOD(device_probe, beri_vtblk_probe), 589275048Sbr DEVMETHOD(device_attach, beri_vtblk_attach), 590275048Sbr { 0, 0 } 591275048Sbr}; 592275048Sbr 593275048Sbrstatic driver_t beri_vtblk_driver = { 594275048Sbr "beri_vtblk", 595275048Sbr beri_vtblk_methods, 596275048Sbr sizeof(struct beri_vtblk_softc), 597275048Sbr}; 598275048Sbr 599275048Sbrstatic devclass_t beri_vtblk_devclass; 600275048Sbr 601275048SbrDRIVER_MODULE(beri_vtblk, simplebus, beri_vtblk_driver, 602275048Sbr beri_vtblk_devclass, 0, 0); 603