qemu/pc-bios/s390-ccw/dasd-ipl.c
<<
>>
Prefs
   1/*
   2 * S390 IPL (boot) from a real DASD device via vfio framework.
   3 *
   4 * Copyright (c) 2019 Jason J. Herne <jjherne@us.ibm.com>
   5 *
   6 * This work is licensed under the terms of the GNU GPL, version 2 or (at
   7 * your option) any later version. See the COPYING file in the top-level
   8 * directory.
   9 */
  10
  11#include "libc.h"
  12#include "s390-ccw.h"
  13#include "s390-arch.h"
  14#include "dasd-ipl.h"
  15#include "helper.h"
  16
  17static char prefix_page[PAGE_SIZE * 2]
  18            __attribute__((__aligned__(PAGE_SIZE * 2)));
  19
  20static void enable_prefixing(void)
  21{
  22    memcpy(&prefix_page, lowcore, 4096);
  23    set_prefix(ptr2u32(&prefix_page));
  24}
  25
  26static void disable_prefixing(void)
  27{
  28    set_prefix(0);
  29    /* Copy io interrupt info back to low core */
  30    memcpy((void *)&lowcore->subchannel_id, prefix_page + 0xB8, 12);
  31}
  32
  33static bool is_read_tic_ccw_chain(Ccw0 *ccw)
  34{
  35    Ccw0 *next_ccw = ccw + 1;
  36
  37    return ((ccw->cmd_code == CCW_CMD_DASD_READ ||
  38            ccw->cmd_code == CCW_CMD_DASD_READ_MT) &&
  39            ccw->chain && next_ccw->cmd_code == CCW_CMD_TIC);
  40}
  41
  42static bool dynamic_cp_fixup(uint32_t ccw_addr, uint32_t  *next_cpa)
  43{
  44    Ccw0 *cur_ccw = (Ccw0 *)(uint64_t)ccw_addr;
  45    Ccw0 *tic_ccw;
  46
  47    while (true) {
  48        /* Skip over inline TIC (it might not have the chain bit on)  */
  49        if (cur_ccw->cmd_code == CCW_CMD_TIC &&
  50            cur_ccw->cda == ptr2u32(cur_ccw) - 8) {
  51            cur_ccw += 1;
  52            continue;
  53        }
  54
  55        if (!cur_ccw->chain) {
  56            break;
  57        }
  58        if (is_read_tic_ccw_chain(cur_ccw)) {
  59            /*
  60             * Breaking a chain of CCWs may alter the semantics or even the
  61             * validity of a channel program. The heuristic implemented below
  62             * seems to work well in practice for the channel programs
  63             * generated by zipl.
  64             */
  65            tic_ccw = cur_ccw + 1;
  66            *next_cpa = tic_ccw->cda;
  67            cur_ccw->chain = 0;
  68            return true;
  69        }
  70        cur_ccw += 1;
  71    }
  72    return false;
  73}
  74
  75static int run_dynamic_ccw_program(SubChannelId schid, uint16_t cutype,
  76                                   uint32_t cpa)
  77{
  78    bool has_next;
  79    uint32_t next_cpa = 0;
  80    int rc;
  81
  82    do {
  83        has_next = dynamic_cp_fixup(cpa, &next_cpa);
  84
  85        print_int("executing ccw chain at ", cpa);
  86        enable_prefixing();
  87        rc = do_cio(schid, cutype, cpa, CCW_FMT0);
  88        disable_prefixing();
  89
  90        if (rc) {
  91            break;
  92        }
  93        cpa = next_cpa;
  94    } while (has_next);
  95
  96    return rc;
  97}
  98
  99static void make_readipl(void)
 100{
 101    Ccw0 *ccwIplRead = (Ccw0 *)0x00;
 102
 103    /* Clear out any existing data */
 104    memset(ccwIplRead, 0, sizeof(Ccw0));
 105
 106    /* Create Read IPL ccw at address 0 */
 107    ccwIplRead->cmd_code = CCW_CMD_READ_IPL;
 108    ccwIplRead->cda = 0x00; /* Read into address 0x00 in main memory */
 109    ccwIplRead->chain = 0; /* Chain flag */
 110    ccwIplRead->count = 0x18; /* Read 0x18 bytes of data */
 111}
 112
 113static void run_readipl(SubChannelId schid, uint16_t cutype)
 114{
 115    if (do_cio(schid, cutype, 0x00, CCW_FMT0)) {
 116        panic("dasd-ipl: Failed to run Read IPL channel program\n");
 117    }
 118}
 119
 120/*
 121 * The architecture states that IPL1 data should consist of a psw followed by
 122 * format-0 READ and TIC CCWs. Let's sanity check.
 123 */
 124static void check_ipl1(void)
 125{
 126    Ccw0 *ccwread = (Ccw0 *)0x08;
 127    Ccw0 *ccwtic = (Ccw0 *)0x10;
 128
 129    if (ccwread->cmd_code != CCW_CMD_DASD_READ ||
 130        ccwtic->cmd_code != CCW_CMD_TIC) {
 131        panic("dasd-ipl: IPL1 data invalid. Is this disk really bootable?\n");
 132    }
 133}
 134
 135static void check_ipl2(uint32_t ipl2_addr)
 136{
 137    Ccw0 *ccw = u32toptr(ipl2_addr);
 138
 139    if (ipl2_addr == 0x00) {
 140        panic("IPL2 address invalid. Is this disk really bootable?\n");
 141    }
 142    if (ccw->cmd_code == 0x00) {
 143        panic("IPL2 ccw data invalid. Is this disk really bootable?\n");
 144    }
 145}
 146
 147static uint32_t read_ipl2_addr(void)
 148{
 149    Ccw0 *ccwtic = (Ccw0 *)0x10;
 150
 151    return ccwtic->cda;
 152}
 153
 154static void ipl1_fixup(void)
 155{
 156    Ccw0 *ccwSeek = (Ccw0 *) 0x08;
 157    Ccw0 *ccwSearchID = (Ccw0 *) 0x10;
 158    Ccw0 *ccwSearchTic = (Ccw0 *) 0x18;
 159    Ccw0 *ccwRead = (Ccw0 *) 0x20;
 160    CcwSeekData *seekData = (CcwSeekData *) 0x30;
 161    CcwSearchIdData *searchData = (CcwSearchIdData *) 0x38;
 162
 163    /* move IPL1 CCWs to make room for CCWs needed to locate record 2 */
 164    memcpy(ccwRead, (void *)0x08, 16);
 165
 166    /* Disable chaining so we don't TIC to IPL2 channel program */
 167    ccwRead->chain = 0x00;
 168
 169    ccwSeek->cmd_code = CCW_CMD_DASD_SEEK;
 170    ccwSeek->cda = ptr2u32(seekData);
 171    ccwSeek->chain = 1;
 172    ccwSeek->count = sizeof(*seekData);
 173    seekData->reserved = 0x00;
 174    seekData->cyl = 0x00;
 175    seekData->head = 0x00;
 176
 177    ccwSearchID->cmd_code = CCW_CMD_DASD_SEARCH_ID_EQ;
 178    ccwSearchID->cda = ptr2u32(searchData);
 179    ccwSearchID->chain = 1;
 180    ccwSearchID->count = sizeof(*searchData);
 181    searchData->cyl = 0;
 182    searchData->head = 0;
 183    searchData->record = 2;
 184
 185    /* Go back to Search CCW if correct record not yet found */
 186    ccwSearchTic->cmd_code = CCW_CMD_TIC;
 187    ccwSearchTic->cda = ptr2u32(ccwSearchID);
 188}
 189
 190static void run_ipl1(SubChannelId schid, uint16_t cutype)
 191 {
 192    uint32_t startAddr = 0x08;
 193
 194    if (do_cio(schid, cutype, startAddr, CCW_FMT0)) {
 195        panic("dasd-ipl: Failed to run IPL1 channel program\n");
 196    }
 197}
 198
 199static void run_ipl2(SubChannelId schid, uint16_t cutype, uint32_t addr)
 200{
 201    if (run_dynamic_ccw_program(schid, cutype, addr)) {
 202        panic("dasd-ipl: Failed to run IPL2 channel program\n");
 203    }
 204}
 205
 206/*
 207 * Limitations in vfio-ccw support complicate the IPL process. Details can
 208 * be found in docs/devel/s390-dasd-ipl.rst
 209 */
 210void dasd_ipl(SubChannelId schid, uint16_t cutype)
 211{
 212    PSWLegacy *pswl = (PSWLegacy *) 0x00;
 213    uint32_t ipl2_addr;
 214
 215    /* Construct Read IPL CCW and run it to read IPL1 from boot disk */
 216    make_readipl();
 217    run_readipl(schid, cutype);
 218    ipl2_addr = read_ipl2_addr();
 219    check_ipl1();
 220
 221    /*
 222     * Fixup IPL1 channel program to account for vfio-ccw limitations, then run
 223     * it to read IPL2 channel program from boot disk.
 224     */
 225    ipl1_fixup();
 226    run_ipl1(schid, cutype);
 227    check_ipl2(ipl2_addr);
 228
 229    /*
 230     * Run IPL2 channel program to read operating system code from boot disk
 231     */
 232    run_ipl2(schid, cutype, ipl2_addr);
 233
 234    /* Transfer control to the guest operating system */
 235    pswl->mask |= PSW_MASK_EAMODE;   /* Force z-mode */
 236    pswl->addr |= PSW_MASK_BAMODE;   /* ...          */
 237    jump_to_low_kernel();
 238}
 239