xref: /openbmc/qemu/pc-bios/s390-ccw/menu.c (revision dda2441b)
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(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(int 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 >= entries);
172 
173     sclp_print("\nBooting entry #");
174     sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
175 
176     return boot_index;
177 }
178 
179 static void zipl_println(const char *data, size_t len)
180 {
181     char buf[len + 2];
182 
183     ebcdic_to_ascii(data, buf, len);
184     buf[len] = '\n';
185     buf[len + 1] = '\0';
186 
187     sclp_print(buf);
188 }
189 
190 int menu_get_zipl_boot_index(const char *menu_data)
191 {
192     size_t len;
193     int entries;
194     uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET);
195     uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET);
196 
197     if (flag == QIPL_FLAG_BM_OPTS_ZIPL) {
198         if (!zipl_flag) {
199             return 0; /* Boot default */
200         }
201         /* zipl stores timeout as seconds */
202         timeout = zipl_timeout * 1000;
203     }
204 
205     /* Print and count all menu items, including the banner */
206     for (entries = 0; *menu_data; entries++) {
207         len = strlen(menu_data);
208         zipl_println(menu_data, len);
209         menu_data += len + 1;
210 
211         if (entries < 2) {
212             sclp_print("\n");
213         }
214     }
215 
216     sclp_print("\n");
217     return get_boot_index(entries - 1); /* subtract 1 to exclude banner */
218 }
219 
220 
221 int menu_get_enum_boot_index(int entries)
222 {
223     char tmp[4];
224 
225     sclp_print("s390x Enumerated Boot Menu.\n\n");
226 
227     sclp_print(uitoa(entries, tmp, sizeof(tmp)));
228     sclp_print(" entries detected. Select from boot index 0 to ");
229     sclp_print(uitoa(entries - 1, tmp, sizeof(tmp)));
230     sclp_print(".\n\n");
231 
232     return get_boot_index(entries);
233 }
234 
235 void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
236 {
237     flag = boot_menu_flag;
238     timeout = boot_menu_timeout;
239 }
240 
241 bool menu_is_enabled_zipl(void)
242 {
243     return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL);
244 }
245 
246 bool menu_is_enabled_enum(void)
247 {
248     return flag & QIPL_FLAG_BM_OPTS_CMD;
249 }
250