kmem.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <strings.h>
  4. #include <stdlib.h>
  5. #include <stdint.h>
  6. #include <stdarg.h>
  7. #include <fcntl.h>
  8. #include <unistd.h>
  9. #include <sys/ioctl.h>
  10. #include <sys/mman.h>
  11. #include <arpa/inet.h>
  12. #include <errno.h>
  13. #include <assert.h>
  14. #include "pcilib.h"
  15. #include "pci.h"
  16. #include "kmem.h"
  17. #include "error.h"
  18. int pcilib_clean_kernel_memory(pcilib_t *ctx, pcilib_kmem_use_t use, pcilib_kmem_flags_t flags) {
  19. kmem_handle_t kh = {0};
  20. kh.use = use;
  21. kh.flags = flags|PCILIB_KMEM_FLAG_MASS;
  22. return ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_FREE, &kh);
  23. }
  24. static int pcilib_free_kernel_buffer(pcilib_t *ctx, pcilib_kmem_list_t *kbuf, size_t i, pcilib_kmem_flags_t flags) {
  25. kmem_handle_t kh = {0};
  26. if (kbuf->buf.blocks[i].ua) munmap((void*)kbuf->buf.blocks[i].ua, kbuf->buf.blocks[i].size + kbuf->buf.blocks[i].alignment_offset);
  27. kh.handle_id = kbuf->buf.blocks[i].handle_id;
  28. kh.pa = kbuf->buf.blocks[i].pa;
  29. kh.flags = flags;
  30. return ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_FREE, &kh);
  31. }
  32. static void pcilib_cancel_kernel_memory(pcilib_t *ctx, pcilib_kmem_list_t *kbuf, pcilib_kmem_flags_t flags, int last_flags) {
  33. int ret;
  34. if (!kbuf->buf.n_blocks) return;
  35. // consistency error during processing of last block, special treatment could be needed
  36. if (last_flags) {
  37. pcilib_kmem_flags_t failed_flags = flags;
  38. if (last_flags&KMEM_FLAG_REUSED_PERSISTENT) failed_flags&=~PCILIB_KMEM_FLAG_PERSISTENT;
  39. if (last_flags&KMEM_FLAG_REUSED_HW) failed_flags&=~PCILIB_KMEM_FLAG_HARDWARE;
  40. if (failed_flags != flags) {
  41. ret = pcilib_free_kernel_buffer(ctx, kbuf, --kbuf->buf.n_blocks, failed_flags);
  42. if (ret) pcilib_error("PCIDRIVER_IOC_KMEM_FREE ioctl have failed");
  43. }
  44. }
  45. pcilib_free_kernel_memory(ctx, kbuf, flags);
  46. }
  47. pcilib_kmem_handle_t *pcilib_alloc_kernel_memory(pcilib_t *ctx, pcilib_kmem_type_t type, size_t nmemb, size_t size, size_t alignment, pcilib_kmem_use_t use, pcilib_kmem_flags_t flags) {
  48. int err = 0;
  49. char error[256];
  50. int ret;
  51. size_t i, allocated = nmemb;
  52. void *addr;
  53. pcilib_tristate_t reused = PCILIB_TRISTATE_NO;
  54. int persistent = -1;
  55. int hardware = -1;
  56. kmem_handle_t kh = {0};
  57. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)malloc(sizeof(pcilib_kmem_list_t) + nmemb * sizeof(pcilib_kmem_addr_t));
  58. if (!kbuf) {
  59. pcilib_error("Memory allocation has failed");
  60. return NULL;
  61. }
  62. memset(kbuf, 0, sizeof(pcilib_kmem_list_t) + nmemb * sizeof(pcilib_kmem_addr_t));
  63. err = pcilib_lock_global(ctx);
  64. if (err) {
  65. pcilib_error("Error (%i) acquiring mmap lock", err);
  66. return NULL;
  67. }
  68. ret = ioctl( ctx->handle, PCIDRIVER_IOC_MMAP_MODE, PCIDRIVER_MMAP_KMEM );
  69. if (ret) {
  70. pcilib_unlock_global(ctx);
  71. pcilib_error("PCIDRIVER_IOC_MMAP_MODE ioctl have failed");
  72. return NULL;
  73. }
  74. kh.type = type;
  75. kh.size = size;
  76. kh.align = alignment;
  77. kh.use = use;
  78. if ((type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_REGION) {
  79. kh.align = 0;
  80. } else if ((type&PCILIB_KMEM_TYPE_MASK) != PCILIB_KMEM_TYPE_PAGE) {
  81. kh.size += alignment;
  82. }
  83. for ( i = 0; (i < nmemb)||(flags&PCILIB_KMEM_FLAG_MASS); i++) {
  84. kh.item = i;
  85. kh.flags = flags;
  86. if (i >= nmemb)
  87. kh.flags |= KMEM_FLAG_TRY;
  88. if ((type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_REGION) {
  89. kh.pa = alignment + i * size;
  90. }
  91. ret = ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_ALLOC, &kh);
  92. if (ret) {
  93. kbuf->buf.n_blocks = i;
  94. if ((i < nmemb)||(errno != ENOENT)) {
  95. err = PCILIB_ERROR_FAILED;
  96. if (errno == EINVAL) {
  97. if (kh.type != type)
  98. sprintf(error, "Driver prevents us from re-using buffer (use 0x%x, block: %zu), we have requested type %u but buffer is of type %lu", use, i, type, kh.type);
  99. else if (kh.size != size)
  100. sprintf(error, "Driver prevents us from re-using buffer (use 0x%x, block: %zu), we have requested size %lu but buffer is of size %lu", use, i, size, kh.size);
  101. else if (kh.align != alignment)
  102. sprintf(error, "Driver prevents us from re-using buffer (use 0x%x, block: %zu), we have requested alignment %lu but buffer is of alignment %lu", use, i, size, kh.size);
  103. else if ((kh.flags&KMEM_FLAG_EXCLUSIVE) != (flags&KMEM_FLAG_EXCLUSIVE))
  104. sprintf(error, "Driver prevents us from re-using buffer (use 0x%x, block: %zu), we have requested size %s but buffer is of size %s", use, i, ((flags&KMEM_FLAG_EXCLUSIVE)?"exclusive":"non-exclusive"), ((kh.flags&KMEM_FLAG_EXCLUSIVE)?"exclusive":"non exclusive"));
  105. else
  106. sprintf(error, "Driver prevents us from re-using buffer (use 0x%x, block: %zu), unknown consistency error", use, i);
  107. } else if (errno == EBUSY) {
  108. sprintf(error, "Driver prevents us from re-using buffer (use 0x%x, block: %zu), reuse counter of kmem_entry is overflown", use, i);
  109. } else if (errno == ENOMEM) {
  110. sprintf(error, "Driver prevents us from re-using buffer (use 0x%x, block: %zu), memory allocation (%zu bytes) failed", use, i, size);
  111. } else {
  112. sprintf(error, "Driver prevents us from re-using buffer (use 0x%x, block: %zu), PCIDRIVER_IOC_KMEM_ALLOC ioctl have failed with errno %i", use, i, errno);
  113. }
  114. }
  115. break;
  116. }
  117. if (i >= allocated) {
  118. void *kbuf_new = realloc(kbuf, sizeof(pcilib_kmem_list_t) + 2 * allocated * sizeof(pcilib_kmem_addr_t));
  119. if (!kbuf_new) {
  120. kbuf->buf.n_blocks = i;
  121. err = PCILIB_ERROR_MEMORY;
  122. sprintf(error, "Failed to allocate extra %zu bytes of user-space memory for kmem structures", allocated * sizeof(pcilib_kmem_addr_t));
  123. break;
  124. }
  125. memset(kbuf_new + sizeof(pcilib_kmem_list_t) + allocated * sizeof(pcilib_kmem_addr_t) , 0, allocated * sizeof(pcilib_kmem_addr_t));
  126. kbuf = kbuf_new;
  127. allocated *= 2;
  128. }
  129. kbuf->buf.blocks[i].handle_id = kh.handle_id;
  130. kbuf->buf.blocks[i].pa = kh.pa;
  131. kbuf->buf.blocks[i].ba = kh.ba;
  132. kbuf->buf.blocks[i].size = kh.size;
  133. if (!i) reused = (kh.flags&KMEM_FLAG_REUSED)?PCILIB_TRISTATE_YES:PCILIB_TRISTATE_NO;
  134. if (kh.flags&KMEM_FLAG_REUSED) {
  135. if (!i) reused = PCILIB_TRISTATE_YES;
  136. else if (!reused) reused = PCILIB_TRISTATE_PARTIAL;
  137. if (persistent) {
  138. if (persistent < 0) {
  139. /*if (((flags&PCILIB_KMEM_FLAG_PERSISTENT) == 0)&&(kh.flags&KMEM_FLAG_REUSED_PERSISTENT)) err = PCILIB_ERROR_INVALID_STATE;
  140. else*/ persistent = (kh.flags&KMEM_FLAG_REUSED_PERSISTENT)?1:0;
  141. } else if ((kh.flags&KMEM_FLAG_REUSED_PERSISTENT) == 0) err = PCILIB_ERROR_INVALID_STATE;
  142. } else if (kh.flags&KMEM_FLAG_REUSED_PERSISTENT) err = PCILIB_ERROR_INVALID_STATE;
  143. if (err) {
  144. kbuf->buf.n_blocks = i + 1;
  145. sprintf(error, "Mistmatch in persistent modes of the re-used kmem blocks. Current buffer (use 0x%x, block: %zu) is %s, but prior ones %s",
  146. use, i, ((kh.flags&KMEM_FLAG_REUSED_PERSISTENT)?"persistent":"not persistent"), (persistent?"are":"are not"));
  147. break;
  148. }
  149. if (hardware) {
  150. if (hardware < 0) {
  151. /*if (((flags&PCILIB_KMEM_FLAG_HARDWARE) == 0)&&(kh.flags&KMEM_FLAG_REUSED_HW)) err = PCILIB_ERROR_INVALID_STATE;
  152. else*/ hardware = (kh.flags&KMEM_FLAG_REUSED_HW)?1:0;
  153. } else if ((kh.flags&KMEM_FLAG_REUSED_HW) == 0) err = PCILIB_ERROR_INVALID_STATE;
  154. } else if (kh.flags&KMEM_FLAG_REUSED_HW) err = PCILIB_ERROR_INVALID_STATE;
  155. if (err) {
  156. kbuf->buf.n_blocks = i + 1;
  157. sprintf(error, "Mistmatch in hardware modes of the re-used kmem blocks. Current buffer (use 0x%x, block: %zu) is %s, but prior ones %s",
  158. use, i, ((kh.flags&KMEM_FLAG_REUSED_HW)?"hardware-locked":"not hardware-locked"), (hardware?"are":"are not"));
  159. break;
  160. }
  161. } else {
  162. if (!i) reused = PCILIB_TRISTATE_NO;
  163. else if (reused) reused = PCILIB_TRISTATE_PARTIAL;
  164. if ((persistent > 0)&&((flags&PCILIB_KMEM_FLAG_PERSISTENT) == 0)) {
  165. err = PCILIB_ERROR_INVALID_STATE;
  166. sprintf(error, "Expecting to re-use persistent blocks, but buffer (use 0x%x, block: %zu) is not", use, i);
  167. }
  168. else if ((hardware > 0)&&((flags&PCILIB_KMEM_FLAG_HARDWARE) == 0)) {
  169. err = PCILIB_ERROR_INVALID_STATE;
  170. sprintf(error, "Expecting to re-use hardware-locked blocks, but buffer (use 0x%x, block: %zu) is not", use, i);
  171. }
  172. if (err) {
  173. kbuf->buf.n_blocks = i + 1;
  174. break;
  175. }
  176. }
  177. if ((kh.align)&&((kh.type&PCILIB_KMEM_TYPE_MASK) != PCILIB_KMEM_TYPE_PAGE)) {
  178. // Physical or bus address here?
  179. if (kh.ba) {
  180. if (kh.ba % kh.align) kbuf->buf.blocks[i].alignment_offset = kh.align - kh.ba % kh.align;
  181. } else {
  182. if (kh.pa % kh.align) kbuf->buf.blocks[i].alignment_offset = kh.align - kh.pa % kh.align;
  183. }
  184. kbuf->buf.blocks[i].size -= kh.align;
  185. }
  186. addr = mmap( 0, kbuf->buf.blocks[i].size + kbuf->buf.blocks[i].alignment_offset, PROT_WRITE | PROT_READ, MAP_SHARED, ctx->handle, 0 );
  187. if ((!addr)||(addr == MAP_FAILED)) {
  188. kbuf->buf.n_blocks = i + 1;
  189. err = PCILIB_ERROR_FAILED;
  190. sprintf(error, "Driver prevents us from mmaping buffer (use 0x%x, block: %zu), mmap have failed with errno %i", use, i, errno);
  191. break;
  192. }
  193. kbuf->buf.blocks[i].ua = addr;
  194. kbuf->buf.blocks[i].mmap_offset = kh.pa & ctx->page_mask;
  195. }
  196. if (err) kbuf->buf.n_blocks = i + 1;
  197. else kbuf->buf.n_blocks = i;
  198. // Check if there are more unpicked buffers
  199. if ((!err)&&((flags&PCILIB_KMEM_FLAG_MASS) == 0)&&(reused == PCILIB_TRISTATE_YES)&&((type&PCILIB_KMEM_TYPE_MASK) != PCILIB_KMEM_TYPE_REGION)) {
  200. kh.item = kbuf->buf.n_blocks;
  201. kh.flags = KMEM_FLAG_REUSE|KMEM_FLAG_TRY;
  202. ret = ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_ALLOC, &kh);
  203. if (!ret) {
  204. kh.flags = KMEM_FLAG_REUSE;
  205. ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_FREE, &kh);
  206. reused = PCILIB_TRISTATE_PARTIAL;
  207. } else if (errno != ENOENT) {
  208. reused = PCILIB_TRISTATE_PARTIAL;
  209. }
  210. }
  211. pcilib_unlock_global(ctx);
  212. //This is possible in the case of error (nothing is allocated yet) or if buffers are not reused
  213. if (persistent < 0) persistent = 0;
  214. if (hardware < 0) hardware = 0;
  215. if (err) {
  216. // do not clean if we have reused (even partially) persistent/hardware-locked buffers
  217. if (((persistent)||(hardware))&&(reused != PCILIB_TRISTATE_NO)) {
  218. pcilib_cancel_kernel_memory(ctx, kbuf, KMEM_FLAG_REUSE, 0);
  219. } else {
  220. pcilib_kmem_flags_t free_flags = 0;
  221. if (flags&PCILIB_KMEM_FLAG_PERSISTENT) {
  222. free_flags |= PCILIB_KMEM_FLAG_PERSISTENT;
  223. }
  224. if (flags&PCILIB_KMEM_FLAG_HARDWARE) {
  225. free_flags |= PCILIB_KMEM_FLAG_HARDWARE;
  226. }
  227. // err indicates consistensy error. The last ioctl have succeeded and we need to clean it in a special way
  228. pcilib_cancel_kernel_memory(ctx, kbuf, free_flags, (err == PCILIB_ERROR_INVALID_STATE)?kh.flags:0);
  229. }
  230. pcilib_warning("Error %i: %s", err, error);
  231. return NULL;
  232. }
  233. if (nmemb == 1) {
  234. memcpy(&kbuf->buf.addr, &kbuf->buf.blocks[0], sizeof(pcilib_kmem_addr_t));
  235. }
  236. kbuf->buf.type = type;
  237. kbuf->buf.use = use;
  238. kbuf->buf.reused = reused|(persistent?PCILIB_KMEM_REUSE_PERSISTENT:0)|(hardware?PCILIB_KMEM_REUSE_HARDWARE:0);
  239. kbuf->prev = NULL;
  240. kbuf->next = ctx->kmem_list;
  241. if (ctx->kmem_list) ctx->kmem_list->prev = kbuf;
  242. ctx->kmem_list = kbuf;
  243. return (pcilib_kmem_handle_t*)kbuf;
  244. }
  245. void pcilib_free_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_flags_t flags) {
  246. int ret, err = 0;
  247. int i;
  248. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  249. // if linked in to the list
  250. if (kbuf->next) kbuf->next->prev = kbuf->prev;
  251. if (kbuf->prev) kbuf->prev->next = kbuf->next;
  252. else if (ctx->kmem_list == kbuf) ctx->kmem_list = kbuf->next;
  253. for (i = 0; i < kbuf->buf.n_blocks; i++) {
  254. ret = pcilib_free_kernel_buffer(ctx, kbuf, i, flags);
  255. if ((ret)&&(!err)) err = ret;
  256. }
  257. free(kbuf);
  258. if (err) {
  259. pcilib_error("PCIDRIVER_IOC_KMEM_FREE ioctl have failed");
  260. }
  261. }
  262. /*
  263. int pcilib_kmem_sync(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_sync_direction_t dir) {
  264. int i;
  265. int ret;
  266. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  267. for (i = 0; i < kbuf->buf.n_blocks; i++) {
  268. ret = pcilib_kmem_sync_block(ctx, k, dir, i);
  269. if (ret) {
  270. pcilib_error("PCIDRIVER_IOC_KMEM_SYNC ioctl have failed");
  271. return PCILIB_ERROR_FAILED;
  272. }
  273. }
  274. return 0;
  275. }
  276. */
  277. int pcilib_kmem_sync_block(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_sync_direction_t dir, size_t block) {
  278. int ret;
  279. kmem_sync_t ks;
  280. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  281. switch (kbuf->buf.type) {
  282. case PCILIB_KMEM_TYPE_DMA_S2C_PAGE:
  283. case PCILIB_KMEM_TYPE_DMA_C2S_PAGE:
  284. case PCILIB_KMEM_TYPE_REGION_S2C:
  285. case PCILIB_KMEM_TYPE_REGION_C2S:
  286. ks.dir = dir;
  287. ks.handle.handle_id = kbuf->buf.blocks[block].handle_id;
  288. ks.handle.pa = kbuf->buf.blocks[block].pa;
  289. ret = ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_SYNC, &ks);
  290. if (ret) {
  291. pcilib_error("PCIDRIVER_IOC_KMEM_SYNC ioctl have failed");
  292. return PCILIB_ERROR_FAILED;
  293. }
  294. break;
  295. default:
  296. ;
  297. }
  298. return 0;
  299. }
  300. volatile void *pcilib_kmem_get_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
  301. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  302. return kbuf->buf.addr.ua + kbuf->buf.addr.alignment_offset + kbuf->buf.addr.mmap_offset;
  303. }
  304. uintptr_t pcilib_kmem_get_pa(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
  305. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  306. return kbuf->buf.addr.pa + kbuf->buf.addr.alignment_offset;
  307. }
  308. uintptr_t pcilib_kmem_get_ba(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
  309. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  310. if (kbuf->buf.addr.ba)
  311. return kbuf->buf.addr.ba + kbuf->buf.addr.alignment_offset;
  312. return 0;
  313. }
  314. volatile void *pcilib_kmem_get_block_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
  315. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  316. return kbuf->buf.blocks[block].ua + kbuf->buf.blocks[block].alignment_offset + kbuf->buf.blocks[block].mmap_offset;
  317. }
  318. uintptr_t pcilib_kmem_get_block_pa(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
  319. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  320. return kbuf->buf.blocks[block].pa + kbuf->buf.blocks[block].alignment_offset;
  321. }
  322. uintptr_t pcilib_kmem_get_block_ba(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
  323. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  324. if (kbuf->buf.blocks[block].ba)
  325. return kbuf->buf.blocks[block].ba + kbuf->buf.blocks[block].alignment_offset;
  326. return 0;
  327. }
  328. size_t pcilib_kmem_get_block_size(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
  329. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  330. return kbuf->buf.blocks[block].size;
  331. }
  332. pcilib_kmem_reuse_state_t pcilib_kmem_is_reused(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
  333. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  334. return kbuf->buf.reused;
  335. }