| /* SPDX-License-Identifier: GPL-2.0-only */ | 
 |  | 
 | #include <linux/linkage.h> | 
 | #include <asm/asm.h> | 
 | #include <asm/alternative-macros.h> | 
 | #include <asm/hwcap.h> | 
 |  | 
 | /* int strncmp(const char *cs, const char *ct, size_t count) */ | 
 | SYM_FUNC_START(strncmp) | 
 |  | 
 | 	__ALTERNATIVE_CFG("nop", "j strncmp_zbb", 0, RISCV_ISA_EXT_ZBB, | 
 | 		IS_ENABLED(CONFIG_RISCV_ISA_ZBB) && IS_ENABLED(CONFIG_TOOLCHAIN_HAS_ZBB)) | 
 |  | 
 | 	/* | 
 | 	 * 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 | 
 |  | 
 | /* | 
 |  * Variant of strncmp using the ZBB extension if available | 
 |  */ | 
 | #if defined(CONFIG_RISCV_ISA_ZBB) && defined(CONFIG_TOOLCHAIN_HAS_ZBB) | 
 | strncmp_zbb: | 
 |  | 
 | .option push | 
 | .option arch,+zbb | 
 |  | 
 | 	/* | 
 | 	 * Returns | 
 | 	 *   a0 - comparison result, like strncmp | 
 | 	 * | 
 | 	 * Parameters | 
 | 	 *   a0 - string1 | 
 | 	 *   a1 - string2 | 
 | 	 *   a2 - number of characters to compare | 
 | 	 * | 
 | 	 * Clobbers | 
 | 	 *   t0, t1, t2, t3, t4, t5, t6 | 
 | 	 */ | 
 |  | 
 | 	or	t2, a0, a1 | 
 | 	li	t5, -1 | 
 | 	and	t2, t2, SZREG-1 | 
 | 	add	t4, a0, a2 | 
 | 	bnez	t2, 3f | 
 |  | 
 | 	/* Adjust limit for fast-path.  */ | 
 | 	andi	t6, t4, -SZREG | 
 |  | 
 | 	/* Main loop for aligned string.  */ | 
 | 	.p2align 3 | 
 | 1: | 
 | 	bge	a0, t6, 3f | 
 | 	REG_L	t0, 0(a0) | 
 | 	REG_L	t1, 0(a1) | 
 | 	orc.b	t3, t0 | 
 | 	bne	t3, t5, 2f | 
 | 	orc.b	t3, t1 | 
 | 	bne	t3, t5, 2f | 
 | 	addi	a0, a0, SZREG | 
 | 	addi	a1, a1, SZREG | 
 | 	beq	t0, t1, 1b | 
 |  | 
 | 	/* | 
 | 	 * Words don't match, and no null byte in the first | 
 | 	 * word. Get bytes in big-endian order and compare. | 
 | 	 */ | 
 | #ifndef CONFIG_CPU_BIG_ENDIAN | 
 | 	rev8	t0, t0 | 
 | 	rev8	t1, t1 | 
 | #endif | 
 |  | 
 | 	/* Synthesize (t0 >= t1) ? 1 : -1 in a branchless sequence.  */ | 
 | 	sltu	a0, t0, t1 | 
 | 	neg	a0, a0 | 
 | 	ori	a0, a0, 1 | 
 | 	ret | 
 |  | 
 | 2: | 
 | 	/* | 
 | 	 * Found a null byte. | 
 | 	 * If words don't match, fall back to simple loop. | 
 | 	 */ | 
 | 	bne	t0, t1, 3f | 
 |  | 
 | 	/* Otherwise, strings are equal.  */ | 
 | 	li	a0, 0 | 
 | 	ret | 
 |  | 
 | 	/* Simple loop for misaligned strings.  */ | 
 | 	.p2align 3 | 
 | 3: | 
 | 	bge	a0, t4, 5f | 
 | 	lbu	t0, 0(a0) | 
 | 	lbu	t1, 0(a1) | 
 | 	addi	a0, a0, 1 | 
 | 	addi	a1, a1, 1 | 
 | 	bne	t0, t1, 4f | 
 | 	bnez	t0, 3b | 
 |  | 
 | 4: | 
 | 	sub	a0, t0, t1 | 
 | 	ret | 
 |  | 
 | 5: | 
 | 	li	a0, 0 | 
 | 	ret | 
 |  | 
 | .option pop | 
 | #endif | 
 | SYM_FUNC_END(strncmp) | 
 | SYM_FUNC_ALIAS(__pi_strncmp, strncmp) | 
 | EXPORT_SYMBOL(strncmp) |