add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,2 @@
spu_save_dump.h
spu_restore_dump.h

View File

@@ -0,0 +1,63 @@
obj-$(CONFIG_SPU_FS) += spufs.o
spufs-y += inode.o file.o context.o syscalls.o coredump.o
spufs-y += sched.o backing_ops.o hw_ops.o run.o gang.o
spufs-y += switch.o fault.o lscsa_alloc.o
# magic for the trace events
CFLAGS_sched.o := -I$(src)
# Rules to build switch.o with the help of SPU tool chain
SPU_CROSS := spu-
SPU_CC := $(SPU_CROSS)gcc
SPU_AS := $(SPU_CROSS)gcc
SPU_LD := $(SPU_CROSS)ld
SPU_OBJCOPY := $(SPU_CROSS)objcopy
SPU_CFLAGS := -O2 -Wall -I$(srctree)/include \
-I$(objtree)/include2 -D__KERNEL__
SPU_AFLAGS := -c -D__ASSEMBLY__ -I$(srctree)/include \
-I$(objtree)/include2 -D__KERNEL__
SPU_LDFLAGS := -N -Ttext=0x0
$(obj)/switch.o: $(obj)/spu_save_dump.h $(obj)/spu_restore_dump.h
clean-files := spu_save_dump.h spu_restore_dump.h
# Compile SPU files
cmd_spu_cc = $(SPU_CC) $(SPU_CFLAGS) -c -o $@ $<
quiet_cmd_spu_cc = SPU_CC $@
$(obj)/spu_%.o: $(src)/spu_%.c
$(call if_changed,spu_cc)
# Assemble SPU files
cmd_spu_as = $(SPU_AS) $(SPU_AFLAGS) -o $@ $<
quiet_cmd_spu_as = SPU_AS $@
$(obj)/spu_%.o: $(src)/spu_%.S
$(call if_changed,spu_as)
# Link SPU Executables
cmd_spu_ld = $(SPU_LD) $(SPU_LDFLAGS) -o $@ $^
quiet_cmd_spu_ld = SPU_LD $@
$(obj)/spu_%: $(obj)/spu_%_crt0.o $(obj)/spu_%.o
$(call if_changed,spu_ld)
# Copy into binary format
cmd_spu_objcopy = $(SPU_OBJCOPY) -O binary $< $@
quiet_cmd_spu_objcopy = OBJCOPY $@
$(obj)/spu_%.bin: $(src)/spu_%
$(call if_changed,spu_objcopy)
# create C code from ELF executable
cmd_hexdump = ( \
echo "/*" ; \
echo " * $*_dump.h: Copyright (C) 2005 IBM." ; \
echo " * Hex-dump auto generated from $*.c." ; \
echo " * Do not edit!" ; \
echo " */" ; \
echo "static unsigned int $*_code[] " \
"__attribute__((__aligned__(128))) = {" ; \
hexdump -v -e '"0x" 4/1 "%02x" "," "\n"' $< ; \
echo "};" ; \
) > $@
quiet_cmd_hexdump = HEXDUMP $@
$(obj)/%_dump.h: $(obj)/%.bin
$(call if_changed,hexdump)

View File

@@ -0,0 +1,414 @@
/* backing_ops.c - query/set operations on saved SPU context.
*
* Copyright (C) IBM 2005
* Author: Mark Nutter <mnutter@us.ibm.com>
*
* These register operations allow SPUFS to operate on saved
* SPU contexts rather than hardware.
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <linux/poll.h>
#include <asm/io.h>
#include <asm/spu.h>
#include <asm/spu_csa.h>
#include <asm/spu_info.h>
#include <asm/mmu_context.h>
#include "spufs.h"
/*
* Reads/writes to various problem and priv2 registers require
* state changes, i.e. generate SPU events, modify channel
* counts, etc.
*/
static void gen_spu_event(struct spu_context *ctx, u32 event)
{
u64 ch0_cnt;
u64 ch0_data;
u64 ch1_data;
ch0_cnt = ctx->csa.spu_chnlcnt_RW[0];
ch0_data = ctx->csa.spu_chnldata_RW[0];
ch1_data = ctx->csa.spu_chnldata_RW[1];
ctx->csa.spu_chnldata_RW[0] |= event;
if ((ch0_cnt == 0) && !(ch0_data & event) && (ch1_data & event)) {
ctx->csa.spu_chnlcnt_RW[0] = 1;
}
}
static int spu_backing_mbox_read(struct spu_context *ctx, u32 * data)
{
u32 mbox_stat;
int ret = 0;
spin_lock(&ctx->csa.register_lock);
mbox_stat = ctx->csa.prob.mb_stat_R;
if (mbox_stat & 0x0000ff) {
/* Read the first available word.
* Implementation note: the depth
* of pu_mb_R is currently 1.
*/
*data = ctx->csa.prob.pu_mb_R;
ctx->csa.prob.mb_stat_R &= ~(0x0000ff);
ctx->csa.spu_chnlcnt_RW[28] = 1;
gen_spu_event(ctx, MFC_PU_MAILBOX_AVAILABLE_EVENT);
ret = 4;
}
spin_unlock(&ctx->csa.register_lock);
return ret;
}
static u32 spu_backing_mbox_stat_read(struct spu_context *ctx)
{
return ctx->csa.prob.mb_stat_R;
}
static unsigned int spu_backing_mbox_stat_poll(struct spu_context *ctx,
unsigned int events)
{
int ret;
u32 stat;
ret = 0;
spin_lock_irq(&ctx->csa.register_lock);
stat = ctx->csa.prob.mb_stat_R;
/* if the requested event is there, return the poll
mask, otherwise enable the interrupt to get notified,
but first mark any pending interrupts as done so
we don't get woken up unnecessarily */
if (events & (POLLIN | POLLRDNORM)) {
if (stat & 0xff0000)
ret |= POLLIN | POLLRDNORM;
else {
ctx->csa.priv1.int_stat_class2_RW &=
~CLASS2_MAILBOX_INTR;
ctx->csa.priv1.int_mask_class2_RW |=
CLASS2_ENABLE_MAILBOX_INTR;
}
}
if (events & (POLLOUT | POLLWRNORM)) {
if (stat & 0x00ff00)
ret = POLLOUT | POLLWRNORM;
else {
ctx->csa.priv1.int_stat_class2_RW &=
~CLASS2_MAILBOX_THRESHOLD_INTR;
ctx->csa.priv1.int_mask_class2_RW |=
CLASS2_ENABLE_MAILBOX_THRESHOLD_INTR;
}
}
spin_unlock_irq(&ctx->csa.register_lock);
return ret;
}
static int spu_backing_ibox_read(struct spu_context *ctx, u32 * data)
{
int ret;
spin_lock(&ctx->csa.register_lock);
if (ctx->csa.prob.mb_stat_R & 0xff0000) {
/* Read the first available word.
* Implementation note: the depth
* of puint_mb_R is currently 1.
*/
*data = ctx->csa.priv2.puint_mb_R;
ctx->csa.prob.mb_stat_R &= ~(0xff0000);
ctx->csa.spu_chnlcnt_RW[30] = 1;
gen_spu_event(ctx, MFC_PU_INT_MAILBOX_AVAILABLE_EVENT);
ret = 4;
} else {
/* make sure we get woken up by the interrupt */
ctx->csa.priv1.int_mask_class2_RW |= CLASS2_ENABLE_MAILBOX_INTR;
ret = 0;
}
spin_unlock(&ctx->csa.register_lock);
return ret;
}
static int spu_backing_wbox_write(struct spu_context *ctx, u32 data)
{
int ret;
spin_lock(&ctx->csa.register_lock);
if ((ctx->csa.prob.mb_stat_R) & 0x00ff00) {
int slot = ctx->csa.spu_chnlcnt_RW[29];
int avail = (ctx->csa.prob.mb_stat_R & 0x00ff00) >> 8;
/* We have space to write wbox_data.
* Implementation note: the depth
* of spu_mb_W is currently 4.
*/
BUG_ON(avail != (4 - slot));
ctx->csa.spu_mailbox_data[slot] = data;
ctx->csa.spu_chnlcnt_RW[29] = ++slot;
ctx->csa.prob.mb_stat_R &= ~(0x00ff00);
ctx->csa.prob.mb_stat_R |= (((4 - slot) & 0xff) << 8);
gen_spu_event(ctx, MFC_SPU_MAILBOX_WRITTEN_EVENT);
ret = 4;
} else {
/* make sure we get woken up by the interrupt when space
becomes available */
ctx->csa.priv1.int_mask_class2_RW |=
CLASS2_ENABLE_MAILBOX_THRESHOLD_INTR;
ret = 0;
}
spin_unlock(&ctx->csa.register_lock);
return ret;
}
static u32 spu_backing_signal1_read(struct spu_context *ctx)
{
return ctx->csa.spu_chnldata_RW[3];
}
static void spu_backing_signal1_write(struct spu_context *ctx, u32 data)
{
spin_lock(&ctx->csa.register_lock);
if (ctx->csa.priv2.spu_cfg_RW & 0x1)
ctx->csa.spu_chnldata_RW[3] |= data;
else
ctx->csa.spu_chnldata_RW[3] = data;
ctx->csa.spu_chnlcnt_RW[3] = 1;
gen_spu_event(ctx, MFC_SIGNAL_1_EVENT);
spin_unlock(&ctx->csa.register_lock);
}
static u32 spu_backing_signal2_read(struct spu_context *ctx)
{
return ctx->csa.spu_chnldata_RW[4];
}
static void spu_backing_signal2_write(struct spu_context *ctx, u32 data)
{
spin_lock(&ctx->csa.register_lock);
if (ctx->csa.priv2.spu_cfg_RW & 0x2)
ctx->csa.spu_chnldata_RW[4] |= data;
else
ctx->csa.spu_chnldata_RW[4] = data;
ctx->csa.spu_chnlcnt_RW[4] = 1;
gen_spu_event(ctx, MFC_SIGNAL_2_EVENT);
spin_unlock(&ctx->csa.register_lock);
}
static void spu_backing_signal1_type_set(struct spu_context *ctx, u64 val)
{
u64 tmp;
spin_lock(&ctx->csa.register_lock);
tmp = ctx->csa.priv2.spu_cfg_RW;
if (val)
tmp |= 1;
else
tmp &= ~1;
ctx->csa.priv2.spu_cfg_RW = tmp;
spin_unlock(&ctx->csa.register_lock);
}
static u64 spu_backing_signal1_type_get(struct spu_context *ctx)
{
return ((ctx->csa.priv2.spu_cfg_RW & 1) != 0);
}
static void spu_backing_signal2_type_set(struct spu_context *ctx, u64 val)
{
u64 tmp;
spin_lock(&ctx->csa.register_lock);
tmp = ctx->csa.priv2.spu_cfg_RW;
if (val)
tmp |= 2;
else
tmp &= ~2;
ctx->csa.priv2.spu_cfg_RW = tmp;
spin_unlock(&ctx->csa.register_lock);
}
static u64 spu_backing_signal2_type_get(struct spu_context *ctx)
{
return ((ctx->csa.priv2.spu_cfg_RW & 2) != 0);
}
static u32 spu_backing_npc_read(struct spu_context *ctx)
{
return ctx->csa.prob.spu_npc_RW;
}
static void spu_backing_npc_write(struct spu_context *ctx, u32 val)
{
ctx->csa.prob.spu_npc_RW = val;
}
static u32 spu_backing_status_read(struct spu_context *ctx)
{
return ctx->csa.prob.spu_status_R;
}
static char *spu_backing_get_ls(struct spu_context *ctx)
{
return ctx->csa.lscsa->ls;
}
static void spu_backing_privcntl_write(struct spu_context *ctx, u64 val)
{
ctx->csa.priv2.spu_privcntl_RW = val;
}
static u32 spu_backing_runcntl_read(struct spu_context *ctx)
{
return ctx->csa.prob.spu_runcntl_RW;
}
static void spu_backing_runcntl_write(struct spu_context *ctx, u32 val)
{
spin_lock(&ctx->csa.register_lock);
ctx->csa.prob.spu_runcntl_RW = val;
if (val & SPU_RUNCNTL_RUNNABLE) {
ctx->csa.prob.spu_status_R &=
~SPU_STATUS_STOPPED_BY_STOP &
~SPU_STATUS_STOPPED_BY_HALT &
~SPU_STATUS_SINGLE_STEP &
~SPU_STATUS_INVALID_INSTR &
~SPU_STATUS_INVALID_CH;
ctx->csa.prob.spu_status_R |= SPU_STATUS_RUNNING;
} else {
ctx->csa.prob.spu_status_R &= ~SPU_STATUS_RUNNING;
}
spin_unlock(&ctx->csa.register_lock);
}
static void spu_backing_runcntl_stop(struct spu_context *ctx)
{
spu_backing_runcntl_write(ctx, SPU_RUNCNTL_STOP);
}
static void spu_backing_master_start(struct spu_context *ctx)
{
struct spu_state *csa = &ctx->csa;
u64 sr1;
spin_lock(&csa->register_lock);
sr1 = csa->priv1.mfc_sr1_RW | MFC_STATE1_MASTER_RUN_CONTROL_MASK;
csa->priv1.mfc_sr1_RW = sr1;
spin_unlock(&csa->register_lock);
}
static void spu_backing_master_stop(struct spu_context *ctx)
{
struct spu_state *csa = &ctx->csa;
u64 sr1;
spin_lock(&csa->register_lock);
sr1 = csa->priv1.mfc_sr1_RW & ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
csa->priv1.mfc_sr1_RW = sr1;
spin_unlock(&csa->register_lock);
}
static int spu_backing_set_mfc_query(struct spu_context * ctx, u32 mask,
u32 mode)
{
struct spu_problem_collapsed *prob = &ctx->csa.prob;
int ret;
spin_lock(&ctx->csa.register_lock);
ret = -EAGAIN;
if (prob->dma_querytype_RW)
goto out;
ret = 0;
/* FIXME: what are the side-effects of this? */
prob->dma_querymask_RW = mask;
prob->dma_querytype_RW = mode;
/* In the current implementation, the SPU context is always
* acquired in runnable state when new bits are added to the
* mask (tagwait), so it's sufficient just to mask
* dma_tagstatus_R with the 'mask' parameter here.
*/
ctx->csa.prob.dma_tagstatus_R &= mask;
out:
spin_unlock(&ctx->csa.register_lock);
return ret;
}
static u32 spu_backing_read_mfc_tagstatus(struct spu_context * ctx)
{
return ctx->csa.prob.dma_tagstatus_R;
}
static u32 spu_backing_get_mfc_free_elements(struct spu_context *ctx)
{
return ctx->csa.prob.dma_qstatus_R;
}
static int spu_backing_send_mfc_command(struct spu_context *ctx,
struct mfc_dma_command *cmd)
{
int ret;
spin_lock(&ctx->csa.register_lock);
ret = -EAGAIN;
/* FIXME: set up priv2->puq */
spin_unlock(&ctx->csa.register_lock);
return ret;
}
static void spu_backing_restart_dma(struct spu_context *ctx)
{
ctx->csa.priv2.mfc_control_RW |= MFC_CNTL_RESTART_DMA_COMMAND;
}
struct spu_context_ops spu_backing_ops = {
.mbox_read = spu_backing_mbox_read,
.mbox_stat_read = spu_backing_mbox_stat_read,
.mbox_stat_poll = spu_backing_mbox_stat_poll,
.ibox_read = spu_backing_ibox_read,
.wbox_write = spu_backing_wbox_write,
.signal1_read = spu_backing_signal1_read,
.signal1_write = spu_backing_signal1_write,
.signal2_read = spu_backing_signal2_read,
.signal2_write = spu_backing_signal2_write,
.signal1_type_set = spu_backing_signal1_type_set,
.signal1_type_get = spu_backing_signal1_type_get,
.signal2_type_set = spu_backing_signal2_type_set,
.signal2_type_get = spu_backing_signal2_type_get,
.npc_read = spu_backing_npc_read,
.npc_write = spu_backing_npc_write,
.status_read = spu_backing_status_read,
.get_ls = spu_backing_get_ls,
.privcntl_write = spu_backing_privcntl_write,
.runcntl_read = spu_backing_runcntl_read,
.runcntl_write = spu_backing_runcntl_write,
.runcntl_stop = spu_backing_runcntl_stop,
.master_start = spu_backing_master_start,
.master_stop = spu_backing_master_stop,
.set_mfc_query = spu_backing_set_mfc_query,
.read_mfc_tagstatus = spu_backing_read_mfc_tagstatus,
.get_mfc_free_elements = spu_backing_get_mfc_free_elements,
.send_mfc_command = spu_backing_send_mfc_command,
.restart_dma = spu_backing_restart_dma,
};

View File

