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 add t4, a0, a1 92 addi t4, t4, -1 93 andi t4, t4, -SZREG 94 95 /* Get the first word. */ 96 REG_L t1, 0(t0) 97 98 /* 99 * Shift away the partial data we loaded to remove the irrelevant bytes 100 * preceding the string with the effect of adding NUL bytes at the 101 * end of the string's first word. 102 */ 103 SHIFT t1, t1, t2 104 105 /* Convert non-NUL into 0xff and NUL into 0x00. */ 106 orc.b t1, t1 107 108 /* Convert non-NUL into 0x00 and NUL into 0xff. */ 109 not t1, t1 110 111 /* 112 * Search for the first set bit (corresponding to a NUL byte in the 113 * original chunk). 114 */ 115 CZ t1, t1 116 117 /* 118 * The first chunk is special: compare against the number 119 * of valid bytes in this chunk. 120 */ 121 srli a0, t1, 3 122 123 /* Limit the result by maxlen. */ 124 minu a0, a0, a1 125 126 bgtu t3, a0, 2f 127 128 /* All remaining bytes are in the first word, no loop needed. */ 129 bgeu t0, t4, 2f 130 131 /* Prepare for the word comparison loop. */ 132 addi t2, t0, SZREG 133 li t3, -1 134 135 /* 136 * Our critical loop is 4 instructions and processes data in 137 * 4 byte or 8 byte chunks. 138 */ 139 .p2align 3 1401: 141 REG_L t1, SZREG(t0) 142 addi t0, t0, SZREG 143 orc.b t1, t1 144 bgeu t0, t4, 4f 145 beq t1, t3, 1b 1464: 147 not t1, t1 148 CZ t1, t1 149 srli t1, t1, 3 150 151 /* Get number of processed bytes. */ 152 sub t2, t0, t2 153 154 /* Add number of characters in the first word. */ 155 add a0, a0, t2 156 157 /* Add number of characters in the last word. */ 158 add a0, a0, t1 159 160 /* Ensure the final result does not exceed maxlen. */ 161 minu a0, a0, a1 1622: 163 ret 1643: 165 mv a0, a1 166 ret 167 168.option pop 169#endif 170SYM_FUNC_END(strnlen) 171SYM_FUNC_ALIAS(__pi_strnlen, strnlen) 172EXPORT_SYMBOL(strnlen) 173