1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3 * EFI efi_selftest
4 *
5 * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de>
6 */
7
8 #include <efi_selftest.h>
9 #include <vsprintf.h>
10
11 struct efi_simple_text_output_protocol *con_out;
12 struct efi_simple_text_input_protocol *con_in;
13
14 /*
15 * Print a MAC address to an u16 string
16 *
17 * @pointer: mac address
18 * @buf: pointer to buffer address
19 * on return position of terminating zero word
20 */
mac(void * pointer,u16 ** buf)21 static void mac(void *pointer, u16 **buf)
22 {
23 int i, j;
24 u16 c;
25 u8 *p = (u8 *)pointer;
26 u8 byte;
27 u16 *pos = *buf;
28
29 for (i = 0; i < ARP_HLEN; ++i) {
30 if (i)
31 *pos++ = ':';
32 byte = p[i];
33 for (j = 4; j >= 0; j -= 4) {
34 c = (byte >> j) & 0x0f;
35 c += '0';
36 if (c > '9')
37 c += 'a' - '9' - 1;
38 *pos++ = c;
39 }
40 }
41 *pos = 0;
42 *buf = pos;
43 }
44
45 /*
46 * Print a pointer to an u16 string
47 *
48 * @pointer: pointer
49 * @buf: pointer to buffer address
50 * on return position of terminating zero word
51 */
pointer(void * pointer,u16 ** buf)52 static void pointer(void *pointer, u16 **buf)
53 {
54 int i;
55 u16 c;
56 uintptr_t p = (uintptr_t)pointer;
57 u16 *pos = *buf;
58
59 for (i = 8 * sizeof(p) - 4; i >= 0; i -= 4) {
60 c = (p >> i) & 0x0f;
61 c += '0';
62 if (c > '9')
63 c += 'a' - '9' - 1;
64 *pos++ = c;
65 }
66 *pos = 0;
67 *buf = pos;
68 }
69
70 /*
71 * Print an unsigned 32bit value as decimal number to an u16 string
72 *
73 * @value: value to be printed
74 * @prec: minimum number of digits to display
75 * @buf: pointer to buffer address
76 * on return position of terminating zero word
77 */
uint2dec(u32 value,int prec,u16 ** buf)78 static void uint2dec(u32 value, int prec, u16 **buf)
79 {
80 u16 *pos = *buf;
81 int i;
82 u16 c;
83 u64 f;
84
85 /*
86 * Increment by .5 and multiply with
87 * (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC
88 * to move the first digit to bit 60-63.
89 */
90 f = 0x225C17D0;
91 f += (0x9B5A52DULL * value) >> 28;
92 f += 0x44B82FA0ULL * value;
93
94 for (i = 0; i < 10; ++i) {
95 /* Write current digit */
96 c = f >> 60;
97 if (c || pos != *buf || 10 - i <= prec)
98 *pos++ = c + '0';
99 /* Eliminate current digit */
100 f &= 0xfffffffffffffff;
101 /* Get next digit */
102 f *= 0xaULL;
103 }
104 if (pos == *buf)
105 *pos++ = '0';
106 *pos = 0;
107 *buf = pos;
108 }
109
110 /*
111 * Print a signed 32bit value as decimal number to an u16 string
112 *
113 * @value: value to be printed
114 * @prec: minimum number of digits to display
115 * @buf: pointer to buffer address
116 * on return position of terminating zero word
117 */
int2dec(s32 value,int prec,u16 ** buf)118 static void int2dec(s32 value, int prec, u16 **buf)
119 {
120 u32 u;
121 u16 *pos = *buf;
122
123 if (value < 0) {
124 *pos++ = '-';
125 u = -value;
126 } else {
127 u = value;
128 }
129 uint2dec(u, prec, &pos);
130 *buf = pos;
131 }
132
133 /*
134 * Print a colored formatted string to the EFI console
135 *
136 * @color color, see constants in efi_api.h, use -1 for no color
137 * @fmt format string
138 * @... optional arguments
139 */
efi_st_printc(int color,const char * fmt,...)140 void efi_st_printc(int color, const char *fmt, ...)
141 {
142 va_list args;
143 u16 buf[160];
144 const char *c;
145 u16 *pos = buf;
146 const char *s;
147 u16 *u;
148 int prec;
149
150 va_start(args, fmt);
151
152 if (color >= 0)
153 con_out->set_attribute(con_out, (unsigned long)color);
154 c = fmt;
155 for (; *c; ++c) {
156 switch (*c) {
157 case '\\':
158 ++c;
159 switch (*c) {
160 case '\0':
161 --c;
162 break;
163 case 'n':
164 *pos++ = '\n';
165 break;
166 case 'r':
167 *pos++ = '\r';
168 break;
169 case 't':
170 *pos++ = '\t';
171 break;
172 default:
173 *pos++ = *c;
174 }
175 break;
176 case '%':
177 ++c;
178 /* Parse precision */
179 if (*c == '.') {
180 ++c;
181 prec = *c - '0';
182 ++c;
183 } else {
184 prec = 0;
185 }
186 switch (*c) {
187 case '\0':
188 --c;
189 break;
190 case 'd':
191 int2dec(va_arg(args, s32), prec, &pos);
192 break;
193 case 'p':
194 ++c;
195 switch (*c) {
196 /* MAC address */
197 case 'm':
198 mac(va_arg(args, void*), &pos);
199 break;
200
201 /* u16 string */
202 case 's':
203 u = va_arg(args, u16*);
204 if (pos > buf) {
205 *pos = 0;
206 con_out->output_string(con_out,
207 buf);
208 }
209 con_out->output_string(con_out, u);
210 pos = buf;
211 break;
212 default:
213 --c;
214 pointer(va_arg(args, void*), &pos);
215 }
216 break;
217 case 's':
218 s = va_arg(args, const char *);
219 for (; *s; ++s)
220 *pos++ = *s;
221 break;
222 case 'u':
223 uint2dec(va_arg(args, u32), prec, &pos);
224 break;
225 default:
226 break;
227 }
228 break;
229 default:
230 *pos++ = *c;
231 }
232 }
233 va_end(args);
234 *pos = 0;
235 con_out->output_string(con_out, buf);
236 if (color >= 0)
237 con_out->set_attribute(con_out, EFI_LIGHTGRAY);
238 }
239
240 /*
241 * Reads an Unicode character from the input device.
242 *
243 * @return: Unicode character
244 */
efi_st_get_key(void)245 u16 efi_st_get_key(void)
246 {
247 struct efi_input_key input_key;
248 efi_status_t ret;
249
250 /* Wait for next key */
251 do {
252 ret = con_in->read_key_stroke(con_in, &input_key);
253 } while (ret == EFI_NOT_READY);
254 return input_key.unicode_char;
255 }
256