@@ -0,0 +1,188 @@
/*
* SPU file system -- SPU context management
*
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005
*
* Author: Arnd Bergmann <arndb@de.ibm.com>
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <asm/atomic.h>
#include <asm/spu.h>
#include <asm/spu_csa.h>
#include "spufs.h"
#include "sputrace.h"
atomic_t nr_spu_contexts = ATOMIC_INIT(0);
struct spu_context *alloc_spu_context(struct spu_gang *gang)
{
struct spu_context *ctx;
struct timespec ts;
ctx = kzalloc(sizeof *ctx, GFP_KERNEL);
if (!ctx)
goto out;
/* Binding to physical processor deferred
* until spu_activate().
*/
if (spu_init_csa(&ctx->csa))
goto out_free;
spin_lock_init(&ctx->mmio_lock);
mutex_init(&ctx->mapping_lock);
kref_init(&ctx->kref);
mutex_init(&ctx->state_mutex);
mutex_init(&ctx->run_mutex);
init_waitqueue_head(&ctx->ibox_wq);
init_waitqueue_head(&ctx->wbox_wq);
init_waitqueue_head(&ctx->stop_wq);
init_waitqueue_head(&ctx->mfc_wq);
init_waitqueue_head(&ctx->run_wq);
ctx->state = SPU_STATE_SAVED;
ctx->ops = &spu_backing_ops;
ctx->owner = get_task_mm(current);
INIT_LIST_HEAD(&ctx->rq);
INIT_LIST_HEAD(&ctx->aff_list);
if (gang)
spu_gang_add_ctx(gang, ctx);
__spu_update_sched_info(ctx);
spu_set_timeslice(ctx);
ctx->stats.util_state = SPU_UTIL_IDLE_LOADED;
ktime_get_ts(&ts);
ctx->stats.tstamp = timespec_to_ns(&ts);
atomic_inc(&nr_spu_contexts);
goto out;
out_free:
kfree(ctx);
ctx = NULL;
out:
return ctx;
}
void destroy_spu_context(struct kref *kref)
{
struct spu_context *ctx;
ctx = container_of(kref, struct spu_context, kref);
spu_context_nospu_trace(destroy_spu_context__enter, ctx);
mutex_lock(&ctx->state_mutex);
spu_deactivate(ctx);
mutex_unlock(&ctx->state_mutex);
spu_fini_csa(&ctx->csa);
if (ctx->gang)
spu_gang_remove_ctx(ctx->gang, ctx);
if (ctx->prof_priv_kref)
kref_put(ctx->prof_priv_kref, ctx->prof_priv_release);
BUG_ON(!list_empty(&ctx->rq));
atomic_dec(&nr_spu_contexts);
kfree(ctx->switch_log);
kfree(ctx);
}
struct spu_context * get_spu_context(struct spu_context *ctx)
{
kref_get(&ctx->kref);
return ctx;
}
int put_spu_context(struct spu_context *ctx)
{
return kref_put(&ctx->kref, &destroy_spu_context);
}
/* give up the mm reference when the context is about to be destroyed */
void spu_forget(struct spu_context *ctx)
{
struct mm_struct *mm;
/*
* This is basically an open-coded spu_acquire_saved, except that
* we don't acquire the state mutex interruptible, and we don't
* want this context to be rescheduled on release.
*/
mutex_lock(&ctx->state_mutex);
if (ctx->state != SPU_STATE_SAVED)
spu_deactivate(ctx);
mm = ctx->owner;
ctx->owner = NULL;
mmput(mm);
spu_release(ctx);
}
void spu_unmap_mappings(struct spu_context *ctx)
{
mutex_lock(&ctx->mapping_lock);
if (ctx->local_store)
unmap_mapping_range(ctx->local_store, 0, LS_SIZE, 1);
if (ctx->mfc)
unmap_mapping_range(ctx->mfc, 0, SPUFS_MFC_MAP_SIZE, 1);
if (ctx->cntl)
unmap_mapping_range(ctx->cntl, 0, SPUFS_CNTL_MAP_SIZE, 1);
if (ctx->signal1)
unmap_mapping_range(ctx->signal1, 0, SPUFS_SIGNAL_MAP_SIZE, 1);
if (ctx->signal2)
unmap_mapping_range(ctx->signal2, 0, SPUFS_SIGNAL_MAP_SIZE, 1);
if (ctx->mss)
unmap_mapping_range(ctx->mss, 0, SPUFS_MSS_MAP_SIZE, 1);
if (ctx->psmap)
unmap_mapping_range(ctx->psmap, 0, SPUFS_PS_MAP_SIZE, 1);
mutex_unlock(&ctx->mapping_lock);
}
/**
* spu_acquire_saved - lock spu contex and make sure it is in saved state
* @ctx: spu contex to lock
*/
int spu_acquire_saved(struct spu_context *ctx)
{
int ret;
spu_context_nospu_trace(spu_acquire_saved__enter, ctx);
ret = spu_acquire(ctx);
if (ret)
return ret;
if (ctx->state != SPU_STATE_SAVED) {
set_bit(SPU_SCHED_WAS_ACTIVE, &ctx->sched_flags);
spu_deactivate(ctx);
}
return 0;
}
/**
* spu_release_saved - unlock spu context and return it to the runqueue
* @ctx: context to unlock
*/
void spu_release_saved(struct spu_context *ctx)
{
BUG_ON(ctx->state != SPU_STATE_SAVED);
if (test_and_clear_bit(SPU_SCHED_WAS_ACTIVE, &ctx->sched_flags) &&
test_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags))
spu_activate(ctx, 0);
spu_release(ctx);
}

View File

@@ -0,0 +1,250 @@
/*
* SPU core dump code
*
* (C) Copyright 2006 IBM Corp.
*
* Author: Dwayne Grant McConnell <decimal@us.ibm.com>
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/elf.h>
#include <linux/file.h>
#include <linux/fdtable.h>
#include <linux/fs.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/syscalls.h>
#include <asm/uaccess.h>
#include "spufs.h"
static ssize_t do_coredump_read(int num, struct spu_context *ctx, void *buffer,
size_t size, loff_t *off)
{
u64 data;
int ret;
if (spufs_coredump_read[num].read)
return spufs_coredump_read[num].read(ctx, buffer, size, off);
data = spufs_coredump_read[num].get(ctx);
ret = snprintf(buffer, size, "0x%.16llx", data);
if (ret >= size)
return size;
return ++ret; /* count trailing NULL */
}
/*
* These are the only things you should do on a core-file: use only these
* functions to write out all the necessary info.
*/
static int spufs_dump_write(struct file *file, const void *addr, int nr, loff_t *foffset)
{
unsigned long limit = current->signal->rlim[RLIMIT_CORE].rlim_cur;
ssize_t written;
if (*foffset + nr > limit)
return -EIO;
written = file->f_op->write(file, addr, nr, &file->f_pos);
*foffset += written;
if (written != nr)
return -EIO;
return 0;
}
static int spufs_dump_align(struct file *file, char *buf, loff_t new_off,
loff_t *foffset)
{
int rc, size;
size = min((loff_t)PAGE_SIZE, new_off - *foffset);
memset(buf, 0, size);
rc = 0;
while (rc == 0 && new_off > *foffset) {
size = min((loff_t)PAGE_SIZE, new_off - *foffset);
rc = spufs_dump_write(file, buf, size, foffset);
}
return rc;
}
static int spufs_ctx_note_size(struct spu_context *ctx, int dfd)
{
int i, sz, total = 0;
char *name;
char fullname[80];
for (i = 0; spufs_coredump_read[i].name != NULL; i++) {
name = spufs_coredump_read[i].name;
sz = spufs_coredump_read[i].size;
sprintf(fullname, "SPU/%d/%s", dfd, name);
total += sizeof(struct elf_note);
total += roundup(strlen(fullname) + 1, 4);
total += roundup(sz, 4);
}
return total;
}
/*
* The additional architecture-specific notes for Cell are various
* context files in the spu context.
*
* This function iterates over all open file descriptors and sees
* if they are a directory in spufs. In that case we use spufs
* internal functionality to dump them without needing to actually
* open the files.
*/
static struct spu_context *coredump_next_context(int *fd)
{
struct fdtable *fdt = files_fdtable(current->files);
struct file *file;
struct spu_context *ctx = NULL;
for (; *fd < fdt->max_fds; (*fd)++) {
if (!FD_ISSET(*fd, fdt->open_fds))
continue;
file = fcheck(*fd);
if (!file || file->f_op != &spufs_context_fops)
continue;
ctx = SPUFS_I(file->f_dentry->d_inode)->i_ctx;
if (ctx->flags & SPU_CREATE_NOSCHED)
continue;
break;
}
return ctx;
}
int spufs_coredump_extra_notes_size(void)
{
struct spu_context *ctx;
int size = 0, rc, fd;
fd = 0;
while ((ctx = coredump_next_context(&fd)) != NULL) {
rc = spu_acquire_saved(ctx);
if (rc)
break;
rc = spufs_ctx_note_size(ctx, fd);
spu_release_saved(ctx);
if (rc < 0)
break;
size += rc;
/* start searching the next fd next time */
fd++;
}
return size;
}
static int spufs_arch_write_note(struct spu_context *ctx, int i,
struct file *file, int dfd, loff_t *foffset)
{
loff_t pos = 0;
int sz, rc, nread, total = 0;
const int bufsz = PAGE_SIZE;
char *name;
char fullname[80], *buf;
struct elf_note en;
buf = (void *)get_zeroed_page(GFP_KERNEL);
if (!buf)
return -ENOMEM;
name = spufs_coredump_read[i].name;
sz = spufs_coredump_read[i].size;
sprintf(fullname, "SPU/%d/%s", dfd, name);
en.n_namesz = strlen(fullname) + 1;
en.n_descsz = sz;
en.n_type = NT_SPU;
rc = spufs_dump_write(file, &en, sizeof(en), foffset);
if (rc)
goto out;
rc = spufs_dump_write(file, fullname, en.n_namesz, foffset);
if (rc)
goto out;
rc = spufs_dump_align(file, buf, roundup(*foffset, 4), foffset);
if (rc)
goto out;
do {
nread = do_coredump_read(i, ctx, buf, bufsz, &pos);
if (nread > 0) {
rc = spufs_dump_write(file, buf, nread, foffset);
if (rc)
goto out;
total += nread;
}
} while (nread == bufsz && total < sz);
if (nread < 0) {
rc = nread;
goto out;
}
rc = spufs_dump_align(file, buf, roundup(*foffset - total + sz, 4),
foffset);
out:
free_page((unsigned long)buf);
return rc;
}
int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset)
{
struct spu_context *ctx;
int fd, j, rc;
fd = 0;
while ((ctx = coredump_next_context(&fd)) != NULL) {
rc = spu_acquire_saved(ctx);
if (rc)
return rc;
for (j = 0; spufs_coredump_read[j].name != NULL; j++) {
rc = spufs_arch_write_note(ctx, j, file, fd, foffset);
if (rc) {
spu_release_saved(ctx);
return rc;
}
}
spu_release_saved(ctx);
/* start searching the next fd next time */
fd++;
}
return 0;
}

View File

