]>
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++) { | |
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 | ||
f7178910 | 161 | static int get_boot_index(int 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; | |
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; | |
ba831b25 CW |
177 | } |
178 | ||
f7178910 CW |
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; | |
53b310ce CW |
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 | } | |
f7178910 CW |
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 | ||
ffb4a1c8 CW |
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 | ||
9eaa654a CW |
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 | } | |
ba831b25 CW |
240 | |
241 | bool menu_is_enabled_zipl(void) | |
242 | { | |
53b310ce | 243 | return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL); |
ba831b25 | 244 | } |
ffb4a1c8 CW |
245 | |
246 | bool menu_is_enabled_enum(void) | |
247 | { | |
248 | return flag & QIPL_FLAG_BM_OPTS_CMD; | |
249 | } |