diff options
Diffstat (limited to 'pc-bios')
-rw-r--r-- | pc-bios/s390-ccw.img | bin | 26472 -> 26480 bytes | |||
-rw-r--r-- | pc-bios/s390-ccw/s390-ccw.h | 7 | ||||
-rw-r--r-- | pc-bios/s390-ccw/scsi.h | 30 | ||||
-rw-r--r-- | pc-bios/s390-ccw/virtio-scsi.c | 85 | ||||
-rw-r--r-- | pc-bios/s390-ccw/virtio-scsi.h | 2 | ||||
-rw-r--r-- | pc-bios/s390-ccw/virtio.h | 1 |
6 files changed, 114 insertions, 11 deletions
diff --git a/pc-bios/s390-ccw.img b/pc-bios/s390-ccw.img Binary files differindex 0b01d49495..5ad0564000 100644 --- a/pc-bios/s390-ccw.img +++ b/pc-bios/s390-ccw.img diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index 07d8cbcb20..2089274842 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -42,6 +42,13 @@ typedef unsigned long long __u64; #ifndef NULL #define NULL 0 #endif +#ifndef MIN +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef MIN_NON_ZERO +#define MIN_NON_ZERO(a, b) ((a) == 0 ? (b) : \ + ((b) == 0 ? (a) : (MIN(a, b)))) +#endif #include "cio.h" #include "iplb.h" diff --git a/pc-bios/s390-ccw/scsi.h b/pc-bios/s390-ccw/scsi.h index fc830f7e52..fe3fd5ac05 100644 --- a/pc-bios/s390-ccw/scsi.h +++ b/pc-bios/s390-ccw/scsi.h @@ -26,6 +26,15 @@ #define SCSI_SENSE_KEY_NO_SENSE 0 #define SCSI_SENSE_KEY_UNIT_ATTENTION 6 +/* SCSI Inquiry Types */ +#define SCSI_INQUIRY_STANDARD 0x00U +#define SCSI_INQUIRY_EVPD 0x01U + +/* SCSI Inquiry Pages */ +#define SCSI_INQUIRY_STANDARD_NONE 0x00U +#define SCSI_INQUIRY_EVPD_SUPPORTED_PAGES 0x00U +#define SCSI_INQUIRY_EVPD_BLOCK_LIMITS 0xb0U + union ScsiLun { uint64_t v64; /* numeric shortcut */ uint8_t v8[8]; /* generic 8 bytes representation */ @@ -71,6 +80,27 @@ struct ScsiInquiryStd { } __attribute__((packed)); typedef struct ScsiInquiryStd ScsiInquiryStd; +struct ScsiInquiryEvpdPages { + uint8_t peripheral_qdt; /* b0, use (b0 & 0x1f) to get SCSI_INQ_RDT */ + uint8_t page_code; /* b1 */ + uint16_t page_length; /* b2..b3 length = N-3 */ + uint8_t byte[28]; /* b4..bN Supported EVPD pages (N=31 here) */ +} __attribute__((packed)); +typedef struct ScsiInquiryEvpdPages ScsiInquiryEvpdPages; + +struct ScsiInquiryEvpdBl { + uint8_t peripheral_qdt; /* b0, use (b0 & 0x1f) to get SCSI_INQ_RDT */ + uint8_t page_code; + uint16_t page_length; + uint8_t b4; + uint8_t b5; + uint16_t b6; + uint32_t max_transfer; /* b8 */ + uint32_t b12[7]; /* b12..b43 (defined fields) */ + uint32_t b44[5]; /* b44..b63 (reserved fields) */ +} __attribute__((packed)); +typedef struct ScsiInquiryEvpdBl ScsiInquiryEvpdBl; + struct ScsiCdbInquiry { uint8_t command; /* b0, == 0x12 */ uint8_t b1; /* b1, |= 0x01 (evpd) */ diff --git a/pc-bios/s390-ccw/virtio-scsi.c b/pc-bios/s390-ccw/virtio-scsi.c index d850a8deed..f61ecf0205 100644 --- a/pc-bios/s390-ccw/virtio-scsi.c +++ b/pc-bios/s390-ccw/virtio-scsi.c @@ -19,6 +19,8 @@ static VirtioScsiCmdReq req; static VirtioScsiCmdResp resp; static uint8_t scsi_inquiry_std_response[256]; +static ScsiInquiryEvpdPages scsi_inquiry_evpd_pages_response; +static ScsiInquiryEvpdBl scsi_inquiry_evpd_bl_response; static inline void vs_assert(bool term, const char **msgs) { @@ -89,10 +91,13 @@ static void vs_run(const char *title, VirtioCmd *cmd, VDev *vdev, /* SCSI protocol implementation routines */ -static bool scsi_inquiry(VDev *vdev, void *data, uint32_t data_size) +static bool scsi_inquiry(VDev *vdev, uint8_t evpd, uint8_t page, + void *data, uint32_t data_size) { ScsiCdbInquiry cdb = { .command = 0x12, + .b1 = evpd, + .b2 = page, .alloc_len = data_size < 65535 ? data_size : 65535, }; VirtioCmd inquiry[] = { @@ -142,19 +147,18 @@ static bool scsi_report_luns(VDev *vdev, void *data, uint32_t data_size) } static bool scsi_read_10(VDev *vdev, - ulong sector, int sectors, void *data) + ulong sector, int sectors, void *data, + unsigned int data_size) { - int f = vdev->blk_factor; - unsigned int data_size = sectors * virtio_get_block_size() * f; ScsiCdbRead10 cdb = { .command = 0x28, - .lba = sector * f, - .xfer_length = sectors * f, + .lba = sector, + .xfer_length = sectors, }; VirtioCmd read_10[] = { { &req, sizeof(req), VRING_DESC_F_NEXT }, { &resp, sizeof(resp), VRING_DESC_F_WRITE | VRING_DESC_F_NEXT }, - { data, data_size * f, VRING_DESC_F_WRITE }, + { data, data_size, VRING_DESC_F_WRITE }, }; debug_print_int("read_10 sector", sector); @@ -203,6 +207,7 @@ static void virtio_scsi_locate_device(VDev *vdev) debug_print_int("config.scsi.max_channel", vdev->config.scsi.max_channel); debug_print_int("config.scsi.max_target ", vdev->config.scsi.max_target); debug_print_int("config.scsi.max_lun ", vdev->config.scsi.max_lun); + debug_print_int("config.scsi.max_sectors", vdev->config.scsi.max_sectors); if (vdev->scsi_device_selected) { sdev->channel = vdev->selected_scsi_device.channel; @@ -255,9 +260,23 @@ static void virtio_scsi_locate_device(VDev *vdev) int virtio_scsi_read_many(VDev *vdev, ulong sector, void *load_addr, int sec_num) { - if (!scsi_read_10(vdev, sector, sec_num, load_addr)) { - virtio_scsi_verify_response(&resp, "virtio-scsi:read_many"); - } + int sector_count; + int f = vdev->blk_factor; + unsigned int data_size; + unsigned int max_transfer = MIN_NON_ZERO(vdev->config.scsi.max_sectors, + vdev->max_transfer); + + do { + sector_count = MIN_NON_ZERO(sec_num, max_transfer); + data_size = sector_count * virtio_get_block_size() * f; + if (!scsi_read_10(vdev, sector * f, sector_count * f, load_addr, + data_size)) { + virtio_scsi_verify_response(&resp, "virtio-scsi:read_many"); + } + load_addr += data_size; + sector += sector_count; + sec_num -= sector_count; + } while (sec_num > 0); return 0; } @@ -304,6 +323,9 @@ void virtio_scsi_setup(VDev *vdev) int retry_test_unit_ready = 3; uint8_t data[256]; uint32_t data_size = sizeof(data); + ScsiInquiryEvpdPages *evpd = &scsi_inquiry_evpd_pages_response; + ScsiInquiryEvpdBl *evpd_bl = &scsi_inquiry_evpd_bl_response; + int i; vdev->scsi_device = &default_scsi_device; virtio_scsi_locate_device(vdev); @@ -334,7 +356,10 @@ void virtio_scsi_setup(VDev *vdev) } /* read and cache SCSI INQUIRY response */ - if (!scsi_inquiry(vdev, scsi_inquiry_std_response, + if (!scsi_inquiry(vdev, + SCSI_INQUIRY_STANDARD, + SCSI_INQUIRY_STANDARD_NONE, + scsi_inquiry_std_response, sizeof(scsi_inquiry_std_response))) { virtio_scsi_verify_response(&resp, "virtio-scsi:setup:inquiry"); } @@ -345,6 +370,44 @@ void virtio_scsi_setup(VDev *vdev) vdev->scsi_block_size = VIRTIO_ISO_BLOCK_SIZE; } + if (!scsi_inquiry(vdev, + SCSI_INQUIRY_EVPD, + SCSI_INQUIRY_EVPD_SUPPORTED_PAGES, + evpd, + sizeof(*evpd))) { + virtio_scsi_verify_response(&resp, "virtio-scsi:setup:supported_pages"); + } + + debug_print_int("EVPD length", evpd->page_length); + + for (i = 0; i <= evpd->page_length; i++) { + debug_print_int("supported EVPD page", evpd->byte[i]); + + if (evpd->byte[i] != SCSI_INQUIRY_EVPD_BLOCK_LIMITS) { + continue; + } + + if (!scsi_inquiry(vdev, + SCSI_INQUIRY_EVPD, + SCSI_INQUIRY_EVPD_BLOCK_LIMITS, + evpd_bl, + sizeof(*evpd_bl))) { + virtio_scsi_verify_response(&resp, "virtio-scsi:setup:blocklimits"); + } + + debug_print_int("max transfer", evpd_bl->max_transfer); + vdev->max_transfer = evpd_bl->max_transfer; + } + + /* + * The host sg driver will often be unhappy with particularly large + * I/Os that exceed the block iovec limits. Let's enforce something + * reasonable, despite what the device configuration tells us. + */ + + vdev->max_transfer = MIN_NON_ZERO(VIRTIO_SCSI_MAX_SECTORS, + vdev->max_transfer); + if (!scsi_read_capacity(vdev, data, data_size)) { virtio_scsi_verify_response(&resp, "virtio-scsi:setup:read_capacity"); } diff --git a/pc-bios/s390-ccw/virtio-scsi.h b/pc-bios/s390-ccw/virtio-scsi.h index f50b38b18b..4c4f4bbc31 100644 --- a/pc-bios/s390-ccw/virtio-scsi.h +++ b/pc-bios/s390-ccw/virtio-scsi.h @@ -19,6 +19,8 @@ #define VIRTIO_SCSI_CDB_SIZE SCSI_DEFAULT_CDB_SIZE #define VIRTIO_SCSI_SENSE_SIZE SCSI_DEFAULT_SENSE_SIZE +#define VIRTIO_SCSI_MAX_SECTORS 2048 + /* command-specific response values */ #define VIRTIO_SCSI_S_OK 0x00 #define VIRTIO_SCSI_S_BAD_TARGET 0x03 diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index 3388a423e5..1eaf865b1f 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -277,6 +277,7 @@ struct VDev { bool scsi_device_selected; ScsiDevice selected_scsi_device; uint64_t netboot_start_addr; + uint32_t max_transfer; }; typedef struct VDev VDev; |