dataset.cpp 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. /*
  2. Copyright (c) 2018 tevador
  3. This file is part of RandomX.
  4. RandomX is free software: you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation, either version 3 of the License, or
  7. (at your option) any later version.
  8. RandomX is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with RandomX. If not, see<http://www.gnu.org/licenses/>.
  14. */
  15. #include <new>
  16. #include <algorithm>
  17. #include <stdexcept>
  18. #include <cstring>
  19. #include "common.hpp"
  20. #include "dataset.hpp"
  21. #include "Pcg32.hpp"
  22. #include "Cache.hpp"
  23. #include "virtualMemory.hpp"
  24. #if defined(__SSE2__)
  25. #include <wmmintrin.h>
  26. #define PREFETCH(memory) _mm_prefetch((const char *)((memory).ds.dataset + (memory).ma), _MM_HINT_T0)
  27. #else
  28. #define PREFETCH(memory)
  29. #endif
  30. namespace RandomX {
  31. template<typename T>
  32. static inline void shuffle(T* buffer, size_t bytes, Pcg32& gen) {
  33. auto count = bytes / sizeof(T);
  34. for (auto i = count - 1; i >= 1; --i) {
  35. int j = gen.getUniform(0, i);
  36. std::swap(buffer[j], buffer[i]);
  37. }
  38. }
  39. template<bool soft>
  40. static inline __m128i aesenc(__m128i in, __m128i key) {
  41. return soft ? soft_aesenc(in, key) : _mm_aesenc_si128(in, key);
  42. }
  43. template<bool soft>
  44. static inline __m128i aesdec(__m128i in, __m128i key) {
  45. return soft ? soft_aesdec(in, key) : _mm_aesdec_si128(in, key);
  46. }
  47. template<bool soft, bool enc>
  48. void initBlock(const uint8_t* in, uint8_t* out, uint32_t blockNumber, const KeysContainer& keys) {
  49. __m128i xin, xout;
  50. //Initialization vector = block number extended to 128 bits
  51. xout = _mm_cvtsi32_si128(blockNumber);
  52. //Expand + AES
  53. for (uint32_t i = 0; i < DatasetBlockSize / sizeof(__m128i); ++i) {
  54. if ((i % 32) == 0) {
  55. xin = _mm_set_epi64x(*(uint64_t*)(in + i / 4), 0);
  56. xout = _mm_xor_si128(xin, xout);
  57. }
  58. if (enc) {
  59. xout = aesenc<soft>(xout, keys[0]);
  60. xout = aesenc<soft>(xout, keys[1]);
  61. xout = aesenc<soft>(xout, keys[2]);
  62. xout = aesenc<soft>(xout, keys[3]);
  63. xout = aesenc<soft>(xout, keys[4]);
  64. xout = aesenc<soft>(xout, keys[5]);
  65. xout = aesenc<soft>(xout, keys[6]);
  66. xout = aesenc<soft>(xout, keys[7]);
  67. xout = aesenc<soft>(xout, keys[8]);
  68. xout = aesenc<soft>(xout, keys[9]);
  69. }
  70. else {
  71. xout = aesdec<soft>(xout, keys[0]);
  72. xout = aesdec<soft>(xout, keys[1]);
  73. xout = aesdec<soft>(xout, keys[2]);
  74. xout = aesdec<soft>(xout, keys[3]);
  75. xout = aesdec<soft>(xout, keys[4]);
  76. xout = aesdec<soft>(xout, keys[5]);
  77. xout = aesdec<soft>(xout, keys[6]);
  78. xout = aesdec<soft>(xout, keys[7]);
  79. xout = aesdec<soft>(xout, keys[8]);
  80. xout = aesdec<soft>(xout, keys[9]);
  81. }
  82. _mm_store_si128((__m128i*)(out + i * sizeof(__m128i)), xout);
  83. }
  84. //Shuffle
  85. Pcg32 gen(&xout);
  86. shuffle<uint32_t>((uint32_t*)out, DatasetBlockSize, gen);
  87. }
  88. template
  89. void initBlock<true, true>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
  90. template
  91. void initBlock<true, false>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
  92. template
  93. void initBlock<false, true>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
  94. template
  95. void initBlock<false, false>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
  96. convertible_t datasetRead(addr_t addr, MemoryRegisters& memory) {
  97. convertible_t data;
  98. data.u64 = *(uint64_t*)(memory.ds.dataset + memory.ma);
  99. memory.ma += 8;
  100. memory.mx ^= addr;
  101. if ((memory.mx & 0xFFF8) == 0) {
  102. memory.ma = memory.mx & ~7;
  103. PREFETCH(memory);
  104. }
  105. return data;
  106. }
  107. template<bool softAes>
  108. void initBlock(const uint8_t* cache, uint8_t* block, uint32_t blockNumber, const KeysContainer& keys) {
  109. if (blockNumber % 2 == 1) {
  110. initBlock<softAes, true>(cache + blockNumber * CacheBlockSize, block, blockNumber, keys);
  111. }
  112. else {
  113. initBlock<softAes, false>(cache + blockNumber * CacheBlockSize, block, blockNumber, keys);
  114. }
  115. }
  116. template
  117. void initBlock<true>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
  118. template
  119. void initBlock<false>(const uint8_t*, uint8_t*, uint32_t, const KeysContainer&);
  120. template<bool softAes>
  121. convertible_t datasetReadLight(addr_t addr, MemoryRegisters& memory) {
  122. convertible_t data;
  123. LightClientDataset* lds = memory.ds.lightDataset;
  124. auto blockNumber = memory.ma / DatasetBlockSize;
  125. if (lds->blockNumber != blockNumber) {
  126. initBlock<softAes>(lds->cache->getCache(), (uint8_t*)lds->block, blockNumber, lds->cache->getKeys());
  127. lds->blockNumber = blockNumber;
  128. }
  129. data.u64 = *(uint64_t*)(lds->block + (memory.ma % DatasetBlockSize));
  130. memory.ma += 8;
  131. memory.mx ^= addr;
  132. if ((memory.mx & 0xFFF8) == 0) {
  133. memory.ma = memory.mx & ~7;
  134. }
  135. return data;
  136. }
  137. template
  138. convertible_t datasetReadLight<false>(addr_t addr, MemoryRegisters& memory);
  139. template
  140. convertible_t datasetReadLight<true>(addr_t addr, MemoryRegisters& memory);
  141. void datasetAlloc(dataset_t& ds, bool largePages) {
  142. if (sizeof(size_t) <= 4)
  143. throw std::runtime_error("Platform doesn't support enough memory for the dataset");
  144. if (largePages) {
  145. ds.dataset = (uint8_t*)allocLargePagesMemory(DatasetSize);
  146. }
  147. else {
  148. ds.dataset = (uint8_t*)_mm_malloc(DatasetSize, 64);
  149. if (ds.dataset == nullptr) {
  150. throw std::runtime_error("Dataset memory allocation failed. >4 GiB of free virtual memory is needed.");
  151. }
  152. }
  153. }
  154. template<bool softAes>
  155. void datasetInit(Cache* cache, dataset_t ds, uint32_t startBlock, uint32_t blockCount) {
  156. for (uint32_t i = startBlock; i < startBlock + blockCount; ++i) {
  157. initBlock<softAes>(cache->getCache(), ds.dataset + i * DatasetBlockSize, i, cache->getKeys());
  158. }
  159. }
  160. template
  161. void datasetInit<false>(Cache*, dataset_t, uint32_t, uint32_t);
  162. template
  163. void datasetInit<true>(Cache*, dataset_t, uint32_t, uint32_t);
  164. template<bool softAes>
  165. void datasetInitCache(const void* seed, dataset_t& ds) {
  166. ds.cache = new Cache();
  167. ds.cache->initialize<softAes>(seed, SeedSize);
  168. }
  169. template
  170. void datasetInitCache<false>(const void*, dataset_t&);
  171. template
  172. void datasetInitCache<true>(const void*, dataset_t&);
  173. }