xref: /openbmc/qemu/pc-bios/s390-ccw/menu.c (revision e17e57e862faf6e1f372385c18dcf6d3fd31158e)
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