1 /*
2  * Copyright (c) 2017 Imagination Technologies.
3  *
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  *      * Redistributions of source code must retain the above copyright
11  *        notice, this list of conditions and the following disclaimer.
12  *      * Redistributions in binary form must reproduce the above copyright
13  *        notice, this list of conditions and the following disclaimer
14  *        in the documentation and/or other materials provided with
15  *        the distribution.
16  *      * Neither the name of Imagination Technologies nor the names of its
17  *        contributors may be used to endorse or promote products derived
18  *        from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
32 
33 #include <string.h>
34 
35 #define op_t        unsigned long int
36 
37 #if !defined(UNALIGNED_INSTR_SUPPORT)
38 /* does target have unaligned lw/ld/ualw/uald instructions? */
39 #define UNALIGNED_INSTR_SUPPORT 0
40 #if __mips_isa_rev < 6 && !__mips1
41 #undef UNALIGNED_INSTR_SUPPORT
42 #define UNALIGNED_INSTR_SUPPORT 1
43 #endif
44 #endif
45 
46 #if !defined(HW_UNALIGNED_SUPPORT)
47 /* Does target have hardware support for unaligned accesses?  */
48 #define HW_UNALIGNED_SUPPORT 0
49 #if __mips_isa_rev >= 6
50 #undef HW_UNALIGNED_SUPPORT
51 #define HW_UNALIGNED_SUPPORT 1
52 #endif
53 #endif
54 
55 #if __mips64
56 typedef struct
57 {
58   op_t B0:8, B1:8, B2:8, B3:8, B4:8, B5:8, B6:8, B7:8;
59 } bits_t;
60 #else
61 typedef struct
62 {
63   op_t B0:8, B1:8, B2:8, B3:8;
64 } bits_t;
65 #endif
66 
67 typedef union
68 {
69   op_t v;
70   bits_t b;
71 } bitfields_t;
72 
73 #if !HW_UNALIGNED_SUPPORT && UNALIGNED_INSTR_SUPPORT
74 /* for MIPS GCC, there are no unaligned builtins - so this struct forces
75    the compiler to treat the pointer access as unaligned.  */
76 struct ulw
77 {
78   op_t uli;
79 } __attribute__ ((packed));
80 #endif /* !HW_UNALIGNED_SUPPORT && UNALIGNED_INSTR_SUPPORT */
81 
82 #define DO_BYTE(i, ptdst) {  \
83   *(ptdst+i) = a.b.B##i;     \
84   if(a.b.B##i == '\0')       \
85     return ret;              \
86 }
87 
88 #if __mips64
89 #define DO_BYTES(val, dst) {   \
90   bitfields_t a;               \
91   char *tdst = (char *)(dst);  \
92   a.v = val;                   \
93   DO_BYTE(0, tdst)             \
94   DO_BYTE(1, tdst)             \
95   DO_BYTE(2, tdst)             \
96   DO_BYTE(3, tdst)             \
97   DO_BYTE(4, tdst)             \
98   DO_BYTE(5, tdst)             \
99   DO_BYTE(6, tdst)             \
100   DO_BYTE(7, tdst)             \
101 }
102 #else
103 #define DO_BYTES(val, dst) {   \
104   bitfields_t a;               \
105   char *tdst = (char *)(dst);  \
106   a.v = val;                   \
107   DO_BYTE(0, tdst)             \
108   DO_BYTE(1, tdst)             \
109   DO_BYTE(2, tdst)             \
110   DO_BYTE(3, tdst)             \
111 }
112 #endif
113 
114 #define DO_WORD_ALIGNED(dst, src) {                 \
115   op_t val = *(src);                                \
116   if ((((val - mask_1) & ~val) & mask_128) != 0) {  \
117     DO_BYTES(val, dst);                             \
118   } else *(dst) = val;                              \
119 }
120 
121 #if !HW_UNALIGNED_SUPPORT
122 #if UNALIGNED_INSTR_SUPPORT
123 #define DO_WORD_UNALIGNED(dst, src) {               \
124   op_t val = *(src);                                \
125   if ((((val - mask_1) & ~val) & mask_128) != 0) {  \
126     DO_BYTES(val, dst);                             \
127   } else {                                          \
128     struct ulw *a = (struct ulw *)(dst);            \
129     a->uli = val;                                   \
130   }                                                 \
131 }
132 #else
133 #define DO_WORD_UNALIGNED(dst, src) {                 \
134   op_t val = *(src);                                  \
135   if ((((val - mask_1) & ~val) & mask_128) != 0) {    \
136     DO_BYTES(val, dst);                               \
137   } else {                                            \
138     char *pdst = (char *) dst;                        \
139     const char *psrc = (const char *) src;            \
140     for (; (*pdst = *psrc) != '\0'; ++psrc, ++pdst);  \
141     return ret;                                       \
142   }                                                   \
143 }
144 #endif /* UNALIGNED_INSTR_SUPPORT */
145 
146 #define PROCESS_UNALIGNED_WORDS(a, b) { \
147   while (1) {                           \
148     DO_WORD_UNALIGNED(a, b);            \
149     DO_WORD_UNALIGNED(a + 1, b + 1);    \
150     DO_WORD_UNALIGNED(a + 2, b + 2);    \
151     DO_WORD_UNALIGNED(a + 3, b + 3);    \
152     a += 4;                             \
153     b += 4;                             \
154   }                                     \
155 }
156 #endif /* HW_UNALIGNED_SUPPORT */
157 
158 #define PROCESS_ALIGNED_WORDS(a, b) {  \
159   while (1) {                          \
160     DO_WORD_ALIGNED(a, b);             \
161     DO_WORD_ALIGNED(a + 1, b + 1);     \
162     DO_WORD_ALIGNED(a + 2, b + 2);     \
163     DO_WORD_ALIGNED(a + 3, b + 3);     \
164     a += 4;                            \
165     b += 4;                            \
166   }                                    \
167 }
168 
169 char *
strcpy(char * to,const char * from)170 strcpy (char *to, const char *from) __overloadable
171 {
172   char *ret = to;
173   op_t mask_1, mask_128;
174   const op_t *src;
175   op_t *dst;
176 
177   for (; (*to = *from) != '\0' && ((size_t) from % sizeof (op_t)) != 0; ++from, ++to);
178 
179   if(*to != '\0') {
180     __asm__ volatile (
181       "li %0, 0x01010101 \n\t"
182       : "=r" (mask_1)
183     );
184 #if __mips64
185     mask_1 |= mask_1 << 32;
186 #endif
187     mask_128 = mask_1 << 7;
188 
189     src = (const op_t *) from;
190     dst = (op_t *) to;
191 
192 #if HW_UNALIGNED_SUPPORT
193     PROCESS_ALIGNED_WORDS(dst, src);
194 #else
195     if (((unsigned long) dst) % sizeof (op_t) == 0) {
196       PROCESS_ALIGNED_WORDS(dst, src);
197     } else {
198       PROCESS_UNALIGNED_WORDS(dst, src);
199     }
200 #endif
201   }
202 
203   return ret;
204 }
205