/* * QEMU S390 Interactive Boot Menu * * Copyright 2018 IBM Corp. * Author: Collin L. Walling * * This work is licensed under the terms of the GNU GPL, version 2 or (at * your option) any later version. See the COPYING file in the top-level * directory. */ #include "libc.h" #include "s390-ccw.h" #include "sclp.h" #define KEYCODE_NO_INP '\0' #define KEYCODE_ESCAPE '\033' #define KEYCODE_BACKSP '\177' #define KEYCODE_ENTER '\r' /* Offsets from zipl fields to zipl banner start */ #define ZIPL_TIMEOUT_OFFSET 138 #define ZIPL_FLAG_OFFSET 140 #define TOD_CLOCK_MILLISECOND 0x3e8000 #define LOW_CORE_EXTERNAL_INT_ADDR 0x86 #define CLOCK_COMPARATOR_INT 0X1004 static uint8_t flag; static uint64_t timeout; static inline void enable_clock_int(void) { uint64_t tmp = 0; asm volatile( "stctg 0,0,%0\n" "oi 6+%0, 0x8\n" "lctlg 0,0,%0" : : "Q" (tmp) : "memory" ); } static inline void disable_clock_int(void) { uint64_t tmp = 0; asm volatile( "stctg 0,0,%0\n" "ni 6+%0, 0xf7\n" "lctlg 0,0,%0" : : "Q" (tmp) : "memory" ); } static inline void set_clock_comparator(uint64_t time) { asm volatile("sckc %0" : : "Q" (time)); } static inline bool check_clock_int(void) { uint16_t *code = (uint16_t *)LOW_CORE_EXTERNAL_INT_ADDR; consume_sclp_int(); return *code == CLOCK_COMPARATOR_INT; } static int read_prompt(char *buf, size_t len) { char inp[2] = {}; uint8_t idx = 0; uint64_t time; if (timeout) { time = get_clock() + timeout * TOD_CLOCK_MILLISECOND; set_clock_comparator(time); enable_clock_int(); timeout = 0; } while (!check_clock_int()) { sclp_read(inp, 1); /* Process only one character at a time */ switch (inp[0]) { case KEYCODE_NO_INP: case KEYCODE_ESCAPE: continue; case KEYCODE_BACKSP: if (idx > 0) { buf[--idx] = 0; sclp_print("\b \b"); } continue; case KEYCODE_ENTER: disable_clock_int(); return idx; default: /* Echo input and add to buffer */ if (idx < len) { buf[idx++] = inp[0]; sclp_print(inp); } } } disable_clock_int(); *buf = 0; return 0; } static int get_index(void) { char buf[11]; int len; int i; memset(buf, 0, sizeof(buf)); sclp_set_write_mask(SCLP_EVENT_MASK_MSG_ASCII, SCLP_EVENT_MASK_MSG_ASCII); len = read_prompt(buf, sizeof(buf) - 1); sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII); /* If no input, boot default */ if (len == 0) { return 0; } /* Check for erroneous input */ for (i = 0; i < len; i++) { if (!isdigit(buf[i])) { return -1; } } return atoui(buf); } static void boot_menu_prompt(bool retry) { char tmp[11]; if (retry) { sclp_print("\nError: undefined configuration" "\nPlease choose:\n"); } else if (timeout > 0) { sclp_print("Please choose (default will boot in "); sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp))); sclp_print(" seconds):\n"); } else { sclp_print("Please choose:\n"); } } static int get_boot_index(bool *valid_entries) { int boot_index; bool retry = false; char tmp[5]; do { boot_menu_prompt(retry); boot_index = get_index(); retry = true; } while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES || !valid_entries[boot_index]); sclp_print("\nBooting entry #"); sclp_print(uitoa(boot_index, tmp, sizeof(tmp))); return boot_index; } /* Returns the entry number that was printed */ static int zipl_print_entry(const char *data, size_t len) { char buf[len + 2]; ebcdic_to_ascii(data, buf, len); buf[len] = '\n'; buf[len + 1] = '\0'; sclp_print(buf); return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf); } int menu_get_zipl_boot_index(const char *menu_data) { size_t len; int entry; bool valid_entries[MAX_BOOT_ENTRIES] = {false}; uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET); uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET); if (flag == QIPL_FLAG_BM_OPTS_ZIPL) { if (!zipl_flag) { return 0; /* Boot default */ } /* zipl stores timeout as seconds */ timeout = zipl_timeout * 1000; } /* Print banner */ sclp_print("s390-ccw zIPL Boot Menu\n\n"); menu_data += strlen(menu_data) + 1; /* Print entries */ while (*menu_data) { len = strlen(menu_data); entry = zipl_print_entry(menu_data, len); menu_data += len + 1; valid_entries[entry] = true; if (entry == 0) { sclp_print("\n"); } } sclp_print("\n"); return get_boot_index(valid_entries); } int menu_get_enum_boot_index(bool *valid_entries) { char tmp[3]; int i; sclp_print("s390-ccw Enumerated Boot Menu.\n\n"); for (i = 0; i < MAX_BOOT_ENTRIES; i++) { if (valid_entries[i]) { if (i < 10) { sclp_print(" "); } sclp_print("["); sclp_print(uitoa(i, tmp, sizeof(tmp))); sclp_print("]"); if (i == 0) { sclp_print(" default\n"); } sclp_print("\n"); } } sclp_print("\n"); return get_boot_index(valid_entries); } void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout) { flag = boot_menu_flag; timeout = boot_menu_timeout; } bool menu_is_enabled_zipl(void) { return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL); } bool menu_is_enabled_enum(void) { return flag & QIPL_FLAG_BM_OPTS_CMD; }