| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| #ifndef OPENTITAN_SW_DEVICE_LIB_BASE_MEMORY_H_ |
| #define OPENTITAN_SW_DEVICE_LIB_BASE_MEMORY_H_ |
| |
| /** |
| * @file |
| * @brief OpenTitan Device Memory Library |
| * |
| * This library provides memory functions for aligned word accesses, and some |
| * useful functions from the C library's <string.h>. |
| */ |
| |
| #include <stdalign.h> |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #ifndef OT_PLATFORM_RV32 |
| #include <string.h> |
| #endif |
| |
| #include "sw/device/lib/base/macros.h" |
| |
| // When compiling unit tests on the host machine, we must mangle the names of |
| // OpenTitan's memory functions to disambiguate them from libc's variants. |
| // Otherwise, the compiler could error when it encounters conflicting |
| // declarations. For instance, OpenTitan's `memcpy()` could have a different |
| // exception specifier than the one declared by the system header <string.h>. |
| // (The <string.h> include is unavoidable because it's pulled in by GoogleTest.) |
| // |
| // Consumers of this header should almost always call the unmangled functions. |
| // Device builds will naturally use OpenTitan's definitions and host builds will |
| // use the system's definitions. However, nothing is stopping host builds from |
| // calling the mangled function names; see :memory_unittest for an example. |
| // |
| // Separately, ASan segfaults during initialization when it calls our |
| // user-defined memory functions. Prefixing the troublesome functions works |
| // around this ASan bug, enabling us to run the unit tests with ASan enabled. |
| // See https://github.com/lowRISC/opentitan/issues/13826. |
| #ifdef OT_PLATFORM_RV32 |
| #define OT_PREFIX_IF_NOT_RV32(name) name |
| #else |
| #define OT_PREFIX_IF_NOT_RV32(name) ot_##name |
| #endif |
| |
| #ifdef __cplusplus |
| extern "C" { |
| #endif // __cplusplus |
| |
| /** |
| * Computes how many bytes `addr` is ahead of the previous 32-bit word alignment |
| * boundary. |
| */ |
| inline ptrdiff_t misalignment32_of(uintptr_t addr) { |
| return addr % alignof(uint32_t); |
| } |
| |
| /** |
| * Load a word from memory directly, bypassing aliasing rules. |
| * |
| * ISO C forbids, in general, casting a pointer to non-character types and |
| * reading them, though it is frequently necessary to read exactly one word out |
| * of a `void *`. This function performs that action in a manner which is |
| * well-defined. |
| * |
| * Of course, `ptr` must point to word-aligned memory that is at least one word |
| * wide. To do otherwise is Undefined Behavior. It goes without saying that the |
| * memory this function intents to read must be initialized. |
| * |
| * This function has reordering properties as weak as a normal, non-atomic, |
| * non-volatile load. |
| * |
| * @param ptr a word-aligned pointer pointed to at least four bytes of memory. |
| * @return the word `ptr` points to. |
| */ |
| inline uint32_t read_32(const void *ptr) { |
| // Both GCC and Clang optimize the code below into a single word-load on most |
| // platforms. It is necessary and sufficient to indicate to the compiler that |
| // the pointer points to four bytes of four-byte-aligned memory. |
| // |
| // Failing to get that particular codegen in either GCC or Clang with -O2 or |
| // -Os set shall be considered a bug in this function. The same applies to |
| // `write32()`. |
| ptr = __builtin_assume_aligned(ptr, alignof(uint32_t)); |
| uint32_t val; |
| __builtin_memcpy(&val, ptr, sizeof(uint32_t)); |
| return val; |
| } |
| |
| /** |
| * Load a 64-bit word from memory directly, bypassing aliasing rules. |
| * |
| * ISO C forbids, in general, casting a pointer to non-character types and |
| * reading them, though it is frequently necessary to read exactly one 64-bit |
| * word out of a `void *`. This function performs that action in a manner which |
| * is well-defined. |
| * |
| * Of course, `ptr` must point to word-aligned memory that is at least one |
| * 64-bit word wide. To do otherwise is Undefined Behavior. It goes without |
| * saying that the memory this function intents to read must be initialized. |
| * |
| * This function has reordering properties as weak as a normal, non-atomic, |
| * non-volatile load. |
| * |
| * @param ptr a word-aligned pointer pointed to at least four bytes of memory. |
| * @return the word `ptr` points to. |
| */ |
| inline uint64_t read_64(const void *ptr) { |
| // Both GCC and Clang optimize the code below into a single word-load on most |
| // platforms. It is necessary and sufficient to indicate to the compiler that |
| // the pointer points to four bytes of four-byte-aligned memory. |
| // |
| // Failing to get that particular codegen in either GCC or Clang with -O2 or |
| // -Os set shall be considred a bug in this function. The same applies to |
| // `write32()`. |
| ptr = __builtin_assume_aligned(ptr, alignof(uint64_t)); |
| uint64_t val; |
| __builtin_memcpy(&val, ptr, sizeof(uint64_t)); |
| return val; |
| } |
| |
| /** |
| * Store a word to memory directly, bypassing aliasing rules. |
| * |
| * ISO C forbids, in general, casting a pointer to non-character types and |
| * reading them, though it is frequently necessary to write exactly one word to |
| * a `void *`. This function performs that action in a manner which is |
| * well-defined. |
| * |
| * Of course, `ptr` must point to word-aligned memory that is at least one word |
| * wide. To do otherwise is Undefined Behavior. |
| * |
| * This function has reordering properties as weak as a normal, non-atomic, |
| * non-volatile load. |
| * |
| * @param value the value to store. |
| * @param ptr a word-aligned pointer pointed to at least four bytes of memory. |
| */ |
| inline void write_32(uint32_t value, void *ptr) { |
| // Both GCC and Clang optimize the code below into a single word-store on most |
| // platforms. See the comment in `read_32()` for more implementation-private |
| // information. |
| ptr = __builtin_assume_aligned(ptr, alignof(uint32_t)); |
| __builtin_memcpy(ptr, &value, sizeof(uint32_t)); |
| } |
| |
| /** |
| * Store a 64-bit word to memory directly, bypassing aliasing rules. |
| * |
| * ISO C forbids, in general, casting a pointer to non-character types and |
| * reading them, though it is frequently necessary to write exactly one 64-bit |
| * word to a `void *`. This function performs that action in a manner which is |
| * well-defined. |
| * |
| * Of course, `ptr` must point to word-aligned memory that is at least one |
| * 64-bit word wide. To do otherwise is Undefined Behavior. |
| * |
| * This function has reordering properties as weak as a normal, non-atomic, |
| * non-volatile load. |
| * |
| * @param value the value to store. |
| * @param ptr a word-aligned pointer pointed to at least four bytes of memory. |
| */ |
| |
| inline void write_64(uint64_t value, void *ptr) { |
| // Both GCC and Clang optimize the code below into a single word-store on most |
| // platforms. See the comment in `read_64()` for more implementation-private |
| // information. |
| ptr = __builtin_assume_aligned(ptr, alignof(uint64_t)); |
| __builtin_memcpy(ptr, &value, sizeof(uint64_t)); |
| } |
| |
| /** |
| * Copy memory between non-overlapping regions. |
| * |
| * This function conforms to the semantics defined in ISO C11 S7.23.2.1. |
| * |
| * This function will be provided by the platform's libc implementation for host |
| * builds. |
| * |
| * @param dest the region to copy to. |
| * @param src the region to copy from. |
| * @param len the number of bytes to copy. |
| * @return the value of `dest`. |
| */ |
| void *OT_PREFIX_IF_NOT_RV32(memcpy)(void *OT_RESTRICT dest, |
| const void *OT_RESTRICT src, size_t len); |
| |
| /** |
| * Set a region of memory to a particular byte value. |
| * |
| * This function conforms to the semantics defined in ISO C11 S7.23.6.1. |
| * |
| * This function will be provided by the platform's libc implementation for host |
| * builds. |
| * |
| * @param dest the region to write to. |
| * @param value the value, converted to a byte, to write to each byte cell. |
| * @param len the number of bytes to write. |
| * @return the value of `dest`. |
| */ |
| void *OT_PREFIX_IF_NOT_RV32(memset)(void *dest, int value, size_t len); |
| |
| /** |
| * Compare two (potentially overlapping) regions of memory for byte-wise |
| * lexicographic order. |
| * |
| * This function conforms to the semantics defined in ISO C11 S7.24.4.1. |
| * |
| * This function will be provided by the platform's libc implementation for host |
| * builds. |
| * |
| * @param lhs the left-hand-side of the comparison. |
| * @param rhs the right-hand-side of the comparison. |
| * @param len the length of both regions, in bytes. |
| * @return a zero, positive, or negative integer, corresponding to the |
| * contingencies of `lhs == rhs`, `lhs > rhs`, and `lhs < rhs` (as buffers, not |
| * pointers), respectively. |
| */ |
| int OT_PREFIX_IF_NOT_RV32(memcmp)(const void *lhs, const void *rhs, size_t len); |
| |
| /** |
| * Compare two (potentially overlapping) regions of memory for reverse |
| * byte-wise lexicographic order (i.e. the same as memcmp, but starting from |
| * the end of the memory regions). |
| * |
| * Can be used for arithmetic comparison of little-endian buffers. |
| * |
| * This function is provided by OpenTitan in host builds, not by the platform's |
| * libc implementation. |
| * |
| * @param lhs the left-hand-side of the comparison. |
| * @param rhs the right-hand-side of the comparison. |
| * @param len the length of both regions, in bytes. |
| * @return a zero, positive, or negative integer, corresponding to the |
| * contingencies of `lhs == rhs`, `lhs > rhs`, and `lhs < rhs` (as buffers, not |
| * pointers), respectively. |
| */ |
| int memrcmp(const void *lhs, const void *rhs, size_t len); |
| |
| /** |
| * Search a region of memory for the first occurrence of a particular byte |
| * value. |
| * |
| * This function conforms to the semantics defined in ISO C11 S7.24.5.1. |
| * |
| * Since libbase does not provide a `strlen()` function, this function can be |
| * used as an approximation: `memchr(my_str, 0, SIZE_MAX) - my_str`. |
| * |
| * This function will be provided by the platform's libc implementation for host |
| * builds. |
| * |
| * @param ptr the region to search. |
| * @param value the value, converted to a byte, to search for. |
| * @param len the length of the region, in bytes. |
| * @return a pointer to the found value, or NULL. |
| */ |
| void *OT_PREFIX_IF_NOT_RV32(memchr)(const void *ptr, int value, size_t len); |
| |
| /** |
| * Search a region of memory for the last occurrence of a particular byte value. |
| * |
| * This function is not specified by C11, but is named for a well-known glibc |
| * extension. |
| * |
| * @param ptr the region to search. |
| * @param value the value, converted to a byte, to search for. |
| * @param len the length of the region, in bytes. |
| * @return a pointer to the found value, or NULL. |
| */ |
| void *OT_PREFIX_IF_NOT_RV32(memrchr)(const void *ptr, int value, size_t len); |
| |
| #ifdef __cplusplus |
| } // extern "C" |
| #endif // __cplusplus |
| |
| #undef OT_PREFIX_IF_NOT_RV32 |
| |
| #endif // OPENTITAN_SW_DEVICE_LIB_BASE_MEMORY_H_ |