qemu/pc-bios/s390-ccw/dasd-ipl.c
Eric Farman d8e5bbdd0d pc-bios: s390x: Ensure Read IPL memory is clean
If, for example, we boot off a virtio device and chreipl to a vfio-ccw
device, the space at lowcore will be non-zero. We build a Read IPL CCW
at address zero, but it will have leftover PSW data that will conflict
with the Format-0 CCW being generated:

0x0: 00080000 80010000
       ------ Ccw0.cda
              -- Ccw0.chainData
                -- Reserved bits

The data address will be overwritten with the correct value (0x0), but
the apparent data chain bit will cause subsequent memory to be used as
the target of the data store, which may not be where we expect (0x0).

Clear out this space when we boot from DASD, so that we know it exists
exactly as we expect.

Signed-off-by: Eric Farman <farman@linux.ibm.com>
Reviewed-by: Jason J. Herne <jjherne@linux.ibm.com>
Reviewed-by: Janosch Frank <frankja@de.ibm.com>
Acked-by: Christian Borntraeger <borntraeger@de.ibm.com>
Acked-by: Cornelia Huck <cohuck@redhat.com>
Message-Id: <20201120160117.59366-2-farman@linux.ibm.com>
Signed-off-by: Thomas Huth <thuth@redhat.com>
2020-11-23 09:48:44 +01:00

239 lines
6.4 KiB
C

