initial import
[ps3linux_kernel_patches_40.git] / 0230-spuldrfs.patch
blob13e87139937fc949cc690a37dd5954043d62cf21
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 2015-03-29 14:05:25.158040360 +0200
25 +++ b/arch/powerpc/platforms/ps3/spuldrfs.c 2015-03-29 14:22:23.491432933 +0200
26 @@ -0,0 +1,1088 @@
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_path.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_path.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_path.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_path.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_path.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_path.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_path.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_path.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_path.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 +#if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,1)
566 + list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child)
567 + dput(dentry);
568 +#else
569 + list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_child)
570 + dput(dentry);
571 +#endif
573 + shrink_dcache_parent(dir);
575 + return (err);
578 +static const struct super_operations spuldrfs_super_ops = {
579 + .alloc_inode = spuldrfs_alloc_inode,
580 + .destroy_inode = spuldrfs_destroy_inode,
581 + .statfs = simple_statfs,
585 + * spuldrfs_fill_super
586 + */
587 +static int spuldrfs_fill_super(struct super_block *sb, void *data, int silent)
589 + const struct spuldrfs_tree_descr root_dir_contents[] = {
590 + { "priv2", &spuldrfs_spe_regs_fops, 0666, sizeof(struct spu_priv2), spuldrfs_spe_priv2_addr, spuldrfs_spe_priv2, },
591 + { "problem", &spuldrfs_spe_regs_fops, 0666, sizeof(struct spu_problem), spuldrfs_spe_problem_addr, spuldrfs_spe_problem, },
592 + { "ls", &spuldrfs_spe_mem_fops, 0666, LS_SIZE, spuldrfs_spe_ls_addr, spuldrfs_spe_ls, },
593 + { "shadow", &spuldrfs_spe_mem_fops, 0444, sizeof(struct spe_shadow), spuldrfs_spe_shadow_addr, spuldrfs_spe_shadow, },
594 + { "metldr", &spuldrfs_mem_fops, 0666, spuldrfs_spe_metldr_size, 0, spuldrfs_spe_metldr, },
595 + { "ldr", &spuldrfs_mem_fops, 0666, spuldrfs_spe_ldr_size, 0, spuldrfs_spe_ldr, },
596 + { "buf1", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf1_size, 0, spuldrfs_spe_buf1, },
597 + { "buf2", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf2_size, 0, spuldrfs_spe_buf2, },
598 + { "buf3", &spuldrfs_mem_fops, 0666, spuldrfs_spe_buf3_size, 0, spuldrfs_spe_buf3, },
599 + { "info", &spuldrfs_info_fops, 0444, 0, 0, NULL, },
600 + { "run", &spuldrfs_run_fops, 0222, 0, 0, NULL, },
601 + { },
602 + };
603 + struct inode *root_inode;
604 + int err;
606 + sb->s_maxbytes = MAX_LFS_FILESIZE;
607 + sb->s_blocksize = PAGE_CACHE_SIZE;
608 + sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
609 + sb->s_magic = SPULDRFS_MAGIC;
610 + sb->s_op = &spuldrfs_super_ops;
611 + sb->s_time_gran = 1;
613 + root_inode = spuldrfs_new_inode(sb, S_IFDIR | 0755);
614 + if (!root_inode)
615 + return (-ENOMEM);
617 + root_inode->i_op = &simple_dir_inode_operations;
618 + root_inode->i_fop = &simple_dir_operations;
620 + /* directory inodes start off with i_nlink == 2 (for "." entry) */
621 + inc_nlink(root_inode);
623 + sb->s_root = d_make_root(root_inode);
624 + if (!sb->s_root)
625 + return (-ENOMEM);
627 + err = spuldrfs_fill_dir(sb->s_root, root_dir_contents);
628 + if (err)
629 + return (err);
631 + return (0);
635 + * spuldrfs_spe_ea_to_kernel_ea
636 + */
637 +static unsigned long spuldrfs_spe_ea_to_kernel_ea(unsigned long spe_ea)
639 + unsigned long kernel_ea, spe_buf1_addr, spe_buf2_addr, spe_buf3_addr;
641 + kernel_ea = spe_ea;
643 + if (!spuldrfs_spe_buf_addr_32bit)
644 + return (kernel_ea);
646 + spe_buf1_addr = (unsigned long) spuldrfs_spe_buf1 & 0xfffffffful;
647 + spe_buf2_addr = (unsigned long) spuldrfs_spe_buf2 & 0xfffffffful;
648 + spe_buf3_addr = (unsigned long) spuldrfs_spe_buf3 & 0xfffffffful;
650 + if ((spe_ea >= spe_buf1_addr) && (spe_ea < (spe_buf1_addr + spuldrfs_spe_buf1_size)))
651 + kernel_ea = (unsigned long) spuldrfs_spe_buf1 + (spe_buf1_addr - spe_ea);
652 + else if ((spe_ea >= spe_buf2_addr) && (spe_ea < (spe_buf2_addr + spuldrfs_spe_buf2_size)))
653 + kernel_ea = (unsigned long) spuldrfs_spe_buf2 + (spe_buf2_addr - spe_ea);
654 + else if ((spe_ea >= spe_buf3_addr) && (spe_ea < (spe_buf3_addr + spuldrfs_spe_buf3_size)))
655 + kernel_ea = (unsigned long) spuldrfs_spe_buf3 + (spe_buf3_addr - spe_ea);
657 + return (kernel_ea);
661 + * spuldrfs_spe_interrupt
662 + */
663 +static irqreturn_t spuldrfs_spe_interrupt(int irq, void *data)
665 + u64 status;
666 + u64 ea, kernel_ea, dsisr, esid, vsid;
667 + u64 puint_mb_R;
668 + u32 spu_status_R;
669 + u64 spe_execution_status;
670 + int err;
672 + if (irq == spuldrfs_spe_virq[0]) {
673 + printk(KERN_INFO"spuldrfs: got class 0 irq\n");
675 + err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 0, &status);
676 + if (err) {
677 + printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err);
678 + goto out;
681 + printk(KERN_INFO"spuldrfs: status %llx\n", status);
683 + err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 0, status, 0);
684 + if (err) {
685 + printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err);
686 + goto out;
688 + } else if (irq == spuldrfs_spe_virq[1]) {
689 + printk(KERN_INFO"spuldrfs: got class 1 irq\n");
691 + err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 1, &status);
692 + if (err) {
693 + printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err);
694 + goto out;
697 + printk(KERN_INFO"spuldrfs: status %llx\n", status);
699 + if (status & CLASS1_SEGMENT_FAULT_INTR) {
700 + ea = in_be64(&spuldrfs_spe_shadow->mfc_dar_RW);
701 + kernel_ea = spuldrfs_spe_ea_to_kernel_ea(ea);
703 + esid = (ea & ESID_MASK) | SLB_ESID_V;
704 + vsid = (get_kernel_vsid(kernel_ea, MMU_SEGSIZE_256M) << SLB_VSID_SHIFT) | SLB_VSID_KERNEL | MMU_PAGE_4K;
706 + printk(KERN_INFO"spuldrfs: data segment fault at %llx (%llx)\n", ea, kernel_ea);
708 + out_be64(&spuldrfs_spe_priv2->slb_index_W, spuldrfs_spe_slb_index);
709 + out_be64(&spuldrfs_spe_priv2->slb_vsid_RW, vsid);
710 + out_be64(&spuldrfs_spe_priv2->slb_esid_RW, esid);
712 + spuldrfs_spe_slb_index++;
713 + if (spuldrfs_spe_slb_index > SLB_INDEX_MASK)
714 + spuldrfs_spe_slb_index = 0;
717 + if (status & CLASS1_STORAGE_FAULT_INTR) {
718 + ea = in_be64(&spuldrfs_spe_shadow->mfc_dar_RW);
719 + kernel_ea = spuldrfs_spe_ea_to_kernel_ea(ea);
720 + dsisr = in_be64(&spuldrfs_spe_shadow->mfc_dsisr_RW);
722 + printk(KERN_INFO"spuldrfs: data storage fault at %llx (%llx)\n", ea, kernel_ea);
724 + if (dsisr & MFC_DSISR_PTE_NOT_FOUND) {
725 + err = hash_page(kernel_ea, _PAGE_PRESENT, 0x300, dsisr);
726 + if (err) {
727 + printk(KERN_INFO"spuldrfs: hash_page failed with %d\n", err);
728 + goto out;
733 + err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 1, status, 0);
734 + if (err) {
735 + printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err);
736 + goto out;
739 + /* restart DMA */
741 + out_be64(&spuldrfs_spe_priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND);
742 + } else if (irq == spuldrfs_spe_virq[2]) {
743 + printk(KERN_INFO"spuldrfs: got class 2 irq\n");
745 + err = lv1_get_spe_interrupt_status(spuldrfs_spe_id, 2, &status);
746 + if (err) {
747 + printk(KERN_INFO"spuldrfs: lv1_get_spe_interrupt_status failed with %d\n", err);
748 + goto out;
751 + printk(KERN_INFO"spuldrfs: status %llx\n", status);
753 + if (status & CLASS2_MAILBOX_INTR) {
754 + puint_mb_R = in_be64(&spuldrfs_spe_priv2->puint_mb_R);
756 + printk(KERN_INFO"spuldrfs: puint_mb_R %llx\n", puint_mb_R);
759 + if (status & CLASS2_SPU_STOP_INTR) {
760 + spu_status_R = in_be32(&spuldrfs_spe_problem->spu_status_R);
762 + printk(KERN_INFO"spuldrfs: spu_status_R %x\n", spu_status_R);
765 + err = lv1_clear_spe_interrupt_status(spuldrfs_spe_id, 2, status, 0);
766 + if (err) {
767 + printk(KERN_INFO"spuldrfs: lv1_clear_spe_interrupt_status failed with %d\n", err);
768 + goto out;
770 + } else if (irq == spuldrfs_spe_virq[3]) {
771 + spe_execution_status = spuldrfs_spe_shadow->spe_execution_status;
773 + printk(KERN_INFO"spuldrfs: transition notification: shadow spe_execution_status %llx\n",
774 + spe_execution_status);
775 + } else {
776 + printk(KERN_INFO"spuldrfs: got unknown irq %d\n", irq);
779 +out:
781 + return (IRQ_HANDLED);
785 + * spuldrfs_create_spe
786 + */
787 +static int spuldrfs_create_spe(void)
789 + u64 vas_id, junk;
790 + int err;
792 + err = lv1_get_virtual_address_space_id_of_ppe(&vas_id);
793 + if (err)
794 + return (-ENXIO);
796 + printk(KERN_INFO"spuldrfs: vas id %llu\n", vas_id);
798 + err = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT,
799 + PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, vas_id, 0,
800 + &spuldrfs_spe_priv2_addr, &spuldrfs_spe_problem_addr, &spuldrfs_spe_ls_addr,
801 + &junk, &spuldrfs_spe_shadow_addr, &spuldrfs_spe_id);
802 + if (err)
803 + return (-ENXIO);
805 + printk(KERN_INFO"spuldrfs: spe id %llu\n", spuldrfs_spe_id);
807 + spuldrfs_spe_priv2 = ioremap(spuldrfs_spe_priv2_addr, sizeof(struct spu_priv2));
808 + if (!spuldrfs_spe_priv2) {
809 + err = -ENOMEM;
810 + goto fail_destruct_spe;
813 + spuldrfs_spe_problem = ioremap(spuldrfs_spe_problem_addr, sizeof(struct spu_problem));
814 + if (!spuldrfs_spe_problem) {
815 + err = -ENOMEM;
816 + goto fail_unmap_priv2;
819 + spuldrfs_spe_ls = ioremap_prot(spuldrfs_spe_ls_addr, LS_SIZE, _PAGE_NO_CACHE);
820 + if (!spuldrfs_spe_ls) {
821 + err = -ENOMEM;
822 + goto fail_unmap_problem;
825 + spuldrfs_spe_shadow = __ioremap(spuldrfs_spe_shadow_addr, sizeof(struct spe_shadow),
826 + _PAGE_NO_CACHE | 3);
827 + if (!spuldrfs_spe_shadow) {
828 + err = -ENOMEM;
829 + goto fail_unmap_ls;
832 + err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 0, &spuldrfs_spe_virq[0]);
833 + if (err) {
834 + err = -ENXIO;
835 + goto fail_unmap_shadow;
838 + err = request_irq(spuldrfs_spe_virq[0], spuldrfs_spe_interrupt, 0,
839 + "spuldrfs_spe_irq0", &spuldrfs_spe_virq[0]);
840 + if (err)
841 + goto fail_destroy_spe_irq_0;
843 + err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 1, &spuldrfs_spe_virq[1]);
844 + if (err) {
845 + err = -ENXIO;
846 + goto fail_free_spe_irq_0;
849 + err = request_irq(spuldrfs_spe_virq[1], spuldrfs_spe_interrupt, 0,
850 + "spuldrfs_spe_irq1", &spuldrfs_spe_virq[1]);
851 + if (err)
852 + goto fail_destroy_spe_irq_1;
854 + err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuldrfs_spe_id, 2, &spuldrfs_spe_virq[2]);
855 + if (err) {
856 + err = -ENXIO;
857 + goto fail_free_spe_irq_1;
860 + err = request_irq(spuldrfs_spe_virq[2], spuldrfs_spe_interrupt, 0,
861 + "spuldrfs_spe_irq2", &spuldrfs_spe_virq[2]);
862 + if (err)
863 + goto fail_destroy_spe_irq_2;
865 + err = ps3_event_receive_port_setup(PS3_BINDING_CPU_ANY, &spuldrfs_spe_virq[3]);
866 + if (err) {
867 + err = -ENXIO;
868 + goto fail_free_spe_irq_2;
871 + err = lv1_set_spe_transition_notifier(spuldrfs_spe_id, spuldrfs_spe_trans_notify_mask,
872 + virq_to_hw(spuldrfs_spe_virq[3]));
873 + if (err) {
874 + printk(KERN_INFO"spuldrfs: lv1_set_spe_transition_notifier failed with %d\n", err);
875 + err = -ENXIO;
876 + goto fail_destroy_event_recv_port;
879 + err = request_irq(spuldrfs_spe_virq[3], spuldrfs_spe_interrupt, 0,
880 + "spuldrfs_spe_irq3", &spuldrfs_spe_virq[3]);
881 + if (err)
882 + goto fail_destroy_event_recv_port;
884 + return (0);
886 +fail_destroy_event_recv_port:
888 + ps3_event_receive_port_destroy(spuldrfs_spe_virq[3]);
890 +fail_free_spe_irq_2:
892 + free_irq(spuldrfs_spe_virq[2], &spuldrfs_spe_virq[2]);
894 +fail_destroy_spe_irq_2:
896 + ps3_spe_irq_destroy(spuldrfs_spe_virq[2]);
898 +fail_free_spe_irq_1:
900 + free_irq(spuldrfs_spe_virq[1], &spuldrfs_spe_virq[1]);
902 +fail_destroy_spe_irq_1:
904 + ps3_spe_irq_destroy(spuldrfs_spe_virq[1]);
906 +fail_free_spe_irq_0:
908 + free_irq(spuldrfs_spe_virq[0], &spuldrfs_spe_virq[0]);
910 +fail_destroy_spe_irq_0:
912 + ps3_spe_irq_destroy(spuldrfs_spe_virq[0]);
914 +fail_unmap_shadow:
916 + iounmap(spuldrfs_spe_shadow);
918 +fail_unmap_ls:
920 + iounmap(spuldrfs_spe_ls);
922 +fail_unmap_problem:
924 + iounmap(spuldrfs_spe_problem);
926 +fail_unmap_priv2:
928 + iounmap(spuldrfs_spe_priv2);
930 +fail_destruct_spe:
932 + lv1_destruct_logical_spe(spuldrfs_spe_id);
934 + return (err);
938 + * spuldrfs_destruct_spe
939 + */
940 +static void spuldrfs_destruct_spe(void)
942 + lv1_disable_logical_spe(spuldrfs_spe_id, 0);
944 + free_irq(spuldrfs_spe_virq[0], &spuldrfs_spe_virq[0]);
945 + ps3_spe_irq_destroy(spuldrfs_spe_virq[0]);
947 + free_irq(spuldrfs_spe_virq[1], &spuldrfs_spe_virq[1]);
948 + ps3_spe_irq_destroy(spuldrfs_spe_virq[1]);
950 + free_irq(spuldrfs_spe_virq[2], &spuldrfs_spe_virq[2]);
951 + ps3_spe_irq_destroy(spuldrfs_spe_virq[2]);
953 + free_irq(spuldrfs_spe_virq[3], &spuldrfs_spe_virq[3]);
954 + ps3_event_receive_port_destroy(spuldrfs_spe_virq[3]);
956 + iounmap(spuldrfs_spe_shadow);
957 + iounmap(spuldrfs_spe_ls);
958 + iounmap(spuldrfs_spe_problem);
959 + iounmap(spuldrfs_spe_priv2);
961 + lv1_destruct_logical_spe(spuldrfs_spe_id);
965 + * spuldrfs_mount
966 + */
967 +static struct dentry *spuldrfs_mount(struct file_system_type *fs_type,
968 + int flags, const char *dev_name, void *data)
970 + struct dentry *root;
971 + int err;
973 + err = spuldrfs_create_spe();
974 + if (err)
975 + return ERR_PTR(err);
977 + spuldrfs_spe_metldr = vmalloc_user(spuldrfs_spe_metldr_size);
978 + if (!spuldrfs_spe_metldr) {
979 + err = -ENOMEM;
980 + goto fail_destruct_spe;
983 + memset(spuldrfs_spe_metldr, 0, spuldrfs_spe_metldr_size);
985 + spuldrfs_spe_ldr = vmalloc_user(spuldrfs_spe_ldr_size);
986 + if (!spuldrfs_spe_ldr) {
987 + err = -ENOMEM;
988 + goto fail_free_spe_metldr;
991 + memset(spuldrfs_spe_ldr, 0, spuldrfs_spe_ldr_size);
993 + spuldrfs_spe_buf1 = vmalloc_user(spuldrfs_spe_buf1_size);
994 + if (!spuldrfs_spe_buf1) {
995 + err = -ENOMEM;
996 + goto fail_free_spe_ldr;
999 + memset(spuldrfs_spe_buf1, 0, spuldrfs_spe_buf1_size);
1001 + spuldrfs_spe_buf2 = vmalloc_user(spuldrfs_spe_buf2_size);
1002 + if (!spuldrfs_spe_buf2) {
1003 + err = -ENOMEM;
1004 + goto fail_free_spe_buf1;
1007 + memset(spuldrfs_spe_buf2, 0, spuldrfs_spe_buf2_size);
1009 + spuldrfs_spe_buf3 = vmalloc_user(spuldrfs_spe_buf3_size);
1010 + if (!spuldrfs_spe_buf3) {
1011 + err = -ENOMEM;
1012 + goto fail_free_spe_buf2;
1015 + memset(spuldrfs_spe_buf3, 0, spuldrfs_spe_buf3_size);
1017 + root = mount_single(fs_type, flags, data, spuldrfs_fill_super);
1018 + if (IS_ERR(root)) {
1019 + err = PTR_ERR(root);
1020 + goto fail_free_spe_buf3;
1023 + return (root);
1025 +fail_free_spe_buf3:
1027 + vfree(spuldrfs_spe_buf3);
1029 +fail_free_spe_buf2:
1031 + vfree(spuldrfs_spe_buf2);
1033 +fail_free_spe_buf1:
1035 + vfree(spuldrfs_spe_buf1);
1037 +fail_free_spe_ldr:
1039 + vfree(spuldrfs_spe_ldr);
1041 +fail_free_spe_metldr:
1043 + vfree(spuldrfs_spe_metldr);
1045 +fail_destruct_spe:
1047 + spuldrfs_destruct_spe();
1049 + return ERR_PTR(err);
1053 + * spuldrfs_kill_sb
1054 + */
1055 +static void spuldrfs_kill_sb(struct super_block *sb)
1057 + kill_litter_super(sb);
1059 + vfree(spuldrfs_spe_metldr);
1060 + vfree(spuldrfs_spe_ldr);
1061 + vfree(spuldrfs_spe_buf1);
1062 + vfree(spuldrfs_spe_buf2);
1063 + vfree(spuldrfs_spe_buf3);
1064 + spuldrfs_destruct_spe();
1067 +static struct file_system_type spuldrfs_type = {
1068 + .owner = THIS_MODULE,
1069 + .name = "spuldrfs",
1070 + .mount = spuldrfs_mount,
1071 + .kill_sb = spuldrfs_kill_sb,
1075 + * spuldrfs_init
1076 + */
1077 +static int __init spuldrfs_init(void)
1079 + int err;
1081 + spuldrfs_inode_cache = kmem_cache_create("spuldrfs_inode_cache",
1082 + sizeof(struct spuldrfs_inode_info), 0, SLAB_HWCACHE_ALIGN,
1083 + spuldrfs_init_once);
1084 + if (!spuldrfs_inode_cache)
1085 + return (-ENOMEM);
1087 + err = register_filesystem(&spuldrfs_type);
1088 + if (err)
1089 + goto fail_destroy_inode_cache;
1091 + return (0);
1093 +fail_destroy_inode_cache:
1095 + kmem_cache_destroy(spuldrfs_inode_cache);
1097 + return (err);
1101 + * spuldrfs_exit
1102 + */
1103 +static void __exit spuldrfs_exit(void)
1105 + unregister_filesystem(&spuldrfs_type);
1106 + kmem_cache_destroy(spuldrfs_inode_cache);
1109 +module_init(spuldrfs_init);
1110 +module_exit(spuldrfs_exit);
1112 +MODULE_DESCRIPTION("PS3 spuldrfs");
1113 +MODULE_AUTHOR("glevand");
1114 +MODULE_LICENSE("GPL");