1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "efi-string.h"
6 # include "proto/simple-text-io.h"
10 # include "alloc-util.h"
11 # define xnew(t, n) ASSERT_SE_PTR(new(t, n))
12 # define xmalloc(n) ASSERT_SE_PTR(malloc(n))
15 /* String functions for both char and char16_t that should behave the same way as their respective
16 * counterpart in userspace. Where it makes sense, these accept NULL and do something sensible whereas
17 * userspace does not allow for this (strlen8(NULL) returns 0 like strlen_ptr(NULL) for example). To make it
18 * easier to tell in code which kind of string they work on, we use 8/16 suffixes. This also makes is easier
19 * to unit test them. */
21 #define DEFINE_STRNLEN(type, name) \
22 size_t name(const type *s, size_t n) { \
27 while (len < n && *s) { \
35 DEFINE_STRNLEN(char, strnlen8
);
36 DEFINE_STRNLEN(char16_t
, strnlen16
);
41 (_c >= 'A' && _c <= 'Z') ? _c + ('a' - 'A') : _c; \
44 #define DEFINE_STRTOLOWER(type, name) \
45 void name(type *s) { \
52 DEFINE_STRTOLOWER(char, strtolower8
);
53 DEFINE_STRTOLOWER(char16_t
, strtolower16
);
55 #define DEFINE_STRNCASECMP(type, name, tolower) \
56 int name(const type *s1, const type *s2, size_t n) { \
61 type c1 = *s1, c2 = *s2; \
66 if (!c1 || c1 != c2) \
77 DEFINE_STRNCASECMP(char, strncmp8
, false);
78 DEFINE_STRNCASECMP(char16_t
, strncmp16
, false);
79 DEFINE_STRNCASECMP(char, strncasecmp8
, true);
80 DEFINE_STRNCASECMP(char16_t
, strncasecmp16
, true);
82 #define DEFINE_STRCPY(type, name) \
83 type *name(type * restrict dest, const type * restrict src) { \
84 type *ret = ASSERT_PTR(dest); \
101 DEFINE_STRCPY(char, strcpy8
);
102 DEFINE_STRCPY(char16_t
, strcpy16
);
104 #define DEFINE_STRCHR(type, name) \
105 type *name(const type *s, type c) { \
115 return c ? NULL : (type *) s; \
118 DEFINE_STRCHR(char, strchr8
);
119 DEFINE_STRCHR(char16_t
, strchr16
);
121 #define DEFINE_STRNDUP(type, name, len_func) \
122 type *name(const type *s, size_t n) { \
126 size_t len = len_func(s, n); \
127 size_t size = len * sizeof(type); \
129 type *dup = xmalloc(size + sizeof(type)); \
131 memcpy(dup, s, size); \
137 DEFINE_STRNDUP(char, xstrndup8
, strnlen8
);
138 DEFINE_STRNDUP(char16_t
, xstrndup16
, strnlen16
);
140 static unsigned utf8_to_unichar(const char *utf8
, size_t n
, char32_t
*c
) {
147 if (!(utf8
[0] & 0x80)) {
150 } else if ((utf8
[0] & 0xe0) == 0xc0) {
152 unichar
= utf8
[0] & 0x1f;
153 } else if ((utf8
[0] & 0xf0) == 0xe0) {
155 unichar
= utf8
[0] & 0x0f;
156 } else if ((utf8
[0] & 0xf8) == 0xf0) {
158 unichar
= utf8
[0] & 0x07;
159 } else if ((utf8
[0] & 0xfc) == 0xf8) {
161 unichar
= utf8
[0] & 0x03;
162 } else if ((utf8
[0] & 0xfe) == 0xfc) {
164 unichar
= utf8
[0] & 0x01;
175 for (unsigned i
= 1; i
< len
; i
++) {
176 if ((utf8
[i
] & 0xc0) != 0x80) {
181 unichar
|= utf8
[i
] & 0x3f;
188 /* Convert UTF-8 to UCS-2, skipping any invalid or short byte sequences. */
189 char16_t
*xstrn8_to_16(const char *str8
, size_t n
) {
194 char16_t
*str16
= xnew(char16_t
, n
+ 1);
196 while (n
> 0 && *str8
!= '\0') {
199 size_t utf8len
= utf8_to_unichar(str8
, n
, &unichar
);
201 n
= LESS_BY(n
, utf8len
);
205 case 0xe000U
... 0xffffU
:
206 str16
[i
++] = unichar
;
215 char *startswith8(const char *s
, const char *prefix
) {
224 if (!strneq8(s
, prefix
, l
))
227 return (char*) s
+ l
;
230 static bool efi_fnmatch_prefix(const char16_t
*p
, const char16_t
*h
, const char16_t
**ret_p
, const char16_t
**ret_h
) {
239 /* End of pattern. Check that haystack is now empty. */
244 if (*p
== '\0' || *p
!= *h
)
245 /* Trailing escape or no match. */
251 /* Early end of haystack. */
256 /* Point ret_p at the remainder of the pattern. */
265 /* Early end of haystack. */
268 bool first
= true, can_range
= true, match
= false;
269 for (;; first
= false) {
284 /* End of set unless it's the first char. */
285 if (*p
== ']' && !first
)
288 /* Range pattern if '-' is not first or last in set. */
289 if (*p
== '-' && can_range
&& !first
&& *(p
+ 1) != ']') {
290 char16_t low
= *(p
- 1);
297 if (low
<= *h
&& *h
<= *p
)
300 /* Ranges cannot be chained: [a-c-f] == [-abcf] */
316 /* Single char mismatch. */
321 /* Patterns are fnmatch-compatible (with reduced feature support). */
322 bool efi_fnmatch(const char16_t
*pattern
, const char16_t
*haystack
) {
323 /* Patterns can be considered as simple patterns (without '*') concatenated by '*'. By doing so we
324 * simply have to make sure the very first simple pattern matches the start of haystack. Then we just
325 * look for the remaining simple patterns *somewhere* within the haystack (in order) as any extra
326 * characters in between would be matches by the '*'. We then only have to ensure that the very last
327 * simple pattern matches at the actual end of the haystack.
329 * This means we do not need to use backtracking which could have catastrophic runtimes with the
330 * right input data. */
332 for (bool first
= true;;) {
333 const char16_t
*pattern_tail
= NULL
, *haystack_tail
= NULL
;
334 bool match
= efi_fnmatch_prefix(pattern
, haystack
, &pattern_tail
, &haystack_tail
);
337 /* Initial simple pattern must match. */
340 /* No '*' was in pattern, we can return early. */
347 pattern
= pattern_tail
;
348 haystack
= haystack_tail
;
350 /* If we have a match this must be at the end of the haystack. Note that
351 * efi_fnmatch_prefix compares the NUL-bytes at the end, so we cannot match the end
352 * of pattern in the middle of haystack). */
353 if (match
|| *haystack
== '\0')
356 /* Match one character using '*'. */
362 #define DEFINE_PARSE_NUMBER(type, name) \
363 bool name(const type *s, uint64_t *ret_u, const type **ret_tail) { \
369 /* Need at least one digit. */ \
370 if (*s < '0' || *s > '9') \
374 while (*s >= '0' && *s <= '9') { \
375 if (__builtin_mul_overflow(u, 10, &u)) \
377 if (__builtin_add_overflow(u, *s - '0', &u)) \
382 if (!ret_tail && *s != '\0') \
391 DEFINE_PARSE_NUMBER(char, parse_number8
);
392 DEFINE_PARSE_NUMBER(char16_t
, parse_number16
);
394 char16_t
*hexdump(const void *data
, size_t size
) {
395 static const char hex
[16] = "0123456789abcdef";
396 const uint8_t *d
= data
;
398 assert(data
|| size
== 0);
400 char16_t
*buf
= xnew(char16_t
, size
* 2 + 1);
402 for (size_t i
= 0; i
< size
; i
++) {
403 buf
[i
* 2] = hex
[d
[i
] >> 4];
404 buf
[i
* 2 + 1] = hex
[d
[i
] & 0x0F];
411 static const char * const warn_table
[] = {
412 [EFI_SUCCESS
] = "Success",
413 [EFI_WARN_UNKNOWN_GLYPH
] = "Unknown glyph",
414 [EFI_WARN_DELETE_FAILURE
] = "Delete failure",
415 [EFI_WARN_WRITE_FAILURE
] = "Write failure",
416 [EFI_WARN_BUFFER_TOO_SMALL
] = "Buffer too small",
417 [EFI_WARN_STALE_DATA
] = "Stale data",
418 [EFI_WARN_FILE_SYSTEM
] = "File system",
419 [EFI_WARN_RESET_REQUIRED
] = "Reset required",
422 /* Errors have MSB set, remove it to keep the table compact. */
423 #define NOERR(err) ((err) & ~EFI_ERROR_MASK)
425 static const char * const err_table
[] = {
426 [NOERR(EFI_ERROR_MASK
)] = "Error",
427 [NOERR(EFI_LOAD_ERROR
)] = "Load error",
428 [NOERR(EFI_INVALID_PARAMETER
)] = "Invalid parameter",
429 [NOERR(EFI_UNSUPPORTED
)] = "Unsupported",
430 [NOERR(EFI_BAD_BUFFER_SIZE
)] = "Bad buffer size",
431 [NOERR(EFI_BUFFER_TOO_SMALL
)] = "Buffer too small",
432 [NOERR(EFI_NOT_READY
)] = "Not ready",
433 [NOERR(EFI_DEVICE_ERROR
)] = "Device error",
434 [NOERR(EFI_WRITE_PROTECTED
)] = "Write protected",
435 [NOERR(EFI_OUT_OF_RESOURCES
)] = "Out of resources",
436 [NOERR(EFI_VOLUME_CORRUPTED
)] = "Volume corrupt",
437 [NOERR(EFI_VOLUME_FULL
)] = "Volume full",
438 [NOERR(EFI_NO_MEDIA
)] = "No media",
439 [NOERR(EFI_MEDIA_CHANGED
)] = "Media changed",
440 [NOERR(EFI_NOT_FOUND
)] = "Not found",
441 [NOERR(EFI_ACCESS_DENIED
)] = "Access denied",
442 [NOERR(EFI_NO_RESPONSE
)] = "No response",
443 [NOERR(EFI_NO_MAPPING
)] = "No mapping",
444 [NOERR(EFI_TIMEOUT
)] = "Time out",
445 [NOERR(EFI_NOT_STARTED
)] = "Not started",
446 [NOERR(EFI_ALREADY_STARTED
)] = "Already started",
447 [NOERR(EFI_ABORTED
)] = "Aborted",
448 [NOERR(EFI_ICMP_ERROR
)] = "ICMP error",
449 [NOERR(EFI_TFTP_ERROR
)] = "TFTP error",
450 [NOERR(EFI_PROTOCOL_ERROR
)] = "Protocol error",
451 [NOERR(EFI_INCOMPATIBLE_VERSION
)] = "Incompatible version",
452 [NOERR(EFI_SECURITY_VIOLATION
)] = "Security violation",
453 [NOERR(EFI_CRC_ERROR
)] = "CRC error",
454 [NOERR(EFI_END_OF_MEDIA
)] = "End of media",
455 [NOERR(EFI_ERROR_RESERVED_29
)] = "Reserved (29)",
456 [NOERR(EFI_ERROR_RESERVED_30
)] = "Reserved (30)",
457 [NOERR(EFI_END_OF_FILE
)] = "End of file",
458 [NOERR(EFI_INVALID_LANGUAGE
)] = "Invalid language",
459 [NOERR(EFI_COMPROMISED_DATA
)] = "Compromised data",
460 [NOERR(EFI_IP_ADDRESS_CONFLICT
)] = "IP address conflict",
461 [NOERR(EFI_HTTP_ERROR
)] = "HTTP error",
464 static const char *status_to_string(EFI_STATUS status
) {
465 if (status
<= ELEMENTSOF(warn_table
) - 1)
466 return warn_table
[status
];
467 if (status
>= EFI_ERROR_MASK
&& status
<= ((ELEMENTSOF(err_table
) - 1) | EFI_ERROR_MASK
))
468 return err_table
[NOERR(status
)];
473 size_t padded_len
; /* Field width in printf. */
474 size_t len
; /* Precision in printf. */
477 bool alternative_form
;
480 bool have_field_width
;
489 char sign_pad
; /* For + and (space) flags. */
493 char16_t stack_buf
[128]; /* We use stack_buf first to avoid allocations in most cases. */
494 char16_t
*dyn_buf
; /* Allocated buf or NULL if stack_buf is used. */
495 char16_t
*buf
; /* Points to the current active buf. */
496 size_t n_buf
; /* Len of buf (in char16_t's, not bytes!). */
497 size_t n
; /* Used len of buf (in char16_t's). This is always <n_buf. */
504 static void grow_buf(FormatContext
*ctx
, size_t need
) {
507 assert_se(!__builtin_add_overflow(ctx
->n
, need
, &need
));
509 if (need
< ctx
->n_buf
)
512 /* Greedily allocate if we can. */
513 if (__builtin_mul_overflow(need
, 2, &ctx
->n_buf
))
516 /* We cannot use realloc here as ctx->buf may be ctx->stack_buf, which we cannot free. */
517 char16_t
*new_buf
= xnew(char16_t
, ctx
->n_buf
);
518 memcpy(new_buf
, ctx
->buf
, ctx
->n
* sizeof(*ctx
->buf
));
521 ctx
->buf
= ctx
->dyn_buf
= new_buf
;
524 static void push_padding(FormatContext
*ctx
, char pad
, size_t len
) {
528 ctx
->buf
[ctx
->n
++] = pad
;
532 static bool push_str(FormatContext
*ctx
, SpecifierContext
*sp
) {
536 sp
->padded_len
= LESS_BY(sp
->padded_len
, sp
->len
);
538 grow_buf(ctx
, sp
->padded_len
+ sp
->len
);
541 push_padding(ctx
, ' ', sp
->padded_len
);
543 /* In userspace unit tests we cannot just memcpy() the wide string. */
544 if (sp
->wstr
&& sizeof(wchar_t) == sizeof(char16_t
)) {
545 memcpy(ctx
->buf
+ ctx
->n
, sp
->wstr
, sp
->len
* sizeof(*sp
->wstr
));
548 for (size_t i
= 0; i
< sp
->len
; i
++)
549 ctx
->buf
[ctx
->n
++] = sp
->str
? sp
->str
[i
] : sp
->wstr
[i
];
552 push_padding(ctx
, ' ', sp
->padded_len
);
554 assert(ctx
->n
< ctx
->n_buf
);
558 static bool push_num(FormatContext
*ctx
, SpecifierContext
*sp
, uint64_t u
) {
559 const char *digits
= sp
->lowercase
? "0123456789abcdef" : "0123456789ABCDEF";
565 assert(IN_SET(sp
->base
, 10, 16));
567 /* "%.0u" prints nothing if value is 0. */
568 if (u
== 0 && sp
->len
== 0)
571 if (sp
->is_signed
&& (int64_t) u
< 0) {
572 /* We cannot just do "u = -(int64_t)u" here because -INT64_MIN overflows. */
574 uint64_t rem
= -((int64_t) u
% sp
->base
);
575 u
= (int64_t) u
/ -sp
->base
;
576 tmp
[n
++] = digits
[rem
];
580 while (u
> 0 || n
== 0) {
581 uint64_t rem
= u
% sp
->base
;
583 tmp
[n
++] = digits
[rem
];
586 /* Note that numbers never get truncated! */
587 size_t prefix
= (sp
->sign_pad
!= 0 ? 1 : 0) + (sp
->alternative_form
? 2 : 0);
588 size_t number_len
= prefix
+ MAX(n
, sp
->len
);
589 grow_buf(ctx
, MAX(sp
->padded_len
, number_len
));
593 /* Leading zeroes go after the sign or 0x prefix. */
594 number_len
= MAX(number_len
, sp
->padded_len
);
596 padding
= LESS_BY(sp
->padded_len
, number_len
);
599 push_padding(ctx
, ' ', padding
);
601 if (sp
->sign_pad
!= 0)
602 ctx
->buf
[ctx
->n
++] = sp
->sign_pad
;
603 if (sp
->alternative_form
) {
604 ctx
->buf
[ctx
->n
++] = '0';
605 ctx
->buf
[ctx
->n
++] = sp
->lowercase
? 'x' : 'X';
608 push_padding(ctx
, '0', LESS_BY(number_len
, n
+ prefix
));
611 ctx
->buf
[ctx
->n
++] = tmp
[--n
];
614 push_padding(ctx
, ' ', padding
);
616 assert(ctx
->n
< ctx
->n_buf
);
620 /* This helps unit testing. */
622 # define NULLSTR "(null)"
623 # define wcsnlen strnlen16
625 # define NULLSTR "(nil)"
628 static bool handle_format_specifier(FormatContext
*ctx
, SpecifierContext
*sp
) {
629 /* Parses one item from the format specifier in ctx and put the info into sp. If we are done with
630 * this specifier returns true, otherwise this function should be called again. */
632 /* This implementation assumes 32-bit ints. Also note that all types smaller than int are promoted to
633 * int in vararg functions, which is why we fetch only ints for any such types. The compiler would
634 * otherwise warn about fetching smaller types. */
635 assert_cc(sizeof(int) == 4);
636 assert_cc(sizeof(wchar_t) <= sizeof(int));
637 assert_cc(sizeof(intmax_t) <= sizeof(long long));
642 switch (*ctx
->format
) {
644 sp
->alternative_form
= true;
647 sp
->have_field_width
= true;
650 sp
->align_left
= true;
654 sp
->sign_pad
= *ctx
->format
;
658 if (!sp
->have_field_width
) {
663 /* If field width has already been provided then 0 is part of precision (%.0s). */
670 if (*ctx
->format
== '*')
671 i
= va_arg(ctx
->ap
, int);
674 if (!parse_number8(ctx
->format
, &u
, &ctx
->format
) || u
> INT_MAX
)
675 assert_not_reached();
676 ctx
->format
--; /* Point it back to the last digit. */
680 if (sp
->have_field_width
) {
681 /* Negative precision is ignored. */
683 sp
->len
= (size_t) i
;
685 /* Negative field width is treated as positive field width with '-' flag. */
688 sp
->align_left
= true;
697 if (*(ctx
->format
+ 1) == 'h')
699 /* char/short gets promoted to int, nothing to do here. */
703 if (*(ctx
->format
+ 1) == 'l') {
705 sp
->longlong_arg
= true;
711 sp
->long_arg
= sizeof(size_t) == sizeof(long);
712 sp
->longlong_arg
= !sp
->long_arg
&& sizeof(size_t) == sizeof(long long);
716 sp
->long_arg
= sizeof(intmax_t) == sizeof(long);
717 sp
->longlong_arg
= !sp
->long_arg
&& sizeof(intmax_t) == sizeof(long long);
721 sp
->long_arg
= sizeof(ptrdiff_t) == sizeof(long);
722 sp
->longlong_arg
= !sp
->long_arg
&& sizeof(ptrdiff_t) == sizeof(long long);
728 return push_str(ctx
, sp
);
731 sp
->wstr
= &(wchar_t){ va_arg(ctx
->ap
, int) };
733 return push_str(ctx
, sp
);
737 sp
->wstr
= va_arg(ctx
->ap
, const wchar_t *) ?: L
"(null)";
738 sp
->len
= wcsnlen(sp
->wstr
, sp
->len
);
740 sp
->str
= va_arg(ctx
->ap
, const char *) ?: "(null)";
741 sp
->len
= strnlen8(sp
->str
, sp
->len
);
743 return push_str(ctx
, sp
);
750 sp
->lowercase
= *ctx
->format
== 'x';
751 sp
->is_signed
= IN_SET(*ctx
->format
, 'd', 'i');
752 sp
->base
= IN_SET(*ctx
->format
, 'x', 'X') ? 16 : 10;
753 if (sp
->len
== SIZE_MAX
)
757 if (sp
->longlong_arg
)
758 v
= sp
->is_signed
? (uint64_t) va_arg(ctx
->ap
, long long) :
759 va_arg(ctx
->ap
, unsigned long long);
760 else if (sp
->long_arg
)
761 v
= sp
->is_signed
? (uint64_t) va_arg(ctx
->ap
, long) : va_arg(ctx
->ap
, unsigned long);
763 v
= sp
->is_signed
? (uint64_t) va_arg(ctx
->ap
, int) : va_arg(ctx
->ap
, unsigned);
765 return push_num(ctx
, sp
, v
);
768 const void *ptr
= va_arg(ctx
->ap
, const void *);
771 sp
->len
= STRLEN(NULLSTR
);
772 return push_str(ctx
, sp
);
776 sp
->lowercase
= true;
777 sp
->alternative_form
= true;
778 sp
->len
= 0; /* Precision is ignored for %p. */
779 return push_num(ctx
, sp
, (uintptr_t) ptr
);
783 sp
->str
= status_to_string(ctx
->status
);
785 sp
->len
= strlen8(sp
->str
);
786 return push_str(ctx
, sp
);
790 sp
->lowercase
= true;
791 sp
->alternative_form
= true;
793 return push_num(ctx
, sp
, ctx
->status
);
797 assert_not_reached();
801 /* printf_internal is largely compatible to userspace vasprintf. Any features omitted should trigger asserts.
804 * - Flags: #, 0, +, -, space
805 * - Lengths: h, hh, l, ll, z, j, t
806 * - Specifiers: %, c, s, u, i, d, x, X, p, m
807 * - Precision and width (inline or as int arg using *)
809 * Notable differences:
810 * - Passing NULL to %s is permitted and will print "(null)"
811 * - %p will also use "(null)"
812 * - The provided EFI_STATUS is used for %m instead of errno
813 * - "\n" is translated to "\r\n" */
814 _printf_(2, 0) static char16_t
*printf_internal(EFI_STATUS status
, const char *format
, va_list ap
, bool ret
) {
817 FormatContext ctx
= {
818 .buf
= ctx
.stack_buf
,
819 .n_buf
= ELEMENTSOF(ctx
.stack_buf
),
824 /* We cannot put this into the struct without making a copy. */
827 while (*ctx
.format
!= '\0') {
828 SpecifierContext sp
= { .len
= SIZE_MAX
};
830 switch (*ctx
.format
) {
833 while (!handle_format_specifier(&ctx
, &sp
))
844 sp
.str
= ctx
.format
++;
845 while (!IN_SET(*ctx
.format
, '%', '\n', '\0'))
847 sp
.len
= ctx
.format
- sp
.str
;
854 assert(ctx
.n
< ctx
.n_buf
);
855 ctx
.buf
[ctx
.n
++] = '\0';
859 return TAKE_PTR(ctx
.dyn_buf
);
861 char16_t
*ret_buf
= xnew(char16_t
, ctx
.n
);
862 memcpy(ret_buf
, ctx
.buf
, ctx
.n
* sizeof(*ctx
.buf
));
867 ST
->ConOut
->OutputString(ST
->ConOut
, ctx
.buf
);
870 return mfree(ctx
.dyn_buf
);
873 void printf_status(EFI_STATUS status
, const char *format
, ...) {
875 va_start(ap
, format
);
876 printf_internal(status
, format
, ap
, false);
880 void vprintf_status(EFI_STATUS status
, const char *format
, va_list ap
) {
881 printf_internal(status
, format
, ap
, false);
884 char16_t
*xasprintf_status(EFI_STATUS status
, const char *format
, ...) {
886 va_start(ap
, format
);
887 char16_t
*ret
= printf_internal(status
, format
, ap
, true);
892 char16_t
*xvasprintf_status(EFI_STATUS status
, const char *format
, va_list ap
) {
893 return printf_internal(status
, format
, ap
, true);
897 /* To provide the actual implementation for these we need to remove the redirection to the builtins. */
902 _used_
void *memchr(const void *p
, int c
, size_t n
);
903 _used_
int memcmp(const void *p1
, const void *p2
, size_t n
);
904 _used_
void *memcpy(void * restrict dest
, const void * restrict src
, size_t n
);
905 _used_
void *memset(void *p
, int c
, size_t n
);
907 /* And for userspace unit testing we need to give them an efi_ prefix. */
908 # define memchr efi_memchr
909 # define memcmp efi_memcmp
910 # define memcpy efi_memcpy
911 # define memset efi_memset
914 void *memchr(const void *p
, int c
, size_t n
) {
918 const uint8_t *q
= p
;
919 for (size_t i
= 0; i
< n
; i
++)
920 if (q
[i
] == (unsigned char) c
)
921 return (void *) (q
+ i
);
926 int memcmp(const void *p1
, const void *p2
, size_t n
) {
927 const uint8_t *up1
= p1
, *up2
= p2
;
946 void *memcpy(void * restrict dest
, const void * restrict src
, size_t n
) {
947 if (!dest
|| !src
|| n
== 0)
951 /* The firmware-provided memcpy is likely optimized, so use that. The function is guaranteed to be
952 * available by the UEFI spec. We still make it depend on the boot services pointer being set just in
953 * case the compiler emits a call before it is available. */
955 BS
->CopyMem(dest
, (void *) src
, n
);
961 const uint8_t *s
= src
;
973 void *memset(void *p
, int c
, size_t n
) {
978 /* See comment in efi_memcpy. Note that the signature has c and n swapped! */