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