diff options
| author | arnd@arndb.de <arnd@arndb.de> | 2006-10-24 12:31:18 -0400 |
|---|---|---|
| committer | Paul Mackerras <paulus@samba.org> | 2006-10-25 00:20:21 -0400 |
| commit | 0afacde3df4c9980f505d9afd7cb0058389732ca (patch) | |
| tree | 0007cbb88b2ed7ba604fd11ca269f24617d2bb61 | |
| parent | eb758ce5b0d84e13cb643b6cc7cb429f6fa28258 (diff) | |
[POWERPC] spufs: allow isolated mode apps by starting the SPE loader
This patch adds general support for isolated mode SPE apps.
Isolated apps are started indirectly, by a dedicated loader "kernel".
This patch starts the loader when spe_create is invoked with the
ISOLATE flag. We do this at spe_create time to allow libspe to pass the
isolated app in before calling spe_run.
The loader is read from the device tree, at the location
"/spu-isolation/loader". If the loader is not present, an attempt to
start an isolated SPE binary will fail with -ENODEV.
Update: loader needs to be correctly aligned - copy to a kmalloced buf.
Update: remove workaround for systemsim/spurom 'L-bit' bug, which has
been fixed.
Update: don't write to runcntl on spu_run_init: SPU is already running.
Update: do spu_setup_isolated earlier
Tested on systemsim.
Signed-off-by: Jeremy Kerr <jk@ozlabs.org>
Signed-off-by: Arnd Bergmann <arnd.bergmann@de.ibm.com>
Signed-off-by: Paul Mackerras <paulus@samba.org>
| -rw-r--r-- | arch/powerpc/platforms/cell/spu_base.c | 35 | ||||
| -rw-r--r-- | arch/powerpc/platforms/cell/spufs/inode.c | 117 | ||||
| -rw-r--r-- | arch/powerpc/platforms/cell/spufs/run.c | 12 |
3 files changed, 148 insertions, 16 deletions
diff --git a/arch/powerpc/platforms/cell/spu_base.c b/arch/powerpc/platforms/cell/spu_base.c index f6c94087db40..d78b0af038e6 100644 --- a/arch/powerpc/platforms/cell/spu_base.c +++ b/arch/powerpc/platforms/cell/spu_base.c | |||
| @@ -89,7 +89,30 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea) | |||
| 89 | printk("%s: invalid access during switch!\n", __func__); | 89 | printk("%s: invalid access during switch!\n", __func__); |
| 90 | return 1; | 90 | return 1; |
| 91 | } | 91 | } |
| 92 | if (!mm || (REGION_ID(ea) != USER_REGION_ID)) { | 92 | esid = (ea & ESID_MASK) | SLB_ESID_V; |
| 93 | |||
| 94 | switch(REGION_ID(ea)) { | ||
| 95 | case USER_REGION_ID: | ||
| 96 | #ifdef CONFIG_HUGETLB_PAGE | ||
| 97 | if (in_hugepage_area(mm->context, ea)) | ||
| 98 | llp = mmu_psize_defs[mmu_huge_psize].sllp; | ||
| 99 | else | ||
| 100 | #endif | ||
| 101 | llp = mmu_psize_defs[mmu_virtual_psize].sllp; | ||
| 102 | vsid = (get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT) | | ||
| 103 | SLB_VSID_USER | llp; | ||
| 104 | break; | ||
| 105 | case VMALLOC_REGION_ID: | ||
| 106 | llp = mmu_psize_defs[mmu_virtual_psize].sllp; | ||
| 107 | vsid = (get_kernel_vsid(ea) << SLB_VSID_SHIFT) | | ||
| 108 | SLB_VSID_KERNEL | llp; | ||
| 109 | break; | ||
| 110 | case KERNEL_REGION_ID: | ||
| 111 | llp = mmu_psize_defs[mmu_linear_psize].sllp; | ||
| 112 | vsid = (get_kernel_vsid(ea) << SLB_VSID_SHIFT) | | ||
| 113 | SLB_VSID_KERNEL | llp; | ||
| 114 | break; | ||
| 115 | default: | ||
| 93 | /* Future: support kernel segments so that drivers | 116 | /* Future: support kernel segments so that drivers |
| 94 | * can use SPUs. | 117 | * can use SPUs. |
| 95 | */ | 118 | */ |
| @@ -97,16 +120,6 @@ static int __spu_trap_data_seg(struct spu *spu, unsigned long ea) | |||
| 97 | return 1; | 120 | return 1; |
| 98 | } | 121 | } |
| 99 | 122 | ||
| 100 | esid = (ea & ESID_MASK) | SLB_ESID_V; | ||
| 101 | #ifdef CONFIG_HUGETLB_PAGE | ||
| 102 | if (in_hugepage_area(mm->context, ea)) | ||
| 103 | llp = mmu_psize_defs[mmu_huge_psize].sllp; | ||
| 104 | else | ||
| 105 | #endif | ||
| 106 | llp = mmu_psize_defs[mmu_virtual_psize].sllp; | ||
| 107 | vsid = (get_vsid(mm->context.id, ea) << SLB_VSID_SHIFT) | | ||
| 108 | SLB_VSID_USER | llp; | ||
| 109 | |||
| 110 | out_be64(&priv2->slb_index_W, spu->slb_replace); | 123 | out_be64(&priv2->slb_index_W, spu->slb_replace); |
| 111 | out_be64(&priv2->slb_vsid_RW, vsid); | 124 | out_be64(&priv2->slb_vsid_RW, vsid); |
| 112 | out_be64(&priv2->slb_esid_RW, esid); | 125 | out_be64(&priv2->slb_esid_RW, esid); |
diff --git a/arch/powerpc/platforms/cell/spufs/inode.c b/arch/powerpc/platforms/cell/spufs/inode.c index 787ae71a6859..c8751936672a 100644 --- a/arch/powerpc/platforms/cell/spufs/inode.c +++ b/arch/powerpc/platforms/cell/spufs/inode.c | |||
| @@ -33,6 +33,8 @@ | |||
| 33 | #include <linux/slab.h> | 33 | #include <linux/slab.h> |
| 34 | #include <linux/parser.h> | 34 | #include <linux/parser.h> |
| 35 | 35 | ||
| 36 | #include <asm/prom.h> | ||
| 37 | #include <asm/spu_priv1.h> | ||
| 36 | #include <asm/io.h> | 38 | #include <asm/io.h> |
| 37 | #include <asm/semaphore.h> | 39 | #include <asm/semaphore.h> |
| 38 | #include <asm/spu.h> | 40 | #include <asm/spu.h> |
| @@ -41,6 +43,7 @@ | |||
| 41 | #include "spufs.h" | 43 | #include "spufs.h" |
| 42 | 44 | ||
| 43 | static kmem_cache_t *spufs_inode_cache; | 45 | static kmem_cache_t *spufs_inode_cache; |
| 46 | static char *isolated_loader; | ||
| 44 | 47 | ||
| 45 | static struct inode * | 48 | static struct inode * |
| 46 | spufs_alloc_inode(struct super_block *sb) | 49 | spufs_alloc_inode(struct super_block *sb) |
| @@ -232,6 +235,89 @@ struct file_operations spufs_context_fops = { | |||
| 232 | .fsync = simple_sync_file, | 235 | .fsync = simple_sync_file, |
| 233 | }; | 236 | }; |
| 234 | 237 | ||
| 238 | static int spu_setup_isolated(struct spu_context *ctx) | ||
| 239 | { | ||
| 240 | int ret; | ||
| 241 | u64 __iomem *mfc_cntl; | ||
| 242 | u64 sr1; | ||
| 243 | u32 status; | ||
| 244 | unsigned long timeout; | ||
| 245 | const u32 status_loading = SPU_STATUS_RUNNING | ||
| 246 | | SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS; | ||
| 247 | |||
| 248 | if (!isolated_loader) | ||
| 249 | return -ENODEV; | ||
| 250 | |||
| 251 | if ((ret = spu_acquire_runnable(ctx)) != 0) | ||
| 252 | return ret; | ||
| 253 | |||
| 254 | mfc_cntl = &ctx->spu->priv2->mfc_control_RW; | ||
| 255 | |||
| 256 | /* purge the MFC DMA queue to ensure no spurious accesses before we | ||
| 257 | * enter kernel mode */ | ||
| 258 | timeout = jiffies + HZ; | ||
| 259 | out_be64(mfc_cntl, MFC_CNTL_PURGE_DMA_REQUEST); | ||
| 260 | while ((in_be64(mfc_cntl) & MFC_CNTL_PURGE_DMA_STATUS_MASK) | ||
| 261 | != MFC_CNTL_PURGE_DMA_COMPLETE) { | ||
| 262 | if (time_after(jiffies, timeout)) { | ||
| 263 | printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n", | ||
| 264 | __FUNCTION__); | ||
| 265 | ret = -EIO; | ||
| 266 | goto out_unlock; | ||
| 267 | } | ||
| 268 | cond_resched(); | ||
| 269 | } | ||
| 270 | |||
| 271 | /* put the SPE in kernel mode to allow access to the loader */ | ||
| 272 | sr1 = spu_mfc_sr1_get(ctx->spu); | ||
| 273 | sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK; | ||
| 274 | spu_mfc_sr1_set(ctx->spu, sr1); | ||
| 275 | |||
| 276 | /* start the loader */ | ||
| 277 | ctx->ops->signal1_write(ctx, (unsigned long)isolated_loader >> 32); | ||
| 278 | ctx->ops->signal2_write(ctx, | ||
| 279 | (unsigned long)isolated_loader & 0xffffffff); | ||
| 280 | |||
| 281 | ctx->ops->runcntl_write(ctx, | ||
| 282 | SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE); | ||
| 283 | |||
| 284 | ret = 0; | ||
| 285 | timeout = jiffies + HZ; | ||
| 286 | while (((status = ctx->ops->status_read(ctx)) & status_loading) == | ||
| 287 | status_loading) { | ||
| 288 | if (time_after(jiffies, timeout)) { | ||
| 289 | printk(KERN_ERR "%s: timeout waiting for loader\n", | ||
| 290 | __FUNCTION__); | ||
| 291 | ret = -EIO; | ||
| 292 | goto out_drop_priv; | ||
| 293 | } | ||
| 294 | cond_resched(); | ||
| 295 | } | ||
| 296 | |||
| 297 | if (!(status & SPU_STATUS_RUNNING)) { | ||
| 298 | /* If isolated LOAD has failed: run SPU, we will get a stop-and | ||
| 299 | * signal later. */ | ||
| 300 | pr_debug("%s: isolated LOAD failed\n", __FUNCTION__); | ||
| 301 | ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE); | ||
| 302 | ret = -EACCES; | ||
| 303 | |||
| 304 | } else if (!(status & SPU_STATUS_ISOLATED_STATE)) { | ||
| 305 | /* This isn't allowed by the CBEA, but check anyway */ | ||
| 306 | pr_debug("%s: SPU fell out of isolated mode?\n", __FUNCTION__); | ||
| 307 | ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP); | ||
| 308 | ret = -EINVAL; | ||
| 309 | } | ||
| 310 | |||
| 311 | out_drop_priv: | ||
| 312 | /* Finished accessing the loader. Drop kernel mode */ | ||
| 313 | sr1 |= MFC_STATE1_PROBLEM_STATE_MASK; | ||
| 314 | spu_mfc_sr1_set(ctx->spu, sr1); | ||
| 315 | |||
| 316 | out_unlock: | ||
| 317 | up_write(&ctx->state_sema); | ||
| 318 | return ret; | ||
| 319 | } | ||
| 320 | |||
| 235 | static int | 321 | static int |
| 236 | spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, | 322 | spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, |
| 237 | int mode) | 323 | int mode) |
| @@ -255,6 +341,11 @@ spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags, | |||
| 255 | goto out_iput; | 341 | goto out_iput; |
| 256 | 342 | ||
| 257 | ctx->flags = flags; | 343 | ctx->flags = flags; |
| 344 | if (flags & SPU_CREATE_ISOLATE) { | ||
| 345 | ret = spu_setup_isolated(ctx); | ||
| 346 | if (ret) | ||
| 347 | goto out_iput; | ||
| 348 | } | ||
| 258 | 349 | ||
| 259 | inode->i_op = &spufs_dir_inode_operations; | 350 | inode->i_op = &spufs_dir_inode_operations; |
| 260 | inode->i_fop = &simple_dir_operations; | 351 | inode->i_fop = &simple_dir_operations; |
| @@ -555,6 +646,30 @@ spufs_parse_options(char *options, struct inode *root) | |||
| 555 | return 1; | 646 | return 1; |
| 556 | } | 647 | } |
| 557 | 648 | ||
| 649 | static void | ||
| 650 | spufs_init_isolated_loader(void) | ||
| 651 | { | ||
| 652 | struct device_node *dn; | ||
| 653 | const char *loader; | ||
| 654 | int size; | ||
| 655 | |||
| 656 | dn = of_find_node_by_path("/spu-isolation"); | ||
| 657 | if (!dn) | ||
| 658 | return; | ||
| 659 | |||
| 660 | loader = get_property(dn, "loader", &size); | ||
| 661 | if (!loader) | ||
| 662 | return; | ||
| 663 | |||
| 664 | /* kmalloc should align on a 16 byte boundary..* */ | ||
| 665 | isolated_loader = kmalloc(size, GFP_KERNEL); | ||
| 666 | if (!isolated_loader) | ||
| 667 | return; | ||
| 668 | |||
| 669 | memcpy(isolated_loader, loader, size); | ||
| 670 | printk(KERN_INFO "spufs: SPU isolation mode enabled\n"); | ||
| 671 | } | ||
| 672 | |||
| 558 | static int | 673 | static int |
| 559 | spufs_create_root(struct super_block *sb, void *data) | 674 | spufs_create_root(struct super_block *sb, void *data) |
| 560 | { | 675 | { |
| @@ -640,6 +755,8 @@ static int __init spufs_init(void) | |||
| 640 | ret = register_spu_syscalls(&spufs_calls); | 755 | ret = register_spu_syscalls(&spufs_calls); |
| 641 | if (ret) | 756 | if (ret) |
| 642 | goto out_fs; | 757 | goto out_fs; |
| 758 | |||
| 759 | spufs_init_isolated_loader(); | ||
| 643 | return 0; | 760 | return 0; |
| 644 | out_fs: | 761 | out_fs: |
| 645 | unregister_filesystem(&spufs_type); | 762 | unregister_filesystem(&spufs_type); |
diff --git a/arch/powerpc/platforms/cell/spufs/run.c b/arch/powerpc/platforms/cell/spufs/run.c index 0c03a04b6a30..a4a0080c2233 100644 --- a/arch/powerpc/platforms/cell/spufs/run.c +++ b/arch/powerpc/platforms/cell/spufs/run.c | |||
| @@ -1,3 +1,5 @@ | |||
| 1 | #define DEBUG | ||
| 2 | |||
| 1 | #include <linux/wait.h> | 3 | #include <linux/wait.h> |
| 2 | #include <linux/ptrace.h> | 4 | #include <linux/ptrace.h> |
| 3 | 5 | ||
| @@ -56,12 +58,12 @@ static inline int spu_run_init(struct spu_context *ctx, u32 * npc) | |||
| 56 | if ((ret = spu_acquire_runnable(ctx)) != 0) | 58 | if ((ret = spu_acquire_runnable(ctx)) != 0) |
| 57 | return ret; | 59 | return ret; |
| 58 | 60 | ||
| 59 | if (ctx->flags & SPU_CREATE_ISOLATE) | 61 | /* if we're in isolated mode, we would have started the SPU |
| 60 | runcntl |= SPU_RUNCNTL_ISOLATE; | 62 | * earlier, so don't do it again now. */ |
| 61 | else | 63 | if (!(ctx->flags & SPU_CREATE_ISOLATE)) { |
| 62 | ctx->ops->npc_write(ctx, *npc); | 64 | ctx->ops->npc_write(ctx, *npc); |
| 63 | 65 | ctx->ops->runcntl_write(ctx, runcntl); | |
| 64 | ctx->ops->runcntl_write(ctx, runcntl); | 66 | } |
| 65 | return 0; | 67 | return 0; |
| 66 | } | 68 | } |
| 67 | 69 | ||
