These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / roms / seabios / src / hw / virtio-scsi.c
1 // Virtio SCSI boot support.
2 //
3 // Copyright (C) 2012 Red Hat Inc.
4 //
5 // Authors:
6 //  Paolo Bonzini <pbonzini@redhat.com>
7 //
8 // This file may be distributed under the terms of the GNU LGPLv3 license.
9
10 #include "biosvar.h" // GET_GLOBALFLAT
11 #include "block.h" // struct drive_s
12 #include "blockcmd.h" // scsi_drive_setup
13 #include "config.h" // CONFIG_*
14 #include "malloc.h" // free
15 #include "output.h" // dprintf
16 #include "pci.h" // foreachpci
17 #include "pci_ids.h" // PCI_DEVICE_ID_VIRTIO_BLK
18 #include "pci_regs.h" // PCI_VENDOR_ID
19 #include "std/disk.h" // DISK_RET_SUCCESS
20 #include "string.h" // memset
21 #include "util.h" // usleep
22 #include "virtio-pci.h"
23 #include "virtio-ring.h"
24 #include "virtio-scsi.h"
25
26 struct virtio_lun_s {
27     struct drive_s drive;
28     struct pci_device *pci;
29     struct vring_virtqueue *vq;
30     struct vp_device *vp;
31     u16 target;
32     u16 lun;
33 };
34
35 int
36 virtio_scsi_process_op(struct disk_op_s *op)
37 {
38     if (! CONFIG_VIRTIO_SCSI)
39         return 0;
40     struct virtio_lun_s *vlun =
41         container_of(op->drive_gf, struct virtio_lun_s, drive);
42     struct vp_device *vp = vlun->vp;
43     struct vring_virtqueue *vq = vlun->vq;
44     struct virtio_scsi_req_cmd req;
45     struct virtio_scsi_resp_cmd resp;
46     struct vring_list sg[3];
47
48     memset(&req, 0, sizeof(req));
49     int blocksize = scsi_fill_cmd(op, req.cdb, 16);
50     if (blocksize < 0)
51         return default_process_op(op);
52     req.lun[0] = 1;
53     req.lun[1] = vlun->target;
54     req.lun[2] = (vlun->lun >> 8) | 0x40;
55     req.lun[3] = (vlun->lun & 0xff);
56
57     u32 len = op->count * blocksize;
58     int datain = scsi_is_read(op);
59     int in_num = (datain ? 2 : 1);
60     int out_num = (len ? 3 : 2) - in_num;
61
62     sg[0].addr   = (void*)(&req);
63     sg[0].length = sizeof(req);
64
65     sg[out_num].addr   = (void*)(&resp);
66     sg[out_num].length = sizeof(resp);
67
68     if (len) {
69         int data_idx = (datain ? 2 : 1);
70         sg[data_idx].addr   = op->buf_fl;
71         sg[data_idx].length = len;
72     }
73
74     /* Add to virtqueue and kick host */
75     vring_add_buf(vq, sg, out_num, in_num, 0, 0);
76     vring_kick(vp, vq, 1);
77
78     /* Wait for reply */
79     while (!vring_more_used(vq))
80         usleep(5);
81
82     /* Reclaim virtqueue element */
83     vring_get_buf(vq, NULL);
84
85     /* Clear interrupt status register.  Avoid leaving interrupts stuck if
86      * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised.
87      */
88     vp_get_isr(vp);
89
90     if (resp.response == VIRTIO_SCSI_S_OK && resp.status == 0) {
91         return DISK_RET_SUCCESS;
92     }
93     return DISK_RET_EBADTRACK;
94 }
95
96 static int
97 virtio_scsi_add_lun(struct pci_device *pci, struct vp_device *vp,
98                     struct vring_virtqueue *vq, u16 target, u16 lun)
99 {
100     struct virtio_lun_s *vlun = malloc_fseg(sizeof(*vlun));
101     if (!vlun) {
102         warn_noalloc();
103         return -1;
104     }
105     memset(vlun, 0, sizeof(*vlun));
106     vlun->drive.type = DTYPE_VIRTIO_SCSI;
107     vlun->drive.cntl_id = pci->bdf;
108     vlun->pci = pci;
109     vlun->vp = vp;
110     vlun->vq = vq;
111     vlun->target = target;
112     vlun->lun = lun;
113
114     int prio = bootprio_find_scsi_device(pci, target, lun);
115     int ret = scsi_drive_setup(&vlun->drive, "virtio-scsi", prio);
116     if (ret)
117         goto fail;
118     return 0;
119
120 fail:
121     free(vlun);
122     return -1;
123 }
124
125 static int
126 virtio_scsi_scan_target(struct pci_device *pci, struct vp_device *vp,
127                         struct vring_virtqueue *vq, u16 target)
128 {
129     /* TODO: send REPORT LUNS.  For now, only LUN 0 is recognized.  */
130     int ret = virtio_scsi_add_lun(pci, vp, vq, target, 0);
131     return ret < 0 ? 0 : 1;
132 }
133
134 static void
135 init_virtio_scsi(struct pci_device *pci)
136 {
137     u16 bdf = pci->bdf;
138     dprintf(1, "found virtio-scsi at %x:%x\n", pci_bdf_to_bus(bdf),
139             pci_bdf_to_dev(bdf));
140     struct vring_virtqueue *vq = NULL;
141     struct vp_device *vp = malloc_high(sizeof(*vp));
142     if (!vp) {
143         warn_noalloc();
144         return;
145     }
146     vp_init_simple(vp, pci);
147     u8 status = VIRTIO_CONFIG_S_ACKNOWLEDGE | VIRTIO_CONFIG_S_DRIVER;
148
149     if (vp->use_modern) {
150         u64 features = vp_get_features(vp);
151         u64 version1 = 1ull << VIRTIO_F_VERSION_1;
152         if (!(features & version1)) {
153             dprintf(1, "modern device without virtio_1 feature bit: %x:%x\n",
154                     pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
155             goto fail;
156         }
157
158         vp_set_features(vp, version1);
159         status |= VIRTIO_CONFIG_S_FEATURES_OK;
160         vp_set_status(vp, status);
161         if (!(vp_get_status(vp) & VIRTIO_CONFIG_S_FEATURES_OK)) {
162             dprintf(1, "device didn't accept features: %x:%x\n",
163                     pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
164             goto fail;
165         }
166     }
167
168     if (vp_find_vq(vp, 2, &vq) < 0 ) {
169         dprintf(1, "fail to find vq for virtio-scsi %x:%x\n",
170                 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
171         goto fail;
172     }
173
174     status |= VIRTIO_CONFIG_S_DRIVER_OK;
175     vp_set_status(vp, status);
176
177     int i, tot;
178     for (tot = 0, i = 0; i < 256; i++)
179         tot += virtio_scsi_scan_target(pci, vp, vq, i);
180
181     if (!tot)
182         goto fail;
183
184     return;
185
186 fail:
187     vp_reset(vp);
188     free(vp);
189     free(vq);
190 }
191
192 void
193 virtio_scsi_setup(void)
194 {
195     ASSERT32FLAT();
196     if (! CONFIG_VIRTIO_SCSI)
197         return;
198
199     dprintf(3, "init virtio-scsi\n");
200
201     struct pci_device *pci;
202     foreachpci(pci) {
203         if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET ||
204             (pci->device != PCI_DEVICE_ID_VIRTIO_SCSI_09 &&
205              pci->device != PCI_DEVICE_ID_VIRTIO_SCSI_10))
206             continue;
207         init_virtio_scsi(pci);
208     }
209 }