#include "s390-ccw.h"
#include "bootmap.h"
+#include "virtio.h"
/* #define DEBUG_FALLBACK */
#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;
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;
{
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;
entry++;
- if ((uint8_t *)(&entry[1]) > (tmp_sec + SECTOR_SIZE)) {
+ if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
goto fail;
}
}
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) {
* 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;
/* 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);
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;
}
if (status) {
virtio_panic("I/O Error");
}
- addr += sec_num * SECTOR_SIZE;
+ addr += sec_num * virtio_get_block_size();
return 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),
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)
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 */