// SPDX-License-Identifier: GPL-2.0+ /* * EFI efi_selftest * * Copyright (c) 2017 Heinrich Schuchardt */ #include #include struct efi_simple_text_output_protocol *con_out; struct efi_simple_text_input_protocol *con_in; /* * Print a MAC address to an u16 string * * @pointer: mac address * @buf: pointer to buffer address * on return position of terminating zero word */ static void mac(void *pointer, u16 **buf) { int i, j; u16 c; u8 *p = (u8 *)pointer; u8 byte; u16 *pos = *buf; for (i = 0; i < ARP_HLEN; ++i) { if (i) *pos++ = ':'; byte = p[i]; for (j = 4; j >= 0; j -= 4) { c = (byte >> j) & 0x0f; c += '0'; if (c > '9') c += 'a' - '9' - 1; *pos++ = c; } } *pos = 0; *buf = pos; } /* * Print a pointer to an u16 string * * @pointer: pointer * @buf: pointer to buffer address * on return position of terminating zero word */ static void pointer(void *pointer, u16 **buf) { int i; u16 c; uintptr_t p = (uintptr_t)pointer; u16 *pos = *buf; for (i = 8 * sizeof(p) - 4; i >= 0; i -= 4) { c = (p >> i) & 0x0f; c += '0'; if (c > '9') c += 'a' - '9' - 1; *pos++ = c; } *pos = 0; *buf = pos; } /* * Print an unsigned 32bit value as decimal number to an u16 string * * @value: value to be printed * @prec: minimum number of digits to display * @buf: pointer to buffer address * on return position of terminating zero word */ static void uint2dec(u32 value, int prec, u16 **buf) { u16 *pos = *buf; int i; u16 c; u64 f; /* * Increment by .5 and multiply with * (2 << 60) / 1,000,000,000 = 0x44B82FA0.9B5A52CC * to move the first digit to bit 60-63. */ f = 0x225C17D0; f += (0x9B5A52DULL * value) >> 28; f += 0x44B82FA0ULL * value; for (i = 0; i < 10; ++i) { /* Write current digit */ c = f >> 60; if (c || pos != *buf || 10 - i <= prec) *pos++ = c + '0'; /* Eliminate current digit */ f &= 0xfffffffffffffff; /* Get next digit */ f *= 0xaULL; } if (pos == *buf) *pos++ = '0'; *pos = 0; *buf = pos; } /* * Print a signed 32bit value as decimal number to an u16 string * * @value: value to be printed * @prec: minimum number of digits to display * @buf: pointer to buffer address * on return position of terminating zero word */ static void int2dec(s32 value, int prec, u16 **buf) { u32 u; u16 *pos = *buf; if (value < 0) { *pos++ = '-'; u = -value; } else { u = value; } uint2dec(u, prec, &pos); *buf = pos; } /* * Print a colored formatted string to the EFI console * * @color color, see constants in efi_api.h, use -1 for no color * @fmt format string * @... optional arguments */ void efi_st_printc(int color, const char *fmt, ...) { va_list args; u16 buf[160]; const char *c; u16 *pos = buf; const char *s; u16 *u; int prec; va_start(args, fmt); if (color >= 0) con_out->set_attribute(con_out, (unsigned long)color); c = fmt; for (; *c; ++c) { switch (*c) { case '\\': ++c; switch (*c) { case '\0': --c; break; case 'n': *pos++ = '\n'; break; case 'r': *pos++ = '\r'; break; case 't': *pos++ = '\t'; break; default: *pos++ = *c; } break; case '%': ++c; /* Parse precision */ if (*c == '.') { ++c; prec = *c - '0'; ++c; } else { prec = 0; } switch (*c) { case '\0': --c; break; case 'd': int2dec(va_arg(args, s32), prec, &pos); break; case 'p': ++c; switch (*c) { /* MAC address */ case 'm': mac(va_arg(args, void*), &pos); break; /* u16 string */ case 's': u = va_arg(args, u16*); if (pos > buf) { *pos = 0; con_out->output_string(con_out, buf); } con_out->output_string(con_out, u); pos = buf; break; default: --c; pointer(va_arg(args, void*), &pos); } break; case 's': s = va_arg(args, const char *); for (; *s; ++s) *pos++ = *s; break; case 'u': uint2dec(va_arg(args, u32), prec, &pos); break; default: break; } break; default: *pos++ = *c; } } va_end(args); *pos = 0; con_out->output_string(con_out, buf); if (color >= 0) con_out->set_attribute(con_out, EFI_LIGHTGRAY); } /* * Reads an Unicode character from the input device. * * @return: Unicode character */ u16 efi_st_get_key(void) { struct efi_input_key input_key; efi_status_t ret; /* Wait for next key */ do { ret = con_in->read_key_stroke(con_in, &input_key); } while (ret == EFI_NOT_READY); return input_key.unicode_char; }