4 * Copyright (c) 2013 Alexander Graf <agraf@suse.de>
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
14 #include "virtio-scsi.h"
16 static int virtio_blk_read_many(VDev
*vdev
, ulong sector
, void *load_addr
,
19 VirtioBlkOuthdr out_hdr
;
21 VRing
*vr
= &vdev
->vrings
[vdev
->cmd_vr_idx
];
23 /* Tell the host we want to read */
24 out_hdr
.type
= VIRTIO_BLK_T_IN
;
26 out_hdr
.sector
= virtio_sector_adjust(sector
);
28 vring_send_buf(vr
, &out_hdr
, sizeof(out_hdr
), VRING_DESC_F_NEXT
);
30 /* This is where we want to receive data */
31 vring_send_buf(vr
, load_addr
, virtio_get_block_size() * sec_num
,
32 VRING_DESC_F_WRITE
| VRING_HIDDEN_IS_CHAIN
|
36 vring_send_buf(vr
, &status
, sizeof(u8
),
37 VRING_DESC_F_WRITE
| VRING_HIDDEN_IS_CHAIN
);
39 /* Now we can tell the host to read */
42 if (drain_irqs(vr
->schid
)) {
43 /* Well, whatever status is supposed to contain... */
49 int virtio_read_many(ulong sector
, void *load_addr
, int sec_num
)
51 VDev
*vdev
= virtio_get_device();
53 switch (vdev
->senseid
.cu_model
) {
55 return virtio_blk_read_many(vdev
, sector
, load_addr
, sec_num
);
57 return virtio_scsi_read_many(vdev
, sector
, load_addr
, sec_num
);
59 panic("\n! No readable IPL device !\n");
63 unsigned long virtio_load_direct(ulong rec_list1
, ulong rec_list2
,
64 ulong subchan_id
, void *load_addr
)
68 int sec_num
= ((rec_list2
>> 32) & 0xffff) + 1;
69 int sec_len
= rec_list2
>> 48;
70 ulong addr
= (ulong
)load_addr
;
72 if (sec_len
!= virtio_get_block_size()) {
77 status
= virtio_read_many(sec
, (void *)addr
, sec_num
);
81 addr
+= sec_num
* virtio_get_block_size();
86 int virtio_read(ulong sector
, void *load_addr
)
88 return virtio_read_many(sector
, load_addr
, 1);
92 * Other supported value pairs, if any, would need to be added here.
93 * Note: head count is always 15.
95 static inline u8
virtio_eckd_sectors_for_block_size(int size
)
110 VirtioGDN
virtio_guessed_disk_nature(void)
112 return virtio_get_device()->guessed_disk_nature
;
115 void virtio_assume_scsi(void)
117 VDev
*vdev
= virtio_get_device();
119 switch (vdev
->senseid
.cu_model
) {
120 case VIRTIO_ID_BLOCK
:
121 vdev
->guessed_disk_nature
= VIRTIO_GDN_SCSI
;
122 vdev
->config
.blk
.blk_size
= VIRTIO_SCSI_BLOCK_SIZE
;
123 vdev
->config
.blk
.physical_block_exp
= 0;
124 vdev
->blk_factor
= 1;
127 vdev
->scsi_block_size
= VIRTIO_SCSI_BLOCK_SIZE
;
132 void virtio_assume_iso9660(void)
134 VDev
*vdev
= virtio_get_device();
136 switch (vdev
->senseid
.cu_model
) {
137 case VIRTIO_ID_BLOCK
:
138 vdev
->guessed_disk_nature
= VIRTIO_GDN_SCSI
;
139 vdev
->config
.blk
.blk_size
= VIRTIO_ISO_BLOCK_SIZE
;
140 vdev
->config
.blk
.physical_block_exp
= 0;
141 vdev
->blk_factor
= VIRTIO_ISO_BLOCK_SIZE
/ VIRTIO_SECTOR_SIZE
;
144 vdev
->scsi_block_size
= VIRTIO_ISO_BLOCK_SIZE
;
149 void virtio_assume_eckd(void)
151 VDev
*vdev
= virtio_get_device();
153 vdev
->guessed_disk_nature
= VIRTIO_GDN_DASD
;
154 vdev
->blk_factor
= 1;
155 vdev
->config
.blk
.physical_block_exp
= 0;
156 switch (vdev
->senseid
.cu_model
) {
157 case VIRTIO_ID_BLOCK
:
158 vdev
->config
.blk
.blk_size
= 4096;
161 vdev
->config
.blk
.blk_size
= vdev
->scsi_block_size
;
164 vdev
->config
.blk
.geometry
.heads
= 15;
165 vdev
->config
.blk
.geometry
.sectors
=
166 virtio_eckd_sectors_for_block_size(vdev
->config
.blk
.blk_size
);
169 bool virtio_disk_is_scsi(void)
171 VDev
*vdev
= virtio_get_device();
173 if (vdev
->guessed_disk_nature
== VIRTIO_GDN_SCSI
) {
176 switch (vdev
->senseid
.cu_model
) {
177 case VIRTIO_ID_BLOCK
:
178 return (vdev
->config
.blk
.geometry
.heads
== 255)
179 && (vdev
->config
.blk
.geometry
.sectors
== 63)
180 && (virtio_get_block_size() == VIRTIO_SCSI_BLOCK_SIZE
);
187 bool virtio_disk_is_eckd(void)
189 VDev
*vdev
= virtio_get_device();
190 const int block_size
= virtio_get_block_size();
192 if (vdev
->guessed_disk_nature
== VIRTIO_GDN_DASD
) {
195 switch (vdev
->senseid
.cu_model
) {
196 case VIRTIO_ID_BLOCK
:
197 return (vdev
->config
.blk
.geometry
.heads
== 15)
198 && (vdev
->config
.blk
.geometry
.sectors
==
199 virtio_eckd_sectors_for_block_size(block_size
));
206 bool virtio_ipl_disk_is_valid(void)
208 return virtio_disk_is_scsi() || virtio_disk_is_eckd();
211 int virtio_get_block_size(void)
213 VDev
*vdev
= virtio_get_device();
215 switch (vdev
->senseid
.cu_model
) {
216 case VIRTIO_ID_BLOCK
:
217 return vdev
->config
.blk
.blk_size
<< vdev
->config
.blk
.physical_block_exp
;
219 return vdev
->scsi_block_size
;
224 uint8_t virtio_get_heads(void)
226 VDev
*vdev
= virtio_get_device();
228 switch (vdev
->senseid
.cu_model
) {
229 case VIRTIO_ID_BLOCK
:
230 return vdev
->config
.blk
.geometry
.heads
;
232 return vdev
->guessed_disk_nature
== VIRTIO_GDN_DASD
233 ? vdev
->config
.blk
.geometry
.heads
: 255;
238 uint8_t virtio_get_sectors(void)
240 VDev
*vdev
= virtio_get_device();
242 switch (vdev
->senseid
.cu_model
) {
243 case VIRTIO_ID_BLOCK
:
244 return vdev
->config
.blk
.geometry
.sectors
;
246 return vdev
->guessed_disk_nature
== VIRTIO_GDN_DASD
247 ? vdev
->config
.blk
.geometry
.sectors
: 63;
252 uint64_t virtio_get_blocks(void)
254 VDev
*vdev
= virtio_get_device();
255 const uint64_t factor
= virtio_get_block_size() / VIRTIO_SECTOR_SIZE
;
257 switch (vdev
->senseid
.cu_model
) {
258 case VIRTIO_ID_BLOCK
:
259 return vdev
->config
.blk
.capacity
/ factor
;
261 return vdev
->scsi_last_block
/ factor
;
266 int virtio_blk_setup_device(SubChannelId schid
)
268 VDev
*vdev
= virtio_get_device();
272 virtio_setup_ccw(vdev
);
274 switch (vdev
->senseid
.cu_model
) {
275 case VIRTIO_ID_BLOCK
:
276 sclp_print("Using virtio-blk.\n");
277 if (!virtio_ipl_disk_is_valid()) {
278 /* make sure all getters but blocksize return 0 for
281 memset(&vdev
->config
.blk
, 0, sizeof(vdev
->config
.blk
));
282 virtio_assume_scsi();
286 IPL_assert(vdev
->config
.scsi
.sense_size
== VIRTIO_SCSI_SENSE_SIZE
,
287 "Config: sense size mismatch");
288 IPL_assert(vdev
->config
.scsi
.cdb_size
== VIRTIO_SCSI_CDB_SIZE
,
289 "Config: CDB size mismatch");
291 sclp_print("Using virtio-scsi.\n");
292 ret
= virtio_scsi_setup(vdev
);
295 panic("\n! No IPL device available !\n");