run.c 5.7 KB
Newer Older
1 2
#define DEBUG

3 4 5 6
#include <linux/wait.h>
#include <linux/ptrace.h>

#include <asm/spu.h>
D
Dave Jones 已提交
7
#include <asm/unistd.h>
8 9 10 11 12 13 14 15 16 17 18

#include "spufs.h"

/* interrupt-level stop callback function. */
void spufs_stop_callback(struct spu *spu)
{
	struct spu_context *ctx = spu->ctx;

	wake_up_all(&ctx->stop_wq);
}

19 20 21 22 23 24 25 26 27 28
void spufs_dma_callback(struct spu *spu, int type)
{
	struct spu_context *ctx = spu->ctx;

	if (ctx->flags & SPU_CREATE_EVENTS_ENABLED) {
		ctx->event_return |= type;
		wake_up_all(&ctx->stop_wq);
	} else {
		switch (type) {
		case SPE_EVENT_DMA_ALIGNMENT:
29
		case SPE_EVENT_SPE_DATA_STORAGE:
30 31 32 33 34 35 36 37 38 39
		case SPE_EVENT_INVALID_DMA:
			force_sig(SIGBUS, /* info, */ current);
			break;
		case SPE_EVENT_SPE_ERROR:
			force_sig(SIGILL, /* info */ current);
			break;
		}
	}
}

40 41 42 43 44 45 46 47 48 49 50 51 52 53
static inline int spu_stopped(struct spu_context *ctx, u32 * stat)
{
	struct spu *spu;
	u64 pte_fault;

	*stat = ctx->ops->status_read(ctx);
	if (ctx->state != SPU_STATE_RUNNABLE)
		return 1;
	spu = ctx->spu;
	pte_fault = spu->dsisr &
	    (MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED);
	return (!(*stat & 0x1) || pte_fault || spu->class_0_pending) ? 1 : 0;
}

54
static inline int spu_run_init(struct spu_context *ctx, u32 * npc)
55 56
{
	int ret;
57
	unsigned long runcntl = SPU_RUNCNTL_RUNNABLE;
58 59 60

	if ((ret = spu_acquire_runnable(ctx)) != 0)
		return ret;
61

62 63 64
	/* if we're in isolated mode, we would have started the SPU
	 * earlier, so don't do it again now. */
	if (!(ctx->flags & SPU_CREATE_ISOLATE)) {
65
		ctx->ops->npc_write(ctx, *npc);
66 67
		ctx->ops->runcntl_write(ctx, runcntl);
	}
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
	return 0;
}

static inline int spu_run_fini(struct spu_context *ctx, u32 * npc,
			       u32 * status)
{
	int ret = 0;

	*status = ctx->ops->status_read(ctx);
	*npc = ctx->ops->npc_read(ctx);
	spu_release(ctx);

	if (signal_pending(current))
		ret = -ERESTARTSYS;
	if (unlikely(current->ptrace & PT_PTRACED)) {
		if ((*status & SPU_STATUS_STOPPED_BY_STOP)
		    && (*status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
			force_sig(SIGTRAP, current);
			ret = -ERESTARTSYS;
		}
	}
	return ret;
}

static inline int spu_reacquire_runnable(struct spu_context *ctx, u32 *npc,
				         u32 *status)
{
	int ret;

	if ((ret = spu_run_fini(ctx, npc, status)) != 0)
		return ret;
	if (*status & (SPU_STATUS_STOPPED_BY_STOP |
		       SPU_STATUS_STOPPED_BY_HALT)) {
		return *status;
	}
103
	if ((ret = spu_run_init(ctx, npc)) != 0)
104 105 106 107
		return ret;
	return 0;
}

108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191
/*
 * 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.
 */
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",
			__FUNCTION__, *spu_ret);
		ret = 0;
	}
	return ret;
}

int spu_process_callback(struct spu_context *ctx)
{
	struct spu_syscall_block s;
	u32 ls_pointer, npc;
	char *ls;
	long spu_ret;
	int ret;

	/* get syscall block from local store */
	npc = ctx->ops->npc_read(ctx);
	ls = ctx->ops->get_ls(ctx);
	ls_pointer = *(u32*)(ls + npc);
	if (ls_pointer > (LS_SIZE - sizeof(s)))
		return -EFAULT;
	memcpy(&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);
		}
		spu_acquire(ctx);
		if (ret == -ERESTARTSYS)
			return ret;
	}

	/* write result, jump over indirect pointer */
	memcpy(ls + ls_pointer, &spu_ret, sizeof (spu_ret));
	ctx->ops->npc_write(ctx, npc);
	ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
	return ret;
}

192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
static inline int spu_process_events(struct spu_context *ctx)
{
	struct spu *spu = ctx->spu;
	u64 pte_fault = MFC_DSISR_PTE_NOT_FOUND | MFC_DSISR_ACCESS_DENIED;
	int ret = 0;

	if (spu->dsisr & pte_fault)
		ret = spu_irq_class_1_bottom(spu);
	if (spu->class_0_pending)
		ret = spu_irq_class_0_bottom(spu);
	if (!ret && signal_pending(current))
		ret = -ERESTARTSYS;
	return ret;
}

long spufs_run_spu(struct file *file, struct spu_context *ctx,
208
		   u32 *npc, u32 *event)
209 210
{
	int ret;
211
	u32 status;
212 213 214 215

	if (down_interruptible(&ctx->run_sema))
		return -ERESTARTSYS;

216 217
	ctx->event_return = 0;
	ret = spu_run_init(ctx, npc);
218 219 220 221
	if (ret)
		goto out;

	do {
222
		ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
223 224
		if (unlikely(ret))
			break;
225 226
		if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
		    (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
227 228 229
			ret = spu_process_callback(ctx);
			if (ret)
				break;
230
			status &= ~SPU_STATUS_STOPPED_BY_STOP;
231
		}
232
		if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
233
			ret = spu_reacquire_runnable(ctx, npc, &status);
234 235 236 237 238 239
			if (ret)
				goto out;
			continue;
		}
		ret = spu_process_events(ctx);

240
	} while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
241 242 243
				      SPU_STATUS_STOPPED_BY_HALT)));

	ctx->ops->runcntl_stop(ctx);
244
	ret = spu_run_fini(ctx, npc, &status);
245
	if (!ret)
246
		ret = status;
247 248 249
	spu_yield(ctx);

out:
250
	*event = ctx->event_return;
251 252 253 254
	up(&ctx->run_sema);
	return ret;
}