#include <stdint.h>
#include "efi-string.h"
-#include "macro-fundamental.h"
/* String functions for both char and char16_t that should behave the same way as their respective
* counterpart in userspace. Where it makes sense, these accept NULL and do something sensible whereas
DEFINE_STRCHR(char, strchr8);
DEFINE_STRCHR(char16_t, strchr16);
+
+int efi_memcmp(const void *p1, const void *p2, size_t n) {
+ if (!p1 || !p2)
+ return CMP(p1, p2);
+
+ const uint8_t *up1 = p1, *up2 = p2;
+ while (n > 0) {
+ if (*up1 != *up2)
+ return *up1 - *up2;
+
+ up1++;
+ up2++;
+ n--;
+ }
+
+ return 0;
+}
+
+void *efi_memcpy(void * restrict dest, const void * restrict src, size_t n) {
+ if (!dest || !src || n == 0)
+ return dest;
+
+ uint8_t *d = dest;
+ const uint8_t *s = src;
+
+ while (n > 0) {
+ *d = *s;
+ d++;
+ s++;
+ n--;
+ }
+
+ return dest;
+}
+
+void *efi_memset(void *p, int c, size_t n) {
+ if (!p || n == 0)
+ return p;
+
+ uint8_t *q = p;
+ while (n > 0) {
+ *q = c;
+ q++;
+ n--;
+ }
+
+ return p;
+}
+
+#ifdef SD_BOOT
+# undef memcmp
+# undef memcpy
+# undef memset
+/* Provide the actual implementation for the builtins. To prevent a linker error, we mark memcpy/memset as
+ * weak, because gnu-efi is currently providing them. */
+__attribute__((alias("efi_memcmp"))) int memcmp(const void *p1, const void *p2, size_t n);
+__attribute__((weak, alias("efi_memcpy"))) void *memcpy(void * restrict dest, const void * restrict src, size_t n);
+__attribute__((weak, alias("efi_memset"))) void *memset(void *p, int c, size_t n);
+#endif
#include <stddef.h>
#include <uchar.h>
+#include "macro-fundamental.h"
+
size_t strnlen8(const char *s, size_t n);
size_t strnlen16(const char16_t *s, size_t n);
char *strchr8(const char *s, char c);
char16_t *strchr16(const char16_t *s, char16_t c);
+
+#ifdef SD_BOOT
+/* The compiler normaly has knowledge about standard functions such as memcmp, but this is not the case when
+ * compiling with -ffreestanding. By referring to builtins, the compiler can check arguments and do
+ * optimizations again. Note that we still need to provide implementations as the compiler is free to not
+ * inline its own implementation and instead issue a library call. */
+# define memcmp __builtin_memcmp
+# define memcpy __builtin_memcpy
+# define memset __builtin_memset
+#endif
+
+/* The actual implementations of builtins with efi_ prefix so we can unit test them. */
+int efi_memcmp(const void *p1, const void *p2, size_t n);
+void *efi_memcpy(void * restrict dest, const void * restrict src, size_t n);
+void *efi_memset(void *p, int c, size_t n);
assert_se(strchr16(str, 'B') == &str[4]);
}
+TEST(efi_memcmp) {
+ assert_se(efi_memcmp(NULL, NULL, 0) == 0);
+ assert_se(efi_memcmp(NULL, NULL, 1) == 0);
+ assert_se(efi_memcmp(NULL, "", 1) < 0);
+ assert_se(efi_memcmp("", NULL, 1) > 0);
+ assert_se(efi_memcmp("", "", 0) == 0);
+ assert_se(efi_memcmp("", "", 1) == 0);
+ assert_se(efi_memcmp("1", "1", 1) == 0);
+ assert_se(efi_memcmp("1", "2", 1) < 0);
+ assert_se(efi_memcmp("A", "a", 1) < 0);
+ assert_se(efi_memcmp("a", "A", 1) > 0);
+ assert_se(efi_memcmp("abc", "ab", 2) == 0);
+ assert_se(efi_memcmp("ab", "abc", 3) < 0);
+ assert_se(efi_memcmp("ab\000bd", "ab\000bd", 6) == 0);
+ assert_se(efi_memcmp("ab\000b\0", "ab\000bd", 6) < 0);
+}
+
+TEST(efi_memcpy) {
+ char buf[10];
+
+ assert_se(!efi_memcpy(NULL, NULL, 0));
+ assert_se(!efi_memcpy(NULL, "", 1));
+ assert_se(efi_memcpy(buf, NULL, 0) == buf);
+ assert_se(efi_memcpy(buf, NULL, 1) == buf);
+ assert_se(efi_memcpy(buf, "a", 0) == buf);
+
+ assert_se(efi_memcpy(buf, "", 1) == buf);
+ assert_se(memcmp(buf, "", 1) == 0);
+ assert_se(efi_memcpy(buf, "1", 1) == buf);
+ assert_se(memcmp(buf, "1", 1) == 0);
+ assert_se(efi_memcpy(buf, "23", 3) == buf);
+ assert_se(memcmp(buf, "23", 3) == 0);
+ assert_se(efi_memcpy(buf, "45\0ab\0\0\0c", 9) == buf);
+ assert_se(memcmp(buf, "45\0ab\0\0\0c", 9) == 0);
+}
+
+TEST(efi_memset) {
+ char buf[10];
+
+ assert_se(!efi_memset(NULL, '1', 0));
+ assert_se(!efi_memset(NULL, '1', 1));
+ assert_se(efi_memset(buf, '1', 0) == buf);
+
+ assert_se(efi_memset(buf, '2', 1) == buf);
+ assert_se(memcmp(buf, "2", 1) == 0);
+ assert_se(efi_memset(buf, '4', 4) == buf);
+ assert_se(memcmp(buf, "4444", 4) == 0);
+ assert_se(efi_memset(buf, 'a', 10) == buf);
+ assert_se(memcmp(buf, "aaaaaaaaaa", 10) == 0);
+}
+
DEFINE_TEST_MAIN(LOG_INFO);