From nobody Tue Feb 10 04:03:42 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass(p=none dis=none) header.from=redhat.com ARC-Seal: i=1; a=rsa-sha256; t=1678109532; cv=none; d=zohomail.com; s=zohoarc; b=PJh8v94tOwoq5nwwhsnXk/XpVSPcLoazkOgVbiZcUSnSGtzhRgk56y1aNi2+fXZoTeFs2yqDyKOk4Z95nt1lXHQ3XkE1QNJlM0+77z+sOUMX1S+TNhO2AsJamqqZwyOCbvRipjNhz3/7BUHbrAu2StxWG/mxWAipN0WF2gqoEvM= ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=zohomail.com; s=zohoarc; t=1678109532; h=Content-Transfer-Encoding:Date:From:In-Reply-To:List-Subscribe:List-Post:List-Id:List-Archive:List-Help:List-Unsubscribe:MIME-Version:Message-ID:References:Sender:Subject:To; bh=2Yyrt6CiWHoBljSro16ah2Kmc5MCn3FrjeEBRMmgB8A=; b=lF1lpLxC7Vz05BA+EDsRjjJCgKw3dnZbMFrRE38ZrSTPaQccTXd4mwm5vM9RvXmH/DDdK68HP6SJS7k3muyfBuDttE967R28MQgS/XB68ftaRw1jGgeTeYKLquu0hmIOKt28Nih557jLAl8zh8om7CmvEGl+c/XhcKRTqbnCRAI= ARC-Authentication-Results: i=1; mx.zohomail.com; dkim=pass; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org; dmarc=pass header.from= (p=none dis=none) Return-Path: Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 1678109532468444.26700630968185; Mon, 6 Mar 2023 05:32:12 -0800 (PST) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1pZAw9-00045M-47; Mon, 06 Mar 2023 08:31:33 -0500 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pZAv5-0002KX-Ba for qemu-devel@nongnu.org; Mon, 06 Mar 2023 08:30:30 -0500 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1pZAv3-0007Ve-63 for qemu-devel@nongnu.org; Mon, 06 Mar 2023 08:30:27 -0500 Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id us-mta-530-fmyLP1n4MQK9J8F-UlrpJw-1; Mon, 06 Mar 2023 08:30:18 -0500 Received: from smtp.corp.redhat.com (int-mx02.intmail.prod.int.rdu2.redhat.com [10.11.54.2]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 7FB9185A5A3; Mon, 6 Mar 2023 13:30:17 +0000 (UTC) Received: from thuth.com (unknown [10.39.193.101]) by smtp.corp.redhat.com (Postfix) with ESMTP id B9E454014CF3; Mon, 6 Mar 2023 13:30:16 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1678109424; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=2Yyrt6CiWHoBljSro16ah2Kmc5MCn3FrjeEBRMmgB8A=; b=B7EG+YiXYRHHN78aNvkgmD1zPeonZN85/cq8j1t9a+w5SgTlbZDFvJcRrPWQ1Ga/6+AaR1 c7cVzNBQLuHZfCRhuaBI4X0M9C9fFOD7qhWX46jJ7BNIE25QD9MN7/DettUqkzkize55FY 1HMeGfCUTd/IlIfblWRgE6MHoGNw3RA= X-MC-Unique: fmyLP1n4MQK9J8F-UlrpJw-1 From: Thomas Huth To: qemu-devel@nongnu.org, Peter Maydell Subject: [PULL 15/16] pc-bios: Add support for List-Directed IPL from ECKD DASD Date: Mon, 6 Mar 2023 14:30:01 +0100 Message-Id: <20230306133002.418421-16-thuth@redhat.com> In-Reply-To: <20230306133002.418421-1-thuth@redhat.com> References: <20230306133002.418421-1-thuth@redhat.com> MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable X-Scanned-By: MIMEDefang 3.1 on 10.11.54.2 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists.gnu.org; Received-SPF: pass client-ip=170.10.133.124; envelope-from=thuth@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -20 X-Spam_score: -2.1 X-Spam_bar: -- X-Spam_report: (-2.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.001, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H2=-0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZohoMail-DKIM: pass (identity @redhat.com) X-ZM-MESSAGEID: 1678109532795100001 Content-Type: text/plain; charset="utf-8" From: Jared Rossi Check for a List Directed IPL Boot Record, which would supersede the CCW ty= pe entries. If the record is valid, proceed to use the new style pointers and perform LD-IPL. Each block pointer is interpreted as either an LD-IPL pointer or a legacy CCW pointer depending on the type of IPL initiated. In either case CCW- or LD-IPL is transparent to the user and will boot the = same image regardless of which set of pointers is used. Because the interactive = boot menu is only written with the old style pointers, the menu will be disabled= for List Directed IPL from ECKD DASD. If the LD-IPL fails, retry the IPL using the CCW type pointers. If no LD-IPL boot record is found, simply perform CCW type IPL as usual. Signed-off-by: Jared Rossi Message-Id: <20230221174548.1866861-2-jrossi@linux.ibm.com> [thuth: Drop some superfluous parantheses] Signed-off-by: Thomas Huth --- pc-bios/s390-ccw/bootmap.h | 30 ++++++- pc-bios/s390-ccw/bootmap.c | 157 ++++++++++++++++++++++++++++--------- 2 files changed, 148 insertions(+), 39 deletions(-) diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h index 3946aa3f8d..d4690a88c2 100644 --- a/pc-bios/s390-ccw/bootmap.h +++ b/pc-bios/s390-ccw/bootmap.h @@ -45,9 +45,23 @@ typedef struct EckdBlockPtr { * it's 0 for TablePtr, ScriptPtr, and SectionPtr */ } __attribute__ ((packed)) EckdBlockPtr; =20 -typedef struct ExtEckdBlockPtr { +typedef struct LdEckdCHS { + uint32_t cylinder; + uint8_t head; + uint8_t sector; +} __attribute__ ((packed)) LdEckdCHS; + +typedef struct LdEckdBlockPtr { + LdEckdCHS chs; /* cylinder/head/sector is an address of the block */ + uint8_t reserved[4]; + uint16_t count; + uint32_t pad; +} __attribute__ ((packed)) LdEckdBlockPtr; + +/* bptr is used for CCW type IPL, while ldptr is for list-directed IPL */ +typedef union ExtEckdBlockPtr { EckdBlockPtr bptr; - uint8_t reserved[8]; + LdEckdBlockPtr ldptr; } __attribute__ ((packed)) ExtEckdBlockPtr; =20 typedef union BootMapPointer { @@ -57,6 +71,15 @@ typedef union BootMapPointer { ExtEckdBlockPtr xeckd; } __attribute__ ((packed)) BootMapPointer; =20 +typedef struct BootRecord { + uint8_t magic[4]; + uint32_t version; + uint64_t res1; + BootMapPointer pgt; + uint8_t reserved[510 - 32]; + uint16_t os_id; +} __attribute__ ((packed)) BootRecord; + /* aka Program Table */ typedef struct BootMapTable { uint8_t magic[4]; @@ -292,7 +315,8 @@ typedef struct IplVolumeLabel { struct { unsigned char key[4]; /* =3D=3D "VOL1" */ unsigned char volser[6]; - unsigned char reserved[6]; + unsigned char reserved[64]; + EckdCHS br; /* Location of Boot Record for list-directed IPL */ } f; }; } __attribute__((packed)) IplVolumeLabel; diff --git a/pc-bios/s390-ccw/bootmap.c b/pc-bios/s390-ccw/bootmap.c index 994e59c0b0..a2137449dc 100644 --- a/pc-bios/s390-ccw/bootmap.c +++ b/pc-bios/s390-ccw/bootmap.c @@ -72,42 +72,74 @@ static inline void verify_boot_info(BootInfo *bip) "Bad block size in zIPL section of the 1st record."); } =20 -static block_number_t eckd_block_num(EckdCHS *chs) +static void eckd_format_chs(ExtEckdBlockPtr *ptr, bool ldipl, + uint64_t *c, + uint64_t *h, + uint64_t *s) +{ + if (ldipl) { + *c =3D ptr->ldptr.chs.cylinder; + *h =3D ptr->ldptr.chs.head; + *s =3D ptr->ldptr.chs.sector; + } else { + *c =3D ptr->bptr.chs.cylinder; + *h =3D ptr->bptr.chs.head; + *s =3D ptr->bptr.chs.sector; + } +} + +static block_number_t eckd_chs_to_block(uint64_t c, uint64_t h, uint64_t s) { const uint64_t sectors =3D virtio_get_sectors(); const uint64_t heads =3D virtio_get_heads(); - const uint64_t cylinder =3D chs->cylinder - + ((chs->head & 0xfff0) << 12); - const uint64_t head =3D chs->head & 0x000f; + const uint64_t cylinder =3D c + ((h & 0xfff0) << 12); + const uint64_t head =3D h & 0x000f; const block_number_t block =3D sectors * heads * cylinder + sectors * head - + chs->sector - - 1; /* block nr starts with zero */ + + s - 1; /* block nr starts with zero */ return block; } =20 -static bool eckd_valid_address(BootMapPointer *p) +static block_number_t eckd_block_num(EckdCHS *chs) { - const uint64_t head =3D p->eckd.chs.head & 0x000f; + return eckd_chs_to_block(chs->cylinder, chs->head, chs->sector); +} + +static block_number_t gen_eckd_block_num(ExtEckdBlockPtr *ptr, bool ldipl) +{ + uint64_t cyl, head, sec; + eckd_format_chs(ptr, ldipl, &cyl, &head, &sec); + return eckd_chs_to_block(cyl, head, sec); +} =20 +static bool eckd_valid_chs(uint64_t cyl, uint64_t head, uint64_t sector) +{ if (head >=3D virtio_get_heads() - || p->eckd.chs.sector > virtio_get_sectors() - || p->eckd.chs.sector <=3D 0) { + || sector > virtio_get_sectors() + || sector <=3D 0) { return false; } =20 if (!virtio_guessed_disk_nature() && - eckd_block_num(&p->eckd.chs) >=3D virtio_get_blocks()) { + eckd_chs_to_block(cyl, head, sector) >=3D virtio_get_blocks()) { return false; } =20 return true; } =20 -static block_number_t load_eckd_segments(block_number_t blk, uint64_t *add= ress) +static bool eckd_valid_address(ExtEckdBlockPtr *ptr, bool ldipl) +{ + uint64_t cyl, head, sec; + eckd_format_chs(ptr, ldipl, &cyl, &head, &sec); + return eckd_valid_chs(cyl, head, sec); +} + +static block_number_t load_eckd_segments(block_number_t blk, bool ldipl, + uint64_t *address) { block_number_t block_nr; - int j, rc; + int j, rc, count; BootMapPointer *bprs =3D (void *)_bprs; bool more_data; =20 @@ -117,7 +149,7 @@ static block_number_t load_eckd_segments(block_number_t= blk, uint64_t *address) do { more_data =3D false; for (j =3D 0;; j++) { - block_nr =3D eckd_block_num(&bprs[j].xeckd.bptr.chs); + block_nr =3D gen_eckd_block_num(&bprs[j].xeckd, ldipl); if (is_null_block_number(block_nr)) { /* end of chunk */ break; } @@ -129,11 +161,26 @@ static block_number_t load_eckd_segments(block_number= _t blk, uint64_t *address) break; } =20 - IPL_assert(block_size_ok(bprs[j].xeckd.bptr.size), + /* List directed pointer does not store block size */ + IPL_assert(ldipl || block_size_ok(bprs[j].xeckd.bptr.size), "bad chunk block size"); - IPL_assert(eckd_valid_address(&bprs[j]), "bad chunk ECKD addr"= ); =20 - if ((bprs[j].xeckd.bptr.count =3D=3D 0) && unused_space(&(bprs= [j+1]), + if (!eckd_valid_address(&bprs[j].xeckd, ldipl)) { + /* + * If an invalid address is found during LD-IPL then break= and + * retry as CCW + */ + IPL_assert(ldipl, "bad chunk ECKD addr"); + break; + } + + if (ldipl) { + count =3D bprs[j].xeckd.ldptr.count; + } else { + count =3D bprs[j].xeckd.bptr.count; + } + + if (count =3D=3D 0 && unused_space(&bprs[j + 1], sizeof(EckdBlockPtr))) { /* This is a "continue" pointer. * This ptr should be the last one in the current @@ -149,11 +196,10 @@ static block_number_t load_eckd_segments(block_number= _t blk, uint64_t *address) /* Load (count+1) blocks of code at (block_nr) * to memory (address). */ - rc =3D virtio_read_many(block_nr, (void *)(*address), - bprs[j].xeckd.bptr.count+1); + rc =3D virtio_read_many(block_nr, (void *)(*address), count + = 1); IPL_assert(rc =3D=3D 0, "code chunk read failed"); =20 - *address +=3D (bprs[j].xeckd.bptr.count+1) * virtio_get_block_= size(); + *address +=3D (count + 1) * virtio_get_block_size(); } } while (more_data); return block_nr; @@ -237,8 +283,10 @@ static void run_eckd_boot_script(block_number_t bmt_bl= ock_nr, uint64_t address; BootMapTable *bmt =3D (void *)sec; BootMapScript *bms =3D (void *)sec; + /* The S1B block number is NULL_BLOCK_NR if and only if it's an LD-IPL= */ + bool ldipl =3D (s1b_block_nr =3D=3D NULL_BLOCK_NR); =20 - if (menu_is_enabled_zipl()) { + if (menu_is_enabled_zipl() && !ldipl) { loadparm =3D eckd_get_boot_menu_index(s1b_block_nr); } =20 @@ -249,7 +297,7 @@ static void run_eckd_boot_script(block_number_t bmt_blo= ck_nr, memset(sec, FREE_SPACE_FILLER, sizeof(sec)); read_block(bmt_block_nr, sec, "Cannot read Boot Map Table"); =20 - block_nr =3D eckd_block_num(&bmt->entry[loadparm].xeckd.bptr.chs); + block_nr =3D gen_eckd_block_num(&bmt->entry[loadparm].xeckd, ldipl); IPL_assert(block_nr !=3D -1, "Cannot find Boot Map Table Entry"); =20 memset(sec, FREE_SPACE_FILLER, sizeof(sec)); @@ -264,13 +312,18 @@ static void run_eckd_boot_script(block_number_t bmt_b= lock_nr, } =20 address =3D bms->entry[i].address.load_address; - block_nr =3D eckd_block_num(&bms->entry[i].blkptr.xeckd.bptr.chs); + block_nr =3D gen_eckd_block_num(&bms->entry[i].blkptr.xeckd, ldipl= ); =20 do { - block_nr =3D load_eckd_segments(block_nr, &address); + block_nr =3D load_eckd_segments(block_nr, ldipl, &address); } while (block_nr !=3D -1); } =20 + if (ldipl && bms->entry[i].type !=3D BOOT_SCRIPT_EXEC) { + /* Abort LD-IPL and retry as CCW-IPL */ + return; + } + IPL_assert(bms->entry[i].type =3D=3D BOOT_SCRIPT_EXEC, "Unknown script entry type"); write_reset_psw(bms->entry[i].address.load_address); /* no return */ @@ -380,6 +433,23 @@ static void ipl_eckd_ldl(ECKD_IPL_mode_t mode) /* no return */ } =20 +static block_number_t eckd_find_bmt(ExtEckdBlockPtr *ptr) +{ + block_number_t blockno; + uint8_t tmp_sec[MAX_SECTOR_SIZE]; + BootRecord *br; + + blockno =3D gen_eckd_block_num(ptr, 0); + read_block(blockno, tmp_sec, "Cannot read boot record"); + br =3D (BootRecord *)tmp_sec; + if (!magic_match(br->magic, ZIPL_MAGIC)) { + /* If the boot record is invalid, return and try CCW-IPL instead */ + return NULL_BLOCK_NR; + } + + return gen_eckd_block_num(&br->pgt.xeckd, 1); +} + static void print_eckd_msg(void) { char msg[] =3D "Using ECKD scheme (block size *****), "; @@ -401,28 +471,43 @@ static void print_eckd_msg(void) =20 static void ipl_eckd(void) { - XEckdMbr *mbr =3D (void *)sec; - LDL_VTOC *vlbl =3D (void *)sec; + IplVolumeLabel *vlbl =3D (void *)sec; + LDL_VTOC *vtoc =3D (void *)sec; + block_number_t ldipl_bmt; /* Boot Map Table for List-Directed IPL */ =20 print_eckd_msg(); =20 - /* Grab the MBR again */ + /* Block 2 can contain either the CDL VOL1 label or the LDL VTOC */ memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(0, mbr, "Cannot read block 0 on DASD"); + read_block(2, vlbl, "Cannot read block 2"); =20 - if (magic_match(mbr->magic, IPL1_MAGIC)) { - ipl_eckd_cdl(); /* only returns in case of error */ - return; + /* + * First check for a list-directed-format pointer which would + * supersede the CCW pointer. + */ + if (eckd_valid_address((ExtEckdBlockPtr *)&vlbl->f.br, 0)) { + ldipl_bmt =3D eckd_find_bmt((ExtEckdBlockPtr *)&vlbl->f.br); + if (ldipl_bmt) { + sclp_print("List-Directed\n"); + /* LD-IPL does not use the S1B bock, just make it NULL */ + run_eckd_boot_script(ldipl_bmt, NULL_BLOCK_NR); + /* Only return in error, retry as CCW-IPL */ + sclp_print("Retrying IPL "); + print_eckd_msg(); + } + memset(sec, FREE_SPACE_FILLER, sizeof(sec)); + read_block(2, vtoc, "Cannot read block 2"); } =20 - /* LDL/CMS? */ - memset(sec, FREE_SPACE_FILLER, sizeof(sec)); - read_block(2, vlbl, "Cannot read block 2"); + /* Not list-directed */ + if (magic_match(vtoc->magic, VOL1_MAGIC)) { + ipl_eckd_cdl(); /* may return in error */ + } =20 - if (magic_match(vlbl->magic, CMS1_MAGIC)) { + if (magic_match(vtoc->magic, CMS1_MAGIC)) { ipl_eckd_ldl(ECKD_CMS); /* no return */ } - if (magic_match(vlbl->magic, LNX1_MAGIC)) { + if (magic_match(vtoc->magic, LNX1_MAGIC)) { ipl_eckd_ldl(ECKD_LDL); /* no return */ } =20 --=20 2.31.1