aboutsummaryrefslogtreecommitdiffstats
path: root/arch/powerpc
diff options
context:
space:
mode:
authorMark Nutter <mnutter@us.ibm.com>2006-03-22 18:00:12 -0500
committerPaul Mackerras <paulus@samba.org>2006-03-26 22:48:28 -0500
commit6df10a82f8de89c66eb91c371d62d76e87b2cbba (patch)
treee8615f4c00a44f4e4a609d59c51e958f0300526b /arch/powerpc
parenta33a7d7309d79656bc19a0e96fc4547a1633283e (diff)
[PATCH] spufs: enable SPE problem state MMIO access.
This patch is layered on top of CONFIG_SPARSEMEM and is patterned after direct mapping of LS. This patch allows mmap() of the following regions: "mfc", which represents the area from [0x3000 - 0x3fff]; "cntl", which represents the area from [0x4000 - 0x4fff]; "signal1" which begins at offset 0x14000; "signal2" which begins at offset 0x1c000. The signal1 & signal2 files may be mmap()'d by regular user processes. The cntl and mfc file, on the other hand, may only be accessed if the owning process has CAP_SYS_RAWIO, because they have the potential to confuse the kernel with regard to parallel access to the same files with regular file operations: the kernel always holds a spinlock when accessing registers in these areas to serialize them, which can not be guaranteed with user mmaps, Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch/powerpc')
-rw-r--r--arch/powerpc/platforms/cell/Kconfig5
-rw-r--r--arch/powerpc/platforms/cell/spu_base.c5
-rw-r--r--arch/powerpc/platforms/cell/spufs/context.c18
-rw-r--r--arch/powerpc/platforms/cell/spufs/file.c229
-rw-r--r--arch/powerpc/platforms/cell/spufs/inode.c2
-rw-r--r--arch/powerpc/platforms/cell/spufs/spufs.h8
6 files changed, 255 insertions, 12 deletions
diff --git a/arch/powerpc/platforms/cell/Kconfig b/arch/powerpc/platforms/cell/Kconfig
index 3157071e241c..c2a3db8edb0c 100644
--- a/arch/powerpc/platforms/cell/Kconfig
+++ b/arch/powerpc/platforms/cell/Kconfig
@@ -10,4 +10,9 @@ config SPU_FS
10 Units on machines implementing the Broadband Processor 10 Units on machines implementing the Broadband Processor
11 Architecture. 11 Architecture.
12 12
13config SPUFS_MMAP
14 bool
15 depends on SPU_FS && SPARSEMEM && !PPC_64K_PAGES
16 default y
17
13endmenu 18endmenu
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c
index 162b6cfa8a43..d152a3fbdb83 100644
--- a/arch/powerpc/platforms/cell/spu_base.c
+++ b/arch/powerpc/platforms/cell/spu_base.c
@@ -570,6 +570,11 @@ static int __init spu_map_device(struct spu *spu, struct device_node *spe)
570 if (!spu->local_store) 570 if (!spu->local_store)
571 goto out; 571 goto out;
572 572
573 prop = get_property(spe, "problem", NULL);
574 if (!prop)
575 goto out_unmap;
576 spu->problem_phys = *(unsigned long *)prop;
577
573 spu->problem= map_spe_prop(spe, "problem"); 578 spu->problem= map_spe_prop(spe, "problem");
574 if (!spu->problem) 579 if (!spu->problem)
575 goto out_unmap; 580 goto out_unmap;
diff --git a/arch/powerpc/platforms/cell/spufs/context.c b/arch/powerpc/platforms/cell/spufs/context.c
index 7e016b9eab21..3f75c1e7adea 100644
--- a/arch/powerpc/platforms/cell/spufs/context.c
+++ b/arch/powerpc/platforms/cell/spufs/context.c
@@ -27,7 +27,7 @@
27#include <asm/spu_csa.h> 27#include <asm/spu_csa.h>
28#include "spufs.h" 28#include "spufs.h"
29 29
30struct spu_context *alloc_spu_context(struct address_space *local_store) 30struct spu_context *alloc_spu_context(void)
31{ 31{
32 struct spu_context *ctx; 32 struct spu_context *ctx;
33 ctx = kmalloc(sizeof *ctx, GFP_KERNEL); 33 ctx = kmalloc(sizeof *ctx, GFP_KERNEL);
@@ -53,7 +53,10 @@ struct spu_context *alloc_spu_context(struct address_space *local_store)
53 ctx->mfc_fasync = NULL; 53 ctx->mfc_fasync = NULL;
54 ctx->tagwait = 0; 54 ctx->tagwait = 0;
55 ctx->state = SPU_STATE_SAVED; 55 ctx->state = SPU_STATE_SAVED;
56 ctx->local_store = local_store; 56 ctx->local_store = NULL;
57 ctx->cntl = NULL;
58 ctx->signal1 = NULL;
59 ctx->signal2 = NULL;
57 ctx->spu = NULL; 60 ctx->spu = NULL;
58 ctx->ops = &spu_backing_ops; 61 ctx->ops = &spu_backing_ops;
59 ctx->owner = get_task_mm(current); 62 ctx->owner = get_task_mm(current);
@@ -110,7 +113,16 @@ void spu_release(struct spu_context *ctx)
110 113
111void spu_unmap_mappings(struct spu_context *ctx) 114void spu_unmap_mappings(struct spu_context *ctx)
112{ 115{
113 unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1); 116 if (ctx->local_store)
117 unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1);
118 if (ctx->mfc)
119 unmap_mapping_range(ctx->mfc, 0, 0x4000, 1);
120 if (ctx->cntl)
121 unmap_mapping_range(ctx->cntl, 0, 0x4000, 1);
122 if (ctx->signal1)
123 unmap_mapping_range(ctx->signal1, 0, 0x4000, 1);
124 if (ctx->signal2)
125 unmap_mapping_range(ctx->signal2, 0, 0x4000, 1);
114} 126}
115 127
116int spu_acquire_runnable(struct spu_context *ctx) 128int spu_acquire_runnable(struct spu_context *ctx)
diff --git a/arch/powerpc/platforms/cell/spufs/file.c b/arch/powerpc/platforms/cell/spufs/file.c
index 62fe9941ccee..366185e92667 100644
--- a/arch/powerpc/platforms/cell/spufs/file.c
+++ b/arch/powerpc/platforms/cell/spufs/file.c
@@ -41,8 +41,10 @@ static int
41spufs_mem_open(struct inode *inode, struct file *file) 41spufs_mem_open(struct inode *inode, struct file *file)
42{ 42{
43 struct spufs_inode_info *i = SPUFS_I(inode); 43 struct spufs_inode_info *i = SPUFS_I(inode);
44 file->private_data = i->i_ctx; 44 struct spu_context *ctx = i->i_ctx;
45 file->f_mapping = i->i_ctx->local_store; 45 file->private_data = ctx;
46 file->f_mapping = inode->i_mapping;
47 ctx->local_store = inode->i_mapping;
46 return 0; 48 return 0;
47} 49}
48 50
@@ -86,7 +88,7 @@ spufs_mem_write(struct file *file, const char __user *buffer,
86 return ret; 88 return ret;
87} 89}
88 90
89#ifdef CONFIG_SPARSEMEM 91#ifdef CONFIG_SPUFS_MMAP
90static struct page * 92static struct page *
91spufs_mem_mmap_nopage(struct vm_area_struct *vma, 93spufs_mem_mmap_nopage(struct vm_area_struct *vma,
92 unsigned long address, int *type) 94 unsigned long address, int *type)
@@ -138,11 +140,113 @@ static struct file_operations spufs_mem_fops = {
138 .read = spufs_mem_read, 140 .read = spufs_mem_read,
139 .write = spufs_mem_write, 141 .write = spufs_mem_write,
140 .llseek = generic_file_llseek, 142 .llseek = generic_file_llseek,
141#ifdef CONFIG_SPARSEMEM 143#ifdef CONFIG_SPUFS_MMAP
142 .mmap = spufs_mem_mmap, 144 .mmap = spufs_mem_mmap,
143#endif 145#endif
144}; 146};
145 147
148#ifdef CONFIG_SPUFS_MMAP
149static struct page *spufs_ps_nopage(struct vm_area_struct *vma,
150 unsigned long address,
151 int *type, unsigned long ps_offs)
152{
153 struct page *page = NOPAGE_SIGBUS;
154 int fault_type = VM_FAULT_SIGBUS;
155 struct spu_context *ctx = vma->vm_file->private_data;
156 unsigned long offset = address - vma->vm_start;
157 unsigned long area;
158 int ret;
159
160 offset += vma->vm_pgoff << PAGE_SHIFT;
161 if (offset >= 0x4000)
162 goto out;
163
164 ret = spu_acquire_runnable(ctx);
165 if (ret)
166 goto out;
167
168 area = ctx->spu->problem_phys + ps_offs;
169 page = pfn_to_page((area + offset) >> PAGE_SHIFT);
170 fault_type = VM_FAULT_MINOR;
171 page_cache_get(page);
172
173 spu_release(ctx);
174
175 out:
176 if (type)
177 *type = fault_type;
178
179 return page;
180}
181
182static struct page *spufs_cntl_mmap_nopage(struct vm_area_struct *vma,
183 unsigned long address, int *type)
184{
185 return spufs_ps_nopage(vma, address, type, 0x4000);
186}
187
188static struct vm_operations_struct spufs_cntl_mmap_vmops = {
189 .nopage = spufs_cntl_mmap_nopage,
190};
191
192/*
193 * mmap support for problem state control area [0x4000 - 0x4fff].
194 * Mapping this area requires that the application have CAP_SYS_RAWIO,
195 * as these registers require special care when read/writing.
196 */
197static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma)
198{
199 if (!(vma->vm_flags & VM_SHARED))
200 return -EINVAL;
201
202 if (!capable(CAP_SYS_RAWIO))
203 return -EPERM;
204
205 vma->vm_flags |= VM_RESERVED;
206 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
207 | _PAGE_NO_CACHE);
208
209 vma->vm_ops = &spufs_cntl_mmap_vmops;
210 return 0;
211}
212#endif
213
214static int spufs_cntl_open(struct inode *inode, struct file *file)
215{
216 struct spufs_inode_info *i = SPUFS_I(inode);
217 struct spu_context *ctx = i->i_ctx;
218
219 file->private_data = ctx;
220 file->f_mapping = inode->i_mapping;
221 ctx->cntl = inode->i_mapping;
222 return 0;
223}
224
225static ssize_t
226spufs_cntl_read(struct file *file, char __user *buffer,
227 size_t size, loff_t *pos)
228{
229 /* FIXME: read from spu status */
230 return -EINVAL;
231}
232
233static ssize_t
234spufs_cntl_write(struct file *file, const char __user *buffer,
235 size_t size, loff_t *pos)
236{
237 /* FIXME: write to runctl bit */
238 return -EINVAL;
239}
240
241static struct file_operations spufs_cntl_fops = {
242 .open = spufs_cntl_open,
243 .read = spufs_cntl_read,
244 .write = spufs_cntl_write,
245#ifdef CONFIG_SPUFS_MMAP
246 .mmap = spufs_cntl_mmap,
247#endif
248};
249
146static int 250static int
147spufs_regs_open(struct inode *inode, struct file *file) 251spufs_regs_open(struct inode *inode, struct file *file)
148{ 252{
@@ -503,6 +607,16 @@ static struct file_operations spufs_wbox_stat_fops = {
503 .read = spufs_wbox_stat_read, 607 .read = spufs_wbox_stat_read,
504}; 608};
505 609
610static int spufs_signal1_open(struct inode *inode, struct file *file)
611{
612 struct spufs_inode_info *i = SPUFS_I(inode);
613 struct spu_context *ctx = i->i_ctx;
614 file->private_data = ctx;
615 file->f_mapping = inode->i_mapping;
616 ctx->signal1 = inode->i_mapping;
617 return nonseekable_open(inode, file);
618}
619
506static ssize_t spufs_signal1_read(struct file *file, char __user *buf, 620static ssize_t spufs_signal1_read(struct file *file, char __user *buf,
507 size_t len, loff_t *pos) 621 size_t len, loff_t *pos)
508{ 622{
@@ -543,12 +657,50 @@ static ssize_t spufs_signal1_write(struct file *file, const char __user *buf,
543 return 4; 657 return 4;
544} 658}
545 659
660#ifdef CONFIG_SPUFS_MMAP
661static struct page *spufs_signal1_mmap_nopage(struct vm_area_struct *vma,
662 unsigned long address, int *type)
663{
664 return spufs_ps_nopage(vma, address, type, 0x14000);
665}
666
667static struct vm_operations_struct spufs_signal1_mmap_vmops = {
668 .nopage = spufs_signal1_mmap_nopage,
669};
670
671static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
672{
673 if (!(vma->vm_flags & VM_SHARED))
674 return -EINVAL;
675
676 vma->vm_flags |= VM_RESERVED;
677 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
678 | _PAGE_NO_CACHE);
679
680 vma->vm_ops = &spufs_signal1_mmap_vmops;
681 return 0;
682}
683#endif
684
546static struct file_operations spufs_signal1_fops = { 685static struct file_operations spufs_signal1_fops = {
547 .open = spufs_pipe_open, 686 .open = spufs_signal1_open,
548 .read = spufs_signal1_read, 687 .read = spufs_signal1_read,
549 .write = spufs_signal1_write, 688 .write = spufs_signal1_write,
689#ifdef CONFIG_SPUFS_MMAP
690 .mmap = spufs_signal1_mmap,
691#endif
550}; 692};
551 693
694static int spufs_signal2_open(struct inode *inode, struct file *file)
695{
696 struct spufs_inode_info *i = SPUFS_I(inode);
697 struct spu_context *ctx = i->i_ctx;
698 file->private_data = ctx;
699 file->f_mapping = inode->i_mapping;
700 ctx->signal2 = inode->i_mapping;
701 return nonseekable_open(inode, file);
702}
703
552static ssize_t spufs_signal2_read(struct file *file, char __user *buf, 704static ssize_t spufs_signal2_read(struct file *file, char __user *buf,
553 size_t len, loff_t *pos) 705 size_t len, loff_t *pos)
554{ 706{
@@ -591,10 +743,39 @@ static ssize_t spufs_signal2_write(struct file *file, const char __user *buf,
591 return 4; 743 return 4;
592} 744}
593 745
746#ifdef CONFIG_SPUFS_MMAP
747static struct page *spufs_signal2_mmap_nopage(struct vm_area_struct *vma,
748 unsigned long address, int *type)
749{
750 return spufs_ps_nopage(vma, address, type, 0x1c000);
751}
752
753static struct vm_operations_struct spufs_signal2_mmap_vmops = {
754 .nopage = spufs_signal2_mmap_nopage,
755};
756
757static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
758{
759 if (!(vma->vm_flags & VM_SHARED))
760 return -EINVAL;
761
762 /* FIXME: */
763 vma->vm_flags |= VM_RESERVED;
764 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
765 | _PAGE_NO_CACHE);
766
767 vma->vm_ops = &spufs_signal2_mmap_vmops;
768 return 0;
769}
770#endif
771
594static struct file_operations spufs_signal2_fops = { 772static struct file_operations spufs_signal2_fops = {
595 .open = spufs_pipe_open, 773 .open = spufs_signal2_open,
596 .read = spufs_signal2_read, 774 .read = spufs_signal2_read,
597 .write = spufs_signal2_write, 775 .write = spufs_signal2_write,
776#ifdef CONFIG_SPUFS_MMAP
777 .mmap = spufs_signal2_mmap,
778#endif
598}; 779};
599 780
600static void spufs_signal1_type_set(void *data, u64 val) 781static void spufs_signal1_type_set(void *data, u64 val)
@@ -643,6 +824,38 @@ static u64 spufs_signal2_type_get(void *data)
643DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get, 824DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get,
644 spufs_signal2_type_set, "%llu"); 825 spufs_signal2_type_set, "%llu");
645 826
827#ifdef CONFIG_SPUFS_MMAP
828static struct page *spufs_mfc_mmap_nopage(struct vm_area_struct *vma,
829 unsigned long address, int *type)
830{
831 return spufs_ps_nopage(vma, address, type, 0x3000);
832}
833
834static struct vm_operations_struct spufs_mfc_mmap_vmops = {
835 .nopage = spufs_mfc_mmap_nopage,
836};
837
838/*
839 * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
840 * Mapping this area requires that the application have CAP_SYS_RAWIO,
841 * as these registers require special care when read/writing.
842 */
843static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma)
844{
845 if (!(vma->vm_flags & VM_SHARED))
846 return -EINVAL;
847
848 if (!capable(CAP_SYS_RAWIO))
849 return -EPERM;
850
851 vma->vm_flags |= VM_RESERVED;
852 vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
853 | _PAGE_NO_CACHE);
854
855 vma->vm_ops = &spufs_mfc_mmap_vmops;
856 return 0;
857}
858#endif
646 859
647static int spufs_mfc_open(struct inode *inode, struct file *file) 860static int spufs_mfc_open(struct inode *inode, struct file *file)
648{ 861{
@@ -932,6 +1145,9 @@ static struct file_operations spufs_mfc_fops = {
932 .flush = spufs_mfc_flush, 1145 .flush = spufs_mfc_flush,
933 .fsync = spufs_mfc_fsync, 1146 .fsync = spufs_mfc_fsync,
934 .fasync = spufs_mfc_fasync, 1147 .fasync = spufs_mfc_fasync,
1148#ifdef CONFIG_SPUFS_MMAP
1149 .mmap = spufs_mfc_mmap,
1150#endif
935}; 1151};
936 1152
937static void spufs_npc_set(void *data, u64 val) 1153static void spufs_npc_set(void *data, u64 val)
@@ -1077,6 +1293,7 @@ struct tree_descr spufs_dir_contents[] = {
1077 { "signal1_type", &spufs_signal1_type, 0666, }, 1293 { "signal1_type", &spufs_signal1_type, 0666, },
1078 { "signal2_type", &spufs_signal2_type, 0666, }, 1294 { "signal2_type", &spufs_signal2_type, 0666, },
1079 { "mfc", &spufs_mfc_fops, 0666, }, 1295 { "mfc", &spufs_mfc_fops, 0666, },
1296 { "cntl", &spufs_cntl_fops, 0666, },
1080 { "npc", &spufs_npc_ops, 0666, }, 1297 { "npc", &spufs_npc_ops, 0666, },
1081 { "fpcr", &spufs_fpcr_fops, 0666, }, 1298 { "fpcr", &spufs_fpcr_fops, 0666, },
1082 { "decr", &spufs_decr_ops, 0666, }, 1299 { "decr", &spufs_decr_ops, 0666, },
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c
index b3962c3a0348..dc06305eecf5 100644
--- a/arch/powerpc/platforms/cell/spufs/inode.c
+++ b/arch/powerpc/platforms/cell/spufs/inode.c
@@ -241,7 +241,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
241 inode->i_gid = dir->i_gid; 241 inode->i_gid = dir->i_gid;
242 inode->i_mode &= S_ISGID; 242 inode->i_mode &= S_ISGID;
243 } 243 }
244 ctx = alloc_spu_context(inode->i_mapping); 244 ctx = alloc_spu_context();
245 SPUFS_I(inode)->i_ctx = ctx; 245 SPUFS_I(inode)->i_ctx = ctx;
246 if (!ctx) 246 if (!ctx)
247 goto out_iput; 247 goto out_iput;
diff --git a/arch/powerpc/platforms/cell/spufs/spufs.h b/arch/powerpc/platforms/cell/spufs/spufs.h
index 57d687ca3f03..4485738e2102 100644
--- a/arch/powerpc/platforms/cell/spufs/spufs.h
+++ b/arch/powerpc/platforms/cell/spufs/spufs.h
@@ -43,7 +43,11 @@ struct spu_context {
43 struct spu *spu; /* pointer to a physical SPU */ 43 struct spu *spu; /* pointer to a physical SPU */
44 struct spu_state csa; /* SPU context save area. */ 44 struct spu_state csa; /* SPU context save area. */
45 spinlock_t mmio_lock; /* protects mmio access */ 45 spinlock_t mmio_lock; /* protects mmio access */
46 struct address_space *local_store;/* local store backing store */ 46 struct address_space *local_store; /* local store mapping. */
47 struct address_space *mfc; /* 'mfc' area mappings. */
48 struct address_space *cntl; /* 'control' area mappings. */
49 struct address_space *signal1; /* 'signal1' area mappings. */
50 struct address_space *signal2; /* 'signal2' area mappings. */
47 51
48 enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state; 52 enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state;
49 struct rw_semaphore state_sema; 53 struct rw_semaphore state_sema;
@@ -125,7 +129,7 @@ long spufs_create_thread(struct nameidata *nd,
125extern struct file_operations spufs_context_fops; 129extern struct file_operations spufs_context_fops;
126 130
127/* context management */ 131/* context management */
128struct spu_context * alloc_spu_context(struct address_space *local_store); 132struct spu_context * alloc_spu_context(void);
129void destroy_spu_context(struct kref *kref); 133void destroy_spu_context(struct kref *kref);
130struct spu_context * get_spu_context(struct spu_context *ctx); 134struct spu_context * get_spu_context(struct spu_context *ctx);
131int put_spu_context(struct spu_context *ctx); 135int put_spu_context(struct spu_context *ctx);