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 
19 static int virtio_blk_read_many(VDev *vdev, ulong 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 
52 int virtio_read_many(ulong 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 
66 unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
67                                  ulong 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     ulong addr = (ulong)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 
89 int virtio_read(ulong 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  */
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 
113 VirtioGDN virtio_guessed_disk_nature(void)
114 {
115     return virtio_get_device()->guessed_disk_nature;
116 }
117 
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 
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 
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 
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 << vdev->config.blk.physical_block_exp;
177     case VIRTIO_ID_SCSI:
178         return vdev->scsi_block_size;
179     }
180     return 0;
181 }
182 
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 
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 
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 
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