Software APIs
memory.c
1 // Copyright lowRISC contributors (OpenTitan project).
2 // Licensed under the Apache License, Version 2.0, see LICENSE for details.
3 // SPDX-License-Identifier: Apache-2.0
4 
6 
7 #include <assert.h>
8 #include <stdint.h>
9 
11 
12 #ifdef OT_PLATFORM_RV32
13 #define OT_PREFIX_IF_NOT_RV32(name) name
14 #else
15 #define OT_PREFIX_IF_NOT_RV32(name) ot_##name
16 #endif
17 
18 static size_t compute_num_leading_bytes(const void *left, const void *right,
19  size_t len) {
20  if (len < alignof(uint32_t)) {
21  return len;
22  }
23  const size_t left_ahead = OT_UNSIGNED(misalignment32_of((uintptr_t)left));
24  const size_t right_ahead = OT_UNSIGNED(misalignment32_of((uintptr_t)right));
25  if (right == NULL || left_ahead == right_ahead) {
26  return (4 - left_ahead) & 0x3;
27  }
28  return len;
29 }
30 
31 /**
32  * Compute the bounds of the word-aligned region for the given buffers.
33  *
34  * It's more efficient for our memory functions to operate on `uint32_t` values
35  * than individual bytes, but we can only read `uint32_t` values from aligned
36  * addresses. This function effectively breaks the given buffers into three
37  * consecutive chunks: the unaligned "head", the aligned "body", and the
38  * unaligned "tail".
39  *
40  * @param[in] left The memory function's first buffer argument. Cannot be NULL.
41  * @param[in] right The memory function's second buffer argument. May be NULL.
42  * @param[in] len The length in bytes of both `left` and `right.`
43  * @param[out] out_body_offset The start of the body region.
44  * @param[out] out_tail_offset The start of the tail region.
45  */
46 static void compute_alignment(const void *left, const void *right, size_t len,
47  size_t *out_body_offset,
48  size_t *out_tail_offset) {
49  const size_t num_leading_bytes = compute_num_leading_bytes(left, right, len);
50  *out_body_offset = num_leading_bytes;
51 
52  const size_t num_words = (len - num_leading_bytes) / sizeof(uint32_t);
53  *out_tail_offset = num_leading_bytes + num_words * sizeof(uint32_t);
54 }
55 
56 static uint32_t repeat_byte_to_u32(uint8_t byte) {
57  const uint32_t word = byte;
58  return word << 24 | word << 16 | word << 8 | word;
59 }
60 
61 void *OT_PREFIX_IF_NOT_RV32(memcpy)(void *restrict dest,
62  const void *restrict src, size_t len) {
63  if (dest == NULL || src == NULL) {
64  return dest;
65  }
66  unsigned char *dest8 = (unsigned char *)dest;
67  const unsigned char *src8 = (const unsigned char *)src;
68  size_t body_offset, tail_offset;
69  compute_alignment(dest, src, len, &body_offset, &tail_offset);
70  size_t i = 0;
71  for (; i < body_offset; ++i) {
72  dest8[i] = src8[i];
73  }
74  for (; i < tail_offset; i += sizeof(uint32_t)) {
75  uint32_t word = read_32(&src8[i]);
76  write_32(word, &dest8[i]);
77  }
78  for (; i < len; ++i) {
79  dest8[i] = src8[i];
80  }
81  return dest;
82 }
83 
84 void *OT_PREFIX_IF_NOT_RV32(memset)(void *dest, int value, size_t len) {
85  unsigned char *dest8 = (unsigned char *)dest;
86  const uint8_t value8 = (uint8_t)value;
87 
88  size_t body_offset, tail_offset;
89  compute_alignment(dest, NULL, len, &body_offset, &tail_offset);
90  size_t i = 0;
91  for (; i < body_offset; ++i) {
92  dest8[i] = value8;
93  }
94  const uint32_t value32 = repeat_byte_to_u32(value8);
95  for (; i < tail_offset; i += sizeof(uint32_t)) {
96  write_32(value32, &dest8[i]);
97  }
98  for (; i < len; ++i) {
99  dest8[i] = value8;
100  }
101  return dest;
102 }
103 
104 enum {
105  kMemCmpEq = 0,
106  kMemCmpLt = -42,
107  kMemCmpGt = 42,
108 };
109 
110 int OT_PREFIX_IF_NOT_RV32(memcmp)(const void *lhs, const void *rhs,
111  size_t len) {
112  const unsigned char *lhs8 = (const unsigned char *)lhs;
113  const unsigned char *rhs8 = (const unsigned char *)rhs;
114  size_t body_offset, tail_offset;
115  compute_alignment(lhs, rhs, len, &body_offset, &tail_offset);
116  size_t i = 0;
117  for (; i < body_offset; ++i) {
118  if (lhs8[i] < rhs8[i]) {
119  return kMemCmpLt;
120  } else if (lhs8[i] > rhs8[i]) {
121  return kMemCmpGt;
122  }
123  }
124  for (; i < tail_offset; i += sizeof(uint32_t)) {
125 #if OT_BUILD_FOR_STATIC_ANALYZER
126  assert(&lhs8[i] != NULL);
127  assert(&rhs8[i] != NULL);
128 #endif
129  uint32_t word_left = __builtin_bswap32(read_32(&lhs8[i]));
130  uint32_t word_right = __builtin_bswap32(read_32(&rhs8[i]));
131  static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__,
132  "memcmp assumes that the system is little endian.");
133  if (word_left < word_right) {
134  return kMemCmpLt;
135  } else if (word_left > word_right) {
136  return kMemCmpGt;
137  }
138  }
139  for (; i < len; ++i) {
140  if (lhs8[i] < rhs8[i]) {
141  return kMemCmpLt;
142  } else if (lhs8[i] > rhs8[i]) {
143  return kMemCmpGt;
144  }
145  }
146  return kMemCmpEq;
147 }
148 
149 int memrcmp(const void *lhs, const void *rhs, size_t len) {
150  const unsigned char *lhs8 = (const unsigned char *)lhs;
151  const unsigned char *rhs8 = (const unsigned char *)rhs;
152  size_t body_offset, tail_offset;
153  compute_alignment(lhs, rhs, len, &body_offset, &tail_offset);
154  size_t end = len;
155  for (; end > tail_offset; --end) {
156  const size_t i = end - 1;
157  if (lhs8[i] < rhs8[i]) {
158  return kMemCmpLt;
159  } else if (lhs8[i] > rhs8[i]) {
160  return kMemCmpGt;
161  }
162  }
163  for (; end > body_offset; end -= sizeof(uint32_t)) {
164  const size_t i = end - sizeof(uint32_t);
165 #if OT_BUILD_FOR_STATIC_ANALYZER
166  assert(&lhs8[i] != NULL);
167  assert(&rhs8[i] != NULL);
168 #endif
169  uint32_t word_left = read_32(&lhs8[i]);
170  uint32_t word_right = read_32(&rhs8[i]);
171  static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__,
172  "memrcmp assumes that the system is little endian.");
173  if (word_left < word_right) {
174  return kMemCmpLt;
175  } else if (word_left > word_right) {
176  return kMemCmpGt;
177  }
178  }
179  for (; end > 0; --end) {
180  const size_t i = end - 1;
181  if (lhs8[i] < rhs8[i]) {
182  return kMemCmpLt;
183  } else if (lhs8[i] > rhs8[i]) {
184  return kMemCmpGt;
185  }
186  }
187  return kMemCmpEq;
188 }
189 
190 void *OT_PREFIX_IF_NOT_RV32(memchr)(const void *ptr, int value, size_t len) {
191  const unsigned char *ptr8 = (const unsigned char *)ptr;
192  const uint8_t value8 = (uint8_t)value;
193 
194  size_t body_offset, tail_offset;
195  compute_alignment(ptr, NULL, len, &body_offset, &tail_offset);
196  size_t i = 0;
197  for (; i < body_offset; ++i) {
198  if (ptr8[i] == value8) {
199  return (void *)&ptr8[i];
200  }
201  }
202  const uint32_t value32 = repeat_byte_to_u32(value8);
203  for (; i < tail_offset; i += sizeof(uint32_t)) {
204  uint32_t word = read_32(&ptr8[i]);
205  uint32_t bits_eq = ~(word ^ value32);
206  static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__,
207  "memchr assumes that the system is little endian.");
208  if ((bits_eq & UINT8_MAX) == UINT8_MAX) {
209  return (void *)&ptr8[i];
210  }
211  if (((bits_eq >> 8) & UINT8_MAX) == UINT8_MAX) {
212  return (void *)&ptr8[i + 1];
213  }
214  if (((bits_eq >> 16) & UINT8_MAX) == UINT8_MAX) {
215  return (void *)&ptr8[i + 2];
216  }
217  if (((bits_eq >> 24) & UINT8_MAX) == UINT8_MAX) {
218  return (void *)&ptr8[i + 3];
219  }
220  }
221  for (; i < len; ++i) {
222  if (ptr8[i] == value8) {
223  return (void *)&ptr8[i];
224  }
225  }
226  return NULL;
227 }
228 
229 void *OT_PREFIX_IF_NOT_RV32(memrchr)(const void *ptr, int value, size_t len) {
230  const unsigned char *ptr8 = (const unsigned char *)ptr;
231  const uint8_t value8 = (uint8_t)value;
232 
233  size_t body_offset, tail_offset;
234  compute_alignment(ptr, NULL, len, &body_offset, &tail_offset);
235 
236  size_t end = len;
237  for (; end > tail_offset; --end) {
238  const size_t i = end - 1;
239  if (ptr8[i] == value8) {
240  return (void *)&ptr8[i];
241  }
242  }
243  const uint32_t value32 = repeat_byte_to_u32(value8);
244  for (; end > body_offset; end -= sizeof(uint32_t)) {
245  const size_t i = end - sizeof(uint32_t);
246  uint32_t word = read_32(&ptr8[i]);
247  uint32_t bits_eq = ~(word ^ value32);
248  static_assert(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__,
249  "memrchr assumes that the system is little endian.");
250  if (((bits_eq >> 24) & UINT8_MAX) == UINT8_MAX) {
251  return (void *)&ptr8[i + 3];
252  }
253  if (((bits_eq >> 16) & UINT8_MAX) == UINT8_MAX) {
254  return (void *)&ptr8[i + 2];
255  }
256  if (((bits_eq >> 8) & UINT8_MAX) == UINT8_MAX) {
257  return (void *)&ptr8[i + 1];
258  }
259  if ((bits_eq & UINT8_MAX) == UINT8_MAX) {
260  return (void *)&ptr8[i];
261  }
262  }
263  for (; end > 0; --end) {
264  const size_t i = end - 1;
265  if (ptr8[i] == value8) {
266  return (void *)&ptr8[i];
267  }
268  }
269  return NULL;
270 }
271 
272 // `extern` declarations to give the inline functions in the corresponding
273 // header a link location.
274 
275 extern ptrdiff_t misalignment32_of(uintptr_t);
276 extern uint32_t read_32(const void *);
277 extern void write_32(uint32_t, void *);
278 extern uint64_t read_64(const void *);
279 extern void write_64(uint64_t, void *);
280 
281 #undef OT_PREFIX_IF_NOT_RV32