@@ -0,0 +1,192 @@
/*
* Low-level SPU handling
*
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005
*
* Author: Arnd Bergmann <arndb@de.ibm.com>
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/module.h>
#include <asm/spu.h>
#include <asm/spu_csa.h>
#include "spufs.h"
/**
* Handle an SPE event, depending on context SPU_CREATE_EVENTS_ENABLED flag.
*
* If the context was created with events, we just set the return event.
* Otherwise, send an appropriate signal to the process.
*/
static void spufs_handle_event(struct spu_context *ctx,
unsigned long ea, int type)
{
siginfo_t info;
if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) {
ctx->event_return |= type;
wake_up_all(&ctx->stop_wq);
return;
}
memset(&info, 0, sizeof(info));
switch (type) {
case SPE_EVENT_INVALID_DMA:
info.si_signo = SIGBUS;
info.si_code = BUS_OBJERR;
break;
case SPE_EVENT_SPE_DATA_STORAGE:
info.si_signo = SIGSEGV;
info.si_addr = (void __user *)ea;
info.si_code = SEGV_ACCERR;
ctx->ops->restart_dma(ctx);
break;
case SPE_EVENT_DMA_ALIGNMENT:
info.si_signo = SIGBUS;
/* DAR isn't set for an alignment fault :( */
info.si_code = BUS_ADRALN;
break;
case SPE_EVENT_SPE_ERROR:
info.si_signo = SIGILL;
info.si_addr = (void __user *)(unsigned long)
ctx->ops->npc_read(ctx) - 4;
info.si_code = ILL_ILLOPC;
break;
}
if (info.si_signo)
force_sig_info(info.si_signo, &info, current);
}
int spufs_handle_class0(struct spu_context *ctx)
{
unsigned long stat = ctx->csa.class_0_pending & CLASS0_INTR_MASK;
if (likely(!stat))
return 0;
if (stat & CLASS0_DMA_ALIGNMENT_INTR)
spufs_handle_event(ctx, ctx->csa.class_0_dar,
SPE_EVENT_DMA_ALIGNMENT);
if (stat & CLASS0_INVALID_DMA_COMMAND_INTR)
spufs_handle_event(ctx, ctx->csa.class_0_dar,
SPE_EVENT_INVALID_DMA);
if (stat & CLASS0_SPU_ERROR_INTR)
spufs_handle_event(ctx, ctx->csa.class_0_dar,
SPE_EVENT_SPE_ERROR);
ctx->csa.class_0_pending = 0;
return -EIO;
}
/*
* bottom half handler for page faults, we can't do this from
* interrupt context, since we might need to sleep.
* we also need to give up the mutex so we can get scheduled
* out while waiting for the backing store.
*
* TODO: try calling hash_page from the interrupt handler first
* in order to speed up the easy case.
*/
int spufs_handle_class1(struct spu_context *ctx)
{
u64 ea, dsisr, access;
unsigned long flags;
unsigned flt = 0;
int ret;
/*
* dar and dsisr get passed from the registers
* to the spu_context, to this function, but not
* back to the spu if it gets scheduled again.
*
* if we don't handle the fault for a saved context
* in time, we can still expect to get the same fault
* the immediately after the context restore.
*/
ea = ctx->csa.class_1_dar;
dsisr = ctx->csa.class_1_dsisr;
if (!(dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED)))
return 0;
spuctx_switch_state(ctx, SPU_UTIL_IOWAIT);
pr_debug("ctx %p: ea %016llx, dsisr %016llx state %d\n", ctx, ea,
dsisr, ctx->state);
ctx->stats.hash_flt++;
if (ctx->state == SPU_STATE_RUNNABLE)
ctx->spu->stats.hash_flt++;
/* we must not hold the lock when entering spu_handle_mm_fault */
spu_release(ctx);
access = (_PAGE_PRESENT | _PAGE_USER);
access |= (dsisr & MFC_DSISR_ACCESS_PUT) ? _PAGE_RW : 0UL;
local_irq_save(flags);
ret = hash_page(ea, access, 0x300);
local_irq_restore(flags);
/* hashing failed, so try the actual fault handler */
if (ret)
ret = spu_handle_mm_fault(current->mm, ea, dsisr, &flt);
/*
* This is nasty: we need the state_mutex for all the bookkeeping even
* if the syscall was interrupted by a signal. ewww.
*/
mutex_lock(&ctx->state_mutex);
/*
* Clear dsisr under ctxt lock after handling the fault, so that
* time slicing will not preempt the context while the page fault
* handler is running. Context switch code removes mappings.
*/
ctx->csa.class_1_dar = ctx->csa.class_1_dsisr = 0;
/*
* If we handled the fault successfully and are in runnable
* state, restart the DMA.
* In case of unhandled error report the problem to user space.
*/
if (!ret) {
if (flt & VM_FAULT_MAJOR)
ctx->stats.maj_flt++;
else
ctx->stats.min_flt++;
if (ctx->state == SPU_STATE_RUNNABLE) {
if (flt & VM_FAULT_MAJOR)
ctx->spu->stats.maj_flt++;
else
ctx->spu->stats.min_flt++;
}
if (ctx->spu)
ctx->ops->restart_dma(ctx);
} else
spufs_handle_event(ctx, ea, SPE_EVENT_SPE_DATA_STORAGE);
spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,87 @@
/*
* SPU file system
*
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005
*
* Author: Arnd Bergmann <arndb@de.ibm.com>
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/list.h>
#include <linux/slab.h>
#include "spufs.h"
struct spu_gang *alloc_spu_gang(void)
{
struct spu_gang *gang;
gang = kzalloc(sizeof *gang, GFP_KERNEL);
if (!gang)
goto out;
kref_init(&gang->kref);
mutex_init(&gang->mutex);
mutex_init(&gang->aff_mutex);
INIT_LIST_HEAD(&gang->list);
INIT_LIST_HEAD(&gang->aff_list_head);
out:
return gang;
}
static void destroy_spu_gang(struct kref *kref)
{
struct spu_gang *gang;
gang = container_of(kref, struct spu_gang, kref);
WARN_ON(gang->contexts || !list_empty(&gang->list));
kfree(gang);
}
struct spu_gang *get_spu_gang(struct spu_gang *gang)
{
kref_get(&gang->kref);
return gang;
}
int put_spu_gang(struct spu_gang *gang)
{
return kref_put(&gang->kref, &destroy_spu_gang);
}
void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx)
{
mutex_lock(&gang->mutex);
ctx->gang = get_spu_gang(gang);
list_add(&ctx->gang_list, &gang->list);
gang->contexts++;
mutex_unlock(&gang->mutex);
}
void spu_gang_remove_ctx(struct spu_gang *gang, struct spu_context *ctx)
{
mutex_lock(&gang->mutex);
WARN_ON(ctx->gang != gang);
if (!list_empty(&ctx->aff_list)) {
list_del_init(&ctx->aff_list);
gang->aff_flags &= ~AFF_OFFSETS_SET;
}
list_del_init(&ctx->gang_list);
gang->contexts--;
mutex_unlock(&gang->mutex);
put_spu_gang(gang);
}

View File

@@ -0,0 +1,350 @@
/* hw_ops.c - query/set operations on active SPU context.
*
* Copyright (C) IBM 2005
* Author: Mark Nutter <mnutter@us.ibm.com>
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/poll.h>
#include <linux/smp.h>
#include <linux/stddef.h>
#include <linux/unistd.h>
#include <asm/io.h>
#include <asm/spu.h>
#include <asm/spu_priv1.h>
#include <asm/spu_csa.h>
#include <asm/mmu_context.h>
#include "spufs.h"
static int spu_hw_mbox_read(struct spu_context *ctx, u32 * data)
{
struct spu *spu = ctx->spu;
struct spu_problem __iomem *prob = spu->problem;
u32 mbox_stat;
int ret = 0;
spin_lock_irq(&spu->register_lock);
mbox_stat = in_be32(&prob->mb_stat_R);
if (mbox_stat & 0x0000ff) {
*data = in_be32(&prob->pu_mb_R);
ret = 4;
}
spin_unlock_irq(&spu->register_lock);
return ret;
}
static u32 spu_hw_mbox_stat_read(struct spu_context *ctx)
{
return in_be32(&ctx->spu->problem->mb_stat_R);
}
static unsigned int spu_hw_mbox_stat_poll(struct spu_context *ctx,
unsigned int events)
{
struct spu *spu = ctx->spu;
int ret = 0;
u32 stat;
spin_lock_irq(&spu->register_lock);
stat = in_be32(&spu->problem->mb_stat_R);
/* if the requested event is there, return the poll
mask, otherwise enable the interrupt to get notified,
but first mark any pending interrupts as done so
we don't get woken up unnecessarily */
if (events & (POLLIN | POLLRDNORM)) {
if (stat & 0xff0000)
ret |= POLLIN | POLLRDNORM;
else {
spu_int_stat_clear(spu, 2, CLASS2_MAILBOX_INTR);
spu_int_mask_or(spu, 2, CLASS2_ENABLE_MAILBOX_INTR);
}
}
if (events & (POLLOUT | POLLWRNORM)) {
if (stat & 0x00ff00)
ret = POLLOUT | POLLWRNORM;
else {
spu_int_stat_clear(spu, 2,
CLASS2_MAILBOX_THRESHOLD_INTR);
spu_int_mask_or(spu, 2,
CLASS2_ENABLE_MAILBOX_THRESHOLD_INTR);
}
}
spin_unlock_irq(&spu->register_lock);
return ret;
}
static int spu_hw_ibox_read(struct spu_context *ctx, u32 * data)
{
struct spu *spu = ctx->spu;
struct spu_problem __iomem *prob = spu->problem;
struct spu_priv2 __iomem *priv2 = spu->priv2;
int ret;
spin_lock_irq(&spu->register_lock);
if (in_be32(&prob->mb_stat_R) & 0xff0000) {
/* read the first available word */
*data = in_be64(&priv2->puint_mb_R);
ret = 4;
} else {
/* make sure we get woken up by the interrupt */
spu_int_mask_or(spu, 2, CLASS2_ENABLE_MAILBOX_INTR);
ret = 0;
}
spin_unlock_irq(&spu->register_lock);
return ret;
}
static int spu_hw_wbox_write(struct spu_context *ctx, u32 data)
{
struct spu *spu = ctx->spu;
struct spu_problem __iomem *prob = spu->problem;
int ret;
spin_lock_irq(&spu->register_lock);
if (in_be32(&prob->mb_stat_R) & 0x00ff00) {
/* we have space to write wbox_data to */
out_be32(&prob->spu_mb_W, data);
ret = 4;
} else {
/* make sure we get woken up by the interrupt when space
becomes available */
spu_int_mask_or(spu, 2, CLASS2_ENABLE_MAILBOX_THRESHOLD_INTR);
ret = 0;
}
spin_unlock_irq(&spu->register_lock);
return ret;
}
static void spu_hw_signal1_write(struct spu_context *ctx, u32 data)
{
out_be32(&ctx->spu->problem->signal_notify1, data);
}
static void spu_hw_signal2_write(struct spu_context *ctx, u32 data)
{
out_be32(&ctx->spu->problem->signal_notify2, data);
}
static void spu_hw_signal1_type_set(struct spu_context *ctx, u64 val)
{
struct spu *spu = ctx->spu;
struct spu_priv2 __iomem *priv2 = spu->priv2;
u64 tmp;
spin_lock_irq(&spu->register_lock);
tmp = in_be64(&priv2->spu_cfg_RW);
if (val)
tmp |= 1;
else
tmp &= ~1;
out_be64(&priv2->spu_cfg_RW, tmp);
spin_unlock_irq(&spu->register_lock);
}
static u64 spu_hw_signal1_type_get(struct spu_context *ctx)
{
return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 1) != 0);
}
static void spu_hw_signal2_type_set(struct spu_context *ctx, u64 val)
{
struct spu *spu = ctx->spu;
struct spu_priv2 __iomem *priv2 = spu->priv2;
u64 tmp;
spin_lock_irq(&spu->register_lock);
tmp = in_be64(&priv2->spu_cfg_RW);
if (val)
tmp |= 2;
else
tmp &= ~2;
out_be64(&priv2->spu_cfg_RW, tmp);
spin_unlock_irq(&spu->register_lock);
}
static u64 spu_hw_signal2_type_get(struct spu_context *ctx)
{
return ((in_be64(&ctx->spu->priv2->spu_cfg_RW) & 2) != 0);
}
static u32 spu_hw_npc_read(struct spu_context *ctx)
{
return in_be32(&ctx->spu->problem->spu_npc_RW);
}
static void spu_hw_npc_write(struct spu_context *ctx, u32 val)
{
out_be32(&ctx->spu->problem->spu_npc_RW, val);
}
static u32 spu_hw_status_read(struct spu_context *ctx)
{
return in_be32(&ctx->spu->problem->spu_status_R);
}
static char *spu_hw_get_ls(struct spu_context *ctx)
{
return ctx->spu->local_store;
}
static void spu_hw_privcntl_write(struct spu_context *ctx, u64 val)
{
out_be64(&ctx->spu->priv2->spu_privcntl_RW, val);
}
static u32 spu_hw_runcntl_read(struct spu_context *ctx)
{
return in_be32(&ctx->spu->problem->spu_runcntl_RW);
}
static void spu_hw_runcntl_write(struct spu_context *ctx, u32 val)
{
spin_lock_irq(&ctx->spu->register_lock);
if (val & SPU_RUNCNTL_ISOLATE)
spu_hw_privcntl_write(ctx,
SPU_PRIVCNT_LOAD_REQUEST_ENABLE_MASK);
out_be32(&ctx->spu->problem->spu_runcntl_RW, val);
spin_unlock_irq(&ctx->spu->register_lock);
}
static void spu_hw_runcntl_stop(struct spu_context *ctx)
{
spin_lock_irq(&ctx->spu->register_lock);
out_be32(&ctx->spu->problem->spu_runcntl_RW, SPU_RUNCNTL_STOP);
while (in_be32(&ctx->spu->problem->spu_status_R) & SPU_STATUS_RUNNING)
cpu_relax();
spin_unlock_irq(&ctx->spu->register_lock);
}
static void spu_hw_master_start(struct spu_context *ctx)
{
struct spu *spu = ctx->spu;
u64 sr1;
spin_lock_irq(&spu->register_lock);
sr1 = spu_mfc_sr1_get(spu) | MFC_STATE1_MASTER_RUN_CONTROL_MASK;
spu_mfc_sr1_set(spu, sr1);
spin_unlock_irq(&spu->register_lock);
}
static void spu_hw_master_stop(struct spu_context *ctx)
{
struct spu *spu = ctx->spu;
u64 sr1;
spin_lock_irq(&spu->register_lock);
sr1 = spu_mfc_sr1_get(spu) & ~MFC_STATE1_MASTER_RUN_CONTROL_MASK;
spu_mfc_sr1_set(spu, sr1);
spin_unlock_irq(&spu->register_lock);
}
static int spu_hw_set_mfc_query(struct spu_context * ctx, u32 mask, u32 mode)
{
struct spu_problem __iomem *prob = ctx->spu->problem;
int ret;
spin_lock_irq(&ctx->spu->register_lock);
ret = -EAGAIN;
if (in_be32(&prob->dma_querytype_RW))
goto out;
ret = 0;
out_be32(&prob->dma_querymask_RW, mask);
out_be32(&prob->dma_querytype_RW, mode);
out:
spin_unlock_irq(&ctx->spu->register_lock);
return ret;
}
static u32 spu_hw_read_mfc_tagstatus(struct spu_context * ctx)
{
return in_be32(&ctx->spu->problem->dma_tagstatus_R);
}
static u32 spu_hw_get_mfc_free_elements(struct spu_context *ctx)
{
return in_be32(&ctx->spu->problem->dma_qstatus_R);
}
static int spu_hw_send_mfc_command(struct spu_context *ctx,
struct mfc_dma_command *cmd)
{
u32 status;
struct spu_problem __iomem *prob = ctx->spu->problem;
spin_lock_irq(&ctx->spu->register_lock);
out_be32(&prob->mfc_lsa_W, cmd->lsa);
out_be64(&prob->mfc_ea_W, cmd->ea);
out_be32(&prob->mfc_union_W.by32.mfc_size_tag32,
cmd->size << 16 | cmd->tag);
out_be32(&prob->mfc_union_W.by32.mfc_class_cmd32,
cmd->class << 16 | cmd->cmd);
status = in_be32(&prob->mfc_union_W.by32.mfc_class_cmd32);
spin_unlock_irq(&ctx->spu->register_lock);
switch (status & 0xffff) {
case 0:
return 0;
case 2:
return -EAGAIN;
default:
return -EINVAL;
}
}
static void spu_hw_restart_dma(struct spu_context *ctx)
{
struct spu_priv2 __iomem *priv2 = ctx->spu->priv2;
if (!test_bit(SPU_CONTEXT_SWITCH_PENDING, &ctx->spu->flags))
out_be64(&priv2->mfc_control_RW, MFC_CNTL_RESTART_DMA_COMMAND);
}
struct spu_context_ops spu_hw_ops = {
.mbox_read = spu_hw_mbox_read,
.mbox_stat_read = spu_hw_mbox_stat_read,
.mbox_stat_poll = spu_hw_mbox_stat_poll,
.ibox_read = spu_hw_ibox_read,
.wbox_write = spu_hw_wbox_write,
.signal1_write = spu_hw_signal1_write,
.signal2_write = spu_hw_signal2_write,
.signal1_type_set = spu_hw_signal1_type_set,
.signal1_type_get = spu_hw_signal1_type_get,
.signal2_type_set = spu_hw_signal2_type_set,
.signal2_type_get = spu_hw_signal2_type_get,
.npc_read = spu_hw_npc_read,
.npc_write = spu_hw_npc_write,
.status_read = spu_hw_status_read,
.get_ls = spu_hw_get_ls,
.privcntl_write = spu_hw_privcntl_write,
.runcntl_read = spu_hw_runcntl_read,
.runcntl_write = spu_hw_runcntl_write,
.runcntl_stop = spu_hw_runcntl_stop,
.master_start = spu_hw_master_start,
.master_stop = spu_hw_master_stop,
.set_mfc_query = spu_hw_set_mfc_query,
.read_mfc_tagstatus = spu_hw_read_mfc_tagstatus,
.get_mfc_free_elements = spu_hw_get_mfc_free_elements,
.send_mfc_command = spu_hw_send_mfc_command,
.restart_dma = spu_hw_restart_dma,
};

View File

