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
enable_clock_int(void)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
disable_clock_int(void)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
set_clock_comparator(uint64_t time)61 static inline void set_clock_comparator(uint64_t time)
62 {
63 asm volatile("sckc %0" : : "Q" (time));
64 }
65
check_clock_int(void)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
read_prompt(char * buf,size_t len)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
get_index(void)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
boot_menu_prompt(bool retry)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
get_boot_index(bool * valid_entries)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 */
zipl_print_entry(const char * data,size_t len)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
menu_get_zipl_boot_index(const char * menu_data)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
menu_get_enum_boot_index(bool * valid_entries)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
menu_set_parms(uint8_t boot_menu_flag,uint32_t boot_menu_timeout)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
menu_is_enabled_zipl(void)259 bool menu_is_enabled_zipl(void)
260 {
261 return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL);
262 }
263
menu_is_enabled_enum(void)264 bool menu_is_enabled_enum(void)
265 {
266 return flag & QIPL_FLAG_BM_OPTS_CMD;
267 }
268