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 31 static uint8_t flag; 32 static uint64_t timeout; 33 34 static 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 46 static 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 58 static inline void set_clock_comparator(uint64_t time) 59 { 60 asm volatile("sckc %0" : : "Q" (time)); 61 } 62 63 static 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 72 static 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 117 static 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 146 static 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 162 static 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 */ 182 static 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 195 int 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 232 int 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 258 void 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 264 bool menu_is_enabled_zipl(void) 265 { 266 return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL); 267 } 268 269 bool menu_is_enabled_enum(void) 270 { 271 return flag & QIPL_FLAG_BM_OPTS_CMD; 272 } 273