add new memory buffer and export it under as 'buf' in filesystem; implement 32bit...
[ps3linux_spuisofs.git] / spuisofs.c
blobed8d5993d57a2abaad1f2af0c171a0b4c78cde4c
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_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_buf_size = 1024 * 1024;
117 module_param(spuisofs_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_buf_addr_32bit = 0;
126 module_param(spuisofs_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 struct inode *inode = file->f_dentry->d_inode;
135 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
137 if (*pos >= inode->i_size)
138 return (0);
140 return simple_read_from_buffer(buffer, size, pos,
141 si->virt_addr, inode->i_size);
145 * spuisofs_spe_regs_write
147 static ssize_t spuisofs_spe_regs_write(struct file *file, const char __user *buffer,
148 size_t size, loff_t *pos)
150 struct inode *inode = file->f_dentry->d_inode;
151 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
153 if (*pos >= inode->i_size)
154 return (-EFBIG);
156 return simple_write_to_buffer(si->virt_addr, inode->i_size,
157 pos, buffer, size);
161 * spuisofs_spe_regs_mmap
163 static int spuisofs_spe_regs_mmap(struct file *file, struct vm_area_struct *vma)
165 struct inode *inode = file->f_dentry->d_inode;
166 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
167 unsigned long size, pfn;
169 size = vma->vm_end - vma->vm_start;
170 pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff;
172 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
173 vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
174 #else
175 vma->vm_flags |= VM_RESERVED | VM_IO;
176 #endif
177 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
179 return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
182 static const struct file_operations spuisofs_spe_regs_fops = {
183 .read = spuisofs_spe_regs_read,
184 .write = spuisofs_spe_regs_write,
185 .mmap = spuisofs_spe_regs_mmap,
189 * spuisofs_spe_mem_read
191 static ssize_t spuisofs_spe_mem_read(struct file *file, char __user *buffer,
192 size_t size, loff_t *pos)
194 struct inode *inode = file->f_dentry->d_inode;
195 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
197 if (*pos >= inode->i_size)
198 return (0);
200 return simple_read_from_buffer(buffer, size, pos,
201 si->virt_addr, inode->i_size);
205 * spuisofs_spe_mem_write
207 static ssize_t spuisofs_spe_mem_write(struct file *file, const char __user *buffer,
208 size_t size, loff_t *pos)
210 struct inode *inode = file->f_dentry->d_inode;
211 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
213 if (*pos >= inode->i_size)
214 return (-EFBIG);
216 return simple_write_to_buffer(si->virt_addr, inode->i_size,
217 pos, buffer, size);
221 * spuisofs_spe_mem_mmap
223 static int spuisofs_spe_mem_mmap(struct file *file, struct vm_area_struct *vma)
225 struct inode *inode = file->f_dentry->d_inode;
226 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
227 unsigned long size, pfn;
229 size = vma->vm_end - vma->vm_start;
230 pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff;
232 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,7,0)
233 vma->vm_flags |= VM_DONTEXPAND | VM_DONTDUMP | VM_IO;
234 #else
235 vma->vm_flags |= VM_RESERVED | VM_IO;
236 #endif
237 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
239 return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot);
242 static const struct file_operations spuisofs_spe_mem_fops = {
243 .read = spuisofs_spe_mem_read,
244 .write = spuisofs_spe_mem_write,
245 .mmap = spuisofs_spe_mem_mmap,
249 * spuisofs_mem_read
251 static ssize_t spuisofs_mem_read(struct file *file, char __user *buffer,
252 size_t size, loff_t *pos)
254 struct inode *inode = file->f_dentry->d_inode;
255 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
257 if (*pos >= inode->i_size)
258 return (0);
260 return simple_read_from_buffer(buffer, size, pos,
261 si->virt_addr, inode->i_size);
265 * spuisofs_mem_write
267 static ssize_t spuisofs_mem_write(struct file *file, const char __user *buffer,
268 size_t size, loff_t *pos)
270 struct inode *inode = file->f_dentry->d_inode;
271 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
273 if (*pos >= inode->i_size)
274 return (-EFBIG);
276 return simple_write_to_buffer(si->virt_addr, inode->i_size,
277 pos, buffer, size);
281 * spuisofs_mem_mmap
283 static int spuisofs_mem_mmap(struct file *file, struct vm_area_struct *vma)
285 struct inode *inode = file->f_dentry->d_inode;
286 struct spuisofs_inode_info *si = SPUISOFS_I(inode);
288 return remap_vmalloc_range(vma, si->virt_addr, 0);
291 static const struct file_operations spuisofs_mem_fops = {
292 .read = spuisofs_mem_read,
293 .write = spuisofs_mem_write,
294 .mmap = spuisofs_mem_mmap,
298 * spuisofs_info_read
300 static ssize_t spuisofs_info_read(struct file *file, char __user *buffer,
301 size_t size, loff_t *pos)
303 char buf[256];
304 size_t len;
305 unsigned long buf_addr;
307 buf_addr = (unsigned long) spuisofs_buf;
308 if (spuisofs_buf_addr_32bit)
309 buf_addr &= 0xfffffffful;
311 len = sprintf(buf, "arg1 %lx\narg2 %lx\nbuf %lx\n",
312 (unsigned long) spuisofs_spe_arg1, (unsigned long) spuisofs_spe_arg2,
313 buf_addr);
315 return simple_read_from_buffer(buffer, size, pos, buf, len);
318 static const struct file_operations spuisofs_info_fops = {
319 .read = spuisofs_info_read,
323 * spuisofs_run_write
325 static ssize_t spuisofs_run_write(struct file *file, const char __user *buffer,
326 size_t size, loff_t *pos)
328 struct inode *inode = file->f_dentry->d_inode;
329 struct spuisofs_run_args args;
330 int err;
332 if (*pos || (size != inode->i_size))
333 return (-EINVAL);
335 if (copy_from_user(&args, buffer, sizeof(struct spuisofs_run_args)))
336 return -EFAULT;
338 if (args.arg1_size == 0)
339 args.arg1_size = spuisofs_spe_arg1_size;
341 if (args.arg2_size == 0)
342 args.arg2_size = spuisofs_spe_arg2_size;
344 if (args.arg1_size > spuisofs_spe_arg1_size)
345 return (-EINVAL);
347 if (args.arg2_size > spuisofs_spe_arg2_size)
348 return (-EINVAL);
350 err = lv1_undocumented_function_201(spuisofs_spe_id);
351 if (err)
352 printk(KERN_INFO"spuisofs: lv1_undocumented_function_201 failed with %d\n", err);
354 err = lv1_undocumented_function_209(spuisofs_spe_id, args.laid, (u64) spuisofs_spe_app,
355 (u64) spuisofs_spe_arg1, args.arg1_size,
356 (u64) spuisofs_spe_arg2, args.arg2_size, spuisofs_spe_resource_id);
357 if (err) {
358 printk(KERN_INFO"spuisofs: lv1_undocumented_function_209 failed with %d\n", err);
359 return (-ENXIO);
362 return (size);
365 static const struct file_operations spuisofs_run_fops = {
366 .write = spuisofs_run_write,
370 * spuisofs_cont_write
372 static ssize_t spuisofs_cont_write(struct file *file, const char __user *buffer,
373 size_t size, loff_t *pos)
375 u64 puint_mb_R;
376 int err;
378 err = lv1_undocumented_function_167(spuisofs_spe_id, 0x4000, &puint_mb_R);
379 if (err) {
380 printk(KERN_INFO"spuisofs: lv1_undocumented_function_167 failed with %d\n", err);
381 return (-ENXIO);
384 printk(KERN_INFO"spuisofs: puint_mb_R %llx\n", puint_mb_R);
386 err = lv1_undocumented_function_200(spuisofs_spe_id);
387 if (err) {
388 printk(KERN_INFO"spuisofs: lv1_undocumented_function_200 failed with %d\n", err);
389 return (-ENXIO);
392 return (size);
395 static const struct file_operations spuisofs_cont_fops = {
396 .write = spuisofs_cont_write,
400 * spuisofs_alloc_inode
402 static struct inode *spuisofs_alloc_inode(struct super_block *sb)
404 struct spuisofs_inode_info *si;
406 si = kmem_cache_alloc(spuisofs_inode_cache, GFP_KERNEL);
407 if (!si)
408 return (NULL);
410 return (&si->vfs_inode);
414 * spuisofs_i_callback
416 static void spuisofs_i_callback(struct rcu_head *head)
418 struct inode *inode = container_of(head, struct inode, i_rcu);
420 kmem_cache_free(spuisofs_inode_cache, SPUISOFS_I(inode));
424 * spuisofs_destroy_inode
426 static void spuisofs_destroy_inode(struct inode *inode)
428 call_rcu(&inode->i_rcu, spuisofs_i_callback);
432 * spuisofs_init_once
434 static void spuisofs_init_once(void *p)
436 struct spuisofs_inode_info *si = p;
438 inode_init_once(&si->vfs_inode);
442 * spuisofs_new_inode
444 static struct inode *spuisofs_new_inode(struct super_block *sb, umode_t mode)
446 struct inode *inode;
448 inode = new_inode(sb);
449 if (!inode)
450 return (NULL);
452 inode->i_ino = get_next_ino();
453 inode->i_mode = mode;
454 inode->i_uid = current_fsuid();
455 inode->i_gid = current_fsgid();
456 inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
458 return (inode);
462 * spuisofs_setattr
464 static int spuisofs_setattr(struct dentry *dentry, struct iattr *attr)
466 struct inode *inode = dentry->d_inode;
468 setattr_copy(inode, attr);
469 mark_inode_dirty(inode);
471 return (0);
474 static const struct inode_operations spuisofs_inode_ops = {
475 .setattr = spuisofs_setattr,
479 * spuisofs_new_file
481 static int spuisofs_new_file(struct super_block *sb, struct dentry *dentry,
482 const struct file_operations *fops, umode_t mode, size_t size,
483 unsigned long io_addr, void *virt_addr)
485 struct inode *inode;
486 struct spuisofs_inode_info *si;
488 inode = spuisofs_new_inode(sb, S_IFREG | mode);
489 if (!inode)
490 return (-ENOMEM);
492 inode->i_op = &spuisofs_inode_ops;
493 inode->i_fop = fops;
494 inode->i_size = size;
495 inode->i_private = NULL;
497 si = SPUISOFS_I(inode);
498 si->io_addr = io_addr;
499 si->virt_addr = virt_addr;
501 d_add(dentry, inode);
503 return (0);
507 * spuisofs_fill_dir
509 static int spuisofs_fill_dir(struct dentry *dir,
510 const struct spuisofs_tree_descr *files)
512 struct dentry *dentry, *tmp;
513 int err;
515 while (files->name && files->name[0]) {
516 dentry = d_alloc_name(dir, files->name);
517 if (!dentry) {
518 err = -ENOMEM;
519 goto fail;
522 err = spuisofs_new_file(dir->d_sb, dentry, files->ops,
523 files->mode, files->size, files->io_addr, files->virt_addr);
524 if (err)
525 goto fail;
527 files++;
530 return (0);
532 fail:
534 list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child)
535 dput(dentry);
537 shrink_dcache_parent(dir);
539 return (err);
542 static const struct super_operations spuisofs_super_ops = {
543 .alloc_inode = spuisofs_alloc_inode,
544 .destroy_inode = spuisofs_destroy_inode,
545 .statfs = simple_statfs,
549 * spuisofs_fill_super
551 static int spuisofs_fill_super(struct super_block *sb, void *data, int silent)
553 const struct spuisofs_tree_descr root_dir_contents[] = {
554 { "priv2", &spuisofs_spe_regs_fops, 0666, sizeof(struct spu_priv2), spuisofs_spe_priv2_addr, spuisofs_spe_priv2, },
555 { "problem", &spuisofs_spe_regs_fops, 0666, sizeof(struct spu_problem), spuisofs_spe_problem_addr, spuisofs_spe_problem, },
556 { "ls", &spuisofs_spe_mem_fops, 0666, LS_SIZE, spuisofs_spe_ls_addr, spuisofs_spe_ls, },
557 { "shadow", &spuisofs_spe_mem_fops, 0444, sizeof(struct spe_shadow), spuisofs_spe_shadow_addr, spuisofs_spe_shadow, },
558 { "app", &spuisofs_mem_fops, 0666, spuisofs_spe_app_size, 0, spuisofs_spe_app, },
559 { "arg1", &spuisofs_mem_fops, 0666, spuisofs_spe_arg1_size, 0, spuisofs_spe_arg1, },
560 { "arg2", &spuisofs_mem_fops, 0666, spuisofs_spe_arg2_size, 0, spuisofs_spe_arg2, },
561 { "buf", &spuisofs_mem_fops, 0666, spuisofs_buf_size, 0, spuisofs_buf, },
562 { "info", &spuisofs_info_fops, 0444, 0, 0, NULL, },
563 { "run", &spuisofs_run_fops, 0222, sizeof(struct spuisofs_run_args), 0, NULL, },
564 { "cont", &spuisofs_cont_fops, 0222, 0, 0, NULL, },
565 { },
567 struct inode *root_inode;
568 int err;
570 sb->s_maxbytes = MAX_LFS_FILESIZE;
571 sb->s_blocksize = PAGE_CACHE_SIZE;
572 sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
573 sb->s_magic = SPUISOFS_MAGIC;
574 sb->s_op = &spuisofs_super_ops;
575 sb->s_time_gran = 1;
577 root_inode = spuisofs_new_inode(sb, S_IFDIR | 0755);
578 if (!root_inode)
579 return (-ENOMEM);
581 root_inode->i_op = &simple_dir_inode_operations;
582 root_inode->i_fop = &simple_dir_operations;
584 /* directory inodes start off with i_nlink == 2 (for "." entry) */
585 inc_nlink(root_inode);
587 sb->s_root = d_make_root(root_inode);
588 if (!sb->s_root)
589 return (-ENOMEM);
591 err = spuisofs_fill_dir(sb->s_root, root_dir_contents);
592 if (err)
593 return (err);
595 return (0);
599 * spuisofs_spe_interrupt
601 static irqreturn_t spuisofs_spe_interrupt(int irq, void *data)
603 u64 status;
604 u64 ea, ea_full, dsisr, esid, vsid, buf_addr;
605 u64 puint_mb_R;
606 u32 spu_status_R;
607 u64 spe_execution_status;
608 int err;
610 if (irq == spuisofs_spe_virq[0]) {
611 printk(KERN_INFO"spuisofs: got class 0 irq\n");
613 err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 0, &status);
614 if (err) {
615 printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err);
616 goto out;
619 printk(KERN_INFO"spuisofs: status %llx\n", status);
621 err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 0, status, 0);
622 if (err) {
623 printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err);
624 goto out;
626 } else if (irq == spuisofs_spe_virq[1]) {
627 printk(KERN_INFO"spuisofs: got class 1 irq\n");
629 err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 1, &status);
630 if (err) {
631 printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err);
632 goto out;
635 printk(KERN_INFO"spuisofs: status %llx\n", status);
637 if (status & CLASS1_SEGMENT_FAULT_INTR) {
638 ea = in_be64(&spuisofs_spe_shadow->mfc_dar_RW);
639 ea_full = ea;
641 buf_addr = (u64) spuisofs_buf;
642 if (spuisofs_buf_addr_32bit)
643 buf_addr &= 0xfffffffful;
645 if ((ea >= buf_addr) && (ea < (buf_addr + spuisofs_buf_size)))
646 ea_full = (u64) spuisofs_buf + (buf_addr - ea);
648 esid = (ea & ESID_MASK) | SLB_ESID_V;
649 vsid = (get_kernel_vsid(ea_full, MMU_SEGSIZE_256M) << SLB_VSID_SHIFT) | SLB_VSID_KERNEL | MMU_PAGE_4K;
651 printk(KERN_INFO"spuisofs: data segment fault at %llx (%llx)\n", ea, ea_full);
653 err = lv1_undocumented_function_62(spuisofs_spe_id, 0, spuisofs_spe_slb_index, esid, vsid);
654 if (err) {
655 printk(KERN_INFO"spuisofs: lv1_undocumented_function_62 failed with %d\n", err);
656 goto out;
659 spuisofs_spe_slb_index++;
660 if (spuisofs_spe_slb_index > SLB_INDEX_MASK)
661 spuisofs_spe_slb_index = 0;
664 if (status & CLASS1_STORAGE_FAULT_INTR) {
665 ea = in_be64(&spuisofs_spe_shadow->mfc_dar_RW);
666 ea_full = ea;
667 dsisr = in_be64(&spuisofs_spe_shadow->mfc_dsisr_RW);
669 buf_addr = (u64) spuisofs_buf;
670 if (spuisofs_buf_addr_32bit)
671 buf_addr &= 0xfffffffful;
673 if ((ea >= buf_addr) && (ea < (buf_addr + spuisofs_buf_size)))
674 ea_full = (u64) spuisofs_buf + (buf_addr - ea);
676 printk(KERN_INFO"spuisofs: data storage fault at %llx (%llx)\n", ea, ea_full);
678 if (dsisr & MFC_DSISR_PTE_NOT_FOUND) {
679 err = hash_page(ea_full, _PAGE_PRESENT, 0x300);
680 if (err) {
681 printk(KERN_INFO"spuisofs: hash_page failed with %d\n", err);
682 goto out;
687 err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 1, status, 0);
688 if (err) {
689 printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err);
690 goto out;
693 /* restart DMA */
695 err = lv1_undocumented_function_168(spuisofs_spe_id, 0x3000, 1ull << 32);
696 if (err) {
697 printk(KERN_INFO"spuisofs: lv1_undocumented_function_168 failed with %d\n", err);
698 goto out;
700 } else if (irq == spuisofs_spe_virq[2]) {
701 printk(KERN_INFO"spuisofs: got class 2 irq\n");
703 err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 2, &status);
704 if (err) {
705 printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err);
706 goto out;
709 printk(KERN_INFO"spuisofs: status %llx\n", status);
711 if (status & CLASS2_MAILBOX_INTR) {
712 err = lv1_undocumented_function_167(spuisofs_spe_id, 0x4000, &puint_mb_R);
713 if (err) {
714 printk(KERN_INFO"spuisofs: lv1_undocumented_function_167 failed with %d\n", err);
715 goto out;
718 printk(KERN_INFO"spuisofs: puint_mb_R %llx\n", puint_mb_R);
721 if (status & CLASS2_SPU_STOP_INTR) {
722 spu_status_R = in_be32(&spuisofs_spe_problem->spu_status_R);
724 printk(KERN_INFO"spuisofs: spu_status_R %x\n", spu_status_R);
727 err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 2, status, 0);
728 if (err) {
729 printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err);
730 goto out;
732 } else if (irq == spuisofs_spe_virq[3]) {
733 spe_execution_status = spuisofs_spe_shadow->spe_execution_status;
735 printk(KERN_INFO"spuisofs: transition notification: shadow spe_execution_status %llx\n",
736 spe_execution_status);
737 } else {
738 printk(KERN_INFO"spuisofs: got unknown irq %d\n", irq);
741 out:
743 return (IRQ_HANDLED);
747 * spuisofs_create_spe
749 static int spuisofs_create_spe(void)
751 u64 vas_id, junk;
752 int err;
754 err = lv1_get_virtual_address_space_id_of_ppe(&vas_id);
755 if (err)
756 return (-ENXIO);
758 printk(KERN_INFO"spuisofs: vas id %llu\n", vas_id);
760 err = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT,
761 PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, vas_id, 2,
762 &spuisofs_spe_priv2_addr, &spuisofs_spe_problem_addr, &spuisofs_spe_ls_addr,
763 &junk, &spuisofs_spe_shadow_addr, &spuisofs_spe_id);
764 if (err)
765 return (-ENXIO);
767 printk(KERN_INFO"spuisofs: spe id %llu\n", spuisofs_spe_id);
769 spuisofs_spe_priv2 = ioremap(spuisofs_spe_priv2_addr, sizeof(struct spu_priv2));
770 if (!spuisofs_spe_priv2) {
771 err = -ENOMEM;
772 goto fail_destruct_spe;
775 spuisofs_spe_problem = ioremap(spuisofs_spe_problem_addr, sizeof(struct spu_problem));
776 if (!spuisofs_spe_problem) {
777 err = -ENOMEM;
778 goto fail_unmap_priv2;
781 spuisofs_spe_ls = ioremap_prot(spuisofs_spe_ls_addr, LS_SIZE, _PAGE_NO_CACHE);
782 if (!spuisofs_spe_ls) {
783 err = -ENOMEM;
784 goto fail_unmap_problem;
787 spuisofs_spe_shadow = __ioremap(spuisofs_spe_shadow_addr, sizeof(struct spe_shadow),
788 _PAGE_NO_CACHE | 3);
789 if (!spuisofs_spe_shadow) {
790 err = -ENOMEM;
791 goto fail_unmap_ls;
794 err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 0, &spuisofs_spe_virq[0]);
795 if (err) {
796 err = -ENXIO;
797 goto fail_unmap_shadow;
800 err = request_irq(spuisofs_spe_virq[0], spuisofs_spe_interrupt, 0,
801 "spuisofs_spe_irq0", &spuisofs_spe_virq[0]);
802 if (err)
803 goto fail_destroy_spe_irq_0;
805 err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 1, &spuisofs_spe_virq[1]);
806 if (err) {
807 err = -ENXIO;
808 goto fail_free_spe_irq_0;
811 err = request_irq(spuisofs_spe_virq[1], spuisofs_spe_interrupt, 0,
812 "spuisofs_spe_irq1", &spuisofs_spe_virq[1]);
813 if (err)
814 goto fail_destroy_spe_irq_1;
816 err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 2, &spuisofs_spe_virq[2]);
817 if (err) {
818 err = -ENXIO;
819 goto fail_free_spe_irq_1;
822 err = request_irq(spuisofs_spe_virq[2], spuisofs_spe_interrupt, 0,
823 "spuisofs_spe_irq2", &spuisofs_spe_virq[2]);
824 if (err)
825 goto fail_destroy_spe_irq_2;
827 err = ps3_event_receive_port_setup(PS3_BINDING_CPU_ANY, &spuisofs_spe_virq[3]);
828 if (err) {
829 err = -ENXIO;
830 goto fail_free_spe_irq_2;
833 err = lv1_set_spe_transition_notifier(spuisofs_spe_id, spuisofs_spe_trans_notify_mask,
834 virq_to_hw(spuisofs_spe_virq[3]));
835 if (err) {
836 printk(KERN_INFO"spuisofs: lv1_set_spe_transition_notifier failed with %d\n", err);
837 err = -ENXIO;
838 goto fail_destroy_event_recv_port;
841 err = request_irq(spuisofs_spe_virq[3], spuisofs_spe_interrupt, 0,
842 "spuisofs_spe_irq3", &spuisofs_spe_virq[3]);
843 if (err)
844 goto fail_destroy_event_recv_port;
846 return (0);
848 fail_destroy_event_recv_port:
850 ps3_event_receive_port_destroy(spuisofs_spe_virq[3]);
852 fail_free_spe_irq_2:
854 free_irq(spuisofs_spe_virq[2], &spuisofs_spe_virq[2]);
856 fail_destroy_spe_irq_2:
858 ps3_spe_irq_destroy(spuisofs_spe_virq[2]);
860 fail_free_spe_irq_1:
862 free_irq(spuisofs_spe_virq[1], &spuisofs_spe_virq[1]);
864 fail_destroy_spe_irq_1:
866 ps3_spe_irq_destroy(spuisofs_spe_virq[1]);
868 fail_free_spe_irq_0:
870 free_irq(spuisofs_spe_virq[0], &spuisofs_spe_virq[0]);
872 fail_destroy_spe_irq_0:
874 ps3_spe_irq_destroy(spuisofs_spe_virq[0]);
876 fail_unmap_shadow:
878 iounmap(spuisofs_spe_shadow);
880 fail_unmap_ls:
882 iounmap(spuisofs_spe_ls);
884 fail_unmap_problem:
886 iounmap(spuisofs_spe_problem);
888 fail_unmap_priv2:
890 iounmap(spuisofs_spe_priv2);
892 fail_destruct_spe:
894 lv1_destruct_logical_spe(spuisofs_spe_id);
896 return (err);
900 * spuisofs_destruct_spe
902 static void spuisofs_destruct_spe(void)
904 free_irq(spuisofs_spe_virq[0], &spuisofs_spe_virq[0]);
905 ps3_spe_irq_destroy(spuisofs_spe_virq[0]);
907 free_irq(spuisofs_spe_virq[1], &spuisofs_spe_virq[1]);
908 ps3_spe_irq_destroy(spuisofs_spe_virq[1]);
910 free_irq(spuisofs_spe_virq[2], &spuisofs_spe_virq[2]);
911 ps3_spe_irq_destroy(spuisofs_spe_virq[2]);
913 free_irq(spuisofs_spe_virq[3], &spuisofs_spe_virq[3]);
914 ps3_event_receive_port_destroy(spuisofs_spe_virq[3]);
916 iounmap(spuisofs_spe_shadow);
917 iounmap(spuisofs_spe_ls);
918 iounmap(spuisofs_spe_problem);
919 iounmap(spuisofs_spe_priv2);
921 lv1_destruct_logical_spe(spuisofs_spe_id);
925 * spuisofs_mount
927 static struct dentry *spuisofs_mount(struct file_system_type *fs_type,
928 int flags, const char *dev_name, void *data)
930 struct dentry *root;
931 int err;
933 err = spuisofs_create_spe();
934 if (err)
935 return ERR_PTR(err);
937 spuisofs_spe_app = vmalloc_user(spuisofs_spe_app_size);
938 if (!spuisofs_spe_app) {
939 err = -ENOMEM;
940 goto fail_destruct_spe;
943 memset(spuisofs_spe_app, 0, spuisofs_spe_app_size);
945 spuisofs_spe_arg1 = vmalloc_user(spuisofs_spe_arg1_size);
946 if (!spuisofs_spe_arg1) {
947 err = -ENOMEM;
948 goto fail_free_spe_app;
951 memset(spuisofs_spe_arg1, 0, spuisofs_spe_arg1_size);
953 spuisofs_spe_arg2 = vmalloc_user(spuisofs_spe_arg2_size);
954 if (!spuisofs_spe_arg2) {
955 err = -ENOMEM;
956 goto fail_free_spe_arg1;
959 memset(spuisofs_spe_arg2, 0, spuisofs_spe_arg2_size);
961 spuisofs_buf = vmalloc_user(spuisofs_buf_size);
962 if (!spuisofs_buf) {
963 err = -ENOMEM;
964 goto fail_free_spe_arg2;
967 memset(spuisofs_buf, 0, spuisofs_buf_size);
969 root = mount_single(fs_type, flags, data, spuisofs_fill_super);
970 if (IS_ERR(root)) {
971 err = PTR_ERR(root);
972 goto fail_free_buf;
975 return (root);
977 fail_free_buf:
979 vfree(spuisofs_buf);
981 fail_free_spe_arg2:
983 vfree(spuisofs_spe_arg2);
985 fail_free_spe_arg1:
987 vfree(spuisofs_spe_arg1);
989 fail_free_spe_app:
991 vfree(spuisofs_spe_app);
993 fail_destruct_spe:
995 spuisofs_destruct_spe();
997 return ERR_PTR(err);
1001 * spuisofs_kill_sb
1003 static void spuisofs_kill_sb(struct super_block *sb)
1005 kill_litter_super(sb);
1007 vfree(spuisofs_spe_app);
1008 vfree(spuisofs_spe_arg1);
1009 vfree(spuisofs_spe_arg2);
1010 vfree(spuisofs_buf);
1011 spuisofs_destruct_spe();
1014 static struct file_system_type spuisofs_type = {
1015 .owner = THIS_MODULE,
1016 .name = "spuisofs",
1017 .mount = spuisofs_mount,
1018 .kill_sb = spuisofs_kill_sb,
1022 * spuisofs_init
1024 static int __init spuisofs_init(void)
1026 int err;
1028 spuisofs_inode_cache = kmem_cache_create("spuisofs_inode_cache",
1029 sizeof(struct spuisofs_inode_info), 0, SLAB_HWCACHE_ALIGN,
1030 spuisofs_init_once);
1031 if (!spuisofs_inode_cache)
1032 return (-ENOMEM);
1034 err = register_filesystem(&spuisofs_type);
1035 if (err)
1036 goto fail_destroy_inode_cache;
1038 return (0);
1040 fail_destroy_inode_cache:
1042 kmem_cache_destroy(spuisofs_inode_cache);
1044 return (err);
1048 * spuisofs_exit
1050 static void __exit spuisofs_exit(void)
1052 unregister_filesystem(&spuisofs_type);
1053 kmem_cache_destroy(spuisofs_inode_cache);
1056 module_init(spuisofs_init);
1057 module_exit(spuisofs_exit);
1059 MODULE_DESCRIPTION("PS3 spuisofs");
1060 MODULE_AUTHOR("glevand");
1061 MODULE_LICENSE("GPL");