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 "libc.h"
12 #include "s390-ccw.h"
13 #include "virtio.h"
14 #include "virtio-scsi.h"
15
16 #define VIRTIO_BLK_F_GEOMETRY (1 << 4)
17 #define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
18
virtio_blk_read_many(VDev * vdev,unsigned long sector,void * load_addr,int sec_num)19 static int virtio_blk_read_many(VDev *vdev, unsigned long sector, void *load_addr,
20 int sec_num)
21 {
22 VirtioBlkOuthdr out_hdr;
23 u8 status;
24 VRing *vr = &vdev->vrings[vdev->cmd_vr_idx];
25
26 /* Tell the host we want to read */
27 out_hdr.type = VIRTIO_BLK_T_IN;
28 out_hdr.ioprio = 99;
29 out_hdr.sector = virtio_sector_adjust(sector);
30
31 vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT);
32
33 /* This is where we want to receive data */
34 vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num,
35 VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
36 VRING_DESC_F_NEXT);
37
38 /* status field */
39 vring_send_buf(vr, &status, sizeof(u8),
40 VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN);
41
42 /* Now we can tell the host to read */
43 vring_wait_reply();
44
45 if (drain_irqs(vr->schid)) {
46 /* Well, whatever status is supposed to contain... */
47 status = 1;
48 }
49 return status;
50 }
51
virtio_read_many(unsigned long sector,void * load_addr,int sec_num)52 int virtio_read_many(unsigned long sector, void *load_addr, int sec_num)
53 {
54 VDev *vdev = virtio_get_device();
55
56 switch (vdev->senseid.cu_model) {
57 case VIRTIO_ID_BLOCK:
58 return virtio_blk_read_many(vdev, sector, load_addr, sec_num);
59 case VIRTIO_ID_SCSI:
60 return virtio_scsi_read_many(vdev, sector, load_addr, sec_num);
61 }
62 panic("\n! No readable IPL device !\n");
63 return -1;
64 }
65
virtio_load_direct(unsigned long rec_list1,unsigned long rec_list2,unsigned long subchan_id,void * load_addr)66 unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
67 unsigned long subchan_id, void *load_addr)
68 {
69 u8 status;
70 int sec = rec_list1;
71 int sec_num = ((rec_list2 >> 32) & 0xffff) + 1;
72 int sec_len = rec_list2 >> 48;
73 unsigned long addr = (unsigned long)load_addr;
74
75 if (sec_len != virtio_get_block_size()) {
76 return -1;
77 }
78
79 sclp_print(".");
80 status = virtio_read_many(sec, (void *)addr, sec_num);
81 if (status) {
82 panic("I/O Error");
83 }
84 addr += sec_num * virtio_get_block_size();
85
86 return addr;
87 }
88
virtio_read(unsigned long sector,void * load_addr)89 int virtio_read(unsigned long sector, void *load_addr)
90 {
91 return virtio_read_many(sector, load_addr, 1);
92 }
93
94 /*
95 * Other supported value pairs, if any, would need to be added here.
96 * Note: head count is always 15.
97 */
virtio_eckd_sectors_for_block_size(int size)98 static inline u8 virtio_eckd_sectors_for_block_size(int size)
99 {
100 switch (size) {
101 case 512:
102 return 49;
103 case 1024:
104 return 33;
105 case 2048:
106 return 21;
107 case 4096:
108 return 12;
109 }
110 return 0;
111 }
112
virtio_guessed_disk_nature(void)113 VirtioGDN virtio_guessed_disk_nature(void)
114 {
115 return virtio_get_device()->guessed_disk_nature;
116 }
117
virtio_assume_iso9660(void)118 void virtio_assume_iso9660(void)
119 {
120 VDev *vdev = virtio_get_device();
121
122 switch (vdev->senseid.cu_model) {
123 case VIRTIO_ID_BLOCK:
124 vdev->guessed_disk_nature = VIRTIO_GDN_SCSI;
125 vdev->config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE;
126 vdev->config.blk.physical_block_exp = 0;
127 vdev->blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE;
128 break;
129 case VIRTIO_ID_SCSI:
130 vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE;
131 break;
132 }
133 }
134
virtio_assume_eckd(void)135 void virtio_assume_eckd(void)
136 {
137 VDev *vdev = virtio_get_device();
138
139 vdev->guessed_disk_nature = VIRTIO_GDN_DASD;
140 vdev->blk_factor = 1;
141 vdev->config.blk.physical_block_exp = 0;
142 switch (vdev->senseid.cu_model) {
143 case VIRTIO_ID_BLOCK:
144 vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE;
145 break;
146 case VIRTIO_ID_SCSI:
147 vdev->config.blk.blk_size = vdev->scsi_block_size;
148 break;
149 }
150 vdev->config.blk.geometry.heads = 15;
151 vdev->config.blk.geometry.sectors =
152 virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size);
153 }
154
virtio_ipl_disk_is_valid(void)155 bool virtio_ipl_disk_is_valid(void)
156 {
157 int blksize = virtio_get_block_size();
158 VDev *vdev = virtio_get_device();
159
160 if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI ||
161 vdev->guessed_disk_nature == VIRTIO_GDN_DASD) {
162 return true;
163 }
164
165 return (vdev->senseid.cu_model == VIRTIO_ID_BLOCK ||
166 vdev->senseid.cu_model == VIRTIO_ID_SCSI) &&
167 blksize >= 512 && blksize <= 4096;
168 }
169
virtio_get_block_size(void)170 int virtio_get_block_size(void)
171 {
172 VDev *vdev = virtio_get_device();
173
174 switch (vdev->senseid.cu_model) {
175 case VIRTIO_ID_BLOCK:
176 return vdev->config.blk.blk_size;
177 case VIRTIO_ID_SCSI:
178 return vdev->scsi_block_size;
179 }
180 return 0;
181 }
182
virtio_get_heads(void)183 uint8_t virtio_get_heads(void)
184 {
185 VDev *vdev = virtio_get_device();
186
187 switch (vdev->senseid.cu_model) {
188 case VIRTIO_ID_BLOCK:
189 return vdev->config.blk.geometry.heads;
190 case VIRTIO_ID_SCSI:
191 return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
192 ? vdev->config.blk.geometry.heads : 255;
193 }
194 return 0;
195 }
196
virtio_get_sectors(void)197 uint8_t virtio_get_sectors(void)
198 {
199 VDev *vdev = virtio_get_device();
200
201 switch (vdev->senseid.cu_model) {
202 case VIRTIO_ID_BLOCK:
203 return vdev->config.blk.geometry.sectors;
204 case VIRTIO_ID_SCSI:
205 return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
206 ? vdev->config.blk.geometry.sectors : 63;
207 }
208 return 0;
209 }
210
virtio_get_blocks(void)211 uint64_t virtio_get_blocks(void)
212 {
213 VDev *vdev = virtio_get_device();
214 const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE;
215
216 switch (vdev->senseid.cu_model) {
217 case VIRTIO_ID_BLOCK:
218 return vdev->config.blk.capacity / factor;
219 case VIRTIO_ID_SCSI:
220 return vdev->scsi_last_block / factor;
221 }
222 return 0;
223 }
224
virtio_blk_setup_device(SubChannelId schid)225 int virtio_blk_setup_device(SubChannelId schid)
226 {
227 VDev *vdev = virtio_get_device();
228
229 vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE;
230 vdev->schid = schid;
231 virtio_setup_ccw(vdev);
232
233 sclp_print("Using virtio-blk.\n");
234
235 return 0;
236 }
237