|
|
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.