support Linux kernel 3.19
[ps3linux_spuisofs.git] / spuisofs.c
blob8fdcb486f42ca15bcd01047b00030836195fb3be
2 /*
3 * PS3 spuisofs
5 * Copyright (C) 2012-2013 glevand <geoffrey.levand@mail.ru>
6 * All rights reserved.
8 * This program is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published
10 * by the Free Software Foundation; version 2 of the License.
12 * This program is distributed in the hope that it will be useful, but
13 * WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * General Public License for more details.
17 * You should have received a copy of the GNU General Public License along
18 * with this program; if not, write to the Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 #include <linux/module.h>
23 #include <linux/kernel.h>
24 #include <linux/version.h>
25 #include <linux/init.h>
26 #include <linux/fs.h>
27 #include <linux/fsnotify.h>
28 #include <linux/file.h>
29 #include <linux/slab.h>
30 #include <linux/vmalloc.h>
31 #include <linux/pagemap.h>
32 #include <linux/io.h>
33 #include <linux/interrupt.h>
35 #include <asm/uaccess.h>
36 #include <asm/ps3.h>
37 #include <asm/spu.h>
38 #include <asm/spu_priv1.h>
39 #include <asm/lv1call.h>
41 #define SPUISOFS_MAGIC 0x73707569
43 struct spe_shadow {
44 u8 padding_0140[0x0140];
45 u64 int_status_class0_RW; /* 0x0140 */
46 u64 int_status_class1_RW; /* 0x0148 */
47 u64 int_status_class2_RW; /* 0x0150 */
48 u8 padding_0158[0x0610-0x0158];
49 u64 mfc_dsisr_RW; /* 0x0610 */
50 u8 padding_0618[0x0620-0x0618];
51 u64 mfc_dar_RW; /* 0x0620 */
52 u8 padding_0628[0x0800-0x0628];
53 u64 mfc_dsipr_R; /* 0x0800 */
54 u8 padding_0808[0x0810-0x0808];
55 u64 mfc_lscrr_R; /* 0x0810 */
56 u8 padding_0818[0x0c00-0x0818];
57 u64 mfc_cer_R; /* 0x0c00 */
58 u8 padding_0c08[0x0f00-0x0c08];
59 u64 spe_execution_status; /* 0x0f00 */
60 u8 padding_0f08[0x1000-0x0f08];
63 struct spuisofs_inode_info {
64 struct inode vfs_inode;
65 unsigned long io_addr;
66 void *virt_addr;
69 struct spuisofs_tree_descr {
70 const char *name;
71 const struct file_operations *ops;
72 umode_t mode;
73 size_t size;
74 unsigned long io_addr;
75 void *virt_addr;
78 #define SPUISOFS_I(inode) container_of(inode, struct spuisofs_inode_info, vfs_inode)
80 struct spuisofs_run_args {
81 u64 laid;
82 u64 arg1_size;
83 u64 arg2_size;
86 static struct kmem_cache *spuisofs_inode_cache;
88 static u64 spuisofs_spe_priv2_addr;
89 static u64 spuisofs_spe_problem_addr;
90 static u64 spuisofs_spe_ls_addr;
91 static u64 spuisofs_spe_shadow_addr;
93 static void *spuisofs_spe_priv2;
94 static struct spu_problem *spuisofs_spe_problem;
95 static void *spuisofs_spe_ls;
96 static struct spe_shadow *spuisofs_spe_shadow;
97 static u64 spuisofs_spe_id;
98 static unsigned int spuisofs_spe_virq[4];
100 static void *spuisofs_spe_app;
101 static void *spuisofs_spe_arg1;
102 static void *spuisofs_spe_arg2;
103 static void *spuisofs_spe_buf;
105 static unsigned int spuisofs_spe_slb_index;
107 static unsigned long spuisofs_spe_app_size = 1024 * 1024;
108 module_param(spuisofs_spe_app_size, ulong, 0);
110 static unsigned long spuisofs_spe_arg1_size = 1024 * 1024;
111 module_param(spuisofs_spe_arg1_size, ulong, 0);
113 static unsigned long spuisofs_spe_arg2_size = 1024 * 1024;
114 module_param(spuisofs_spe_arg2_size, ulong, 0);
116 static unsigned long spuisofs_spe_buf_size = 1024 * 1024;
117 module_param(spuisofs_spe_buf_size, ulong, 0);
119 static unsigned long spuisofs_spe_trans_notify_mask = 0xf;
120 module_param(spuisofs_spe_trans_notify_mask, ulong, 0);
122 static unsigned long spuisofs_spe_resource_id = 6;
123 module_param(spuisofs_spe_resource_id, ulong, 0);
125 static int spuisofs_spe_buf_addr_32bit = 0;
126 module_param(spuisofs_spe_buf_addr_32bit, int, 0);
129 * spuisofs_spe_regs_read
131 static ssize_t spuisofs_spe_regs_read(struct file *file, char __user *buffer,
132 size_t size, loff_t *pos)
134 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
135 struct inode *inode = file->f_dentry->d_inode;
136 #else
137 struct inode *inode = file->f_path.dentry->d_inode;
138 #endif
139 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
141 if (*pos >= inode->i_size)
142 return (0);
144 return simple_read_from_buffer(buffer, size, pos,
145 si->virt_addr, inode->i_size);
149 * spuisofs_spe_regs_write
151 static ssize_t spuisofs_spe_regs_write(struct file *file, const char __user *buffer,
152 size_t size, loff_t *pos)
154 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
155 struct inode *inode = file->f_dentry->d_inode;
156 #else
157 struct inode *inode = file->f_path.dentry->d_inode;
158 #endif
159 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
161 if (*pos >= inode->i_size)
162 return (-EFBIG);
164 return simple_write_to_buffer(si->virt_addr, inode->i_size,
165 pos, buffer, size);
169 * spuisofs_spe_regs_mmap
171 static int spuisofs_spe_regs_mmap(struct file *file, struct vm_area_struct *vma)
173 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
174 struct inode *inode = file->f_dentry->d_inode;
175 #else
176 struct inode *inode = file->f_path.dentry->d_inode;
177 #endif
178 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
179 unsigned long size, pfn;
181 size = vma->vm_end - vma->vm_start;
182 pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff;
184 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
185 vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
186 #else
187 vma->vm_flags |= VM_RESERVED | VM_IO;
188 #endif
189 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
191 return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
194 static const struct file_operations spuisofs_spe_regs_fops = {
195 .read = spuisofs_spe_regs_read,
196 .write = spuisofs_spe_regs_write,
197 .mmap = spuisofs_spe_regs_mmap,
201 * spuisofs_spe_mem_read
203 static ssize_t spuisofs_spe_mem_read(struct file *file, char __user *buffer,
204 size_t size, loff_t *pos)
206 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
207 struct inode *inode = file->f_dentry->d_inode;
208 #else
209 struct inode *inode = file->f_path.dentry->d_inode;
210 #endif
211 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
213 if (*pos >= inode->i_size)
214 return (0);
216 return simple_read_from_buffer(buffer, size, pos,
217 si->virt_addr, inode->i_size);
221 * spuisofs_spe_mem_write
223 static ssize_t spuisofs_spe_mem_write(struct file *file, const char __user *buffer,
224 size_t size, loff_t *pos)
226 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
227 struct inode *inode = file->f_dentry->d_inode;
228 #else
229 struct inode *inode = file->f_path.dentry->d_inode;
230 #endif
231 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
233 if (*pos >= inode->i_size)
234 return (-EFBIG);
236 return simple_write_to_buffer(si->virt_addr, inode->i_size,
237 pos, buffer, size);
241 * spuisofs_spe_mem_mmap
243 static int spuisofs_spe_mem_mmap(struct file *file, struct vm_area_struct *vma)
245 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
246 struct inode *inode = file->f_dentry->d_inode;
247 #else
248 struct inode *inode = file->f_path.dentry->d_inode;
249 #endif
250 struct spuisofs_inode_info *si = SPUISOFS_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 spuisofs_spe_mem_fops = {
267 .read = spuisofs_spe_mem_read,
268 .write = spuisofs_spe_mem_write,
269 .mmap = spuisofs_spe_mem_mmap,
273 * spuisofs_mem_read
275 static ssize_t spuisofs_mem_read(struct file *file, char __user *buffer,
276 size_t size, loff_t *pos)
278 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
279 struct inode *inode = file->f_dentry->d_inode;
280 #else
281 struct inode *inode = file->f_path.dentry->d_inode;
282 #endif
283 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
285 if (*pos >= inode->i_size)
286 return (0);
288 return simple_read_from_buffer(buffer, size, pos,
289 si->virt_addr, inode->i_size);
293 * spuisofs_mem_write
295 static ssize_t spuisofs_mem_write(struct file *file, const char __user *buffer,
296 size_t size, loff_t *pos)
298 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
299 struct inode *inode = file->f_dentry->d_inode;
300 #else
301 struct inode *inode = file->f_path.dentry->d_inode;
302 #endif
303 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
305 if (*pos >= inode->i_size)
306 return (-EFBIG);
308 return simple_write_to_buffer(si->virt_addr, inode->i_size,
309 pos, buffer, size);
313 * spuisofs_mem_mmap
315 static int spuisofs_mem_mmap(struct file *file, struct vm_area_struct *vma)
317 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
318 struct inode *inode = file->f_dentry->d_inode;
319 #else
320 struct inode *inode = file->f_path.dentry->d_inode;
321 #endif
322 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
324 return remap_vmalloc_range(vma, si->virt_addr, 0);
327 static const struct file_operations spuisofs_mem_fops = {
328 .read = spuisofs_mem_read,
329 .write = spuisofs_mem_write,
330 .mmap = spuisofs_mem_mmap,
334 * spuisofs_info_read
336 static ssize_t spuisofs_info_read(struct file *file, char __user *buffer,
337 size_t size, loff_t *pos)
339 char buf[256];
340 size_t len;
341 unsigned long spe_buf_addr;
343 spe_buf_addr = (unsigned long) spuisofs_spe_buf;
345 if (spuisofs_spe_buf_addr_32bit)
346 spe_buf_addr &= 0xfffffffful;
348 len = sprintf(buf, "arg1 %lx\narg2 %lx\nbuf %lx\n",
349 (unsigned long) spuisofs_spe_arg1, (unsigned long) spuisofs_spe_arg2,
350 spe_buf_addr);
352 return simple_read_from_buffer(buffer, size, pos, buf, len);
355 static const struct file_operations spuisofs_info_fops = {
356 .read = spuisofs_info_read,
360 * spuisofs_run_write
362 static ssize_t spuisofs_run_write(struct file *file, const char __user *buffer,
363 size_t size, loff_t *pos)
365 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
366 struct inode *inode = file->f_dentry->d_inode;
367 #else
368 struct inode *inode = file->f_path.dentry->d_inode;
369 #endif
370 struct spuisofs_run_args args;
371 int err;
373 if (*pos || (size != inode->i_size))
374 return (-EINVAL);
376 if (copy_from_user(&args, buffer, sizeof(struct spuisofs_run_args)))
377 return -EFAULT;
379 if (args.arg1_size == 0)
380 args.arg1_size = spuisofs_spe_arg1_size;
382 if (args.arg2_size == 0)
383 args.arg2_size = spuisofs_spe_arg2_size;
385 if (args.arg1_size > spuisofs_spe_arg1_size)
386 return (-EINVAL);
388 if (args.arg2_size > spuisofs_spe_arg2_size)
389 return (-EINVAL);
391 err = lv1_undocumented_function_201(spuisofs_spe_id);
392 if (err)
393 printk(KERN_INFO"spuisofs: lv1_undocumented_function_201 failed with %d\n", err);
395 err = lv1_undocumented_function_209(spuisofs_spe_id, args.laid, (u64) spuisofs_spe_app,
396 (u64) spuisofs_spe_arg1, args.arg1_size,
397 (u64) spuisofs_spe_arg2, args.arg2_size, spuisofs_spe_resource_id);
398 if (err) {
399 printk(KERN_INFO"spuisofs: lv1_undocumented_function_209 failed with %d\n", err);
400 return (-ENXIO);
403 return (size);
406 static const struct file_operations spuisofs_run_fops = {
407 .write = spuisofs_run_write,
411 * spuisofs_cont_write
413 static ssize_t spuisofs_cont_write(struct file *file, const char __user *buffer,
414 size_t size, loff_t *pos)
416 u64 puint_mb_R;
417 int err;
419 err = lv1_undocumented_function_167(spuisofs_spe_id, 0x4000, &puint_mb_R);
420 if (err) {
421 printk(KERN_INFO"spuisofs: lv1_undocumented_function_167 failed with %d\n", err);
422 return (-ENXIO);
425 printk(KERN_INFO"spuisofs: puint_mb_R %llx\n", puint_mb_R);
427 err = lv1_undocumented_function_200(spuisofs_spe_id);
428 if (err) {
429 printk(KERN_INFO"spuisofs: lv1_undocumented_function_200 failed with %d\n", err);
430 return (-ENXIO);
433 return (size);
436 static const struct file_operations spuisofs_cont_fops = {
437 .write = spuisofs_cont_write,
441 * spuisofs_alloc_inode
443 static struct inode *spuisofs_alloc_inode(struct super_block *sb)
445 struct spuisofs_inode_info *si;
447 si = kmem_cache_alloc(spuisofs_inode_cache, GFP_KERNEL);
448 if (!si)
449 return (NULL);
451 return (&si->vfs_inode);
455 * spuisofs_i_callback
457 static void spuisofs_i_callback(struct rcu_head *head)
459 struct inode *inode = container_of(head, struct inode, i_rcu);
461 kmem_cache_free(spuisofs_inode_cache, SPUISOFS_I(inode));
465 * spuisofs_destroy_inode
467 static void spuisofs_destroy_inode(struct inode *inode)
469 call_rcu(&inode->i_rcu, spuisofs_i_callback);
473 * spuisofs_init_once
475 static void spuisofs_init_once(void *p)
477 struct spuisofs_inode_info *si = p;
479 inode_init_once(&si->vfs_inode);
483 * spuisofs_new_inode
485 static struct inode *spuisofs_new_inode(struct super_block *sb, umode_t mode)
487 struct inode *inode;
489 inode = new_inode(sb);
490 if (!inode)
491 return (NULL);
493 inode->i_ino = get_next_ino();
494 inode->i_mode = mode;
495 inode->i_uid = current_fsuid();
496 inode->i_gid = current_fsgid();
497 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
499 return (inode);
503 * spuisofs_setattr
505 static int spuisofs_setattr(struct dentry *dentry, struct iattr *attr)
507 struct inode *inode = dentry->d_inode;
509 setattr_copy(inode, attr);
510 mark_inode_dirty(inode);
512 return (0);
515 static const struct inode_operations spuisofs_inode_ops = {
516 .setattr = spuisofs_setattr,
520 * spuisofs_new_file
522 static int spuisofs_new_file(struct super_block *sb, struct dentry *dentry,
523 const struct file_operations *fops, umode_t mode, size_t size,
524 unsigned long io_addr, void *virt_addr)
526 struct inode *inode;
527 struct spuisofs_inode_info *si;
529 inode = spuisofs_new_inode(sb, S_IFREG | mode);
530 if (!inode)
531 return (-ENOMEM);
533 inode->i_op = &spuisofs_inode_ops;
534 inode->i_fop = fops;
535 inode->i_size = size;
536 inode->i_private = NULL;
538 si = SPUISOFS_I(inode);
539 si->io_addr = io_addr;
540 si->virt_addr = virt_addr;
542 d_add(dentry, inode);
544 return (0);
548 * spuisofs_fill_dir
550 static int spuisofs_fill_dir(struct dentry *dir,
551 const struct spuisofs_tree_descr *files)
553 struct dentry *dentry, *tmp;
554 int err;
556 while (files->name && files->name[0]) {
557 dentry = d_alloc_name(dir, files->name);
558 if (!dentry) {
559 err = -ENOMEM;
560 goto fail;
563 err = spuisofs_new_file(dir->d_sb, dentry, files->ops,
564 files->mode, files->size, files->io_addr, files->virt_addr);
565 if (err)
566 goto fail;
568 files++;
571 return (0);
573 fail:
575 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,18,1)
576 list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child)
577 dput(dentry);
578 #else
579 list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_child)
580 dput(dentry);
581 #endif
583 shrink_dcache_parent(dir);
585 return (err);
588 static const struct super_operations spuisofs_super_ops = {
589 .alloc_inode = spuisofs_alloc_inode,
590 .destroy_inode = spuisofs_destroy_inode,
591 .statfs = simple_statfs,
595 * spuisofs_fill_super
597 static int spuisofs_fill_super(struct super_block *sb, void *data, int silent)
599 const struct spuisofs_tree_descr root_dir_contents[] = {
600 { "priv2", &spuisofs_spe_regs_fops, 0666, sizeof(struct spu_priv2), spuisofs_spe_priv2_addr, spuisofs_spe_priv2, },
601 { "problem", &spuisofs_spe_regs_fops, 0666, sizeof(struct spu_problem), spuisofs_spe_problem_addr, spuisofs_spe_problem, },
602 { "ls", &spuisofs_spe_mem_fops, 0666, LS_SIZE, spuisofs_spe_ls_addr, spuisofs_spe_ls, },
603 { "shadow", &spuisofs_spe_mem_fops, 0444, sizeof(struct spe_shadow), spuisofs_spe_shadow_addr, spuisofs_spe_shadow, },
604 { "app", &spuisofs_mem_fops, 0666, spuisofs_spe_app_size, 0, spuisofs_spe_app, },
605 { "arg1", &spuisofs_mem_fops, 0666, spuisofs_spe_arg1_size, 0, spuisofs_spe_arg1, },
606 { "arg2", &spuisofs_mem_fops, 0666, spuisofs_spe_arg2_size, 0, spuisofs_spe_arg2, },
607 { "buf", &spuisofs_mem_fops, 0666, spuisofs_spe_buf_size, 0, spuisofs_spe_buf, },
608 { "info", &spuisofs_info_fops, 0444, 0, 0, NULL, },
609 { "run", &spuisofs_run_fops, 0222, sizeof(struct spuisofs_run_args), 0, NULL, },
610 { "cont", &spuisofs_cont_fops, 0222, 0, 0, NULL, },
611 { },
613 struct inode *root_inode;
614 int err;
616 sb->s_maxbytes = MAX_LFS_FILESIZE;
617 sb->s_blocksize = PAGE_CACHE_SIZE;
618 sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
619 sb->s_magic = SPUISOFS_MAGIC;
620 sb->s_op = &spuisofs_super_ops;
621 sb->s_time_gran = 1;
623 root_inode = spuisofs_new_inode(sb, S_IFDIR | 0755);
624 if (!root_inode)
625 return (-ENOMEM);
627 root_inode->i_op = &simple_dir_inode_operations;
628 root_inode->i_fop = &simple_dir_operations;
630 /* directory inodes start off with i_nlink == 2 (for "." entry) */
631 inc_nlink(root_inode);
633 sb->s_root = d_make_root(root_inode);
634 if (!sb->s_root)
635 return (-ENOMEM);
637 err = spuisofs_fill_dir(sb->s_root, root_dir_contents);
638 if (err)
639 return (err);
641 return (0);
645 * spuisofs_spe_ea_to_kernel_ea
647 static unsigned long spuisofs_spe_ea_to_kernel_ea(unsigned long spe_ea)
649 unsigned long kernel_ea, spe_buf_addr;
651 kernel_ea = spe_ea;
653 if (!spuisofs_spe_buf_addr_32bit)
654 return (kernel_ea);
656 spe_buf_addr = (unsigned long) spuisofs_spe_buf & 0xfffffffful;
658 if ((spe_ea >= spe_buf_addr) && (spe_ea < (spe_buf_addr + spuisofs_spe_buf_size)))
659 kernel_ea = (unsigned long) spuisofs_spe_buf + (spe_buf_addr - spe_ea);
661 return (kernel_ea);
665 * spuisofs_spe_interrupt
667 static irqreturn_t spuisofs_spe_interrupt(int irq, void *data)
669 u64 status;
670 u64 ea, kernel_ea, dsisr, esid, vsid;
671 u64 puint_mb_R;
672 u32 spu_status_R;
673 u64 spe_execution_status;
674 int err;
676 if (irq == spuisofs_spe_virq[0]) {
677 printk(KERN_INFO"spuisofs: got class 0 irq\n");
679 err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 0, &status);
680 if (err) {
681 printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err);
682 goto out;
685 printk(KERN_INFO"spuisofs: status %llx\n", status);
687 err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 0, status, 0);
688 if (err) {
689 printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err);
690 goto out;
692 } else if (irq == spuisofs_spe_virq[1]) {
693 printk(KERN_INFO"spuisofs: got class 1 irq\n");
695 err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 1, &status);
696 if (err) {
697 printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err);
698 goto out;
701 printk(KERN_INFO"spuisofs: status %llx\n", status);
703 if (status & CLASS1_SEGMENT_FAULT_INTR) {
704 ea = in_be64(&spuisofs_spe_shadow->mfc_dar_RW);
705 kernel_ea = spuisofs_spe_ea_to_kernel_ea(ea);
707 esid = (ea & ESID_MASK) | SLB_ESID_V;
708 vsid = (get_kernel_vsid(kernel_ea, MMU_SEGSIZE_256M) << SLB_VSID_SHIFT) | SLB_VSID_KERNEL | MMU_PAGE_4K;
710 printk(KERN_INFO"spuisofs: data segment fault at %llx (%llx)\n", ea, kernel_ea);
712 err = lv1_undocumented_function_62(spuisofs_spe_id, 0, spuisofs_spe_slb_index, esid, vsid);
713 if (err) {
714 printk(KERN_INFO"spuisofs: lv1_undocumented_function_62 failed with %d\n", err);
715 goto out;
718 spuisofs_spe_slb_index++;
719 if (spuisofs_spe_slb_index > SLB_INDEX_MASK)
720 spuisofs_spe_slb_index = 0;
723 if (status & CLASS1_STORAGE_FAULT_INTR) {
724 ea = in_be64(&spuisofs_spe_shadow->mfc_dar_RW);
725 kernel_ea = spuisofs_spe_ea_to_kernel_ea(ea);
726 dsisr = in_be64(&spuisofs_spe_shadow->mfc_dsisr_RW);
728 printk(KERN_INFO"spuisofs: data storage fault at %llx (%llx)\n", ea, kernel_ea);
730 if (dsisr & MFC_DSISR_PTE_NOT_FOUND) {
731 #if LINUX_VERSION_CODE < KERNEL_VERSION(3,19,0)
732 err = hash_page(kernel_ea, _PAGE_PRESENT, 0x300);
733 #else
734 err = hash_page(kernel_ea, _PAGE_PRESENT, 0x300, dsisr);
735 #endif
736 if (err) {
737 printk(KERN_INFO"spuisofs: hash_page failed with %d\n", err);
738 goto out;
743 err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 1, status, 0);
744 if (err) {
745 printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err);
746 goto out;
749 /* restart DMA */
751 err = lv1_undocumented_function_168(spuisofs_spe_id, 0x3000, 1ull << 32);
752 if (err) {
753 printk(KERN_INFO"spuisofs: lv1_undocumented_function_168 failed with %d\n", err);
754 goto out;
756 } else if (irq == spuisofs_spe_virq[2]) {
757 printk(KERN_INFO"spuisofs: got class 2 irq\n");
759 err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 2, &status);
760 if (err) {
761 printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err);
762 goto out;
765 printk(KERN_INFO"spuisofs: status %llx\n", status);
767 if (status & CLASS2_MAILBOX_INTR) {
768 err = lv1_undocumented_function_167(spuisofs_spe_id, 0x4000, &puint_mb_R);
769 if (err) {
770 printk(KERN_INFO"spuisofs: lv1_undocumented_function_167 failed with %d\n", err);
771 goto out;
774 printk(KERN_INFO"spuisofs: puint_mb_R %llx\n", puint_mb_R);
777 if (status & CLASS2_SPU_STOP_INTR) {
778 spu_status_R = in_be32(&spuisofs_spe_problem->spu_status_R);
780 printk(KERN_INFO"spuisofs: spu_status_R %x\n", spu_status_R);
783 err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 2, status, 0);
784 if (err) {
785 printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err);
786 goto out;
788 } else if (irq == spuisofs_spe_virq[3]) {
789 spe_execution_status = spuisofs_spe_shadow->spe_execution_status;
791 printk(KERN_INFO"spuisofs: transition notification: shadow spe_execution_status %llx\n",
792 spe_execution_status);
793 } else {
794 printk(KERN_INFO"spuisofs: got unknown irq %d\n", irq);
797 out:
799 return (IRQ_HANDLED);
803 * spuisofs_create_spe
805 static int spuisofs_create_spe(void)
807 u64 vas_id, junk;
808 int err;
810 err = lv1_get_virtual_address_space_id_of_ppe(&vas_id);
811 if (err)
812 return (-ENXIO);
814 printk(KERN_INFO"spuisofs: vas id %llu\n", vas_id);
816 err = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT,
817 PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, vas_id, 2,
818 &spuisofs_spe_priv2_addr, &spuisofs_spe_problem_addr, &spuisofs_spe_ls_addr,
819 &junk, &spuisofs_spe_shadow_addr, &spuisofs_spe_id);
820 if (err)
821 return (-ENXIO);
823 printk(KERN_INFO"spuisofs: spe id %llu\n", spuisofs_spe_id);
825 spuisofs_spe_priv2 = ioremap(spuisofs_spe_priv2_addr, sizeof(struct spu_priv2));
826 if (!spuisofs_spe_priv2) {
827 err = -ENOMEM;
828 goto fail_destruct_spe;
831 spuisofs_spe_problem = ioremap(spuisofs_spe_problem_addr, sizeof(struct spu_problem));
832 if (!spuisofs_spe_problem) {
833 err = -ENOMEM;
834 goto fail_unmap_priv2;
837 spuisofs_spe_ls = ioremap_prot(spuisofs_spe_ls_addr, LS_SIZE, _PAGE_NO_CACHE);
838 if (!spuisofs_spe_ls) {
839 err = -ENOMEM;
840 goto fail_unmap_problem;
843 spuisofs_spe_shadow = __ioremap(spuisofs_spe_shadow_addr, sizeof(struct spe_shadow),
844 _PAGE_NO_CACHE | 3);
845 if (!spuisofs_spe_shadow) {
846 err = -ENOMEM;
847 goto fail_unmap_ls;
850 err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 0, &spuisofs_spe_virq[0]);
851 if (err) {
852 err = -ENXIO;
853 goto fail_unmap_shadow;
856 err = request_irq(spuisofs_spe_virq[0], spuisofs_spe_interrupt, 0,
857 "spuisofs_spe_irq0", &spuisofs_spe_virq[0]);
858 if (err)
859 goto fail_destroy_spe_irq_0;
861 err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 1, &spuisofs_spe_virq[1]);
862 if (err) {
863 err = -ENXIO;
864 goto fail_free_spe_irq_0;
867 err = request_irq(spuisofs_spe_virq[1], spuisofs_spe_interrupt, 0,
868 "spuisofs_spe_irq1", &spuisofs_spe_virq[1]);
869 if (err)
870 goto fail_destroy_spe_irq_1;
872 err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 2, &spuisofs_spe_virq[2]);
873 if (err) {
874 err = -ENXIO;
875 goto fail_free_spe_irq_1;
878 err = request_irq(spuisofs_spe_virq[2], spuisofs_spe_interrupt, 0,
879 "spuisofs_spe_irq2", &spuisofs_spe_virq[2]);
880 if (err)
881 goto fail_destroy_spe_irq_2;
883 err = ps3_event_receive_port_setup(PS3_BINDING_CPU_ANY, &spuisofs_spe_virq[3]);
884 if (err) {
885 err = -ENXIO;
886 goto fail_free_spe_irq_2;
889 err = lv1_set_spe_transition_notifier(spuisofs_spe_id, spuisofs_spe_trans_notify_mask,
890 virq_to_hw(spuisofs_spe_virq[3]));
891 if (err) {
892 printk(KERN_INFO"spuisofs: lv1_set_spe_transition_notifier failed with %d\n", err);
893 err = -ENXIO;
894 goto fail_destroy_event_recv_port;
897 err = request_irq(spuisofs_spe_virq[3], spuisofs_spe_interrupt, 0,
898 "spuisofs_spe_irq3", &spuisofs_spe_virq[3]);
899 if (err)
900 goto fail_destroy_event_recv_port;
902 return (0);
904 fail_destroy_event_recv_port:
906 ps3_event_receive_port_destroy(spuisofs_spe_virq[3]);
908 fail_free_spe_irq_2:
910 free_irq(spuisofs_spe_virq[2], &spuisofs_spe_virq[2]);
912 fail_destroy_spe_irq_2:
914 ps3_spe_irq_destroy(spuisofs_spe_virq[2]);
916 fail_free_spe_irq_1:
918 free_irq(spuisofs_spe_virq[1], &spuisofs_spe_virq[1]);
920 fail_destroy_spe_irq_1:
922 ps3_spe_irq_destroy(spuisofs_spe_virq[1]);
924 fail_free_spe_irq_0:
926 free_irq(spuisofs_spe_virq[0], &spuisofs_spe_virq[0]);
928 fail_destroy_spe_irq_0:
930 ps3_spe_irq_destroy(spuisofs_spe_virq[0]);
932 fail_unmap_shadow:
934 iounmap(spuisofs_spe_shadow);
936 fail_unmap_ls:
938 iounmap(spuisofs_spe_ls);
940 fail_unmap_problem:
942 iounmap(spuisofs_spe_problem);
944 fail_unmap_priv2:
946 iounmap(spuisofs_spe_priv2);
948 fail_destruct_spe:
950 lv1_destruct_logical_spe(spuisofs_spe_id);
952 return (err);
956 * spuisofs_destruct_spe
958 static void spuisofs_destruct_spe(void)
960 free_irq(spuisofs_spe_virq[0], &spuisofs_spe_virq[0]);
961 ps3_spe_irq_destroy(spuisofs_spe_virq[0]);
963 free_irq(spuisofs_spe_virq[1], &spuisofs_spe_virq[1]);
964 ps3_spe_irq_destroy(spuisofs_spe_virq[1]);
966 free_irq(spuisofs_spe_virq[2], &spuisofs_spe_virq[2]);
967 ps3_spe_irq_destroy(spuisofs_spe_virq[2]);
969 free_irq(spuisofs_spe_virq[3], &spuisofs_spe_virq[3]);
970 ps3_event_receive_port_destroy(spuisofs_spe_virq[3]);
972 iounmap(spuisofs_spe_shadow);
973 iounmap(spuisofs_spe_ls);
974 iounmap(spuisofs_spe_problem);
975 iounmap(spuisofs_spe_priv2);
977 lv1_destruct_logical_spe(spuisofs_spe_id);
981 * spuisofs_mount
983 static struct dentry *spuisofs_mount(struct file_system_type *fs_type,
984 int flags, const char *dev_name, void *data)
986 struct dentry *root;
987 int err;
989 err = spuisofs_create_spe();
990 if (err)
991 return ERR_PTR(err);
993 spuisofs_spe_app = vmalloc_user(spuisofs_spe_app_size);
994 if (!spuisofs_spe_app) {
995 err = -ENOMEM;
996 goto fail_destruct_spe;
999 memset(spuisofs_spe_app, 0, spuisofs_spe_app_size);
1001 spuisofs_spe_arg1 = vmalloc_user(spuisofs_spe_arg1_size);
1002 if (!spuisofs_spe_arg1) {
1003 err = -ENOMEM;
1004 goto fail_free_spe_app;
1007 memset(spuisofs_spe_arg1, 0, spuisofs_spe_arg1_size);
1009 spuisofs_spe_arg2 = vmalloc_user(spuisofs_spe_arg2_size);
1010 if (!spuisofs_spe_arg2) {
1011 err = -ENOMEM;
1012 goto fail_free_spe_arg1;
1015 memset(spuisofs_spe_arg2, 0, spuisofs_spe_arg2_size);
1017 spuisofs_spe_buf = vmalloc_user(spuisofs_spe_buf_size);
1018 if (!spuisofs_spe_buf) {
1019 err = -ENOMEM;
1020 goto fail_free_spe_arg2;
1023 memset(spuisofs_spe_buf, 0, spuisofs_spe_buf_size);
1025 root = mount_single(fs_type, flags, data, spuisofs_fill_super);
1026 if (IS_ERR(root)) {
1027 err = PTR_ERR(root);
1028 goto fail_free_buf;
1031 return (root);
1033 fail_free_buf:
1035 vfree(spuisofs_spe_buf);
1037 fail_free_spe_arg2:
1039 vfree(spuisofs_spe_arg2);
1041 fail_free_spe_arg1:
1043 vfree(spuisofs_spe_arg1);
1045 fail_free_spe_app:
1047 vfree(spuisofs_spe_app);
1049 fail_destruct_spe:
1051 spuisofs_destruct_spe();
1053 return ERR_PTR(err);
1057 * spuisofs_kill_sb
1059 static void spuisofs_kill_sb(struct super_block *sb)
1061 kill_litter_super(sb);
1063 vfree(spuisofs_spe_app);
1064 vfree(spuisofs_spe_arg1);
1065 vfree(spuisofs_spe_arg2);
1066 vfree(spuisofs_spe_buf);
1067 spuisofs_destruct_spe();
1070 static struct file_system_type spuisofs_type = {
1071 .owner = THIS_MODULE,
1072 .name = "spuisofs",
1073 .mount = spuisofs_mount,
1074 .kill_sb = spuisofs_kill_sb,
1078 * spuisofs_init
1080 static int __init spuisofs_init(void)
1082 int err;
1084 spuisofs_inode_cache = kmem_cache_create("spuisofs_inode_cache",
1085 sizeof(struct spuisofs_inode_info), 0, SLAB_HWCACHE_ALIGN,
1086 spuisofs_init_once);
1087 if (!spuisofs_inode_cache)
1088 return (-ENOMEM);
1090 err = register_filesystem(&spuisofs_type);
1091 if (err)
1092 goto fail_destroy_inode_cache;
1094 return (0);
1096 fail_destroy_inode_cache:
1098 kmem_cache_destroy(spuisofs_inode_cache);
1100 return (err);
1104 * spuisofs_exit
1106 static void __exit spuisofs_exit(void)
1108 unregister_filesystem(&spuisofs_type);
1109 kmem_cache_destroy(spuisofs_inode_cache);
1112 module_init(spuisofs_init);
1113 module_exit(spuisofs_exit);
1115 MODULE_DESCRIPTION("PS3 spuisofs");
1116 MODULE_AUTHOR("glevand");
1117 MODULE_LICENSE("GPL");