Add qemu 2.4.0
[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     u16 ioaddr;
31     u16 target;
32     u16 lun;
33 };
34
35 static int
36 virtio_scsi_cmd(u16 ioaddr, struct vring_virtqueue *vq, struct disk_op_s *op,
37                 void *cdbcmd, u16 target, u16 lun, u16 blocksize)
38 {
39     struct virtio_scsi_req_cmd req;
40     struct virtio_scsi_resp_cmd resp;
41     struct vring_list sg[3];
42
43     memset(&req, 0, sizeof(req));
44     req.lun[0] = 1;
45     req.lun[1] = target;
46     req.lun[2] = (lun >> 8) | 0x40;
47     req.lun[3] = (lun & 0xff);
48     memcpy(req.cdb, cdbcmd, 16);
49
50     u32 len = op->count * blocksize;
51     int datain = cdb_is_read(cdbcmd, blocksize);
52     int in_num = (datain ? 2 : 1);
53     int out_num = (len ? 3 : 2) - in_num;
54
55     sg[0].addr   = MAKE_FLATPTR(GET_SEG(SS), &req);
56     sg[0].length = sizeof(req);
57
58     sg[out_num].addr   = MAKE_FLATPTR(GET_SEG(SS), &resp);
59     sg[out_num].length = sizeof(resp);
60
61     if (len) {
62         int data_idx = (datain ? 2 : 1);
63         sg[data_idx].addr   = op->buf_fl;
64         sg[data_idx].length = len;
65     }
66
67     /* Add to virtqueue and kick host */
68     vring_add_buf(vq, sg, out_num, in_num, 0, 0);
69     vring_kick(ioaddr, vq, 1);
70
71     /* Wait for reply */
72     while (!vring_more_used(vq))
73         usleep(5);
74
75     /* Reclaim virtqueue element */
76     vring_get_buf(vq, NULL);
77
78     /* Clear interrupt status register.  Avoid leaving interrupts stuck if
79      * VRING_AVAIL_F_NO_INTERRUPT was ignored and interrupts were raised.
80      */
81     vp_get_isr(ioaddr);
82
83     if (resp.response == VIRTIO_SCSI_S_OK && resp.status == 0) {
84         return DISK_RET_SUCCESS;
85     }
86     return DISK_RET_EBADTRACK;
87 }
88
89 int
90 virtio_scsi_cmd_data(struct disk_op_s *op, void *cdbcmd, u16 blocksize)
91 {
92     struct virtio_lun_s *vlun_gf =
93         container_of(op->drive_gf, struct virtio_lun_s, drive);
94
95     return virtio_scsi_cmd(GET_GLOBALFLAT(vlun_gf->ioaddr),
96                            GET_GLOBALFLAT(vlun_gf->vq), op, cdbcmd,
97                            GET_GLOBALFLAT(vlun_gf->target),
98                            GET_GLOBALFLAT(vlun_gf->lun),
99                            blocksize);
100 }
101
102 static int
103 virtio_scsi_add_lun(struct pci_device *pci, u16 ioaddr,
104                     struct vring_virtqueue *vq, u16 target, u16 lun)
105 {
106     struct virtio_lun_s *vlun = malloc_fseg(sizeof(*vlun));
107     if (!vlun) {
108         warn_noalloc();
109         return -1;
110     }
111     memset(vlun, 0, sizeof(*vlun));
112     vlun->drive.type = DTYPE_VIRTIO_SCSI;
113     vlun->drive.cntl_id = pci->bdf;
114     vlun->pci = pci;
115     vlun->ioaddr = ioaddr;
116     vlun->vq = vq;
117     vlun->target = target;
118     vlun->lun = lun;
119
120     int prio = bootprio_find_scsi_device(pci, target, lun);
121     int ret = scsi_drive_setup(&vlun->drive, "virtio-scsi", prio);
122     if (ret)
123         goto fail;
124     return 0;
125
126 fail:
127     free(vlun);
128     return -1;
129 }
130
131 static int
132 virtio_scsi_scan_target(struct pci_device *pci, u16 ioaddr,
133                         struct vring_virtqueue *vq, u16 target)
134 {
135     /* TODO: send REPORT LUNS.  For now, only LUN 0 is recognized.  */
136     int ret = virtio_scsi_add_lun(pci, ioaddr, vq, target, 0);
137     return ret < 0 ? 0 : 1;
138 }
139
140 static void
141 init_virtio_scsi(struct pci_device *pci)
142 {
143     u16 bdf = pci->bdf;
144     dprintf(1, "found virtio-scsi at %x:%x\n", pci_bdf_to_bus(bdf),
145             pci_bdf_to_dev(bdf));
146     struct vring_virtqueue *vq = NULL;
147     u16 ioaddr = vp_init_simple(bdf);
148     if (vp_find_vq(ioaddr, 2, &vq) < 0 ) {
149         dprintf(1, "fail to find vq for virtio-scsi %x:%x\n",
150                 pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf));
151         goto fail;
152     }
153
154     vp_set_status(ioaddr, VIRTIO_CONFIG_S_ACKNOWLEDGE |
155                   VIRTIO_CONFIG_S_DRIVER | VIRTIO_CONFIG_S_DRIVER_OK);
156
157     int i, tot;
158     for (tot = 0, i = 0; i < 256; i++)
159         tot += virtio_scsi_scan_target(pci, ioaddr, vq, i);
160
161     if (!tot)
162         goto fail;
163
164     return;
165
166 fail:
167     vp_reset(ioaddr);
168     free(vq);
169 }
170
171 void
172 virtio_scsi_setup(void)
173 {
174     ASSERT32FLAT();
175     if (! CONFIG_VIRTIO_SCSI)
176         return;
177
178     dprintf(3, "init virtio-scsi\n");
179
180     struct pci_device *pci;
181     foreachpci(pci) {
182         if (pci->vendor != PCI_VENDOR_ID_REDHAT_QUMRANET
183             || pci->device != PCI_DEVICE_ID_VIRTIO_SCSI)
184             continue;
185         init_virtio_scsi(pci);
186     }
187 }