xref: /qemu/pc-bios/s390-ccw/menu.c (revision abff1abfe8113a989215ed33c48839a827e531f1)
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  #include "sclp.h"
15  #include "s390-time.h"
16  
17  #define KEYCODE_NO_INP '\0'
18  #define KEYCODE_ESCAPE '\033'
19  #define KEYCODE_BACKSP '\177'
20  #define KEYCODE_ENTER  '\r'
21  
22  /* Offsets from zipl fields to zipl banner start */
23  #define ZIPL_TIMEOUT_OFFSET 138
24  #define ZIPL_FLAG_OFFSET    140
25  
26  #define TOD_CLOCK_MILLISECOND   0x3e8000
27  
28  #define LOW_CORE_EXTERNAL_INT_ADDR   0x86
29  #define CLOCK_COMPARATOR_INT         0X1004
30  
31  static uint8_t flag;
32  static uint64_t timeout;
33  
34  static inline void enable_clock_int(void)
35  {
36      uint64_t tmp = 0;
37  
38      asm volatile(
39          "stctg      0,0,%0\n"
40          "oi         6+%0, 0x8\n"
41          "lctlg      0,0,%0"
42          : : "Q" (tmp) : "memory"
43      );
44  }
45  
46  static inline void disable_clock_int(void)
47  {
48      uint64_t tmp = 0;
49  
50      asm volatile(
51          "stctg      0,0,%0\n"
52          "ni         6+%0, 0xf7\n"
53          "lctlg      0,0,%0"
54          : : "Q" (tmp) : "memory"
55      );
56  }
57  
58  static inline void set_clock_comparator(uint64_t time)
59  {
60      asm volatile("sckc %0" : : "Q" (time));
61  }
62  
63  static inline bool check_clock_int(void)
64  {
65      uint16_t *code = (uint16_t *)LOW_CORE_EXTERNAL_INT_ADDR;
66  
67      consume_sclp_int();
68  
69      return *code == CLOCK_COMPARATOR_INT;
70  }
71  
72  static int read_prompt(char *buf, size_t len)
73  {
74      char inp[2] = {};
75      uint8_t idx = 0;
76      uint64_t time;
77  
78      if (timeout) {
79          time = get_clock() + timeout * TOD_CLOCK_MILLISECOND;
80          set_clock_comparator(time);
81          enable_clock_int();
82          timeout = 0;
83      }
84  
85      while (!check_clock_int()) {
86  
87          sclp_read(inp, 1); /* Process only one character at a time */
88  
89          switch (inp[0]) {
90          case KEYCODE_NO_INP:
91          case KEYCODE_ESCAPE:
92              continue;
93          case KEYCODE_BACKSP:
94              if (idx > 0) {
95                  buf[--idx] = 0;
96                  sclp_print("\b \b");
97              }
98              continue;
99          case KEYCODE_ENTER:
100              disable_clock_int();
101              return idx;
102          default:
103              /* Echo input and add to buffer */
104              if (idx < len) {
105                  buf[idx++] = inp[0];
106                  sclp_print(inp);
107              }
108          }
109      }
110  
111      disable_clock_int();
112      *buf = 0;
113  
114      return 0;
115  }
116  
117  static int get_index(void)
118  {
119      char buf[11];
120      int len;
121      int i;
122  
123      memset(buf, 0, sizeof(buf));
124  
125      sclp_set_write_mask(SCLP_EVENT_MASK_MSG_ASCII, SCLP_EVENT_MASK_MSG_ASCII);
126  
127      len = read_prompt(buf, sizeof(buf) - 1);
128  
129      sclp_set_write_mask(0, SCLP_EVENT_MASK_MSG_ASCII);
130  
131      /* If no input, boot default */
132      if (len == 0) {
133          return 0;
134      }
135  
136      /* Check for erroneous input */
137      for (i = 0; i < len; i++) {
138          if (!isdigit((unsigned char)buf[i])) {
139              return -1;
140          }
141      }
142  
143      return atoui(buf);
144  }
145  
146  static void boot_menu_prompt(bool retry)
147  {
148      char tmp[11];
149  
150      if (retry) {
151          sclp_print("\nError: undefined configuration"
152                     "\nPlease choose:\n");
153      } else if (timeout > 0) {
154          sclp_print("Please choose (default will boot in ");
155          sclp_print(uitoa(timeout / 1000, tmp, sizeof(tmp)));
156          sclp_print(" seconds):\n");
157      } else {
158          sclp_print("Please choose:\n");
159      }
160  }
161  
162  static int get_boot_index(bool *valid_entries)
163  {
164      int boot_index;
165      bool retry = false;
166      char tmp[5];
167  
168      do {
169          boot_menu_prompt(retry);
170          boot_index = get_index();
171          retry = true;
172      } while (boot_index < 0 || boot_index >= MAX_BOOT_ENTRIES ||
173               !valid_entries[boot_index]);
174  
175      sclp_print("\nBooting entry #");
176      sclp_print(uitoa(boot_index, tmp, sizeof(tmp)));
177  
178      return boot_index;
179  }
180  
181  /* Returns the entry number that was printed */
182  static int zipl_print_entry(const char *data, size_t len)
183  {
184      char buf[len + 2];
185  
186      ebcdic_to_ascii(data, buf, len);
187      buf[len] = '\n';
188      buf[len + 1] = '\0';
189  
190      sclp_print(buf);
191  
192      return buf[0] == ' ' ? atoui(buf + 1) : atoui(buf);
193  }
194  
195  int menu_get_zipl_boot_index(const char *menu_data)
196  {
197      size_t len;
198      int entry;
199      bool valid_entries[MAX_BOOT_ENTRIES] = {false};
200      uint16_t zipl_flag = *(uint16_t *)(menu_data - ZIPL_FLAG_OFFSET);
201      uint16_t zipl_timeout = *(uint16_t *)(menu_data - ZIPL_TIMEOUT_OFFSET);
202  
203      if (flag == QIPL_FLAG_BM_OPTS_ZIPL) {
204          if (!zipl_flag) {
205              return 0; /* Boot default */
206          }
207          /* zipl stores timeout as seconds */
208          timeout = zipl_timeout * 1000;
209      }
210  
211      /* Print banner */
212      sclp_print("s390-ccw zIPL Boot Menu\n\n");
213      menu_data += strlen(menu_data) + 1;
214  
215      /* Print entries */
216      while (*menu_data) {
217          len = strlen(menu_data);
218          entry = zipl_print_entry(menu_data, len);
219          menu_data += len + 1;
220  
221          valid_entries[entry] = true;
222  
223          if (entry == 0) {
224              sclp_print("\n");
225          }
226      }
227  
228      sclp_print("\n");
229      return get_boot_index(valid_entries);
230  }
231  
232  int menu_get_enum_boot_index(bool *valid_entries)
233  {
234      char tmp[3];
235      int i;
236  
237      sclp_print("s390-ccw Enumerated Boot Menu.\n\n");
238  
239      for (i = 0; i < MAX_BOOT_ENTRIES; i++) {
240          if (valid_entries[i]) {
241              if (i < 10) {
242                  sclp_print(" ");
243              }
244              sclp_print("[");
245              sclp_print(uitoa(i, tmp, sizeof(tmp)));
246              sclp_print("]");
247              if (i == 0) {
248                  sclp_print(" default\n");
249              }
250              sclp_print("\n");
251          }
252      }
253  
254      sclp_print("\n");
255      return get_boot_index(valid_entries);
256  }
257  
258  void menu_set_parms(uint8_t boot_menu_flag, uint32_t boot_menu_timeout)
259  {
260      flag = boot_menu_flag;
261      timeout = boot_menu_timeout;
262  }
263  
264  bool menu_is_enabled_zipl(void)
265  {
266      return flag & (QIPL_FLAG_BM_OPTS_CMD | QIPL_FLAG_BM_OPTS_ZIPL);
267  }
268  
269  bool menu_is_enabled_enum(void)
270  {
271      return flag & QIPL_FLAG_BM_OPTS_CMD;
272  }
273