1 /*        $NetBSD: memset2.c,v 1.11 2022/01/15 10:38:56 andvar Exp $  */
2 
3 /*-
4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by Matt Thomas <matt@3am-software.com>.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #if defined(LIBC_SCCS) && !defined(lint)
34 __RCSID("$NetBSD: memset2.c,v 1.11 2022/01/15 10:38:56 andvar Exp $");
35 #endif /* LIBC_SCCS and not lint */
36 
37 #include <sys/types.h>
38 
39 #if !defined(_KERNEL) && !defined(_STANDALONE)
40 #include <assert.h>
41 #include <limits.h>
42 #include <string.h>
43 #include <inttypes.h>
44 #else
45 #include <lib/libkern/libkern.h>
46 #include <machine/limits.h>
47 #endif
48 
49 #include <sys/endian.h>
50 #include <machine/types.h>
51 
52 #undef __OPTIMIZE_SIZE__
53 #define __OPTIMIZE_SIZE__ 1   /* other code path is very broken */
54 
55 #ifdef TEST
56 #include <assert.h>
57 #define _DIAGASSERT(a)                  assert(a)
58 #endif
59 
60 #ifdef _FORTIFY_SOURCE
61 #undef bzero
62 #endif
63 #undef memset
64 
65 /*
66  * Assume __register_t is the widest non-synthetic unsigned type.
67  */
68 typedef __register_t memword_t;
69 
70 #ifdef BZERO
71 static inline
72 #define   memset memset0
73 #endif
74 
75 #ifdef TEST
76 static
77 #define memset test_memset
78 #endif
79 
80 void *
memset(void * addr,int c,size_t len)81 memset(void *addr, int c, size_t len)
82 {
83           memword_t *dstp = addr;
84           memword_t *edstp;
85           memword_t fill;
86 #ifndef __OPTIMIZE_SIZE__
87           memword_t keep_mask = 0;
88 #endif
89           size_t fill_count;
90 
91           _DIAGASSERT(addr != 0);
92 
93           if (__predict_false(len == 0))
94                     return addr;
95 
96           /*
97            * Pad out the fill byte (v) across a memword_t.
98            * The conditional at the end prevents GCC from complaining about
99            * shift count >= width of type
100            */
101           fill = (unsigned char)c;
102           fill |= fill << 8;
103           fill |= fill << 16;
104           fill |= fill << (sizeof(c) < sizeof(fill) ? 32 : 0);
105 
106           /*
107            * Get the number of unaligned bytes to fill in the first word.
108            */
109           fill_count = -(uintptr_t)addr & (sizeof(memword_t) - 1);
110 
111           if (__predict_false(fill_count != 0)) {
112 #ifndef __OPTIMIZE_SIZE__
113                     /*
114                      * We want to clear <fill_count> trailing bytes in the word.
115                      * On big/little endian, these are the least/most significant,
116                      * bits respectively.  So as we shift, the keep_mask will only
117                      * have bits set for the bytes we won't be filling.
118                      */
119 #if BYTE_ORDER == BIG_ENDIAN
120                     keep_mask = ~(memword_t)0U << (fill_count * 8);
121 #endif
122 #if BYTE_ORDER == LITTLE_ENDIAN
123                     keep_mask = ~(memword_t)0U >> (fill_count * 8);
124 #endif
125                     /*
126                      * Make sure dstp is aligned to a memword_t boundary.
127                      */
128                     dstp = (memword_t *)((uintptr_t)addr & -sizeof(memword_t));
129                     if (len >= fill_count) {
130                               /*
131                                * If we can fill the rest of this word, then we mask
132                                * off the bytes we are filling and then fill in those
133                                * bytes with the new fill value.
134                                */
135                               *dstp = (*dstp & keep_mask) | (fill & ~keep_mask);
136                               len -= fill_count;
137                               if (__predict_false(len == 0))
138                                         return addr;
139                               /*
140                                * Since we were able to fill the rest of this word,
141                                * we will advance to the next word and thus have no
142                                * bytes to preserve.
143                                *
144                                * If we don't have enough to fill the rest of this
145                                * word, we will fall through the following loop
146                                * (since there are no full words to fill).  Then we
147                                * use the keep_mask above to preserve the leading
148                                * bytes of word.
149                                */
150                               dstp++;
151                               keep_mask = 0;
152                     } else {
153                               len += (uintptr_t)addr & (sizeof(memword_t) - 1);
154                     }
155 #else /* __OPTIMIZE_SIZE__ */
156                     uint8_t *dp, *ep;
157                     if (len < fill_count)
158                               fill_count = len;
159                     for (dp = (uint8_t *)dstp, ep = dp + fill_count;
160                          dp != ep; dp++)
161                               *dp = fill;
162                     if ((len -= fill_count) == 0)
163                               return addr;
164                     dstp = (memword_t *)ep;
165 #endif /* __OPTIMIZE_SIZE__ */
166           }
167 
168           /*
169            * Simply fill memory one word at time (for as many full words we have
170            * to write).
171            */
172           for (edstp = dstp + len / sizeof(memword_t); dstp != edstp; dstp++)
173                     *dstp = fill;
174 
175           /*
176            * We didn't subtract out the full words we just filled since we know
177            * by the time we get here we will have less than a words worth to
178            * write.  So we can concern ourselves with only the subword len bits.
179            */
180           len &= sizeof(memword_t)-1;
181           if (len > 0) {
182 #ifndef __OPTIMIZE_SIZE__
183                     /*
184                      * We want to clear <len> leading bytes in the word.
185                      * On big/little endian, these are the most/least significant
186                      * bits, respectively.  But as we want the mask of the bytes to
187                      * keep, we have to complement the mask.  So after we shift,
188                      * the keep_mask will only have bits set for the bytes we won't
189                      * be filling.
190                      *
191                      * But the keep_mask could already have bytes to preserve
192                      * if the amount to fill was less than the amount of trailing
193                      * space in the first word.
194                      */
195 #if BYTE_ORDER == BIG_ENDIAN
196                     keep_mask |= ~(memword_t)0U >> (len * 8);
197 #endif
198 #if BYTE_ORDER == LITTLE_ENDIAN
199                     keep_mask |= ~(memword_t)0U << (len * 8);
200 #endif
201                     /*
202                      * Now we mask off the bytes we are filling and then fill in
203                      * those bytes with the new fill value.
204                      */
205                     *dstp = (*dstp & keep_mask) | (fill & ~keep_mask);
206 #else /* __OPTIMIZE_SIZE__ */
207                     uint8_t *dp, *ep;
208                     for (dp = (uint8_t *)dstp, ep = dp + len;
209                          dp != ep; dp++)
210                               *dp = fill;
211 #endif /* __OPTIMIZE_SIZE__ */
212           }
213 
214           /*
215            * Return the initial addr
216            */
217           return addr;
218 }
219 
220 #ifdef BZERO
221 /*
222  * For bzero, simply inline memset and let the compiler optimize things away.
223  */
224 void
bzero(void * addr,size_t len)225 bzero(void *addr, size_t len)
226 {
227           memset(addr, 0, len);
228 }
229 #endif
230 
231 #ifdef TEST
232 #include <stdbool.h>
233 #include <stdio.h>
234 
235 #undef memset
236 
237 static union {
238           uint8_t bytes[sizeof(memword_t) * 4];
239           memword_t words[4];
240 } testmem;
241 
242 int
main(int argc,char ** argv)243 main(int argc, char **argv)
244 {
245           size_t start;
246           size_t len;
247           bool failed = false;
248 
249           for (start = 1; start < sizeof(testmem) - 1; start++) {
250                     for (len = 1; start + len < sizeof(testmem) - 1; len++) {
251                               bool ok = true;
252                               size_t i;
253                               uint8_t check_value;
254                               memset(testmem.bytes, 0xff, sizeof(testmem));
255                               test_memset(testmem.bytes + start, 0x00, len);
256                               for (i = 0; i < sizeof(testmem); i++) {
257                                         if (i == 0 || i == start + len)
258                                                   check_value = 0xff;
259                                         else if (i == start)
260                                                   check_value = 0x00;
261                                         if (testmem.bytes[i] != check_value) {
262                                                   if (ok)
263                                                             printf("pass @ %zu .. %zu failed",
264                                                                 start, start + len - 1);
265                                                   ok = false;
266                                                   printf(" [%zu]=0x%02x(!0x%02x)",
267                                                       i, testmem.bytes[i], check_value);
268                                         }
269                               }
270                               if (!ok) {
271                                         printf("\n");
272                                         failed = 1;
273                               }
274                     }
275           }
276 
277           return failed ? 1 : 0;
278 }
279 #endif /* TEST */
280