static int emulate_spe()

in kernel/align.c [107:288]


static int emulate_spe(struct pt_regs *regs, unsigned int reg,
		       ppc_inst_t ppc_instr)
{
	union {
		u64 ll;
		u32 w[2];
		u16 h[4];
		u8 v[8];
	} data, temp;
	unsigned char __user *p, *addr;
	unsigned long *evr = &current->thread.evr[reg];
	unsigned int nb, flags, instr;

	instr = ppc_inst_val(ppc_instr);
	instr = (instr >> 1) & 0x1f;

	/* DAR has the operand effective address */
	addr = (unsigned char __user *)regs->dar;

	nb = spe_aligninfo[instr].len;
	flags = spe_aligninfo[instr].flags;

	/* userland only */
	if (unlikely(!user_mode(regs)))
		return 0;

	flush_spe_to_thread(current);

	/* If we are loading, get the data from user space, else
	 * get it from register values
	 */
	if (flags & ST) {
		data.ll = 0;
		switch (instr) {
		case EVSTDD:
		case EVSTDW:
		case EVSTDH:
			data.w[0] = *evr;
			data.w[1] = regs->gpr[reg];
			break;
		case EVSTWHE:
			data.h[2] = *evr >> 16;
			data.h[3] = regs->gpr[reg] >> 16;
			break;
		case EVSTWHO:
			data.h[2] = *evr & 0xffff;
			data.h[3] = regs->gpr[reg] & 0xffff;
			break;
		case EVSTWWE:
			data.w[1] = *evr;
			break;
		case EVSTWWO:
			data.w[1] = regs->gpr[reg];
			break;
		default:
			return -EINVAL;
		}
	} else {
		temp.ll = data.ll = 0;
		p = addr;

		if (!user_read_access_begin(addr, nb))
			return -EFAULT;

		switch (nb) {
		case 8:
			unsafe_get_user(temp.v[0], p++, Efault_read);
			unsafe_get_user(temp.v[1], p++, Efault_read);
			unsafe_get_user(temp.v[2], p++, Efault_read);
			unsafe_get_user(temp.v[3], p++, Efault_read);
			fallthrough;
		case 4:
			unsafe_get_user(temp.v[4], p++, Efault_read);
			unsafe_get_user(temp.v[5], p++, Efault_read);
			fallthrough;
		case 2:
			unsafe_get_user(temp.v[6], p++, Efault_read);
			unsafe_get_user(temp.v[7], p++, Efault_read);
		}
		user_read_access_end();

		switch (instr) {
		case EVLDD:
		case EVLDW:
		case EVLDH:
			data.ll = temp.ll;
			break;
		case EVLHHESPLAT:
			data.h[0] = temp.h[3];
			data.h[2] = temp.h[3];
			break;
		case EVLHHOUSPLAT:
		case EVLHHOSSPLAT:
			data.h[1] = temp.h[3];
			data.h[3] = temp.h[3];
			break;
		case EVLWHE:
			data.h[0] = temp.h[2];
			data.h[2] = temp.h[3];
			break;
		case EVLWHOU:
		case EVLWHOS:
			data.h[1] = temp.h[2];
			data.h[3] = temp.h[3];
			break;
		case EVLWWSPLAT:
			data.w[0] = temp.w[1];
			data.w[1] = temp.w[1];
			break;
		case EVLWHSPLAT:
			data.h[0] = temp.h[2];
			data.h[1] = temp.h[2];
			data.h[2] = temp.h[3];
			data.h[3] = temp.h[3];
			break;
		default:
			return -EINVAL;
		}
	}

	if (flags & SW) {
		switch (flags & 0xf0) {
		case E8:
			data.ll = swab64(data.ll);
			break;
		case E4:
			data.w[0] = swab32(data.w[0]);
			data.w[1] = swab32(data.w[1]);
			break;
		/* Its half word endian */
		default:
			data.h[0] = swab16(data.h[0]);
			data.h[1] = swab16(data.h[1]);
			data.h[2] = swab16(data.h[2]);
			data.h[3] = swab16(data.h[3]);
			break;
		}
	}

	if (flags & SE) {
		data.w[0] = (s16)data.h[1];
		data.w[1] = (s16)data.h[3];
	}

	/* Store result to memory or update registers */
	if (flags & ST) {
		p = addr;

		if (!user_write_access_begin(addr, nb))
			return -EFAULT;

		switch (nb) {
		case 8:
			unsafe_put_user(data.v[0], p++, Efault_write);
			unsafe_put_user(data.v[1], p++, Efault_write);
			unsafe_put_user(data.v[2], p++, Efault_write);
			unsafe_put_user(data.v[3], p++, Efault_write);
			fallthrough;
		case 4:
			unsafe_put_user(data.v[4], p++, Efault_write);
			unsafe_put_user(data.v[5], p++, Efault_write);
			fallthrough;
		case 2:
			unsafe_put_user(data.v[6], p++, Efault_write);
			unsafe_put_user(data.v[7], p++, Efault_write);
		}
		user_write_access_end();
	} else {
		*evr = data.w[0];
		regs->gpr[reg] = data.w[1];
	}

	return 1;

Efault_read:
	user_read_access_end();
	return -EFAULT;

Efault_write:
	user_write_access_end();
	return -EFAULT;
}