Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 6df10a82 authored by Mark Nutter's avatar Mark Nutter Committed by Paul Mackerras
Browse files

[PATCH] spufs: enable SPE problem state MMIO access.



This patch is layered on top of CONFIG_SPARSEMEM
and is patterned after direct mapping of LS.

This patch allows mmap() of the following regions:
"mfc", which represents the area from [0x3000 - 0x3fff];
"cntl", which represents the area from [0x4000 - 0x4fff];
"signal1" which begins at offset 0x14000; "signal2" which
begins at offset 0x1c000.

The signal1 & signal2 files may be mmap()'d by regular user
processes.  The cntl and mfc file, on the other hand, may
only be accessed if the owning process has CAP_SYS_RAWIO,
because they have the potential to confuse the kernel
with regard to parallel access to the same files with
regular file operations: the kernel always holds a spinlock
when accessing registers in these areas to serialize them,
which can not be guaranteed with user mmaps,

Signed-off-by: default avatarArnd Bergmann <arnd.bergmann@de.ibm.com>
Signed-off-by: default avatarPaul Mackerras <paulus@samba.org>
parent a33a7d73
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -10,4 +10,9 @@ config SPU_FS
	  Units on machines implementing the Broadband Processor
	  Architecture.

config SPUFS_MMAP
	bool
	depends on SPU_FS && SPARSEMEM && !PPC_64K_PAGES
	default y

endmenu
+5 −0
Original line number Diff line number Diff line
@@ -570,6 +570,11 @@ static int __init spu_map_device(struct spu *spu, struct device_node *spe)
	if (!spu->local_store)
		goto out;

	prop = get_property(spe, "problem", NULL);
	if (!prop)
		goto out_unmap;
	spu->problem_phys = *(unsigned long *)prop;

	spu->problem= map_spe_prop(spe, "problem");
	if (!spu->problem)
		goto out_unmap;
+15 −3
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@
#include <asm/spu_csa.h>
#include "spufs.h"

struct spu_context *alloc_spu_context(struct address_space *local_store)
struct spu_context *alloc_spu_context(void)
{
	struct spu_context *ctx;
	ctx = kmalloc(sizeof *ctx, GFP_KERNEL);
@@ -53,7 +53,10 @@ struct spu_context *alloc_spu_context(struct address_space *local_store)
	ctx->mfc_fasync = NULL;
	ctx->tagwait = 0;
	ctx->state = SPU_STATE_SAVED;
	ctx->local_store = local_store;
	ctx->local_store = NULL;
	ctx->cntl = NULL;
	ctx->signal1 = NULL;
	ctx->signal2 = NULL;
	ctx->spu = NULL;
	ctx->ops = &spu_backing_ops;
	ctx->owner = get_task_mm(current);
@@ -110,7 +113,16 @@ void spu_release(struct spu_context *ctx)

void spu_unmap_mappings(struct spu_context *ctx)
{
	if (ctx->local_store)
		unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1);
	if (ctx->mfc)
		unmap_mapping_range(ctx->mfc, 0, 0x4000, 1);
	if (ctx->cntl)
		unmap_mapping_range(ctx->cntl, 0, 0x4000, 1);
	if (ctx->signal1)
		unmap_mapping_range(ctx->signal1, 0, 0x4000, 1);
	if (ctx->signal2)
		unmap_mapping_range(ctx->signal2, 0, 0x4000, 1);
}

int spu_acquire_runnable(struct spu_context *ctx)
+223 −6
Original line number Diff line number Diff line
@@ -41,8 +41,10 @@ static int
spufs_mem_open(struct inode *inode, struct file *file)
{
	struct spufs_inode_info *i = SPUFS_I(inode);
	file->private_data = i->i_ctx;
	file->f_mapping = i->i_ctx->local_store;
	struct spu_context *ctx = i->i_ctx;
	file->private_data = ctx;
	file->f_mapping = inode->i_mapping;
	ctx->local_store = inode->i_mapping;
	return 0;
}

@@ -86,7 +88,7 @@ spufs_mem_write(struct file *file, const char __user *buffer,
	return ret;
}

#ifdef CONFIG_SPARSEMEM
#ifdef CONFIG_SPUFS_MMAP
static struct page *
spufs_mem_mmap_nopage(struct vm_area_struct *vma,
		      unsigned long address, int *type)
@@ -138,11 +140,113 @@ static struct file_operations spufs_mem_fops = {
	.read    = spufs_mem_read,
	.write   = spufs_mem_write,
	.llseek  = generic_file_llseek,
#ifdef CONFIG_SPARSEMEM
#ifdef CONFIG_SPUFS_MMAP
	.mmap    = spufs_mem_mmap,
#endif
};

