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 */ 68typedef __register_t memword_t; 69 70#ifdef BZERO 71static inline 72#define memset memset0 73#endif 74 75#ifdef TEST 76static 77#define memset test_memset 78#endif 79 80void * 81memset(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 */ 224void 225bzero(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 237static union { 238 uint8_t bytes[sizeof(memword_t) * 4]; 239 memword_t words[4]; 240} testmem; 241 242int 243main(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