Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
cache.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2004-2006 Atmel Corporation
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License version 2 as
6  * published by the Free Software Foundation.
7  */
8 
9 #include <linux/highmem.h>
10 #include <linux/unistd.h>
11 
12 #include <asm/cacheflush.h>
13 #include <asm/cachectl.h>
14 #include <asm/processor.h>
15 #include <asm/uaccess.h>
16 #include <asm/syscalls.h>
17 
18 /*
19  * If you attempt to flush anything more than this, you need superuser
20  * privileges. The value is completely arbitrary.
21  */
22 #define CACHEFLUSH_MAX_LEN 1024
23 
24 void invalidate_dcache_region(void *start, size_t size)
25 {
26  unsigned long v, begin, end, linesz, mask;
27 
28  linesz = boot_cpu_data.dcache.linesz;
29  mask = linesz - 1;
30 
31  /* when first and/or last cachelines are shared, flush them
32  * instead of invalidating ... never discard valid data!
33  */
34  begin = (unsigned long)start;
35  end = begin + size;
36 
37  if (begin & mask) {
38  flush_dcache_line(start);
39  begin += linesz;
40  }
41  if (end & mask) {
42  flush_dcache_line((void *)end);
43  end &= ~mask;
44  }
45 
46  /* remaining cachelines only need invalidation */
47  for (v = begin; v < end; v += linesz)
48  invalidate_dcache_line((void *)v);
50 }
51 
52 void clean_dcache_region(void *start, size_t size)
53 {
54  unsigned long v, begin, end, linesz;
55 
56  linesz = boot_cpu_data.dcache.linesz;
57  begin = (unsigned long)start & ~(linesz - 1);
58  end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
59 
60  for (v = begin; v < end; v += linesz)
61  clean_dcache_line((void *)v);
63 }
64 
65 void flush_dcache_region(void *start, size_t size)
66 {
67  unsigned long v, begin, end, linesz;
68 
69  linesz = boot_cpu_data.dcache.linesz;
70  begin = (unsigned long)start & ~(linesz - 1);
71  end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
72 
73  for (v = begin; v < end; v += linesz)
74  flush_dcache_line((void *)v);
76 }
77 
78 void invalidate_icache_region(void *start, size_t size)
79 {
80  unsigned long v, begin, end, linesz;
81 
82  linesz = boot_cpu_data.icache.linesz;
83  begin = (unsigned long)start & ~(linesz - 1);
84  end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1);
85 
86  for (v = begin; v < end; v += linesz)
87  invalidate_icache_line((void *)v);
88 }
89 
90 static inline void __flush_icache_range(unsigned long start, unsigned long end)
91 {
92  unsigned long v, linesz;
93 
94  linesz = boot_cpu_data.dcache.linesz;
95  for (v = start; v < end; v += linesz) {
96  clean_dcache_line((void *)v);
97  invalidate_icache_line((void *)v);
98  }
99 
101 }
102 
103 /*
104  * This one is called after a module has been loaded.
105  */
106 void flush_icache_range(unsigned long start, unsigned long end)
107 {
108  unsigned long linesz;
109 
110  linesz = boot_cpu_data.dcache.linesz;
111  __flush_icache_range(start & ~(linesz - 1),
112  (end + linesz - 1) & ~(linesz - 1));
113 }
114 
115 /*
116  * This one is called from __do_fault() and do_swap_page().
117  */
118 void flush_icache_page(struct vm_area_struct *vma, struct page *page)
119 {
120  if (vma->vm_flags & VM_EXEC) {
121  void *v = page_address(page);
122  __flush_icache_range((unsigned long)v, (unsigned long)v + PAGE_SIZE);
123  }
124 }
125 
126 asmlinkage int sys_cacheflush(int operation, void __user *addr, size_t len)
127 {
128  int ret;
129 
130  if (len > CACHEFLUSH_MAX_LEN) {
131  ret = -EPERM;
132  if (!capable(CAP_SYS_ADMIN))
133  goto out;
134  }
135 
136  ret = -EFAULT;
137  if (!access_ok(VERIFY_WRITE, addr, len))
138  goto out;
139 
140  switch (operation) {
141  case CACHE_IFLUSH:
142  flush_icache_range((unsigned long)addr,
143  (unsigned long)addr + len);
144  ret = 0;
145  break;
146  default:
147  ret = -EINVAL;
148  }
149 
150 out:
151  return ret;
152 }
153 
154 void copy_to_user_page(struct vm_area_struct *vma, struct page *page,
155  unsigned long vaddr, void *dst, const void *src,
156  unsigned long len)
157 {
158  memcpy(dst, src, len);
159  if (vma->vm_flags & VM_EXEC)
160  flush_icache_range((unsigned long)dst,
161  (unsigned long)dst + len);
162 }