qemu

FORK: QEMU emulator
git clone https://git.neptards.moe/neptards/qemu.git
Log | Files | Refs | Submodules | LICENSE

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 
     17 static char prefix_page[PAGE_SIZE * 2]
     18             __attribute__((__aligned__(PAGE_SIZE * 2)));
     19 
     20 static void enable_prefixing(void)
     21 {
     22     memcpy(&prefix_page, lowcore, 4096);
     23     set_prefix(ptr2u32(&prefix_page));
     24 }
     25 
     26 static 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 
     33 static 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 
     42 static 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 
     75 static 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 
     99 static 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 
    113 static 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  */
    124 static 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 
    135 static 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 
    147 static uint32_t read_ipl2_addr(void)
    148 {
    149     Ccw0 *ccwtic = (Ccw0 *)0x10;
    150 
    151     return ccwtic->cda;
    152 }
    153 
    154 static 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 
    190 static 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 
    199 static 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  */
    210 void 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 }