master
c 264 lines 6.4 KB
Raw
1 /*
2 * Virtio driver bits
3 *
4 * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
5 *
6 * This work is licensed under the terms of the GNU GPL, version 2 or (at
7 * your option) any later version. See the COPYING file in the top-level
8 * directory.
9 */
10
11 #include <stdio.h>
12 #include "s390-ccw.h"
13 #include "virtio.h"
14 #include "virtio-scsi.h"
15 #include "virtio-ccw.h"
16 #include "virtio-pci.h"
17 #include "bswap.h"
18
19 #define VIRTIO_BLK_F_GEOMETRY (1 << 4)
20 #define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
21
22 /*
23 * Format header for little endian IPL
24 */
25 static void fmt_blk_hdr_le(VirtioBlkOuthdr *hdr)
26 {
27 hdr->type = bswap32(hdr->type);
28 hdr->ioprio = bswap32(hdr->ioprio);
29 hdr->sector = bswap64(hdr->sector);
30 }
31
32 static int virtio_blk_read_many(VDev *vdev, unsigned long sector, void *load_addr,
33 int sec_num)
34 {
35 VirtioBlkOuthdr out_hdr;
36 u8 status;
37 VRing *vr = &vdev->vrings[vdev->cmd_vr_idx];
38
39 /* Tell the host we want to read */
40 out_hdr.type = VIRTIO_BLK_T_IN;
41 out_hdr.ioprio = 99;
42 out_hdr.sector = virtio_sector_adjust(sector);
43
44 if (!be_ipl()) {
45 fmt_blk_hdr_le(&out_hdr);
46 }
47
48 vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT);
49
50 /* This is where we want to receive data */
51 vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num,
52 VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
53 VRING_DESC_F_NEXT);
54
55 /* status field */
56 vring_send_buf(vr, &status, sizeof(u8),
57 VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN);
58
59 /* Now we can tell the host to read */
60 vring_wait_reply();
61
62 if (drain_irqs()) {
63 /* Well, whatever status is supposed to contain... */
64 status = 1;
65 }
66 return status;
67 }
68
69 int virtio_read_many(unsigned long sector, void *load_addr, int sec_num)
70 {
71 VDev *vdev = virtio_get_device();
72
73 switch (vdev->dev_type) {
74 case VIRTIO_ID_BLOCK:
75 return virtio_blk_read_many(vdev, sector, load_addr, sec_num);
76 case VIRTIO_ID_SCSI:
77 return virtio_scsi_read_many(vdev, sector, load_addr, sec_num);
78 default:
79 return -1;
80 }
81 }
82
83 unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
84 void *load_addr)
85 {
86 u8 status;
87 int sec = rec_list1;
88 int sec_num = ((rec_list2 >> 32) & 0xffff) + 1;
89 int sec_len = rec_list2 >> 48;
90 unsigned long addr = (unsigned long)load_addr;
91
92 if (sec_len != virtio_get_block_size()) {
93 return 0;
94 }
95
96 printf(".");
97 status = virtio_read_many(sec, (void *)addr, sec_num);
98 if (status) {
99 return 0;
100 }
101 addr += sec_num * virtio_get_block_size();
102
103 return addr;
104 }
105
106 int virtio_read(unsigned long sector, void *load_addr)
107 {
108 return virtio_read_many(sector, load_addr, 1);
109 }
110
111 /*
112 * Other supported value pairs, if any, would need to be added here.
113 * Note: head count is always 15.
114 */
115 static inline u8 virtio_eckd_sectors_for_block_size(int size)
116 {
117 switch (size) {
118 case 512:
119 return 49;
120 case 1024:
121 return 33;
122 case 2048:
123 return 21;
124 case 4096:
125 return 12;
126 }
127 return 0;
128 }
129
130 VirtioGDN virtio_guessed_disk_nature(void)
131 {
132 return virtio_get_device()->guessed_disk_nature;
133 }
134
135 void virtio_assume_iso9660(void)
136 {
137 VDev *vdev = virtio_get_device();
138
139 switch (vdev->dev_type) {
140 case VIRTIO_ID_BLOCK:
141 vdev->guessed_disk_nature = VIRTIO_GDN_SCSI;
142 vdev->config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE;
143 vdev->config.blk.physical_block_exp = 0;
144 vdev->blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE;
145 break;
146 case VIRTIO_ID_SCSI:
147 vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE;
148 break;
149 default:
150 return;
151 }
152 }
153
154 void virtio_assume_eckd(void)
155 {
156 VDev *vdev = virtio_get_device();
157
158 vdev->guessed_disk_nature = VIRTIO_GDN_DASD;
159 vdev->blk_factor = 1;
160 vdev->config.blk.physical_block_exp = 0;
161 switch (vdev->dev_type) {
162 case VIRTIO_ID_BLOCK:
163 vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE;
164 break;
165 case VIRTIO_ID_SCSI:
166 vdev->config.blk.blk_size = vdev->scsi_block_size;
167 break;
168 default:
169 break;
170 }
171 vdev->config.blk.geometry.heads = 15;
172 vdev->config.blk.geometry.sectors =
173 virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size);
174 }
175
176 bool virtio_ipl_disk_is_valid(void)
177 {
178 int blksize = virtio_get_block_size();
179 VDev *vdev = virtio_get_device();
180
181 if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI ||
182 vdev->guessed_disk_nature == VIRTIO_GDN_DASD) {
183 return true;
184 }
185
186 return (vdev->dev_type == VIRTIO_ID_BLOCK || vdev->dev_type == VIRTIO_ID_SCSI)
187 && blksize >= 512 && blksize <= 4096;
188 }
189
190 int virtio_get_block_size(void)
191 {
192 VDev *vdev = virtio_get_device();
193
194 switch (vdev->dev_type) {
195 case VIRTIO_ID_BLOCK:
196 return vdev->config.blk.blk_size;
197 case VIRTIO_ID_SCSI:
198 return vdev->scsi_block_size;
199 default:
200 return 0;
201 }
202 }
203
204 uint8_t virtio_get_heads(void)
205 {
206 VDev *vdev = virtio_get_device();
207
208 switch (vdev->dev_type) {
209 case VIRTIO_ID_BLOCK:
210 return vdev->config.blk.geometry.heads;
211 case VIRTIO_ID_SCSI:
212 return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
213 ? vdev->config.blk.geometry.heads : 255;
214 default:
215 return 0;
216 }
217 }
218
219 uint8_t virtio_get_sectors(void)
220 {
221 VDev *vdev = virtio_get_device();
222
223 switch (vdev->dev_type) {
224 case VIRTIO_ID_BLOCK:
225 return vdev->config.blk.geometry.sectors;
226 case VIRTIO_ID_SCSI:
227 return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
228 ? vdev->config.blk.geometry.sectors : 63;
229 default:
230 return 0;
231 }
232 }
233
234 uint64_t virtio_get_blocks(void)
235 {
236 VDev *vdev = virtio_get_device();
237 const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE;
238
239 switch (vdev->dev_type) {
240 case VIRTIO_ID_BLOCK:
241 return vdev->config.blk.capacity / factor;
242 case VIRTIO_ID_SCSI:
243 return vdev->scsi_last_block / factor;
244 default:
245 return 0;
246 }
247 }
248
249 int virtio_blk_setup_device(VDev *vdev)
250 {
251 vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE;
252
253 puts("Using virtio-blk.");
254
255 switch (vdev->ipl_type) {
256 case S390_IPL_TYPE_QEMU_SCSI:
257 case S390_IPL_TYPE_CCW:
258 return virtio_ccw_setup(vdev);
259 case S390_IPL_TYPE_PCI:
260 return virtio_pci_setup(vdev);
261 default:
262 return 1;
263 }
264 }