|
|
1.1 ! root 1: // Virtio SCSI boot support. ! 2: // ! 3: // Copyright (C) 2012 Red Hat Inc. ! 4: // ! 5: // Authors: ! 6: // Paolo Bonzini <[email protected]> ! 7: // ! 8: // This file may be distributed under the terms of the GNU LGPLv3 license. ! 9: ! 10: #include "util.h" // dprintf ! 11: #include "pci.h" // foreachpci ! 12: #include "config.h" // CONFIG_* ! 13: #include "biosvar.h" // GET_GLOBAL ! 14: #include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK ! 15: #include "pci_regs.h" // PCI_VENDOR_ID ! 16: #include "boot.h" // bootprio_find_scsi_device ! 17: #include "blockcmd.h" // scsi_init_drive ! 18: #include "virtio-pci.h" ! 19: #include "virtio-ring.h" ! 20: #include "virtio-scsi.h" ! 21: #include "disk.h" ! 22: ! 23: struct virtio_lun_s { ! 24: struct drive_s drive; ! 25: struct pci_device *pci; ! 26: struct vring_virtqueue *vq; ! 27: u16 ioaddr; ! 28: u16 target; ! 29: u16 lun; ! 30: }; ! 31: ! 32: static int ! 33: virtio_scsi_cmd(u16 ioaddr, struct vring_virtqueue *vq, struct disk_op_s *op, ! 34: void *cdbcmd, u16 target, u16 lun, u16 blocksize) ! 35: { ! 36: struct virtio_scsi_req_cmd req; ! 37: struct virtio_scsi_resp_cmd resp; ! 38: struct vring_list sg[3]; ! 39: ! 40: memset(&req, 0, sizeof(req)); ! 41: req.lun[0] = 1; ! 42: req.lun[1] = target; ! 43: req.lun[2] = (lun >> 8) | 0x40; ! 44: req.lun[3] = (lun & 0xff); ! 45: memcpy(req.cdb, cdbcmd, 16); ! 46: ! 47: u32 len = op->count * blocksize; ! 48: int datain = cdb_is_read(cdbcmd, blocksize); ! 49: int in_num = (datain ? 2 : 1); ! 50: int out_num = (len ? 3 : 2) - in_num; ! 51: ! 52: sg[0].addr = MAKE_FLATPTR(GET_SEG(SS), &req); ! 53: sg[0].length = sizeof(req); ! 54: ! 55: sg[out_num].addr = MAKE_FLATPTR(GET_SEG(SS), &resp); ! 56: sg[out_num].length = sizeof(resp); ! 57: ! 58: if (len) { ! 59: int data_idx = (datain ? 2 : 1); ! 60: sg[data_idx].addr = op->buf_fl; ! 61: sg[data_idx].length = len; ! 62: } ! 63: ! 64: /* Add to virtqueue and kick host */ ! 65: vring_add_buf(vq, sg, out_num, in_num, 0, 0); ! 66: vring_kick(ioaddr, vq, 1); ! 67: ! 68: /* Wait for reply */ ! 69: while (!vring_more_used(vq)) ! 70: usleep(5); ! 71: ! 72: /* Reclaim virtqueue element */ ! 73: vring_get_buf(vq, NULL); ! 74: ! 75: /* Clear interrupt status register. Avoid leaving interrupts stuck if ! 76: * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised. ! 77: */ ! 78: vp_get_isr(ioaddr); ! 79: ! 80: if (resp.response == VIRTIO_SCSI_S_OK && resp.status == 0) { ! 81: return DISK_RET_SUCCESS; ! 82: } ! 83: return DISK_RET_EBADTRACK; ! 84: } ! 85: ! 86: int ! 87: virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize) ! 88: { ! 89: struct virtio_lun_s *vlun = ! 90: container_of(op->drive_g, struct virtio_lun_s, drive); ! 91: ! 92: return virtio_scsi_cmd(GET_GLOBAL(vlun->ioaddr), ! 93: GET_GLOBAL(vlun->vq), op, cdbcmd, ! 94: GET_GLOBAL(vlun->target), GET_GLOBAL(vlun->lun), ! 95: blocksize); ! 96: } ! 97: ! 98: static int ! 99: virtio_scsi_add_lun(struct pci_device *pci, u16 ioaddr, ! 100: struct vring_virtqueue *vq, u16 target, u16 lun) ! 101: { ! 102: struct virtio_lun_s *vlun = malloc_fseg(sizeof(*vlun)); ! 103: if (!vlun) { ! 104: warn_noalloc(); ! 105: return -1; ! 106: } ! 107: memset(vlun, 0, sizeof(*vlun)); ! 108: vlun->drive.type = DTYPE_VIRTIO_SCSI; ! 109: vlun->drive.cntl_id = pci->bdf; ! 110: vlun->pci = pci; ! 111: vlun->ioaddr = ioaddr; ! 112: vlun->vq = vq; ! 113: vlun->target = target; ! 114: vlun->lun = lun; ! 115: ! 116: int prio = bootprio_find_scsi_device(pci, target, lun); ! 117: int ret = scsi_init_drive(&vlun->drive, "virtio-scsi", prio); ! 118: if (ret) ! 119: goto fail; ! 120: return 0; ! 121: ! 122: fail: ! 123: free(vlun); ! 124: return -1; ! 125: } ! 126: ! 127: static int ! 128: virtio_scsi_scan_target(struct pci_device *pci, u16 ioaddr, ! 129: struct vring_virtqueue *vq, u16 target) ! 130: { ! 131: /* TODO: send REPORT LUNS. For now, only LUN 0 is recognized. */ ! 132: int ret = virtio_scsi_add_lun(pci, ioaddr, vq, target, 0); ! 133: return ret < 0 ? 0 : 1; ! 134: } ! 135: ! 136: static void ! 137: init_virtio_scsi(struct pci_device *pci) ! 138: { ! 139: u16 bdf = pci->bdf; ! 140: dprintf(1, "found virtio-scsi at %x:%x\n", pci_bdf_to_bus(bdf), ! 141: pci_bdf_to_dev(bdf)); ! 142: struct vring_virtqueue *vq = NULL; ! 143: u16 ioaddr = vp_init_simple(bdf); ! 144: if (vp_find_vq(ioaddr, 2, &vq) < 0 ) { ! 145: dprintf(1, "fail to find vq for virtio-scsi %x:%x\n", ! 146: pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)); ! 147: goto fail; ! 148: } ! 149: ! 150: int i, tot; ! 151: for (tot = 0, i = 0; i < 256; i++) ! 152: tot += virtio_scsi_scan_target(pci, ioaddr, vq, i); ! 153: ! 154: if (!tot) ! 155: goto fail; ! 156: ! 157: vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE | ! 158: VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK); ! 159: return; ! 160: ! 161: fail: ! 162: free(vq); ! 163: } ! 164: ! 165: void ! 166: virtio_scsi_setup(void) ! 167: { ! 168: ASSERT32FLAT(); ! 169: if (! CONFIG_VIRTIO_SCSI || CONFIG_COREBOOT) ! 170: return; ! 171: ! 172: dprintf(3, "init virtio-scsi\n"); ! 173: ! 174: struct pci_device *pci; ! 175: foreachpci(pci) { ! 176: if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET ! 177: || pci->device != PCI_DEVICE_ID_VIRTIO_SCSI) ! 178: continue; ! 179: init_virtio_scsi(pci); ! 180: } ! 181: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.