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
  16#define KEYCODE_NO_INP '\0'
  17#define KEYCODE_ESCAPE '\033'
  18#define KEYCODE_BACKSP '\177'
  19#define KEYCODE_ENTER  '\r'
  20
  21/* Offsets from zipl fields to zipl banner start */
  22#define ZIPL_TIMEOUT_OFFSET 138
  23#define ZIPL_FLAG_OFFSET    140
  24
  25#define TOD_CLOCK_MILLISECOND   0x3e8000
  26
  27#define LOW_CORE_EXTERNAL_INT_ADDR   0x86
  28#define CLOCK_COMPARATOR_INT         0X1004
  29
  30static uint8_t flag;
  31static uint64_t timeout;
  32
  33static inline void enable_clock_int(void)
  34{
  35    uint64_t tmp = 0;
  36
  37    asm volatile(
  38        "stctg      0,0,%0\n"
  39        "oi         6+%0, 0x8\n"
  40        "lctlg      0,0,%0"
  41        : : "Q" (tmp) : "memory"
  42    );
  43}
  44
  45static inline void disable_clock_int(void)
  46{
  47    uint64_t tmp = 0;
  48
  49    asm volatile(
  50        "stctg      0,0,%0\n"
  51        "ni         6+%0, 0xf7\n"
  52        "lctlg      0,0,%0"
  53        : : "Q" (tmp) : "memory"
  54    );
  55}
  56
  57static inline void set_clock_comparator(uint64_t time)
  58{
  59    asm volatile("sckc %0" : : "Q" (time));
  60}
  61
  62static inline bool check_clock_int(void)
  63{
  64    uint16_t *code = (uint16_t *)LOW_CORE_EXTERNAL_INT_ADDR;
  65
  66    consume_sclp_int();
  67
  68    return *code == CLOCK_COMPARATOR_INT;
  69}
  70
  71static int read_prompt(char *buf, size_t len)
  72{
  73    char inp[2] = {};
  74    uint8_t idx = 0;
  75    uint64_t time;
  76
  77    if (timeout) {
  78        time = get_clock() + timeout * TOD_CLOCK_MILLISECOND;
  79        set_clock_comparator(time);
  80        enable_clock_int();
  81        timeout = 0;
  82    }
  83
  84    while (!check_clock_int()) {
  85
  86        sclp_read(inp, 1); /* Process only one character at a time */
  87
  88        switch (inp[0]) {
  89        case KEYCODE_NO_INP:
  90        case KEYCODE_ESCAPE:
  91            continue;
  92        case KEYCODE_BACKSP:
  93            if (idx > 0) {
  94                buf[--idx] = 0;
  95                sclp_print("\b \b");
  96            }
  97            continue;
  98        case KEYCODE_ENTER:
  99            disable_clock_int();
 100            return idx;
 101        default:
 102            /* Echo input and add to buffer */
 103            if (idx < len) {
 104                buf[idx++] = inp[0];
 105                sclp_print(inp);
 106            }
 107        }
 108    }
 109
 110    disable_clock_int();
 111    *buf = 0;
 112
 113    return 0;
 114}
 115
 116static int get_index(void)
 117{
 118    char buf[11];
 119    int len;
 120    int i;
 121
 122    memset(buf, 0, sizeof(buf));
 123
 124    sclp_set_write_mask(SCLP_EVENT_MASK_MSG_ASCII, SCLP_EVENT_MASK_MSG_ASCII);
 125
 126    len = read_prompt(buf, sizeof(buf) - 1);
 127
 128    sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII);
 129
 130    /* If no input, boot default */
 131    if (len == 0) {
 132        return 0;
 133    }
 134
 135    /* Check for erroneous input */
 136    for (i = 0; i < len; i++) {
 137        if (!isdigit(buf[i])) {
 138            return -1;
 139        }
 140    }
 141
 142    return atoui(buf);
 143}
 144
 145static void boot_menu_prompt(bool retry)
 146{
 147    char tmp[11];
 148
 149    if (retry) {
 150        sclp_print("\nError: undefined configuration"
 151                   "\nPlease choose:\n");
 152    } else if (timeout > 0) {
 153        sclp_print("Please choose (default will boot in ");
 154        sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp)));
 155        sclp_print(" seconds):\n");
 156    } else {
 157        sclp_print("Please choose:\n");
 158    }
 159}
 160
 161static int get_boot_index(int entries)
 162{
 163    int boot_index;
 164    bool retry = false;
 165    char tmp[5];
 166
 167    do {
 168        boot_menu_prompt(retry);
 169        boot_index = get_index();
 170        retry = true;
 171    } while (boot_index < 0 || boot_index >= entries);
 172
 173    sclp_print("\nBooting entry #");
 174    sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
 175
 176    return boot_index;
 177}
 178
 179static void zipl_println(const char *data, size_t len)
 180{
 181    char buf[len + 2];
 182
 183    ebcdic_to_ascii(data, buf, len);
 184    buf[len] = '\n';
 185    buf[len + 1] = '\0';
 186
 187    sclp_print(buf);
 188}
 189
 190int menu_get_zipl_boot_index(const char *menu_data)
 191{
 192    size_t len;
 193    int entries;
 194    uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET);
 195    uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET);
 196
 197    if (flag == QIPL_FLAG_BM_OPTS_ZIPL) {
 198        if (!zipl_flag) {
 199            return 0; /* Boot default */
 200        }
 201        /* zipl stores timeout as seconds */
 202        timeout = zipl_timeout * 1000;
 203    }
 204
 205    /* Print and count all menu items, including the banner */
 206    for (entries = 0; *menu_data; entries++) {
 207        len = strlen(menu_data);
 208        zipl_println(menu_data, len);
 209        menu_data += len + 1;
 210
 211        if (entries < 2) {
 212            sclp_print("\n");
 213        }
 214    }
 215
 216    sclp_print("\n");
 217    return get_boot_index(entries - 1); /* subtract 1 to exclude banner */
 218}
 219
 220
 221int menu_get_enum_boot_index(int entries)
 222{
 223    char tmp[4];
 224
 225    sclp_print("s390x Enumerated Boot Menu.\n\n");
 226
 227    sclp_print(uitoa(entries, tmp, sizeof(tmp)));
 228    sclp_print(" entries detected. Select from boot index 0 to ");
 229    sclp_print(uitoa(entries - 1, tmp, sizeof(tmp)));
 230    sclp_print(".\n\n");
 231
 232    return get_boot_index(entries);
 233}
 234
 235void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
 236{
 237    flag = boot_menu_flag;
 238    timeout = boot_menu_timeout;
 239}
 240
 241bool menu_is_enabled_zipl(void)
 242{
 243    return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL);
 244}
 245
 246bool menu_is_enabled_enum(void)
 247{
 248    return flag & QIPL_FLAG_BM_OPTS_CMD;
 249}
 250