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((unsigned char)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(bool *valid_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 >= MAX_BOOT_ENTRIES ||
 172             !valid_entries[boot_index]);
 173
 174    sclp_print("\nBooting entry #");
 175    sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
 176
 177    return boot_index;
 178}
 179
 180/* Returns the entry number that was printed */
 181static int zipl_print_entry(const char *data, size_t len)
 182{
 183    char buf[len + 2];
 184
 185    ebcdic_to_ascii(data, buf, len);
 186    buf[len] = '\n';
 187    buf[len + 1] = '\0';
 188
 189    sclp_print(buf);
 190
 191    return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf);
 192}
 193
 194int menu_get_zipl_boot_index(const char *menu_data)
 195{
 196    size_t len;
 197    int entry;
 198    bool valid_entries[MAX_BOOT_ENTRIES] = {false};
 199    uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET);
 200    uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET);
 201
 202    if (flag == QIPL_FLAG_BM_OPTS_ZIPL) {
 203        if (!zipl_flag) {
 204            return 0; /* Boot default */
 205        }
 206        /* zipl stores timeout as seconds */
 207        timeout = zipl_timeout * 1000;
 208    }
 209
 210    /* Print banner */
 211    sclp_print("s390-ccw zIPL Boot Menu\n\n");
 212    menu_data += strlen(menu_data) + 1;
 213
 214    /* Print entries */
 215    while (*menu_data) {
 216        len = strlen(menu_data);
 217        entry = zipl_print_entry(menu_data, len);
 218        menu_data += len + 1;
 219
 220        valid_entries[entry] = true;
 221
 222        if (entry == 0) {
 223            sclp_print("\n");
 224        }
 225    }
 226
 227    sclp_print("\n");
 228    return get_boot_index(valid_entries);
 229}
 230
 231int menu_get_enum_boot_index(bool *valid_entries)
 232{
 233    char tmp[3];
 234    int i;
 235
 236    sclp_print("s390-ccw Enumerated Boot Menu.\n\n");
 237
 238    for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
 239        if (valid_entries[i]) {
 240            if (i < 10) {
 241                sclp_print(" ");
 242            }
 243            sclp_print("[");
 244            sclp_print(uitoa(i, tmp, sizeof(tmp)));
 245            sclp_print("]");
 246            if (i == 0) {
 247                sclp_print(" default\n");
 248            }
 249            sclp_print("\n");
 250        }
 251    }
 252
 253    sclp_print("\n");
 254    return get_boot_index(valid_entries);
 255}
 256
 257void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
 258{
 259    flag = boot_menu_flag;
 260    timeout = boot_menu_timeout;
 261}
 262
 263bool menu_is_enabled_zipl(void)
 264{
 265    return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL);
 266}
 267
 268bool menu_is_enabled_enum(void)
 269{
 270    return flag & QIPL_FLAG_BM_OPTS_CMD;
 271}
 272