aboutsummaryrefslogtreecommitdiff
path: root/pc-bios/s390-ccw
diff options
context:
space:
mode:
Diffstat (limited to 'pc-bios/s390-ccw')
-rw-r--r--pc-bios/s390-ccw/bootmap.c107
-rw-r--r--pc-bios/s390-ccw/bootmap.h2
-rw-r--r--pc-bios/s390-ccw/virtio.c48
-rw-r--r--pc-bios/s390-ccw/virtio.h2
4 files changed, 103 insertions, 56 deletions
diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c
index f175679..115d8bb 100644
--- a/pc-bios/s390-ccw/bootmap.c
+++ b/pc-bios/s390-ccw/bootmap.c
@@ -40,11 +40,6 @@ static void jump_to_IPL_2(void)
ResetInfo *current = 0;
void (*ipl)(void) = (void *) (uint64_t) current->ipl_continue;
- debug_print_addr("set IPL addr to", ipl);
-
- /* Ensure the guest output starts fresh */
- sclp_print("\n");
-
*current = save;
ipl(); /* should not return */
}
@@ -64,6 +59,11 @@ static void jump_to_IPL_code(uint64_t address)
current->ipl_addr = (uint32_t) (uint64_t) &jump_to_IPL_2;
current->ipl_continue = address & 0x7fffffff;
+ debug_print_int("set IPL addr to", current->ipl_continue);
+
+ /* Ensure the guest output starts fresh */
+ sclp_print("\n");
+
/*
* HACK ALERT.
* We use the load normal reset to keep r15 unchanged. jump_to_IPL_2
@@ -93,11 +93,23 @@ static inline void verify_boot_info(BootInfo *bip)
"Bad block size in zIPL section of the 1st record.");
}
-static bool eckd_valid_address(BootMapPointer *p)
+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 bool eckd_valid_address(BootMapPointer *p)
+{
+ const uint64_t head = p->eckd.head & 0x000f;
if (head >= virtio_get_heads()
|| p->eckd.sector > virtio_get_sectors()
@@ -105,27 +117,14 @@ static bool eckd_valid_address(BootMapPointer *p)
return false;
}
- if (!virtio_guessed_disk_nature() && cylinder >= virtio_get_cylinders()) {
+ if (!virtio_guessed_disk_nature() &&
+ eckd_block_num(p) >= virtio_get_blocks()) {
return false;
}
return true;
}
-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;
@@ -223,7 +222,6 @@ static void ipl_eckd_cdl(void)
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.");
@@ -247,12 +245,10 @@ static void ipl_eckd_cdl(void)
/* no return */
}
-static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
+static void print_eckd_ldl_msg(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 ");
@@ -272,12 +268,27 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
}
sclp_print(msg);
print_volser(vlbl->volser);
+}
+
+static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
+{
+ block_number_t block_nr;
+ BootInfo *bip = (void *)(sec + 0x70); /* BootInfo is MBR for LDL */
+
+ if (mode != ECKD_LDL_UNLABELED) {
+ print_eckd_ldl_msg(mode);
+ }
/* 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 */
+ read_block(0, sec, "Cannot read block 0 to grab boot info.");
+ if (mode == ECKD_LDL_UNLABELED) {
+ if (!magic_match(bip->magic, ZIPL_MAGIC)) {
+ return; /* not applicable layout */
+ }
+ sclp_print("unlabeled LDL.\n");
+ }
verify_boot_info(bip);
block_nr = eckd_block_num((void *)&(bip->bp.ipl.bm_ptr.eckd.bptr));
@@ -285,17 +296,23 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode)
/* no return */
}
-static void ipl_eckd(ECKD_IPL_mode_t mode)
+static void print_eckd_msg(void)
{
- 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");
+ char msg[] = "Using ECKD scheme (block size *****), ";
+ char *p = &msg[34], *q = &msg[30];
+ int n = virtio_get_block_size();
+
+ /* Fill in the block size and show up the message */
+ if (n > 0 && n <= 99999) {
+ while (n) {
+ *p-- = '0' + (n % 10);
+ n /= 10;
+ }
+ while (p >= q) {
+ *p-- = ' ';
+ }
}
+ sclp_print(msg);
}
/***********************************************************************
@@ -447,14 +464,13 @@ void zipl_load(void)
}
/* 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();
}
-
+ print_eckd_msg();
if (magic_match(mbr->magic, IPL1_MAGIC)) {
- ipl_eckd(ECKD_CDL); /* no return */
+ ipl_eckd_cdl(); /* no return */
}
/* LDL/CMS? */
@@ -462,11 +478,18 @@ void zipl_load(void)
read_block(2, vlbl, "Cannot read block 2");
if (magic_match(vlbl->magic, CMS1_MAGIC)) {
- ipl_eckd(ECKD_CMS); /* no return */
+ ipl_eckd_ldl(ECKD_CMS); /* no return */
}
if (magic_match(vlbl->magic, LNX1_MAGIC)) {
- ipl_eckd(ECKD_LDL); /* no return */
+ ipl_eckd_ldl(ECKD_LDL); /* no return */
}
- virtio_panic("\n* invalid MBR magic *\n");
+ ipl_eckd_ldl(ECKD_LDL_UNLABELED); /* it still may return */
+ /*
+ * Ok, it is not a LDL by any means.
+ * It still might be a CDL with zero record keys for IPL1 and IPL2
+ */
+ ipl_eckd_cdl();
+
+ virtio_panic("\n* this can never happen *\n");
}
diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
index 30ef22f..6a4823d 100644
--- a/pc-bios/s390-ccw/bootmap.h
+++ b/pc-bios/s390-ccw/bootmap.h
@@ -257,9 +257,9 @@ typedef struct IplVolumeLabel {
typedef enum {
ECKD_NO_IPL,
- ECKD_CDL,
ECKD_CMS,
ECKD_LDL,
+ ECKD_LDL_UNLABELED,
} ECKD_IPL_mode_t;
/* utility code below */
diff --git a/pc-bios/s390-ccw/virtio.c b/pc-bios/s390-ccw/virtio.c
index 31b23b0..c0540d1 100644
--- a/pc-bios/s390-ccw/virtio.c
+++ b/pc-bios/s390-ccw/virtio.c
@@ -275,12 +275,14 @@ void virtio_assume_scsi(void)
{
guessed_disk_nature = true;
blk_cfg.blk_size = 512;
+ blk_cfg.physical_block_exp = 0;
}
void virtio_assume_eckd(void)
{
guessed_disk_nature = true;
blk_cfg.blk_size = 4096;
+ blk_cfg.physical_block_exp = 0;
/* this must be here to calculate code segment position */
blk_cfg.geometry.heads = 15;
@@ -290,36 +292,52 @@ void virtio_assume_eckd(void)
bool virtio_disk_is_scsi(void)
{
if (guessed_disk_nature) {
- return (blk_cfg.blk_size == 512);
+ return (virtio_get_block_size() == 512);
}
return (blk_cfg.geometry.heads == 255)
&& (blk_cfg.geometry.sectors == 63)
- && (blk_cfg.blk_size == 512);
+ && (virtio_get_block_size() == 512);
+}
+
+/*
+ * Other supported value pairs, if any, would need to be added here.
+ * Note: head count is always 15.
+ */
+static inline u8 virtio_eckd_sectors_for_block_size(int size)
+{
+ switch (size) {
+ case 512:
+ return 49;
+ case 1024:
+ return 33;
+ case 2048:
+ return 21;
+ case 4096:
+ return 12;
+ }
+ return 0;
}
bool virtio_disk_is_eckd(void)
{
+ const int block_size = virtio_get_block_size();
+
if (guessed_disk_nature) {
- return (blk_cfg.blk_size == 4096);
+ return (block_size == 4096);
}
return (blk_cfg.geometry.heads == 15)
- && (blk_cfg.geometry.sectors == 12)
- && (blk_cfg.blk_size == 4096);
+ && (blk_cfg.geometry.sectors ==
+ virtio_eckd_sectors_for_block_size(block_size));
}
bool virtio_ipl_disk_is_valid(void)
{
- return blk_cfg.blk_size && (virtio_disk_is_scsi() || virtio_disk_is_eckd());
+ return 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;
+ return blk_cfg.blk_size << blk_cfg.physical_block_exp;
}
uint8_t virtio_get_heads(void)
@@ -332,6 +350,12 @@ uint8_t virtio_get_sectors(void)
return blk_cfg.geometry.sectors;
}
+uint64_t virtio_get_blocks(void)
+{
+ return blk_cfg.capacity /
+ (virtio_get_block_size() / VIRTIO_SECTOR_SIZE);
+}
+
void virtio_setup_block(struct subchannel_id schid)
{
struct vq_info_block info;
diff --git a/pc-bios/s390-ccw/virtio.h b/pc-bios/s390-ccw/virtio.h
index f1fb1b0..c23466b 100644
--- a/pc-bios/s390-ccw/virtio.h
+++ b/pc-bios/s390-ccw/virtio.h
@@ -192,9 +192,9 @@ 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 uint64_t virtio_get_blocks(void);
extern int virtio_read_many(ulong sector, void *load_addr, int sec_num);
#define VIRTIO_SECTOR_SIZE 512