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

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

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

#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);
}

21 22 23 24 25 26 27 28 29 30
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:
31
		case SPE_EVENT_SPE_DATA_STORAGE:
32 33 34 35 36 37 38 39 40 41
		case SPE_EVENT_INVALID_DMA:
			force_sig(SIGBUS, /* info, */ current);
			break;
		case SPE_EVENT_SPE_ERROR:
			force_sig(SIGILL, /* info */ current);
			break;
		}
	}
}

42 43 44 45 46 47 48 49 50 51 52 53 54 55
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;
}

56 57 58 59 60 61 62 63 64 65 66 67 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 103 104 105 106 107 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
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;

	if (!isolated_loader)
		return -ENODEV;

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

	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",
					__FUNCTION__);
			ret = -EIO;
			goto out_unlock;
		}
		cond_resched();
	}

	/* 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",
					__FUNCTION__);
			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", __FUNCTION__);
		ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_RUNNABLE);
		ret = -EACCES;

	} else 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", __FUNCTION__);
		ctx->ops->runcntl_write(ctx, SPU_RUNCNTL_STOP);
		ret = -EINVAL;
	}

out_drop_priv:
	/* Finished accessing the loader. Drop kernel mode */
	sr1 |= MFC_STATE1_PROBLEM_STATE_MASK;
	spu_mfc_sr1_set(ctx->spu, sr1);

out_unlock:
136
	spu_release(ctx);
137 138 139 140
out:
	return ret;
}

141
static inline int spu_run_init(struct spu_context *ctx, u32 * npc)
142 143
{
	int ret;
144
	unsigned long runcntl = SPU_RUNCNTL_RUNNABLE;
145

146
	ret = spu_acquire_runnable(ctx, 0);
147
	if (ret)
148
		return ret;
149

150 151 152 153 154 155 156 157
	if (ctx->flags & SPU_CREATE_ISOLATE) {
		if (!(ctx->ops->status_read(ctx) & SPU_STATUS_ISOLATED_STATE)) {
			/* Need to release ctx, because spu_setup_isolated will
			 * acquire it exclusively.
			 */
			spu_release(ctx);
			ret = spu_setup_isolated(ctx);
			if (!ret)
158
				ret = spu_acquire_runnable(ctx, 0);
159 160 161 162 163 164 165 166
		}

		/* 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;
167 168
	} else {
		spu_start_tick(ctx);
169
		ctx->ops->npc_write(ctx, *npc);
170
	}
171 172 173

	ctx->ops->runcntl_write(ctx, runcntl);
	return ret;
174 175 176 177 178 179 180
}

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

181
	spu_stop_tick(ctx);
182 183 184 185 186 187
	*status = ctx->ops->status_read(ctx);
	*npc = ctx->ops->npc_read(ctx);
	spu_release(ctx);

	if (signal_pending(current))
		ret = -ERESTARTSYS;
188

189 190 191 192 193 194 195 196 197 198 199 200 201 202
	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;
	}
203
	if ((ret = spu_run_init(ctx, npc)) != 0)
204 205 206 207
		return ret;
	return 0;
}

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291
/*
 * 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;
}

292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
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,
308
		   u32 *npc, u32 *event)
309 310
{
	int ret;
311
	u32 status;
312 313 314 315

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

316
	ctx->ops->master_start(ctx);
317 318
	ctx->event_return = 0;
	ret = spu_run_init(ctx, npc);
319 320 321 322
	if (ret)
		goto out;

	do {
323
		ret = spufs_wait(ctx->stop_wq, spu_stopped(ctx, &status));
324 325
		if (unlikely(ret))
			break;
326 327
		if ((status & SPU_STATUS_STOPPED_BY_STOP) &&
		    (status >> SPU_STOP_STATUS_SHIFT == 0x2104)) {
328 329 330
			ret = spu_process_callback(ctx);
			if (ret)
				break;
331
			status &= ~SPU_STATUS_STOPPED_BY_STOP;
332
		}
333
		if (unlikely(ctx->state != SPU_STATE_RUNNABLE)) {
334
			ret = spu_reacquire_runnable(ctx, npc, &status);
335 336
			if (ret) {
				spu_stop_tick(ctx);
337
				goto out2;
338
			}
339 340 341 342
			continue;
		}
		ret = spu_process_events(ctx);

343
	} while (!ret && !(status & (SPU_STATUS_STOPPED_BY_STOP |
344 345
				      SPU_STATUS_STOPPED_BY_HALT)));

346
	ctx->ops->master_stop(ctx);
347
	ret = spu_run_fini(ctx, npc, &status);
348 349
	spu_yield(ctx);

350 351 352 353 354 355 356 357
out2:
	if ((ret == 0) ||
	    ((ret == -ERESTARTSYS) &&
	     ((status & SPU_STATUS_STOPPED_BY_HALT) ||
	      ((status & SPU_STATUS_STOPPED_BY_STOP) &&
	       (status >> SPU_STOP_STATUS_SHIFT != 0x2104)))))
		ret = status;

358 359 360 361
	if ((status & SPU_STATUS_STOPPED_BY_STOP)
	    && (status >> SPU_STOP_STATUS_SHIFT) == 0x3fff) {
		force_sig(SIGTRAP, current);
		ret = -ERESTARTSYS;
362 363
	}

364
out:
365
	*event = ctx->event_return;
366 367 368
	up(&ctx->run_sema);
	return ret;
}