]>
Commit | Line | Data |
---|---|---|
9eaa654a CW |
1 | /* |
2 | * QEMU S390 Interactive Boot Menu | |
3 | * | |
4 | * Copyright 2018 IBM Corp. | |
5 | * Author: Collin L. Walling <[email protected]> | |
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" | |
dbf2091a | 14 | #include "sclp.h" |
9eaa654a | 15 | |
ff5dbf1b CW |
16 | #define KEYCODE_NO_INP '\0' |
17 | #define KEYCODE_ESCAPE '\033' | |
18 | #define KEYCODE_BACKSP '\177' | |
19 | #define KEYCODE_ENTER '\r' | |
20 | ||
53b310ce CW |
21 | /* Offsets from zipl fields to zipl banner start */ |
22 | #define ZIPL_TIMEOUT_OFFSET 138 | |
23 | #define ZIPL_FLAG_OFFSET 140 | |
24 | ||
ff5dbf1b CW |
25 | #define TOD_CLOCK_MILLISECOND 0x3e8000 |
26 | ||
27 | #define LOW_CORE_EXTERNAL_INT_ADDR 0x86 | |
28 | #define CLOCK_COMPARATOR_INT 0X1004 | |
29 | ||
9eaa654a CW |
30 | static uint8_t flag; |
31 | static uint64_t timeout; | |
32 | ||
ff5dbf1b CW |
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 | ||
dbf2091a CW |
124 | sclp_set_write_mask(SCLP_EVENT_MASK_MSG_ASCII, SCLP_EVENT_MASK_MSG_ASCII); |
125 | ||
ff5dbf1b CW |
126 | len = read_prompt(buf, sizeof(buf) - 1); |
127 | ||
dbf2091a CW |
128 | sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII); |
129 | ||
ff5dbf1b CW |
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++) { | |
d796588b | 137 | if (!isdigit((unsigned char)buf[i])) { |
ff5dbf1b CW |
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 | ||
7385e947 | 161 | static int get_boot_index(bool *valid_entries) |
ba831b25 | 162 | { |
ff5dbf1b CW |
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; | |
7385e947 CW |
171 | } while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES || |
172 | !valid_entries[boot_index]); | |
ff5dbf1b CW |
173 | |
174 | sclp_print("\nBooting entry #"); | |
175 | sclp_print(uitoa(boot_index, tmp, sizeof(tmp))); | |
176 | ||
177 | return boot_index; | |
ba831b25 CW |
178 | } |
179 | ||
7385e947 CW |
180 | /* Returns the entry number that was printed */ |
181 | static int zipl_print_entry(const char *data, size_t len) | |
f7178910 CW |
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); | |
7385e947 CW |
190 | |
191 | return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf); | |
f7178910 CW |
192 | } |
193 | ||
194 | int menu_get_zipl_boot_index(const char *menu_data) | |
195 | { | |
196 | size_t len; | |
7385e947 CW |
197 | int entry; |
198 | bool valid_entries[MAX_BOOT_ENTRIES] = {false}; | |
53b310ce CW |
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 | } | |
f7178910 | 209 | |
7385e947 CW |
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) { | |
f7178910 | 216 | len = strlen(menu_data); |
7385e947 | 217 | entry = zipl_print_entry(menu_data, len); |
f7178910 CW |
218 | menu_data += len + 1; |
219 | ||
7385e947 CW |
220 | valid_entries[entry] = true; |
221 | ||
222 | if (entry == 0) { | |
f7178910 CW |
223 | sclp_print("\n"); |
224 | } | |
225 | } | |
226 | ||
227 | sclp_print("\n"); | |
7385e947 | 228 | return get_boot_index(valid_entries); |
f7178910 CW |
229 | } |
230 | ||
622b3917 | 231 | int menu_get_enum_boot_index(bool *valid_entries) |
ffb4a1c8 | 232 | { |
622b3917 CW |
233 | char tmp[3]; |
234 | int i; | |
ffb4a1c8 | 235 | |
622b3917 | 236 | sclp_print("s390-ccw Enumerated Boot Menu.\n\n"); |
ffb4a1c8 | 237 | |
622b3917 CW |
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 | } | |
ffb4a1c8 | 252 | |
622b3917 CW |
253 | sclp_print("\n"); |
254 | return get_boot_index(valid_entries); | |
ffb4a1c8 CW |
255 | } |
256 | ||
9eaa654a CW |
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 | } | |
ba831b25 CW |
262 | |
263 | bool menu_is_enabled_zipl(void) | |
264 | { | |
53b310ce | 265 | return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL); |
ba831b25 | 266 | } |
ffb4a1c8 CW |
267 | |
268 | bool menu_is_enabled_enum(void) | |
269 | { | |
270 | return flag & QIPL_FLAG_BM_OPTS_CMD; | |
271 | } |