1867e039aSThomas Huth /*
2867e039aSThomas Huth * Virtio driver bits
3867e039aSThomas Huth *
4867e039aSThomas Huth * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
5867e039aSThomas Huth *
6867e039aSThomas Huth * This work is licensed under the terms of the GNU GPL, version 2 or (at
7867e039aSThomas Huth * your option) any later version. See the COPYING file in the top-level
8867e039aSThomas Huth * directory.
9867e039aSThomas Huth */
10867e039aSThomas Huth
119f427883SJared Rossi #include <stdio.h>
12867e039aSThomas Huth #include "s390-ccw.h"
13867e039aSThomas Huth #include "virtio.h"
14867e039aSThomas Huth #include "virtio-scsi.h"
15867e039aSThomas Huth
169125a314SThomas Huth #define VIRTIO_BLK_F_GEOMETRY (1 << 4)
179125a314SThomas Huth #define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
189125a314SThomas Huth
virtio_blk_read_many(VDev * vdev,unsigned long sector,void * load_addr,int sec_num)19f7f2f96fSJuan Quintela static int virtio_blk_read_many(VDev *vdev, unsigned long sector, void *load_addr,
20867e039aSThomas Huth int sec_num)
21867e039aSThomas Huth {
22867e039aSThomas Huth VirtioBlkOuthdr out_hdr;
23867e039aSThomas Huth u8 status;
24867e039aSThomas Huth VRing *vr = &vdev->vrings[vdev->cmd_vr_idx];
25867e039aSThomas Huth
26867e039aSThomas Huth /* Tell the host we want to read */
27867e039aSThomas Huth out_hdr.type = VIRTIO_BLK_T_IN;
28867e039aSThomas Huth out_hdr.ioprio = 99;
29867e039aSThomas Huth out_hdr.sector = virtio_sector_adjust(sector);
30867e039aSThomas Huth
31867e039aSThomas Huth vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT);
32867e039aSThomas Huth
33867e039aSThomas Huth /* This is where we want to receive data */
34867e039aSThomas Huth vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num,
35867e039aSThomas Huth VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
36867e039aSThomas Huth VRING_DESC_F_NEXT);
37867e039aSThomas Huth
38867e039aSThomas Huth /* status field */
39867e039aSThomas Huth vring_send_buf(vr, &status, sizeof(u8),
40867e039aSThomas Huth VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN);
41867e039aSThomas Huth
42867e039aSThomas Huth /* Now we can tell the host to read */
43867e039aSThomas Huth vring_wait_reply();
44867e039aSThomas Huth
45867e039aSThomas Huth if (drain_irqs(vr->schid)) {
46867e039aSThomas Huth /* Well, whatever status is supposed to contain... */
47867e039aSThomas Huth status = 1;
48867e039aSThomas Huth }
49867e039aSThomas Huth return status;
50867e039aSThomas Huth }
51867e039aSThomas Huth
virtio_read_many(unsigned long sector,void * load_addr,int sec_num)52f7f2f96fSJuan Quintela int virtio_read_many(unsigned long sector, void *load_addr, int sec_num)
53867e039aSThomas Huth {
54867e039aSThomas Huth VDev *vdev = virtio_get_device();
55867e039aSThomas Huth
56867e039aSThomas Huth switch (vdev->senseid.cu_model) {
57867e039aSThomas Huth case VIRTIO_ID_BLOCK:
58867e039aSThomas Huth return virtio_blk_read_many(vdev, sector, load_addr, sec_num);
59867e039aSThomas Huth case VIRTIO_ID_SCSI:
60867e039aSThomas Huth return virtio_scsi_read_many(vdev, sector, load_addr, sec_num);
61867e039aSThomas Huth }
62*0181e237SJared Rossi
63867e039aSThomas Huth return -1;
64867e039aSThomas Huth }
65867e039aSThomas Huth
virtio_load_direct(unsigned long rec_list1,unsigned long rec_list2,unsigned long subchan_id,void * load_addr)66f7f2f96fSJuan Quintela unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
67f7f2f96fSJuan Quintela unsigned long subchan_id, void *load_addr)
68867e039aSThomas Huth {
69867e039aSThomas Huth u8 status;
70867e039aSThomas Huth int sec = rec_list1;
71867e039aSThomas Huth int sec_num = ((rec_list2 >> 32) & 0xffff) + 1;
72867e039aSThomas Huth int sec_len = rec_list2 >> 48;
73f7f2f96fSJuan Quintela unsigned long addr = (unsigned long)load_addr;
74867e039aSThomas Huth
75867e039aSThomas Huth if (sec_len != virtio_get_block_size()) {
76facd91acSJared Rossi return 0;
77867e039aSThomas Huth }
78867e039aSThomas Huth
799f427883SJared Rossi printf(".");
80867e039aSThomas Huth status = virtio_read_many(sec, (void *)addr, sec_num);
81867e039aSThomas Huth if (status) {
82facd91acSJared Rossi return 0;
83867e039aSThomas Huth }
84867e039aSThomas Huth addr += sec_num * virtio_get_block_size();
85867e039aSThomas Huth
86867e039aSThomas Huth return addr;
87867e039aSThomas Huth }
88867e039aSThomas Huth
virtio_read(unsigned long sector,void * load_addr)89f7f2f96fSJuan Quintela int virtio_read(unsigned long sector, void *load_addr)
90867e039aSThomas Huth {
91867e039aSThomas Huth return virtio_read_many(sector, load_addr, 1);
92867e039aSThomas Huth }
93867e039aSThomas Huth
94867e039aSThomas Huth /*
95867e039aSThomas Huth * Other supported value pairs, if any, would need to be added here.
96867e039aSThomas Huth * Note: head count is always 15.
97867e039aSThomas Huth */
virtio_eckd_sectors_for_block_size(int size)98867e039aSThomas Huth static inline u8 virtio_eckd_sectors_for_block_size(int size)
99867e039aSThomas Huth {
100867e039aSThomas Huth switch (size) {
101867e039aSThomas Huth case 512:
102867e039aSThomas Huth return 49;
103867e039aSThomas Huth case 1024:
104867e039aSThomas Huth return 33;
105867e039aSThomas Huth case 2048:
106867e039aSThomas Huth return 21;
107867e039aSThomas Huth case 4096:
108867e039aSThomas Huth return 12;
109867e039aSThomas Huth }
110867e039aSThomas Huth return 0;
111867e039aSThomas Huth }
112867e039aSThomas Huth
virtio_guessed_disk_nature(void)113867e039aSThomas Huth VirtioGDN virtio_guessed_disk_nature(void)
114867e039aSThomas Huth {
115867e039aSThomas Huth return virtio_get_device()->guessed_disk_nature;
116867e039aSThomas Huth }
117867e039aSThomas Huth
virtio_assume_iso9660(void)118867e039aSThomas Huth void virtio_assume_iso9660(void)
119867e039aSThomas Huth {
120867e039aSThomas Huth VDev *vdev = virtio_get_device();
121867e039aSThomas Huth
122867e039aSThomas Huth switch (vdev->senseid.cu_model) {
123867e039aSThomas Huth case VIRTIO_ID_BLOCK:
124867e039aSThomas Huth vdev->guessed_disk_nature = VIRTIO_GDN_SCSI;
125867e039aSThomas Huth vdev->config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE;
126867e039aSThomas Huth vdev->config.blk.physical_block_exp = 0;
127867e039aSThomas Huth vdev->blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE;
128867e039aSThomas Huth break;
129867e039aSThomas Huth case VIRTIO_ID_SCSI:
130867e039aSThomas Huth vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE;
131867e039aSThomas Huth break;
132867e039aSThomas Huth }
133867e039aSThomas Huth }
134867e039aSThomas Huth
virtio_assume_eckd(void)135867e039aSThomas Huth void virtio_assume_eckd(void)
136867e039aSThomas Huth {
137867e039aSThomas Huth VDev *vdev = virtio_get_device();
138867e039aSThomas Huth
139867e039aSThomas Huth vdev->guessed_disk_nature = VIRTIO_GDN_DASD;
140867e039aSThomas Huth vdev->blk_factor = 1;
141867e039aSThomas Huth vdev->config.blk.physical_block_exp = 0;
142867e039aSThomas Huth switch (vdev->senseid.cu_model) {
143867e039aSThomas Huth case VIRTIO_ID_BLOCK:
1441f2c2ee4SThomas Huth vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE;
145867e039aSThomas Huth break;
146867e039aSThomas Huth case VIRTIO_ID_SCSI:
147867e039aSThomas Huth vdev->config.blk.blk_size = vdev->scsi_block_size;
148867e039aSThomas Huth break;
149867e039aSThomas Huth }
150867e039aSThomas Huth vdev->config.blk.geometry.heads = 15;
151867e039aSThomas Huth vdev->config.blk.geometry.sectors =
152867e039aSThomas Huth virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size);
153867e039aSThomas Huth }
154867e039aSThomas Huth
virtio_ipl_disk_is_valid(void)155867e039aSThomas Huth bool virtio_ipl_disk_is_valid(void)
156867e039aSThomas Huth {
157bbf615f7SThomas Huth int blksize = virtio_get_block_size();
158bbf615f7SThomas Huth VDev *vdev = virtio_get_device();
159bbf615f7SThomas Huth
160bbf615f7SThomas Huth if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI ||
161bbf615f7SThomas Huth vdev->guessed_disk_nature == VIRTIO_GDN_DASD) {
162bbf615f7SThomas Huth return true;
163bbf615f7SThomas Huth }
164bbf615f7SThomas Huth
165bbf615f7SThomas Huth return (vdev->senseid.cu_model == VIRTIO_ID_BLOCK ||
166bbf615f7SThomas Huth vdev->senseid.cu_model == VIRTIO_ID_SCSI) &&
167bbf615f7SThomas Huth blksize >= 512 && blksize <= 4096;
168867e039aSThomas Huth }
169867e039aSThomas Huth
virtio_get_block_size(void)170867e039aSThomas Huth int virtio_get_block_size(void)
171867e039aSThomas Huth {
172867e039aSThomas Huth VDev *vdev = virtio_get_device();
173867e039aSThomas Huth
174867e039aSThomas Huth switch (vdev->senseid.cu_model) {
175867e039aSThomas Huth case VIRTIO_ID_BLOCK:
176393296deSThomas Huth return vdev->config.blk.blk_size;
177867e039aSThomas Huth case VIRTIO_ID_SCSI:
178867e039aSThomas Huth return vdev->scsi_block_size;
179867e039aSThomas Huth }
180867e039aSThomas Huth return 0;
181867e039aSThomas Huth }
182867e039aSThomas Huth
virtio_get_heads(void)183867e039aSThomas Huth uint8_t virtio_get_heads(void)
184867e039aSThomas Huth {
185867e039aSThomas Huth VDev *vdev = virtio_get_device();
186867e039aSThomas Huth
187867e039aSThomas Huth switch (vdev->senseid.cu_model) {
188867e039aSThomas Huth case VIRTIO_ID_BLOCK:
189867e039aSThomas Huth return vdev->config.blk.geometry.heads;
190867e039aSThomas Huth case VIRTIO_ID_SCSI:
191867e039aSThomas Huth return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
192867e039aSThomas Huth ? vdev->config.blk.geometry.heads : 255;
193867e039aSThomas Huth }
194867e039aSThomas Huth return 0;
195867e039aSThomas Huth }
196867e039aSThomas Huth
virtio_get_sectors(void)197867e039aSThomas Huth uint8_t virtio_get_sectors(void)
198867e039aSThomas Huth {
199867e039aSThomas Huth VDev *vdev = virtio_get_device();
200867e039aSThomas Huth
201867e039aSThomas Huth switch (vdev->senseid.cu_model) {
202867e039aSThomas Huth case VIRTIO_ID_BLOCK:
203867e039aSThomas Huth return vdev->config.blk.geometry.sectors;
204867e039aSThomas Huth case VIRTIO_ID_SCSI:
205867e039aSThomas Huth return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
206867e039aSThomas Huth ? vdev->config.blk.geometry.sectors : 63;
207867e039aSThomas Huth }
208867e039aSThomas Huth return 0;
209867e039aSThomas Huth }
210867e039aSThomas Huth
virtio_get_blocks(void)211867e039aSThomas Huth uint64_t virtio_get_blocks(void)
212867e039aSThomas Huth {
213867e039aSThomas Huth VDev *vdev = virtio_get_device();
214867e039aSThomas Huth const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE;
215867e039aSThomas Huth
216867e039aSThomas Huth switch (vdev->senseid.cu_model) {
217867e039aSThomas Huth case VIRTIO_ID_BLOCK:
218867e039aSThomas Huth return vdev->config.blk.capacity / factor;
219867e039aSThomas Huth case VIRTIO_ID_SCSI:
220867e039aSThomas Huth return vdev->scsi_last_block / factor;
221867e039aSThomas Huth }
222867e039aSThomas Huth return 0;
223867e039aSThomas Huth }
224867e039aSThomas Huth
virtio_blk_setup_device(SubChannelId schid)225605751b5SThomas Huth int virtio_blk_setup_device(SubChannelId schid)
226867e039aSThomas Huth {
227867e039aSThomas Huth VDev *vdev = virtio_get_device();
228867e039aSThomas Huth
2299125a314SThomas Huth vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE;
230867e039aSThomas Huth vdev->schid = schid;
231867e039aSThomas Huth virtio_setup_ccw(vdev);
232867e039aSThomas Huth
2339f427883SJared Rossi puts("Using virtio-blk.");
234867e039aSThomas Huth
235cf30b7c4SThomas Huth return 0;
236867e039aSThomas Huth }
237