From 91a03f9b6953cca0e8c05402e141a576b01063e2 Mon Sep 17 00:00:00 2001 From: "Eugene (jno) Dvurechenski" Date: Mon, 19 May 2014 20:10:27 +0200 Subject: pc-bios/s390-ccw: handle different sector sizes Use the virtio device's configuration to figure out the disk geometry and use a sector size based upon the layout. [CH: s/SECTOR_SIZE/MAX_SECTOR_SIZE/g] Acked-by: Christian Borntraeger Signed-off-by: Eugene (jno) Dvurechenski Signed-off-by: Jens Freimann Signed-off-by: Cornelia Huck --- pc-bios/s390-ccw/bootmap.c | 12 +++--- pc-bios/s390-ccw/s390-ccw.h | 2 +- pc-bios/s390-ccw/virtio.c | 96 ++++++++++++++++++++++++++++++++++++++++++--- pc-bios/s390-ccw/virtio.h | 48 +++++++++++++++++++++++ 4 files changed, 147 insertions(+), 11 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index c216030..fa2ca26 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -10,6 +10,7 @@ #include "s390-ccw.h" #include "bootmap.h" +#include "virtio.h" /* #define DEBUG_FALLBACK */ @@ -22,7 +23,8 @@ #endif /* Scratch space */ -static uint8_t sec[SECTOR_SIZE] __attribute__((__aligned__(SECTOR_SIZE))); +static uint8_t sec[MAX_SECTOR_SIZE] +__attribute__((__aligned__(MAX_SECTOR_SIZE))); typedef struct ResetInfo { uint32_t ipl_mask; @@ -99,7 +101,7 @@ static inline bool unused_space(const void *p, unsigned int size) static int zipl_load_segment(ComponentEntry *entry) { - const int max_entries = (SECTOR_SIZE / sizeof(ScsiBlockPtr)); + const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr)); ScsiBlockPtr *bprs = (void *)sec; const int bprs_size = sizeof(sec); uint64_t blockno; @@ -163,7 +165,7 @@ static int zipl_run(ScsiBlockPtr *pte) { ComponentHeader *header; ComponentEntry *entry; - uint8_t tmp_sec[SECTOR_SIZE]; + uint8_t tmp_sec[MAX_SECTOR_SIZE]; virtio_read(pte->blockno, tmp_sec); header = (ComponentHeader *)tmp_sec; @@ -187,7 +189,7 @@ static int zipl_run(ScsiBlockPtr *pte) entry++; - if ((uint8_t *)(&entry[1]) > (tmp_sec + SECTOR_SIZE)) { + if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) { goto fail; } } @@ -238,7 +240,7 @@ int zipl_load(void) goto fail; } - ns_end = sec + SECTOR_SIZE; + ns_end = sec + virtio_get_block_size(); for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns++) { prog_table_entry = (ScsiBlockPtr *)ns; if (!prog_table_entry->blockno) { diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h index fe1dd22..b6c0a5b 100644 --- a/pc-bios/s390-ccw/s390-ccw.h +++ b/pc-bios/s390-ccw/s390-ccw.h @@ -130,6 +130,6 @@ static inline void yield(void) : "memory", "cc"); } -#define SECTOR_SIZE 512 +#define MAX_SECTOR_SIZE 4096 #endif /* S390_CCW_H */ diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c index c845b14..31b23b0 100644 --- a/pc-bios/s390-ccw/virtio.c +++ b/pc-bios/s390-ccw/virtio.c @@ -202,7 +202,7 @@ static int vring_wait_reply(struct vring *vr, int timeout) * Virtio block * ***********************************************/ -static int virtio_read_many(ulong sector, void *load_addr, int sec_num) +int virtio_read_many(ulong sector, void *load_addr, int sec_num) { struct virtio_blk_outhdr out_hdr; u8 status; @@ -211,12 +211,12 @@ static int virtio_read_many(ulong sector, void *load_addr, int sec_num) /* Tell the host we want to read */ out_hdr.type = VIRTIO_BLK_T_IN; out_hdr.ioprio = 99; - out_hdr.sector = sector; + out_hdr.sector = virtio_sector_adjust(sector); vring_send_buf(&block, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT); /* This is where we want to receive data */ - vring_send_buf(&block, load_addr, SECTOR_SIZE * sec_num, + vring_send_buf(&block, load_addr, virtio_get_block_size() * sec_num, VRING_DESC_F_WRITE | VRING_HIDDEN_IS_CHAIN | VRING_DESC_F_NEXT); @@ -244,7 +244,7 @@ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, int sec_len = rec_list2 >> 48; ulong addr = (ulong)load_addr; - if (sec_len != SECTOR_SIZE) { + if (sec_len != virtio_get_block_size()) { return -1; } @@ -253,7 +253,7 @@ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2, if (status) { virtio_panic("I/O Error"); } - addr += sec_num * SECTOR_SIZE; + addr += sec_num * virtio_get_block_size(); return addr; } @@ -263,15 +263,95 @@ int virtio_read(ulong sector, void *load_addr) return virtio_read_many(sector, load_addr, 1); } +static VirtioBlkConfig blk_cfg = {}; +static bool guessed_disk_nature; + +bool virtio_guessed_disk_nature(void) +{ + return guessed_disk_nature; +} + +void virtio_assume_scsi(void) +{ + guessed_disk_nature = true; + blk_cfg.blk_size = 512; +} + +void virtio_assume_eckd(void) +{ + guessed_disk_nature = true; + blk_cfg.blk_size = 4096; + + /* this must be here to calculate code segment position */ + blk_cfg.geometry.heads = 15; + blk_cfg.geometry.sectors = 12; +} + +bool virtio_disk_is_scsi(void) +{ + if (guessed_disk_nature) { + return (blk_cfg.blk_size == 512); + } + return (blk_cfg.geometry.heads == 255) + && (blk_cfg.geometry.sectors == 63) + && (blk_cfg.blk_size == 512); +} + +bool virtio_disk_is_eckd(void) +{ + if (guessed_disk_nature) { + return (blk_cfg.blk_size == 4096); + } + return (blk_cfg.geometry.heads == 15) + && (blk_cfg.geometry.sectors == 12) + && (blk_cfg.blk_size == 4096); +} + +bool virtio_ipl_disk_is_valid(void) +{ + return blk_cfg.blk_size && (virtio_disk_is_scsi() || virtio_disk_is_eckd()); +} + +int virtio_get_block_size(void) +{ + return blk_cfg.blk_size; +} + +uint16_t virtio_get_cylinders(void) +{ + return blk_cfg.geometry.cylinders; +} + +uint8_t virtio_get_heads(void) +{ + return blk_cfg.geometry.heads; +} + +uint8_t virtio_get_sectors(void) +{ + return blk_cfg.geometry.sectors; +} + void virtio_setup_block(struct subchannel_id schid) { struct vq_info_block info; struct vq_config_block config = {}; + blk_cfg.blk_size = 0; /* mark "illegal" - setup started... */ + virtio_reset(schid); + /* + * Skipping CCW_CMD_READ_FEAT. We're not doing anything fancy, and + * we'll just stop dead anyway if anything does not work like we + * expect it. + */ + config.index = 0; if (run_ccw(schid, CCW_CMD_READ_VQ_CONF, &config, sizeof(config))) { + virtio_panic("Could not get block device VQ configuration\n"); + } + if (run_ccw(schid, CCW_CMD_READ_CONF, &blk_cfg, sizeof(blk_cfg))) { virtio_panic("Could not get block device configuration\n"); } vring_init(&block, config.num, (void *)(100 * 1024 * 1024), @@ -286,6 +366,12 @@ void virtio_setup_block(struct subchannel_id schid) if (!run_ccw(schid, CCW_CMD_SET_VQ, &info, sizeof(info))) { virtio_set_status(schid, VIRTIO_CONFIG_S_DRIVER_OK); } + + if (!virtio_ipl_disk_is_valid()) { + /* make sure all getters but blocksize return 0 for invalid IPL disk */ + memset(&blk_cfg, 0, sizeof(blk_cfg)); + virtio_assume_scsi(); + } } bool virtio_is_blk(struct subchannel_id schid) diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h index c2990ea..f1fb1b0 100644 --- a/pc-bios/s390-ccw/virtio.h +++ b/pc-bios/s390-ccw/virtio.h @@ -161,4 +161,52 @@ struct virtio_blk_outhdr { u64 sector; }; +typedef struct VirtioBlkConfig { + u64 capacity; /* in 512-byte sectors */ + u32 size_max; /* max segment size (if VIRTIO_BLK_F_SIZE_MAX) */ + u32 seg_max; /* max number of segments (if VIRTIO_BLK_F_SEG_MAX) */ + + struct virtio_blk_geometry { + u16 cylinders; + u8 heads; + u8 sectors; + } geometry; /* (if VIRTIO_BLK_F_GEOMETRY) */ + + u32 blk_size; /* block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ + + /* the next 4 entries are guarded by VIRTIO_BLK_F_TOPOLOGY */ + u8 physical_block_exp; /* exponent for physical block per logical block */ + u8 alignment_offset; /* alignment offset in logical blocks */ + u16 min_io_size; /* min I/O size without performance penalty + in logical blocks */ + u32 opt_io_size; /* optimal sustained I/O size in logical blocks */ + + u8 wce; /* writeback mode (if VIRTIO_BLK_F_CONFIG_WCE) */ +} __attribute__((packed)) VirtioBlkConfig; + +bool virtio_guessed_disk_nature(void); +void virtio_assume_scsi(void); +void virtio_assume_eckd(void); + +extern bool virtio_disk_is_scsi(void); +extern bool virtio_disk_is_eckd(void); +extern bool virtio_ipl_disk_is_valid(void); +extern int virtio_get_block_size(void); +extern uint16_t virtio_get_cylinders(void); +extern uint8_t virtio_get_heads(void); +extern uint8_t virtio_get_sectors(void); +extern int virtio_read_many(ulong sector, void *load_addr, int sec_num); + +#define VIRTIO_SECTOR_SIZE 512 + +static inline ulong virtio_eckd_sector_adjust(ulong sector) +{ + return sector * (virtio_get_block_size() / VIRTIO_SECTOR_SIZE); +} + +static inline ulong virtio_sector_adjust(ulong sector) +{ + return virtio_disk_is_eckd() ? virtio_eckd_sector_adjust(sector) : sector; +} + #endif /* VIRTIO_H */ -- cgit v1.1