@@ -0,0 +1,867 @@
/*
* SPU file system
*
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005
*
* Author: Arnd Bergmann <arndb@de.ibm.com>
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/fsnotify.h>
#include <linux/backing-dev.h>
#include <linux/init.h>
#include <linux/ioctl.h>
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <linux/pagemap.h>
#include <linux/poll.h>
#include <linux/slab.h>
#include <linux/parser.h>
#include <asm/prom.h>
#include <asm/spu.h>
#include <asm/spu_priv1.h>
#include <asm/uaccess.h>
#include "spufs.h"
struct spufs_sb_info {
int debug;
};
static struct kmem_cache *spufs_inode_cache;
char *isolated_loader;
static int isolated_loader_size;
static struct spufs_sb_info *spufs_get_sb_info(struct super_block *sb)
{
return sb->s_fs_info;
}
static struct inode *
spufs_alloc_inode(struct super_block *sb)
{
struct spufs_inode_info *ei;
ei = kmem_cache_alloc(spufs_inode_cache, GFP_KERNEL);
if (!ei)
return NULL;
ei->i_gang = NULL;
ei->i_ctx = NULL;
ei->i_openers = 0;
return &ei->vfs_inode;
}
static void
spufs_destroy_inode(struct inode *inode)
{
kmem_cache_free(spufs_inode_cache, SPUFS_I(inode));
}
static void
spufs_init_once(void *p)
{
struct spufs_inode_info *ei = p;
inode_init_once(&ei->vfs_inode);
}
static struct inode *
spufs_new_inode(struct super_block *sb, int mode)
{
struct inode *inode;
inode = new_inode(sb);
if (!inode)
goto out;
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;
out:
return inode;
}
static int
spufs_setattr(struct dentry *dentry, struct iattr *attr)
{
struct inode *inode = dentry->d_inode;
if ((attr->ia_valid & ATTR_SIZE) &&
(attr->ia_size != inode->i_size))
return -EINVAL;
return inode_setattr(inode, attr);
}
static int
spufs_new_file(struct super_block *sb, struct dentry *dentry,
const struct file_operations *fops, int mode,
size_t size, struct spu_context *ctx)
{
static const struct inode_operations spufs_file_iops = {
.setattr = spufs_setattr,
};
struct inode *inode;
int ret;
ret = -ENOSPC;
inode = spufs_new_inode(sb, S_IFREG | mode);
if (!inode)
goto out;
ret = 0;
inode->i_op = &spufs_file_iops;
inode->i_fop = fops;
inode->i_size = size;
inode->i_private = SPUFS_I(inode)->i_ctx = get_spu_context(ctx);
d_add(dentry, inode);
out:
return ret;
}
static void
spufs_delete_inode(struct inode *inode)
{
struct spufs_inode_info *ei = SPUFS_I(inode);
if (ei->i_ctx)
put_spu_context(ei->i_ctx);
if (ei->i_gang)
put_spu_gang(ei->i_gang);
clear_inode(inode);
}
static void spufs_prune_dir(struct dentry *dir)
{
struct dentry *dentry, *tmp;
mutex_lock(&dir->d_inode->i_mutex);
list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
spin_lock(&dcache_lock);
spin_lock(&dentry->d_lock);
if (!(d_unhashed(dentry)) && dentry->d_inode) {
dget_locked(dentry);
__d_drop(dentry);
spin_unlock(&dentry->d_lock);
simple_unlink(dir->d_inode, dentry);
spin_unlock(&dcache_lock);
dput(dentry);
} else {
spin_unlock(&dentry->d_lock);
spin_unlock(&dcache_lock);
}
}
shrink_dcache_parent(dir);
mutex_unlock(&dir->d_inode->i_mutex);
}
/* Caller must hold parent->i_mutex */
static int spufs_rmdir(struct inode *parent, struct dentry *dir)
{
/* remove all entries */
spufs_prune_dir(dir);
d_drop(dir);
return simple_rmdir(parent, dir);
}
static int spufs_fill_dir(struct dentry *dir,
const struct spufs_tree_descr *files, int mode,
struct spu_context *ctx)
{
struct dentry *dentry, *tmp;
int ret;
while (files->name && files->name[0]) {
ret = -ENOMEM;
dentry = d_alloc_name(dir, files->name);
if (!dentry)
goto out;
ret = spufs_new_file(dir->d_sb, dentry, files->ops,
files->mode & mode, files->size, ctx);
if (ret)
goto out;
files++;
}
return 0;
out:
/*
* remove all children from dir. dir->inode is not set so don't
* just simply use spufs_prune_dir() and panic afterwards :)
* dput() looks like it will do the right thing:
* - dec parent's ref counter
* - remove child from parent's child list
* - free child's inode if possible
* - free child
*/
list_for_each_entry_safe(dentry, tmp, &dir->d_subdirs, d_u.d_child) {
dput(dentry);
}
shrink_dcache_parent(dir);
return ret;
}
static int spufs_dir_close(struct inode *inode, struct file *file)
{
struct spu_context *ctx;
struct inode *parent;
struct dentry *dir;
int ret;
dir = file->f_path.dentry;
parent = dir->d_parent->d_inode;
ctx = SPUFS_I(dir->d_inode)->i_ctx;
mutex_lock_nested(&parent->i_mutex, I_MUTEX_PARENT);
ret = spufs_rmdir(parent, dir);
mutex_unlock(&parent->i_mutex);
WARN_ON(ret);
/* We have to give up the mm_struct */
spu_forget(ctx);
return dcache_dir_close(inode, file);
}
const struct file_operations spufs_context_fops = {
.open = dcache_dir_open,
.release = spufs_dir_close,
.llseek = dcache_dir_lseek,
.read = generic_read_dir,
.readdir = dcache_readdir,
.fsync = simple_sync_file,
};
EXPORT_SYMBOL_GPL(spufs_context_fops);
static int
spufs_mkdir(struct inode *dir, struct dentry *dentry, unsigned int flags,
int mode)
{
int ret;
struct inode *inode;
struct spu_context *ctx;
ret = -ENOSPC;
inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
if (!inode)
goto out;
if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
inode->i_mode &= S_ISGID;
}
ctx = alloc_spu_context(SPUFS_I(dir)->i_gang); /* XXX gang */
SPUFS_I(inode)->i_ctx = ctx;
if (!ctx)
goto out_iput;
ctx->flags = flags;
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
if (flags & SPU_CREATE_NOSCHED)
ret = spufs_fill_dir(dentry, spufs_dir_nosched_contents,
mode, ctx);
else
ret = spufs_fill_dir(dentry, spufs_dir_contents, mode, ctx);
if (ret)
goto out_free_ctx;
if (spufs_get_sb_info(dir->i_sb)->debug)
ret = spufs_fill_dir(dentry, spufs_dir_debug_contents,
mode, ctx);
if (ret)
goto out_free_ctx;
d_instantiate(dentry, inode);
dget(dentry);
inc_nlink(dir);
inc_nlink(dentry->d_inode);
goto out;
out_free_ctx:
spu_forget(ctx);
put_spu_context(ctx);
out_iput:
iput(inode);
out:
return ret;
}
static int spufs_context_open(struct dentry *dentry, struct vfsmount *mnt)
{
int ret;
struct file *filp;
ret = get_unused_fd();
if (ret < 0) {
dput(dentry);
mntput(mnt);
goto out;
}
filp = dentry_open(dentry, mnt, O_RDONLY, current_cred());
if (IS_ERR(filp)) {
put_unused_fd(ret);
ret = PTR_ERR(filp);
goto out;
}
filp->f_op = &spufs_context_fops;
fd_install(ret, filp);
out:
return ret;
}
static struct spu_context *
spufs_assert_affinity(unsigned int flags, struct spu_gang *gang,
struct file *filp)
{
struct spu_context *tmp, *neighbor, *err;
int count, node;
int aff_supp;
aff_supp = !list_empty(&(list_entry(cbe_spu_info[0].spus.next,
struct spu, cbe_list))->aff_list);
if (!aff_supp)
return ERR_PTR(-EINVAL);
if (flags & SPU_CREATE_GANG)
return ERR_PTR(-EINVAL);
if (flags & SPU_CREATE_AFFINITY_MEM &&
gang->aff_ref_ctx &&
gang->aff_ref_ctx->flags & SPU_CREATE_AFFINITY_MEM)
return ERR_PTR(-EEXIST);
if (gang->aff_flags & AFF_MERGED)
return ERR_PTR(-EBUSY);
neighbor = NULL;
if (flags & SPU_CREATE_AFFINITY_SPU) {
if (!filp || filp->f_op != &spufs_context_fops)
return ERR_PTR(-EINVAL);
neighbor = get_spu_context(
SPUFS_I(filp->f_dentry->d_inode)->i_ctx);
if (!list_empty(&neighbor->aff_list) && !(neighbor->aff_head) &&
!list_is_last(&neighbor->aff_list, &gang->aff_list_head) &&
!list_entry(neighbor->aff_list.next, struct spu_context,
aff_list)->aff_head) {
err = ERR_PTR(-EEXIST);
goto out_put_neighbor;
}
if (gang != neighbor->gang) {
err = ERR_PTR(-EINVAL);
goto out_put_neighbor;
}
count = 1;
list_for_each_entry(tmp, &gang->aff_list_head, aff_list)
count++;
if (list_empty(&neighbor->aff_list))
count++;
for (node = 0; node < MAX_NUMNODES; node++) {
if ((cbe_spu_info[node].n_spus - atomic_read(
&cbe_spu_info[node].reserved_spus)) >= count)
break;
}
if (node == MAX_NUMNODES) {
err = ERR_PTR(-EEXIST);
goto out_put_neighbor;
}
}
return neighbor;
out_put_neighbor:
put_spu_context(neighbor);
return err;
}
static void
spufs_set_affinity(unsigned int flags, struct spu_context *ctx,
struct spu_context *neighbor)
{
if (flags & SPU_CREATE_AFFINITY_MEM)
ctx->gang->aff_ref_ctx = ctx;
if (flags & SPU_CREATE_AFFINITY_SPU) {
if (list_empty(&neighbor->aff_list)) {
list_add_tail(&neighbor->aff_list,
&ctx->gang->aff_list_head);
neighbor->aff_head = 1;
}
if (list_is_last(&neighbor->aff_list, &ctx->gang->aff_list_head)
|| list_entry(neighbor->aff_list.next, struct spu_context,
aff_list)->aff_head) {
list_add(&ctx->aff_list, &neighbor->aff_list);
} else {
list_add_tail(&ctx->aff_list, &neighbor->aff_list);
if (neighbor->aff_head) {
neighbor->aff_head = 0;
ctx->aff_head = 1;
}
}
if (!ctx->gang->aff_ref_ctx)
ctx->gang->aff_ref_ctx = ctx;
}
}
static int
spufs_create_context(struct inode *inode, struct dentry *dentry,
struct vfsmount *mnt, int flags, int mode,
struct file *aff_filp)
{
int ret;
int affinity;
struct spu_gang *gang;
struct spu_context *neighbor;
ret = -EPERM;
if ((flags & SPU_CREATE_NOSCHED) &&
!capable(CAP_SYS_NICE))
goto out_unlock;
ret = -EINVAL;
if ((flags & (SPU_CREATE_NOSCHED | SPU_CREATE_ISOLATE))
== SPU_CREATE_ISOLATE)
goto out_unlock;
ret = -ENODEV;
if ((flags & SPU_CREATE_ISOLATE) && !isolated_loader)
goto out_unlock;
gang = NULL;
neighbor = NULL;
affinity = flags & (SPU_CREATE_AFFINITY_MEM | SPU_CREATE_AFFINITY_SPU);
if (affinity) {
gang = SPUFS_I(inode)->i_gang;
ret = -EINVAL;
if (!gang)
goto out_unlock;
mutex_lock(&gang->aff_mutex);
neighbor = spufs_assert_affinity(flags, gang, aff_filp);
if (IS_ERR(neighbor)) {
ret = PTR_ERR(neighbor);
goto out_aff_unlock;
}
}
ret = spufs_mkdir(inode, dentry, flags, mode & S_IRWXUGO);
if (ret)
goto out_aff_unlock;
if (affinity) {
spufs_set_affinity(flags, SPUFS_I(dentry->d_inode)->i_ctx,
neighbor);
if (neighbor)
put_spu_context(neighbor);
}
/*
* get references for dget and mntget, will be released
* in error path of *_open().
*/
ret = spufs_context_open(dget(dentry), mntget(mnt));
if (ret < 0) {
WARN_ON(spufs_rmdir(inode, dentry));
if (affinity)
mutex_unlock(&gang->aff_mutex);
mutex_unlock(&inode->i_mutex);
spu_forget(SPUFS_I(dentry->d_inode)->i_ctx);
goto out;
}
out_aff_unlock:
if (affinity)
mutex_unlock(&gang->aff_mutex);
out_unlock:
mutex_unlock(&inode->i_mutex);
out:
dput(dentry);
return ret;
}
static int
spufs_mkgang(struct inode *dir, struct dentry *dentry, int mode)
{
int ret;
struct inode *inode;
struct spu_gang *gang;
ret = -ENOSPC;
inode = spufs_new_inode(dir->i_sb, mode | S_IFDIR);
if (!inode)
goto out;
ret = 0;
if (dir->i_mode & S_ISGID) {
inode->i_gid = dir->i_gid;
inode->i_mode &= S_ISGID;
}
gang = alloc_spu_gang();
SPUFS_I(inode)->i_ctx = NULL;
SPUFS_I(inode)->i_gang = gang;
if (!gang)
goto out_iput;
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
d_instantiate(dentry, inode);
inc_nlink(dir);
inc_nlink(dentry->d_inode);
return ret;
out_iput:
iput(inode);
out:
return ret;
}
static int spufs_gang_open(struct dentry *dentry, struct vfsmount *mnt)
{
int ret;
struct file *filp;
ret = get_unused_fd();
if (ret < 0) {
dput(dentry);
mntput(mnt);
goto out;
}
filp = dentry_open(dentry, mnt, O_RDONLY, current_cred());
if (IS_ERR(filp)) {
put_unused_fd(ret);
ret = PTR_ERR(filp);
goto out;
}
filp->f_op = &simple_dir_operations;
fd_install(ret, filp);
out:
return ret;
}
static int spufs_create_gang(struct inode *inode,
struct dentry *dentry,
struct vfsmount *mnt, int mode)
{
int ret;
ret = spufs_mkgang(inode, dentry, mode & S_IRWXUGO);
if (ret)
goto out;
/*
* get references for dget and mntget, will be released
* in error path of *_open().
*/
ret = spufs_gang_open(dget(dentry), mntget(mnt));
if (ret < 0) {
int err = simple_rmdir(inode, dentry);
WARN_ON(err);
}
out:
mutex_unlock(&inode->i_mutex);
dput(dentry);
return ret;
}
static struct file_system_type spufs_type;
long spufs_create(struct nameidata *nd, unsigned int flags, mode_t mode,
struct file *filp)
{
struct dentry *dentry;
int ret;
ret = -EINVAL;
/* check if we are on spufs */
if (nd->path.dentry->d_sb->s_type != &spufs_type)
goto out;
/* don't accept undefined flags */
if (flags & (~SPU_CREATE_FLAG_ALL))
goto out;
/* only threads can be underneath a gang */
if (nd->path.dentry != nd->path.dentry->d_sb->s_root) {
if ((flags & SPU_CREATE_GANG) ||
!SPUFS_I(nd->path.dentry->d_inode)->i_gang)
goto out;
}
dentry = lookup_create(nd, 1);
ret = PTR_ERR(dentry);
if (IS_ERR(dentry))
goto out_dir;
mode &= ~current_umask();
if (flags & SPU_CREATE_GANG)
ret = spufs_create_gang(nd->path.dentry->d_inode,
dentry, nd->path.mnt, mode);
else
ret = spufs_create_context(nd->path.dentry->d_inode,
dentry, nd->path.mnt, flags, mode,
filp);
if (ret >= 0)
fsnotify_mkdir(nd->path.dentry->d_inode, dentry);
return ret;
out_dir:
mutex_unlock(&nd->path.dentry->d_inode->i_mutex);
out:
return ret;
}
/* File system initialization */
enum {
Opt_uid, Opt_gid, Opt_mode, Opt_debug, Opt_err,
};
static const match_table_t spufs_tokens = {
{ Opt_uid, "uid=%d" },
{ Opt_gid, "gid=%d" },
{ Opt_mode, "mode=%o" },
{ Opt_debug, "debug" },
{ Opt_err, NULL },
};
static int
spufs_parse_options(struct super_block *sb, char *options, struct inode *root)
{
char *p;
substring_t args[MAX_OPT_ARGS];
while ((p = strsep(&options, ",")) != NULL) {
int token, option;
if (!*p)
continue;
token = match_token(p, spufs_tokens, args);
switch (token) {
case Opt_uid:
if (match_int(&args[0], &option))
return 0;
root->i_uid = option;
break;
case Opt_gid:
if (match_int(&args[0], &option))
return 0;
root->i_gid = option;
break;
case Opt_mode:
if (match_octal(&args[0], &option))
return 0;
root->i_mode = option | S_IFDIR;
break;
case Opt_debug:
spufs_get_sb_info(sb)->debug = 1;
break;
default:
return 0;
}
}
return 1;
}
static void spufs_exit_isolated_loader(void)
{
free_pages((unsigned long) isolated_loader,
get_order(isolated_loader_size));
}
static void
spufs_init_isolated_loader(void)
{
struct device_node *dn;
const char *loader;
int size;
dn = of_find_node_by_path("/spu-isolation");
if (!dn)
return;
loader = of_get_property(dn, "loader", &size);
if (!loader)
return;
/* the loader must be align on a 16 byte boundary */
isolated_loader = (char *)__get_free_pages(GFP_KERNEL, get_order(size));
if (!isolated_loader)
return;
isolated_loader_size = size;
memcpy(isolated_loader, loader, size);
printk(KERN_INFO "spufs: SPU isolation mode enabled\n");
}
static int
spufs_create_root(struct super_block *sb, void *data)
{
struct inode *inode;
int ret;
ret = -ENODEV;
if (!spu_management_ops)
goto out;
ret = -ENOMEM;
inode = spufs_new_inode(sb, S_IFDIR | 0775);
if (!inode)
goto out;
inode->i_op = &simple_dir_inode_operations;
inode->i_fop = &simple_dir_operations;
SPUFS_I(inode)->i_ctx = NULL;
inc_nlink(inode);
ret = -EINVAL;
if (!spufs_parse_options(sb, data, inode))
goto out_iput;
ret = -ENOMEM;
sb->s_root = d_alloc_root(inode);
if (!sb->s_root)
goto out_iput;
return 0;
out_iput:
iput(inode);
out:
return ret;
}
static int
spufs_fill_super(struct super_block *sb, void *data, int silent)
{
struct spufs_sb_info *info;
static const struct super_operations s_ops = {
.alloc_inode = spufs_alloc_inode,
.destroy_inode = spufs_destroy_inode,
.statfs = simple_statfs,
.delete_inode = spufs_delete_inode,
.drop_inode = generic_delete_inode,
.show_options = generic_show_options,
};
save_mount_options(sb, data);
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (!info)
return -ENOMEM;
sb->s_maxbytes = MAX_LFS_FILESIZE;
sb->s_blocksize = PAGE_CACHE_SIZE;
sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
sb->s_magic = SPUFS_MAGIC;
sb->s_op = &s_ops;
sb->s_fs_info = info;
return spufs_create_root(sb, data);
}
static int
spufs_get_sb(struct file_system_type *fstype, int flags,
const char *name, void *data, struct vfsmount *mnt)
{
return get_sb_single(fstype, flags, data, spufs_fill_super, mnt);
}
static struct file_system_type spufs_type = {
.owner = THIS_MODULE,
.name = "spufs",
.get_sb = spufs_get_sb,
.kill_sb = kill_litter_super,
};
static int __init spufs_init(void)
{
int ret;
ret = -ENODEV;
if (!spu_management_ops)
goto out;
ret = -ENOMEM;
spufs_inode_cache = kmem_cache_create("spufs_inode_cache",
sizeof(struct spufs_inode_info), 0,
SLAB_HWCACHE_ALIGN, spufs_init_once);
if (!spufs_inode_cache)
goto out;
ret = spu_sched_init();
if (ret)
goto out_cache;
ret = register_filesystem(&spufs_type);
if (ret)
goto out_sched;
ret = register_spu_syscalls(&spufs_calls);
if (ret)
goto out_fs;
spufs_init_isolated_loader();
return 0;
out_fs:
unregister_filesystem(&spufs_type);
out_sched:
spu_sched_exit();
out_cache:
kmem_cache_destroy(spufs_inode_cache);
out:
return ret;
}
module_init(spufs_init);
static void __exit spufs_exit(void)
{
spu_sched_exit();
spufs_exit_isolated_loader();
unregister_spu_syscalls(&spufs_calls);
unregister_filesystem(&spufs_type);
kmem_cache_destroy(spufs_inode_cache);
}
module_exit(spufs_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arnd Bergmann <arndb@de.ibm.com>");

View File

@@ -0,0 +1,183 @@
/*
* SPU local store allocation routines
*
* Copyright 2007 Benjamin Herrenschmidt, IBM Corp.
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#undef DEBUG
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/vmalloc.h>
#include <asm/spu.h>
#include <asm/spu_csa.h>
#include <asm/mmu.h>
#include "spufs.h"
static int spu_alloc_lscsa_std(struct spu_state *csa)
{
struct spu_lscsa *lscsa;
unsigned char *p;
lscsa = vmalloc(sizeof(struct spu_lscsa));
if (!lscsa)
return -ENOMEM;
memset(lscsa, 0, sizeof(struct spu_lscsa));
csa->lscsa = lscsa;
/* Set LS pages reserved to allow for user-space mapping. */
for (p = lscsa->ls; p < lscsa->ls + LS_SIZE; p += PAGE_SIZE)
SetPageReserved(vmalloc_to_page(p));
return 0;
}
static void spu_free_lscsa_std(struct spu_state *csa)
{
/* Clear reserved bit before vfree. */
unsigned char *p;
if (csa->lscsa == NULL)
return;
for (p = csa->lscsa->ls; p < csa->lscsa->ls + LS_SIZE; p += PAGE_SIZE)
ClearPageReserved(vmalloc_to_page(p));
vfree(csa->lscsa);
}
#ifdef CONFIG_SPU_FS_64K_LS
#define SPU_64K_PAGE_SHIFT 16
#define SPU_64K_PAGE_ORDER (SPU_64K_PAGE_SHIFT - PAGE_SHIFT)
#define SPU_64K_PAGE_COUNT (1ul << SPU_64K_PAGE_ORDER)
int spu_alloc_lscsa(struct spu_state *csa)
{
struct page **pgarray;
unsigned char *p;
int i, j, n_4k;
/* Check availability of 64K pages */
if (!spu_64k_pages_available())
goto fail;
csa->use_big_pages = 1;
pr_debug("spu_alloc_lscsa(csa=0x%p), trying to allocate 64K pages\n",
csa);
/* First try to allocate our 64K pages. We need 5 of them
* with the current implementation. In the future, we should try
* to separate the lscsa with the actual local store image, thus
* allowing us to require only 4 64K pages per context
*/
for (i = 0; i < SPU_LSCSA_NUM_BIG_PAGES; i++) {
/* XXX This is likely to fail, we should use a special pool
* similiar to what hugetlbfs does.
*/
csa->lscsa_pages[i] = alloc_pages(GFP_KERNEL,
SPU_64K_PAGE_ORDER);
if (csa->lscsa_pages[i] == NULL)
goto fail;
}
pr_debug(" success ! creating vmap...\n");
/* Now we need to create a vmalloc mapping of these for the kernel
* and SPU context switch code to use. Currently, we stick to a
* normal kernel vmalloc mapping, which in our case will be 4K
*/
n_4k = SPU_64K_PAGE_COUNT * SPU_LSCSA_NUM_BIG_PAGES;
pgarray = kmalloc(sizeof(struct page *) * n_4k, GFP_KERNEL);
if (pgarray == NULL)
goto fail;
for (i = 0; i < SPU_LSCSA_NUM_BIG_PAGES; i++)
for (j = 0; j < SPU_64K_PAGE_COUNT; j++)
/* We assume all the struct page's are contiguous
* which should be hopefully the case for an order 4
* allocation..
*/
pgarray[i * SPU_64K_PAGE_COUNT + j] =
csa->lscsa_pages[i] + j;
csa->lscsa = vmap(pgarray, n_4k, VM_USERMAP, PAGE_KERNEL);
kfree(pgarray);
if (csa->lscsa == NULL)
goto fail;
memset(csa->lscsa, 0, sizeof(struct spu_lscsa));
/* Set LS pages reserved to allow for user-space mapping.
*
* XXX isn't that a bit obsolete ? I think we should just
* make sure the page count is high enough. Anyway, won't harm
* for now
*/
for (p = csa->lscsa->ls; p < csa->lscsa->ls + LS_SIZE; p += PAGE_SIZE)
SetPageReserved(vmalloc_to_page(p));
pr_debug(" all good !\n");
return 0;
fail:
pr_debug("spufs: failed to allocate lscsa 64K pages, falling back\n");
spu_free_lscsa(csa);
return spu_alloc_lscsa_std(csa);
}
void spu_free_lscsa(struct spu_state *csa)
{
unsigned char *p;
int i;
if (!csa->use_big_pages) {
spu_free_lscsa_std(csa);
return;
}
csa->use_big_pages = 0;
if (csa->lscsa == NULL)
goto free_pages;
for (p = csa->lscsa->ls; p < csa->lscsa->ls + LS_SIZE; p += PAGE_SIZE)
ClearPageReserved(vmalloc_to_page(p));
vunmap(csa->lscsa);
csa->lscsa = NULL;
free_pages:
for (i = 0; i < SPU_LSCSA_NUM_BIG_PAGES; i++)
if (csa->lscsa_pages[i])
__free_pages(csa->lscsa_pages[i], SPU_64K_PAGE_ORDER);
}
#else /* CONFIG_SPU_FS_64K_LS */
int spu_alloc_lscsa(struct spu_state *csa)
{
return spu_alloc_lscsa_std(csa);
}
void spu_free_lscsa(struct spu_state *csa)
{
spu_free_lscsa_std(csa);
}
#endif /* !defined(CONFIG_SPU_FS_64K_LS) */

