xref: /qemu/pc-bios/s390-ccw/menu.c (revision 12b35405)
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      0,0,%0\n"
40         "oi         6+%0, 0x8\n"
41         "lctlg      0,0,%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      0,0,%0\n"
52         "ni         6+%0, 0xf7\n"
53         "lctlg      0,0,%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