diff options
-rw-r--r-- | pc-bios/s390-ccw/Makefile | 2 | ||||
-rw-r--r-- | pc-bios/s390-ccw/main.c | 8 | ||||
-rw-r--r-- | pc-bios/s390-ccw/virtio.c | 113 | ||||
-rw-r--r-- | pc-bios/s390-ccw/virtio.h | 32 |
4 files changed, 116 insertions, 39 deletions
diff --git a/pc-bios/s390-ccw/Makefile b/pc-bios/s390-ccw/Makefile index 11c5dd4799..4208cb4295 100644 --- a/pc-bios/s390-ccw/Makefile +++ b/pc-bios/s390-ccw/Makefile @@ -9,7 +9,7 @@ $(call set-vpath, $(SRC_PATH)/pc-bios/s390-ccw) .PHONY : all clean build-all -OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o +OBJECTS = start.o main.o bootmap.o sclp-ascii.o virtio.o virtio-scsi.o CFLAGS += -fPIE -fno-stack-protector -ffreestanding -march=z900 CFLAGS += -fno-delete-null-pointer-checks -msoft-float LDFLAGS += -Wl,-pie -nostdlib diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c index 69a02feb87..1c9e0791ab 100644 --- a/pc-bios/s390-ccw/main.c +++ b/pc-bios/s390-ccw/main.c @@ -91,15 +91,11 @@ static void virtio_setup(uint64_t dev_info) } } - if (!found) { - panic("No virtio-blk device found!\n"); - } + IPL_assert(found, "No virtio device found"); virtio_setup_device(blk_schid); - if (!virtio_ipl_disk_is_valid()) { - panic("No valid hard disk detected.\n"); - } + IPL_assert(virtio_ipl_disk_is_valid(), "No valid IPL device detected"); } int main(void) diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index 4ab4d475c0..1d34e8c1aa 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -10,6 +10,7 @@ #include "s390-ccw.h" #include "virtio.h" +#include "virtio-scsi.h" #define VRING_WAIT_REPLY_TIMEOUT 3 @@ -26,6 +27,8 @@ static VDev vdev = { .ring_area = ring_area, .wait_reply_timeout = VRING_WAIT_REPLY_TIMEOUT, .schid = { .one = 1 }, + .scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE, + .blk_factor = 1, }; VDev *virtio_get_device(void) @@ -284,6 +287,8 @@ int virtio_read_many(ulong sector, void *load_addr, int sec_num) 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; @@ -317,6 +322,25 @@ int virtio_read(ulong 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 vdev.guessed_disk_nature; @@ -324,22 +348,30 @@ VirtioGDN virtio_guessed_disk_nature(void) void virtio_assume_scsi(void) { - vdev.guessed_disk_nature = VIRTIO_GDN_SCSI; switch (vdev.senseid.cu_model) { case VIRTIO_ID_BLOCK: - vdev.config.blk.blk_size = 512; + vdev.guessed_disk_nature = VIRTIO_GDN_SCSI; + vdev.config.blk.blk_size = VIRTIO_SCSI_BLOCK_SIZE; vdev.config.blk.physical_block_exp = 0; + vdev.blk_factor = 1; + break; + case VIRTIO_ID_SCSI: + vdev.scsi_block_size = VIRTIO_SCSI_BLOCK_SIZE; break; } } void virtio_assume_iso9660(void) { - vdev.guessed_disk_nature = VIRTIO_GDN_CDROM; switch (vdev.senseid.cu_model) { case VIRTIO_ID_BLOCK: - vdev.config.blk.blk_size = 2048; + 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; } } @@ -347,16 +379,19 @@ void virtio_assume_iso9660(void) void virtio_assume_eckd(void) { 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 = 4096; - vdev.config.blk.physical_block_exp = 0; - - /* this must be here to calculate code segment position */ - vdev.config.blk.geometry.heads = 15; - vdev.config.blk.geometry.sectors = 12; + 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_disk_is_scsi(void) @@ -368,30 +403,13 @@ bool virtio_disk_is_scsi(void) case VIRTIO_ID_BLOCK: return (vdev.config.blk.geometry.heads == 255) && (vdev.config.blk.geometry.sectors == 63) - && (virtio_get_block_size() == 512); + && (virtio_get_block_size() == VIRTIO_SCSI_BLOCK_SIZE); + case VIRTIO_ID_SCSI: + return true; } return false; } -/* - * 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; -} - bool virtio_disk_is_eckd(void) { const int block_size = virtio_get_block_size(); @@ -404,6 +422,8 @@ bool virtio_disk_is_eckd(void) return (vdev.config.blk.geometry.heads == 15) && (vdev.config.blk.geometry.sectors == virtio_eckd_sectors_for_block_size(block_size)); + case VIRTIO_ID_SCSI: + return false; } return false; } @@ -418,6 +438,8 @@ int virtio_get_block_size(void) switch (vdev.senseid.cu_model) { case VIRTIO_ID_BLOCK: return vdev.config.blk.blk_size << vdev.config.blk.physical_block_exp; + case VIRTIO_ID_SCSI: + return vdev.scsi_block_size; } return 0; } @@ -427,6 +449,9 @@ uint8_t virtio_get_heads(void) 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; } @@ -436,25 +461,33 @@ uint8_t virtio_get_sectors(void) 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) { + 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 / - (virtio_get_block_size() / VIRTIO_SECTOR_SIZE); + return vdev.config.blk.capacity / factor; + case VIRTIO_ID_SCSI: + return vdev.scsi_last_block / factor; } return 0; } static void virtio_setup_ccw(VDev *vdev) { - int i, cfg_size; + int i, cfg_size = 0; unsigned char status = VIRTIO_CONFIG_S_DRIVER_OK; + IPL_assert(virtio_is_supported(vdev->schid), "PE"); + /* device ID has been established now */ + vdev->config.blk.blk_size = 0; /* mark "illegal" - setup started... */ vdev->guessed_disk_nature = VIRTIO_GDN_NONE; @@ -466,6 +499,11 @@ static void virtio_setup_ccw(VDev *vdev) vdev->cmd_vr_idx = 0; cfg_size = sizeof(vdev->config.blk); break; + case VIRTIO_ID_SCSI: + vdev->nr_vqs = 3; + vdev->cmd_vr_idx = VR_REQUEST; + cfg_size = sizeof(vdev->config.scsi); + break; default: panic("Unsupported virtio device\n"); } @@ -511,6 +549,7 @@ void virtio_setup_device(SubChannelId schid) switch (vdev.senseid.cu_model) { case VIRTIO_ID_BLOCK: + sclp_print("Using virtio-blk.\n"); if (!virtio_ipl_disk_is_valid()) { /* make sure all getters but blocksize return 0 for * invalid IPL disk @@ -519,6 +558,15 @@ void virtio_setup_device(SubChannelId schid) virtio_assume_scsi(); } break; + case VIRTIO_ID_SCSI: + IPL_assert(vdev.config.scsi.sense_size == VIRTIO_SCSI_SENSE_SIZE, + "Config: sense size mismatch"); + IPL_assert(vdev.config.scsi.cdb_size == VIRTIO_SCSI_CDB_SIZE, + "Config: CDB size mismatch"); + + sclp_print("Using virtio-scsi.\n"); + virtio_scsi_setup(&vdev); + break; default: panic("\n! No IPL device available !\n"); } @@ -535,6 +583,7 @@ bool virtio_is_supported(SubChannelId schid) if (vdev.senseid.cu_type == 0x3832) { switch (vdev.senseid.cu_model) { case VIRTIO_ID_BLOCK: + case VIRTIO_ID_SCSI: return true; } } diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index 57c71a2c93..3c6e91510e 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -28,6 +28,7 @@ enum VirtioDevType { VIRTIO_ID_BLOCK = 2, VIRTIO_ID_CONSOLE = 3, VIRTIO_ID_BALLOON = 5, + VIRTIO_ID_SCSI = 8, }; typedef enum VirtioDevType VirtioDevType; @@ -224,12 +225,35 @@ extern uint64_t virtio_get_blocks(void); extern int virtio_read_many(ulong sector, void *load_addr, int sec_num); #define VIRTIO_SECTOR_SIZE 512 +#define VIRTIO_ISO_BLOCK_SIZE 2048 +#define VIRTIO_SCSI_BLOCK_SIZE 512 static inline ulong virtio_sector_adjust(ulong sector) { return sector * (virtio_get_block_size() / VIRTIO_SECTOR_SIZE); } +struct VirtioScsiConfig { + uint32_t num_queues; + uint32_t seg_max; + uint32_t max_sectors; + uint32_t cmd_per_lun; + uint32_t event_info_size; + uint32_t sense_size; + uint32_t cdb_size; + uint16_t max_channel; + uint16_t max_target; + uint32_t max_lun; +} __attribute__((packed)); +typedef struct VirtioScsiConfig VirtioScsiConfig; + +struct ScsiDevice { + uint16_t channel; /* Always 0 in QEMU */ + uint16_t target; /* will be scanned over */ + uint32_t lun; /* will be reported */ +}; +typedef struct ScsiDevice ScsiDevice; + struct VDev { int nr_vqs; VRing *vrings; @@ -241,7 +265,15 @@ struct VDev { SenseId senseid; union { VirtioBlkConfig blk; + VirtioScsiConfig scsi; } config; + ScsiDevice *scsi_device; + bool is_cdrom; + int scsi_block_size; + int blk_factor; + uint64_t scsi_last_block; + uint32_t scsi_dev_cyls; + uint8_t scsi_dev_heads; }; typedef struct VDev VDev; |