gnu-efi/lib/print.c
Pete Batard 0c7af72ba8
Fix exception on ARM32 with VS2022 when Print() is invoked
On ARM32 only, it appears that whenever Visual Studio inlines the FloatToString()
call (which it does for Release builds), the resulting executable produces an
"Undefined OpCode Exception" on Print() invocation, regardless of whether there
is an actual float to string conversion occurring there.

To work around this, add an explicit clause to prevent inlining, and do so on all
platform just in case.

Signed-off-by: Pete Batard <pete@akeo.ie>
2024-05-14 13:56:03 +01:00

1558 lines
30 KiB
C

/*++
Copyright (c) 1998 Intel Corporation
Module Name:
print.c
Abstract:
Revision History
--*/
#include "lib.h"
#include "efistdarg.h" // !!!
//
// Declare runtime functions
//
#ifdef RUNTIME_CODE
#ifndef __GNUC__
#pragma RUNTIME_CODE(DbgPrint)
// For debugging..
/*
#pragma RUNTIME_CODE(_Print)
#pragma RUNTIME_CODE(PFLUSH)
#pragma RUNTIME_CODE(PSETATTR)
#pragma RUNTIME_CODE(PPUTC)
#pragma RUNTIME_CODE(PGETC)
#pragma RUNTIME_CODE(PITEM)
#pragma RUNTIME_CODE(ValueToHex)
#pragma RUNTIME_CODE(ValueToString)
#pragma RUNTIME_CODE(TimeToString)
*/
#endif /* !defined(__GNUC__) */
#endif
//
//
//
#define PRINT_STRING_LEN 200
#define PRINT_ITEM_BUFFER_LEN 100
typedef struct {
BOOLEAN Ascii;
UINTN Index;
union {
CONST CHAR16 *pw;
CONST CHAR8 *pc;
} un;
} POINTER;
#define pw un.pw
#define pc un.pc
typedef struct _pitem {
POINTER Item;
CHAR16 Scratch[PRINT_ITEM_BUFFER_LEN];
UINTN Width;
UINTN FieldWidth;
UINTN *WidthParse;
CHAR16 Pad;
BOOLEAN PadBefore;
BOOLEAN Comma;
BOOLEAN Long;
} PRINT_ITEM;
typedef struct _pstate {
// Input
POINTER fmt;
va_list args;
// Output
CHAR16 *Buffer;
CHAR16 *End;
CHAR16 *Pos;
UINTN Len;
UINTN Attr;
UINTN RestoreAttr;
UINTN AttrNorm;
UINTN AttrHighlight;
UINTN AttrError;
INTN (EFIAPI *Output)(VOID *context, CHAR16 *str);
INTN (EFIAPI *SetAttr)(VOID *context, UINTN attr);
VOID *Context;
// Current item being formatted
struct _pitem *Item;
} PRINT_STATE;
//
// Internal fucntions
//
STATIC
UINTN
_Print (
IN PRINT_STATE *ps
);
STATIC
UINTN
_IPrint (
IN UINTN Column,
IN UINTN Row,
IN SIMPLE_TEXT_OUTPUT_INTERFACE *Out,
IN CONST CHAR16 *fmt,
IN CONST CHAR8 *fmta,
IN va_list args
);
STATIC
INTN EFIAPI
_DbgOut (
IN VOID *Context,
IN CHAR16 *Buffer
);
STATIC
VOID
PFLUSH (
IN OUT PRINT_STATE *ps
);
STATIC
VOID
PPUTC (
IN OUT PRINT_STATE *ps,
IN CHAR16 c
);
STATIC
VOID
PITEM (
IN OUT PRINT_STATE *ps
);
STATIC
CHAR16
PGETC (
IN POINTER *p
);
STATIC
VOID
PSETATTR (
IN OUT PRINT_STATE *ps,
IN UINTN Attr
);
//
//
//
INTN EFIAPI
_SPrint (
IN VOID *Context,
IN CHAR16 *Buffer
);
INTN EFIAPI
_PoolPrint (
IN VOID *Context,
IN CHAR16 *Buffer
);
INTN
DbgPrint (
IN INTN mask,
IN CONST CHAR8 *fmt,
...
)
/*++
Routine Description:
Prints a formatted unicode string to the default StandardError console
Arguments:
mask - Bit mask of debug string. If a bit is set in the
mask that is also set in EFIDebug the string is
printed; otherwise, the string is not printed
fmt - Format string
Returns:
Length of string printed to the StandardError console
--*/
{
SIMPLE_TEXT_OUTPUT_INTERFACE *DbgOut;
PRINT_STATE ps;
va_list args;
UINTN back;
UINTN attr;
UINTN SavedAttribute;
if (!(EFIDebug & mask)) {
return 0;
}
va_start (args, fmt);
ZeroMem (&ps, sizeof(ps));
ps.Output = _DbgOut;
ps.fmt.Ascii = TRUE;
ps.fmt.pc = fmt;
va_copy(ps.args, args);
ps.Attr = EFI_TEXT_ATTR(EFI_LIGHTGRAY, EFI_RED);
DbgOut = LibRuntimeDebugOut;
if (!DbgOut) {
DbgOut = ST->StdErr;
}
if (DbgOut) {
ps.Attr = DbgOut->Mode->Attribute;
ps.Context = DbgOut;
ps.SetAttr = (INTN (EFIAPI *)(VOID *, UINTN)) DbgOut->SetAttribute;
}
SavedAttribute = ps.Attr;
back = (ps.Attr >> 4) & 0xf;
ps.AttrNorm = EFI_TEXT_ATTR(EFI_LIGHTGRAY, back);
ps.AttrHighlight = EFI_TEXT_ATTR(EFI_WHITE, back);
ps.AttrError = EFI_TEXT_ATTR(EFI_YELLOW, back);
attr = ps.AttrNorm;
if (mask & D_WARN) {
attr = ps.AttrHighlight;
}
if (mask & D_ERROR) {
attr = ps.AttrError;
}
if (ps.SetAttr) {
ps.Attr = attr;
uefi_call_wrapper(ps.SetAttr, 2, ps.Context, attr);
}
_Print (&ps);
va_end (ps.args);
va_end (args);
//
// Restore original attributes
//
if (ps.SetAttr) {
uefi_call_wrapper(ps.SetAttr, 2, ps.Context, SavedAttribute);
}
return 0;
}
STATIC
INTN
IsLocalPrint(void *func)
{
if (func == _DbgOut || func == _SPrint || func == _PoolPrint)
return 1;
return 0;
}
STATIC
INTN EFIAPI
_DbgOut (
IN VOID *Context,
IN CHAR16 *Buffer
)
// Append string worker for DbgPrint
{
SIMPLE_TEXT_OUTPUT_INTERFACE *DbgOut;
DbgOut = Context;
// if (!DbgOut && ST && ST->ConOut) {
// DbgOut = ST->ConOut;
// }
if (DbgOut) {
if (IsLocalPrint(DbgOut->OutputString))
DbgOut->OutputString(DbgOut, Buffer);
else
uefi_call_wrapper(DbgOut->OutputString, 2, DbgOut, Buffer);
}
return 0;
}
INTN EFIAPI
_SPrint (
IN VOID *Context,
IN CHAR16 *Buffer
)
// Append string worker for UnicodeSPrint, PoolPrint and CatPrint
{
UINTN len;
POOL_PRINT *spc;
spc = Context;
len = StrLen(Buffer);
//
// Is the string is over the max truncate it
//
if (spc->len + len > spc->maxlen) {
len = spc->maxlen - spc->len;
}
//
// Append the new text
//
CopyMem (spc->str + spc->len, Buffer, len * sizeof(CHAR16));
spc->len += len;
//
// Null terminate it
//
if (spc->len < spc->maxlen) {
spc->str[spc->len] = 0;
} else if (spc->maxlen) {
spc->str[spc->maxlen] = 0;
}
return 0;
}
INTN EFIAPI
_PoolPrint (
IN VOID *Context,
IN CHAR16 *Buffer
)
// Append string worker for PoolPrint and CatPrint
{
UINTN newlen;
POOL_PRINT *spc;
spc = Context;
newlen = spc->len + StrLen(Buffer) + 1;
//
// Is the string is over the max, grow the buffer
//
if (newlen > spc->maxlen) {
//
// Grow the pool buffer
//
newlen += PRINT_STRING_LEN;
spc->maxlen = newlen;
spc->str = ReallocatePool (
spc->str,
spc->len * sizeof(CHAR16),
spc->maxlen * sizeof(CHAR16)
);
if (!spc->str) {
spc->len = 0;
spc->maxlen = 0;
}
}
//
// Append the new text
//
return _SPrint (Context, Buffer);
}
VOID
_PoolCatPrint (
IN CONST CHAR16 *fmt,
IN va_list args,
IN OUT POOL_PRINT *spc,
IN INTN (EFIAPI *Output)(VOID *context, CHAR16 *str)
)
// Dispatch function for UnicodeSPrint, PoolPrint, and CatPrint
{
PRINT_STATE ps;
ZeroMem (&ps, sizeof(ps));
ps.Output = Output;
ps.Context = spc;
ps.fmt.pw = fmt;
va_copy(ps.args, args);
_Print (&ps);
va_end(ps.args);
}
UINTN
UnicodeVSPrint (
OUT CHAR16 *Str,
IN UINTN StrSize,
IN CONST CHAR16 *fmt,
va_list args
)
/*++
Routine Description:
Prints a formatted unicode string to a buffer using a va_list
Arguments:
Str - Output buffer to print the formatted string into
StrSize - Size of Str. String is truncated to this size.
A size of 0 means there is no limit
fmt - The format string
args - va_list
Returns:
String length returned in buffer
--*/
{
POOL_PRINT spc;
spc.str = Str;
spc.maxlen = StrSize / sizeof(CHAR16) - 1;
spc.len = 0;
_PoolCatPrint (fmt, args, &spc, _SPrint);
return spc.len;
}
UINTN
UnicodeSPrint (
OUT CHAR16 *Str,
IN UINTN StrSize,
IN CONST CHAR16 *fmt,
...
)
/*++
Routine Description:
Prints a formatted unicode string to a buffer
Arguments:
Str - Output buffer to print the formatted string into
StrSize - Size of Str. String is truncated to this size.
A size of 0 means there is no limit
fmt - The format string
Returns:
String length returned in buffer
--*/
{
va_list args;
UINTN len;
va_start (args, fmt);
len = UnicodeVSPrint(Str, StrSize, fmt, args);
va_end (args);
return len;
}
CHAR16 *
VPoolPrint (
IN CONST CHAR16 *fmt,
va_list args
)
/*++
Routine Description:
Prints a formatted unicode string to allocated pool using va_list argument.
The caller must free the resulting buffer.
Arguments:
fmt - The format string
args - The arguments in va_list form
Returns:
Allocated buffer with the formatted string printed in it.
The caller must free the allocated buffer. The buffer
allocation is not packed.
--*/
{
POOL_PRINT spc;
ZeroMem (&spc, sizeof(spc));
_PoolCatPrint (fmt, args, &spc, _PoolPrint);
return spc.str;
}
CHAR16 *
PoolPrint (
IN CONST CHAR16 *fmt,
...
)
/*++
Routine Description:
Prints a formatted unicode string to allocated pool. The caller
must free the resulting buffer.
Arguments:
fmt - The format string
Returns:
Allocated buffer with the formatted string printed in it.
The caller must free the allocated buffer. The buffer
allocation is not packed.
--*/
{
va_list args;
CHAR16 *pool;
va_start (args, fmt);
pool = VPoolPrint(fmt, args);
va_end (args);
return pool;
}
CHAR16 *
CatPrint (
IN OUT POOL_PRINT *Str,
IN CONST CHAR16 *fmt,
...
)
/*++
Routine Description:
Concatenates a formatted unicode string to allocated pool.
The caller must free the resulting buffer.
Arguments:
Str - Tracks the allocated pool, size in use, and
amount of pool allocated.
fmt - The format string
Returns:
Allocated buffer with the formatted string printed in it.
The caller must free the allocated buffer. The buffer
allocation is not packed.
--*/
{
va_list args;
va_start (args, fmt);
_PoolCatPrint (fmt, args, Str, _PoolPrint);
va_end (args);
return Str->str;
}
UINTN
Print (
IN CONST CHAR16 *fmt,
...
)
/*++
Routine Description:
Prints a formatted unicode string to the default console
Arguments:
fmt - Format string
Returns:
Length of string printed to the console
--*/
{
va_list args;
UINTN back;
va_start (args, fmt);
back = _IPrint ((UINTN) -1, (UINTN) -1, ST->ConOut, fmt, NULL, args);
va_end (args);
return back;
}
UINTN
VPrint (
IN CONST CHAR16 *fmt,
va_list args
)
/*++
Routine Description:
Prints a formatted unicode string to the default console using a va_list
Arguments:
fmt - Format string
args - va_list
Returns:
Length of string printed to the console
--*/
{
return _IPrint ((UINTN) -1, (UINTN) -1, ST->ConOut, fmt, NULL, args);
}
UINTN
PrintAt (
IN UINTN Column,
IN UINTN Row,
IN CONST CHAR16 *fmt,
...
)
/*++
Routine Description:
Prints a formatted unicode string to the default console, at
the supplied cursor position
Arguments:
Column, Row - The cursor position to print the string at
fmt - Format string
Returns:
Length of string printed to the console
--*/
{
va_list args;
UINTN back;
va_start (args, fmt);
back = _IPrint (Column, Row, ST->ConOut, fmt, NULL, args);
va_end (args);
return back;
}
UINTN
IPrint (
IN SIMPLE_TEXT_OUTPUT_INTERFACE *Out,
IN CONST CHAR16 *fmt,
...
)
/*++
Routine Description:
Prints a formatted unicode string to the specified console
Arguments:
Out - The console to print the string too
fmt - Format string
Returns:
Length of string printed to the console
--*/
{
va_list args;
UINTN back;
va_start (args, fmt);
back = _IPrint ((UINTN) -1, (UINTN) -1, Out, fmt, NULL, args);
va_end (args);
return back;
}
UINTN
IPrintAt (
IN SIMPLE_TEXT_OUTPUT_INTERFACE *Out,
IN UINTN Column,
IN UINTN Row,
IN CONST CHAR16 *fmt,
...
)
/*++
Routine Description:
Prints a formatted unicode string to the specified console, at
the supplied cursor position
Arguments:
Out - The console to print the string to
Column, Row - The cursor position to print the string at
fmt - Format string
Returns:
Length of string printed to the console
--*/
{
va_list args;
UINTN back;
va_start (args, fmt);
back = _IPrint (Column, Row, Out, fmt, NULL, args);
va_end (args);
return back;
}
UINTN
_IPrint (
IN UINTN Column,
IN UINTN Row,
IN SIMPLE_TEXT_OUTPUT_INTERFACE *Out,
IN CONST CHAR16 *fmt,
IN CONST CHAR8 *fmta,
IN va_list args
)
// Display string worker for: Print, PrintAt, IPrint, IPrintAt
{
PRINT_STATE ps;
UINTN back;
ZeroMem (&ps, sizeof(ps));
ps.Context = Out;
ps.Output = (INTN (EFIAPI *)(VOID *, CHAR16 *)) Out->OutputString;
ps.SetAttr = (INTN (EFIAPI *)(VOID *, UINTN)) Out->SetAttribute;
ps.Attr = Out->Mode->Attribute;
back = (ps.Attr >> 4) & 0xF;
ps.AttrNorm = EFI_TEXT_ATTR(EFI_LIGHTGRAY, back);
ps.AttrHighlight = EFI_TEXT_ATTR(EFI_WHITE, back);
ps.AttrError = EFI_TEXT_ATTR(EFI_YELLOW, back);
if (fmt) {
ps.fmt.pw = fmt;
} else {
ps.fmt.Ascii = TRUE;
ps.fmt.pc = fmta;
}
va_copy(ps.args, args);
if (Column != (UINTN) -1) {
uefi_call_wrapper(Out->SetCursorPosition, 3, Out, Column, Row);
}
back = _Print (&ps);
va_end(ps.args);
return back;
}
UINTN
AsciiPrint (
IN CONST CHAR8 *fmt,
...
)
/*++
Routine Description:
For those whom really can't deal with unicode, a print
function that takes an ascii format string
Arguments:
fmt - ascii format string
Returns:
Length of string printed to the console
--*/
{
va_list args;
UINTN back;
va_start (args, fmt);
back = _IPrint ((UINTN) -1, (UINTN) -1, ST->ConOut, NULL, fmt, args);
va_end (args);
return back;
}
UINTN
AsciiVSPrint (
OUT CHAR8 *Str,
IN UINTN StrSize,
IN CONST CHAR8 *fmt,
va_list args
)
/*++
Routine Description:
Prints a formatted ascii string to a buffer using a va_list
Arguments:
Str - Output buffer to print the formatted string into
StrSize - Size of Str. String is truncated to this size.
A size of 0 means there is no limit
fmt - The format string
args - va_list
Returns:
String length returned in buffer
--*/
// Use UnicodeVSPrint() and convert back to ASCII
{
CHAR16 *UnicodeStr, *UnicodeFmt;
UINTN i, Len;
UnicodeStr = AllocatePool(StrSize * sizeof(CHAR16));
if (!UnicodeStr)
return 0;
UnicodeFmt = PoolPrint(L"%a", fmt);
if (!UnicodeFmt) {
FreePool(UnicodeStr);
return 0;
}
Len = UnicodeVSPrint(UnicodeStr, StrSize, UnicodeFmt, args);
FreePool(UnicodeFmt);
// The strings are ASCII so just do a plain Unicode conversion
for (i = 0; i < Len; i++)
Str[i] = (CHAR8)UnicodeStr[i];
Str[Len] = 0;
FreePool(UnicodeStr);
return Len;
}
STATIC
VOID
PFLUSH (
IN OUT PRINT_STATE *ps
)
{
*ps->Pos = 0;
if (IsLocalPrint(ps->Output))
ps->Output(ps->Context, ps->Buffer);
else
uefi_call_wrapper(ps->Output, 2, ps->Context, ps->Buffer);
ps->Pos = ps->Buffer;
}
STATIC
VOID
PSETATTR (
IN OUT PRINT_STATE *ps,
IN UINTN Attr
)
{
PFLUSH (ps);
ps->RestoreAttr = ps->Attr;
if (ps->SetAttr) {
uefi_call_wrapper(ps->SetAttr, 2, ps->Context, Attr);
}
ps->Attr = Attr;
}
STATIC
VOID
PPUTC (
IN OUT PRINT_STATE *ps,
IN CHAR16 c
)
{
// if this is a newline, add a carraige return
if (c == '\n') {
PPUTC (ps, '\r');
}
*ps->Pos = c;
ps->Pos += 1;
ps->Len += 1;
// if at the end of the buffer, flush it
if (ps->Pos >= ps->End) {
PFLUSH(ps);
}
}
STATIC
CHAR16
PGETC (
IN POINTER *p
)
{
CHAR16 c;
c = p->Ascii ? p->pc[p->Index] : p->pw[p->Index];
p->Index += 1;
return c;
}
STATIC
VOID
PITEM (
IN OUT PRINT_STATE *ps
)
{
UINTN Len, i;
PRINT_ITEM *Item;
CHAR16 c;
// Get the length of the item
Item = ps->Item;
Item->Item.Index = 0;
while (Item->Item.Index < Item->FieldWidth) {
c = PGETC(&Item->Item);
if (!c) {
Item->Item.Index -= 1;
break;
}
}
Len = Item->Item.Index;
// if there is no item field width, use the items width
if (Item->FieldWidth == (UINTN) -1) {
Item->FieldWidth = Len;
}
// if item is larger then width, update width
if (Len > Item->Width) {
Item->Width = Len;
}
// if pad field before, add pad char
if (Item->PadBefore) {
for (i=Item->Width; i < Item->FieldWidth; i+=1) {
PPUTC (ps, ' ');
}
}
// pad item
for (i=Len; i < Item->Width; i++) {
PPUTC (ps, Item->Pad);
}
// add the item
Item->Item.Index=0;
while (Item->Item.Index < Len) {
PPUTC (ps, PGETC(&Item->Item));
}
// If pad at the end, add pad char
if (!Item->PadBefore) {
for (i=Item->Width; i < Item->FieldWidth; i+=1) {
PPUTC (ps, ' ');
}
}
}
STATIC
UINTN
_Print (
IN PRINT_STATE *ps
)
/*++
Routine Description:
%w.lF - w = width
l = field width
F = format of arg
Args F:
0 - pad with zeros
- - justify on left (default is on right)
, - add comma's to field
* - width provided on stack
n - Set output attribute to normal (for this field only)
h - Set output attribute to highlight (for this field only)
e - Set output attribute to error (for this field only)
l - Value is 64 bits
a - ascii string
s - unicode string
X - fixed 8 byte value in hex
x - hex value
d - value as signed decimal
u - value as unsigned decimal
f - value as floating point
c - Unicode char
t - EFI time structure
g - Pointer to GUID
r - EFI status code (result code)
D - pointer to Device Path with normal ending.
N - Set output attribute to normal
H - Set output attribute to highlight
E - Set output attribute to error
% - Print a %
Arguments:
SystemTable - The system table
Returns:
Number of charactors written
--*/
{
CHAR16 c;
UINTN Attr;
PRINT_ITEM Item;
CHAR16 Buffer[PRINT_STRING_LEN];
ps->Len = 0;
ps->Buffer = Buffer;
ps->Pos = Buffer;
ps->End = Buffer + PRINT_STRING_LEN - 1;
ps->Item = &Item;
ps->fmt.Index = 0;
while ((c = PGETC(&ps->fmt))) {
if (c != '%') {
PPUTC ( ps, c );
continue;
}
// setup for new item
Item.FieldWidth = (UINTN) -1;
Item.Width = 0;
Item.WidthParse = &Item.Width;
Item.Pad = ' ';
Item.PadBefore = TRUE;
Item.Comma = FALSE;
Item.Long = FALSE;
Item.Item.Ascii = FALSE;
Item.Item.pw = NULL;
ps->RestoreAttr = 0;
Attr = 0;
while ((c = PGETC(&ps->fmt))) {
switch (c) {
case '%':
//
// %% -> %
//
Item.Scratch[0] = '%';
Item.Scratch[1] = 0;
Item.Item.pw = Item.Scratch;
break;
case ',':
Item.Comma = TRUE;
break;
case '-':
Item.PadBefore = FALSE;
break;
case '*':
*Item.WidthParse = va_arg(ps->args, UINTN);
break;
case '.':
Item.WidthParse = &Item.FieldWidth;
break;
case '0':
Item.Pad = '0';
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
*Item.WidthParse = 0;
do {
*Item.WidthParse = *Item.WidthParse * 10 + c - '0';
c = PGETC(&ps->fmt);
} while (c >= '0' && c <= '9') ;
ps->fmt.Index -= 1;
break;
case 'a':
Item.Item.pc = va_arg(ps->args, CHAR8 *);
Item.Item.Ascii = TRUE;
if (!Item.Item.pc) {
Item.Item.pc = (CHAR8 *)"(null)";
}
break;
case 'c':
Item.Scratch[0] = (CHAR16) va_arg(ps->args, UINTN);
Item.Scratch[1] = 0;
Item.Item.pw = Item.Scratch;
break;
case 'D':
{
EFI_DEVICE_PATH *dp = va_arg(ps->args, EFI_DEVICE_PATH *);
CHAR16 *dpstr = DevicePathToStr(dp);
StrnCpy(Item.Scratch, dpstr, PRINT_ITEM_BUFFER_LEN);
Item.Scratch[PRINT_ITEM_BUFFER_LEN-1] = L'\0';
FreePool(dpstr);
Item.Item.pw = Item.Scratch;
break;
}
case 'd':
ValueToString (
Item.Scratch,
Item.Comma,
Item.Long ? va_arg(ps->args, INT64) : va_arg(ps->args, INT32)
);
Item.Item.pw = Item.Scratch;
break;
case 'E':
Attr = ps->AttrError;
break;
case 'e':
PSETATTR(ps, ps->AttrError);
break;
case 'f':
FloatToString (
Item.Scratch,
Item.Comma,
va_arg(ps->args, double)
);
Item.Item.pw = Item.Scratch;
break;
case 'g':
GuidToString (Item.Scratch, va_arg(ps->args, EFI_GUID *));
Item.Item.pw = Item.Scratch;
break;
case 'H':
Attr = ps->AttrHighlight;
break;
case 'h':
PSETATTR(ps, ps->AttrHighlight);
break;
case 'l':
Item.Long = TRUE;
break;
case 'N':
Attr = ps->AttrNorm;
break;
case 'n':
PSETATTR(ps, ps->AttrNorm);
break;
case 'p':
Item.Width = sizeof(void *) == (8 ? 16 : 8) + 2;
Item.Pad = '0';
Item.Scratch[0] = ' ';
Item.Scratch[1] = ' ';
ValueToHex (
Item.Scratch+2,
Item.Long ? va_arg(ps->args, UINT64) : va_arg(ps->args, UINT32)
);
Item.Scratch[0] = '0';
Item.Scratch[1] = 'x';
Item.Item.pw = Item.Scratch;
break;
case 'r':
StatusToString (Item.Scratch, va_arg(ps->args, EFI_STATUS));
Item.Item.pw = Item.Scratch;
break;
case 's':
Item.Item.pw = va_arg(ps->args, CHAR16 *);
if (!Item.Item.pw) {
Item.Item.pw = L"(null)";
}
break;
case 't':
TimeToString (Item.Scratch, va_arg(ps->args, EFI_TIME *));
Item.Item.pw = Item.Scratch;
break;
case 'u':
ValueToString (
Item.Scratch,
Item.Comma,
Item.Long ? va_arg(ps->args, UINT64) : va_arg(ps->args, UINT32)
);
Item.Item.pw = Item.Scratch;
break;
case 'X':
Item.Width = Item.Long ? 16 : 8;
Item.Pad = '0';
#if __GNUC__ >= 7
__attribute__ ((fallthrough));
#endif
case 'x':
ValueToHex (
Item.Scratch,
Item.Long ? va_arg(ps->args, UINT64) : va_arg(ps->args, UINT32)
);
Item.Item.pw = Item.Scratch;
break;
default:
Item.Scratch[0] = '?';
Item.Scratch[1] = 0;
Item.Item.pw = Item.Scratch;
break;
}
// if we have an Item
if (Item.Item.pw) {
PITEM (ps);
break;
}
// if we have an Attr set
if (Attr) {
PSETATTR(ps, Attr);
ps->RestoreAttr = 0;
break;
}
}
if (ps->RestoreAttr) {
PSETATTR(ps, ps->RestoreAttr);
}
}
// Flush buffer
PFLUSH (ps);
return ps->Len;
}
STATIC CHAR8 Hex[] = {'0','1','2','3','4','5','6','7',
'8','9','A','B','C','D','E','F'};
VOID
ValueToHex (
IN CHAR16 *Buffer,
IN UINT64 v
)
{
CHAR8 str[30], *p1;
CHAR16 *p2;
if (!v) {
Buffer[0] = '0';
Buffer[1] = 0;
return ;
}
p1 = str;
p2 = Buffer;
while (v) {
// Without the cast, the MSVC compiler may insert a reference to __allmull
*(p1++) = Hex[(UINTN)(v & 0xf)];
v = RShiftU64 (v, 4);
}
while (p1 != str) {
*(p2++) = *(--p1);
}
*p2 = 0;
}
VOID
ValueToString (
IN CHAR16 *Buffer,
IN BOOLEAN Comma,
IN INT64 v
)
{
STATIC CHAR8 ca[] = { 3, 1, 2 };
CHAR8 str[40], *p1;
CHAR16 *p2;
UINTN c, r;
if (!v) {
Buffer[0] = '0';
Buffer[1] = 0;
return ;
}
p1 = str;
p2 = Buffer;
if (v < 0) {
*(p2++) = '-';
v = -v;
}
while (v) {
v = (INT64)DivU64x32 ((UINT64)v, 10, &r);
*(p1++) = (CHAR8)r + '0';
}
c = (UINTN) (Comma ? ca[(p1 - str) % 3] : 999) + 1;
while (p1 != str) {
c -= 1;
if (!c) {
*(p2++) = ',';
c = 3;
}
*(p2++) = *(--p1);
}
*p2 = 0;
}
// Having this call inlined by VS2022 on Release builds produces an
// "Undefined OpCode Exception" on ARM32 whenever Print() is invoked,
// even when no part of the code below is actually being executed...
// For safety, add an explicit clause to prevent inlining on all platforms.
EFI_NOINLINE
VOID
FloatToString (
IN CHAR16 *Buffer,
IN BOOLEAN Comma,
IN double v
)
{
/*
* Integer part.
*/
INTN i = (INTN)v;
ValueToString(Buffer, Comma, i);
/*
* Decimal point.
*/
UINTN x = StrLen(Buffer);
Buffer[x] = L'.';
x++;
/*
* Keep fractional part.
*/
float f = (float)(v - i);
if (f < 0) f = -f;
/*
* Leading fractional zeroes.
*/
f *= 10.0;
while ( (f != 0)
&& ((INTN)f == 0))
{
Buffer[x] = L'0';
x++;
f *= 10.0;
}
/*
* Fractional digits.
*/
while ((float)(INTN)f != f)
{
f *= 10;
}
ValueToString(Buffer + x, FALSE, (INTN)f);
return;
}
VOID
TimeToString (
OUT CHAR16 *Buffer,
IN EFI_TIME *Time
)
{
UINTN Hour, Year;
CHAR16 AmPm;
AmPm = 'a';
Hour = Time->Hour;
if (Time->Hour == 0) {
Hour = 12;
} else if (Time->Hour >= 12) {
AmPm = 'p';
if (Time->Hour >= 13) {
Hour -= 12;
}
}
Year = Time->Year % 100;
// bugbug: for now just print it any old way
UnicodeSPrint (Buffer, 0, L"%02d/%02d/%02d %02d:%02d%c",
Time->Month,
Time->Day,
Year,
Hour,
Time->Minute,
AmPm
);
}
VOID
DumpHex (
IN UINTN Indent,
IN UINTN Offset,
IN UINTN DataSize,
IN VOID *UserData
)
{
CHAR8 *Data, Val[50], Str[20], c;
UINTN Size, Index;
UINTN ScreenCount;
UINTN TempColumn;
UINTN ScreenSize;
CHAR16 ReturnStr[1];
uefi_call_wrapper(ST->ConOut->QueryMode, 4, ST->ConOut, ST->ConOut->Mode->Mode, &TempColumn, &ScreenSize);
ScreenCount = 0;
ScreenSize -= 2;
Data = UserData;
while (DataSize) {
Size = 16;
if (Size > DataSize) {
Size = DataSize;
}
for (Index=0; Index < Size; Index += 1) {
c = Data[Index];
Val[Index*3+0] = Hex[c>>4];
Val[Index*3+1] = Hex[c&0xF];
Val[Index*3+2] = (Index == 7)?'-':' ';
Str[Index] = (c < ' ' || c > 'z') ? '.' : c;
}
Val[Index*3] = 0;
Str[Index] = 0;
Print (L"%*a%X: %-.48a *%a*\n", Indent, "", Offset, Val, Str);
Data += Size;
Offset += Size;
DataSize -= Size;
ScreenCount++;
if (ScreenCount >= ScreenSize && ScreenSize != 0) {
//
// If ScreenSize == 0 we have the console redirected so don't
// block updates
//
ScreenCount = 0;
Print (L"Press Enter to continue :");
Input (L"", ReturnStr, sizeof(ReturnStr)/sizeof(CHAR16));
Print (L"\n");
}
}
}