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 /* 87 * Aligned boundary. Use the address of the last valid byte 88 * (s + count - 1) to avoid loading a word past the count 89 * boundary in the loop below. count == 0 is handled above. 90 * 91 * Saturate the boundary when s + count would wrap around (very 92 * large counts, e.g. SIZE_MAX passed by FORTIFY strcat/strlcat 93 * with a destination whose size is unknown at compile time). 94 * Without this, the wrapped boundary lands before s and the 95 * pre-loop guard below always exits, returning a truncated 96 * length. 97 * 98 * Clamping the increment to ~s (== SIZE_MAX - s) keeps the 99 * computation branchless and wrap-free: 100 * 101 * s + min(count - 1, ~s) == saturating_add(s, count - 1) 102 * 103 * Saturating makes the scan equivalent to strlen(). 104 */ 105 addi t4, a1, -1 /* count - 1 */ 106 not t1, a0 /* SIZE_MAX - s */ 107 minu t4, t4, t1 /* clamp, so s + t4 can never wrap */ 108 add t4, a0, t4 /* saturated s + count - 1 */ 109 andi t4, t4, -SZREG 110 111 /* Get the first word. */ 112 REG_L t1, 0(t0) 113 114 /* 115 * Shift away the partial data we loaded to remove the irrelevant bytes 116 * preceding the string with the effect of adding NUL bytes at the 117 * end of the string's first word. 118 */ 119 SHIFT t1, t1, t2 120 121 /* Convert non-NUL into 0xff and NUL into 0x00. */ 122 orc.b t1, t1 123 124 /* Convert non-NUL into 0x00 and NUL into 0xff. */ 125 not t1, t1 126 127 /* 128 * Search for the first set bit (corresponding to a NUL byte in the 129 * original chunk). 130 */ 131 CZ t1, t1 132 133 /* 134 * The first chunk is special: compare against the number 135 * of valid bytes in this chunk. 136 */ 137 srli a0, t1, 3 138 139 /* Limit the result by maxlen. */ 140 minu a0, a0, a1 141 142 bgtu t3, a0, 2f 143 144 /* All remaining bytes are in the first word, no loop needed. */ 145 bgeu t0, t4, 2f 146 147 /* Prepare for the word comparison loop. */ 148 addi t2, t0, SZREG 149 li t3, -1 150 151 /* 152 * Our critical loop is 4 instructions and processes data in 153 * 4 byte or 8 byte chunks. 154 */ 155 .p2align 3 1561: 157 REG_L t1, SZREG(t0) 158 addi t0, t0, SZREG 159 orc.b t1, t1 160 bgeu t0, t4, 4f 161 beq t1, t3, 1b 1624: 163 not t1, t1 164 CZ t1, t1 165 srli t1, t1, 3 166 167 /* Get number of processed bytes. */ 168 sub t2, t0, t2 169 170 /* Add number of characters in the first word. */ 171 add a0, a0, t2 172 173 /* Add number of characters in the last word. */ 174 add a0, a0, t1 175 176 /* Ensure the final result does not exceed maxlen. */ 177 minu a0, a0, a1 1782: 179 ret 1803: 181 mv a0, a1 182 ret 183 184.option pop 185#endif 186SYM_FUNC_END(strnlen) 187SYM_FUNC_ALIAS(__pi_strnlen, strnlen) 188EXPORT_SYMBOL(strnlen) 189