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