/*
* S390 IPL (boot) from a real DASD device via vfio framework.
*
* Copyright (c) 2019 Jason J. Herne <jjherne@us.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.
*/
#include "libc.h"
#include "s390-ccw.h"
#include "s390-arch.h"
#include "dasd-ipl.h"
#include "helper.h"
static char prefix_page[PAGE_SIZE * 2]
__attribute__((__aligned__(PAGE_SIZE * 2)));
static void enable_prefixing(void)
{
memcpy(&prefix_page, lowcore, 4096);
set_prefix(ptr2u32(&prefix_page));
}
static void disable_prefixing(void)
{
set_prefix(0);
/* Copy io interrupt info back to low core */
memcpy((void *)&lowcore->subchannel_id, prefix_page + 0xB8, 12);
}
static bool is_read_tic_ccw_chain(Ccw0 *ccw)
{
Ccw0 *next_ccw = ccw + 1;
return ((ccw->cmd_code == CCW_CMD_DASD_READ ||
ccw->cmd_code == CCW_CMD_DASD_READ_MT) &&
ccw->chain && next_ccw->cmd_code == CCW_CMD_TIC);
}
static bool dynamic_cp_fixup(uint32_t ccw_addr, uint32_t *next_cpa)
{
Ccw0 *cur_ccw = (Ccw0 *)(uint64_t)ccw_addr;
Ccw0 *tic_ccw;
while (true) {
/* Skip over inline TIC (it might not have the chain bit on) */
if (cur_ccw->cmd_code == CCW_CMD_TIC &&
cur_ccw->cda == ptr2u32(cur_ccw) - 8) {
cur_ccw += 1;
continue;
}
if (!cur_ccw->chain) {
break;
}
if (is_read_tic_ccw_chain(cur_ccw)) {
/*
* Breaking a chain of CCWs may alter the semantics or even the
* validity of a channel program. The heuristic implemented below
* seems to work well in practice for the channel programs
* generated by zipl.
*/
tic_ccw = cur_ccw + 1;
*next_cpa = tic_ccw->cda;
cur_ccw->chain = 0;
return true;
}
cur_ccw += 1;
}
return false;
}
static int run_dynamic_ccw_program(SubChannelId schid, uint16_t cutype,
uint32_t cpa)
{
bool has_next;
uint32_t next_cpa = 0;
int rc;
do {
has_next = dynamic_cp_fixup(cpa, &next_cpa);
print_int("executing ccw chain at ", cpa);
enable_prefixing();
rc = do_cio(schid, cutype, cpa, CCW_FMT0);
disable_prefixing();
if (rc) {
break;
}
cpa = next_cpa;
} while (has_next);
return rc;
}
static void make_readipl(void)
{
Ccw0 *ccwIplRead = (Ccw0 *)0x00;
/* Clear out any existing data */
memset(ccwIplRead, 0, sizeof(Ccw0));
/* Create Read IPL ccw at address 0 */
ccwIplRead->cmd_code = CCW_CMD_READ_IPL;
ccwIplRead->cda = 0x00; /* Read into address 0x00 in main memory */
ccwIplRead->chain = 0; /* Chain flag */
ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */
}
static void run_readipl(SubChannelId schid, uint16_t cutype)
{
if (do_cio(schid, cutype, 0x00, CCW_FMT0)) {
panic("dasd-ipl: Failed to run Read IPL channel program\n");
}
}
/*
* The architecture states that IPL1 data should consist of a psw followed by
* format-0 READ and TIC CCWs. Let's sanity check.
*/
static void check_ipl1(void)
{
Ccw0 *ccwread = (Ccw0 *)0x08;
Ccw0 *ccwtic = (Ccw0 *)0x10;
if (ccwread->cmd_code != CCW_CMD_DASD_READ ||
ccwtic->cmd_code != CCW_CMD_TIC) {
panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n");
}
}
static void check_ipl2(uint32_t ipl2_addr)
{
Ccw0 *ccw = u32toptr(ipl2_addr);
if (ipl2_addr == 0x00) {
panic("IPL2 address invalid. Is this disk really bootable?\n");
}
if (ccw->cmd_code == 0x00) {
panic("IPL2 ccw data invalid. Is this disk really bootable?\n");
}
}
static uint32_t read_ipl2_addr(void)
{
Ccw0 *ccwtic = (Ccw0 *)0x10;
return ccwtic->cda;
}
static void ipl1_fixup(void)
{
Ccw0 *ccwSeek = (Ccw0 *) 0x08;
Ccw0 *ccwSearchID = (Ccw0 *) 0x10;
Ccw0 *ccwSearchTic = (Ccw0 *) 0x18;
Ccw0 *ccwRead = (Ccw0 *) 0x20;
CcwSeekData *seekData = (CcwSeekData *) 0x30;
CcwSearchIdData *searchData = (CcwSearchIdData *) 0x38;
/* move IPL1 CCWs to make room for CCWs needed to locate record 2 */
memcpy(ccwRead, (void *)0x08, 16);
/* Disable chaining so we don't TIC to IPL2 channel program */
ccwRead->chain = 0x00;
ccwSeek->cmd_code = CCW_CMD_DASD_SEEK;
ccwSeek->cda = ptr2u32(seekData);
ccwSeek->chain = 1;
ccwSeek->count = sizeof(*seekData);
seekData->reserved = 0x00;
seekData->cyl = 0x00;
seekData->head = 0x00;
ccwSearchID->cmd_code = CCW_CMD_DASD_SEARCH_ID_EQ;
ccwSearchID->cda = ptr2u32(searchData);
ccwSearchID->chain = 1;
ccwSearchID->count = sizeof(*searchData);
searchData->cyl = 0;
searchData->head = 0;
searchData->record = 2;
/* Go back to Search CCW if correct record not yet found */
ccwSearchTic->cmd_code = CCW_CMD_TIC;
ccwSearchTic->cda = ptr2u32(ccwSearchID);
}
static void run_ipl1(SubChannelId schid, uint16_t cutype)
{
uint32_t startAddr = 0x08;
if (do_cio(schid, cutype, startAddr, CCW_FMT0)) {
panic("dasd-ipl: Failed to run IPL1 channel program\n");
}
}
static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
{
if (run_dynamic_ccw_program(schid, cutype, addr)) {
panic("dasd-ipl: Failed to run IPL2 channel program\n");
}
}
/*
* Limitations in vfio-ccw support complicate the IPL process. Details can
* be found in docs/devel/s390-dasd-ipl.txt
*/
void dasd_ipl(SubChannelId schid, uint16_t cutype)
{
PSWLegacy *pswl = (PSWLegacy *) 0x00;
uint32_t ipl2_addr;
/* Construct Read IPL CCW and run it to read IPL1 from boot disk */
make_readipl();
run_readipl(schid, cutype);
ipl2_addr = read_ipl2_addr();
check_ipl1();
/*
* Fixup IPL1 channel program to account for vfio-ccw limitations, then run
* it to read IPL2 channel program from boot disk.
*/
ipl1_fixup();
run_ipl1(schid, cutype);
check_ipl2(ipl2_addr);
/*
* Run IPL2 channel program to read operating system code from boot disk
*/
run_ipl2(schid, cutype, ipl2_addr);
/* Transfer control to the guest operating system */
pswl->mask |= PSW_MASK_EAMODE; /* Force z-mode */
pswl->addr |= PSW_MASK_BAMODE; /* ... */
jump_to_low_kernel();
}