memcpy.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524
  1. /*
  2. * Optimized memory copy routines.
  3. *
  4. * Copyright (C) 2004 Randolph Chung <tausq@debian.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2, or (at your option)
  9. * any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  19. *
  20. * Portions derived from the GNU C Library
  21. * Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc.
  22. *
  23. * Several strategies are tried to try to get the best performance for various
  24. * conditions. In the optimal case, we copy 64-bytes in an unrolled loop using
  25. * fp regs. This is followed by loops that copy 32- or 16-bytes at a time using
  26. * general registers. Unaligned copies are handled either by aligning the
  27. * destination and then using shift-and-write method, or in a few cases by
  28. * falling back to a byte-at-a-time copy.
  29. *
  30. * I chose to implement this in C because it is easier to maintain and debug,
  31. * and in my experiments it appears that the C code generated by gcc (3.3/3.4
  32. * at the time of writing) is fairly optimal. Unfortunately some of the
  33. * semantics of the copy routine (exception handling) is difficult to express
  34. * in C, so we have to play some tricks to get it to work.
  35. *
  36. * All the loads and stores are done via explicit asm() code in order to use
  37. * the right space registers.
  38. *
  39. * Testing with various alignments and buffer sizes shows that this code is
  40. * often >10x faster than a simple byte-at-a-time copy, even for strangely
  41. * aligned operands. It is interesting to note that the glibc version
  42. * of memcpy (written in C) is actually quite fast already. This routine is
  43. * able to beat it by 30-40% for aligned copies because of the loop unrolling,
  44. * but in some cases the glibc version is still slightly faster. This lends
  45. * more credibility that gcc can generate very good code as long as we are
  46. * careful.
  47. *
  48. * TODO:
  49. * - cache prefetching needs more experimentation to get optimal settings
  50. * - try not to use the post-increment address modifiers; they create additional
  51. * interlocks
  52. * - replace byte-copy loops with stybs sequences
  53. */
  54. #ifdef __KERNEL__
  55. #include <linux/config.h>
  56. #include <linux/module.h>
  57. #include <linux/compiler.h>
  58. #include <asm/uaccess.h>
  59. #define s_space "%%sr1"
  60. #define d_space "%%sr2"
  61. #else
  62. #include "memcpy.h"
  63. #define s_space "%%sr0"
  64. #define d_space "%%sr0"
  65. #define pa_memcpy new2_copy
  66. #endif
  67. DECLARE_PER_CPU(struct exception_data, exception_data);
  68. #define preserve_branch(label) do { \
  69. volatile int dummy; \
  70. /* The following branch is never taken, it's just here to */ \
  71. /* prevent gcc from optimizing away our exception code. */ \
  72. if (unlikely(dummy != dummy)) \
  73. goto label; \
  74. } while (0)
  75. #define get_user_space() (segment_eq(get_fs(), KERNEL_DS) ? 0 : mfsp(3))
  76. #define get_kernel_space() (0)
  77. #define MERGE(w0, sh_1, w1, sh_2) ({ \
  78. unsigned int _r; \
  79. asm volatile ( \
  80. "mtsar %3\n" \
  81. "shrpw %1, %2, %%sar, %0\n" \
  82. : "=r"(_r) \
  83. : "r"(w0), "r"(w1), "r"(sh_2) \
  84. ); \
  85. _r; \
  86. })
  87. #define THRESHOLD 16
  88. #ifdef DEBUG_MEMCPY
  89. #define DPRINTF(fmt, args...) do { printk(KERN_DEBUG "%s:%d:%s ", __FILE__, __LINE__, __FUNCTION__ ); printk(KERN_DEBUG fmt, ##args ); } while (0)
  90. #else
  91. #define DPRINTF(fmt, args...)
  92. #endif
  93. #ifndef __LP64__
  94. #define EXC_WORD ".word"
  95. #else
  96. #define EXC_WORD ".dword"
  97. #endif
  98. #define def_load_ai_insn(_insn,_sz,_tt,_s,_a,_t,_e) \
  99. __asm__ __volatile__ ( \
  100. "1:\t" #_insn ",ma " #_sz "(" _s ",%1), %0\n" \
  101. "\t.section __ex_table,\"aw\"\n" \
  102. "\t" EXC_WORD "\t1b\n" \
  103. "\t" EXC_WORD "\t" #_e "\n" \
  104. "\t.previous\n" \
  105. : _tt(_t), "+r"(_a) \
  106. : \
  107. : "r8")
  108. #define def_store_ai_insn(_insn,_sz,_tt,_s,_a,_t,_e) \
  109. __asm__ __volatile__ ( \
  110. "1:\t" #_insn ",ma %1, " #_sz "(" _s ",%0)\n" \
  111. "\t.section __ex_table,\"aw\"\n" \
  112. "\t" EXC_WORD "\t1b\n" \
  113. "\t" EXC_WORD "\t" #_e "\n" \
  114. "\t.previous\n" \
  115. : "+r"(_a) \
  116. : _tt(_t) \
  117. : "r8")
  118. #define ldbma(_s, _a, _t, _e) def_load_ai_insn(ldbs,1,"=r",_s,_a,_t,_e)
  119. #define stbma(_s, _t, _a, _e) def_store_ai_insn(stbs,1,"r",_s,_a,_t,_e)
  120. #define ldwma(_s, _a, _t, _e) def_load_ai_insn(ldw,4,"=r",_s,_a,_t,_e)
  121. #define stwma(_s, _t, _a, _e) def_store_ai_insn(stw,4,"r",_s,_a,_t,_e)
  122. #define flddma(_s, _a, _t, _e) def_load_ai_insn(fldd,8,"=f",_s,_a,_t,_e)
  123. #define fstdma(_s, _t, _a, _e) def_store_ai_insn(fstd,8,"f",_s,_a,_t,_e)
  124. #define def_load_insn(_insn,_tt,_s,_o,_a,_t,_e) \
  125. __asm__ __volatile__ ( \
  126. "1:\t" #_insn " " #_o "(" _s ",%1), %0\n" \
  127. "\t.section __ex_table,\"aw\"\n" \
  128. "\t" EXC_WORD "\t1b\n" \
  129. "\t" EXC_WORD "\t" #_e "\n" \
  130. "\t.previous\n" \
  131. : _tt(_t) \
  132. : "r"(_a) \
  133. : "r8")
  134. #define def_store_insn(_insn,_tt,_s,_t,_o,_a,_e) \
  135. __asm__ __volatile__ ( \
  136. "1:\t" #_insn " %0, " #_o "(" _s ",%1)\n" \
  137. "\t.section __ex_table,\"aw\"\n" \
  138. "\t" EXC_WORD "\t1b\n" \
  139. "\t" EXC_WORD "\t" #_e "\n" \
  140. "\t.previous\n" \
  141. : \
  142. : _tt(_t), "r"(_a) \
  143. : "r8")
  144. #define ldw(_s,_o,_a,_t,_e) def_load_insn(ldw,"=r",_s,_o,_a,_t,_e)
  145. #define stw(_s,_t,_o,_a,_e) def_store_insn(stw,"r",_s,_t,_o,_a,_e)
  146. #ifdef CONFIG_PREFETCH
  147. extern inline void prefetch_src(const void *addr)
  148. {
  149. __asm__("ldw 0(" s_space ",%0), %%r0" : : "r" (addr));
  150. }
  151. extern inline void prefetch_dst(const void *addr)
  152. {
  153. __asm__("ldd 0(" d_space ",%0), %%r0" : : "r" (addr));
  154. }
  155. #else
  156. #define prefetch_src(addr)
  157. #define prefetch_dst(addr)
  158. #endif
  159. /* Copy from a not-aligned src to an aligned dst, using shifts. Handles 4 words
  160. * per loop. This code is derived from glibc.
  161. */
  162. static inline unsigned long copy_dstaligned(unsigned long dst, unsigned long src, unsigned long len, unsigned long o_dst, unsigned long o_src, unsigned long o_len)
  163. {
  164. /* gcc complains that a2 and a3 may be uninitialized, but actually
  165. * they cannot be. Initialize a2/a3 to shut gcc up.
  166. */
  167. register unsigned int a0, a1, a2 = 0, a3 = 0;
  168. int sh_1, sh_2;
  169. struct exception_data *d;
  170. /* prefetch_src((const void *)src); */
  171. /* Calculate how to shift a word read at the memory operation
  172. aligned srcp to make it aligned for copy. */
  173. sh_1 = 8 * (src % sizeof(unsigned int));
  174. sh_2 = 8 * sizeof(unsigned int) - sh_1;
  175. /* Make src aligned by rounding it down. */
  176. src &= -sizeof(unsigned int);
  177. switch (len % 4)
  178. {
  179. case 2:
  180. /* a1 = ((unsigned int *) src)[0];
  181. a2 = ((unsigned int *) src)[1]; */
  182. ldw(s_space, 0, src, a1, cda_ldw_exc);
  183. ldw(s_space, 4, src, a2, cda_ldw_exc);
  184. src -= 1 * sizeof(unsigned int);
  185. dst -= 3 * sizeof(unsigned int);
  186. len += 2;
  187. goto do1;
  188. case 3:
  189. /* a0 = ((unsigned int *) src)[0];
  190. a1 = ((unsigned int *) src)[1]; */
  191. ldw(s_space, 0, src, a0, cda_ldw_exc);
  192. ldw(s_space, 4, src, a1, cda_ldw_exc);
  193. src -= 0 * sizeof(unsigned int);
  194. dst -= 2 * sizeof(unsigned int);
  195. len += 1;
  196. goto do2;
  197. case 0:
  198. if (len == 0)
  199. return 0;
  200. /* a3 = ((unsigned int *) src)[0];
  201. a0 = ((unsigned int *) src)[1]; */
  202. ldw(s_space, 0, src, a3, cda_ldw_exc);
  203. ldw(s_space, 4, src, a0, cda_ldw_exc);
  204. src -=-1 * sizeof(unsigned int);
  205. dst -= 1 * sizeof(unsigned int);
  206. len += 0;
  207. goto do3;
  208. case 1:
  209. /* a2 = ((unsigned int *) src)[0];
  210. a3 = ((unsigned int *) src)[1]; */
  211. ldw(s_space, 0, src, a2, cda_ldw_exc);
  212. ldw(s_space, 4, src, a3, cda_ldw_exc);
  213. src -=-2 * sizeof(unsigned int);
  214. dst -= 0 * sizeof(unsigned int);
  215. len -= 1;
  216. if (len == 0)
  217. goto do0;
  218. goto do4; /* No-op. */
  219. }
  220. do
  221. {
  222. /* prefetch_src((const void *)(src + 4 * sizeof(unsigned int))); */
  223. do4:
  224. /* a0 = ((unsigned int *) src)[0]; */
  225. ldw(s_space, 0, src, a0, cda_ldw_exc);
  226. /* ((unsigned int *) dst)[0] = MERGE (a2, sh_1, a3, sh_2); */
  227. stw(d_space, MERGE (a2, sh_1, a3, sh_2), 0, dst, cda_stw_exc);
  228. do3:
  229. /* a1 = ((unsigned int *) src)[1]; */
  230. ldw(s_space, 4, src, a1, cda_ldw_exc);
  231. /* ((unsigned int *) dst)[1] = MERGE (a3, sh_1, a0, sh_2); */
  232. stw(d_space, MERGE (a3, sh_1, a0, sh_2), 4, dst, cda_stw_exc);
  233. do2:
  234. /* a2 = ((unsigned int *) src)[2]; */
  235. ldw(s_space, 8, src, a2, cda_ldw_exc);
  236. /* ((unsigned int *) dst)[2] = MERGE (a0, sh_1, a1, sh_2); */
  237. stw(d_space, MERGE (a0, sh_1, a1, sh_2), 8, dst, cda_stw_exc);
  238. do1:
  239. /* a3 = ((unsigned int *) src)[3]; */
  240. ldw(s_space, 12, src, a3, cda_ldw_exc);
  241. /* ((unsigned int *) dst)[3] = MERGE (a1, sh_1, a2, sh_2); */
  242. stw(d_space, MERGE (a1, sh_1, a2, sh_2), 12, dst, cda_stw_exc);
  243. src += 4 * sizeof(unsigned int);
  244. dst += 4 * sizeof(unsigned int);
  245. len -= 4;
  246. }
  247. while (len != 0);
  248. do0:
  249. /* ((unsigned int *) dst)[0] = MERGE (a2, sh_1, a3, sh_2); */
  250. stw(d_space, MERGE (a2, sh_1, a3, sh_2), 0, dst, cda_stw_exc);
  251. preserve_branch(handle_load_error);
  252. preserve_branch(handle_store_error);
  253. return 0;
  254. handle_load_error:
  255. __asm__ __volatile__ ("cda_ldw_exc:\n");
  256. d = &__get_cpu_var(exception_data);
  257. DPRINTF("cda_ldw_exc: o_len=%lu fault_addr=%lu o_src=%lu ret=%lu\n",
  258. o_len, d->fault_addr, o_src, o_len - d->fault_addr + o_src);
  259. return o_len * 4 - d->fault_addr + o_src;
  260. handle_store_error:
  261. __asm__ __volatile__ ("cda_stw_exc:\n");
  262. d = &__get_cpu_var(exception_data);
  263. DPRINTF("cda_stw_exc: o_len=%lu fault_addr=%lu o_dst=%lu ret=%lu\n",
  264. o_len, d->fault_addr, o_dst, o_len - d->fault_addr + o_dst);
  265. return o_len * 4 - d->fault_addr + o_dst;
  266. }
  267. /* Returns 0 for success, otherwise, returns number of bytes not transferred. */
  268. unsigned long pa_memcpy(void *dstp, const void *srcp, unsigned long len)
  269. {
  270. register unsigned long src, dst, t1, t2, t3;
  271. register unsigned char *pcs, *pcd;
  272. register unsigned int *pws, *pwd;
  273. register double *pds, *pdd;
  274. unsigned long ret = 0;
  275. unsigned long o_dst, o_src, o_len;
  276. struct exception_data *d;
  277. src = (unsigned long)srcp;
  278. dst = (unsigned long)dstp;
  279. pcs = (unsigned char *)srcp;
  280. pcd = (unsigned char *)dstp;
  281. o_dst = dst; o_src = src; o_len = len;
  282. /* prefetch_src((const void *)srcp); */
  283. if (len < THRESHOLD)
  284. goto byte_copy;
  285. /* Check alignment */
  286. t1 = (src ^ dst);
  287. if (unlikely(t1 & (sizeof(double)-1)))
  288. goto unaligned_copy;
  289. /* src and dst have same alignment. */
  290. /* Copy bytes till we are double-aligned. */
  291. t2 = src & (sizeof(double) - 1);
  292. if (unlikely(t2 != 0)) {
  293. t2 = sizeof(double) - t2;
  294. while (t2 && len) {
  295. /* *pcd++ = *pcs++; */
  296. ldbma(s_space, pcs, t3, pmc_load_exc);
  297. len--;
  298. stbma(d_space, t3, pcd, pmc_store_exc);
  299. t2--;
  300. }
  301. }
  302. pds = (double *)pcs;
  303. pdd = (double *)pcd;
  304. #if 0
  305. /* Copy 8 doubles at a time */
  306. while (len >= 8*sizeof(double)) {
  307. register double r1, r2, r3, r4, r5, r6, r7, r8;
  308. /* prefetch_src((char *)pds + L1_CACHE_BYTES); */
  309. flddma(s_space, pds, r1, pmc_load_exc);
  310. flddma(s_space, pds, r2, pmc_load_exc);
  311. flddma(s_space, pds, r3, pmc_load_exc);
  312. flddma(s_space, pds, r4, pmc_load_exc);
  313. fstdma(d_space, r1, pdd, pmc_store_exc);
  314. fstdma(d_space, r2, pdd, pmc_store_exc);
  315. fstdma(d_space, r3, pdd, pmc_store_exc);
  316. fstdma(d_space, r4, pdd, pmc_store_exc);
  317. #if 0
  318. if (L1_CACHE_BYTES <= 32)
  319. prefetch_src((char *)pds + L1_CACHE_BYTES);
  320. #endif
  321. flddma(s_space, pds, r5, pmc_load_exc);
  322. flddma(s_space, pds, r6, pmc_load_exc);
  323. flddma(s_space, pds, r7, pmc_load_exc);
  324. flddma(s_space, pds, r8, pmc_load_exc);
  325. fstdma(d_space, r5, pdd, pmc_store_exc);
  326. fstdma(d_space, r6, pdd, pmc_store_exc);
  327. fstdma(d_space, r7, pdd, pmc_store_exc);
  328. fstdma(d_space, r8, pdd, pmc_store_exc);
  329. len -= 8*sizeof(double);
  330. }
  331. #endif
  332. pws = (unsigned int *)pds;
  333. pwd = (unsigned int *)pdd;
  334. word_copy:
  335. while (len >= 8*sizeof(unsigned int)) {
  336. register unsigned int r1,r2,r3,r4,r5,r6,r7,r8;
  337. /* prefetch_src((char *)pws + L1_CACHE_BYTES); */
  338. ldwma(s_space, pws, r1, pmc_load_exc);
  339. ldwma(s_space, pws, r2, pmc_load_exc);
  340. ldwma(s_space, pws, r3, pmc_load_exc);
  341. ldwma(s_space, pws, r4, pmc_load_exc);
  342. stwma(d_space, r1, pwd, pmc_store_exc);
  343. stwma(d_space, r2, pwd, pmc_store_exc);
  344. stwma(d_space, r3, pwd, pmc_store_exc);
  345. stwma(d_space, r4, pwd, pmc_store_exc);
  346. ldwma(s_space, pws, r5, pmc_load_exc);
  347. ldwma(s_space, pws, r6, pmc_load_exc);
  348. ldwma(s_space, pws, r7, pmc_load_exc);
  349. ldwma(s_space, pws, r8, pmc_load_exc);
  350. stwma(d_space, r5, pwd, pmc_store_exc);
  351. stwma(d_space, r6, pwd, pmc_store_exc);
  352. stwma(d_space, r7, pwd, pmc_store_exc);
  353. stwma(d_space, r8, pwd, pmc_store_exc);
  354. len -= 8*sizeof(unsigned int);
  355. }
  356. while (len >= 4*sizeof(unsigned int)) {
  357. register unsigned int r1,r2,r3,r4;
  358. ldwma(s_space, pws, r1, pmc_load_exc);
  359. ldwma(s_space, pws, r2, pmc_load_exc);
  360. ldwma(s_space, pws, r3, pmc_load_exc);
  361. ldwma(s_space, pws, r4, pmc_load_exc);
  362. stwma(d_space, r1, pwd, pmc_store_exc);
  363. stwma(d_space, r2, pwd, pmc_store_exc);
  364. stwma(d_space, r3, pwd, pmc_store_exc);
  365. stwma(d_space, r4, pwd, pmc_store_exc);
  366. len -= 4*sizeof(unsigned int);
  367. }
  368. pcs = (unsigned char *)pws;
  369. pcd = (unsigned char *)pwd;
  370. byte_copy:
  371. while (len) {
  372. /* *pcd++ = *pcs++; */
  373. ldbma(s_space, pcs, t3, pmc_load_exc);
  374. stbma(d_space, t3, pcd, pmc_store_exc);
  375. len--;
  376. }
  377. return 0;
  378. unaligned_copy:
  379. /* possibly we are aligned on a word, but not on a double... */
  380. if (likely(t1 & (sizeof(unsigned int)-1)) == 0) {
  381. t2 = src & (sizeof(unsigned int) - 1);
  382. if (unlikely(t2 != 0)) {
  383. t2 = sizeof(unsigned int) - t2;
  384. while (t2) {
  385. /* *pcd++ = *pcs++; */
  386. ldbma(s_space, pcs, t3, pmc_load_exc);
  387. stbma(d_space, t3, pcd, pmc_store_exc);
  388. len--;
  389. t2--;
  390. }
  391. }
  392. pws = (unsigned int *)pcs;
  393. pwd = (unsigned int *)pcd;
  394. goto word_copy;
  395. }
  396. /* Align the destination. */
  397. if (unlikely((dst & (sizeof(unsigned int) - 1)) != 0)) {
  398. t2 = sizeof(unsigned int) - (dst & (sizeof(unsigned int) - 1));
  399. while (t2) {
  400. /* *pcd++ = *pcs++; */
  401. ldbma(s_space, pcs, t3, pmc_load_exc);
  402. stbma(d_space, t3, pcd, pmc_store_exc);
  403. len--;
  404. t2--;
  405. }
  406. dst = (unsigned long)pcd;
  407. src = (unsigned long)pcs;
  408. }
  409. ret = copy_dstaligned(dst, src, len / sizeof(unsigned int),
  410. o_dst, o_src, o_len);
  411. if (ret)
  412. return ret;
  413. pcs += (len & -sizeof(unsigned int));
  414. pcd += (len & -sizeof(unsigned int));
  415. len %= sizeof(unsigned int);
  416. preserve_branch(handle_load_error);
  417. preserve_branch(handle_store_error);
  418. goto byte_copy;
  419. handle_load_error:
  420. __asm__ __volatile__ ("pmc_load_exc:\n");
  421. d = &__get_cpu_var(exception_data);
  422. DPRINTF("pmc_load_exc: o_len=%lu fault_addr=%lu o_src=%lu ret=%lu\n",
  423. o_len, d->fault_addr, o_src, o_len - d->fault_addr + o_src);
  424. return o_len - d->fault_addr + o_src;
  425. handle_store_error:
  426. __asm__ __volatile__ ("pmc_store_exc:\n");
  427. d = &__get_cpu_var(exception_data);
  428. DPRINTF("pmc_store_exc: o_len=%lu fault_addr=%lu o_dst=%lu ret=%lu\n",
  429. o_len, d->fault_addr, o_dst, o_len - d->fault_addr + o_dst);
  430. return o_len - d->fault_addr + o_dst;
  431. }
  432. #ifdef __KERNEL__
  433. unsigned long copy_to_user(void __user *dst, const void *src, unsigned long len)
  434. {
  435. mtsp(get_kernel_space(), 1);
  436. mtsp(get_user_space(), 2);
  437. return pa_memcpy((void __force *)dst, src, len);
  438. }
  439. unsigned long copy_from_user(void *dst, const void __user *src, unsigned long len)
  440. {
  441. mtsp(get_user_space(), 1);
  442. mtsp(get_kernel_space(), 2);
  443. return pa_memcpy(dst, (void __force *)src, len);
  444. }
  445. unsigned long copy_in_user(void __user *dst, const void __user *src, unsigned long len)
  446. {
  447. mtsp(get_user_space(), 1);
  448. mtsp(get_user_space(), 2);
  449. return pa_memcpy((void __force *)dst, (void __force *)src, len);
  450. }
  451. void * memcpy(void * dst,const void *src, size_t count)
  452. {
  453. mtsp(get_kernel_space(), 1);
  454. mtsp(get_kernel_space(), 2);
  455. pa_memcpy(dst, src, count);
  456. return dst;
  457. }
  458. EXPORT_SYMBOL(copy_to_user);
  459. EXPORT_SYMBOL(copy_from_user);
  460. EXPORT_SYMBOL(copy_in_user);
  461. EXPORT_SYMBOL(memcpy);
  462. #endif