- Updated to 2.6.22-rc2-git7:
[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 mmaping support, Feb 2000, Kanoj Sarcar <kanoj@sgi.com>
9  */
10
11 #include <linux/mm.h>
12 #include <linux/miscdevice.h>
13 #include <linux/slab.h>
14 #include <linux/vmalloc.h>
15 #include <linux/mman.h>
16 #include <linux/random.h>
17 #include <linux/init.h>
18 #include <linux/raw.h>
19 #include <linux/tty.h>
20 #include <linux/capability.h>
21 #include <linux/smp_lock.h>
22 #include <linux/ptrace.h>
23 #include <linux/device.h>
24 #include <asm/pgalloc.h>
25 #include <asm/uaccess.h>
26 #include <asm/io.h>
27 #include <asm/hypervisor.h>
28
29 #ifndef ARCH_HAS_VALID_PHYS_ADDR_RANGE
30 static inline int valid_phys_addr_range(unsigned long addr, size_t count)
31 {
32         return 1;
33 }
34 #endif
35
36 /*
37  * This funcion reads the *physical* memory. The f_pos points directly to the 
38  * memory location. 
39  */
40 static ssize_t read_mem(struct file * file, char __user * buf,
41                         size_t count, loff_t *ppos)
42 {
43         unsigned long p = *ppos, ignored;
44         ssize_t read = 0, sz;
45         void __iomem *v;
46
47         if (!valid_phys_addr_range(p, count))
48                 return -EFAULT;
49
50         while (count > 0) {
51                 /*
52                  * Handle first page in case it's not aligned
53                  */
54                 if (-p & (PAGE_SIZE - 1))
55                         sz = -p & (PAGE_SIZE - 1);
56                 else
57                         sz = PAGE_SIZE;
58
59                 sz = min_t(unsigned long, sz, count);
60
61                 v = xlate_dev_mem_ptr(p, sz);
62                 if (IS_ERR(v) || v == NULL) {
63                         /*
64                          * Some programs (e.g., dmidecode) groove off into
65                          * weird RAM areas where no tables can possibly exist
66                          * (because Xen will have stomped on them!). These
67                          * programs get rather upset if we let them know that
68                          * Xen failed their access, so we fake out a read of
69                          * all zeroes.
70                          */
71                         if (clear_user(buf, count))
72                                 return -EFAULT;
73                         read += count;
74                         break;
75                 }
76
77                 ignored = copy_to_user(buf, v, sz);
78                 xlate_dev_mem_ptr_unmap(v);
79                 if (ignored)
80                         return -EFAULT;
81                 buf += sz;
82                 p += sz;
83                 count -= sz;
84                 read += sz;
85         }
86
87         *ppos += read;
88         return read;
89 }
90
91 static ssize_t write_mem(struct file * file, const char __user * buf, 
92                          size_t count, loff_t *ppos)
93 {
94         unsigned long p = *ppos, ignored;
95         ssize_t written = 0, sz;
96         void __iomem *v;
97
98         if (!valid_phys_addr_range(p, count))
99                 return -EFAULT;
100
101         while (count > 0) {
102                 /*
103                  * Handle first page in case it's not aligned
104                  */
105                 if (-p & (PAGE_SIZE - 1))
106                         sz = -p & (PAGE_SIZE - 1);
107                 else
108                         sz = PAGE_SIZE;
109
110                 sz = min_t(unsigned long, sz, count);
111
112                 v = xlate_dev_mem_ptr(p, sz);
113                 if (v == NULL)
114                         break;
115                 if (IS_ERR(v)) {
116                         if (written == 0)
117                                 return PTR_ERR(v);
118                         break;
119                 }
120
121                 ignored = copy_from_user(v, buf, sz);
122                 xlate_dev_mem_ptr_unmap(v);
123                 if (ignored) {
124                         written += sz - ignored;
125                         if (written)
126                                 break;
127                         return -EFAULT;
128                 }
129                 buf += sz;
130                 p += sz;
131                 count -= sz;
132                 written += sz;
133         }
134
135         *ppos += written;
136         return written;
137 }
138
139 #ifndef ARCH_HAS_DEV_MEM_MMAP_MEM
140 static inline int uncached_access(struct file *file)
141 {
142         if (file->f_flags & O_SYNC)
143                 return 1;
144         /* Xen sets correct MTRR type on non-RAM for us. */
145         return 0;
146 }
147
148 static int xen_mmap_mem(struct file * file, struct vm_area_struct * vma)
149 {
150         size_t size = vma->vm_end - vma->vm_start;
151
152         if (uncached_access(file))
153                 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
154
155         /* We want to return the real error code, not EAGAIN. */
156         return direct_remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff,
157                                       size, vma->vm_page_prot, DOMID_IO);
158 }
159 #endif
160
161 /*
162  * The memory devices use the full 32/64 bits of the offset, and so we cannot
163  * check against negative addresses: they are ok. The return value is weird,
164  * though, in that case (0).
165  *
166  * also note that seeking relative to the "end of file" isn't supported:
167  * it has no meaning, so it returns -EINVAL.
168  */
169 static loff_t memory_lseek(struct file * file, loff_t offset, int orig)
170 {
171         loff_t ret;
172
173         mutex_lock(&file->f_dentry->d_inode->i_mutex);
174         switch (orig) {
175                 case 0:
176                         file->f_pos = offset;
177                         ret = file->f_pos;
178                         force_successful_syscall_return();
179                         break;
180                 case 1:
181                         file->f_pos += offset;
182                         ret = file->f_pos;
183                         force_successful_syscall_return();
184                         break;
185                 default:
186                         ret = -EINVAL;
187         }
188         mutex_unlock(&file->f_dentry->d_inode->i_mutex);
189         return ret;
190 }
191
192 static int open_mem(struct inode * inode, struct file * filp)
193 {
194         return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
195 }
196
197 const struct file_operations mem_fops = {
198         .llseek         = memory_lseek,
199         .read           = read_mem,
200         .write          = write_mem,
201         .mmap           = xen_mmap_mem,
202         .open           = open_mem,
203 };