RISC-V: add infrastructure to allow different str* implementations
authorHeiko Stuebner <heiko.stuebner@vrull.eu>
Fri, 13 Jan 2023 21:23:00 +0000 (22:23 +0100)
committerPalmer Dabbelt <palmer@rivosinc.com>
Tue, 31 Jan 2023 19:43:23 +0000 (11:43 -0800)
Depending on supported extensions on specific RISC-V cores,
optimized str* functions might make sense.

This adds basic infrastructure to allow patching the function calls
via alternatives later on.

The Linux kernel provides standard implementations for string functions
but when architectures want to extend them, they need to provide their
own.

The added generic string functions are done in assembler (taken from
disassembling the main-kernel functions for now) to allow us to control
the used registers and extend them with optimized variants.

This doesn't override the compiler's use of builtin replacements. So still
first of all the compiler will select if a builtin will be better suitable
i.e. for known strings. For all regular cases we will want to later
select possible optimized variants and in the worst case fall back to the
generic implemention added with this change.

Reviewed-by: Andrew Jones <ajones@ventanamicro.com>
Signed-off-by: Heiko Stuebner <heiko.stuebner@vrull.eu>
Reviewed-by: Conor Dooley <conor.dooley@microchip.com>
Link: https://lore.kernel.org/r/20230113212301.3534711-2-heiko@sntech.de
Signed-off-by: Palmer Dabbelt <palmer@rivosinc.com>
arch/riscv/include/asm/string.h
arch/riscv/kernel/riscv_ksyms.c
arch/riscv/lib/Makefile
arch/riscv/lib/strcmp.S [new file with mode: 0644]
arch/riscv/lib/strlen.S [new file with mode: 0644]
arch/riscv/lib/strncmp.S [new file with mode: 0644]
arch/riscv/purgatory/Makefile

index 9090493665555c0d12248aae799c80509c2deadc..a96b1fea24fe43089d1f9e5d00f8417a2213ff0f 100644 (file)
@@ -18,6 +18,16 @@ extern asmlinkage void *__memcpy(void *, const void *, size_t);
 #define __HAVE_ARCH_MEMMOVE
 extern asmlinkage void *memmove(void *, const void *, size_t);
 extern asmlinkage void *__memmove(void *, const void *, size_t);
+
+#define __HAVE_ARCH_STRCMP
+extern asmlinkage int strcmp(const char *cs, const char *ct);
+
+#define __HAVE_ARCH_STRLEN
+extern asmlinkage __kernel_size_t strlen(const char *);
+
+#define __HAVE_ARCH_STRNCMP
+extern asmlinkage int strncmp(const char *cs, const char *ct, size_t count);
+
 /* For those files which don't want to check by kasan. */
 #if defined(CONFIG_KASAN) && !defined(__SANITIZE_ADDRESS__)
 #define memcpy(dst, src, len) __memcpy(dst, src, len)
index 5ab1c7e1a6ed5dc098034e88ecbddb196bdc060f..a72879b4249a5d54813ffb5f0204745dbd3c06ac 100644 (file)
@@ -12,6 +12,9 @@
 EXPORT_SYMBOL(memset);
 EXPORT_SYMBOL(memcpy);
 EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(strcmp);
+EXPORT_SYMBOL(strlen);
+EXPORT_SYMBOL(strncmp);
 EXPORT_SYMBOL(__memset);
 EXPORT_SYMBOL(__memcpy);
 EXPORT_SYMBOL(__memmove);
index 25d5c9664e57e4f20e7c0c36ef47c1e1d096ac1c..6c74b0bedd60df2ec995b8fc9b684a28388a655c 100644 (file)
@@ -3,6 +3,9 @@ lib-y                   += delay.o
 lib-y                  += memcpy.o
 lib-y                  += memset.o
 lib-y                  += memmove.o
+lib-y                  += strcmp.o
+lib-y                  += strlen.o
+lib-y                  += strncmp.o
 lib-$(CONFIG_MMU)      += uaccess.o
 lib-$(CONFIG_64BIT)    += tishift.o
 
