mirror of https://github.com/xemu-project/xemu.git
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 <borntraeger@de.ibm.com> Signed-off-by: Eugene (jno) Dvurechenski <jno@linux.vnet.ibm.com> Signed-off-by: Jens Freimann <jfrei@linux.vnet.ibm.com> Signed-off-by: Cornelia Huck <cornelia.huck@de.ibm.com>
This commit is contained in:
parent
26f2bbd6b1
commit
91a03f9b69
|
@ -10,6 +10,7 @@
|
||||||
|
|
||||||
#include "s390-ccw.h"
|
#include "s390-ccw.h"
|
||||||
#include "bootmap.h"
|
#include "bootmap.h"
|
||||||
|
#include "virtio.h"
|
||||||
|
|
||||||
/* #define DEBUG_FALLBACK */
|
/* #define DEBUG_FALLBACK */
|
||||||
|
|
||||||
|
@ -22,7 +23,8 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Scratch space */
|
/* 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 {
|
typedef struct ResetInfo {
|
||||||
uint32_t ipl_mask;
|
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)
|
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;
|
ScsiBlockPtr *bprs = (void *)sec;
|
||||||
const int bprs_size = sizeof(sec);
|
const int bprs_size = sizeof(sec);
|
||||||
uint64_t blockno;
|
uint64_t blockno;
|
||||||
|
@ -163,7 +165,7 @@ static int zipl_run(ScsiBlockPtr *pte)
|
||||||
{
|
{
|
||||||
ComponentHeader *header;
|
ComponentHeader *header;
|
||||||
ComponentEntry *entry;
|
ComponentEntry *entry;
|
||||||
uint8_t tmp_sec[SECTOR_SIZE];
|
uint8_t tmp_sec[MAX_SECTOR_SIZE];
|
||||||
|
|
||||||
virtio_read(pte->blockno, tmp_sec);
|
virtio_read(pte->blockno, tmp_sec);
|
||||||
header = (ComponentHeader *)tmp_sec;
|
header = (ComponentHeader *)tmp_sec;
|
||||||
|
@ -187,7 +189,7 @@ static int zipl_run(ScsiBlockPtr *pte)
|
||||||
|
|
||||||
entry++;
|
entry++;
|
||||||
|
|
||||||
if ((uint8_t *)(&entry[1]) > (tmp_sec + SECTOR_SIZE)) {
|
if ((uint8_t *)(&entry[1]) > (tmp_sec + MAX_SECTOR_SIZE)) {
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -238,7 +240,7 @@ int zipl_load(void)
|
||||||
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++) {
|
for (ns = (sec + pte_len); (ns + pte_len) < ns_end; ns++) {
|
||||||
prog_table_entry = (ScsiBlockPtr *)ns;
|
prog_table_entry = (ScsiBlockPtr *)ns;
|
||||||
if (!prog_table_entry->blockno) {
|
if (!prog_table_entry->blockno) {
|
||||||
|
|
|
@ -130,6 +130,6 @@ static inline void yield(void)
|
||||||
: "memory", "cc");
|
: "memory", "cc");
|
||||||
}
|
}
|
||||||
|
|
||||||
#define SECTOR_SIZE 512
|
#define MAX_SECTOR_SIZE 4096
|
||||||
|
|
||||||
#endif /* S390_CCW_H */
|
#endif /* S390_CCW_H */
|
||||||
|
|
|
@ -202,7 +202,7 @@ static int vring_wait_reply(struct vring *vr, int timeout)
|
||||||
* Virtio block *
|
* 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;
|
struct virtio_blk_outhdr out_hdr;
|
||||||
u8 status;
|
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 */
|
/* Tell the host we want to read */
|
||||||
out_hdr.type = VIRTIO_BLK_T_IN;
|
out_hdr.type = VIRTIO_BLK_T_IN;
|
||||||
out_hdr.ioprio = 99;
|
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);
|
vring_send_buf(&block, &out_hdr, sizeof(out_hdr), VRING_DESC_F_NEXT);
|
||||||
|
|
||||||
/* This is where we want to receive data */
|
/* 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_WRITE | VRING_HIDDEN_IS_CHAIN |
|
||||||
VRING_DESC_F_NEXT);
|
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;
|
int sec_len = rec_list2 >> 48;
|
||||||
ulong addr = (ulong)load_addr;
|
ulong addr = (ulong)load_addr;
|
||||||
|
|
||||||
if (sec_len != SECTOR_SIZE) {
|
if (sec_len != virtio_get_block_size()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -253,7 +253,7 @@ unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
|
||||||
if (status) {
|
if (status) {
|
||||||
virtio_panic("I/O Error");
|
virtio_panic("I/O Error");
|
||||||
}
|
}
|
||||||
addr += sec_num * SECTOR_SIZE;
|
addr += sec_num * virtio_get_block_size();
|
||||||
|
|
||||||
return addr;
|
return addr;
|
||||||
}
|
}
|
||||||
|
@ -263,15 +263,95 @@ int virtio_read(ulong sector, void *load_addr)
|
||||||
return virtio_read_many(sector, load_addr, 1);
|
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)
|
void virtio_setup_block(struct subchannel_id schid)
|
||||||
{
|
{
|
||||||
struct vq_info_block info;
|
struct vq_info_block info;
|
||||||
struct vq_config_block config = {};
|
struct vq_config_block config = {};
|
||||||
|
|
||||||
|
blk_cfg.blk_size = 0; /* mark "illegal" - setup started... */
|
||||||
|
|
||||||
virtio_reset(schid);
|
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;
|
config.index = 0;
|
||||||
if (run_ccw(schid, CCW_CMD_READ_VQ_CONF, &config, sizeof(config))) {
|
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");
|
virtio_panic("Could not get block device configuration\n");
|
||||||
}
|
}
|
||||||
vring_init(&block, config.num, (void *)(100 * 1024 * 1024),
|
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))) {
|
if (!run_ccw(schid, CCW_CMD_SET_VQ, &info, sizeof(info))) {
|
||||||
virtio_set_status(schid, VIRTIO_CONFIG_S_DRIVER_OK);
|
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)
|
bool virtio_is_blk(struct subchannel_id schid)
|
||||||
|
|
|
@ -161,4 +161,52 @@ struct virtio_blk_outhdr {
|
||||||
u64 sector;
|
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 */
|
#endif /* VIRTIO_H */
|
||||||
|
|
Loading…
Reference in New Issue