xref: /openbmc/qemu/pc-bios/s390-ccw/menu.c (revision ff5dbf1bc3b81248f4f1c253b586491bc8daeda5)
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 
15 #define KEYCODE_NO_INP '\0'
16 #define KEYCODE_ESCAPE '\033'
17 #define KEYCODE_BACKSP '\177'
18 #define KEYCODE_ENTER  '\r'
19 
20 #define TOD_CLOCK_MILLISECOND   0x3e8000
21 
22 #define LOW_CORE_EXTERNAL_INT_ADDR   0x86
23 #define CLOCK_COMPARATOR_INT         0X1004
24 
25 static uint8_t flag;
26 static uint64_t timeout;
27 
28 static inline void enable_clock_int(void)
29 {
30     uint64_t tmp = 0;
31 
32     asm volatile(
33         "stctg      0,0,%0\n"
34         "oi         6+%0, 0x8\n"
35         "lctlg      0,0,%0"
36         : : "Q" (tmp) : "memory"
37     );
38 }
39 
40 static inline void disable_clock_int(void)
41 {
42     uint64_t tmp = 0;
43 
44     asm volatile(
45         "stctg      0,0,%0\n"
46         "ni         6+%0, 0xf7\n"
47         "lctlg      0,0,%0"
48         : : "Q" (tmp) : "memory"
49     );
50 }
51 
52 static inline void set_clock_comparator(uint64_t time)
53 {
54     asm volatile("sckc %0" : : "Q" (time));
55 }
56 
57 static inline bool check_clock_int(void)
58 {
59     uint16_t *code = (uint16_t *)LOW_CORE_EXTERNAL_INT_ADDR;
60 
61     consume_sclp_int();
62 
63     return *code == CLOCK_COMPARATOR_INT;
64 }
65 
66 static int read_prompt(char *buf, size_t len)
67 {
68     char inp[2] = {};
69     uint8_t idx = 0;
70     uint64_t time;
71 
72     if (timeout) {
73         time = get_clock() + timeout * TOD_CLOCK_MILLISECOND;
74         set_clock_comparator(time);
75         enable_clock_int();
76         timeout = 0;
77     }
78 
79     while (!check_clock_int()) {
80 
81         sclp_read(inp, 1); /* Process only one character at a time */
82 
83         switch (inp[0]) {
84         case KEYCODE_NO_INP:
85         case KEYCODE_ESCAPE:
86             continue;
87         case KEYCODE_BACKSP:
88             if (idx > 0) {
89                 buf[--idx] = 0;
90                 sclp_print("\b \b");
91             }
92             continue;
93         case KEYCODE_ENTER:
94             disable_clock_int();
95             return idx;
96         default:
97             /* Echo input and add to buffer */
98             if (idx < len) {
99                 buf[idx++] = inp[0];
100                 sclp_print(inp);
101             }
102         }
103     }
104 
105     disable_clock_int();
106     *buf = 0;
107 
108     return 0;
109 }
110 
111 static int get_index(void)
112 {
113     char buf[11];
114     int len;
115     int i;
116 
117     memset(buf, 0, sizeof(buf));
118 
119     len = read_prompt(buf, sizeof(buf) - 1);
120 
121     /* If no input, boot default */
122     if (len == 0) {
123         return 0;
124     }
125 
126     /* Check for erroneous input */
127     for (i = 0; i < len; i++) {
128         if (!isdigit(buf[i])) {
129             return -1;
130         }
131     }
132 
133     return atoui(buf);
134 }
135 
136 static void boot_menu_prompt(bool retry)
137 {
138     char tmp[11];
139 
140     if (retry) {
141         sclp_print("\nError: undefined configuration"
142                    "\nPlease choose:\n");
143     } else if (timeout > 0) {
144         sclp_print("Please choose (default will boot in ");
145         sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp)));
146         sclp_print(" seconds):\n");
147     } else {
148         sclp_print("Please choose:\n");
149     }
150 }
151 
152 static int get_boot_index(int entries)
153 {
154     int boot_index;
155     bool retry = false;
156     char tmp[5];
157 
158     do {
159         boot_menu_prompt(retry);
160         boot_index = get_index();
161         retry = true;
162     } while (boot_index < 0 || boot_index >= entries);
163 
164     sclp_print("\nBooting entry #");
165     sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
166 
167     return boot_index;
168 }
169 
170 static void zipl_println(const char *data, size_t len)
171 {
172     char buf[len + 2];
173 
174     ebcdic_to_ascii(data, buf, len);
175     buf[len] = '\n';
176     buf[len + 1] = '\0';
177 
178     sclp_print(buf);
179 }
180 
181 int menu_get_zipl_boot_index(const char *menu_data)
182 {
183     size_t len;
184     int entries;
185 
186     /* Print and count all menu items, including the banner */
187     for (entries = 0; *menu_data; entries++) {
188         len = strlen(menu_data);
189         zipl_println(menu_data, len);
190         menu_data += len + 1;
191 
192         if (entries < 2) {
193             sclp_print("\n");
194         }
195     }
196 
197     sclp_print("\n");
198     return get_boot_index(entries - 1); /* subtract 1 to exclude banner */
199 }
200 
201 void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
202 {
203     flag = boot_menu_flag;
204     timeout = boot_menu_timeout;
205 }
206 
207 bool menu_is_enabled_zipl(void)
208 {
209     return flag & QIPL_FLAG_BM_OPTS_CMD;
210 }
211