]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
boot: Add memcmp/memcpy/memset
authorJan Janssen <medhefgo@web.de>
Tue, 24 May 2022 11:29:43 +0000 (13:29 +0200)
committerJan Janssen <medhefgo@web.de>
Tue, 31 May 2022 13:14:56 +0000 (15:14 +0200)
src/boot/efi/efi-string.c
src/boot/efi/efi-string.h
src/boot/efi/test-efi-string.c

index 540ee0b223e586c8db7fec4864a3a2afe860c400..1626ccf2049c8025b10dcb564f1d40e7a66b1d00 100644 (file)
@@ -4,7 +4,6 @@
 #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
@@ -138,3 +137,62 @@ DEFINE_STRCPY(char16_t, strcpy16);
 
 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
index 9307810a5b9304aaf372874490bbb5d998b33755..79d810402218c041ec4c1b3a2a0d5d084d7f12be 100644 (file)
@@ -5,6 +5,8 @@
 #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);
 
@@ -71,3 +73,18 @@ char16_t *strcpy16(char16_t * restrict dest, const char16_t * restrict src);
 
 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);
index bc033d6fc246fb7eee77f812bf66adbb3ba4309c..087f9d7e9958a0241ef6707449576db05b07203c 100644 (file)
@@ -240,4 +240,55 @@ TEST(strchr16) {
         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);