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 9090493..a96b1fe 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 5ab1c7e..a72879b 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 25d5c96..6c74b0b 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 dd58e1d..d16bf71 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)