kmem.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  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|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(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) flags&=~PCILIB_KMEM_FLAG_PERSISTENT;
  39. if (last_flags&KMEM_FLAG_REUSED_HW) 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. const char *error = NULL;
  50. int ret;
  51. int i;
  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. ret = ioctl( ctx->handle, PCIDRIVER_IOC_MMAP_MODE, PCIDRIVER_MMAP_KMEM );
  64. if (ret) {
  65. pcilib_error("PCIDRIVER_IOC_MMAP_MODE ioctl have failed");
  66. return NULL;
  67. }
  68. kh.type = type;
  69. kh.size = size;
  70. kh.align = alignment;
  71. kh.use = use;
  72. if ((type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_REGION) {
  73. kh.align = 0;
  74. } else if ((type&PCILIB_KMEM_TYPE_MASK) != PCILIB_KMEM_TYPE_PAGE) {
  75. kh.size += alignment;
  76. }
  77. for ( i = 0; i < nmemb; i++) {
  78. kh.item = i;
  79. kh.flags = flags;
  80. if ((type&PCILIB_KMEM_TYPE_MASK) == PCILIB_KMEM_TYPE_REGION) {
  81. kh.pa = alignment + i * size;
  82. }
  83. ret = ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_ALLOC, &kh);
  84. if (ret) {
  85. kbuf->buf.n_blocks = i;
  86. error = "PCIDRIVER_IOC_KMEM_ALLOC ioctl have failed";
  87. break;
  88. }
  89. kbuf->buf.blocks[i].handle_id = kh.handle_id;
  90. kbuf->buf.blocks[i].pa = kh.pa;
  91. kbuf->buf.blocks[i].size = kh.size;
  92. if (!i) reused = (kh.flags&KMEM_FLAG_REUSED)?PCILIB_TRISTATE_YES:PCILIB_TRISTATE_NO;
  93. if (kh.flags&KMEM_FLAG_REUSED) {
  94. if (!i) reused = PCILIB_TRISTATE_YES;
  95. else if (!reused) reused = PCILIB_TRISTATE_PARTIAL;
  96. if (persistent) {
  97. if (persistent < 0) {
  98. /*if (((flags&PCILIB_KMEM_FLAG_PERSISTENT) == 0)&&(kh.flags&KMEM_FLAG_REUSED_PERSISTENT)) err = PCILIB_ERROR_INVALID_STATE;
  99. else*/ persistent = (kh.flags&KMEM_FLAG_REUSED_PERSISTENT)?1:0;
  100. } else if ((kh.flags&KMEM_FLAG_REUSED_PERSISTENT) == 0) err = PCILIB_ERROR_INVALID_STATE;
  101. } else if (kh.flags&KMEM_FLAG_REUSED_PERSISTENT) err = PCILIB_ERROR_INVALID_STATE;
  102. if (hardware) {
  103. if (hardware < 0) {
  104. /*if (((flags&PCILIB_KMEM_FLAG_HARDWARE) == 0)&&(kh.flags&KMEM_FLAG_REUSED_HW)) err = PCILIB_ERROR_INVALID_STATE;
  105. else*/ hardware = (kh.flags&KMEM_FLAG_REUSED_HW)?1:0;
  106. } else if ((kh.flags&KMEM_FLAG_REUSED_HW) == 0) err = PCILIB_ERROR_INVALID_STATE;
  107. } else if (kh.flags&KMEM_FLAG_REUSED_HW) err = PCILIB_ERROR_INVALID_STATE;
  108. } else {
  109. if (!i) reused = PCILIB_TRISTATE_NO;
  110. else if (reused) reused = PCILIB_TRISTATE_PARTIAL;
  111. if ((persistent > 0)&&((flags&PCILIB_KMEM_FLAG_PERSISTENT) == 0)) err = PCILIB_ERROR_INVALID_STATE;
  112. if ((hardware > 0)&&((flags&PCILIB_KMEM_FLAG_HARDWARE) == 0)) err = PCILIB_ERROR_INVALID_STATE;
  113. }
  114. if (err) {
  115. kbuf->buf.n_blocks = i + 1;
  116. break;
  117. }
  118. if ((kh.align)&&((kh.type&PCILIB_KMEM_TYPE_MASK) != PCILIB_KMEM_TYPE_PAGE)) {
  119. if (kh.pa % kh.align) kbuf->buf.blocks[i].alignment_offset = kh.align - kh.pa % kh.align;
  120. kbuf->buf.blocks[i].size -= kh.align;
  121. }
  122. addr = mmap( 0, kbuf->buf.blocks[i].size + kbuf->buf.blocks[i].alignment_offset, PROT_WRITE | PROT_READ, MAP_SHARED, ctx->handle, 0 );
  123. if ((!addr)||(addr == MAP_FAILED)) {
  124. kbuf->buf.n_blocks = i + 1;
  125. error = "Failed to mmap allocated kernel memory";
  126. break;
  127. }
  128. kbuf->buf.blocks[i].ua = addr;
  129. // if (use == PCILIB_KMEM_USE_DMA_PAGES) {
  130. // memset(addr, 10, kbuf->buf.blocks[i].size + kbuf->buf.blocks[i].alignment_offset);
  131. // }
  132. kbuf->buf.blocks[i].mmap_offset = kh.pa & ctx->page_mask;
  133. }
  134. //This is possible in the case of error (nothing is allocated yet) or if buffers are not reused
  135. if (persistent < 0) persistent = 0;
  136. if (hardware < 0) hardware = 0;
  137. if (err||error) {
  138. pcilib_kmem_flags_t free_flags = 0;
  139. // for the sake of simplicity always clean partialy reused buffers
  140. if ((persistent == PCILIB_TRISTATE_PARTIAL)||((persistent <= 0)&&(flags&PCILIB_KMEM_FLAG_PERSISTENT))) {
  141. free_flags |= PCILIB_KMEM_FLAG_PERSISTENT;
  142. }
  143. if ((hardware <= 0)&&(flags&PCILIB_KMEM_FLAG_HARDWARE)) {
  144. free_flags |= PCILIB_KMEM_FLAG_HARDWARE;
  145. }
  146. // do not clean if we have reused peresistent buffers
  147. // we don't care about -1, because it will be the value only if no buffers actually allocated
  148. if ((!persistent)||(reused != PCILIB_TRISTATE_YES)) {
  149. pcilib_cancel_kernel_memory(ctx, kbuf, free_flags, err?kh.flags:0);
  150. }
  151. if (!error) error = "Reused buffers are inconsistent";
  152. pcilib_error(error);
  153. return NULL;
  154. }
  155. if (nmemb == 1) {
  156. memcpy(&kbuf->buf.addr, &kbuf->buf.blocks[0], sizeof(pcilib_kmem_addr_t));
  157. }
  158. kbuf->buf.reused = reused|(persistent?PCILIB_KMEM_REUSE_PERSISTENT:0)|(hardware?PCILIB_KMEM_REUSE_HARDWARE:0);
  159. kbuf->buf.n_blocks = nmemb;
  160. kbuf->prev = NULL;
  161. kbuf->next = ctx->kmem_list;
  162. if (ctx->kmem_list) ctx->kmem_list->prev = kbuf;
  163. ctx->kmem_list = kbuf;
  164. return (pcilib_kmem_handle_t*)kbuf;
  165. }
  166. void pcilib_free_kernel_memory(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_flags_t flags) {
  167. int ret, err = 0;
  168. int i;
  169. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  170. // if linked in to the list
  171. if (kbuf->next) kbuf->next->prev = kbuf->prev;
  172. if (kbuf->prev) kbuf->prev->next = kbuf->next;
  173. else if (ctx->kmem_list == kbuf) ctx->kmem_list = kbuf->next;
  174. for (i = 0; i < kbuf->buf.n_blocks; i++) {
  175. ret = pcilib_free_kernel_buffer(ctx, kbuf, i, flags);
  176. if ((ret)&&(!err)) err = ret;
  177. }
  178. free(kbuf);
  179. if (err) {
  180. pcilib_error("PCIDRIVER_IOC_KMEM_FREE ioctl have failed");
  181. }
  182. }
  183. /*
  184. int pcilib_kmem_sync(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_sync_direction_t dir) {
  185. int i;
  186. int ret;
  187. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  188. for (i = 0; i < kbuf->buf.n_blocks; i++) {
  189. ret = pcilib_kmem_sync_block(ctx, k, dir, i);
  190. if (ret) {
  191. pcilib_error("PCIDRIVER_IOC_KMEM_SYNC ioctl have failed");
  192. return PCILIB_ERROR_FAILED;
  193. }
  194. }
  195. return 0;
  196. }
  197. */
  198. int pcilib_kmem_sync_block(pcilib_t *ctx, pcilib_kmem_handle_t *k, pcilib_kmem_sync_direction_t dir, size_t block) {
  199. int ret;
  200. kmem_sync_t ks;
  201. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  202. ks.dir = dir;
  203. ks.handle.handle_id = kbuf->buf.blocks[block].handle_id;
  204. ks.handle.pa = kbuf->buf.blocks[block].pa;
  205. ret = ioctl(ctx->handle, PCIDRIVER_IOC_KMEM_SYNC, &ks);
  206. if (ret) {
  207. pcilib_error("PCIDRIVER_IOC_KMEM_SYNC ioctl have failed");
  208. return PCILIB_ERROR_FAILED;
  209. }
  210. return 0;
  211. }
  212. void *pcilib_kmem_get_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
  213. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  214. return kbuf->buf.addr.ua + kbuf->buf.addr.alignment_offset + kbuf->buf.addr.mmap_offset;
  215. }
  216. uintptr_t pcilib_kmem_get_pa(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
  217. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  218. return kbuf->buf.addr.pa + kbuf->buf.addr.alignment_offset;
  219. }
  220. uintptr_t pcilib_kmem_get_ba(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
  221. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  222. return kbuf->buf.addr.pa + kbuf->buf.addr.alignment_offset;
  223. }
  224. void *pcilib_kmem_get_block_ua(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
  225. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  226. return kbuf->buf.blocks[block].ua + kbuf->buf.blocks[block].alignment_offset + kbuf->buf.blocks[block].mmap_offset;
  227. }
  228. uintptr_t pcilib_kmem_get_block_pa(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
  229. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  230. return kbuf->buf.blocks[block].pa + kbuf->buf.blocks[block].alignment_offset;
  231. }
  232. uintptr_t pcilib_kmem_get_block_ba(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
  233. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  234. return kbuf->buf.blocks[block].pa + kbuf->buf.blocks[block].alignment_offset;
  235. }
  236. size_t pcilib_kmem_get_block_size(pcilib_t *ctx, pcilib_kmem_handle_t *k, size_t block) {
  237. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  238. return kbuf->buf.blocks[block].size;
  239. }
  240. pcilib_kmem_reuse_state_t pcilib_kmem_is_reused(pcilib_t *ctx, pcilib_kmem_handle_t *k) {
  241. pcilib_kmem_list_t *kbuf = (pcilib_kmem_list_t*)k;
  242. return kbuf->buf.reused;
  243. }