initial import
[ps3linux_kernel_patches_314.git] / 0230-spuldrfs.patch
blobe32b33c8493d9d604a7269fa7f28b884ddcccd4a
1 --- a/arch/powerpc/platforms/ps3/Kconfig 2012-08-16 21:28:29.947236099 +0200
2 +++ b/arch/powerpc/platforms/ps3/Kconfig 2012-08-16 21:30:29.967243082 +0200
3 @@ -209,6 +209,13 @@
4 help
5 The isolated SPU file system is used to execute isolated SPU modules.
7 +config SPULDR_FS
8 + tristate "PS3 isolated SPU loader file system"
9 + default m
10 + depends on PPC_PS3
11 + help
12 + The isolated SPU loader file system is used to execute isolated SPU loaders.
14 config PS3GELIC_UDBG
15 bool "PS3 udbg output via UDP broadcasts on Ethernet"
16 depends on PPC_PS3
17 --- a/arch/powerpc/platforms/ps3/Makefile 2012-08-16 21:28:29.947236099 +0200
18 +++ b/arch/powerpc/platforms/ps3/Makefile 2012-08-16 21:30:54.793911193 +0200
19 @@ -8,3 +8,4 @@
20 obj-y += device-init.o
22 obj-$(CONFIG_SPUISO_FS) += spuisofs.o
23 +obj-$(CONFIG_SPULDR_FS) += spuldrfs.o
24 --- /dev/null 2013-10-07 20:20:12.741358433 +0200
25 +++ b/arch/powerpc/platforms/ps3/spuldrfs.c 2013-10-07 22:16:03.015096113 +0200
26 @@ -0,0 +1,1083 @@
28 +/*
29 + * PS3 spuldrfs
30 + *
31 + * Copyright (C) 2012 glevand <geoffrey.levand@mail.ru>
32 + * All rights reserved.
33 + *
34 + * This program is free software; you can redistribute it and/or modify it
35 + * under the terms of the GNU General Public License as published
36 + * by the Free Software Foundation; version 2 of the License.
37 + *
38 + * This program is distributed in the hope that it will be useful, but
39 + * WITHOUT ANY WARRANTY; without even the implied warranty of
40 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
41 + * General Public License for more details.
42 + *
43 + * You should have received a copy of the GNU General Public License along
44 + * with this program; if not, write to the Free Software Foundation, Inc.,
45 + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
46 + */
48 +#include <linux/module.h>
49 +#include <linux/kernel.h>
50 +#include <linux/version.h>
51 +#include <linux/init.h>
52 +#include <linux/fs.h>
53 +#include <linux/fsnotify.h>
54 +#include <linux/file.h>
55 +#include <linux/slab.h>
56 +#include <linux/vmalloc.h>
57 +#include <linux/pagemap.h>
58 +#include <linux/io.h>
59 +#include <linux/interrupt.h>
61 +#include <asm/uaccess.h>
62 +#include <asm/ps3.h>
63 +#include <asm/spu.h>
64 +#include <asm/spu_priv1.h>
65 +#include <asm/lv1call.h>
67 +#define SPULDRFS_MAGIC 0x7370756c
69 +struct spe_shadow {
70 + u8 padding_0140[0x0140];
71 + u64 int_status_class0_RW; /* 0x0140 */
72 + u64 int_status_class1_RW; /* 0x0148 */
73 + u64 int_status_class2_RW; /* 0x0150 */
74 + u8 padding_0158[0x0610-0x0158];
75 + u64 mfc_dsisr_RW; /* 0x0610 */
76 + u8 padding_0618[0x0620-0x0618];
77 + u64 mfc_dar_RW; /* 0x0620 */
78 + u8 padding_0628[0x0800-0x0628];
79 + u64 mfc_dsipr_R; /* 0x0800 */
80 + u8 padding_0808[0x0810-0x0808];
81 + u64 mfc_lscrr_R; /* 0x0810 */
82 + u8 padding_0818[0x0c00-0x0818];
83 + u64 mfc_cer_R; /* 0x0c00 */
84 + u8 padding_0c08[0x0f00-0x0c08];
85 + u64 spe_execution_status; /* 0x0f00 */
86 + u8 padding_0f08[0x1000-0x0f08];
87 +};
89 +struct spuldrfs_inode_info {
90 + struct inode vfs_inode;
91 + unsigned long io_addr;
92 + void *virt_addr;
93 +};
95 +struct spuldrfs_tree_descr {
96 + const char *name;
97 + const struct file_operations *ops;
98 + umode_t mode;
99 + size_t size;
100 + unsigned long io_addr;
101 + void *virt_addr;
104 +#define SPULDRFS_I(inode) container_of(inode, struct spuldrfs_inode_info, vfs_inode)
106 +static struct kmem_cache *spuldrfs_inode_cache;
108 +static u64 spuldrfs_spe_priv2_addr;
109 +static u64 spuldrfs_spe_problem_addr;
110 +static u64 spuldrfs_spe_ls_addr;
111 +static u64 spuldrfs_spe_shadow_addr;
113 +static struct spu_priv2 *spuldrfs_spe_priv2;
114 +static struct spu_problem *spuldrfs_spe_problem;
115 +static void *spuldrfs_spe_ls;
116 +static struct spe_shadow *spuldrfs_spe_shadow;
117 +static u64 spuldrfs_spe_id;
118 +static unsigned int spuldrfs_spe_virq[4];
120 +static void *spuldrfs_spe_metldr;
121 +static void *spuldrfs_spe_ldr;
122 +static void *spuldrfs_spe_buf1;
123 +static void *spuldrfs_spe_buf2;
124 +static void *spuldrfs_spe_buf3;
126 +static unsigned int spuldrfs_spe_slb_index;
128 +static unsigned long spuldrfs_spe_metldr_size = 1024 * 1024;
129 +module_param(spuldrfs_spe_metldr_size, ulong, 0);
131 +static unsigned long spuldrfs_spe_ldr_size = 1024 * 1024;
132 +module_param(spuldrfs_spe_ldr_size, ulong, 0);
134 +static unsigned long spuldrfs_spe_buf1_size = 1024 * 1024;
135 +module_param(spuldrfs_spe_buf1_size, ulong, 0);
137 +static unsigned long spuldrfs_spe_buf2_size = 1024 * 1024;
138 +module_param(spuldrfs_spe_buf2_size, ulong, 0);
140 +static unsigned long spuldrfs_spe_buf3_size = 1024 * 1024;
141 +module_param(spuldrfs_spe_buf3_size, ulong, 0);
143 +static unsigned long spuldrfs_spe_trans_notify_mask = 0x7;
144 +module_param(spuldrfs_spe_trans_notify_mask, ulong, 0);
146 +static unsigned long spuldrfs_spe_resource_id = 6;
147 +module_param(spuldrfs_spe_resource_id, ulong, 0);
149 +static int spuldrfs_spe_buf_addr_32bit = 0;
150 +module_param(spuldrfs_spe_buf_addr_32bit, int, 0);
153 + * spuldrfs_spe_regs_read
154 + */
155 +static ssize_t spuldrfs_spe_regs_read(struct file *file, char __user *buffer,
156 + size_t size, loff_t *pos)
158 + struct inode *inode = file->f_dentry->d_inode;
159 + struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
161 + if (*pos >= inode->i_size)
162 + return (0);
164 + return simple_read_from_buffer(buffer, size, pos,
165 + si->virt_addr, inode->i_size);
169 + * spuldrfs_spe_regs_write
170 + */
171 +static ssize_t spuldrfs_spe_regs_write(struct file *file, const char __user *buffer,
172 + size_t size, loff_t *pos)
174 + struct inode *inode = file->f_dentry->d_inode;
175 + struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
177 + if (*pos >= inode->i_size)
178 + return (-EFBIG);
180 + return simple_write_to_buffer(si->virt_addr, inode->i_size,
181 + pos, buffer, size);
185 + * spuldrfs_spe_regs_mmap
186 + */
187 +static int spuldrfs_spe_regs_mmap(struct file *file, struct vm_area_struct *vma)
189 + struct inode *inode = file->f_dentry->d_inode;
190 + struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
191 + unsigned long size, pfn;
193 + size = vma->vm_end - vma->vm_start;
194 + pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff;
196 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
197 + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
198 +#else
199 + vma->vm_flags |= VM_RESERVED | VM_IO;
200 +#endif
201 + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
203 + return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
206 +static const struct file_operations spuldrfs_spe_regs_fops = {
207 + .read = spuldrfs_spe_regs_read,
208 + .write = spuldrfs_spe_regs_write,
209 + .mmap = spuldrfs_spe_regs_mmap,
213 + * spuldrfs_spe_mem_read
214 + */
215 +static ssize_t spuldrfs_spe_mem_read(struct file *file, char __user *buffer,
216 + size_t size, loff_t *pos)
218 + struct inode *inode = file->f_dentry->d_inode;
219 + struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
221 + if (*pos >= inode->i_size)
222 + return (0);
224 + return simple_read_from_buffer(buffer, size, pos,
225 + si->virt_addr, inode->i_size);
229 + * spuldrfs_spe_mem_write
230 + */
231 +static ssize_t spuldrfs_spe_mem_write(struct file *file, const char __user *buffer,
232 + size_t size, loff_t *pos)
234 + struct inode *inode = file->f_dentry->d_inode;
235 + struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
237 + if (*pos >= inode->i_size)
238 + return (-EFBIG);
240 + return simple_write_to_buffer(si->virt_addr, inode->i_size,
241 + pos, buffer, size);
245 + * spuldrfs_spe_mem_mmap
246 + */
247 +static int spuldrfs_spe_mem_mmap(struct file *file, struct vm_area_struct *vma)
249 + struct inode *inode = file->f_dentry->d_inode;
250 + struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
251 + unsigned long size, pfn;
253 + size = vma->vm_end - vma->vm_start;
254 + pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff;
256 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
257 + vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
258 +#else
259 + vma->vm_flags |= VM_RESERVED | VM_IO;
260 +#endif
261 + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
263 + return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
266 +static const struct file_operations spuldrfs_spe_mem_fops = {
267 + .read = spuldrfs_spe_mem_read,
268 + .write = spuldrfs_spe_mem_write,
269 + .mmap = spuldrfs_spe_mem_mmap,
273 + * spuldrfs_mem_read
274 + */
275 +static ssize_t spuldrfs_mem_read(struct file *file, char __user *buffer,
276 + size_t size, loff_t *pos)
278 + struct inode *inode = file->f_dentry->d_inode;
279 + struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
281 + if (*pos >= inode->i_size)
282 + return (0);
284 + return simple_read_from_buffer(buffer, size, pos,
285 + si->virt_addr, inode->i_size);
289 + * spuldrfs_mem_write
290 + */
291 +static ssize_t spuldrfs_mem_write(struct file *file, const char __user *buffer,
292 + size_t size, loff_t *pos)
294 + struct inode *inode = file->f_dentry->d_inode;
295 + struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
297 + if (*pos >= inode->i_size)
298 + return (-EFBIG);
300 + return simple_write_to_buffer(si->virt_addr, inode->i_size,
301 + pos, buffer, size);
305 + * spuldrfs_mem_mmap
306 + */
307 +static int spuldrfs_mem_mmap(struct file *file, struct vm_area_struct *vma)
309 + struct inode *inode = file->f_dentry->d_inode;
310 + struct spuldrfs_inode_info *si = SPULDRFS_I(inode);
312 + return remap_vmalloc_range(vma, si->virt_addr, 0);
315 +static const struct file_operations spuldrfs_mem_fops = {
316 + .read = spuldrfs_mem_read,
317 + .write = spuldrfs_mem_write,
318 + .mmap = spuldrfs_mem_mmap,
322 + * spuldrfs_info_read
323 + */
324 +static ssize_t spuldrfs_info_read(struct file *file, char __user *buffer,
325 + size_t size, loff_t *pos)
327 + char buf[256];
328 + size_t len;
329 + unsigned long spe_buf1_addr, spe_buf2_addr, spe_buf3_addr;
331 + spe_buf1_addr = (unsigned long) spuldrfs_spe_buf1;
332 + spe_buf2_addr = (unsigned long) spuldrfs_spe_buf2;
333 + spe_buf3_addr = (unsigned long) spuldrfs_spe_buf3;
335 + if (spuldrfs_spe_buf_addr_32bit) {
336 + spe_buf1_addr &= 0xfffffffful;
337 + spe_buf2_addr &= 0xfffffffful;
338 + spe_buf3_addr &= 0xfffffffful;
341 + len = sprintf(buf, "buf1 %lx\nbuf2 %lx\nbuf3 %lx",
342 + spe_buf1_addr, spe_buf2_addr, spe_buf3_addr);
344 + return simple_read_from_buffer(buffer, size, pos, buf, len);
347 +static const struct file_operations spuldrfs_info_fops = {
348 + .read = spuldrfs_info_read,
352 + * spuldrfs_run_write
353 + */
354 +static ssize_t spuldrfs_run_write(struct file *file, const char __user *buffer,
355 + size_t size, loff_t *pos)
357 + int i, err;
359 + if (*pos)
360 + return (-EINVAL);
362 + err = lv1_disable_logical_spe(spuldrfs_spe_id, 0);
363 + if (err)
364 + printk(KERN_INFO"spuldrfs: lv1_disable_logical_spe failed with %d\n", err);
366 + err = lv1_enable_logical_spe(spuldrfs_spe_id, spuldrfs_spe_resource_id);
367 + if (err) {
368 + printk(KERN_INFO"spuldrfs: lv1_enable_logical_spe failed with %d\n", err);
369 + return (-ENXIO);
372 + out_be32(&spuldrfs_spe_problem->spu_runcntl_RW, SPU_RUNCNTL_ISOLATE | SPU_RUNCNTL_STOP);
374 + /* enable interrupts */
376 + err = lv1_set_spe_interrupt_mask(spuldrfs_spe_id, 0, 0x7);
377 + if (err) {
378 + printk(KERN_INFO"spuldrfs: lv1_set_spe_interrupt_mask failed with %d\n", err);
379 + return (-ENXIO);
382 + err = lv1_set_spe_interrupt_mask(spuldrfs_spe_id, 1, 0xf);
383 + if (err) {
384 + printk(KERN_INFO"spuldrfs: lv1_set_spe_interrupt_mask failed with %d\n", err);
385 + return (-ENXIO);
388 + err = lv1_set_spe_interrupt_mask(spuldrfs_spe_id, 2, 0xf);
389 + if (err) {
390 + printk(KERN_INFO"spuldrfs: lv1_set_spe_interrupt_mask failed with %d\n", err);
391 + return (-ENXIO);
394 + err = lv1_set_spe_privilege_state_area_1_register(spuldrfs_spe_id, offsetof(struct spu_priv1, mfc_sr1_RW),
395 + MFC_STATE1_RELOCATE_MASK);
396 + if (err) {
397 + printk(KERN_INFO"spuldrfs: lv1_set_spe_privilege_state_area_1_register failed with %d\n", err);
398 + return (-ENXIO);
401 + /* invalidate all SLB entries */
403 + out_be64(&spuldrfs_spe_priv2->slb_invalidate_all_W, 0);
405 + for (i = 0; i <= SLB_INDEX_MASK; i++) {
406 + out_be64(&spuldrfs_spe_priv2->slb_index_W, i);
407 + out_be64(&spuldrfs_spe_priv2->slb_vsid_RW, 0);
408 + out_be64(&spuldrfs_spe_priv2->slb_esid_RW, 0);
411 + out_be64(&spuldrfs_spe_priv2->spu_cfg_RW, 0);
413 + out_be32(&spuldrfs_spe_problem->spu_mb_W, (unsigned long) spuldrfs_spe_ldr >> 32);
414 + out_be32(&spuldrfs_spe_problem->spu_mb_W, (unsigned long) spuldrfs_spe_ldr);
416 + out_be32(&spuldrfs_spe_problem->signal_notify1, (unsigned long) spuldrfs_spe_metldr >> 32);
417 + out_be32(&spuldrfs_spe_problem->signal_notify2, (unsigned long) spuldrfs_spe_metldr);
419 + out_be64(&spuldrfs_spe_priv2->spu_privcntl_RW, SPU_PRIVCNT_LOAD_REQUEST_ENABLE_MASK);
421 + out_be32(&spuldrfs_spe_problem->spu_runcntl_RW, SPU_RUNCNTL_ISOLATE | SPU_RUNCNTL_RUNNABLE);
423 + return (size);
426 +static const struct file_operations spuldrfs_run_fops = {
427 + .write = spuldrfs_run_write,
431 + * spuldrfs_alloc_inode
432 + */
433 +static struct inode *spuldrfs_alloc_inode(struct super_block *sb)
435 + struct spuldrfs_inode_info *si;
437 + si = kmem_cache_alloc(spuldrfs_inode_cache, GFP_KERNEL);
438 + if (!si)
439 + return (NULL);
441 + return (&si->vfs_inode);
445 + * spuldrfs_i_callback
446 + */
447 +static void spuldrfs_i_callback(struct rcu_head *head)
449 + struct inode *inode = container_of(head, struct inode, i_rcu);
451 + kmem_cache_free(spuldrfs_inode_cache, SPULDRFS_I(inode));
455 + * spuldrfs_destroy_inode
456 + */
457 +static void spuldrfs_destroy_inode(struct inode *inode)
459 + call_rcu(&inode->i_rcu, spuldrfs_i_callback);
463 + * spuldrfs_init_once
464 + */
465 +static void spuldrfs_init_once(void *p)
467 + struct spuldrfs_inode_info *si = p;
469 + inode_init_once(&si->vfs_inode);
473 + * spuldrfs_new_inode
474 + */
475 +static struct inode *spuldrfs_new_inode(struct super_block *sb, umode_t mode)
477 + struct inode *inode;
479 + inode = new_inode(sb);
480 + if (!inode)
481 + return (NULL);
483 + inode->i_ino = get_next_ino();
484 + inode->i_mode = mode;
485 + inode->i_uid = current_fsuid();
486 + inode->i_gid = current_fsgid();
487 + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
489 + return (inode);
493 + * spuldrfs_setattr
494 + */
495 +static int spuldrfs_setattr(struct dentry *dentry, struct iattr *attr)
497 + struct inode *inode = dentry->d_inode;
499 + setattr_copy(inode, attr);
500 + mark_inode_dirty(inode);
502 + return (0);
505 +static const struct inode_operations spuldrfs_inode_ops = {
506 + .setattr = spuldrfs_setattr,
510 + * spuldrfs_new_file
511 + */
512 +static int spuldrfs_new_file(struct super_block *sb, struct dentry *dentry,
513 + const struct file_operations *fops, umode_t mode, size_t size,
514 + unsigned long io_addr, void *virt_addr)
516 + struct inode *inode;
517 + struct spuldrfs_inode_info *si;
519 + inode = spuldrfs_new_inode(sb, S_IFREG | mode);
520 + if (!inode)
521 + return (-ENOMEM);
523 + inode->i_op = &spuldrfs_inode_ops;
524 + inode->i_fop = fops;
525 + inode->i_size = size;
526 + inode->i_private = NULL;
528 + si = SPULDRFS_I(inode);
529 + si->io_addr = io_addr;
530 + si->virt_addr = virt_addr;
532 + d_add(dentry, inode);
534 + return (0);
538 + * spuldrfs_fill_dir
539 + */
540 +static int spuldrfs_fill_dir(struct dentry *dir,
541 + const struct spuldrfs_tree_descr *files)
543 + struct dentry *dentry, *tmp;
544 + int err;
546 + while (files->name && files->name[0]) {
547 + dentry = d_alloc_name(dir, files->name);
548 + if (!dentry) {
549 + err = -ENOMEM;
550 + goto fail;
553 + err = spuldrfs_new_file(dir->d_sb, dentry, files->ops,
554 + files->mode, files->size, files->io_addr, files->virt_addr);
555 + if (err)
556 + goto fail;
558 + files++;
561 + return (0);
563 +fail:
565 + list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child)
566 + dput(dentry);
568 + shrink_dcache_parent(dir);
570 + return (err);
573 +static const struct super_operations spuldrfs_super_ops = {
574 + .alloc_inode = spuldrfs_alloc_inode,
575 + .destroy_inode = spuldrfs_destroy_inode,
576 + .statfs = simple_statfs,
580 + * spuldrfs_fill_super
581 + */
582 +static int spuldrfs_fill_super(struct super_block *sb, void *data, int silent)
584 + const struct spuldrfs_tree_descr root_dir_contents[] = {
585 + { "priv2", &spuldrfs_spe_regs_fops, 0666, sizeof(struct spu_priv2), spuldrfs_spe_priv2_addr, spuldrfs_spe_priv2, },
586 + { "problem", &spuldrfs_spe_regs_fops, 0666, sizeof(struct spu_problem), spuldrfs_spe_problem_addr, spuldrfs_spe_problem, },
587 + { "ls", &spuldrfs_spe_mem_fops, 0666, LS_SIZE, spuldrfs_spe_ls_addr, spuldrfs_spe_ls, },
588 + { "shadow", &spuldrfs_spe_mem_fops, 0444, sizeof(struct spe_shadow), spuldrfs_spe_shadow_addr, spuldrfs_spe_shadow, },
589 + { "metldr", &spuldrfs_mem_fops, 0666, spuldrfs_spe_metldr_size, 0, spuldrfs_spe_metldr, },
590 + { "ldr", &spuldrfs_mem_fops, 0666, spuldrfs_spe_ldr_size, 0, spuldrfs_spe_ldr, },
591 + { "buf1", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf1_size, 0, spuldrfs_spe_buf1, },
592 + { "buf2", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf2_size, 0, spuldrfs_spe_buf2, },
593 + { "buf3", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf3_size, 0, spuldrfs_spe_buf3, },
594 + { "info", &spuldrfs_info_fops, 0444, 0, 0, NULL, },
595 + { "run", &spuldrfs_run_fops, 0222, 0, 0, NULL, },
596 + { },
597 + };
598 + struct inode *root_inode;
599 + int err;
601 + sb->s_maxbytes = MAX_LFS_FILESIZE;
602 + sb->s_blocksize = PAGE_CACHE_SIZE;
603 + sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
604 + sb->s_magic = SPULDRFS_MAGIC;
605 + sb->s_op = &spuldrfs_super_ops;
606 + sb->s_time_gran = 1;
608 + root_inode = spuldrfs_new_inode(sb, S_IFDIR | 0755);
609 + if (!root_inode)
610 + return (-ENOMEM);
612 + root_inode->i_op = &simple_dir_inode_operations;
613 + root_inode->i_fop = &simple_dir_operations;
615 + /* directory inodes start off with i_nlink == 2 (for "." entry) */
616 + inc_nlink(root_inode);
618 + sb->s_root = d_make_root(root_inode);
619 + if (!sb->s_root)
620 + return (-ENOMEM);
622 + err = spuldrfs_fill_dir(sb->s_root, root_dir_contents);
623 + if (err)
624 + return (err);
626 + return (0);
630 + * spuldrfs_spe_ea_to_kernel_ea
631 + */
632 +static unsigned long spuldrfs_spe_ea_to_kernel_ea(unsigned long spe_ea)
634 + unsigned long kernel_ea, spe_buf1_addr, spe_buf2_addr, spe_buf3_addr;
636 + kernel_ea = spe_ea;
638 + if (!spuldrfs_spe_buf_addr_32bit)
639 + return (kernel_ea);
641 + spe_buf1_addr = (unsigned long) spuldrfs_spe_buf1 & 0xfffffffful;
642 + spe_buf2_addr = (unsigned long) spuldrfs_spe_buf2 & 0xfffffffful;
643 + spe_buf3_addr = (unsigned long) spuldrfs_spe_buf3 & 0xfffffffful;
645 + if ((spe_ea >= spe_buf1_addr) && (spe_ea < (spe_buf1_addr + spuldrfs_spe_buf1_size)))
646 + kernel_ea = (unsigned long) spuldrfs_spe_buf1 + (spe_buf1_addr - spe_ea);
647 + else if ((spe_ea >= spe_buf2_addr) && (spe_ea < (spe_buf2_addr + spuldrfs_spe_buf2_size)))
648 + kernel_ea = (unsigned long) spuldrfs_spe_buf2 + (spe_buf2_addr - spe_ea);
649 + else if ((spe_ea >= spe_buf3_addr) && (spe_ea < (spe_buf3_addr + spuldrfs_spe_buf3_size)))
650 + kernel_ea = (unsigned long) spuldrfs_spe_buf3 + (spe_buf3_addr - spe_ea);
652 + return (kernel_ea);
656 + * spuldrfs_spe_interrupt
657 + */
658 +static irqreturn_t spuldrfs_spe_interrupt(int irq, void *data)
660 + u64 status;
661 + u64 ea, kernel_ea, dsisr, esid, vsid;
662 + u64 puint_mb_R;
663 + u32 spu_status_R;
664 + u64 spe_execution_status;
665 + int err;
667 + if (irq == spuldrfs_spe_virq[0]) {
668 + printk(KERN_INFO"spuldrfs: got class 0 irq\n");
670 + err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 0, &status);
671 + if (err) {
672 + printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err);
673 + goto out;
676 + printk(KERN_INFO"spuldrfs: status %llx\n", status);
678 + err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 0, status, 0);
679 + if (err) {
680 + printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err);
681 + goto out;
683 + } else if (irq == spuldrfs_spe_virq[1]) {
684 + printk(KERN_INFO"spuldrfs: got class 1 irq\n");
686 + err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 1, &status);
687 + if (err) {
688 + printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err);
689 + goto out;
692 + printk(KERN_INFO"spuldrfs: status %llx\n", status);
694 + if (status & CLASS1_SEGMENT_FAULT_INTR) {
695 + ea = in_be64(&spuldrfs_spe_shadow->mfc_dar_RW);
696 + kernel_ea = spuldrfs_spe_ea_to_kernel_ea(ea);
698 + esid = (ea & ESID_MASK) | SLB_ESID_V;
699 + vsid = (get_kernel_vsid(kernel_ea, MMU_SEGSIZE_256M) << SLB_VSID_SHIFT) | SLB_VSID_KERNEL | MMU_PAGE_4K;
701 + printk(KERN_INFO"spuldrfs: data segment fault at %llx (%llx)\n", ea, kernel_ea);
703 + out_be64(&spuldrfs_spe_priv2->slb_index_W, spuldrfs_spe_slb_index);
704 + out_be64(&spuldrfs_spe_priv2->slb_vsid_RW, vsid);
705 + out_be64(&spuldrfs_spe_priv2->slb_esid_RW, esid);
707 + spuldrfs_spe_slb_index++;
708 + if (spuldrfs_spe_slb_index > SLB_INDEX_MASK)
709 + spuldrfs_spe_slb_index = 0;
712 + if (status & CLASS1_STORAGE_FAULT_INTR) {
713 + ea = in_be64(&spuldrfs_spe_shadow->mfc_dar_RW);
714 + kernel_ea = spuldrfs_spe_ea_to_kernel_ea(ea);
715 + dsisr = in_be64(&spuldrfs_spe_shadow->mfc_dsisr_RW);
717 + printk(KERN_INFO"spuldrfs: data storage fault at %llx (%llx)\n", ea, kernel_ea);
719 + if (dsisr & MFC_DSISR_PTE_NOT_FOUND) {
720 + err = hash_page(kernel_ea, _PAGE_PRESENT, 0x300);
721 + if (err) {
722 + printk(KERN_INFO"spuldrfs: hash_page failed with %d\n", err);
723 + goto out;
728 + err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 1, status, 0);
729 + if (err) {
730 + printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err);
731 + goto out;
734 + /* restart DMA */
736 + out_be64(&spuldrfs_spe_priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND);
737 + } else if (irq == spuldrfs_spe_virq[2]) {
738 + printk(KERN_INFO"spuldrfs: got class 2 irq\n");
740 + err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 2, &status);
741 + if (err) {
742 + printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err);
743 + goto out;
746 + printk(KERN_INFO"spuldrfs: status %llx\n", status);
748 + if (status & CLASS2_MAILBOX_INTR) {
749 + puint_mb_R = in_be64(&spuldrfs_spe_priv2->puint_mb_R);
751 + printk(KERN_INFO"spuldrfs: puint_mb_R %llx\n", puint_mb_R);
754 + if (status & CLASS2_SPU_STOP_INTR) {
755 + spu_status_R = in_be32(&spuldrfs_spe_problem->spu_status_R);
757 + printk(KERN_INFO"spuldrfs: spu_status_R %x\n", spu_status_R);
760 + err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 2, status, 0);
761 + if (err) {
762 + printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err);
763 + goto out;
765 + } else if (irq == spuldrfs_spe_virq[3]) {
766 + spe_execution_status = spuldrfs_spe_shadow->spe_execution_status;
768 + printk(KERN_INFO"spuldrfs: transition notification: shadow spe_execution_status %llx\n",
769 + spe_execution_status);
770 + } else {
771 + printk(KERN_INFO"spuldrfs: got unknown irq %d\n", irq);
774 +out:
776 + return (IRQ_HANDLED);
780 + * spuldrfs_create_spe
781 + */
782 +static int spuldrfs_create_spe(void)
784 + u64 vas_id, junk;
785 + int err;
787 + err = lv1_get_virtual_address_space_id_of_ppe(&vas_id);
788 + if (err)
789 + return (-ENXIO);
791 + printk(KERN_INFO"spuldrfs: vas id %llu\n", vas_id);
793 + err = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT,
794 + PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, vas_id, 0,
795 + &spuldrfs_spe_priv2_addr, &spuldrfs_spe_problem_addr, &spuldrfs_spe_ls_addr,
796 + &junk, &spuldrfs_spe_shadow_addr, &spuldrfs_spe_id);
797 + if (err)
798 + return (-ENXIO);
800 + printk(KERN_INFO"spuldrfs: spe id %llu\n", spuldrfs_spe_id);
802 + spuldrfs_spe_priv2 = ioremap(spuldrfs_spe_priv2_addr, sizeof(struct spu_priv2));
803 + if (!spuldrfs_spe_priv2) {
804 + err = -ENOMEM;
805 + goto fail_destruct_spe;
808 + spuldrfs_spe_problem = ioremap(spuldrfs_spe_problem_addr, sizeof(struct spu_problem));
809 + if (!spuldrfs_spe_problem) {
810 + err = -ENOMEM;
811 + goto fail_unmap_priv2;
814 + spuldrfs_spe_ls = ioremap_prot(spuldrfs_spe_ls_addr, LS_SIZE, _PAGE_NO_CACHE);
815 + if (!spuldrfs_spe_ls) {
816 + err = -ENOMEM;
817 + goto fail_unmap_problem;
820 + spuldrfs_spe_shadow = __ioremap(spuldrfs_spe_shadow_addr, sizeof(struct spe_shadow),
821 + _PAGE_NO_CACHE | 3);
822 + if (!spuldrfs_spe_shadow) {
823 + err = -ENOMEM;
824 + goto fail_unmap_ls;
827 + err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 0, &spuldrfs_spe_virq[0]);
828 + if (err) {
829 + err = -ENXIO;
830 + goto fail_unmap_shadow;
833 + err = request_irq(spuldrfs_spe_virq[0], spuldrfs_spe_interrupt, 0,
834 + "spuldrfs_spe_irq0", &spuldrfs_spe_virq[0]);
835 + if (err)
836 + goto fail_destroy_spe_irq_0;
838 + err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 1, &spuldrfs_spe_virq[1]);
839 + if (err) {
840 + err = -ENXIO;
841 + goto fail_free_spe_irq_0;
844 + err = request_irq(spuldrfs_spe_virq[1], spuldrfs_spe_interrupt, 0,
845 + "spuldrfs_spe_irq1", &spuldrfs_spe_virq[1]);
846 + if (err)
847 + goto fail_destroy_spe_irq_1;
849 + err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 2, &spuldrfs_spe_virq[2]);
850 + if (err) {
851 + err = -ENXIO;
852 + goto fail_free_spe_irq_1;
855 + err = request_irq(spuldrfs_spe_virq[2], spuldrfs_spe_interrupt, 0,
856 + "spuldrfs_spe_irq2", &spuldrfs_spe_virq[2]);
857 + if (err)
858 + goto fail_destroy_spe_irq_2;
860 + err = ps3_event_receive_port_setup(PS3_BINDING_CPU_ANY, &spuldrfs_spe_virq[3]);
861 + if (err) {
862 + err = -ENXIO;
863 + goto fail_free_spe_irq_2;
866 + err = lv1_set_spe_transition_notifier(spuldrfs_spe_id, spuldrfs_spe_trans_notify_mask,
867 + virq_to_hw(spuldrfs_spe_virq[3]));
868 + if (err) {
869 + printk(KERN_INFO"spuldrfs: lv1_set_spe_transition_notifier failed with %d\n", err);
870 + err = -ENXIO;
871 + goto fail_destroy_event_recv_port;
874 + err = request_irq(spuldrfs_spe_virq[3], spuldrfs_spe_interrupt, 0,
875 + "spuldrfs_spe_irq3", &spuldrfs_spe_virq[3]);
876 + if (err)
877 + goto fail_destroy_event_recv_port;
879 + return (0);
881 +fail_destroy_event_recv_port:
883 + ps3_event_receive_port_destroy(spuldrfs_spe_virq[3]);
885 +fail_free_spe_irq_2:
887 + free_irq(spuldrfs_spe_virq[2], &spuldrfs_spe_virq[2]);
889 +fail_destroy_spe_irq_2:
891 + ps3_spe_irq_destroy(spuldrfs_spe_virq[2]);
893 +fail_free_spe_irq_1:
895 + free_irq(spuldrfs_spe_virq[1], &spuldrfs_spe_virq[1]);
897 +fail_destroy_spe_irq_1:
899 + ps3_spe_irq_destroy(spuldrfs_spe_virq[1]);
901 +fail_free_spe_irq_0:
903 + free_irq(spuldrfs_spe_virq[0], &spuldrfs_spe_virq[0]);
905 +fail_destroy_spe_irq_0:
907 + ps3_spe_irq_destroy(spuldrfs_spe_virq[0]);
909 +fail_unmap_shadow:
911 + iounmap(spuldrfs_spe_shadow);
913 +fail_unmap_ls:
915 + iounmap(spuldrfs_spe_ls);
917 +fail_unmap_problem:
919 + iounmap(spuldrfs_spe_problem);
921 +fail_unmap_priv2:
923 + iounmap(spuldrfs_spe_priv2);
925 +fail_destruct_spe:
927 + lv1_destruct_logical_spe(spuldrfs_spe_id);
929 + return (err);
933 + * spuldrfs_destruct_spe
934 + */
935 +static void spuldrfs_destruct_spe(void)
937 + lv1_disable_logical_spe(spuldrfs_spe_id, 0);
939 + free_irq(spuldrfs_spe_virq[0], &spuldrfs_spe_virq[0]);
940 + ps3_spe_irq_destroy(spuldrfs_spe_virq[0]);
942 + free_irq(spuldrfs_spe_virq[1], &spuldrfs_spe_virq[1]);
943 + ps3_spe_irq_destroy(spuldrfs_spe_virq[1]);
945 + free_irq(spuldrfs_spe_virq[2], &spuldrfs_spe_virq[2]);
946 + ps3_spe_irq_destroy(spuldrfs_spe_virq[2]);
948 + free_irq(spuldrfs_spe_virq[3], &spuldrfs_spe_virq[3]);
949 + ps3_event_receive_port_destroy(spuldrfs_spe_virq[3]);
951 + iounmap(spuldrfs_spe_shadow);
952 + iounmap(spuldrfs_spe_ls);
953 + iounmap(spuldrfs_spe_problem);
954 + iounmap(spuldrfs_spe_priv2);
956 + lv1_destruct_logical_spe(spuldrfs_spe_id);
960 + * spuldrfs_mount
961 + */
962 +static struct dentry *spuldrfs_mount(struct file_system_type *fs_type,
963 + int flags, const char *dev_name, void *data)
965 + struct dentry *root;
966 + int err;
968 + err = spuldrfs_create_spe();
969 + if (err)
970 + return ERR_PTR(err);
972 + spuldrfs_spe_metldr = vmalloc_user(spuldrfs_spe_metldr_size);
973 + if (!spuldrfs_spe_metldr) {
974 + err = -ENOMEM;
975 + goto fail_destruct_spe;
978 + memset(spuldrfs_spe_metldr, 0, spuldrfs_spe_metldr_size);
980 + spuldrfs_spe_ldr = vmalloc_user(spuldrfs_spe_ldr_size);
981 + if (!spuldrfs_spe_ldr) {
982 + err = -ENOMEM;
983 + goto fail_free_spe_metldr;
986 + memset(spuldrfs_spe_ldr, 0, spuldrfs_spe_ldr_size);
988 + spuldrfs_spe_buf1 = vmalloc_user(spuldrfs_spe_buf1_size);
989 + if (!spuldrfs_spe_buf1) {
990 + err = -ENOMEM;
991 + goto fail_free_spe_ldr;
994 + memset(spuldrfs_spe_buf1, 0, spuldrfs_spe_buf1_size);
996 + spuldrfs_spe_buf2 = vmalloc_user(spuldrfs_spe_buf2_size);
997 + if (!spuldrfs_spe_buf2) {
998 + err = -ENOMEM;
999 + goto fail_free_spe_buf1;
1002 + memset(spuldrfs_spe_buf2, 0, spuldrfs_spe_buf2_size);
1004 + spuldrfs_spe_buf3 = vmalloc_user(spuldrfs_spe_buf3_size);
1005 + if (!spuldrfs_spe_buf3) {
1006 + err = -ENOMEM;
1007 + goto fail_free_spe_buf2;
1010 + memset(spuldrfs_spe_buf3, 0, spuldrfs_spe_buf3_size);
1012 + root = mount_single(fs_type, flags, data, spuldrfs_fill_super);
1013 + if (IS_ERR(root)) {
1014 + err = PTR_ERR(root);
1015 + goto fail_free_spe_buf3;
1018 + return (root);
1020 +fail_free_spe_buf3:
1022 + vfree(spuldrfs_spe_buf3);
1024 +fail_free_spe_buf2:
1026 + vfree(spuldrfs_spe_buf2);
1028 +fail_free_spe_buf1:
1030 + vfree(spuldrfs_spe_buf1);
1032 +fail_free_spe_ldr:
1034 + vfree(spuldrfs_spe_ldr);
1036 +fail_free_spe_metldr:
1038 + vfree(spuldrfs_spe_metldr);
1040 +fail_destruct_spe:
1042 + spuldrfs_destruct_spe();
1044 + return ERR_PTR(err);
1048 + * spuldrfs_kill_sb
1049 + */
1050 +static void spuldrfs_kill_sb(struct super_block *sb)
1052 + kill_litter_super(sb);
1054 + vfree(spuldrfs_spe_metldr);
1055 + vfree(spuldrfs_spe_ldr);
1056 + vfree(spuldrfs_spe_buf1);
1057 + vfree(spuldrfs_spe_buf2);
1058 + vfree(spuldrfs_spe_buf3);
1059 + spuldrfs_destruct_spe();
1062 +static struct file_system_type spuldrfs_type = {
1063 + .owner = THIS_MODULE,
1064 + .name = "spuldrfs",
1065 + .mount = spuldrfs_mount,
1066 + .kill_sb = spuldrfs_kill_sb,
1070 + * spuldrfs_init
1071 + */
1072 +static int __init spuldrfs_init(void)
1074 + int err;
1076 + spuldrfs_inode_cache = kmem_cache_create("spuldrfs_inode_cache",
1077 + sizeof(struct spuldrfs_inode_info), 0, SLAB_HWCACHE_ALIGN,
1078 + spuldrfs_init_once);
1079 + if (!spuldrfs_inode_cache)
1080 + return (-ENOMEM);
1082 + err = register_filesystem(&spuldrfs_type);
1083 + if (err)
1084 + goto fail_destroy_inode_cache;
1086 + return (0);
1088 +fail_destroy_inode_cache:
1090 + kmem_cache_destroy(spuldrfs_inode_cache);
1092 + return (err);
1096 + * spuldrfs_exit
1097 + */
1098 +static void __exit spuldrfs_exit(void)
1100 + unregister_filesystem(&spuldrfs_type);
1101 + kmem_cache_destroy(spuldrfs_inode_cache);
1104 +module_init(spuldrfs_init);
1105 +module_exit(spuldrfs_exit);
1107 +MODULE_DESCRIPTION("PS3 spuldrfs");
1108 +MODULE_AUTHOR("glevand");
1109 +MODULE_LICENSE("GPL");