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