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_input_interface *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  */
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  */
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  */
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  */
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  */
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  */
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