cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

dasd-ipl.c (6510B)


      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}