qemu/pc-bios/s390-ccw/virtio-blkdev.c

237 lines
6.0 KiB
C
Raw Normal View History

/*
* Virtio driver bits
*
* Copyright (c) 2013 Alexander Graf <agraf@suse.de>
*
* This work is licensed under the terms of the GNU GPL, version 2 or (at
* your option) any later version. See the COPYING file in the top-level
* directory.
*/
#include "libc.h"
#include "s390-ccw.h"
#include "virtio.h"
#include "virtio-scsi.h"
#define VIRTIO_BLK_F_GEOMETRY (1 << 4)
#define VIRTIO_BLK_F_BLK_SIZE (1 << 6)
static int virtio_blk_read_many(VDev *vdev, unsigned long sector, void *load_addr,
int sec_num)
{
VirtioBlkOuthdr out_hdr;
u8 status;
VRing *vr = &vdev->vrings[vdev->cmd_vr_idx];
/* Tell the host we want to read */
out_hdr.type = VIRTIO_BLK_T_IN;
out_hdr.ioprio = 99;
out_hdr.sector = virtio_sector_adjust(sector);
vring_send_buf(vr, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT);
/* This is where we want to receive data */
vring_send_buf(vr, load_addr, virtio_get_block_size() * sec_num,
VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN |
VRING_DESC_F_NEXT);
/* status field */
vring_send_buf(vr, &status, sizeof(u8),
VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN);
/* Now we can tell the host to read */
vring_wait_reply();
if (drain_irqs(vr->schid)) {
/* Well, whatever status is supposed to contain... */
status = 1;
}
return status;
}
int virtio_read_many(unsigned long sector, void *load_addr, int sec_num)
{
VDev *vdev = virtio_get_device();
switch (vdev->senseid.cu_model) {
case VIRTIO_ID_BLOCK:
return virtio_blk_read_many(vdev, sector, load_addr, sec_num);
case VIRTIO_ID_SCSI:
return virtio_scsi_read_many(vdev, sector, load_addr, sec_num);
}
panic("\n! No readable IPL device !\n");
return -1;
}
unsigned long virtio_load_direct(unsigned long rec_list1, unsigned long rec_list2,
unsigned long subchan_id, void *load_addr)
{
u8 status;
int sec = rec_list1;
int sec_num = ((rec_list2 >> 32) & 0xffff) + 1;
int sec_len = rec_list2 >> 48;
unsigned long addr = (unsigned long)load_addr;
if (sec_len != virtio_get_block_size()) {
return -1;
}
sclp_print(".");
status = virtio_read_many(sec, (void *)addr, sec_num);
if (status) {
panic("I/O Error");
}
addr += sec_num * virtio_get_block_size();
return addr;
}
int virtio_read(unsigned long sector, void *load_addr)
{
return virtio_read_many(sector, load_addr, 1);
}
/*
* Other supported value pairs, if any, would need to be added here.
* Note: head count is always 15.
*/
static inline u8 virtio_eckd_sectors_for_block_size(int size)
{
switch (size) {
case 512:
return 49;
case 1024:
return 33;
case 2048:
return 21;
case 4096:
return 12;
}
return 0;
}
VirtioGDN virtio_guessed_disk_nature(void)
{
return virtio_get_device()->guessed_disk_nature;
}
void virtio_assume_iso9660(void)
{
VDev *vdev = virtio_get_device();
switch (vdev->senseid.cu_model) {
case VIRTIO_ID_BLOCK:
vdev->guessed_disk_nature = VIRTIO_GDN_SCSI;
vdev->config.blk.blk_size = VIRTIO_ISO_BLOCK_SIZE;
vdev->config.blk.physical_block_exp = 0;
vdev->blk_factor = VIRTIO_ISO_BLOCK_SIZE / VIRTIO_SECTOR_SIZE;
break;
case VIRTIO_ID_SCSI:
vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE;
break;
}
}
void virtio_assume_eckd(void)
{
VDev *vdev = virtio_get_device();
vdev->guessed_disk_nature = VIRTIO_GDN_DASD;
vdev->blk_factor = 1;
vdev->config.blk.physical_block_exp = 0;
switch (vdev->senseid.cu_model) {
case VIRTIO_ID_BLOCK:
vdev->config.blk.blk_size = VIRTIO_DASD_DEFAULT_BLOCK_SIZE;
break;
case VIRTIO_ID_SCSI:
vdev->config.blk.blk_size = vdev->scsi_block_size;
break;
}
vdev->config.blk.geometry.heads = 15;
vdev->config.blk.geometry.sectors =
virtio_eckd_sectors_for_block_size(vdev->config.blk.blk_size);
}
bool virtio_ipl_disk_is_valid(void)
{
int blksize = virtio_get_block_size();
VDev *vdev = virtio_get_device();
if (vdev->guessed_disk_nature == VIRTIO_GDN_SCSI ||
vdev->guessed_disk_nature == VIRTIO_GDN_DASD) {
return true;
}
return (vdev->senseid.cu_model == VIRTIO_ID_BLOCK ||
vdev->senseid.cu_model == VIRTIO_ID_SCSI) &&
blksize >= 512 && blksize <= 4096;
}
int virtio_get_block_size(void)
{
VDev *vdev = virtio_get_device();
switch (vdev->senseid.cu_model) {
case VIRTIO_ID_BLOCK:
pc-bios/s390-ccw: Fix booting with logical block size < physical block size For accessing single blocks during boot, it's the logical block size that matters. (Physical block sizes are rather interesting e.g. for creating file systems with the correct alignment for speed reasons etc.). So the s390-ccw bios has to use the logical block size for calculating sector numbers during the boot phase, the "physical_block_exp" shift value must not be taken into account. This change fixes the boot process when the guest hast been installed on a disk where the logical block size differs from the physical one, e.g. if the guest has been installed like this: qemu-system-s390x -nographic -accel kvm -m 2G \ -drive if=none,id=d1,file=fedora.iso,format=raw,media=cdrom \ -device virtio-scsi -device scsi-cd,drive=d1 \ -drive if=none,id=d2,file=test.qcow2,format=qcow2 -device virtio-blk,drive=d2,physical_block_size=4096,logical_block_size=512 Linux correctly uses the logical block size of 512 for the installation, but the s390-ccw bios tries to boot from a disk with 4096 block size so far, as long as this patch has not been applied yet (well, it used to work by accident in the past due to the virtio_assume_scsi() hack that used to enforce 512 byte sectors on all virtio-block disks, but that hack has been well removed in commit 5447de2619050a0a4d to fix other scenarios). Fixes: 5447de2619 ("pc-bios/s390-ccw/virtio-blkdev: Remove virtio_assume_scsi()") Buglink: https://bugzilla.redhat.com/show_bug.cgi?id=2112303 Message-Id: <20220805094214.285223-1-thuth@redhat.com> Reviewed-by: Cornelia Huck <cohuck@redhat.com> Reviewed-by: Eric Farman <farman@linux.ibm.com> Signed-off-by: Thomas Huth <thuth@redhat.com>
2022-08-05 12:42:14 +03:00
return vdev->config.blk.blk_size;
case VIRTIO_ID_SCSI:
return vdev->scsi_block_size;
}
return 0;
}
uint8_t virtio_get_heads(void)
{
VDev *vdev = virtio_get_device();
switch (vdev->senseid.cu_model) {
case VIRTIO_ID_BLOCK:
return vdev->config.blk.geometry.heads;
case VIRTIO_ID_SCSI:
return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
? vdev->config.blk.geometry.heads : 255;
}
return 0;
}
uint8_t virtio_get_sectors(void)
{
VDev *vdev = virtio_get_device();
switch (vdev->senseid.cu_model) {
case VIRTIO_ID_BLOCK:
return vdev->config.blk.geometry.sectors;
case VIRTIO_ID_SCSI:
return vdev->guessed_disk_nature == VIRTIO_GDN_DASD
? vdev->config.blk.geometry.sectors : 63;
}
return 0;
}
uint64_t virtio_get_blocks(void)
{
VDev *vdev = virtio_get_device();
const uint64_t factor = virtio_get_block_size() / VIRTIO_SECTOR_SIZE;
switch (vdev->senseid.cu_model) {
case VIRTIO_ID_BLOCK:
return vdev->config.blk.capacity / factor;
case VIRTIO_ID_SCSI:
return vdev->scsi_last_block / factor;
}
return 0;
}
int virtio_blk_setup_device(SubChannelId schid)
{
VDev *vdev = virtio_get_device();
vdev->guest_features[0] = VIRTIO_BLK_F_GEOMETRY | VIRTIO_BLK_F_BLK_SIZE;
vdev->schid = schid;
virtio_setup_ccw(vdev);
sclp_print("Using virtio-blk.\n");
return 0;
}