1/* SPDX-License-Identifier: GPL-2.0-only */ 2 3/* 4 * Base on arch/riscv/lib/strlen.S 5 * 6 * Copyright (C) Feng Jiang <jiangfeng@kylinos.cn> 7 */ 8 9#include <linux/linkage.h> 10#include <asm/asm.h> 11#include <asm/alternative-macros.h> 12#include <asm/hwcap.h> 13 14/* size_t strnlen(const char *s, size_t count) */ 15SYM_FUNC_START(strnlen) 16 17 __ALTERNATIVE_CFG("nop", "j strnlen_zbb", 0, RISCV_ISA_EXT_ZBB, 18 IS_ENABLED(CONFIG_RISCV_ISA_ZBB) && IS_ENABLED(CONFIG_TOOLCHAIN_HAS_ZBB)) 19 20 21 /* 22 * Returns 23 * a0 - String length 24 * 25 * Parameters 26 * a0 - String to measure 27 * a1 - Max length of string 28 * 29 * Clobbers 30 * t0, t1, t2 31 */ 32 addi t1, a0, -1 33 add t2, a0, a1 341: 35 addi t1, t1, 1 36 beq t1, t2, 2f 37 lbu t0, 0(t1) 38 bnez t0, 1b 392: 40 sub a0, t1, a0 41 ret 42 43 44/* 45 * Variant of strnlen using the ZBB extension if available 46 */ 47#if defined(CONFIG_RISCV_ISA_ZBB) && defined(CONFIG_TOOLCHAIN_HAS_ZBB) 48strnlen_zbb: 49 50#ifdef CONFIG_CPU_BIG_ENDIAN 51# define CZ clz 52# define SHIFT sll 53#else 54# define CZ ctz 55# define SHIFT srl 56#endif 57 58.option push 59.option arch,+zbb 60 61 /* 62 * Returns 63 * a0 - String length 64 * 65 * Parameters 66 * a0 - String to measure 67 * a1 - Max length of string 68 * 69 * Clobbers 70 * t0, t1, t2, t3, t4 71 */ 72 73 /* If maxlen is 0, return 0. */ 74 beqz a1, 3f 75 76 /* Number of irrelevant bytes in the first word. */ 77 andi t2, a0, SZREG-1 78 79 /* Align pointer. */ 80 andi t0, a0, -SZREG 81 82 li t3, SZREG 83 sub t3, t3, t2 84 slli t2, t2, 3 85 86 /* Aligned boundary. */ 87 add t4, a0, a1 88 andi t4, t4, -SZREG 89 90 /* Get the first word. */ 91 REG_L t1, 0(t0) 92 93 /* 94 * Shift away the partial data we loaded to remove the irrelevant bytes 95 * preceding the string with the effect of adding NUL bytes at the 96 * end of the string's first word. 97 */ 98 SHIFT t1, t1, t2 99 100 /* Convert non-NUL into 0xff and NUL into 0x00. */ 101 orc.b t1, t1 102 103 /* Convert non-NUL into 0x00 and NUL into 0xff. */ 104 not t1, t1 105 106 /* 107 * Search for the first set bit (corresponding to a NUL byte in the 108 * original chunk). 109 */ 110 CZ t1, t1 111 112 /* 113 * The first chunk is special: compare against the number 114 * of valid bytes in this chunk. 115 */ 116 srli a0, t1, 3 117 118 /* Limit the result by maxlen. */ 119 minu a0, a0, a1 120 121 bgtu t3, a0, 2f 122 123 /* Prepare for the word comparison loop. */ 124 addi t2, t0, SZREG 125 li t3, -1 126 127 /* 128 * Our critical loop is 4 instructions and processes data in 129 * 4 byte or 8 byte chunks. 130 */ 131 .p2align 3 1321: 133 REG_L t1, SZREG(t0) 134 addi t0, t0, SZREG 135 orc.b t1, t1 136 bgeu t0, t4, 4f 137 beq t1, t3, 1b 1384: 139 not t1, t1 140 CZ t1, t1 141 srli t1, t1, 3 142 143 /* Get number of processed bytes. */ 144 sub t2, t0, t2 145 146 /* Add number of characters in the first word. */ 147 add a0, a0, t2 148 149 /* Add number of characters in the last word. */ 150 add a0, a0, t1 151 152 /* Ensure the final result does not exceed maxlen. */ 153 minu a0, a0, a1 1542: 155 ret 1563: 157 mv a0, a1 158 ret 159 160.option pop 161#endif 162SYM_FUNC_END(strnlen) 163SYM_FUNC_ALIAS(__pi_strnlen, strnlen) 164EXPORT_SYMBOL(strnlen) 165