diff --git a/arch/riscv/lib/strcmp.S b/arch/riscv/lib/strcmp.S
new file mode 100644 (file)
index 0000000..8babd71
--- /dev/null
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm-generic/export.h>
+
+/* int strcmp(const char *cs, const char *ct) */
+SYM_FUNC_START(strcmp)
+       /*
+        * Returns
+        *   a0 - comparison result, value like strcmp
+        *
+        * Parameters
+        *   a0 - string1
+        *   a1 - string2
+        *
+        * Clobbers
+        *   t0, t1
+        */
+1:
+       lbu     t0, 0(a0)
+       lbu     t1, 0(a1)
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+       bne     t0, t1, 2f
+       bnez    t0, 1b
+       li      a0, 0
+       ret
+2:
+       /*
+        * strcmp only needs to return (< 0, 0, > 0) values
+        * not necessarily -1, 0, +1
+        */
+       sub     a0, t0, t1
+       ret
+SYM_FUNC_END(strcmp)
diff --git a/arch/riscv/lib/strlen.S b/arch/riscv/lib/strlen.S
new file mode 100644 (file)
index 0000000..0a3b118
--- /dev/null
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm-generic/export.h>
+
+/* int strlen(const char *s) */
+SYM_FUNC_START(strlen)
+       /*
+        * Returns
+        *   a0 - string length
+        *
+        * Parameters
+        *   a0 - String to measure
+        *
+        * Clobbers:
+        *   t0, t1
+        */
+       mv      t1, a0
+1:
+       lbu     t0, 0(t1)
+       beqz    t0, 2f
+       addi    t1, t1, 1
+       j       1b
+2:
+       sub     a0, t1, a0
+       ret
+SYM_FUNC_END(strlen)
diff --git a/arch/riscv/lib/strncmp.S b/arch/riscv/lib/strncmp.S
new file mode 100644 (file)
index 0000000..1f644d0
--- /dev/null
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm-generic/export.h>
+
+/* int strncmp(const char *cs, const char *ct, size_t count) */
+SYM_FUNC_START(strncmp)
+       /*
+        * Returns
+        *   a0 - comparison result, value like strncmp
+        *
+        * Parameters
+        *   a0 - string1
+        *   a1 - string2
+        *   a2 - number of characters to compare
+        *
+        * Clobbers
+        *   t0, t1, t2
+        */
+       li      t2, 0
+1:
+       beq     a2, t2, 2f
+       lbu     t0, 0(a0)
+       lbu     t1, 0(a1)
+       addi    a0, a0, 1
+       addi    a1, a1, 1
+       bne     t0, t1, 3f
+       addi    t2, t2, 1
+       bnez    t0, 1b
+2:
+       li      a0, 0
+       ret
+3:
+       /*
+        * strncmp only needs to return (< 0, 0, > 0) values
+        * not necessarily -1, 0, +1
+        */
+       sub     a0, t0, t1
+       ret
+SYM_FUNC_END(strncmp)
index dd58e1d99397291a21a0f7ebbbca9799e37f0415..d16bf715a586bb5595e5017da7c99870b096d36e 100644 (file)
@@ -2,6 +2,7 @@
 OBJECT_FILES_NON_STANDARD := y
 
 purgatory-y := purgatory.o sha256.o entry.o string.o ctype.o memcpy.o memset.o
+purgatory-y += strcmp.o strlen.o strncmp.o
 
 targets += $(purgatory-y)
 PURGATORY_OBJS = $(addprefix $(obj)/,$(purgatory-y))
@@ -18,6 +19,15 @@ $(obj)/memcpy.o: $(srctree)/arch/riscv/lib/memcpy.S FORCE
 $(obj)/memset.o: $(srctree)/arch/riscv/lib/memset.S FORCE
        $(call if_changed_rule,as_o_S)
 
+$(obj)/strcmp.o: $(srctree)/arch/riscv/lib/strcmp.S FORCE
+       $(call if_changed_rule,as_o_S)
+
+$(obj)/strlen.o: $(srctree)/arch/riscv/lib/strlen.S FORCE
+       $(call if_changed_rule,as_o_S)
+
+$(obj)/strncmp.o: $(srctree)/arch/riscv/lib/strncmp.S FORCE
+       $(call if_changed_rule,as_o_S)
+
 $(obj)/sha256.o: $(srctree)/lib/crypto/sha256.c FORCE
        $(call if_changed_rule,cc_o_c)
 
@@ -77,6 +87,9 @@ CFLAGS_ctype.o                        += $(PURGATORY_CFLAGS)
 AFLAGS_REMOVE_entry.o          += -Wa,-gdwarf-2
 AFLAGS_REMOVE_memcpy.o         += -Wa,-gdwarf-2
 AFLAGS_REMOVE_memset.o         += -Wa,-gdwarf-2
+AFLAGS_REMOVE_strcmp.o         += -Wa,-gdwarf-2
+AFLAGS_REMOVE_strlen.o         += -Wa,-gdwarf-2
+AFLAGS_REMOVE_strncmp.o                += -Wa,-gdwarf-2
 
 $(obj)/purgatory.ro: $(PURGATORY_OBJS) FORCE
                $(call if_changed,ld)