# --- T2-COPYRIGHT-NOTE-BEGIN --- # T2 SDE: architecture/powerpc64/package/*/0220-spuisofs.patch # Copyright (C) 2020 - 2024 The T2 SDE Project # # This Copyright note is generated by scripts/Create-CopyPatch, # more information can be found in the files COPYING and README. # # This patch file is dual-licensed. It is available under the license the # patched project is licensed under, as long as it is an OpenSource license # as defined at http://www.opensource.org/ (e.g. BSD, X11) or under the terms # of the GNU General Public License version 2 as used by the T2 SDE. # --- T2-COPYRIGHT-NOTE-END --- --- a/arch/powerpc/platforms/ps3/Kconfig 2024-04-08 21:45:40.869511244 +0200 +++ b/arch/powerpc/platforms/ps3/Kconfig 2024-04-08 21:43:09.502835770 +0200 @@ -202,4 +202,11 @@ This support is required to access the PS3 ENCDEC device. In general, all users will say Y or M. +config SPUISO_FS + tristate "PS3 isolated SPU file system" + default m + depends on PPC_PS3 + help + The isolated SPU file system is used to execute isolated SPU modules. + endmenu --- a/arch/powerpc/platforms/ps3/Makefile 2012-08-08 21:45:25.436177008 +0200 +++ b/arch/powerpc/platforms/ps3/Makefile 2012-08-08 21:43:40.846170928 +0200 @@ -6,3 +6,5 @@ obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_SPU_BASE) += spu.o obj-y += device-init.o + +obj-$(CONFIG_SPUISO_FS) += spuisofs.o --- /dev/null 2013-10-07 20:20:12.741358433 +0200 +++ b/arch/powerpc/platforms/ps3/spuisofs.c 2013-10-07 22:15:48.445095266 +0200 @@ -0,0 +1,1062 @@ +/* + * PS3 spuisofs + * + * Copyright (C) 2021-2024 René Rebe + * Copyright (C) 2012-2013 glevand + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published + * by the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define SPUISOFS_MAGIC 0x73707569 + +struct spe_shadow { + u8 padding_0140[0x0140]; + u64 int_status_class0_RW; /* 0x0140 */ + u64 int_status_class1_RW; /* 0x0148 */ + u64 int_status_class2_RW; /* 0x0150 */ + u8 padding_0158[0x0610-0x0158]; + u64 mfc_dsisr_RW; /* 0x0610 */ + u8 padding_0618[0x0620-0x0618]; + u64 mfc_dar_RW; /* 0x0620 */ + u8 padding_0628[0x0800-0x0628]; + u64 mfc_dsipr_R; /* 0x0800 */ + u8 padding_0808[0x0810-0x0808]; + u64 mfc_lscrr_R; /* 0x0810 */ + u8 padding_0818[0x0c00-0x0818]; + u64 mfc_cer_R; /* 0x0c00 */ + u8 padding_0c08[0x0f00-0x0c08]; + u64 spe_execution_status; /* 0x0f00 */ + u8 padding_0f08[0x1000-0x0f08]; +}; + +struct spuisofs_inode_info { + struct inode vfs_inode; + unsigned long io_addr; + void *virt_addr; +}; + +struct spuisofs_tree_descr { + const char *name; + const struct file_operations *ops; + umode_t mode; + size_t size; + unsigned long io_addr; + void *virt_addr; +}; + +#define SPUISOFS_I(inode) container_of(inode, struct spuisofs_inode_info, vfs_inode) + +struct spuisofs_run_args { + u64 laid; + u64 arg1_size; + u64 arg2_size; +}; + +static struct kmem_cache *spuisofs_inode_cache; + +static u64 spuisofs_spe_priv2_addr; +static u64 spuisofs_spe_problem_addr; +static u64 spuisofs_spe_ls_addr; +static u64 spuisofs_spe_shadow_addr; + +static void *spuisofs_spe_priv2; +static struct spu_problem *spuisofs_spe_problem; +static void *spuisofs_spe_ls; +static struct spe_shadow *spuisofs_spe_shadow; +static u64 spuisofs_spe_id; +static unsigned int spuisofs_spe_virq[4]; + +static void *spuisofs_spe_app; +static void *spuisofs_spe_arg1; +static void *spuisofs_spe_arg2; +static void *spuisofs_spe_buf; + +static unsigned int spuisofs_spe_slb_index; + +static unsigned long spuisofs_spe_app_size = 1024 * 1024; +module_param(spuisofs_spe_app_size, ulong, 0); + +static unsigned long spuisofs_spe_arg1_size = 1024 * 1024; +module_param(spuisofs_spe_arg1_size, ulong, 0); + +static unsigned long spuisofs_spe_arg2_size = 1024 * 1024; +module_param(spuisofs_spe_arg2_size, ulong, 0); + +static unsigned long spuisofs_spe_buf_size = 1024 * 1024; +module_param(spuisofs_spe_buf_size, ulong, 0); + +static unsigned long spuisofs_spe_trans_notify_mask = 0xf; +module_param(spuisofs_spe_trans_notify_mask, ulong, 0); + +static unsigned long spuisofs_spe_resource_id = 6; +module_param(spuisofs_spe_resource_id, ulong, 0); + +static int spuisofs_spe_buf_addr_32bit = 0; +module_param(spuisofs_spe_buf_addr_32bit, int, 0); + +/* + * spuisofs_spe_regs_read + */ +static ssize_t spuisofs_spe_regs_read(struct file *file, char __user *buffer, + size_t size, loff_t *pos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spuisofs_inode_info *si = SPUISOFS_I(inode); + + if (*pos >= inode->i_size) + return 0; + + return simple_read_from_buffer(buffer, size, pos, + si->virt_addr, inode->i_size); +} + +/* + * spuisofs_spe_regs_write + */ +static ssize_t spuisofs_spe_regs_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spuisofs_inode_info *si = SPUISOFS_I(inode); + + if (*pos >= inode->i_size) + return -EFBIG; + + return simple_write_to_buffer(si->virt_addr, inode->i_size, + pos, buffer, size); +} + +/* + * spuisofs_spe_regs_mmap + */ +static int spuisofs_spe_regs_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spuisofs_inode_info *si = SPUISOFS_I(inode); + unsigned long size, pfn; + + size = vma->vm_end - vma->vm_start; + pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff; + + vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP | VM_IO); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); +} + +static const struct file_operations spuisofs_spe_regs_fops = { + .read = spuisofs_spe_regs_read, + .write = spuisofs_spe_regs_write, + .mmap = spuisofs_spe_regs_mmap, +}; + +/* + * spuisofs_spe_mem_read + */ +static ssize_t spuisofs_spe_mem_read(struct file *file, char __user *buffer, + size_t size, loff_t *pos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spuisofs_inode_info *si = SPUISOFS_I(inode); + + if (*pos >= inode->i_size) + return 0; + + return simple_read_from_buffer(buffer, size, pos, + si->virt_addr, inode->i_size); +} + +/* + * spuisofs_spe_mem_write + */ +static ssize_t spuisofs_spe_mem_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spuisofs_inode_info *si = SPUISOFS_I(inode); + + if (*pos >= inode->i_size) + return -EFBIG; + + return simple_write_to_buffer(si->virt_addr, inode->i_size, + pos, buffer, size); +} + +/* + * spuisofs_spe_mem_mmap + */ +static int spuisofs_spe_mem_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spuisofs_inode_info *si = SPUISOFS_I(inode); + unsigned long size, pfn; + + size = vma->vm_end - vma->vm_start; + pfn = (si->io_addr >> PAGE_SHIFT) + vma->vm_pgoff; + + vm_flags_set(vma, VM_DONTEXPAND | VM_DONTDUMP | VM_IO); + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + return io_remap_pfn_range(vma, vma->vm_start, pfn, size, vma->vm_page_prot); +} + +static const struct file_operations spuisofs_spe_mem_fops = { + .read = spuisofs_spe_mem_read, + .write = spuisofs_spe_mem_write, + .mmap = spuisofs_spe_mem_mmap, +}; + +/* + * spuisofs_mem_read + */ +static ssize_t spuisofs_mem_read(struct file *file, char __user *buffer, + size_t size, loff_t *pos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spuisofs_inode_info *si = SPUISOFS_I(inode); + + if (*pos >= inode->i_size) + return 0; + + return simple_read_from_buffer(buffer, size, pos, + si->virt_addr, inode->i_size); +} + +/* + * spuisofs_mem_write + */ +static ssize_t spuisofs_mem_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spuisofs_inode_info *si = SPUISOFS_I(inode); + + if (*pos >= inode->i_size) + return -EFBIG; + + return simple_write_to_buffer(si->virt_addr, inode->i_size, + pos, buffer, size); +} + +/* + * spuisofs_mem_mmap + */ +static int spuisofs_mem_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spuisofs_inode_info *si = SPUISOFS_I(inode); + + return remap_vmalloc_range(vma, si->virt_addr, 0); +} + +static const struct file_operations spuisofs_mem_fops = { + .read = spuisofs_mem_read, + .write = spuisofs_mem_write, + .mmap = spuisofs_mem_mmap, +}; + +/* + * spuisofs_info_read + */ +static ssize_t spuisofs_info_read(struct file *file, char __user *buffer, + size_t size, loff_t *pos) +{ + char buf[256]; + size_t len; + unsigned long spe_buf_addr; + + spe_buf_addr = (unsigned long) spuisofs_spe_buf; + + if (spuisofs_spe_buf_addr_32bit) + spe_buf_addr &= 0xfffffffful; + + len = sprintf(buf, "arg1 %lx\narg2 %lx\nbuf %lx\n", + (unsigned long) spuisofs_spe_arg1, (unsigned long) spuisofs_spe_arg2, + spe_buf_addr); + + return simple_read_from_buffer(buffer, size, pos, buf, len); +} + +static const struct file_operations spuisofs_info_fops = { + .read = spuisofs_info_read, +}; + +/* + * spuisofs_run_write + */ +static ssize_t spuisofs_run_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + struct inode *inode = file->f_path.dentry->d_inode; + struct spuisofs_run_args args; + int err; + + if (*pos || (size != inode->i_size)) + return -EINVAL; + + if (copy_from_user(&args, buffer, sizeof(struct spuisofs_run_args))) + return -EFAULT; + + if (args.arg1_size == 0) + args.arg1_size = spuisofs_spe_arg1_size; + + if (args.arg2_size == 0) + args.arg2_size = spuisofs_spe_arg2_size; + + if (args.arg1_size > spuisofs_spe_arg1_size) + return -EINVAL; + + if (args.arg2_size > spuisofs_spe_arg2_size) + return -EINVAL; + + err = lv1_undocumented_function_201(spuisofs_spe_id); + if (err) + printk(KERN_INFO"spuisofs: lv1_undocumented_function_201 failed with %d\n", err); + + err = lv1_undocumented_function_209(spuisofs_spe_id, args.laid, (u64) spuisofs_spe_app, + (u64) spuisofs_spe_arg1, args.arg1_size, + (u64) spuisofs_spe_arg2, args.arg2_size, spuisofs_spe_resource_id); + if (err) { + printk(KERN_INFO"spuisofs: lv1_undocumented_function_209 failed with %d\n", err); + return -ENXIO; + } + + return size; +} + +static const struct file_operations spuisofs_run_fops = { + .write = spuisofs_run_write, +}; + +/* + * spuisofs_cont_write + */ +static ssize_t spuisofs_cont_write(struct file *file, const char __user *buffer, + size_t size, loff_t *pos) +{ + u64 puint_mb_R; + int err; + + err = lv1_undocumented_function_167(spuisofs_spe_id, 0x4000, &puint_mb_R); + if (err) { + printk(KERN_INFO"spuisofs: lv1_undocumented_function_167 failed with %d\n", err); + return -ENXIO; + } + + printk(KERN_INFO"spuisofs: puint_mb_R %llx\n", puint_mb_R); + + err = lv1_undocumented_function_200(spuisofs_spe_id); + if (err) { + printk(KERN_INFO"spuisofs: lv1_undocumented_function_200 failed with %d\n", err); + return -ENXIO; + } + + return size; +} + +static const struct file_operations spuisofs_cont_fops = { + .write = spuisofs_cont_write, +}; + +/* + * spuisofs_alloc_inode + */ +static struct inode *spuisofs_alloc_inode(struct super_block *sb) +{ + struct spuisofs_inode_info *si; + + si = kmem_cache_alloc(spuisofs_inode_cache, GFP_KERNEL); + if (!si) + return NULL; + + return &si->vfs_inode; +} + +/* + * spuisofs_i_callback + */ +static void spuisofs_i_callback(struct rcu_head *head) +{ + struct inode *inode = container_of(head, struct inode, i_rcu); + + kmem_cache_free(spuisofs_inode_cache, SPUISOFS_I(inode)); +} + +/* + * spuisofs_destroy_inode + */ +static void spuisofs_destroy_inode(struct inode *inode) +{ + call_rcu(&inode->i_rcu, spuisofs_i_callback); +} + +/* + * spuisofs_init_once + */ +static void spuisofs_init_once(void *p) +{ + struct spuisofs_inode_info *si = p; + + inode_init_once(&si->vfs_inode); +} + +/* + * spuisofs_new_inode + */ +static struct inode *spuisofs_new_inode(struct super_block *sb, umode_t mode) +{ + struct inode *inode; + + inode = new_inode(sb); + if (!inode) + return NULL; + + inode->i_ino = get_next_ino(); + inode->i_mode = mode; + inode->i_uid = current_fsuid(); + inode->i_gid = current_fsgid(); + inode->__i_atime = inode->__i_mtime = inode->__i_ctime = current_time(inode); + + return inode; +} + +/* + * spuisofs_setattr + */ +static int spuisofs_setattr(struct mnt_idmap *idmap, + struct dentry *dentry, struct iattr *attr) +{ + struct inode *inode = dentry->d_inode; + + setattr_copy(idmap, inode, attr); + mark_inode_dirty(inode); + + return 0; +} + +static const struct inode_operations spuisofs_inode_ops = { + .setattr = spuisofs_setattr, +}; + +/* + * spuisofs_new_file + */ +static int spuisofs_new_file(struct super_block *sb, struct dentry *dentry, + const struct file_operations *fops, umode_t mode, size_t size, + unsigned long io_addr, void *virt_addr) +{ + struct inode *inode; + struct spuisofs_inode_info *si; + + inode = spuisofs_new_inode(sb, S_IFREG | mode); + if (!inode) + return -ENOMEM; + + inode->i_op = &spuisofs_inode_ops; + inode->i_fop = fops; + inode->i_size = size; + inode->i_private = NULL; + + si = SPUISOFS_I(inode); + si->io_addr = io_addr; + si->virt_addr = virt_addr; + + d_add(dentry, inode); + + return 0; +} + +/* + * spuisofs_fill_dir + */ +static int spuisofs_fill_dir(struct dentry *dir, + const struct spuisofs_tree_descr *files) +{ + struct dentry *dentry; + struct hlist_node *n; + int err; + + while (files->name && files->name[0]) { + dentry = d_alloc_name(dir, files->name); + if (!dentry) { + err = -ENOMEM; + goto fail; + } + + err = spuisofs_new_file(dir->d_sb, dentry, files->ops, + files->mode, files->size, files->io_addr, files->virt_addr); + if (err) + goto fail; + + files++; + } + + return 0; + +fail: + hlist_for_each_entry_safe(dentry, n, &dir->d_children, d_sib) + dput(dentry); + + shrink_dcache_parent(dir); + + return err; +} + +static const struct super_operations spuisofs_super_ops = { + .alloc_inode = spuisofs_alloc_inode, + .destroy_inode = spuisofs_destroy_inode, + .statfs = simple_statfs, +}; + +/* + * spuisofs_fill_super + */ +static int spuisofs_fill_super(struct super_block *sb, void *data, int silent) +{ + const struct spuisofs_tree_descr root_dir_contents[] = { + { "priv2", &spuisofs_spe_regs_fops, 0666, sizeof(struct spu_priv2), spuisofs_spe_priv2_addr, spuisofs_spe_priv2, }, + { "problem", &spuisofs_spe_regs_fops, 0666, sizeof(struct spu_problem), spuisofs_spe_problem_addr, spuisofs_spe_problem, }, + { "ls", &spuisofs_spe_mem_fops, 0666, LS_SIZE, spuisofs_spe_ls_addr, spuisofs_spe_ls, }, + { "shadow", &spuisofs_spe_mem_fops, 0444, sizeof(struct spe_shadow), spuisofs_spe_shadow_addr, spuisofs_spe_shadow, }, + { "app", &spuisofs_mem_fops, 0666, spuisofs_spe_app_size, 0, spuisofs_spe_app, }, + { "arg1", &spuisofs_mem_fops, 0666, spuisofs_spe_arg1_size, 0, spuisofs_spe_arg1, }, + { "arg2", &spuisofs_mem_fops, 0666, spuisofs_spe_arg2_size, 0, spuisofs_spe_arg2, }, + { "buf", &spuisofs_mem_fops, 0666, spuisofs_spe_buf_size, 0, spuisofs_spe_buf, }, + { "info", &spuisofs_info_fops, 0444, 0, 0, NULL, }, + { "run", &spuisofs_run_fops, 0222, sizeof(struct spuisofs_run_args), 0, NULL, }, + { "cont", &spuisofs_cont_fops, 0222, 0, 0, NULL, }, + { }, + }; + struct inode *root_inode; + int err; + + sb->s_maxbytes = MAX_LFS_FILESIZE; + sb->s_blocksize = PAGE_SIZE; + sb->s_blocksize_bits = PAGE_SHIFT; + sb->s_magic = SPUISOFS_MAGIC; + sb->s_op = &spuisofs_super_ops; + sb->s_time_gran = 1; + + root_inode = spuisofs_new_inode(sb, S_IFDIR | 0755); + if (!root_inode) + return -ENOMEM; + + root_inode->i_op = &simple_dir_inode_operations; + root_inode->i_fop = &simple_dir_operations; + + /* directory inodes start off with i_nlink == 2 (for "." entry) */ + inc_nlink(root_inode); + + sb->s_root = d_make_root(root_inode); + if (!sb->s_root) + return -ENOMEM; + + err = spuisofs_fill_dir(sb->s_root, root_dir_contents); + if (err) + return err; + + return 0; +} + +/* + * spuisofs_spe_ea_to_kernel_ea + */ +static unsigned long spuisofs_spe_ea_to_kernel_ea(unsigned long spe_ea) +{ + unsigned long kernel_ea, spe_buf_addr; + + kernel_ea = spe_ea; + + if (!spuisofs_spe_buf_addr_32bit) + return kernel_ea; + + spe_buf_addr = (unsigned long) spuisofs_spe_buf & 0xfffffffful; + + if ((spe_ea >= spe_buf_addr) && (spe_ea < (spe_buf_addr + spuisofs_spe_buf_size))) + kernel_ea = (unsigned long) spuisofs_spe_buf + (spe_buf_addr - spe_ea); + + return kernel_ea; +} + +/* + * spuisofs_spe_interrupt + */ +static irqreturn_t spuisofs_spe_interrupt(int irq, void *data) +{ + u64 status; + u64 ea, kernel_ea, dsisr, esid, vsid; + u64 puint_mb_R; + u32 spu_status_R; + u64 spe_execution_status; + int err; + + if (irq == spuisofs_spe_virq[0]) { + printk(KERN_INFO"spuisofs: got class 0 irq\n"); + + err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 0, &status); + if (err) { + printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err); + goto out; + } + + printk(KERN_INFO"spuisofs: status %llx\n", status); + + err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 0, status, 0); + if (err) { + printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err); + goto out; + } + } else if (irq == spuisofs_spe_virq[1]) { + printk(KERN_INFO"spuisofs: got class 1 irq\n"); + + err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 1, &status); + if (err) { + printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err); + goto out; + } + + printk(KERN_INFO"spuisofs: status %llx\n", status); + + if (status & CLASS1_SEGMENT_FAULT_INTR) { + ea = in_be64(&spuisofs_spe_shadow->mfc_dar_RW); + kernel_ea = spuisofs_spe_ea_to_kernel_ea(ea); + + esid = (ea & ESID_MASK) | SLB_ESID_V; + vsid = (get_kernel_vsid(kernel_ea, MMU_SEGSIZE_256M) << SLB_VSID_SHIFT) | SLB_VSID_KERNEL | MMU_PAGE_4K; + + printk(KERN_INFO"spuisofs: data segment fault at %llx (%llx)\n", ea, kernel_ea); + + err = lv1_undocumented_function_62(spuisofs_spe_id, 0, spuisofs_spe_slb_index, esid, vsid); + if (err) { + printk(KERN_INFO"spuisofs: lv1_undocumented_function_62 failed with %d\n", err); + goto out; + } + + spuisofs_spe_slb_index++; + if (spuisofs_spe_slb_index > SLB_INDEX_MASK) + spuisofs_spe_slb_index = 0; + } + + if (status & CLASS1_STORAGE_FAULT_INTR) { + ea = in_be64(&spuisofs_spe_shadow->mfc_dar_RW); + kernel_ea = spuisofs_spe_ea_to_kernel_ea(ea); + dsisr = in_be64(&spuisofs_spe_shadow->mfc_dsisr_RW); + + printk(KERN_INFO"spuisofs: data storage fault at %llx (%llx)\n", ea, kernel_ea); + + if (dsisr & MFC_DSISR_PTE_NOT_FOUND) { + err = hash_page(kernel_ea, _PAGE_PRESENT, 0x300, dsisr); + if (err) { + printk(KERN_INFO"spuisofs: hash_page failed with %d\n", err); + goto out; + } + } + } + + err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 1, status, 0); + if (err) { + printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err); + goto out; + } + + /* restart DMA */ + + err = lv1_undocumented_function_168(spuisofs_spe_id, 0x3000, 1ull << 32); + if (err) { + printk(KERN_INFO"spuisofs: lv1_undocumented_function_168 failed with %d\n", err); + goto out; + } + } else if (irq == spuisofs_spe_virq[2]) { + printk(KERN_INFO"spuisofs: got class 2 irq\n"); + + err = lv1_get_spe_interrupt_status(spuisofs_spe_id, 2, &status); + if (err) { + printk(KERN_INFO"spuisofs: lv1_get_spe_interrupt_status failed with %d\n", err); + goto out; + } + + printk(KERN_INFO"spuisofs: status %llx\n", status); + + if (status & CLASS2_MAILBOX_INTR) { + err = lv1_undocumented_function_167(spuisofs_spe_id, 0x4000, &puint_mb_R); + if (err) { + printk(KERN_INFO"spuisofs: lv1_undocumented_function_167 failed with %d\n", err); + goto out; + } + + printk(KERN_INFO"spuisofs: puint_mb_R %llx\n", puint_mb_R); + } + + if (status & CLASS2_SPU_STOP_INTR) { + spu_status_R = in_be32(&spuisofs_spe_problem->spu_status_R); + + printk(KERN_INFO"spuisofs: spu_status_R %x\n", spu_status_R); + } + + err = lv1_clear_spe_interrupt_status(spuisofs_spe_id, 2, status, 0); + if (err) { + printk(KERN_INFO"spuisofs: lv1_clear_spe_interrupt_status failed with %d\n", err); + goto out; + } + } else if (irq == spuisofs_spe_virq[3]) { + spe_execution_status = spuisofs_spe_shadow->spe_execution_status; + + printk(KERN_INFO"spuisofs: transition notification: shadow spe_execution_status %llx\n", + spe_execution_status); + } else { + printk(KERN_INFO"spuisofs: got unknown irq %d\n", irq); + } + +out: + + return IRQ_HANDLED; +} + +/* + * spuisofs_create_spe + */ +static int spuisofs_create_spe(void) +{ + u64 vas_id, junk; + int err; + + err = lv1_get_virtual_address_space_id_of_ppe(&vas_id); + if (err) + return -ENXIO; + + printk(KERN_INFO"spuisofs: vas id %llu\n", vas_id); + + err = lv1_construct_logical_spe(PAGE_SHIFT, PAGE_SHIFT, + PAGE_SHIFT, PAGE_SHIFT, PAGE_SHIFT, vas_id, 2, + &spuisofs_spe_priv2_addr, &spuisofs_spe_problem_addr, &spuisofs_spe_ls_addr, + &junk, &spuisofs_spe_shadow_addr, &spuisofs_spe_id); + if (err) + return -ENXIO; + + printk(KERN_INFO"spuisofs: spe id %llu\n", spuisofs_spe_id); + + spuisofs_spe_priv2 = ioremap(spuisofs_spe_priv2_addr, sizeof(struct spu_priv2)); + if (!spuisofs_spe_priv2) { + err = -ENOMEM; + goto fail_destruct_spe; + } + + spuisofs_spe_problem = ioremap(spuisofs_spe_problem_addr, sizeof(struct spu_problem)); + if (!spuisofs_spe_problem) { + err = -ENOMEM; + goto fail_unmap_priv2; + } + + spuisofs_spe_ls = ioremap(spuisofs_spe_ls_addr, LS_SIZE); + if (!spuisofs_spe_ls) { + err = -ENOMEM; + goto fail_unmap_problem; + } + + spuisofs_spe_shadow = ioremap_prot(spuisofs_spe_shadow_addr, sizeof(struct spe_shadow), + pgprot_val(pgprot_noncached_wc(PAGE_KERNEL_RO))); + if (!spuisofs_spe_shadow) { + err = -ENOMEM; + goto fail_unmap_ls; + } + + err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 0, &spuisofs_spe_virq[0]); + if (err) { + err = -ENXIO; + goto fail_unmap_shadow; + } + + err = request_irq(spuisofs_spe_virq[0], spuisofs_spe_interrupt, 0, + "spuisofs_spe_irq0", &spuisofs_spe_virq[0]); + if (err) + goto fail_destroy_spe_irq_0; + + err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 1, &spuisofs_spe_virq[1]); + if (err) { + err = -ENXIO; + goto fail_free_spe_irq_0; + } + + err = request_irq(spuisofs_spe_virq[1], spuisofs_spe_interrupt, 0, + "spuisofs_spe_irq1", &spuisofs_spe_virq[1]); + if (err) + goto fail_destroy_spe_irq_1; + + err = ps3_spe_irq_setup(PS3_BINDING_CPU_ANY, spuisofs_spe_id, 2, &spuisofs_spe_virq[2]); + if (err) { + err = -ENXIO; + goto fail_free_spe_irq_1; + } + + err = request_irq(spuisofs_spe_virq[2], spuisofs_spe_interrupt, 0, + "spuisofs_spe_irq2", &spuisofs_spe_virq[2]); + if (err) + goto fail_destroy_spe_irq_2; + + err = ps3_event_receive_port_setup(PS3_BINDING_CPU_ANY, &spuisofs_spe_virq[3]); + if (err) { + err = -ENXIO; + goto fail_free_spe_irq_2; + } + + err = lv1_set_spe_transition_notifier(spuisofs_spe_id, spuisofs_spe_trans_notify_mask, + virq_to_hw(spuisofs_spe_virq[3])); + if (err) { + printk(KERN_INFO"spuisofs: lv1_set_spe_transition_notifier failed with %d\n", err); + err = -ENXIO; + goto fail_destroy_event_recv_port; + } + + err = request_irq(spuisofs_spe_virq[3], spuisofs_spe_interrupt, 0, + "spuisofs_spe_irq3", &spuisofs_spe_virq[3]); + if (err) + goto fail_destroy_event_recv_port; + + return 0; + +fail_destroy_event_recv_port: + + ps3_event_receive_port_destroy(spuisofs_spe_virq[3]); + +fail_free_spe_irq_2: + + free_irq(spuisofs_spe_virq[2], &spuisofs_spe_virq[2]); + +fail_destroy_spe_irq_2: + + ps3_spe_irq_destroy(spuisofs_spe_virq[2]); + +fail_free_spe_irq_1: + + free_irq(spuisofs_spe_virq[1], &spuisofs_spe_virq[1]); + +fail_destroy_spe_irq_1: + + ps3_spe_irq_destroy(spuisofs_spe_virq[1]); + +fail_free_spe_irq_0: + + free_irq(spuisofs_spe_virq[0], &spuisofs_spe_virq[0]); + +fail_destroy_spe_irq_0: + + ps3_spe_irq_destroy(spuisofs_spe_virq[0]); + +fail_unmap_shadow: + + iounmap(spuisofs_spe_shadow); + +fail_unmap_ls: + + iounmap(spuisofs_spe_ls); + +fail_unmap_problem: + + iounmap(spuisofs_spe_problem); + +fail_unmap_priv2: + + iounmap(spuisofs_spe_priv2); + +fail_destruct_spe: + + lv1_destruct_logical_spe(spuisofs_spe_id); + + return err; +} + +/* + * spuisofs_destruct_spe + */ +static void spuisofs_destruct_spe(void) +{ + free_irq(spuisofs_spe_virq[0], &spuisofs_spe_virq[0]); + ps3_spe_irq_destroy(spuisofs_spe_virq[0]); + + free_irq(spuisofs_spe_virq[1], &spuisofs_spe_virq[1]); + ps3_spe_irq_destroy(spuisofs_spe_virq[1]); + + free_irq(spuisofs_spe_virq[2], &spuisofs_spe_virq[2]); + ps3_spe_irq_destroy(spuisofs_spe_virq[2]); + + free_irq(spuisofs_spe_virq[3], &spuisofs_spe_virq[3]); + ps3_event_receive_port_destroy(spuisofs_spe_virq[3]); + + iounmap(spuisofs_spe_shadow); + iounmap(spuisofs_spe_ls); + iounmap(spuisofs_spe_problem); + iounmap(spuisofs_spe_priv2); + + lv1_destruct_logical_spe(spuisofs_spe_id); +} + +/* + * spuisofs_mount + */ +static struct dentry *spuisofs_mount(struct file_system_type *fs_type, + int flags, const char *dev_name, void *data) +{ + struct dentry *root; + int err; + + err = spuisofs_create_spe(); + if (err) + return ERR_PTR(err); + + spuisofs_spe_app = vmalloc_user(spuisofs_spe_app_size); + if (!spuisofs_spe_app) { + err = -ENOMEM; + goto fail_destruct_spe; + } + + memset(spuisofs_spe_app, 0, spuisofs_spe_app_size); + + spuisofs_spe_arg1 = vmalloc_user(spuisofs_spe_arg1_size); + if (!spuisofs_spe_arg1) { + err = -ENOMEM; + goto fail_free_spe_app; + } + + memset(spuisofs_spe_arg1, 0, spuisofs_spe_arg1_size); + + spuisofs_spe_arg2 = vmalloc_user(spuisofs_spe_arg2_size); + if (!spuisofs_spe_arg2) { + err = -ENOMEM; + goto fail_free_spe_arg1; + } + + memset(spuisofs_spe_arg2, 0, spuisofs_spe_arg2_size); + + spuisofs_spe_buf = vmalloc_user(spuisofs_spe_buf_size); + if (!spuisofs_spe_buf) { + err = -ENOMEM; + goto fail_free_spe_arg2; + } + + memset(spuisofs_spe_buf, 0, spuisofs_spe_buf_size); + + root = mount_single(fs_type, flags, data, spuisofs_fill_super); + if (IS_ERR(root)) { + err = PTR_ERR(root); + goto fail_free_buf; + } + + return root; + +fail_free_buf: + + vfree(spuisofs_spe_buf); + +fail_free_spe_arg2: + + vfree(spuisofs_spe_arg2); + +fail_free_spe_arg1: + + vfree(spuisofs_spe_arg1); + +fail_free_spe_app: + + vfree(spuisofs_spe_app); + +fail_destruct_spe: + + spuisofs_destruct_spe(); + + return ERR_PTR(err); +} + +/* + * spuisofs_kill_sb + */ +static void spuisofs_kill_sb(struct super_block *sb) +{ + kill_litter_super(sb); + + vfree(spuisofs_spe_app); + vfree(spuisofs_spe_arg1); + vfree(spuisofs_spe_arg2); + vfree(spuisofs_spe_buf); + spuisofs_destruct_spe(); +} + +static struct file_system_type spuisofs_type = { + .owner = THIS_MODULE, + .name = "spuisofs", + .mount = spuisofs_mount, + .kill_sb = spuisofs_kill_sb, +}; + +/* + * spuisofs_init + */ +static int __init spuisofs_init(void) +{ + int err; + + spuisofs_inode_cache = kmem_cache_create("spuisofs_inode_cache", + sizeof(struct spuisofs_inode_info), 0, SLAB_HWCACHE_ALIGN, + spuisofs_init_once); + if (!spuisofs_inode_cache) + return -ENOMEM; + + err = register_filesystem(&spuisofs_type); + if (err) + goto fail_destroy_inode_cache; + + return 0; + +fail_destroy_inode_cache: + + kmem_cache_destroy(spuisofs_inode_cache); + + return err; +} + +/* + * spuisofs_exit + */ +static void __exit spuisofs_exit(void) +{ + unregister_filesystem(&spuisofs_type); + kmem_cache_destroy(spuisofs_inode_cache); +} + +module_init(spuisofs_init); +module_exit(spuisofs_exit); + +MODULE_DESCRIPTION("PS3 spuisofs"); +MODULE_AUTHOR("glevand"); +MODULE_LICENSE("GPL");