19eaa654aSCollin L. Walling /*
29eaa654aSCollin L. Walling * QEMU S390 Interactive Boot Menu
39eaa654aSCollin L. Walling *
49eaa654aSCollin L. Walling * Copyright 2018 IBM Corp.
59eaa654aSCollin L. Walling * Author: Collin L. Walling <walling@linux.vnet.ibm.com>
69eaa654aSCollin L. Walling *
79eaa654aSCollin L. Walling * This work is licensed under the terms of the GNU GPL, version 2 or (at
89eaa654aSCollin L. Walling * your option) any later version. See the COPYING file in the top-level
99eaa654aSCollin L. Walling * directory.
109eaa654aSCollin L. Walling */
119eaa654aSCollin L. Walling
12*9f427883SJared Rossi #include <ctype.h>
13*9f427883SJared Rossi #include <stdio.h>
14*9f427883SJared Rossi #include <stdlib.h>
15*9f427883SJared Rossi #include <string.h>
169eaa654aSCollin L. Walling #include "s390-ccw.h"
17dbf2091aSCollin L. Walling #include "sclp.h"
18e70bc57bSJanosch Frank #include "s390-time.h"
199eaa654aSCollin L. Walling
20ff5dbf1bSCollin L. Walling #define KEYCODE_NO_INP '\0'
21ff5dbf1bSCollin L. Walling #define KEYCODE_ESCAPE '\033'
22ff5dbf1bSCollin L. Walling #define KEYCODE_BACKSP '\177'
23ff5dbf1bSCollin L. Walling #define KEYCODE_ENTER '\r'
24ff5dbf1bSCollin L. Walling
2553b310ceSCollin L. Walling /* Offsets from zipl fields to zipl banner start */
2653b310ceSCollin L. Walling #define ZIPL_TIMEOUT_OFFSET 138
2753b310ceSCollin L. Walling #define ZIPL_FLAG_OFFSET 140
2853b310ceSCollin L. Walling
29ff5dbf1bSCollin L. Walling #define TOD_CLOCK_MILLISECOND 0x3e8000
30ff5dbf1bSCollin L. Walling
31ff5dbf1bSCollin L. Walling #define LOW_CORE_EXTERNAL_INT_ADDR 0x86
32ff5dbf1bSCollin L. Walling #define CLOCK_COMPARATOR_INT 0X1004
33ff5dbf1bSCollin L. Walling
349eaa654aSCollin L. Walling static uint8_t flag;
359eaa654aSCollin L. Walling static uint64_t timeout;
369eaa654aSCollin L. Walling
enable_clock_int(void)37ff5dbf1bSCollin L. Walling static inline void enable_clock_int(void)
38ff5dbf1bSCollin L. Walling {
39ff5dbf1bSCollin L. Walling uint64_t tmp = 0;
40ff5dbf1bSCollin L. Walling
41ff5dbf1bSCollin L. Walling asm volatile(
42052b66e7SThomas Huth "stctg %%c0,%%c0,%0\n"
43ff5dbf1bSCollin L. Walling "oi 6+%0, 0x8\n"
44052b66e7SThomas Huth "lctlg %%c0,%%c0,%0"
45ff5dbf1bSCollin L. Walling : : "Q" (tmp) : "memory"
46ff5dbf1bSCollin L. Walling );
47ff5dbf1bSCollin L. Walling }
48ff5dbf1bSCollin L. Walling
disable_clock_int(void)49ff5dbf1bSCollin L. Walling static inline void disable_clock_int(void)
50ff5dbf1bSCollin L. Walling {
51ff5dbf1bSCollin L. Walling uint64_t tmp = 0;
52ff5dbf1bSCollin L. Walling
53ff5dbf1bSCollin L. Walling asm volatile(
54052b66e7SThomas Huth "stctg %%c0,%%c0,%0\n"
55ff5dbf1bSCollin L. Walling "ni 6+%0, 0xf7\n"
56052b66e7SThomas Huth "lctlg %%c0,%%c0,%0"
57ff5dbf1bSCollin L. Walling : : "Q" (tmp) : "memory"
58ff5dbf1bSCollin L. Walling );
59ff5dbf1bSCollin L. Walling }
60ff5dbf1bSCollin L. Walling
set_clock_comparator(uint64_t time)61ff5dbf1bSCollin L. Walling static inline void set_clock_comparator(uint64_t time)
62ff5dbf1bSCollin L. Walling {
63ff5dbf1bSCollin L. Walling asm volatile("sckc %0" : : "Q" (time));
64ff5dbf1bSCollin L. Walling }
65ff5dbf1bSCollin L. Walling
check_clock_int(void)66ff5dbf1bSCollin L. Walling static inline bool check_clock_int(void)
67ff5dbf1bSCollin L. Walling {
68ff5dbf1bSCollin L. Walling uint16_t *code = (uint16_t *)LOW_CORE_EXTERNAL_INT_ADDR;
69ff5dbf1bSCollin L. Walling
70ff5dbf1bSCollin L. Walling consume_sclp_int();
71ff5dbf1bSCollin L. Walling
72ff5dbf1bSCollin L. Walling return *code == CLOCK_COMPARATOR_INT;
73ff5dbf1bSCollin L. Walling }
74ff5dbf1bSCollin L. Walling
read_prompt(char * buf,size_t len)75ff5dbf1bSCollin L. Walling static int read_prompt(char *buf, size_t len)
76ff5dbf1bSCollin L. Walling {
77ff5dbf1bSCollin L. Walling char inp[2] = {};
78ff5dbf1bSCollin L. Walling uint8_t idx = 0;
79ff5dbf1bSCollin L. Walling uint64_t time;
80ff5dbf1bSCollin L. Walling
81ff5dbf1bSCollin L. Walling if (timeout) {
82ff5dbf1bSCollin L. Walling time = get_clock() + timeout * TOD_CLOCK_MILLISECOND;
83ff5dbf1bSCollin L. Walling set_clock_comparator(time);
84ff5dbf1bSCollin L. Walling enable_clock_int();
85ff5dbf1bSCollin L. Walling timeout = 0;
86ff5dbf1bSCollin L. Walling }
87ff5dbf1bSCollin L. Walling
88ff5dbf1bSCollin L. Walling while (!check_clock_int()) {
89ff5dbf1bSCollin L. Walling
90ff5dbf1bSCollin L. Walling sclp_read(inp, 1); /* Process only one character at a time */
91ff5dbf1bSCollin L. Walling
92ff5dbf1bSCollin L. Walling switch (inp[0]) {
93ff5dbf1bSCollin L. Walling case KEYCODE_NO_INP:
94ff5dbf1bSCollin L. Walling case KEYCODE_ESCAPE:
95ff5dbf1bSCollin L. Walling continue;
96ff5dbf1bSCollin L. Walling case KEYCODE_BACKSP:
97ff5dbf1bSCollin L. Walling if (idx > 0) {
98ff5dbf1bSCollin L. Walling buf[--idx] = 0;
99*9f427883SJared Rossi printf("\b \b");
100ff5dbf1bSCollin L. Walling }
101ff5dbf1bSCollin L. Walling continue;
102ff5dbf1bSCollin L. Walling case KEYCODE_ENTER:
103ff5dbf1bSCollin L. Walling disable_clock_int();
104ff5dbf1bSCollin L. Walling return idx;
105ff5dbf1bSCollin L. Walling default:
106ff5dbf1bSCollin L. Walling /* Echo input and add to buffer */
107ff5dbf1bSCollin L. Walling if (idx < len) {
108ff5dbf1bSCollin L. Walling buf[idx++] = inp[0];
109*9f427883SJared Rossi printf("%s", inp);
110ff5dbf1bSCollin L. Walling }
111ff5dbf1bSCollin L. Walling }
112ff5dbf1bSCollin L. Walling }
113ff5dbf1bSCollin L. Walling
114ff5dbf1bSCollin L. Walling disable_clock_int();
115ff5dbf1bSCollin L. Walling *buf = 0;
116ff5dbf1bSCollin L. Walling
117ff5dbf1bSCollin L. Walling return 0;
118ff5dbf1bSCollin L. Walling }
119ff5dbf1bSCollin L. Walling
get_index(void)120ff5dbf1bSCollin L. Walling static int get_index(void)
121ff5dbf1bSCollin L. Walling {
122ff5dbf1bSCollin L. Walling char buf[11];
123ff5dbf1bSCollin L. Walling int len;
124ff5dbf1bSCollin L. Walling int i;
125ff5dbf1bSCollin L. Walling
126ff5dbf1bSCollin L. Walling memset(buf, 0, sizeof(buf));
127ff5dbf1bSCollin L. Walling
128dbf2091aSCollin L. Walling sclp_set_write_mask(SCLP_EVENT_MASK_MSG_ASCII, SCLP_EVENT_MASK_MSG_ASCII);
129dbf2091aSCollin L. Walling
130ff5dbf1bSCollin L. Walling len = read_prompt(buf, sizeof(buf) - 1);
131ff5dbf1bSCollin L. Walling
132dbf2091aSCollin L. Walling sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII);
133dbf2091aSCollin L. Walling
134ff5dbf1bSCollin L. Walling /* If no input, boot default */
135ff5dbf1bSCollin L. Walling if (len == 0) {
136ff5dbf1bSCollin L. Walling return 0;
137ff5dbf1bSCollin L. Walling }
138ff5dbf1bSCollin L. Walling
139ff5dbf1bSCollin L. Walling /* Check for erroneous input */
140ff5dbf1bSCollin L. Walling for (i = 0; i < len; i++) {
141d796588bSMarkus Armbruster if (!isdigit((unsigned char)buf[i])) {
142ff5dbf1bSCollin L. Walling return -1;
143ff5dbf1bSCollin L. Walling }
144ff5dbf1bSCollin L. Walling }
145ff5dbf1bSCollin L. Walling
146*9f427883SJared Rossi return atoi(buf);
147ff5dbf1bSCollin L. Walling }
148ff5dbf1bSCollin L. Walling
boot_menu_prompt(bool retry)149ff5dbf1bSCollin L. Walling static void boot_menu_prompt(bool retry)
150ff5dbf1bSCollin L. Walling {
151ff5dbf1bSCollin L. Walling if (retry) {
152*9f427883SJared Rossi printf("\nError: undefined configuration"
153ff5dbf1bSCollin L. Walling "\nPlease choose:\n");
154ff5dbf1bSCollin L. Walling } else if (timeout > 0) {
155*9f427883SJared Rossi printf("Please choose (default will boot in %d seconds):\n",
156*9f427883SJared Rossi (int)(timeout / 1000));
157ff5dbf1bSCollin L. Walling } else {
158*9f427883SJared Rossi puts("Please choose:");
159ff5dbf1bSCollin L. Walling }
160ff5dbf1bSCollin L. Walling }
161ff5dbf1bSCollin L. Walling
get_boot_index(bool * valid_entries)1627385e947SCollin Walling static int get_boot_index(bool *valid_entries)
163ba831b25SCollin L. Walling {
164ff5dbf1bSCollin L. Walling int boot_index;
165ff5dbf1bSCollin L. Walling bool retry = false;
166ff5dbf1bSCollin L. Walling
167ff5dbf1bSCollin L. Walling do {
168ff5dbf1bSCollin L. Walling boot_menu_prompt(retry);
169ff5dbf1bSCollin L. Walling boot_index = get_index();
170ff5dbf1bSCollin L. Walling retry = true;
1717385e947SCollin Walling } while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES ||
1727385e947SCollin Walling !valid_entries[boot_index]);
173ff5dbf1bSCollin L. Walling
174*9f427883SJared Rossi printf("\nBooting entry #%d", boot_index);
175ff5dbf1bSCollin L. Walling
176ff5dbf1bSCollin L. Walling return boot_index;
177ba831b25SCollin L. Walling }
178ba831b25SCollin L. Walling
1797385e947SCollin Walling /* Returns the entry number that was printed */
zipl_print_entry(const char * data,size_t len)1807385e947SCollin Walling static int zipl_print_entry(const char *data, size_t len)
181f7178910SCollin L. Walling {
182f7178910SCollin L. Walling char buf[len + 2];
183f7178910SCollin L. Walling
184f7178910SCollin L. Walling ebcdic_to_ascii(data, buf, len);
185f7178910SCollin L. Walling buf[len] = '\n';
186f7178910SCollin L. Walling buf[len + 1] = '\0';
187f7178910SCollin L. Walling
188*9f427883SJared Rossi printf("%s", buf);
1897385e947SCollin Walling
190*9f427883SJared Rossi return buf[0] == ' ' ? atoi(buf + 1) : atoi(buf);
191f7178910SCollin L. Walling }
192f7178910SCollin L. Walling
menu_get_zipl_boot_index(const char * menu_data)193f7178910SCollin L. Walling int menu_get_zipl_boot_index(const char *menu_data)
194f7178910SCollin L. Walling {
195f7178910SCollin L. Walling size_t len;
1967385e947SCollin Walling int entry;
1977385e947SCollin Walling bool valid_entries[MAX_BOOT_ENTRIES] = {false};
19853b310ceSCollin L. Walling uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET);
19953b310ceSCollin L. Walling uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET);
20053b310ceSCollin L. Walling
20153b310ceSCollin L. Walling if (flag == QIPL_FLAG_BM_OPTS_ZIPL) {
20253b310ceSCollin L. Walling if (!zipl_flag) {
20353b310ceSCollin L. Walling return 0; /* Boot default */
20453b310ceSCollin L. Walling }
20553b310ceSCollin L. Walling /* zipl stores timeout as seconds */
20653b310ceSCollin L. Walling timeout = zipl_timeout * 1000;
20753b310ceSCollin L. Walling }
208f7178910SCollin L. Walling
2097385e947SCollin Walling /* Print banner */
210*9f427883SJared Rossi puts("s390-ccw zIPL Boot Menu\n");
2117385e947SCollin Walling menu_data += strlen(menu_data) + 1;
2127385e947SCollin Walling
2137385e947SCollin Walling /* Print entries */
2147385e947SCollin Walling while (*menu_data) {
215f7178910SCollin L. Walling len = strlen(menu_data);
2167385e947SCollin Walling entry = zipl_print_entry(menu_data, len);
217f7178910SCollin L. Walling menu_data += len + 1;
218f7178910SCollin L. Walling
2197385e947SCollin Walling valid_entries[entry] = true;
2207385e947SCollin Walling
2217385e947SCollin Walling if (entry == 0) {
222*9f427883SJared Rossi printf("\n");
223f7178910SCollin L. Walling }
224f7178910SCollin L. Walling }
225f7178910SCollin L. Walling
226*9f427883SJared Rossi printf("\n");
2277385e947SCollin Walling return get_boot_index(valid_entries);
228f7178910SCollin L. Walling }
229f7178910SCollin L. Walling
menu_get_enum_boot_index(bool * valid_entries)230622b3917SCollin Walling int menu_get_enum_boot_index(bool *valid_entries)
231ffb4a1c8SCollin L. Walling {
232622b3917SCollin Walling int i;
233ffb4a1c8SCollin L. Walling
234*9f427883SJared Rossi puts("s390-ccw Enumerated Boot Menu.\n");
235ffb4a1c8SCollin L. Walling
236622b3917SCollin Walling for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
237622b3917SCollin Walling if (valid_entries[i]) {
238622b3917SCollin Walling if (i < 10) {
239*9f427883SJared Rossi printf(" ");
240622b3917SCollin Walling }
241*9f427883SJared Rossi printf("[%d]", i);
242622b3917SCollin Walling if (i == 0) {
243*9f427883SJared Rossi printf(" default\n");
244622b3917SCollin Walling }
245*9f427883SJared Rossi printf("\n");
246622b3917SCollin Walling }
247622b3917SCollin Walling }
248ffb4a1c8SCollin L. Walling
249*9f427883SJared Rossi printf("\n");
250622b3917SCollin Walling return get_boot_index(valid_entries);
251ffb4a1c8SCollin L. Walling }
252ffb4a1c8SCollin L. Walling
menu_set_parms(uint8_t boot_menu_flag,uint32_t boot_menu_timeout)2539eaa654aSCollin L. Walling void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
2549eaa654aSCollin L. Walling {
2559eaa654aSCollin L. Walling flag = boot_menu_flag;
2569eaa654aSCollin L. Walling timeout = boot_menu_timeout;
2579eaa654aSCollin L. Walling }
258ba831b25SCollin L. Walling
menu_is_enabled_zipl(void)259ba831b25SCollin L. Walling bool menu_is_enabled_zipl(void)
260ba831b25SCollin L. Walling {
26153b310ceSCollin L. Walling return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL);
262ba831b25SCollin L. Walling }
263ffb4a1c8SCollin L. Walling
menu_is_enabled_enum(void)264ffb4a1c8SCollin L. Walling bool menu_is_enabled_enum(void)
265ffb4a1c8SCollin L. Walling {
266ffb4a1c8SCollin L. Walling return flag & QIPL_FLAG_BM_OPTS_CMD;
267ffb4a1c8SCollin L. Walling }
268