#ifdef CONFIG_SPUFS_MMAP
static struct page *spufs_ps_nopage(struct vm_area_struct *vma,
				    unsigned long address,
				    int *type, unsigned long ps_offs)
{
	struct page *page = NOPAGE_SIGBUS;
	int fault_type = VM_FAULT_SIGBUS;
	struct spu_context *ctx = vma->vm_file->private_data;
	unsigned long offset = address - vma->vm_start;
	unsigned long area;
	int ret;

	offset += vma->vm_pgoff << PAGE_SHIFT;
	if (offset >= 0x4000)
		goto out;

	ret = spu_acquire_runnable(ctx);
	if (ret)
		goto out;

	area = ctx->spu->problem_phys + ps_offs;
	page = pfn_to_page((area + offset) >> PAGE_SHIFT);
	fault_type = VM_FAULT_MINOR;
	page_cache_get(page);

	spu_release(ctx);

      out:
	if (type)
		*type = fault_type;

	return page;
}

static struct page *spufs_cntl_mmap_nopage(struct vm_area_struct *vma,
					   unsigned long address, int *type)
{
	return spufs_ps_nopage(vma, address, type, 0x4000);
}

static struct vm_operations_struct spufs_cntl_mmap_vmops = {
	.nopage = spufs_cntl_mmap_nopage,
};

/*
 * mmap support for problem state control area [0x4000 - 0x4fff].
 * Mapping this area requires that the application have CAP_SYS_RAWIO,
 * as these registers require special care when read/writing.
 */
static int spufs_cntl_mmap(struct file *file, struct vm_area_struct *vma)
{
	if (!(vma->vm_flags & VM_SHARED))
		return -EINVAL;

	if (!capable(CAP_SYS_RAWIO))
		return -EPERM;

	vma->vm_flags |= VM_RESERVED;
	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
				     | _PAGE_NO_CACHE);

	vma->vm_ops = &spufs_cntl_mmap_vmops;
	return 0;
}
#endif

static int spufs_cntl_open(struct inode *inode, struct file *file)
{
	struct spufs_inode_info *i = SPUFS_I(inode);
	struct spu_context *ctx = i->i_ctx;

	file->private_data = ctx;
	file->f_mapping = inode->i_mapping;
	ctx->cntl = inode->i_mapping;
	return 0;
}

static ssize_t
spufs_cntl_read(struct file *file, char __user *buffer,
		size_t size, loff_t *pos)
{
	/* FIXME: read from spu status */
	return -EINVAL;
}

static ssize_t
spufs_cntl_write(struct file *file, const char __user *buffer,
		 size_t size, loff_t *pos)
{
	/* FIXME: write to runctl bit */
	return -EINVAL;
}

static struct file_operations spufs_cntl_fops = {
	.open = spufs_cntl_open,
	.read = spufs_cntl_read,
	.write = spufs_cntl_write,
#ifdef CONFIG_SPUFS_MMAP
	.mmap = spufs_cntl_mmap,
#endif
};

