xref: /openbmc/qemu/pc-bios/s390-ccw/menu.c (revision 7d87775f)
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 
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 
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 
61 static inline void set_clock_comparator(uint64_t time)
62 {
63     asm volatile("sckc %0" : : "Q" (time));
64 }
65 
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 
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 
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 
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 
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 */
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 
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 
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 
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 
259 bool menu_is_enabled_zipl(void)
260 {
261     return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL);
262 }
263 
264 bool menu_is_enabled_enum(void)
265 {
266     return flag & QIPL_FLAG_BM_OPTS_CMD;
267 }
268