[PATCH v6 1/3] riscv: add platform-specific double word shifts for riscv32

Dmitry Antipov
2026-04-25T18:58:19Z

Add riscv32-specific '__ashldi3()', '__ashrdi3()', and '__lshrdi3()'.
Initially it was intended to fix the following link error observed
when building EFI-enabled kernel with CONFIG_EFI_STUB=y and
CONFIG_EFI_GENERIC_STUB=y:

riscv32-linux-gnu-ld: ./drivers/firmware/efi/libstub/lib-cmdline.stub.o: in function `__efistub_.L49':
__efistub_cmdline.c:(.init.text+0x1f2): undefined reference to `__efistub___ashldi3'
riscv32-linux-gnu-ld: __efistub_cmdline.c:(.init.text+0x202): undefined reference to `__efistub___lshrdi3'

Reported at [1] trying to build https://patchew.org/linux/20260212164413.889625-1-dmantipov@yandex.ru,
tested with 'qemu-system-riscv32 -M virt' only.

Reported-by: kernel test robot <lkp@intel.com>
Closes: https://lore.kernel.org/oe-kbuild-all/202603041925.KLKqpK6N-lkp@intel.com [1]
Suggested-by: Ard Biesheuvel <ardb@kernel.org>
Tested-by: Charlie Jenkins <thecharlesjenkins@gmail.com>
Signed-off-by: Dmitry Antipov <dmantipov@yandex.ru>
---
v4 and upwards: bump version to match the series
v3: more tests by Charlie Jenkins
v2: adjust commit message
---
arch/riscv/Kconfig | 3 ---
arch/riscv/kernel/image-vars.h | 7 +++++++
arch/riscv/lib/Makefile | 1 +
arch/riscv/lib/ashldi3.S | 36 +++++++++++++++++++++++++++++++++
arch/riscv/lib/ashrdi3.S | 37 ++++++++++++++++++++++++++++++++++
arch/riscv/lib/lshrdi3.S | 36 +++++++++++++++++++++++++++++++++
6 files changed, 117 insertions(+), 3 deletions(-)
create mode 100644 arch/riscv/lib/ashldi3.S
create mode 100644 arch/riscv/lib/ashrdi3.S
create mode 100644 arch/riscv/lib/lshrdi3.S

diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig
index d235396c4514..b1198b41c486 100644
--- a/arch/riscv/Kconfig
+++ b/arch/riscv/Kconfig
@@ -404,9 +404,6 @@ config ARCH_RV32I
bool "RV32I"
depends on NONPORTABLE
select 32BIT
- select GENERIC_LIB_ASHLDI3
- select GENERIC_LIB_ASHRDI3
- select GENERIC_LIB_LSHRDI3
select GENERIC_LIB_UCMPDI2

config ARCH_RV64I
diff --git a/arch/riscv/kernel/image-vars.h b/arch/riscv/kernel/image-vars.h
index 3bd9d06a8b8f..d66e06daf13e 100644
--- a/arch/riscv/kernel/image-vars.h
+++ b/arch/riscv/kernel/image-vars.h
@@ -32,6 +32,13 @@ __efistub___init_text_end = __init_text_end;
__efistub_sysfb_primary_display = sysfb_primary_display;
#endif

+/*
+ * These double-word integer shifts are used
+ * by the library code and so EFI stub as well.
+ */
+PROVIDE(__efistub___lshrdi3 = __lshrdi3);
+PROVIDE(__efistub___ashldi3 = __ashldi3);
+
#endif

#endif /* __RISCV_KERNEL_IMAGE_VARS_H */
diff --git a/arch/riscv/lib/Makefile b/arch/riscv/lib/Makefile
index 6f767b2a349d..f668b98970bd 100644
--- a/arch/riscv/lib/Makefile
+++ b/arch/riscv/lib/Makefile
@@ -16,6 +16,7 @@ ifeq ($(CONFIG_MMU), y)
lib-$(CONFIG_RISCV_ISA_V) += uaccess_vector.o
endif
lib-$(CONFIG_MMU) += uaccess.o
+lib-$(CONFIG_32BIT) += ashldi3.o ashrdi3.o lshrdi3.o
lib-$(CONFIG_64BIT) += tishift.o
lib-$(CONFIG_RISCV_ISA_ZICBOZ) += clear_page.o
obj-$(CONFIG_FUNCTION_ERROR_INJECTION) += error-inject.o
diff --git a/arch/riscv/lib/ashldi3.S b/arch/riscv/lib/ashldi3.S
new file mode 100644
index 000000000000..c3408862e2f6
--- /dev/null
+++ b/arch/riscv/lib/ashldi3.S
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/**
+ * Adopted for the Linux kernel from IPXE project, see
+ * https://github.com/ipxe/ipxe/blob/master/src/arch/riscv32/libgcc/llshift.S
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+
+/**
+ * Shift left
+ *
+ * @v a1:a0 Value to shift
+ * @v a2 Shift amount
+ * @ret a1:a0 Shifted value
+ */
+
+SYM_FUNC_START(__ashldi3)
+
+ /* Perform shift by 32 bits, if applicable */
+ li t0, 32
+ sub t1, t0, a2
+ bgtz t1, 1f
+ mv a1, a0
+ mv a0, zero
+1: /* Perform shift by modulo-32 bits, if applicable */
+ andi a2, a2, 0x1f
+ beqz a2, 2f
+ srl t2, a0, t1
+ sll a0, a0, a2
+ sll a1, a1, a2
+ or a1, a1, t2
+2: ret
+
+SYM_FUNC_END(__ashldi3)
+EXPORT_SYMBOL(__ashldi3)
diff --git a/arch/riscv/lib/ashrdi3.S b/arch/riscv/lib/ashrdi3.S
new file mode 100644
index 000000000000..dd42b3cbb173
--- /dev/null
+++ b/arch/riscv/lib/ashrdi3.S
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/**
+ * Adopted for the Linux kernel from IPXE project, see
+ * https://github.com/ipxe/ipxe/blob/master/src/arch/riscv32/libgcc/llshift.S
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+
+/**
+ * Arithmetic shift right
+ *
+ * @v a1:a0 Value to shift
+ * @v a2 Shift amount
+ * @ret a1:a0 Shifted value
+ */
+
+SYM_FUNC_START(__ashrdi3)
+
+ /* Perform shift by 32 bits, if applicable */
+ li t0, 32
+ sub t1, t0, a2
+ bgtz t1, 1f
+ mv a0, a1
+ srai a1, a1, 16
+ srai a1, a1, 16
+1: /* Perform shift by modulo-32 bits, if applicable */
+ andi a2, a2, 0x1f
+ beqz a2, 2f
+ sll t2, a1, t1
+ sra a1, a1, a2
+ srl a0, a0, a2
+ or a0, a0, t2
+2: ret
+
+SYM_FUNC_END(__ashrdi3)
+EXPORT_SYMBOL(__ashrdi3)
diff --git a/arch/riscv/lib/lshrdi3.S b/arch/riscv/lib/lshrdi3.S
new file mode 100644
index 000000000000..1af03985ccb7
--- /dev/null
+++ b/arch/riscv/lib/lshrdi3.S
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/**
+ * Adopted for the Linux kernel from IPXE project, see
+ * https://github.com/ipxe/ipxe/blob/master/src/arch/riscv32/libgcc/llshift.S
+ */
+
+#include <linux/linkage.h>
+#include <asm/asm.h>
+
+/**
+ * Logical shift right
+ *
+ * @v a1:a0 Value to shift
+ * @v a2 Shift amount
+ * @ret a1:a0 Shifted value
+ */
+
+SYM_FUNC_START(__lshrdi3)
+
+ /* Perform shift by 32 bits, if applicable */
+ li t0, 32
+ sub t1, t0, a2
+ bgtz t1, 1f
+ mv a0, a1
+ mv a1, zero
+1: /* Perform shift by modulo-32 bits, if applicable */
+ andi a2, a2, 0x1f
+ beqz a2, 2f
+ sll t2, a1, t1
+ srl a1, a1, a2
+ srl a0, a0, a2
+ or a0, a0, t2
+2: ret
+
+SYM_FUNC_END(__lshrdi3)
+EXPORT_SYMBOL(__lshrdi3)
--
2.53.0