A series of patches to the s390-ccw bios:
- code cleanup - improved error reporting - most important, support to ipl (boot) from ECKD DASD (CDL, LDL or CMS formatted) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJTrU+CAAoJEN7Pa5PG8C+vKp8P/22BKb0ga75s1hbzmMcSETTU EgyHefOtYx3pjaBOo5k5j83KUblPaoPC7KCr5ZZWsvCvtgRFddqA6L5nS9QNhzSz XepwFC1UaNB2DNDWdqOibOguILSoD6d2SDNqnV4obGyJ2TPnMjZfw6jmHmP7Mxbf +P8sUhbdZ4Esp/DQ5dQxfxcgfaf5KZXrl8mTVhfSRacvQxqOWM8+aor+rfX7F0pC eKJtWgHT98RUQSZyt0cwyd4/0yzChlZRElc+GQYbpdToSlYzDwM8AyLFPRzLJsaB 06EKw0KlvAz55unMfwW0eEX3NrIOxwV2STaTOIKpaiTt48wymC/pH8fyfRCIywT2 s8+K5nivebTir1D3Vis9xYMif/4NOjIif0cPFbfqh5E5ViSn1I4wjp18Cutd9dXB AztbRM95g1wzGCWs1XNfXNxPyzDD/1jQM0hW1anj43sEEQoqV2is15AG7RUqSkS3 mBHVoh5P//SUCf+KPaEnryXlHfNgLcdSMEfC3KQUs8L96cMoTkLFbVHbx+eLb0g3 hZfTsB54J+HoiRcCUnG3m+iU4hOAjXT5tjNfuA4HH51W3nQ8kZt9SQbmtZujt5jO WaL330Xd/5qgDn9fe6O3BnPOP0u1AIth6DB0fu2wN9Cy5sGS0jm9c6rVcbkV7ESx IL3z3X+Rr5WRyp7b3tqU =hh2q -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/cohuck/tags/s390x-20140627' into staging A series of patches to the s390-ccw bios: - code cleanup - improved error reporting - most important, support to ipl (boot) from ECKD DASD (CDL, LDL or CMS formatted) # gpg: Signature made Fri 27 Jun 2014 12:03:30 BST using RSA key ID C6F02FAF # gpg: Can't check signature: public key not found * remotes/cohuck/tags/s390x-20140627: pc-bios/s390-ccw: update binary pc-bios/s390-ccw: IPL from LDL/CMS-formatted ECKD DASD pc-bios/s390-ccw: IPL from CDL-formatted ECKD DASD pc-bios/s390-ccw: factor out ipl code pc-bios/s390-ccw: Add fill_hex_val func to provide better msgs pc-bios/s390-ccw: Unify error handling pc-bios/s390-ccw: add some utility code pc-bios/s390-ccw: handle different sector sizes pc-bios/s390-ccw: cleanup and enhance bootmap defintions pc-bios/s390-ccw: make checkpatch happy Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
de6793e8c2
Binary file not shown.
@ -9,8 +9,12 @@
|
||||
*/
|
||||
|
||||
#include "s390-ccw.h"
|
||||
#include "bootmap.h"
|
||||
#include "virtio.h"
|
||||
|
||||
// #define DEBUG_FALLBACK
|
||||
#ifdef DEBUG
|
||||
/* #define DEBUG_FALLBACK */
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_FALLBACK
|
||||
#define dputs(txt) \
|
||||
@ -20,43 +24,8 @@
|
||||
do { } while (0)
|
||||
#endif
|
||||
|
||||
struct scsi_blockptr {
|
||||
uint64_t blockno;
|
||||
uint16_t size;
|
||||
uint16_t blockct;
|
||||
uint8_t reserved[4];
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct component_entry {
|
||||
struct scsi_blockptr data;
|
||||
uint8_t pad[7];
|
||||
uint8_t component_type;
|
||||
uint64_t load_address;
|
||||
} __attribute((packed));
|
||||
|
||||
struct component_header {
|
||||
uint8_t magic[4];
|
||||
uint8_t type;
|
||||
uint8_t reserved[27];
|
||||
} __attribute((packed));
|
||||
|
||||
struct mbr {
|
||||
uint8_t magic[4];
|
||||
uint32_t version_id;
|
||||
uint8_t reserved[8];
|
||||
struct scsi_blockptr blockptr;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define ZIPL_MAGIC "zIPL"
|
||||
|
||||
#define ZIPL_COMP_HEADER_IPL 0x00
|
||||
#define ZIPL_COMP_HEADER_DUMP 0x01
|
||||
|
||||
#define ZIPL_COMP_ENTRY_LOAD 0x02
|
||||
#define ZIPL_COMP_ENTRY_EXEC 0x01
|
||||
|
||||
/* Scratch space */
|
||||
static uint8_t sec[SECTOR_SIZE] __attribute__((__aligned__(SECTOR_SIZE)));
|
||||
static uint8_t sec[MAX_SECTOR_SIZE*4] __attribute__((__aligned__(PAGE_SIZE)));
|
||||
|
||||
typedef struct ResetInfo {
|
||||
uint32_t ipl_mask;
|
||||
@ -104,43 +73,243 @@ static void jump_to_IPL_code(uint64_t address)
|
||||
virtio_panic("\n! IPL returns !\n");
|
||||
}
|
||||
|
||||
/* Check for ZIPL magic. Returns 0 if not matched. */
|
||||
static int zipl_magic(uint8_t *ptr)
|
||||
/***********************************************************************
|
||||
* IPL an ECKD DASD (CDL or LDL/CMS format)
|
||||
*/
|
||||
|
||||
static unsigned char _bprs[8*1024]; /* guessed "max" ECKD sector size */
|
||||
const int max_bprs_entries = sizeof(_bprs) / sizeof(ExtEckdBlockPtr);
|
||||
|
||||
static inline void verify_boot_info(BootInfo *bip)
|
||||
{
|
||||
uint32_t *p = (void*)ptr;
|
||||
uint32_t *z = (void*)ZIPL_MAGIC;
|
||||
|
||||
if (*p != *z) {
|
||||
debug_print_int("invalid magic", *p);
|
||||
virtio_panic("invalid magic");
|
||||
}
|
||||
|
||||
return 1;
|
||||
IPL_assert(magic_match(bip->magic, ZIPL_MAGIC), "No zIPL magic");
|
||||
IPL_assert(bip->version == BOOT_INFO_VERSION, "Wrong zIPL version");
|
||||
IPL_assert(bip->bp_type == BOOT_INFO_BP_TYPE_IPL, "DASD is not for IPL");
|
||||
IPL_assert(bip->dev_type == BOOT_INFO_DEV_TYPE_ECKD, "DASD is not ECKD");
|
||||
IPL_assert(bip->flags == BOOT_INFO_FLAGS_ARCH, "Not for this arch");
|
||||
IPL_assert(block_size_ok(bip->bp.ipl.bm_ptr.eckd.bptr.size),
|
||||
"Bad block size in zIPL section of the 1st record.");
|
||||
}
|
||||
|
||||
#define FREE_SPACE_FILLER '\xAA'
|
||||
|
||||
static inline bool unused_space(const void *p, unsigned int size)
|
||||
static bool eckd_valid_address(BootMapPointer *p)
|
||||
{
|
||||
int i;
|
||||
const unsigned char *m = p;
|
||||
const uint64_t cylinder = p->eckd.cylinder
|
||||
+ ((p->eckd.head & 0xfff0) << 12);
|
||||
const uint64_t head = p->eckd.head & 0x000f;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (m[i] != FREE_SPACE_FILLER) {
|
||||
return false;
|
||||
}
|
||||
if (head >= virtio_get_heads()
|
||||
|| p->eckd.sector > virtio_get_sectors()
|
||||
|| p->eckd.sector <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!virtio_guessed_disk_nature() && cylinder >= virtio_get_cylinders()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int zipl_load_segment(struct component_entry *entry)
|
||||
static block_number_t eckd_block_num(BootMapPointer *p)
|
||||
{
|
||||
const uint64_t sectors = virtio_get_sectors();
|
||||
const uint64_t heads = virtio_get_heads();
|
||||
const uint64_t cylinder = p->eckd.cylinder
|
||||
+ ((p->eckd.head & 0xfff0) << 12);
|
||||
const uint64_t head = p->eckd.head & 0x000f;
|
||||
const block_number_t block = sectors * heads * cylinder
|
||||
+ sectors * head
|
||||
+ p->eckd.sector
|
||||
- 1; /* block nr starts with zero */
|
||||
return block;
|
||||
}
|
||||
|
||||
static block_number_t load_eckd_segments(block_number_t blk, uint64_t *address)
|
||||
{
|
||||
block_number_t block_nr;
|
||||
int j, rc;
|
||||
BootMapPointer *bprs = (void *)_bprs;
|
||||
bool more_data;
|
||||
|
||||
memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
|
||||
read_block(blk, bprs, "BPRS read failed");
|
||||
|
||||
do {
|
||||
more_data = false;
|
||||
for (j = 0;; j++) {
|
||||
block_nr = eckd_block_num((void *)&(bprs[j].xeckd));
|
||||
if (is_null_block_number(block_nr)) { /* end of chunk */
|
||||
break;
|
||||
}
|
||||
|
||||
/* we need the updated blockno for the next indirect entry
|
||||
* in the chain, but don't want to advance address
|
||||
*/
|
||||
if (j == (max_bprs_entries - 1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
IPL_assert(block_size_ok(bprs[j].xeckd.bptr.size),
|
||||
"bad chunk block size");
|
||||
IPL_assert(eckd_valid_address(&bprs[j]), "bad chunk ECKD addr");
|
||||
|
||||
if ((bprs[j].xeckd.bptr.count == 0) && unused_space(&(bprs[j+1]),
|
||||
sizeof(EckdBlockPtr))) {
|
||||
/* This is a "continue" pointer.
|
||||
* This ptr should be the last one in the current
|
||||
* script section.
|
||||
* I.e. the next ptr must point to the unused memory area
|
||||
*/
|
||||
memset(_bprs, FREE_SPACE_FILLER, sizeof(_bprs));
|
||||
read_block(block_nr, bprs, "BPRS continuation read failed");
|
||||
more_data = true;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Load (count+1) blocks of code at (block_nr)
|
||||
* to memory (address).
|
||||
*/
|
||||
rc = virtio_read_many(block_nr, (void *)(*address),
|
||||
bprs[j].xeckd.bptr.count+1);
|
||||
IPL_assert(rc == 0, "code chunk read failed");
|
||||
|
||||
*address += (bprs[j].xeckd.bptr.count+1) * virtio_get_block_size();
|
||||
}
|
||||
} while (more_data);
|
||||
return block_nr;
|
||||
}
|
||||
|
||||
static void run_eckd_boot_script(block_number_t mbr_block_nr)
|
||||
{
|
||||
const int max_entries = (SECTOR_SIZE / sizeof(struct scsi_blockptr));
|
||||
struct scsi_blockptr *bprs = (void*)sec;
|
||||
const int bprs_size = sizeof(sec);
|
||||
uint64_t blockno;
|
||||
long address;
|
||||
int i;
|
||||
block_number_t block_nr;
|
||||
uint64_t address;
|
||||
ScsiMbr *scsi_mbr = (void *)sec;
|
||||
BootMapScript *bms = (void *)sec;
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(mbr_block_nr, sec, "Cannot read MBR");
|
||||
|
||||
block_nr = eckd_block_num((void *)&(scsi_mbr->blockptr));
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(block_nr, sec, "Cannot read Boot Map Script");
|
||||
|
||||
for (i = 0; bms->entry[i].type == BOOT_SCRIPT_LOAD; i++) {
|
||||
address = bms->entry[i].address.load_address;
|
||||
block_nr = eckd_block_num(&(bms->entry[i].blkptr));
|
||||
|
||||
do {
|
||||
block_nr = load_eckd_segments(block_nr, &address);
|
||||
} while (block_nr != -1);
|
||||
}
|
||||
|
||||
IPL_assert(bms->entry[i].type == BOOT_SCRIPT_EXEC,
|
||||
"Unknown script entry type");
|
||||
jump_to_IPL_code(bms->entry[i].address.load_address); /* no return */
|
||||
}
|
||||
|
||||
static void ipl_eckd_cdl(void)
|
||||
{
|
||||
XEckdMbr *mbr;
|
||||
Ipl2 *ipl2 = (void *)sec;
|
||||
IplVolumeLabel *vlbl = (void *)sec;
|
||||
block_number_t block_nr;
|
||||
|
||||
/* we have just read the block #0 and recognized it as "IPL1" */
|
||||
sclp_print("CDL\n");
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(1, ipl2, "Cannot read IPL2 record at block 1");
|
||||
IPL_assert(magic_match(ipl2, IPL2_MAGIC), "No IPL2 record");
|
||||
|
||||
mbr = &ipl2->u.x.mbr;
|
||||
IPL_assert(magic_match(mbr, ZIPL_MAGIC), "No zIPL section in IPL2 record.");
|
||||
IPL_assert(block_size_ok(mbr->blockptr.xeckd.bptr.size),
|
||||
"Bad block size in zIPL section of IPL2 record.");
|
||||
IPL_assert(mbr->dev_type == DEV_TYPE_ECKD,
|
||||
"Non-ECKD device type in zIPL section of IPL2 record.");
|
||||
|
||||
/* save pointer to Boot Script */
|
||||
block_nr = eckd_block_num((void *)&(mbr->blockptr));
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(2, vlbl, "Cannot read Volume Label at block 2");
|
||||
IPL_assert(magic_match(vlbl->key, VOL1_MAGIC),
|
||||
"Invalid magic of volume label block");
|
||||
IPL_assert(magic_match(vlbl->f.key, VOL1_MAGIC),
|
||||
"Invalid magic of volser block");
|
||||
print_volser(vlbl->f.volser);
|
||||
|
||||
run_eckd_boot_script(block_nr);
|
||||
/* no return */
|
||||
}
|
||||
|
||||
static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
|
||||
{
|
||||
LDL_VTOC *vlbl = (void *)sec; /* already read, 3rd block */
|
||||
char msg[4] = { '?', '.', '\n', '\0' };
|
||||
block_number_t block_nr;
|
||||
BootInfo *bip;
|
||||
|
||||
sclp_print((mode == ECKD_CMS) ? "CMS" : "LDL");
|
||||
sclp_print(" version ");
|
||||
switch (vlbl->LDL_version) {
|
||||
case LDL1_VERSION:
|
||||
msg[0] = '1';
|
||||
break;
|
||||
case LDL2_VERSION:
|
||||
msg[0] = '2';
|
||||
break;
|
||||
default:
|
||||
msg[0] = vlbl->LDL_version;
|
||||
msg[0] &= 0x0f; /* convert EBCDIC */
|
||||
msg[0] |= 0x30; /* to ASCII (digit) */
|
||||
msg[1] = '?';
|
||||
break;
|
||||
}
|
||||
sclp_print(msg);
|
||||
print_volser(vlbl->volser);
|
||||
|
||||
/* DO NOT read BootMap pointer (only one, xECKD) at block #2 */
|
||||
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(0, sec, "Cannot read block 0");
|
||||
bip = (void *)(sec + 0x70); /* "boot info" is "eckd mbr" for LDL */
|
||||
verify_boot_info(bip);
|
||||
|
||||
block_nr = eckd_block_num((void *)&(bip->bp.ipl.bm_ptr.eckd.bptr));
|
||||
run_eckd_boot_script(block_nr);
|
||||
/* no return */
|
||||
}
|
||||
|
||||
static void ipl_eckd(ECKD_IPL_mode_t mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case ECKD_CDL:
|
||||
ipl_eckd_cdl(); /* no return */
|
||||
case ECKD_CMS:
|
||||
case ECKD_LDL:
|
||||
ipl_eckd_ldl(mode); /* no return */
|
||||
default:
|
||||
virtio_panic("\n! Unknown ECKD IPL mode !\n");
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* IPL a SCSI disk
|
||||
*/
|
||||
|
||||
static void zipl_load_segment(ComponentEntry *entry)
|
||||
{
|
||||
const int max_entries = (MAX_SECTOR_SIZE / sizeof(ScsiBlockPtr));
|
||||
ScsiBlockPtr *bprs = (void *)sec;
|
||||
const int bprs_size = sizeof(sec);
|
||||
block_number_t blockno;
|
||||
uint64_t address;
|
||||
int i;
|
||||
char err_msg[] = "zIPL failed to read BPRS at 0xZZZZZZZZZZZZZZZZ";
|
||||
char *blk_no = &err_msg[30]; /* where to print blockno in (those ZZs) */
|
||||
|
||||
blockno = entry->data.blockno;
|
||||
address = entry->load_address;
|
||||
@ -150,25 +319,25 @@ static int zipl_load_segment(struct component_entry *entry)
|
||||
|
||||
do {
|
||||
memset(bprs, FREE_SPACE_FILLER, bprs_size);
|
||||
if (virtio_read(blockno, (uint8_t *)bprs)) {
|
||||
debug_print_int("failed reading bprs at", blockno);
|
||||
goto fail;
|
||||
}
|
||||
fill_hex_val(blk_no, &blockno, sizeof(blockno));
|
||||
read_block(blockno, bprs, err_msg);
|
||||
|
||||
for (i = 0;; i++) {
|
||||
u64 *cur_desc = (void*)&bprs[i];
|
||||
uint64_t *cur_desc = (void *)&bprs[i];
|
||||
|
||||
blockno = bprs[i].blockno;
|
||||
if (!blockno)
|
||||
if (!blockno) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* we need the updated blockno for the next indirect entry in the
|
||||
chain, but don't want to advance address */
|
||||
if (i == (max_entries - 1))
|
||||
if (i == (max_entries - 1)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (bprs[i].blockct == 0 && unused_space(&bprs[i + 1],
|
||||
sizeof(struct scsi_blockptr))) {
|
||||
sizeof(ScsiBlockPtr))) {
|
||||
/* This is a "continue" pointer.
|
||||
* This ptr is the last one in the current script section.
|
||||
* I.e. the next ptr must point to the unused memory area.
|
||||
@ -178,102 +347,66 @@ static int zipl_load_segment(struct component_entry *entry)
|
||||
break;
|
||||
}
|
||||
address = virtio_load_direct(cur_desc[0], cur_desc[1], 0,
|
||||
(void*)address);
|
||||
if (address == -1)
|
||||
goto fail;
|
||||
(void *)address);
|
||||
IPL_assert(address != -1, "zIPL load segment failed");
|
||||
}
|
||||
} while (blockno);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
sclp_print("failed loading segment\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Run a zipl program */
|
||||
static int zipl_run(struct scsi_blockptr *pte)
|
||||
static void zipl_run(ScsiBlockPtr *pte)
|
||||
{
|
||||
struct component_header *header;
|
||||
struct component_entry *entry;
|
||||
uint8_t tmp_sec[SECTOR_SIZE];
|
||||
ComponentHeader *header;
|
||||
ComponentEntry *entry;
|
||||
uint8_t tmp_sec[MAX_SECTOR_SIZE];
|
||||
|
||||
virtio_read(pte->blockno, tmp_sec);
|
||||
header = (struct component_header *)tmp_sec;
|
||||
read_block(pte->blockno, tmp_sec, "Cannot read header");
|
||||
header = (ComponentHeader *)tmp_sec;
|
||||
|
||||
if (!zipl_magic(tmp_sec)) {
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (header->type != ZIPL_COMP_HEADER_IPL) {
|
||||
goto fail;
|
||||
}
|
||||
IPL_assert(magic_match(tmp_sec, ZIPL_MAGIC), "No zIPL magic");
|
||||
IPL_assert(header->type == ZIPL_COMP_HEADER_IPL, "Bad header type");
|
||||
|
||||
dputs("start loading images\n");
|
||||
|
||||
/* Load image(s) into RAM */
|
||||
entry = (struct component_entry *)(&header[1]);
|
||||
entry = (ComponentEntry *)(&header[1]);
|
||||
while (entry->component_type == ZIPL_COMP_ENTRY_LOAD) {
|
||||
if (zipl_load_segment(entry) < 0) {
|
||||
goto fail;
|
||||
}
|
||||
zipl_load_segment(entry);
|
||||
|
||||
entry++;
|
||||
|
||||
if ((uint8_t*)(&entry[1]) > (tmp_sec + SECTOR_SIZE)) {
|
||||
goto fail;
|
||||
}
|
||||
IPL_assert((uint8_t *)(&entry[1]) <= (tmp_sec + MAX_SECTOR_SIZE),
|
||||
"Wrong entry value");
|
||||
}
|
||||
|
||||
if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
|
||||
goto fail;
|
||||
}
|
||||
IPL_assert(entry->component_type == ZIPL_COMP_ENTRY_EXEC, "No EXEC entry");
|
||||
|
||||
/* should not return */
|
||||
jump_to_IPL_code(entry->load_address);
|
||||
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
sclp_print("failed running zipl\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int zipl_load(void)
|
||||
static void ipl_scsi(void)
|
||||
{
|
||||
struct mbr *mbr = (void*)sec;
|
||||
ScsiMbr *mbr = (void *)sec;
|
||||
uint8_t *ns, *ns_end;
|
||||
int program_table_entries = 0;
|
||||
int pte_len = sizeof(struct scsi_blockptr);
|
||||
struct scsi_blockptr *prog_table_entry;
|
||||
const char *error = "";
|
||||
const int pte_len = sizeof(ScsiBlockPtr);
|
||||
ScsiBlockPtr *prog_table_entry;
|
||||
|
||||
/* Grab the MBR */
|
||||
virtio_read(0, (void*)mbr);
|
||||
|
||||
dputs("checking magic\n");
|
||||
|
||||
if (!zipl_magic(mbr->magic)) {
|
||||
error = "zipl_magic 1";
|
||||
goto fail;
|
||||
}
|
||||
/* The 0-th block (MBR) was already read into sec[] */
|
||||
|
||||
sclp_print("Using SCSI scheme.\n");
|
||||
debug_print_int("program table", mbr->blockptr.blockno);
|
||||
|
||||
/* Parse the program table */
|
||||
if (virtio_read(mbr->blockptr.blockno, sec)) {
|
||||
error = "virtio_read";
|
||||
goto fail;
|
||||
}
|
||||
read_block(mbr->blockptr.blockno, sec,
|
||||
"Error reading Program Table");
|
||||
|
||||
if (!zipl_magic(sec)) {
|
||||
error = "zipl_magic 2";
|
||||
goto fail;
|
||||
}
|
||||
IPL_assert(magic_match(sec, ZIPL_MAGIC), "No zIPL magic");
|
||||
|
||||
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 = (struct scsi_blockptr *)ns;
|
||||
prog_table_entry = (ScsiBlockPtr *)ns;
|
||||
if (!prog_table_entry->blockno) {
|
||||
break;
|
||||
}
|
||||
@ -283,19 +416,55 @@ int zipl_load(void)
|
||||
|
||||
debug_print_int("program table entries", program_table_entries);
|
||||
|
||||
if (!program_table_entries) {
|
||||
goto fail;
|
||||
}
|
||||
IPL_assert(program_table_entries != 0, "Empty Program Table");
|
||||
|
||||
/* Run the default entry */
|
||||
|
||||
prog_table_entry = (struct scsi_blockptr *)(sec + pte_len);
|
||||
prog_table_entry = (ScsiBlockPtr *)(sec + pte_len);
|
||||
|
||||
return zipl_run(prog_table_entry);
|
||||
|
||||
fail:
|
||||
sclp_print("failed loading zipl: ");
|
||||
sclp_print(error);
|
||||
sclp_print("\n");
|
||||
return -1;
|
||||
zipl_run(prog_table_entry); /* no return */
|
||||
}
|
||||
|
||||
/***********************************************************************
|
||||
* IPL starts here
|
||||
*/
|
||||
|
||||
void zipl_load(void)
|
||||
{
|
||||
ScsiMbr *mbr = (void *)sec;
|
||||
LDL_VTOC *vlbl = (void *)sec;
|
||||
|
||||
/* Grab the MBR */
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(0, mbr, "Cannot read block 0");
|
||||
|
||||
dputs("checking magic\n");
|
||||
|
||||
if (magic_match(mbr->magic, ZIPL_MAGIC)) {
|
||||
ipl_scsi(); /* no return */
|
||||
}
|
||||
|
||||
/* We have failed to follow the SCSI scheme, so */
|
||||
sclp_print("Using ECKD scheme.\n");
|
||||
if (virtio_guessed_disk_nature()) {
|
||||
sclp_print("Using guessed DASD geometry.\n");
|
||||
virtio_assume_eckd();
|
||||
}
|
||||
|
||||
if (magic_match(mbr->magic, IPL1_MAGIC)) {
|
||||
ipl_eckd(ECKD_CDL); /* no return */
|
||||
}
|
||||
|
||||
/* LDL/CMS? */
|
||||
memset(sec, FREE_SPACE_FILLER, sizeof(sec));
|
||||
read_block(2, vlbl, "Cannot read block 2");
|
||||
|
||||
if (magic_match(vlbl->magic, CMS1_MAGIC)) {
|
||||
ipl_eckd(ECKD_CMS); /* no return */
|
||||
}
|
||||
if (magic_match(vlbl->magic, LNX1_MAGIC)) {
|
||||
ipl_eckd(ECKD_LDL); /* no return */
|
||||
}
|
||||
|
||||
virtio_panic("\n* invalid MBR magic *\n");
|
||||
}
|
||||
|
344
pc-bios/s390-ccw/bootmap.h
Normal file
344
pc-bios/s390-ccw/bootmap.h
Normal file
@ -0,0 +1,344 @@
|
||||
/*
|
||||
* QEMU S390 bootmap interpreter -- declarations
|
||||
*
|
||||
* Copyright 2014 IBM Corp.
|
||||
* Author(s): Eugene (jno) Dvurechenski <jno@linux.vnet.ibm.com>
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or (at
|
||||
* your option) any later version. See the COPYING file in the top-level
|
||||
* directory.
|
||||
*/
|
||||
#ifndef _PC_BIOS_S390_CCW_BOOTMAP_H
|
||||
#define _PC_BIOS_S390_CCW_BOOTMAP_H
|
||||
|
||||
#include "s390-ccw.h"
|
||||
#include "virtio.h"
|
||||
|
||||
typedef uint64_t block_number_t;
|
||||
#define NULL_BLOCK_NR 0xffffffffffffffff
|
||||
|
||||
#define FREE_SPACE_FILLER '\xAA'
|
||||
|
||||
typedef struct ScsiBlockPtr {
|
||||
uint64_t blockno;
|
||||
uint16_t size;
|
||||
uint16_t blockct;
|
||||
uint8_t reserved[4];
|
||||
} __attribute__ ((packed)) ScsiBlockPtr;
|
||||
|
||||
typedef struct FbaBlockPtr {
|
||||
uint32_t blockno;
|
||||
uint16_t size;
|
||||
uint16_t blockct;
|
||||
} __attribute__ ((packed)) FbaBlockPtr;
|
||||
|
||||
typedef struct EckdBlockPtr {
|
||||
uint16_t cylinder; /* cylinder/head/sector is an address of the block */
|
||||
uint16_t head;
|
||||
uint8_t sector;
|
||||
uint16_t size;
|
||||
uint8_t count; /* (size_in_blocks-1);
|
||||
* it's 0 for TablePtr, ScriptPtr, and SectionPtr */
|
||||
} __attribute__ ((packed)) EckdBlockPtr;
|
||||
|
||||
typedef struct ExtEckdBlockPtr {
|
||||
EckdBlockPtr bptr;
|
||||
uint8_t reserved[8];
|
||||
} __attribute__ ((packed)) ExtEckdBlockPtr;
|
||||
|
||||
typedef union BootMapPointer {
|
||||
ScsiBlockPtr scsi;
|
||||
FbaBlockPtr fba;
|
||||
EckdBlockPtr eckd;
|
||||
ExtEckdBlockPtr xeckd;
|
||||
} __attribute__ ((packed)) BootMapPointer;
|
||||
|
||||
typedef struct ComponentEntry {
|
||||
ScsiBlockPtr data;
|
||||
uint8_t pad[7];
|
||||
uint8_t component_type;
|
||||
uint64_t load_address;
|
||||
} __attribute((packed)) ComponentEntry;
|
||||
|
||||
typedef struct ComponentHeader {
|
||||
uint8_t magic[4]; /* == "zIPL" */
|
||||
uint8_t type; /* == ZIPL_COMP_HEADER_* */
|
||||
uint8_t reserved[27];
|
||||
} __attribute((packed)) ComponentHeader;
|
||||
|
||||
typedef struct ScsiMbr {
|
||||
uint8_t magic[4];
|
||||
uint32_t version_id;
|
||||
uint8_t reserved[8];
|
||||
ScsiBlockPtr blockptr;
|
||||
} __attribute__ ((packed)) ScsiMbr;
|
||||
|
||||
#define ZIPL_MAGIC "zIPL"
|
||||
#define IPL1_MAGIC "\xc9\xd7\xd3\xf1" /* == "IPL1" in EBCDIC */
|
||||
#define IPL2_MAGIC "\xc9\xd7\xd3\xf2" /* == "IPL2" in EBCDIC */
|
||||
#define VOL1_MAGIC "\xe5\xd6\xd3\xf1" /* == "VOL1" in EBCDIC */
|
||||
#define LNX1_MAGIC "\xd3\xd5\xe7\xf1" /* == "LNX1" in EBCDIC */
|
||||
#define CMS1_MAGIC "\xc3\xd4\xe2\xf1" /* == "CMS1" in EBCDIC */
|
||||
|
||||
#define LDL1_VERSION '\x40' /* == ' ' in EBCDIC */
|
||||
#define LDL2_VERSION '\xf2' /* == '2' in EBCDIC */
|
||||
|
||||
#define ZIPL_COMP_HEADER_IPL 0x00
|
||||
#define ZIPL_COMP_HEADER_DUMP 0x01
|
||||
|
||||
#define ZIPL_COMP_ENTRY_LOAD 0x02
|
||||
#define ZIPL_COMP_ENTRY_EXEC 0x01
|
||||
|
||||
typedef struct XEckdMbr {
|
||||
uint8_t magic[4]; /* == "xIPL" */
|
||||
uint8_t version;
|
||||
uint8_t bp_type;
|
||||
uint8_t dev_type; /* == DEV_TYPE_* */
|
||||
#define DEV_TYPE_ECKD 0x00
|
||||
#define DEV_TYPE_FBA 0x01
|
||||
uint8_t flags;
|
||||
BootMapPointer blockptr;
|
||||
uint8_t reserved[8];
|
||||
} __attribute__ ((packed)) XEckdMbr; /* see also BootInfo */
|
||||
|
||||
typedef struct BootMapScriptEntry {
|
||||
BootMapPointer blkptr;
|
||||
uint8_t pad[7];
|
||||
uint8_t type; /* == BOOT_SCRIPT_* */
|
||||
#define BOOT_SCRIPT_EXEC 0x01
|
||||
#define BOOT_SCRIPT_LOAD 0x02
|
||||
union {
|
||||
uint64_t load_address;
|
||||
uint64_t load_psw;
|
||||
} address;
|
||||
} __attribute__ ((packed)) BootMapScriptEntry;
|
||||
|
||||
typedef struct BootMapScriptHeader {
|
||||
uint32_t magic;
|
||||
uint8_t type;
|
||||
#define BOOT_SCRIPT_HDR_IPL 0x00
|
||||
uint8_t reserved[27];
|
||||
} __attribute__ ((packed)) BootMapScriptHeader;
|
||||
|
||||
typedef struct BootMapScript {
|
||||
BootMapScriptHeader header;
|
||||
BootMapScriptEntry entry[0];
|
||||
} __attribute__ ((packed)) BootMapScript;
|
||||
|
||||
/*
|
||||
* These aren't real VTOCs, but referred to this way in some docs.
|
||||
* They are "volume labels" actually.
|
||||
*
|
||||
* Some structures looks similar to described above, but left
|
||||
* separate as there is no indication that they are the same.
|
||||
* So, the value definitions are left separate too.
|
||||
*/
|
||||
typedef struct LDL_VTOC { /* @ rec.3 cyl.0 trk.0 for ECKD */
|
||||
char magic[4]; /* "LNX1", EBCDIC */
|
||||
char volser[6]; /* volser, EBCDIC */
|
||||
uint8_t reserved[69]; /* reserved, 0x40 */
|
||||
uint8_t LDL_version; /* 0x40 or 0xF2 */
|
||||
uint64_t formatted_blocks; /* if LDL_version >= 0xF2 */
|
||||
} __attribute__ ((packed)) LDL_VTOC;
|
||||
|
||||
typedef struct format_date {
|
||||
uint8_t YY;
|
||||
uint8_t MM;
|
||||
uint8_t DD;
|
||||
uint8_t hh;
|
||||
uint8_t mm;
|
||||
uint8_t ss;
|
||||
} __attribute__ ((packed)) format_date_t;
|
||||
|
||||
typedef struct CMS_VTOC { /* @ rec.3 cyl.0 trk.0 for ECKD */
|
||||
/* @ blk.1 (zero based) for FBA */
|
||||
char magic[4]; /* 'CMS1', EBCDIC */
|
||||
char volser[6]; /* volser, EBCDIC */
|
||||
uint16_t version; /* = 0 */
|
||||
uint32_t block_size; /* = 512, 1024, 2048, or 4096 */
|
||||
uint32_t disk_origin; /* = 4 or 5 */
|
||||
uint32_t blocks; /* Number of usable cyls/blocks */
|
||||
uint32_t formatted; /* Max number of fmtd cyls/blks */
|
||||
uint32_t CMS_blocks; /* disk size in CMS blocks */
|
||||
uint32_t CMS_used; /* Number of CMS blocks in use */
|
||||
uint32_t FST_size; /* = 64, bytes */
|
||||
uint32_t FST_per_CMS_blk; /* */
|
||||
format_date_t format_date; /* YYMMDDhhmmss as 6 bytes */
|
||||
uint8_t reserved1[2]; /* = 0 */
|
||||
uint32_t offset; /* disk offset when reserved */
|
||||
uint32_t next_hole; /* block nr */
|
||||
uint32_t HBLK_hole_offset; /* >> HBLK data of next hole */
|
||||
uint32_t alloc_map_usr_off; /* >> user part of Alloc map */
|
||||
uint8_t reserved2[4]; /* = 0 */
|
||||
char shared_seg_name[8]; /* */
|
||||
} __attribute__ ((packed)) CMS_VTOC;
|
||||
|
||||
/* from zipl/include/boot.h */
|
||||
typedef struct BootInfoBpIpl {
|
||||
union {
|
||||
ExtEckdBlockPtr eckd;
|
||||
ScsiBlockPtr linr;
|
||||
} bm_ptr;
|
||||
uint8_t unused[16];
|
||||
} __attribute__ ((packed)) BootInfoBpIpl;
|
||||
|
||||
typedef struct EckdDumpParam {
|
||||
uint32_t start_blk;
|
||||
uint32_t end_blk;
|
||||
uint16_t blocksize;
|
||||
uint8_t num_heads;
|
||||
uint8_t bpt;
|
||||
char reserved[4];
|
||||
} __attribute((packed, may_alias)) EckdDumpParam;
|
||||
|
||||
typedef struct FbaDumpParam {
|
||||
uint64_t start_blk;
|
||||
uint64_t blockct;
|
||||
} __attribute((packed)) FbaDumpParam;
|
||||
|
||||
typedef struct BootInfoBpDump {
|
||||
union {
|
||||
EckdDumpParam eckd;
|
||||
FbaDumpParam fba;
|
||||
} param;
|
||||
uint8_t unused[16];
|
||||
} __attribute__ ((packed)) BootInfoBpDump;
|
||||
|
||||
typedef struct BootInfo { /* @ 0x70, record #0 */
|
||||
unsigned char magic[4]; /* = 'zIPL', ASCII */
|
||||
uint8_t version; /* = 1 */
|
||||
#define BOOT_INFO_VERSION 1
|
||||
uint8_t bp_type; /* = 0 */
|
||||
#define BOOT_INFO_BP_TYPE_IPL 0x00
|
||||
#define BOOT_INFO_BP_TYPE_DUMP 0x01
|
||||
uint8_t dev_type; /* = 0 */
|
||||
#define BOOT_INFO_DEV_TYPE_ECKD 0x00
|
||||
#define BOOT_INFO_DEV_TYPE_FBA 0x01
|
||||
uint8_t flags; /* = 1 */
|
||||
#ifdef __s390x__
|
||||
#define BOOT_INFO_FLAGS_ARCH 0x01
|
||||
#else
|
||||
#define BOOT_INFO_FLAGS_ARCH 0x00
|
||||
#endif
|
||||
union {
|
||||
BootInfoBpDump dump;
|
||||
BootInfoBpIpl ipl;
|
||||
} bp;
|
||||
} __attribute__ ((packed)) BootInfo; /* see also XEckdMbr */
|
||||
|
||||
typedef struct Ipl1 {
|
||||
unsigned char key[4]; /* == "IPL1" */
|
||||
unsigned char data[24];
|
||||
} __attribute__((packed)) Ipl1;
|
||||
|
||||
typedef struct Ipl2 {
|
||||
unsigned char key[4]; /* == "IPL2" */
|
||||
union {
|
||||
unsigned char data[144];
|
||||
struct {
|
||||
unsigned char reserved1[92-4];
|
||||
XEckdMbr mbr;
|
||||
unsigned char reserved2[144-(92-4)-sizeof(XEckdMbr)];
|
||||
} x;
|
||||
} u;
|
||||
} __attribute__((packed)) Ipl2;
|
||||
|
||||
typedef struct IplVolumeLabel {
|
||||
unsigned char key[4]; /* == "VOL1" */
|
||||
union {
|
||||
unsigned char data[80];
|
||||
struct {
|
||||
unsigned char key[4]; /* == "VOL1" */
|
||||
unsigned char volser[6];
|
||||
unsigned char reserved[6];
|
||||
} f;
|
||||
};
|
||||
} __attribute__((packed)) IplVolumeLabel;
|
||||
|
||||
typedef enum {
|
||||
ECKD_NO_IPL,
|
||||
ECKD_CDL,
|
||||
ECKD_CMS,
|
||||
ECKD_LDL,
|
||||
} ECKD_IPL_mode_t;
|
||||
|
||||
/* utility code below */
|
||||
|
||||
static inline void IPL_assert(bool term, const char *message)
|
||||
{
|
||||
if (!term) {
|
||||
sclp_print("\n! ");
|
||||
sclp_print(message);
|
||||
virtio_panic(" !\n"); /* no return */
|
||||
}
|
||||
}
|
||||
|
||||
static const unsigned char ebc2asc[256] =
|
||||
/* 0123456789abcdef0123456789abcdef */
|
||||
"................................" /* 1F */
|
||||
"................................" /* 3F */
|
||||
" ...........<(+|&.........!$*);." /* 5F first.chr.here.is.real.space */
|
||||
"-/.........,%_>?.........`:#@'=\""/* 7F */
|
||||
".abcdefghi.......jklmnopqr......" /* 9F */
|
||||
"..stuvwxyz......................" /* BF */
|
||||
".ABCDEFGHI.......JKLMNOPQR......" /* DF */
|
||||
"..STUVWXYZ......0123456789......";/* FF */
|
||||
|
||||
static inline void ebcdic_to_ascii(const char *src,
|
||||
char *dst,
|
||||
unsigned int size)
|
||||
{
|
||||
unsigned int i;
|
||||
for (i = 0; i < size; i++) {
|
||||
unsigned c = src[i];
|
||||
dst[i] = ebc2asc[c];
|
||||
}
|
||||
}
|
||||
|
||||
static inline void print_volser(const void *volser)
|
||||
{
|
||||
char ascii[8];
|
||||
|
||||
ebcdic_to_ascii((char *)volser, ascii, 6);
|
||||
ascii[6] = '\0';
|
||||
sclp_print("VOLSER=[");
|
||||
sclp_print(ascii);
|
||||
sclp_print("]\n");
|
||||
}
|
||||
|
||||
static inline bool unused_space(const void *p, size_t size)
|
||||
{
|
||||
size_t i;
|
||||
const unsigned char *m = p;
|
||||
|
||||
for (i = 0; i < size; i++) {
|
||||
if (m[i] != FREE_SPACE_FILLER) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool is_null_block_number(block_number_t x)
|
||||
{
|
||||
return x == NULL_BLOCK_NR;
|
||||
}
|
||||
|
||||
static inline void read_block(block_number_t blockno,
|
||||
void *buffer,
|
||||
const char *errmsg)
|
||||
{
|
||||
IPL_assert(virtio_read(blockno, buffer) == 0, errmsg);
|
||||
}
|
||||
|
||||
static inline bool block_size_ok(uint32_t block_size)
|
||||
{
|
||||
return block_size == virtio_get_block_size();
|
||||
}
|
||||
|
||||
static inline bool magic_match(const void *data, const void *magic)
|
||||
{
|
||||
return *((uint32_t *)data) == *((uint32_t *)magic);
|
||||
}
|
||||
|
||||
#endif /* _PC_BIOS_S390_CCW_BOOTMAP_H */
|
@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
#include "s390-ccw.h"
|
||||
#include "virtio.h"
|
||||
|
||||
char stack[PAGE_SIZE * 8] __attribute__((__aligned__(PAGE_SIZE)));
|
||||
uint64_t boot_value;
|
||||
@ -64,6 +65,10 @@ static void virtio_setup(uint64_t dev_info)
|
||||
}
|
||||
|
||||
virtio_setup_block(blk_schid);
|
||||
|
||||
if (!virtio_ipl_disk_is_valid()) {
|
||||
virtio_panic("No valid hard disk detected.\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
@ -72,8 +77,8 @@ int main(void)
|
||||
debug_print_int("boot reg[7] ", boot_value);
|
||||
virtio_setup(boot_value);
|
||||
|
||||
if (zipl_load() < 0)
|
||||
sclp_print("Failed to load OS from hard disk\n");
|
||||
disabled_wait();
|
||||
while (1) { }
|
||||
zipl_load(); /* no return */
|
||||
|
||||
virtio_panic("Failed to load OS from hard disk\n");
|
||||
return 0; /* make compiler happy */
|
||||
}
|
||||
|
@ -34,10 +34,10 @@ typedef unsigned long long __u64;
|
||||
#define PAGE_SIZE 4096
|
||||
|
||||
#ifndef EIO
|
||||
#define EIO 1
|
||||
#define EIO 1
|
||||
#endif
|
||||
#ifndef EBUSY
|
||||
#define EBUSY 2
|
||||
#define EBUSY 2
|
||||
#endif
|
||||
#ifndef NULL
|
||||
#define NULL 0
|
||||
@ -57,14 +57,14 @@ void sclp_setup(void);
|
||||
|
||||
/* virtio.c */
|
||||
unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
|
||||
ulong subchan_id, void *load_addr);
|
||||
ulong subchan_id, void *load_addr);
|
||||
bool virtio_is_blk(struct subchannel_id schid);
|
||||
void virtio_setup_block(struct subchannel_id schid);
|
||||
int virtio_read(ulong sector, void *load_addr);
|
||||
int enable_mss_facility(void);
|
||||
|
||||
/* bootmap.c */
|
||||
int zipl_load(void);
|
||||
void zipl_load(void);
|
||||
|
||||
static inline void *memset(void *s, int c, size_t n)
|
||||
{
|
||||
@ -86,15 +86,21 @@ static inline void fill_hex(char *out, unsigned char val)
|
||||
out[1] = hex[val & 0xf];
|
||||
}
|
||||
|
||||
static inline void print_int(const char *desc, u64 addr)
|
||||
static inline void fill_hex_val(char *out, void *ptr, unsigned size)
|
||||
{
|
||||
unsigned char *addr_c = (unsigned char*)&addr;
|
||||
char out[] = ": 0xffffffffffffffff\n";
|
||||
unsigned char *value = ptr;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < sizeof(addr); i++) {
|
||||
fill_hex(&out[4 + (i*2)], addr_c[i]);
|
||||
for (i = 0; i < size; i++) {
|
||||
fill_hex(&out[i*2], value[i]);
|
||||
}
|
||||
}
|
||||
|
||||
static inline void print_int(const char *desc, u64 addr)
|
||||
{
|
||||
char out[] = ": 0xffffffffffffffff\n";
|
||||
|
||||
fill_hex_val(&out[4], &addr, sizeof(addr));
|
||||
|
||||
sclp_print(desc);
|
||||
sclp_print(out);
|
||||
@ -118,18 +124,18 @@ static inline void debug_print_addr(const char *desc, void *p)
|
||||
* Hypercall functions *
|
||||
***********************************************/
|
||||
|
||||
#define KVM_S390_VIRTIO_NOTIFY 0
|
||||
#define KVM_S390_VIRTIO_RESET 1
|
||||
#define KVM_S390_VIRTIO_SET_STATUS 2
|
||||
#define KVM_S390_VIRTIO_NOTIFY 0
|
||||
#define KVM_S390_VIRTIO_RESET 1
|
||||
#define KVM_S390_VIRTIO_SET_STATUS 2
|
||||
#define KVM_S390_VIRTIO_CCW_NOTIFY 3
|
||||
|
||||
static inline void yield(void)
|
||||
{
|
||||
asm volatile ("diag 0,0,0x44"
|
||||
: :
|
||||
: "memory", "cc");
|
||||
asm volatile ("diag 0,0,0x44"
|
||||
: :
|
||||
: "memory", "cc");
|
||||
}
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
#define MAX_SECTOR_SIZE 4096
|
||||
|
||||
#endif /* S390_CCW_H */
|
||||
|
@ -33,7 +33,7 @@ static int sclp_service_call(unsigned int command, void *sccb)
|
||||
|
||||
static void sclp_set_write_mask(void)
|
||||
{
|
||||
WriteEventMask *sccb = (void*)_sccb;
|
||||
WriteEventMask *sccb = (void *)_sccb;
|
||||
|
||||
sccb->h.length = sizeof(WriteEventMask);
|
||||
sccb->mask_length = sizeof(unsigned int);
|
||||
@ -68,7 +68,7 @@ static void _memcpy(char *dest, const char *src, int len)
|
||||
void sclp_print(const char *str)
|
||||
{
|
||||
int len = _strlen(str);
|
||||
WriteEventData *sccb = (void*)_sccb;
|
||||
WriteEventData *sccb = (void *)_sccb;
|
||||
|
||||
sccb->h.length = sizeof(WriteEventData) + len;
|
||||
sccb->h.function_code = SCLP_FC_NORMAL_WRITE;
|
||||
|
@ -18,22 +18,22 @@ static char chsc_page[PAGE_SIZE] __attribute__((__aligned__(PAGE_SIZE)));
|
||||
static long kvm_hypercall(unsigned long nr, unsigned long param1,
|
||||
unsigned long param2)
|
||||
{
|
||||
register ulong r_nr asm("1") = nr;
|
||||
register ulong r_param1 asm("2") = param1;
|
||||
register ulong r_param2 asm("3") = param2;
|
||||
register long retval asm("2");
|
||||
register ulong r_nr asm("1") = nr;
|
||||
register ulong r_param1 asm("2") = param1;
|
||||
register ulong r_param2 asm("3") = param2;
|
||||
register long retval asm("2");
|
||||
|
||||
asm volatile ("diag 2,4,0x500"
|
||||
: "=d" (retval)
|
||||
: "d" (r_nr), "0" (r_param1), "r"(r_param2)
|
||||
: "memory", "cc");
|
||||
asm volatile ("diag 2,4,0x500"
|
||||
: "=d" (retval)
|
||||
: "d" (r_nr), "0" (r_param1), "r"(r_param2)
|
||||
: "memory", "cc");
|
||||
|
||||
return retval;
|
||||
return retval;
|
||||
}
|
||||
|
||||
static void virtio_notify(struct subchannel_id schid)
|
||||
{
|
||||
kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32*)&schid, 0);
|
||||
kvm_hypercall(KVM_S390_VIRTIO_CCW_NOTIFY, *(u32 *)&schid, 0);
|
||||
}
|
||||
|
||||
/***********************************************
|
||||
@ -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);
|
||||
|
||||
@ -236,7 +236,7 @@ static int virtio_read_many(ulong sector, void *load_addr, int sec_num)
|
||||
}
|
||||
|
||||
unsigned long virtio_load_direct(ulong rec_list1, ulong rec_list2,
|
||||
ulong subchan_id, void *load_addr)
|
||||
ulong subchan_id, void *load_addr)
|
||||
{
|
||||
u8 status;
|
||||
int sec = rec_list1;
|
||||
@ -244,16 +244,16 @@ 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;
|
||||
}
|
||||
|
||||
sclp_print(".");
|
||||
status = virtio_read_many(sec, (void*)addr, sec_num);
|
||||
status = virtio_read_many(sec, (void *)addr, sec_num);
|
||||
if (status) {
|
||||
virtio_panic("I/O Error");
|
||||
}
|
||||
addr += sec_num * SECTOR_SIZE;
|
||||
addr += sec_num * virtio_get_block_size();
|
||||
|
||||
return addr;
|
||||
}
|
||||
@ -263,18 +263,98 @@ 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),
|
||||
vring_init(&block, config.num, (void *)(100 * 1024 * 1024),
|
||||
KVM_S390_VIRTIO_RING_ALIGN);
|
||||
|
||||
info.queue = (100ULL * 1024ULL* 1024ULL);
|
||||
@ -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)
|
||||
|
@ -66,7 +66,7 @@ struct virtio_dev {
|
||||
char *config;
|
||||
};
|
||||
|
||||
#define KVM_S390_VIRTIO_RING_ALIGN 4096
|
||||
#define KVM_S390_VIRTIO_RING_ALIGN 4096
|
||||
|
||||
#define VRING_USED_F_NO_NOTIFY 1
|
||||
|
||||
@ -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 */
|
||||
|
Loading…
Reference in New Issue
Block a user