Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / char / mem.c
1 /*
2  *  Originally from linux/drivers/char/mem.c
3  *
4  *  Copyright (C) 1991, 1992  Linus Torvalds
5  *
6  *  Added devfs support.
7  *    Jan-11-1998, C. Scott Ananian <cananian@alumni.princeton.edu>
8  *  Shared /dev/zero mmapping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
9  */
10
11 #include <linux/mm.h>
12 #include <linux/init.h>
13 #include <linux/fs.h>
14 #include <linux/capability.h>
15 #include <linux/ptrace.h>
16 #include <asm/uaccess.h>
17 #include <asm/io.h>
18 #include <asm/hypervisor.h>
19
20 static inline unsigned long size_inside_page(unsigned long start,
21                                              unsigned long size)
22 {
23         unsigned long sz;
24
25         sz = PAGE_SIZE - (start & (PAGE_SIZE - 1));
26
27         return min(sz, size);
28 }
29
30 static inline int uncached_access(struct file *file)
31 {
32         if (file->f_flags & O_DSYNC)
33                 return 1;
34         /* Xen sets correct MTRR type on non-RAM for us. */
35         return 0;
36 }
37
38 static inline int range_is_allowed(unsigned long pfn, unsigned long size)
39 {
40 #ifdef CONFIG_STRICT_DEVMEM
41         u64 from = ((u64)pfn) << PAGE_SHIFT;
42         u64 to = from + size;
43         u64 cursor = from;
44
45         while (cursor < to) {
46                 if (!devmem_is_allowed(pfn)) {
47                         printk(KERN_INFO
48                 "Program %s tried to access /dev/mem between %Lx->%Lx.\n",
49                                 current->comm, from, to);
50                         return 0;
51                 }
52                 cursor += PAGE_SIZE;
53                 pfn++;
54         }
55 #endif
56         return 1;
57 }
58
59 /*
60  * This funcion reads the *physical* memory. The f_pos points directly to the
61  * memory location.
62  */
63 static ssize_t read_mem(struct file *file, char __user *buf,
64                         size_t count, loff_t *ppos)
65 {
66         unsigned long p = *ppos;
67         ssize_t read = 0, sz;
68         void __iomem *v;
69
70         while (count > 0) {
71                 unsigned long remaining;
72
73                 sz = size_inside_page(p, count);
74
75                 if (!range_is_allowed(p >> PAGE_SHIFT, count))
76                         return -EPERM;
77
78                 v = ioremap(p, sz);
79                 if (IS_ERR(v) || v == NULL) {
80                         /*
81                          * Some programs (e.g., dmidecode) groove off into
82                          * weird RAM areas where no tables can possibly exist
83                          * (because Xen will have stomped on them!). These
84                          * programs get rather upset if we let them know that
85                          * Xen failed their access, so we fake out a read of
86                          * all zeroes.
87                          */
88                         if (clear_user(buf, count))
89                                 return -EFAULT;
90                         read += count;
91                         break;
92                 }
93
94                 remaining = copy_to_user(buf, v, sz);
95                 iounmap(v);
96                 if (remaining)
97                         return -EFAULT;
98
99                 buf += sz;
100                 p += sz;
101                 count -= sz;
102                 read += sz;
103         }
104
105         *ppos += read;
106         return read;
107 }
108
109 static ssize_t write_mem(struct file *file, const char __user *buf,
110                          size_t count, loff_t *ppos)
111 {
112         unsigned long p = *ppos, ignored;
113         ssize_t written = 0, sz;
114         void __iomem *v;
115
116         while (count > 0) {
117                 sz = size_inside_page(p, count);
118
119                 if (!range_is_allowed(p >> PAGE_SHIFT, sz))
120                         return -EPERM;
121
122                 v = ioremap(p, sz);
123                 if (v == NULL)
124                         break;
125                 if (IS_ERR(v)) {
126                         if (written == 0)
127                                 return PTR_ERR(v);
128                         break;
129                 }
130
131                 ignored = copy_from_user(v, buf, sz);
132                 iounmap(v);
133                 if (ignored) {
134                         written += sz - ignored;
135                         if (written)
136                                 break;
137                         return -EFAULT;
138                 }
139                 buf += sz;
140                 p += sz;
141                 count -= sz;
142                 written += sz;
143         }
144
145         *ppos += written;
146         return written;
147 }
148
149 #ifndef ARCH_HAS_DEV_MEM_MMAP_MEM
150 static struct vm_operations_struct mmap_mem_ops = {
151 #ifdef CONFIG_HAVE_IOREMAP_PROT
152         .access = generic_access_phys
153 #endif
154 };
155
156 static int xen_mmap_mem(struct file *file, struct vm_area_struct *vma)
157 {
158         size_t size = vma->vm_end - vma->vm_start;
159
160         if (uncached_access(file))
161                 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
162
163         if (!range_is_allowed(vma->vm_pgoff, size))
164                 return -EPERM;
165
166         if (!phys_mem_access_prot_allowed(file, vma->vm_pgoff, size,
167                                                 &vma->vm_page_prot))
168                 return -EINVAL;
169
170         vma->vm_ops = &mmap_mem_ops;
171
172         /* We want to return the real error code, not EAGAIN. */
173         return direct_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
174                                       size, vma->vm_page_prot, DOMID_IO);
175 }
176 #endif
177
178 /*
179  * The memory devices use the full 32/64 bits of the offset, and so we cannot
180  * check against negative addresses: they are ok. The return value is weird,
181  * though, in that case (0).
182  *
183  * also note that seeking relative to the "end of file" isn't supported:
184  * it has no meaning, so it returns -EINVAL.
185  */
186 static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
187 {
188         loff_t ret;
189
190         mutex_lock(&file->f_path.dentry->d_inode->i_mutex);
191         switch (orig) {
192         case SEEK_CUR:
193                 offset += file->f_pos;
194         case SEEK_SET:
195                 /* to avoid userland mistaking f_pos=-9 as -EBADF=-9 */
196                 if ((unsigned long long)offset >= ~0xFFFULL) {
197                         ret = -EOVERFLOW;
198                         break;
199                 }
200                 file->f_pos = offset;
201                 ret = file->f_pos;
202                 force_successful_syscall_return();
203                 break;
204         default:
205                 ret = -EINVAL;
206         }
207         mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
208         return ret;
209 }
210
211 static int open_mem(struct inode * inode, struct file * filp)
212 {
213         return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
214 }
215
216 const struct file_operations mem_fops = {
217         .llseek         = memory_lseek,
218         .read           = read_mem,
219         .write          = write_mem,
220         .mmap           = xen_mmap_mem,
221         .open           = open_mem,
222 };