View File

@@ -0,0 +1,454 @@
#define DEBUG
#include <linux/wait.h>
#include <linux/ptrace.h>
#include <asm/spu.h>
#include <asm/spu_priv1.h>
#include <asm/io.h>
#include <asm/unistd.h>
#include "spufs.h"
/* interrupt-level stop callback function. */
void spufs_stop_callback(struct spu *spu, int irq)
{
struct spu_context *ctx = spu->ctx;
/*
* It should be impossible to preempt a context while an exception
* is being processed, since the context switch code is specially
* coded to deal with interrupts ... But, just in case, sanity check
* the context pointer. It is OK to return doing nothing since
* the exception will be regenerated when the context is resumed.
*/
if (ctx) {
/* Copy exception arguments into module specific structure */
switch(irq) {
case 0 :
ctx->csa.class_0_pending = spu->class_0_pending;
ctx->csa.class_0_dar = spu->class_0_dar;
break;
case 1 :
ctx->csa.class_1_dsisr = spu->class_1_dsisr;
ctx->csa.class_1_dar = spu->class_1_dar;
break;
case 2 :
break;
}
/* ensure that the exception status has hit memory before a
* thread waiting on the context's stop queue is woken */
smp_wmb();
wake_up_all(&ctx->stop_wq);
}
}
int spu_stopped(struct spu_context *ctx, u32 *stat)
{
u64 dsisr;
u32 stopped;
stopped = SPU_STATUS_INVALID_INSTR | SPU_STATUS_SINGLE_STEP |
SPU_STATUS_STOPPED_BY_HALT | SPU_STATUS_STOPPED_BY_STOP;
top:
*stat = ctx->ops->status_read(ctx);
if (*stat & stopped) {
/*
* If the spu hasn't finished stopping, we need to
* re-read the register to get the stopped value.
*/
if (*stat & SPU_STATUS_RUNNING)
goto top;
return 1;
}
if (test_bit(SPU_SCHED_NOTIFY_ACTIVE, &ctx->sched_flags))
return 1;
dsisr = ctx->csa.class_1_dsisr;
if (dsisr & (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED))
return 1;
if (ctx->csa.class_0_pending)
return 1;
return 0;
}
static int spu_setup_isolated(struct spu_context *ctx)
{
int ret;
u64 __iomem *mfc_cntl;
u64 sr1;
u32 status;
unsigned long timeout;
const u32 status_loading = SPU_STATUS_RUNNING
| SPU_STATUS_ISOLATED_STATE | SPU_STATUS_ISOLATED_LOAD_STATUS;
ret = -ENODEV;
if (!isolated_loader)
goto out;
/*
* We need to exclude userspace access to the context.
*
* To protect against memory access we invalidate all ptes
* and make sure the pagefault handlers block on the mutex.
*/
spu_unmap_mappings(ctx);
mfc_cntl = &ctx->spu->priv2->mfc_control_RW;
/* purge the MFC DMA queue to ensure no spurious accesses before we
* enter kernel mode */
timeout = jiffies + HZ;
out_be64(mfc_cntl, MFC_CNTL_PURGE_DMA_REQUEST);
while ((in_be64(mfc_cntl) & MFC_CNTL_PURGE_DMA_STATUS_MASK)
!= MFC_CNTL_PURGE_DMA_COMPLETE) {
if (time_after(jiffies, timeout)) {
printk(KERN_ERR "%s: timeout flushing MFC DMA queue\n",
__func__);
ret = -EIO;
goto out;
}
cond_resched();
}
/* clear purge status */
out_be64(mfc_cntl, 0);
/* put the SPE in kernel mode to allow access to the loader */
sr1 = spu_mfc_sr1_get(ctx->spu);
sr1 &= ~MFC_STATE1_PROBLEM_STATE_MASK;
spu_mfc_sr1_set(ctx->spu, sr1);
/* start the loader */
ctx->ops->signal1_write(ctx, (unsigned long)isolated_loader >> 32);
ctx->ops->signal2_write(ctx,
(unsigned long)isolated_loader & 0xffffffff);
ctx->ops->runcntl_write(ctx,
SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE);
ret = 0;
timeout = jiffies + HZ;
while (((status = ctx->ops->status_read(ctx)) & status_loading) ==
status_loading) {
if (time_after(jiffies, timeout)) {
printk(KERN_ERR "%s: timeout waiting for loader\n",
__func__);
ret = -EIO;
goto out_drop_priv;
}
cond_resched();
}
if (!(status & SPU_STATUS_RUNNING)) {
/* If isolated LOAD has failed: run SPU, we will get a stop-and
* signal later. */
pr_debug("%s: isolated LOAD failed\n", __func__);
ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
ret = -EACCES;
goto out_drop_priv;
}
if (!(status & SPU_STATUS_ISOLATED_STATE)) {
/* This isn't allowed by the CBEA, but check anyway */
pr_debug("%s: SPU fell out of isolated mode?\n", __func__);
ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP);
ret = -EINVAL;
goto out_drop_priv;
}
out_drop_priv:
/* Finished accessing the loader. Drop kernel mode */
sr1 |= MFC_STATE1_PROBLEM_STATE_MASK;
spu_mfc_sr1_set(ctx->spu, sr1);
out:
return ret;
}
static int spu_run_init(struct spu_context *ctx, u32 *npc)
{
unsigned long runcntl = SPU_RUNCNTL_RUNNABLE;
int ret;
spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
/*
* NOSCHED is synchronous scheduling with respect to the caller.
* The caller waits for the context to be loaded.
*/
if (ctx->flags & SPU_CREATE_NOSCHED) {
if (ctx->state == SPU_STATE_SAVED) {
ret = spu_activate(ctx, 0);
if (ret)
return ret;
}
}
/*
* Apply special setup as required.
*/
if (ctx->flags & SPU_CREATE_ISOLATE) {
if (!(ctx->ops->status_read(ctx) & SPU_STATUS_ISOLATED_STATE)) {
ret = spu_setup_isolated(ctx);
if (ret)
return ret;
}
/*
* If userspace has set the runcntrl register (eg, to
* issue an isolated exit), we need to re-set it here
*/
runcntl = ctx->ops->runcntl_read(ctx) &
(SPU_RUNCNTL_RUNNABLE | SPU_RUNCNTL_ISOLATE);
if (runcntl == 0)
runcntl = SPU_RUNCNTL_RUNNABLE;
} else {
unsigned long privcntl;
if (test_thread_flag(TIF_SINGLESTEP))
privcntl = SPU_PRIVCNTL_MODE_SINGLE_STEP;
else
privcntl = SPU_PRIVCNTL_MODE_NORMAL;
ctx->ops->privcntl_write(ctx, privcntl);
ctx->ops->npc_write(ctx, *npc);
}
ctx->ops->runcntl_write(ctx, runcntl);
if (ctx->flags & SPU_CREATE_NOSCHED) {
spuctx_switch_state(ctx, SPU_UTIL_USER);
} else {
if (ctx->state == SPU_STATE_SAVED) {
ret = spu_activate(ctx, 0);
if (ret)
return ret;
} else {
spuctx_switch_state(ctx, SPU_UTIL_USER);
}
}
set_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags);
return 0;
}
static int spu_run_fini(struct spu_context *ctx, u32 *npc,
u32 *status)
{
int ret = 0;
spu_del_from_rq(ctx);
*status = ctx->ops->status_read(ctx);
*npc = ctx->ops->npc_read(ctx);
spuctx_switch_state(ctx, SPU_UTIL_IDLE_LOADED);
clear_bit(SPU_SCHED_SPU_RUN, &ctx->sched_flags);
spu_switch_log_notify(NULL, ctx, SWITCH_LOG_EXIT, *status);
spu_release(ctx);
if (signal_pending(current))
ret = -ERESTARTSYS;
return ret;
}
/*
* SPU syscall restarting is tricky because we violate the basic
* assumption that the signal handler is running on the interrupted
* thread. Here instead, the handler runs on PowerPC user space code,
* while the syscall was called from the SPU.
* This means we can only do a very rough approximation of POSIX
* signal semantics.
*/
static int spu_handle_restartsys(struct spu_context *ctx, long *spu_ret,
unsigned int *npc)
{
int ret;
switch (*spu_ret) {
case -ERESTARTSYS:
case -ERESTARTNOINTR:
/*
* Enter the regular syscall restarting for
* sys_spu_run, then restart the SPU syscall
* callback.
*/
*npc -= 8;
ret = -ERESTARTSYS;
break;
case -ERESTARTNOHAND:
case -ERESTART_RESTARTBLOCK:
/*
* Restart block is too hard for now, just return -EINTR
* to the SPU.
* ERESTARTNOHAND comes from sys_pause, we also return
* -EINTR from there.
* Assume that we need to be restarted ourselves though.
*/
*spu_ret = -EINTR;
ret = -ERESTARTSYS;
break;
default:
printk(KERN_WARNING "%s: unexpected return code %ld\n",
__func__, *spu_ret);
ret = 0;
}
return ret;
}
static int spu_process_callback(struct spu_context *ctx)
{
struct spu_syscall_block s;
u32 ls_pointer, npc;
void __iomem *ls;
long spu_ret;
int ret;
/* get syscall block from local store */
npc = ctx->ops->npc_read(ctx) & ~3;
ls = (void __iomem *)ctx->ops->get_ls(ctx);
ls_pointer = in_be32(ls + npc);
if (ls_pointer > (LS_SIZE - sizeof(s)))
return -EFAULT;
memcpy_fromio(&s, ls + ls_pointer, sizeof(s));
/* do actual syscall without pinning the spu */
ret = 0;
spu_ret = -ENOSYS;
npc += 4;
if (s.nr_ret < __NR_syscalls) {
spu_release(ctx);
/* do actual system call from here */
spu_ret = spu_sys_callback(&s);
if (spu_ret <= -ERESTARTSYS) {
ret = spu_handle_restartsys(ctx, &spu_ret, &npc);
}
mutex_lock(&ctx->state_mutex);
if (ret == -ERESTARTSYS)
return ret;
}
/* need to re-get the ls, as it may have changed when we released the
* spu */
ls = (void __iomem *)ctx->ops->get_ls(ctx);
/* write result, jump over indirect pointer */
memcpy_toio(ls + ls_pointer, &spu_ret, sizeof(spu_ret));
ctx->ops->npc_write(ctx, npc);
ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
return ret;
}
long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *event)
{
int ret;
struct spu *spu;
u32 status;
if (mutex_lock_interruptible(&ctx->run_mutex))
return -ERESTARTSYS;
ctx->event_return = 0;
ret = spu_acquire(ctx);
if (ret)
goto out_unlock;
spu_enable_spu(ctx);
spu_update_sched_info(ctx);
ret = spu_run_init(ctx, npc);
if (ret) {
spu_release(ctx);
goto out;
}
do {
ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
if (unlikely(ret)) {
/*
* This is nasty: we need the state_mutex for all the
* bookkeeping even if the syscall was interrupted by
* a signal. ewww.
*/
mutex_lock(&ctx->state_mutex);
break;
}
spu = ctx->spu;
if (unlikely(test_and_clear_bit(SPU_SCHED_NOTIFY_ACTIVE,
&ctx->sched_flags))) {
if (!(status & SPU_STATUS_STOPPED_BY_STOP)) {
spu_switch_notify(spu, ctx);
continue;
}
}
spuctx_switch_state(ctx, SPU_UTIL_SYSTEM);
if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
(status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
ret = spu_process_callback(ctx);
if (ret)
break;
status &= ~SPU_STATUS_STOPPED_BY_STOP;
}
ret = spufs_handle_class1(ctx);
if (ret)
break;
ret = spufs_handle_class0(ctx);
if (ret)
break;
if (signal_pending(current))
ret = -ERESTARTSYS;
} while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
SPU_STATUS_STOPPED_BY_HALT |
SPU_STATUS_SINGLE_STEP)));
spu_disable_spu(ctx);
ret = spu_run_fini(ctx, npc, &status);
spu_yield(ctx);
if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
(((status >> SPU_STOP_STATUS_SHIFT) & 0x3f00) == 0x2100))
ctx->stats.libassist++;
if ((ret == 0) ||
((ret == -ERESTARTSYS) &&
((status & SPU_STATUS_STOPPED_BY_HALT) ||
(status & SPU_STATUS_SINGLE_STEP) ||
((status & SPU_STATUS_STOPPED_BY_STOP) &&
(status >> SPU_STOP_STATUS_SHIFT != 0x2104)))))
ret = status;
/* Note: we don't need to force_sig SIGTRAP on single-step
* since we have TIF_SINGLESTEP set, thus the kernel will do
* it upon return from the syscall anyawy
*/
if (unlikely(status & SPU_STATUS_SINGLE_STEP))
ret = -ERESTARTSYS;
else if (unlikely((status & SPU_STATUS_STOPPED_BY_STOP)
&& (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff)) {
force_sig(SIGTRAP, current);
ret = -ERESTARTSYS;
}
out:
*event = ctx->event_return;
out_unlock:
mutex_unlock(&ctx->run_mutex);
return ret;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,336 @@
/*
* spu_restore.c
*
* (C) Copyright IBM Corp. 2005
*
* SPU-side context restore sequence outlined in
* Synergistic Processor Element Book IV
*
* Author: Mark Nutter <mnutter@us.ibm.com>
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef LS_SIZE
#define LS_SIZE 0x40000 /* 256K (in bytes) */
#endif
typedef unsigned int u32;
typedef unsigned long long u64;
#include <spu_intrinsics.h>
#include <asm/spu_csa.h>
#include "spu_utils.h"
#define BR_INSTR 0x327fff80 /* br -4 */
#define NOP_INSTR 0x40200000 /* nop */
#define HEQ_INSTR 0x7b000000 /* heq $0, $0 */
#define STOP_INSTR 0x00000000 /* stop 0x0 */
#define ILLEGAL_INSTR 0x00800000 /* illegal instr */
#define RESTORE_COMPLETE 0x00003ffc /* stop 0x3ffc */
static inline void fetch_regs_from_mem(addr64 lscsa_ea)
{
unsigned int ls = (unsigned int)&regs_spill[0];
unsigned int size = sizeof(regs_spill);
unsigned int tag_id = 0;
unsigned int cmd = 0x40; /* GET */
spu_writech(MFC_LSA, ls);
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
spu_writech(MFC_EAL, lscsa_ea.ui[1]);
spu_writech(MFC_Size, size);
spu_writech(MFC_TagID, tag_id);
spu_writech(MFC_Cmd, cmd);
}
static inline void restore_upper_240kb(addr64 lscsa_ea)
{
unsigned int ls = 16384;
unsigned int list = (unsigned int)&dma_list[0];
unsigned int size = sizeof(dma_list);
unsigned int tag_id = 0;
unsigned int cmd = 0x44; /* GETL */
/* Restore, Step 4:
* Enqueue the GETL command (tag 0) to the MFC SPU command
* queue to transfer the upper 240 kb of LS from CSA.
*/
spu_writech(MFC_LSA, ls);
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
spu_writech(MFC_EAL, list);
spu_writech(MFC_Size, size);
spu_writech(MFC_TagID, tag_id);
spu_writech(MFC_Cmd, cmd);
}
static inline void restore_decr(void)
{
unsigned int offset;
unsigned int decr_running;
unsigned int decr;
/* Restore, Step 6(moved):
* If the LSCSA "decrementer running" flag is set
* then write the SPU_WrDec channel with the
* decrementer value from LSCSA.
*/
offset = LSCSA_QW_OFFSET(decr_status);
decr_running = regs_spill[offset].slot[0] & SPU_DECR_STATUS_RUNNING;
if (decr_running) {
offset = LSCSA_QW_OFFSET(decr);
decr = regs_spill[offset].slot[0];
spu_writech(SPU_WrDec, decr);
}
}
static inline void write_ppu_mb(void)
{
unsigned int offset;
unsigned int data;
/* Restore, Step 11:
* Write the MFC_WrOut_MB channel with the PPU_MB
* data from LSCSA.
*/
offset = LSCSA_QW_OFFSET(ppu_mb);
data = regs_spill[offset].slot[0];
spu_writech(SPU_WrOutMbox, data);
}
static inline void write_ppuint_mb(void)
{
unsigned int offset;
unsigned int data;
/* Restore, Step 12:
* Write the MFC_WrInt_MB channel with the PPUINT_MB
* data from LSCSA.
*/
offset = LSCSA_QW_OFFSET(ppuint_mb);
data = regs_spill[offset].slot[0];
spu_writech(SPU_WrOutIntrMbox, data);
}
static inline void restore_fpcr(void)
{
unsigned int offset;
vector unsigned int fpcr;
/* Restore, Step 13:
* Restore the floating-point status and control
* register from the LSCSA.
*/
offset = LSCSA_QW_OFFSET(fpcr);
fpcr = regs_spill[offset].v;
spu_mtfpscr(fpcr);
}
static inline void restore_srr0(void)
{
unsigned int offset;
unsigned int srr0;
/* Restore, Step 14:
* Restore the SPU SRR0 data from the LSCSA.
*/
offset = LSCSA_QW_OFFSET(srr0);
srr0 = regs_spill[offset].slot[0];
spu_writech(SPU_WrSRR0, srr0);
}
static inline void restore_event_mask(void)
{
unsigned int offset;
unsigned int event_mask;
/* Restore, Step 15:
* Restore the SPU_RdEventMsk data from the LSCSA.
*/
offset = LSCSA_QW_OFFSET(event_mask);
event_mask = regs_spill[offset].slot[0];
spu_writech(SPU_WrEventMask, event_mask);
}
static inline void restore_tag_mask(void)
{
unsigned int offset;
unsigned int tag_mask;
/* Restore, Step 16:
* Restore the SPU_RdTagMsk data from the LSCSA.
*/
offset = LSCSA_QW_OFFSET(tag_mask);
tag_mask = regs_spill[offset].slot[0];
spu_writech(MFC_WrTagMask, tag_mask);
}
static inline void restore_complete(void)
{
extern void exit_fini(void);
unsigned int *exit_instrs = (unsigned int *)exit_fini;
unsigned int offset;
unsigned int stopped_status;
unsigned int stopped_code;
/* Restore, Step 18:
* Issue a stop-and-signal instruction with
* "good context restore" signal value.
*
* Restore, Step 19:
* There may be additional instructions placed
* here by the PPE Sequence for SPU Context
* Restore in order to restore the correct
* "stopped state".
*
* This step is handled here by analyzing the
* LSCSA.stopped_status and then modifying the
* exit() function to behave appropriately.
*/
offset = LSCSA_QW_OFFSET(stopped_status);
stopped_status = regs_spill[offset].slot[0];
stopped_code = regs_spill[offset].slot[1];
switch (stopped_status) {
case SPU_STOPPED_STATUS_P_I:
/* SPU_Status[P,I]=1. Add illegal instruction
* followed by stop-and-signal instruction after
* end of restore code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = ILLEGAL_INSTR;
exit_instrs[2] = STOP_INSTR | stopped_code;
break;
case SPU_STOPPED_STATUS_P_H:
/* SPU_Status[P,H]=1. Add 'heq $0, $0' followed
* by stop-and-signal instruction after end of
* restore code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = HEQ_INSTR;
exit_instrs[2] = STOP_INSTR | stopped_code;
break;
case SPU_STOPPED_STATUS_S_P:
/* SPU_Status[S,P]=1. Add nop instruction
* followed by 'br -4' after end of restore
* code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = STOP_INSTR | stopped_code;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
case SPU_STOPPED_STATUS_S_I:
/* SPU_Status[S,I]=1. Add illegal instruction
* followed by 'br -4' after end of restore code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = ILLEGAL_INSTR;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
case SPU_STOPPED_STATUS_I:
/* SPU_Status[I]=1. Add illegal instruction followed
* by infinite loop after end of restore sequence.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = ILLEGAL_INSTR;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
case SPU_STOPPED_STATUS_S:
/* SPU_Status[S]=1. Add two 'nop' instructions. */
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = NOP_INSTR;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
case SPU_STOPPED_STATUS_H:
/* SPU_Status[H]=1. Add 'heq $0, $0' instruction
* after end of restore code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = HEQ_INSTR;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
case SPU_STOPPED_STATUS_P:
/* SPU_Status[P]=1. Add stop-and-signal instruction
* after end of restore code.
*/
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = STOP_INSTR | stopped_code;
break;
case SPU_STOPPED_STATUS_R:
/* SPU_Status[I,S,H,P,R]=0. Add infinite loop. */
exit_instrs[0] = RESTORE_COMPLETE;
exit_instrs[1] = NOP_INSTR;
exit_instrs[2] = NOP_INSTR;
exit_instrs[3] = BR_INSTR;
break;
default:
/* SPU_Status[R]=1. No additonal instructions. */
break;
}
spu_sync();
}
/**
* main - entry point for SPU-side context restore.
*
* This code deviates from the documented sequence in the
* following aspects:
*
* 1. The EA for LSCSA is passed from PPE in the
* signal notification channels.
* 2. The register spill area is pulled by SPU
* into LS, rather than pushed by PPE.
* 3. All 128 registers are restored by exit().
* 4. The exit() function is modified at run
* time in order to properly restore the
* SPU_Status register.
*/
int main()
{
addr64 lscsa_ea;
lscsa_ea.ui[0] = spu_readch(SPU_RdSigNotify1);
lscsa_ea.ui[1] = spu_readch(SPU_RdSigNotify2);
fetch_regs_from_mem(lscsa_ea);
set_event_mask(); /* Step 1. */
set_tag_mask(); /* Step 2. */
build_dma_list(lscsa_ea); /* Step 3. */
restore_upper_240kb(lscsa_ea); /* Step 4. */
/* Step 5: done by 'exit'. */
enqueue_putllc(lscsa_ea); /* Step 7. */
set_tag_update(); /* Step 8. */
read_tag_status(); /* Step 9. */
restore_decr(); /* moved Step 6. */
read_llar_status(); /* Step 10. */
write_ppu_mb(); /* Step 11. */
write_ppuint_mb(); /* Step 12. */
restore_fpcr(); /* Step 13. */
restore_srr0(); /* Step 14. */
restore_event_mask(); /* Step 15. */
restore_tag_mask(); /* Step 16. */
/* Step 17. done by 'exit'. */
restore_complete(); /* Step 18. */
return 0;
}

View File

@@ -0,0 +1,116 @@
/*
* crt0_r.S: Entry function for SPU-side context restore.
*
* Copyright (C) 2005 IBM
*
* Entry and exit function for SPU-side of the context restore
* sequence. Sets up an initial stack frame, then branches to
* 'main'. On return, restores all 128 registers from the LSCSA
* and exits.
*
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <asm/spu_csa.h>
.data
.align 7
.globl regs_spill
regs_spill:
.space SIZEOF_SPU_SPILL_REGS, 0x0
.text
.global _start
_start:
/* Initialize the stack pointer to point to 16368
* (16kb-16). The back chain pointer is initialized
* to NULL.
*/
il $0, 0
il $SP, 16368
stqd $0, 0($SP)
/* Allocate a minimum stack frame for the called main.
* This is needed so that main has a place to save the
* link register when it calls another function.
*/
stqd $SP, -160($SP)
ai $SP, $SP, -160
/* Call the program's main function. */
brsl $0, main
.global exit
.global _exit
exit:
_exit:
/* SPU Context Restore, Step 5: Restore the remaining 112 GPRs. */
ila $3, regs_spill + 256
restore_regs:
lqr $4, restore_reg_insts
restore_reg_loop:
ai $4, $4, 4
.balignl 16, 0x40200000
restore_reg_insts: /* must be quad-word aligned. */
lqd $16, 0($3)
lqd $17, 16($3)
lqd $18, 32($3)
lqd $19, 48($3)
andi $5, $4, 0x7F
stqr $4, restore_reg_insts
ai $3, $3, 64
brnz $5, restore_reg_loop
/* SPU Context Restore Step 17: Restore the first 16 GPRs. */
lqa $0, regs_spill + 0
lqa $1, regs_spill + 16
lqa $2, regs_spill + 32
lqa $3, regs_spill + 48
lqa $4, regs_spill + 64
lqa $5, regs_spill + 80
lqa $6, regs_spill + 96
lqa $7, regs_spill + 112
lqa $8, regs_spill + 128
lqa $9, regs_spill + 144
lqa $10, regs_spill + 160
lqa $11, regs_spill + 176
lqa $12, regs_spill + 192
lqa $13, regs_spill + 208
lqa $14, regs_spill + 224
lqa $15, regs_spill + 240
/* Under normal circumstances, the 'exit' function
* terminates with 'stop SPU_RESTORE_COMPLETE',
* indicating that the SPU-side restore code has
* completed.
*
* However it is possible that instructions immediately
* following the 'stop 0x3ffc' have been modified at run
* time so as to recreate the exact SPU_Status settings
* from the application, e.g. illegal instruciton, halt,
* etc.
*/
.global exit_fini
.global _exit_fini
exit_fini:
_exit_fini:
stop SPU_RESTORE_COMPLETE
stop 0
stop 0
stop 0
/* Pad the size of this crt0.o to be multiple of 16 bytes. */
.balignl 16, 0x0

View File

@@ -0,0 +1,935 @@
/*
* spu_restore_dump.h: Copyright (C) 2005 IBM.
* Hex-dump auto generated from spu_restore.c.
* Do not edit!
*/
static unsigned int spu_restore_code[] __attribute__((__aligned__(128))) = {
0x40800000,
0x409ff801,
0x24000080,
0x24fd8081,
0x1cd80081,
0x33001180,
0x42034003,
0x33800284,
0x1c010204,
0x40200000,
0x40200000,
0x40200000,
0x34000190,
0x34004191,
0x34008192,
0x3400c193,
0x141fc205,
0x23fffd84,
0x1c100183,
0x217ffa85,
0x3080b000,
0x3080b201,
0x3080b402,
0x3080b603,
0x3080b804,
0x3080ba05,
0x3080bc06,
0x3080be07,
0x3080c008,
0x3080c209,
0x3080c40a,
0x3080c60b,
0x3080c80c,
0x3080ca0d,
0x3080cc0e,
0x3080ce0f,
0x00003ffc,
0x00000000,
0x00000000,
0x00000000,
0x01a00182,
0x3ec00083,
0xb0a14103,
0x01a00204,
0x3ec10083,
0x4202c002,
0xb0a14203,
0x21a00802,
0x3fbf028a,
0x3f20050a,
0x3fbe0502,
0x3fe30102,
0x21a00882,
0x3f82028b,
0x3fe3058b,
0x3fbf0584,
0x3f200204,
0x3fbe0204,
0x3fe30204,
0x04000203,
0x21a00903,
0x40848002,
0x21a00982,
0x40800003,
0x21a00a03,
0x40802002,
0x21a00a82,
0x21a00083,
0x40800082,
0x21a00b02,
0x10002612,
0x42a00003,
0x42074006,
0x1800c204,
0x40a00008,
0x40800789,
0x1c010305,
0x34000302,
0x1cffc489,
0x3ec00303,
0x3ec00287,
0xb0408403,
0x24000302,
0x34000282,
0x1c020306,
0xb0408207,
0x18020204,
0x24000282,
0x217ffa09,
0x04000402,
0x21a00802,
0x3fbe0504,
0x3fe30204,
0x21a00884,
0x42074002,
0x21a00902,
0x40803c03,
0x21a00983,
0x04000485,
0x21a00a05,
0x40802202,
0x21a00a82,
0x21a00805,
0x21a00884,
0x3fbf0582,
0x3f200102,
0x3fbe0102,
0x3fe30102,
0x21a00902,
0x40804003,
0x21a00983,
0x21a00a05,
0x40805a02,
0x21a00a82,
0x40800083,
0x21a00b83,
0x01a00c02,
0x30809c03,
0x34000182,
0x14004102,
0x21002082,
0x01a00d82,
0x3080a003,
0x34000182,
0x21a00e02,
0x3080a203,
0x34000182,
0x21a00f02,
0x3080a403,
0x34000182,
0x77400100,
0x3080a603,
0x34000182,
0x21a00702,
0x3080a803,
0x34000182,
0x21a00082,
0x3080aa03,
0x34000182,
0x21a00b02,
0x4020007f,
0x3080ae02,
0x42004805,
0x3080ac04,
0x34000103,
0x34000202,
0x1cffc183,
0x3b810106,
0x0f608184,
0x42013802,
0x5c020183,
0x38810102,
0x3b810102,
0x21000e83,
0x4020007f,
0x35000100,
0x00000470,
0x000002f8,
0x00000430,
0x00000360,
0x000002f8,
0x000003c8,
0x000004a8,
0x00000298,
0x00000360,
0x00200000,
0x409ffe02,
0x30801203,
0x40800208,
0x3ec40084,
0x40800407,
0x3ac20289,
0xb060c104,
0x3ac1c284,
0x20801203,
0x38820282,
0x41004003,
0xb0408189,
0x28820282,
0x3881c282,
0xb0408304,
0x2881c282,
0x00400000,
0x40800003,
0x35000000,
0x30809e03,
0x34000182,
0x21a00382,
0x4020007f,
0x327fde00,
0x409ffe02,
0x30801203,
0x40800206,
0x3ec40084,
0x40800407,
0x40800608,
0x3ac1828a,
0x3ac20289,
0xb060c104,
0x3ac1c284,
0x20801203,
0x38818282,
0x41004003,
0xb040818a,
0x10005b0b,
0x41201003,
0x28818282,
0x3881c282,
0xb0408184,
0x41193f83,
0x60ffc003,
0x2881c282,
0x38820282,
0xb0408189,
0x28820282,
0x327fef80,
0x409ffe02,
0x30801203,
0x40800207,
0x3ec40086,
0x4120100b,
0x10005b14,
0x40800404,
0x3ac1c289,
0x40800608,
0xb060c106,
0x3ac10286,
0x3ac2028a,
0x20801203,
0x3881c282,
0x41193f83,
0x60ffc003,
0xb0408589,
0x2881c282,
0x38810282,
0xb0408586,
0x28810282,
0x38820282,
0xb040818a,
0x28820282,
0x4020007f,
0x327fe280,
0x409ffe02,
0x30801203,
0x40800207,
0x3ec40084,
0x40800408,
0x10005b14,
0x40800609,
0x3ac1c28a,
0x3ac2028b,
0xb060c104,
0x3ac24284,
0x20801203,
0x41201003,
0x3881c282,
0xb040830a,
0x2881c282,
0x38820282,
0xb040818b,
0x41193f83,
0x60ffc003,
0x28820282,
0x38824282,
0xb0408184,
0x28824282,
0x4020007f,
0x327fd580,
0x409ffe02,
0x1000658e,
0x40800206,
0x30801203,
0x40800407,
0x3ec40084,
0x40800608,
0x3ac1828a,
0x3ac20289,
0xb060c104,
0x3ac1c284,
0x20801203,
0x413d8003,
0x38818282,
0x4020007f,
0x327fd800,
0x409ffe03,
0x30801202,
0x40800207,
0x3ec40084,
0x10005b09,
0x3ac1c288,
0xb0408184,
0x4020007f,
0x4020007f,
0x20801202,
0x3881c282,
0xb0408308,
0x2881c282,
0x327fc680,
0x409ffe02,
0x1000588b,
0x40800208,
0x30801203,
0x40800407,
0x3ec40084,
0x3ac20289,
0xb060c104,
0x3ac1c284,
0x20801203,
0x413d8003,
0x38820282,
0x327fbd80,
0x00200000,
0x00000da0,
0x00000000,
0x00000000,
0x00000000,
0x00000d90,
0x00000000,
0x00000000,
0x00000000,
0x00000db0,
0x00000000,
0x00000000,
0x00000000,
0x00000dc0,
0x00000000,
0x00000000,
0x00000000,
0x00000d80,
0x00000000,
0x00000000,
0x00000000,
0x00000df0,
0x00000000,
0x00000000,
0x00000000,
0x00000de0,
0x00000000,
0x00000000,
0x00000000,
0x00000dd0,
0x00000000,
0x00000000,
0x00000000,
0x00000e04,
0x00000000,
0x00000000,
0x00000000,
0x00000e00,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
};

View File

@@ -0,0 +1,195 @@
/*
* spu_save.c
*
* (C) Copyright IBM Corp. 2005
*
* SPU-side context save sequence outlined in
* Synergistic Processor Element Book IV
*
* Author: Mark Nutter <mnutter@us.ibm.com>
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*
*/
#ifndef LS_SIZE
#define LS_SIZE 0x40000 /* 256K (in bytes) */
#endif
typedef unsigned int u32;
typedef unsigned long long u64;
#include <spu_intrinsics.h>
#include <asm/spu_csa.h>
#include "spu_utils.h"
static inline void save_event_mask(void)
{
unsigned int offset;
/* Save, Step 2:
* Read the SPU_RdEventMsk channel and save to the LSCSA.
*/
offset = LSCSA_QW_OFFSET(event_mask);
regs_spill[offset].slot[0] = spu_readch(SPU_RdEventMask);
}
static inline void save_tag_mask(void)
{
unsigned int offset;
/* Save, Step 3:
* Read the SPU_RdTagMsk channel and save to the LSCSA.
*/
offset = LSCSA_QW_OFFSET(tag_mask);
regs_spill[offset].slot[0] = spu_readch(MFC_RdTagMask);
}
static inline void save_upper_240kb(addr64 lscsa_ea)
{
unsigned int ls = 16384;
unsigned int list = (unsigned int)&dma_list[0];
unsigned int size = sizeof(dma_list);
unsigned int tag_id = 0;
unsigned int cmd = 0x24; /* PUTL */
/* Save, Step 7:
* Enqueue the PUTL command (tag 0) to the MFC SPU command
* queue to transfer the remaining 240 kb of LS to CSA.
*/
spu_writech(MFC_LSA, ls);
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
spu_writech(MFC_EAL, list);
spu_writech(MFC_Size, size);
spu_writech(MFC_TagID, tag_id);
spu_writech(MFC_Cmd, cmd);
}
static inline void save_fpcr(void)
{
// vector unsigned int fpcr;
unsigned int offset;
/* Save, Step 9:
* Issue the floating-point status and control register
* read instruction, and save to the LSCSA.
*/
offset = LSCSA_QW_OFFSET(fpcr);
regs_spill[offset].v = spu_mffpscr();
}
static inline void save_decr(void)
{
unsigned int offset;
/* Save, Step 10:
* Read and save the SPU_RdDec channel data to
* the LSCSA.
*/
offset = LSCSA_QW_OFFSET(decr);
regs_spill[offset].slot[0] = spu_readch(SPU_RdDec);
}
static inline void save_srr0(void)
{
unsigned int offset;
/* Save, Step 11:
* Read and save the SPU_WSRR0 channel data to
* the LSCSA.
*/
offset = LSCSA_QW_OFFSET(srr0);
regs_spill[offset].slot[0] = spu_readch(SPU_RdSRR0);
}
static inline void spill_regs_to_mem(addr64 lscsa_ea)
{
unsigned int ls = (unsigned int)&regs_spill[0];
unsigned int size = sizeof(regs_spill);
unsigned int tag_id = 0;
unsigned int cmd = 0x20; /* PUT */
/* Save, Step 13:
* Enqueue a PUT command (tag 0) to send the LSCSA
* to the CSA.
*/
spu_writech(MFC_LSA, ls);
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
spu_writech(MFC_EAL, lscsa_ea.ui[1]);
spu_writech(MFC_Size, size);
spu_writech(MFC_TagID, tag_id);
spu_writech(MFC_Cmd, cmd);
}
static inline void enqueue_sync(addr64 lscsa_ea)
{
unsigned int tag_id = 0;
unsigned int cmd = 0xCC;
/* Save, Step 14:
* Enqueue an MFC_SYNC command (tag 0).
*/
spu_writech(MFC_TagID, tag_id);
spu_writech(MFC_Cmd, cmd);
}
static inline void save_complete(void)
{
/* Save, Step 18:
* Issue a stop-and-signal instruction indicating
* "save complete". Note: This function will not
* return!!
*/
spu_stop(SPU_SAVE_COMPLETE);
}
/**
* main - entry point for SPU-side context save.
*
* This code deviates from the documented sequence as follows:
*
* 1. The EA for LSCSA is passed from PPE in the
* signal notification channels.
* 2. All 128 registers are saved by crt0.o.
*/
int main()
{
addr64 lscsa_ea;
lscsa_ea.ui[0] = spu_readch(SPU_RdSigNotify1);
lscsa_ea.ui[1] = spu_readch(SPU_RdSigNotify2);
/* Step 1: done by exit(). */
save_event_mask(); /* Step 2. */
save_tag_mask(); /* Step 3. */
set_event_mask(); /* Step 4. */
set_tag_mask(); /* Step 5. */
build_dma_list(lscsa_ea); /* Step 6. */
save_upper_240kb(lscsa_ea); /* Step 7. */
/* Step 8: done by exit(). */
save_fpcr(); /* Step 9. */
save_decr(); /* Step 10. */
save_srr0(); /* Step 11. */
enqueue_putllc(lscsa_ea); /* Step 12. */
spill_regs_to_mem(lscsa_ea); /* Step 13. */
enqueue_sync(lscsa_ea); /* Step 14. */
set_tag_update(); /* Step 15. */
read_tag_status(); /* Step 16. */
read_llar_status(); /* Step 17. */
save_complete(); /* Step 18. */
return 0;
}

View File

@@ -0,0 +1,102 @@
/*
* crt0_s.S: Entry function for SPU-side context save.
*
* Copyright (C) 2005 IBM
*
* Entry function for SPU-side of the context save sequence.
* Saves all 128 GPRs, sets up an initial stack frame, then
* branches to 'main'.
*
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <asm/spu_csa.h>
.data
.align 7
.globl regs_spill
regs_spill:
.space SIZEOF_SPU_SPILL_REGS, 0x0
.text
.global _start
_start:
/* SPU Context Save Step 1: Save the first 16 GPRs. */
stqa $0, regs_spill + 0
stqa $1, regs_spill + 16
stqa $2, regs_spill + 32
stqa $3, regs_spill + 48
stqa $4, regs_spill + 64
stqa $5, regs_spill + 80
stqa $6, regs_spill + 96
stqa $7, regs_spill + 112
stqa $8, regs_spill + 128
stqa $9, regs_spill + 144
stqa $10, regs_spill + 160
stqa $11, regs_spill + 176
stqa $12, regs_spill + 192
stqa $13, regs_spill + 208
stqa $14, regs_spill + 224
stqa $15, regs_spill + 240
/* SPU Context Save, Step 8: Save the remaining 112 GPRs. */
ila $3, regs_spill + 256
save_regs:
lqr $4, save_reg_insts
save_reg_loop:
ai $4, $4, 4
.balignl 16, 0x40200000
save_reg_insts: /* must be quad-word aligned. */
stqd $16, 0($3)
stqd $17, 16($3)
stqd $18, 32($3)
stqd $19, 48($3)
andi $5, $4, 0x7F
stqr $4, save_reg_insts
ai $3, $3, 64
brnz $5, save_reg_loop
/* Initialize the stack pointer to point to 16368
* (16kb-16). The back chain pointer is initialized
* to NULL.
*/
il $0, 0
il $SP, 16368
stqd $0, 0($SP)
/* Allocate a minimum stack frame for the called main.
* This is needed so that main has a place to save the
* link register when it calls another function.
*/
stqd $SP, -160($SP)
ai $SP, $SP, -160
/* Call the program's main function. */
brsl $0, main
/* In this case main should not return; if it does
* there has been an error in the sequence. Execute
* stop-and-signal with code=0.
*/
.global exit
.global _exit
exit:
_exit:
stop 0x0
/* Pad the size of this crt0.o to be multiple of 16 bytes. */
.balignl 16, 0x0

View File

@@ -0,0 +1,743 @@
/*
* spu_save_dump.h: Copyright (C) 2005 IBM.
* Hex-dump auto generated from spu_save.c.
* Do not edit!
*/
static unsigned int spu_save_code[] __attribute__((__aligned__(128))) = {
0x20805000,
0x20805201,
0x20805402,
0x20805603,
0x20805804,
0x20805a05,
0x20805c06,
0x20805e07,
0x20806008,
0x20806209,
0x2080640a,
0x2080660b,
0x2080680c,
0x20806a0d,
0x20806c0e,
0x20806e0f,
0x4201c003,
0x33800184,
0x1c010204,
0x40200000,
0x24000190,
0x24004191,
0x24008192,
0x2400c193,
0x141fc205,
0x23fffd84,
0x1c100183,
0x217ffb85,
0x40800000,
0x409ff801,
0x24000080,
0x24fd8081,
0x1cd80081,
0x33000180,
0x00000000,
0x00000000,
0x01a00182,
0x3ec00083,
0xb1c38103,
0x01a00204,
0x3ec10082,
0x4201400d,
0xb1c38202,
0x01a00583,
0x34218682,
0x3ed80684,
0xb0408184,
0x24218682,
0x01a00603,
0x00200000,
0x34214682,
0x3ed40684,
0xb0408184,
0x40800003,
0x24214682,
0x21a00083,
0x40800082,
0x21a00b02,
0x4020007f,
0x1000251e,
0x42a00002,
0x32800008,
0x4205c00c,
0x00200000,
0x40a0000b,
0x3f82070f,
0x4080020a,
0x40800709,
0x3fe3078f,
0x3fbf0783,
0x3f200183,
0x3fbe0183,
0x3fe30187,
0x18008387,
0x4205c002,
0x3ac30404,
0x1cffc489,
0x00200000,
0x18008403,
0x38830402,
0x4cffc486,
0x3ac28185,
0xb0408584,
0x28830402,
0x1c020408,
0x38828182,
0xb0408385,
0x1802c387,
0x28828182,
0x217ff886,
0x04000582,
0x32800007,
0x21a00802,
0x3fbf0705,
0x3f200285,
0x3fbe0285,
0x3fe30285,
0x21a00885,
0x04000603,
0x21a00903,
0x40803c02,
0x21a00982,
0x04000386,
0x21a00a06,
0x40801202,
0x21a00a82,
0x73000003,
0x24200683,
0x01a00404,
0x00200000,
0x34204682,
0x3ec40683,
0xb0408203,
0x24204682,
0x01a00783,
0x00200000,
0x3421c682,
0x3edc0684,
0xb0408184,
0x2421c682,
0x21a00806,
0x21a00885,
0x3fbf0784,
0x3f200204,
0x3fbe0204,
0x3fe30204,
0x21a00904,
0x40804002,
0x21a00982,
0x21a00a06,
0x40805a02,
0x21a00a82,
0x04000683,
0x21a00803,
0x21a00885,
0x21a00904,
0x40848002,
0x21a00982,
0x21a00a06,
0x40801002,
0x21a00a82,
0x21a00a06,
0x40806602,
0x00200000,
0x35800009,
0x21a00a82,
0x40800083,
0x21a00b83,
0x01a00c02,
0x01a00d83,
0x00003ffb,
0x40800003,
0x4020007f,
0x35000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
0x00000000,
};

View File

@@ -0,0 +1,160 @@
/*
* utils.h: Utilities for SPU-side of the context switch operation.
*
* (C) Copyright IBM 2005
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef _SPU_CONTEXT_UTILS_H_
#define _SPU_CONTEXT_UTILS_H_
/*
* 64-bit safe EA.
*/
typedef union {
unsigned long long ull;
unsigned int ui[2];
} addr64;
/*
* 128-bit register template.
*/
typedef union {
unsigned int slot[4];
vector unsigned int v;
} spu_reg128v;
/*
* DMA list structure.
*/
struct dma_list_elem {
unsigned int size;
unsigned int ea_low;
};
/*
* Declare storage for 8-byte aligned DMA list.
*/
struct dma_list_elem dma_list[15] __attribute__ ((aligned(8)));
/*
* External definition for storage
* declared in crt0.
*/
extern spu_reg128v regs_spill[NR_SPU_SPILL_REGS];
/*
* Compute LSCSA byte offset for a given field.
*/
static struct spu_lscsa *dummy = (struct spu_lscsa *)0;
#define LSCSA_BYTE_OFFSET(_field) \
((char *)(&(dummy->_field)) - (char *)(&(dummy->gprs[0].slot[0])))
#define LSCSA_QW_OFFSET(_field) (LSCSA_BYTE_OFFSET(_field) >> 4)
static inline void set_event_mask(void)
{
unsigned int event_mask = 0;
/* Save, Step 4:
* Restore, Step 1:
* Set the SPU_RdEventMsk channel to zero to mask
* all events.
*/
spu_writech(SPU_WrEventMask, event_mask);
}
static inline void set_tag_mask(void)
{
unsigned int tag_mask = 1;
/* Save, Step 5:
* Restore, Step 2:
* Set the SPU_WrTagMsk channel to '01' to unmask
* only tag group 0.
*/
spu_writech(MFC_WrTagMask, tag_mask);
}
static inline void build_dma_list(addr64 lscsa_ea)
{
unsigned int ea_low;
int i;
/* Save, Step 6:
* Restore, Step 3:
* Update the effective address for the CSA in the
* pre-canned DMA-list in local storage.
*/
ea_low = lscsa_ea.ui[1];
ea_low += LSCSA_BYTE_OFFSET(ls[16384]);
for (i = 0; i < 15; i++, ea_low += 16384) {
dma_list[i].size = 16384;
dma_list[i].ea_low = ea_low;
}
}
static inline void enqueue_putllc(addr64 lscsa_ea)
{
unsigned int ls = 0;
unsigned int size = 128;
unsigned int tag_id = 0;
unsigned int cmd = 0xB4; /* PUTLLC */
/* Save, Step 12:
* Restore, Step 7:
* Send a PUTLLC (tag 0) command to the MFC using
* an effective address in the CSA in order to
* remove any possible lock-line reservation.
*/
spu_writech(MFC_LSA, ls);
spu_writech(MFC_EAH, lscsa_ea.ui[0]);
spu_writech(MFC_EAL, lscsa_ea.ui[1]);
spu_writech(MFC_Size, size);
spu_writech(MFC_TagID, tag_id);
spu_writech(MFC_Cmd, cmd);
}
static inline void set_tag_update(void)
{
unsigned int update_any = 1;
/* Save, Step 15:
* Restore, Step 8:
* Write the MFC_TagUpdate channel with '01'.
*/
spu_writech(MFC_WrTagUpdate, update_any);
}
static inline void read_tag_status(void)
{
/* Save, Step 16:
* Restore, Step 9:
* Read the MFC_TagStat channel data.
*/
spu_readch(MFC_RdTagStat);
}
static inline void read_llar_status(void)
{
/* Save, Step 17:
* Restore, Step 10:
* Read the MFC_AtomicStat channel data.
*/
spu_readch(MFC_RdAtomicStat);
}
#endif /* _SPU_CONTEXT_UTILS_H_ */

View File

@@ -0,0 +1,376 @@
/*
* SPU file system
*
* (C) Copyright IBM Deutschland Entwicklung GmbH 2005
*
* Author: Arnd Bergmann <arndb@de.ibm.com>
*
* 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; either version 2, or (at your option)
* any later version.
*
* 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef SPUFS_H
#define SPUFS_H
#include <linux/kref.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>
#include <linux/fs.h>
#include <linux/cpumask.h>
#include <asm/spu.h>
#include <asm/spu_csa.h>
#include <asm/spu_info.h>
#define SPUFS_PS_MAP_SIZE 0x20000
#define SPUFS_MFC_MAP_SIZE 0x1000
#define SPUFS_CNTL_MAP_SIZE 0x1000
#define SPUFS_CNTL_MAP_SIZE 0x1000
#define SPUFS_SIGNAL_MAP_SIZE PAGE_SIZE
#define SPUFS_MSS_MAP_SIZE 0x1000
/* The magic number for our file system */
enum {
SPUFS_MAGIC = 0x23c9b64e,
};
struct spu_context_ops;
struct spu_gang;
/* ctx->sched_flags */
enum {
SPU_SCHED_NOTIFY_ACTIVE,
SPU_SCHED_WAS_ACTIVE, /* was active upon spu_acquire_saved() */
SPU_SCHED_SPU_RUN, /* context is within spu_run */
};
enum {
SWITCH_LOG_BUFSIZE = 4096,
};
enum {
SWITCH_LOG_START,
SWITCH_LOG_STOP,
SWITCH_LOG_EXIT,
};
struct switch_log {
wait_queue_head_t wait;
unsigned long head;
unsigned long tail;
struct switch_log_entry {
struct timespec tstamp;
s32 spu_id;
u32 type;
u32 val;
u64 timebase;
} log[];
};
struct spu_context {
struct spu *spu; /* pointer to a physical SPU */
struct spu_state csa; /* SPU context save area. */
spinlock_t mmio_lock; /* protects mmio access */
struct address_space *local_store; /* local store mapping. */
struct address_space *mfc; /* 'mfc' area mappings. */
struct address_space *cntl; /* 'control' area mappings. */
struct address_space *signal1; /* 'signal1' area mappings. */
struct address_space *signal2; /* 'signal2' area mappings. */
struct address_space *mss; /* 'mss' area mappings. */
struct address_space *psmap; /* 'psmap' area mappings. */
struct mutex mapping_lock;
u64 object_id; /* user space pointer for oprofile */
enum { SPU_STATE_RUNNABLE, SPU_STATE_SAVED } state;
struct mutex state_mutex;
struct mutex run_mutex;
struct mm_struct *owner;
struct kref kref;
wait_queue_head_t ibox_wq;
wait_queue_head_t wbox_wq;
wait_queue_head_t stop_wq;
wait_queue_head_t mfc_wq;
wait_queue_head_t run_wq;
struct fasync_struct *ibox_fasync;
struct fasync_struct *wbox_fasync;
struct fasync_struct *mfc_fasync;
u32 tagwait;
struct spu_context_ops *ops;
struct work_struct reap_work;
unsigned long flags;
unsigned long event_return;
struct list_head gang_list;
struct spu_gang *gang;
struct kref *prof_priv_kref;
void ( * prof_priv_release) (struct kref *kref);
/* owner thread */
pid_t tid;
/* scheduler fields */
struct list_head rq;
unsigned int time_slice;
unsigned long sched_flags;
cpumask_t cpus_allowed;
int policy;
int prio;
int last_ran;
/* statistics */
struct {
/* updates protected by ctx->state_mutex */
enum spu_utilization_state util_state;
unsigned long long tstamp; /* time of last state switch */
unsigned long long times[SPU_UTIL_MAX];
unsigned long long vol_ctx_switch;
unsigned long long invol_ctx_switch;
unsigned long long min_flt;
unsigned long long maj_flt;
unsigned long long hash_flt;
unsigned long long slb_flt;
unsigned long long slb_flt_base; /* # at last ctx switch */
unsigned long long class2_intr;
unsigned long long class2_intr_base; /* # at last ctx switch */
unsigned long long libassist;
} stats;
/* context switch log */
struct switch_log *switch_log;
struct list_head aff_list;
int aff_head;
int aff_offset;
};
struct spu_gang {
struct list_head list;
struct mutex mutex;
struct kref kref;
int contexts;
struct spu_context *aff_ref_ctx;
struct list_head aff_list_head;
struct mutex aff_mutex;
int aff_flags;
struct spu *aff_ref_spu;
atomic_t aff_sched_count;
};
/* Flag bits for spu_gang aff_flags */
#define AFF_OFFSETS_SET 1
#define AFF_MERGED 2
struct mfc_dma_command {
int32_t pad; /* reserved */
uint32_t lsa; /* local storage address */
uint64_t ea; /* effective address */
uint16_t size; /* transfer size */
uint16_t tag; /* command tag */
uint16_t class; /* class ID */
uint16_t cmd; /* command opcode */
};
/* SPU context query/set operations. */
struct spu_context_ops {
int (*mbox_read) (struct spu_context * ctx, u32 * data);
u32(*mbox_stat_read) (struct spu_context * ctx);
unsigned int (*mbox_stat_poll)(struct spu_context *ctx,
unsigned int events);
int (*ibox_read) (struct spu_context * ctx, u32 * data);
int (*wbox_write) (struct spu_context * ctx, u32 data);
u32(*signal1_read) (struct spu_context * ctx);
void (*signal1_write) (struct spu_context * ctx, u32 data);
u32(*signal2_read) (struct spu_context * ctx);
void (*signal2_write) (struct spu_context * ctx, u32 data);
void (*signal1_type_set) (struct spu_context * ctx, u64 val);
u64(*signal1_type_get) (struct spu_context * ctx);
void (*signal2_type_set) (struct spu_context * ctx, u64 val);
u64(*signal2_type_get) (struct spu_context * ctx);
u32(*npc_read) (struct spu_context * ctx);
void (*npc_write) (struct spu_context * ctx, u32 data);
u32(*status_read) (struct spu_context * ctx);
char*(*get_ls) (struct spu_context * ctx);
void (*privcntl_write) (struct spu_context *ctx, u64 data);
u32 (*runcntl_read) (struct spu_context * ctx);
void (*runcntl_write) (struct spu_context * ctx, u32 data);
void (*runcntl_stop) (struct spu_context * ctx);
void (*master_start) (struct spu_context * ctx);
void (*master_stop) (struct spu_context * ctx);
int (*set_mfc_query)(struct spu_context * ctx, u32 mask, u32 mode);
u32 (*read_mfc_tagstatus)(struct spu_context * ctx);
u32 (*get_mfc_free_elements)(struct spu_context *ctx);
int (*send_mfc_command)(struct spu_context * ctx,
struct mfc_dma_command * cmd);
void (*dma_info_read) (struct spu_context * ctx,
struct spu_dma_info * info);
void (*proxydma_info_read) (struct spu_context * ctx,
struct spu_proxydma_info * info);
void (*restart_dma)(struct spu_context *ctx);
};
extern struct spu_context_ops spu_hw_ops;
extern struct spu_context_ops spu_backing_ops;
struct spufs_inode_info {
struct spu_context *i_ctx;
struct spu_gang *i_gang;
struct inode vfs_inode;
int i_openers;
};
#define SPUFS_I(inode) \
container_of(inode, struct spufs_inode_info, vfs_inode)
struct spufs_tree_descr {
const char *name;
const struct file_operations *ops;
int mode;
size_t size;
};
extern const struct spufs_tree_descr spufs_dir_contents[];
extern const struct spufs_tree_descr spufs_dir_nosched_contents[];
extern const struct spufs_tree_descr spufs_dir_debug_contents[];
/* system call implementation */
extern struct spufs_calls spufs_calls;
long spufs_run_spu(struct spu_context *ctx, u32 *npc, u32 *status);
long spufs_create(struct nameidata *nd, unsigned int flags,
mode_t mode, struct file *filp);
/* ELF coredump callbacks for writing SPU ELF notes */
extern int spufs_coredump_extra_notes_size(void);
extern int spufs_coredump_extra_notes_write(struct file *file, loff_t *foffset);
extern const struct file_operations spufs_context_fops;
/* gang management */
struct spu_gang *alloc_spu_gang(void);
struct spu_gang *get_spu_gang(struct spu_gang *gang);
int put_spu_gang(struct spu_gang *gang);
void spu_gang_remove_ctx(struct spu_gang *gang, struct spu_context *ctx);
void spu_gang_add_ctx(struct spu_gang *gang, struct spu_context *ctx);
/* fault handling */
int spufs_handle_class1(struct spu_context *ctx);
int spufs_handle_class0(struct spu_context *ctx);
/* affinity */
struct spu *affinity_check(struct spu_context *ctx);
/* context management */
extern atomic_t nr_spu_contexts;
static inline int __must_check spu_acquire(struct spu_context *ctx)
{
return mutex_lock_interruptible(&ctx->state_mutex);
}
static inline void spu_release(struct spu_context *ctx)
{
mutex_unlock(&ctx->state_mutex);
}
struct spu_context * alloc_spu_context(struct spu_gang *gang);
void destroy_spu_context(struct kref *kref);
struct spu_context * get_spu_context(struct spu_context *ctx);
int put_spu_context(struct spu_context *ctx);
void spu_unmap_mappings(struct spu_context *ctx);
void spu_forget(struct spu_context *ctx);
int __must_check spu_acquire_saved(struct spu_context *ctx);
void spu_release_saved(struct spu_context *ctx);
int spu_stopped(struct spu_context *ctx, u32 * stat);
void spu_del_from_rq(struct spu_context *ctx);
int spu_activate(struct spu_context *ctx, unsigned long flags);
void spu_deactivate(struct spu_context *ctx);
void spu_yield(struct spu_context *ctx);
void spu_switch_notify(struct spu *spu, struct spu_context *ctx);
void spu_switch_log_notify(struct spu *spu, struct spu_context *ctx,
u32 type, u32 val);
void spu_set_timeslice(struct spu_context *ctx);
void spu_update_sched_info(struct spu_context *ctx);
void __spu_update_sched_info(struct spu_context *ctx);
int __init spu_sched_init(void);
void spu_sched_exit(void);
extern char *isolated_loader;
/*
* spufs_wait
* Same as wait_event_interruptible(), except that here
* we need to call spu_release(ctx) before sleeping, and
* then spu_acquire(ctx) when awoken.
*
* Returns with state_mutex re-acquired when successful or
* with -ERESTARTSYS and the state_mutex dropped when interrupted.
*/
#define spufs_wait(wq, condition) \
({ \
int __ret = 0; \
DEFINE_WAIT(__wait); \
for (;;) { \
prepare_to_wait(&(wq), &__wait, TASK_INTERRUPTIBLE); \
if (condition) \
break; \
spu_release(ctx); \
if (signal_pending(current)) { \
__ret = -ERESTARTSYS; \
break; \
} \
schedule(); \
__ret = spu_acquire(ctx); \
if (__ret) \
break; \
} \
finish_wait(&(wq), &__wait); \
__ret; \
})
size_t spu_wbox_write(struct spu_context *ctx, u32 data);
size_t spu_ibox_read(struct spu_context *ctx, u32 *data);
/* irq callback funcs. */
void spufs_ibox_callback(struct spu *spu);
void spufs_wbox_callback(struct spu *spu);
void spufs_stop_callback(struct spu *spu, int irq);
void spufs_mfc_callback(struct spu *spu);
void spufs_dma_callback(struct spu *spu, int type);
extern struct spu_coredump_calls spufs_coredump_calls;
struct spufs_coredump_reader {
char *name;
ssize_t (*read)(struct spu_context *ctx,
char __user *buffer, size_t size, loff_t *pos);
u64 (*get)(struct spu_context *ctx);
size_t size;
};
extern const struct spufs_coredump_reader spufs_coredump_read[];
extern int spufs_coredump_num_notes;
extern int spu_init_csa(struct spu_state *csa);
extern void spu_fini_csa(struct spu_state *csa);
extern int spu_save(struct spu_state *prev, struct spu *spu);
extern int spu_restore(struct spu_state *new, struct spu *spu);
extern int spu_switch(struct spu_state *prev, struct spu_state *new,
struct spu *spu);
extern int spu_alloc_lscsa(struct spu_state *csa);
extern void spu_free_lscsa(struct spu_state *csa);
extern void spuctx_switch_state(struct spu_context *ctx,
enum spu_utilization_state new_state);
#endif

View File

@@ -0,0 +1,39 @@
#if !defined(_TRACE_SPUFS_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_SPUFS_H
#include <linux/tracepoint.h>
#undef TRACE_SYSTEM
#define TRACE_SYSTEM spufs
TRACE_EVENT(spufs_context,
TP_PROTO(struct spu_context *ctx, struct spu *spu, const char *name),
TP_ARGS(ctx, spu, name),
TP_STRUCT__entry(
__field(const char *, name)
__field(int, owner_tid)
__field(int, number)
),
TP_fast_assign(
__entry->name = name;
__entry->owner_tid = ctx->tid;
__entry->number = spu ? spu->number : -1;
),
TP_printk("%s (ctxthread = %d, spu = %d)",
__entry->name, __entry->owner_tid, __entry->number)
);
#define spu_context_trace(name, ctx, spu) \
trace_spufs_context(ctx, spu, __stringify(name))
#define spu_context_nospu_trace(name, ctx) \
trace_spufs_context(ctx, NULL, __stringify(name))
#endif /* _TRACE_SPUFS_H */
#undef TRACE_INCLUDE_PATH
#define TRACE_INCLUDE_PATH .
#define TRACE_INCLUDE_FILE sputrace
#include <trace/define_trace.h>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,91 @@
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/mount.h>
#include <linux/namei.h>
#include <asm/uaccess.h>
#include "spufs.h"
/**
* sys_spu_run - run code loaded into an SPU
*
* @unpc: next program counter for the SPU
* @ustatus: status of the SPU
*
* This system call transfers the control of execution of a
* user space thread to an SPU. It will return when the
* SPU has finished executing or when it hits an error
* condition and it will be interrupted if a signal needs
* to be delivered to a handler in user space.
*
* The next program counter is set to the passed value
* before the SPU starts fetching code and the user space
* pointer gets updated with the new value when returning
* from kernel space.
*
* The status value returned from spu_run reflects the
* value of the spu_status register after the SPU has stopped.
*
*/
static long do_spu_run(struct file *filp,
__u32 __user *unpc,
__u32 __user *ustatus)
{
long ret;
struct spufs_inode_info *i;
u32 npc, status;
ret = -EFAULT;
if (get_user(npc, unpc))
goto out;
/* check if this file was created by spu_create */
ret = -EINVAL;
if (filp->f_op != &spufs_context_fops)
goto out;
i = SPUFS_I(filp->f_path.dentry->d_inode);
ret = spufs_run_spu(i->i_ctx, &npc, &status);
if (put_user(npc, unpc))
ret = -EFAULT;
if (ustatus && put_user(status, ustatus))
ret = -EFAULT;
out:
return ret;
}
static long do_spu_create(const char __user *pathname, unsigned int flags,
mode_t mode, struct file *neighbor)
{
char *tmp;
int ret;
tmp = getname(pathname);
ret = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
struct nameidata nd;
ret = path_lookup(tmp, LOOKUP_PARENT, &nd);
if (!ret) {
nd.flags |= LOOKUP_OPEN | LOOKUP_CREATE;
ret = spufs_create(&nd, flags, mode, neighbor);
path_put(&nd.path);
}
putname(tmp);
}
return ret;
}
struct spufs_calls spufs_calls = {
.create_thread = do_spu_create,
.spu_run = do_spu_run,
.coredump_extra_notes_size = spufs_coredump_extra_notes_size,
.coredump_extra_notes_write = spufs_coredump_extra_notes_write,
.notify_spus_active = do_notify_spus_active,
.owner = THIS_MODULE,
};