static int
spufs_regs_open(struct inode *inode, struct file *file)
{
@@ -503,6 +607,16 @@ static struct file_operations spufs_wbox_stat_fops = {
	.read	= spufs_wbox_stat_read,
};

static int spufs_signal1_open(struct inode *inode, struct file *file)
{
	struct spufs_inode_info *i = SPUFS_I(inode);
	struct spu_context *ctx = i->i_ctx;
	file->private_data = ctx;
	file->f_mapping = inode->i_mapping;
	ctx->signal1 = inode->i_mapping;
	return nonseekable_open(inode, file);
}

static ssize_t spufs_signal1_read(struct file *file, char __user *buf,
			size_t len, loff_t *pos)
{
@@ -543,12 +657,50 @@ static ssize_t spufs_signal1_write(struct file *file, const char __user *buf,
	return 4;
}

#ifdef CONFIG_SPUFS_MMAP
static struct page *spufs_signal1_mmap_nopage(struct vm_area_struct *vma,
					      unsigned long address, int *type)
{
	return spufs_ps_nopage(vma, address, type, 0x14000);
}

static struct vm_operations_struct spufs_signal1_mmap_vmops = {
	.nopage = spufs_signal1_mmap_nopage,
};

static int spufs_signal1_mmap(struct file *file, struct vm_area_struct *vma)
{
	if (!(vma->vm_flags & VM_SHARED))
		return -EINVAL;

	vma->vm_flags |= VM_RESERVED;
	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
				     | _PAGE_NO_CACHE);

	vma->vm_ops = &spufs_signal1_mmap_vmops;
	return 0;
}
#endif

static struct file_operations spufs_signal1_fops = {
	.open = spufs_pipe_open,
	.open = spufs_signal1_open,
	.read = spufs_signal1_read,
	.write = spufs_signal1_write,
#ifdef CONFIG_SPUFS_MMAP
	.mmap = spufs_signal1_mmap,
#endif
};

static int spufs_signal2_open(struct inode *inode, struct file *file)
{
	struct spufs_inode_info *i = SPUFS_I(inode);
	struct spu_context *ctx = i->i_ctx;
	file->private_data = ctx;
	file->f_mapping = inode->i_mapping;
	ctx->signal2 = inode->i_mapping;
	return nonseekable_open(inode, file);
}

static ssize_t spufs_signal2_read(struct file *file, char __user *buf,
			size_t len, loff_t *pos)
{
@@ -591,10 +743,39 @@ static ssize_t spufs_signal2_write(struct file *file, const char __user *buf,
	return 4;
}

#ifdef CONFIG_SPUFS_MMAP
static struct page *spufs_signal2_mmap_nopage(struct vm_area_struct *vma,
					      unsigned long address, int *type)
{
	return spufs_ps_nopage(vma, address, type, 0x1c000);
}

static struct vm_operations_struct spufs_signal2_mmap_vmops = {
	.nopage = spufs_signal2_mmap_nopage,
};

static int spufs_signal2_mmap(struct file *file, struct vm_area_struct *vma)
{
	if (!(vma->vm_flags & VM_SHARED))
		return -EINVAL;

	/* FIXME: */
	vma->vm_flags |= VM_RESERVED;
	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
				     | _PAGE_NO_CACHE);

	vma->vm_ops = &spufs_signal2_mmap_vmops;
	return 0;
}
#endif

static struct file_operations spufs_signal2_fops = {
	.open = spufs_pipe_open,
	.open = spufs_signal2_open,
	.read = spufs_signal2_read,
	.write = spufs_signal2_write,
#ifdef CONFIG_SPUFS_MMAP
	.mmap = spufs_signal2_mmap,
#endif
};

static void spufs_signal1_type_set(void *data, u64 val)
@@ -643,6 +824,38 @@ static u64 spufs_signal2_type_get(void *data)
DEFINE_SIMPLE_ATTRIBUTE(spufs_signal2_type, spufs_signal2_type_get,
					spufs_signal2_type_set, "%llu");

#ifdef CONFIG_SPUFS_MMAP
static struct page *spufs_mfc_mmap_nopage(struct vm_area_struct *vma,
					   unsigned long address, int *type)
{
	return spufs_ps_nopage(vma, address, type, 0x3000);
}

static struct vm_operations_struct spufs_mfc_mmap_vmops = {
	.nopage = spufs_mfc_mmap_nopage,
};

/*
 * mmap support for problem state MFC DMA area [0x0000 - 0x0fff].
 * Mapping this area requires that the application have CAP_SYS_RAWIO,
 * as these registers require special care when read/writing.
 */
static int spufs_mfc_mmap(struct file *file, struct vm_area_struct *vma)
{
	if (!(vma->vm_flags & VM_SHARED))
		return -EINVAL;

	if (!capable(CAP_SYS_RAWIO))
		return -EPERM;

	vma->vm_flags |= VM_RESERVED;
	vma->vm_page_prot = __pgprot(pgprot_val(vma->vm_page_prot)
				     | _PAGE_NO_CACHE);

	vma->vm_ops = &spufs_mfc_mmap_vmops;
	return 0;
}
#endif

static int spufs_mfc_open(struct inode *inode, struct file *file)
{
@@ -932,6 +1145,9 @@ static struct file_operations spufs_mfc_fops = {
	.flush	 = spufs_mfc_flush,
	.fsync	 = spufs_mfc_fsync,
	.fasync	 = spufs_mfc_fasync,
#ifdef CONFIG_SPUFS_MMAP
	.mmap	 = spufs_mfc_mmap,
#endif
};

static void spufs_npc_set(void *data, u64 val)
@@ -1077,6 +1293,7 @@ struct tree_descr spufs_dir_contents[] = {
	{ "signal1_type", &spufs_signal1_type, 0666, },
	{ "signal2_type", &spufs_signal2_type, 0666, },
	{ "mfc", &spufs_mfc_fops, 0666, },
	{ "cntl", &spufs_cntl_fops,  0666, },
	{ "npc", &spufs_npc_ops, 0666, },
	{ "fpcr", &spufs_fpcr_fops, 0666, },
	{ "decr", &spufs_decr_ops, 0666, },
+1 −1
Original line number Diff line number Diff line
@@ -241,7 +241,7 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
		inode->i_gid = dir->i_gid;
		inode->i_mode &= S_ISGID;
	}
	ctx = alloc_spu_context(inode->i_mapping);
	ctx = alloc_spu_context();
	SPUFS_I(inode)->i_ctx = ctx;
	if (!ctx)
		goto out_iput;
Loading