1
2
3
4
5
6
7
8
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
19static 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
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
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
39 vring_send_buf(vr, &status, sizeof(u8),
40 VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN);
41
42
43 vring_wait_reply();
44
45 if (drain_irqs(vr->schid)) {
46
47 status = 1;
48 }
49 return status;
50}
51
52int 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
66unsigned 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
89int virtio_read(ulong sector, void *load_addr)
90{
91 return virtio_read_many(sector, load_addr, 1);
92}
93
94
95
96
97
98static 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
113VirtioGDN virtio_guessed_disk_nature(void)
114{
115 return virtio_get_device()->guessed_disk_nature;
116}
117
118void 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
135void 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
155bool 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
170int 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
183uint8_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
197uint8_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
211uint64_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
225int 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