qemu/pc-bios/s390-ccw/menu.c
<<
>>
Prefs
   1/*
   2 * QEMU S390 Interactive Boot Menu
   3 *
   4 * Copyright 2018 IBM Corp.
   5 * Author: Collin L. Walling <walling@linux.vnet.ibm.com>
   6 *
   7 * This work is licensed under the terms of the GNU GPL, version 2 or (at
   8 * your option) any later version. See the COPYING file in the top-level
   9 * directory.
  10 */
  11
  12#include "libc.h"
  13#include "s390-ccw.h"
  14#include "sclp.h"
  15#include "s390-time.h"
  16
  17#define KEYCODE_NO_INP '\0'
  18#define KEYCODE_ESCAPE '\033'
  19#define KEYCODE_BACKSP '\177'
  20#define KEYCODE_ENTER  '\r'
  21
  22/* Offsets from zipl fields to zipl banner start */
  23#define ZIPL_TIMEOUT_OFFSET 138
  24#define ZIPL_FLAG_OFFSET    140
  25
  26#define TOD_CLOCK_MILLISECOND   0x3e8000
  27
  28#define LOW_CORE_EXTERNAL_INT_ADDR   0x86
  29#define CLOCK_COMPARATOR_INT         0X1004
  30
  31static uint8_t flag;
  32static uint64_t timeout;
  33
  34static inline void enable_clock_int(void)
  35{
  36    uint64_t tmp = 0;
  37
  38    asm volatile(
  39        "stctg      %%c0,%%c0,%0\n"
  40        "oi         6+%0, 0x8\n"
  41        "lctlg      %%c0,%%c0,%0"
  42        : : "Q" (tmp) : "memory"
  43    );
  44}
  45
  46static inline void disable_clock_int(void)
  47{
  48    uint64_t tmp = 0;
  49
  50    asm volatile(
  51        "stctg      %%c0,%%c0,%0\n"
  52        "ni         6+%0, 0xf7\n"
  53        "lctlg      %%c0,%%c0,%0"
  54        : : "Q" (tmp) : "memory"
  55    );
  56}
  57
  58static inline void set_clock_comparator(uint64_t time)
  59{
  60    asm volatile("sckc %0" : : "Q" (time));
  61}
  62
  63static inline bool check_clock_int(void)
  64{
  65    uint16_t *code = (uint16_t *)LOW_CORE_EXTERNAL_INT_ADDR;
  66
  67    consume_sclp_int();
  68
  69    return *code == CLOCK_COMPARATOR_INT;
  70}
  71
  72static int read_prompt(char *buf, size_t len)
  73{
  74    char inp[2] = {};
  75    uint8_t idx = 0;
  76    uint64_t time;
  77
  78    if (timeout) {
  79        time = get_clock() + timeout * TOD_CLOCK_MILLISECOND;
  80        set_clock_comparator(time);
  81        enable_clock_int();
  82        timeout = 0;
  83    }
  84
  85    while (!check_clock_int()) {
  86
  87        sclp_read(inp, 1); /* Process only one character at a time */
  88
  89        switch (inp[0]) {
  90        case KEYCODE_NO_INP:
  91        case KEYCODE_ESCAPE:
  92            continue;
  93        case KEYCODE_BACKSP:
  94            if (idx > 0) {
  95                buf[--idx] = 0;
  96                sclp_print("\b \b");
  97            }
  98            continue;
  99        case KEYCODE_ENTER:
 100            disable_clock_int();
 101            return idx;
 102        default:
 103            /* Echo input and add to buffer */
 104            if (idx < len) {
 105                buf[idx++] = inp[0];
 106                sclp_print(inp);
 107            }
 108        }
 109    }
 110
 111    disable_clock_int();
 112    *buf = 0;
 113
 114    return 0;
 115}
 116
 117static int get_index(void)
 118{
 119    char buf[11];
 120    int len;
 121    int i;
 122
 123    memset(buf, 0, sizeof(buf));
 124
 125    sclp_set_write_mask(SCLP_EVENT_MASK_MSG_ASCII, SCLP_EVENT_MASK_MSG_ASCII);
 126
 127    len = read_prompt(buf, sizeof(buf) - 1);
 128
 129    sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII);
 130
 131    /* If no input, boot default */
 132    if (len == 0) {
 133        return 0;
 134    }
 135
 136    /* Check for erroneous input */
 137    for (i = 0; i < len; i++) {
 138        if (!isdigit((unsigned char)buf[i])) {
 139            return -1;
 140        }
 141    }
 142
 143    return atoui(buf);
 144}
 145
 146static void boot_menu_prompt(bool retry)
 147{
 148    char tmp[11];
 149
 150    if (retry) {
 151        sclp_print("\nError: undefined configuration"
 152                   "\nPlease choose:\n");
 153    } else if (timeout > 0) {
 154        sclp_print("Please choose (default will boot in ");
 155        sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp)));
 156        sclp_print(" seconds):\n");
 157    } else {
 158        sclp_print("Please choose:\n");
 159    }
 160}
 161
 162static int get_boot_index(bool *valid_entries)
 163{
 164    int boot_index;
 165    bool retry = false;
 166    char tmp[5];
 167
 168    do {
 169        boot_menu_prompt(retry);
 170        boot_index = get_index();
 171        retry = true;
 172    } while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES ||
 173             !valid_entries[boot_index]);
 174
 175    sclp_print("\nBooting entry #");
 176    sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
 177
 178    return boot_index;
 179}
 180
 181/* Returns the entry number that was printed */
 182static int zipl_print_entry(const char *data, size_t len)
 183{
 184    char buf[len + 2];
 185
 186    ebcdic_to_ascii(data, buf, len);
 187    buf[len] = '\n';
 188    buf[len + 1] = '\0';
 189
 190    sclp_print(buf);
 191
 192    return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf);
 193}
 194
 195int menu_get_zipl_boot_index(const char *menu_data)
 196{
 197    size_t len;
 198    int entry;
 199    bool valid_entries[MAX_BOOT_ENTRIES] = {false};
 200    uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET);
 201    uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET);
 202
 203    if (flag == QIPL_FLAG_BM_OPTS_ZIPL) {
 204        if (!zipl_flag) {
 205            return 0; /* Boot default */
 206        }
 207        /* zipl stores timeout as seconds */
 208        timeout = zipl_timeout * 1000;
 209    }
 210
 211    /* Print banner */
 212    sclp_print("s390-ccw zIPL Boot Menu\n\n");
 213    menu_data += strlen(menu_data) + 1;
 214
 215    /* Print entries */
 216    while (*menu_data) {
 217        len = strlen(menu_data);
 218        entry = zipl_print_entry(menu_data, len);
 219        menu_data += len + 1;
 220
 221        valid_entries[entry] = true;
 222
 223        if (entry == 0) {
 224            sclp_print("\n");
 225        }
 226    }
 227
 228    sclp_print("\n");
 229    return get_boot_index(valid_entries);
 230}
 231
 232int menu_get_enum_boot_index(bool *valid_entries)
 233{
 234    char tmp[3];
 235    int i;
 236
 237    sclp_print("s390-ccw Enumerated Boot Menu.\n\n");
 238
 239    for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
 240        if (valid_entries[i]) {
 241            if (i < 10) {
 242                sclp_print(" ");
 243            }
 244            sclp_print("[");
 245            sclp_print(uitoa(i, tmp, sizeof(tmp)));
 246            sclp_print("]");
 247            if (i == 0) {
 248                sclp_print(" default\n");
 249            }
 250            sclp_print("\n");
 251        }
 252    }
 253
 254    sclp_print("\n");
 255    return get_boot_index(valid_entries);
 256}
 257
 258void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
 259{
 260    flag = boot_menu_flag;
 261    timeout = boot_menu_timeout;
 262}
 263
 264bool menu_is_enabled_zipl(void)
 265{
 266    return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL);
 267}
 268
 269bool menu_is_enabled_enum(void)
 270{
 271    return flag & QIPL_FLAG_BM_OPTS_CMD;
 272}
 273