| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * EFI efi_selftest |
| * |
| * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de> |
| */ |
| |
| #include <efi_selftest.h> |
| #include <net.h> |
| #include <vsprintf.h> |
| |
| 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; |
| } |