static int handle_unaligned_ins()

in kernel/traps_32.c [85:259]


static int handle_unaligned_ins(insn_size_t instruction, struct pt_regs *regs,
				struct mem_access *ma)
{
	int ret, index, count;
	unsigned long *rm, *rn;
	unsigned char *src, *dst;
	unsigned char __user *srcu, *dstu;

	index = (instruction>>8)&15;	/* 0x0F00 */
	rn = &regs->regs[index];

	index = (instruction>>4)&15;	/* 0x00F0 */
	rm = &regs->regs[index];

	count = 1<<(instruction&3);

	switch (count) {
	case 1: inc_unaligned_byte_access(); break;
	case 2: inc_unaligned_word_access(); break;
	case 4: inc_unaligned_dword_access(); break;
	case 8: inc_unaligned_multi_access(); break;
	}

	ret = -EFAULT;
	switch (instruction>>12) {
	case 0: /* mov.[bwl] to/from memory via r0+rn */
		if (instruction & 8) {
			/* from memory */
			srcu = (unsigned char __user *)*rm;
			srcu += regs->regs[0];
			dst = (unsigned char *)rn;
			*(unsigned long *)dst = 0;

#if !defined(__LITTLE_ENDIAN__)
			dst += 4-count;
#endif
			if (ma->from(dst, srcu, count))
				goto fetch_fault;

			sign_extend(count, dst);
		} else {
			/* to memory */
			src = (unsigned char *)rm;
#if !defined(__LITTLE_ENDIAN__)
			src += 4-count;
#endif
			dstu = (unsigned char __user *)*rn;
			dstu += regs->regs[0];

			if (ma->to(dstu, src, count))
				goto fetch_fault;
		}
		ret = 0;
		break;

	case 1: /* mov.l Rm,@(disp,Rn) */
		src = (unsigned char*) rm;
		dstu = (unsigned char __user *)*rn;
		dstu += (instruction&0x000F)<<2;

		if (ma->to(dstu, src, 4))
			goto fetch_fault;
		ret = 0;
		break;

	case 2: /* mov.[bwl] to memory, possibly with pre-decrement */
		if (instruction & 4)
			*rn -= count;
		src = (unsigned char*) rm;
		dstu = (unsigned char __user *)*rn;
#if !defined(__LITTLE_ENDIAN__)
		src += 4-count;
#endif
		if (ma->to(dstu, src, count))
			goto fetch_fault;
		ret = 0;
		break;

	case 5: /* mov.l @(disp,Rm),Rn */
		srcu = (unsigned char __user *)*rm;
		srcu += (instruction & 0x000F) << 2;
		dst = (unsigned char *)rn;
		*(unsigned long *)dst = 0;

		if (ma->from(dst, srcu, 4))
			goto fetch_fault;
		ret = 0;
		break;

	case 6:	/* mov.[bwl] from memory, possibly with post-increment */
		srcu = (unsigned char __user *)*rm;
		if (instruction & 4)
			*rm += count;
		dst = (unsigned char*) rn;
		*(unsigned long*)dst = 0;

#if !defined(__LITTLE_ENDIAN__)
		dst += 4-count;
#endif
		if (ma->from(dst, srcu, count))
			goto fetch_fault;
		sign_extend(count, dst);
		ret = 0;
		break;

	case 8:
		switch ((instruction&0xFF00)>>8) {
		case 0x81: /* mov.w R0,@(disp,Rn) */
			src = (unsigned char *) &regs->regs[0];
#if !defined(__LITTLE_ENDIAN__)
			src += 2;
#endif
			dstu = (unsigned char __user *)*rm; /* called Rn in the spec */
			dstu += (instruction & 0x000F) << 1;

			if (ma->to(dstu, src, 2))
				goto fetch_fault;
			ret = 0;
			break;

		case 0x85: /* mov.w @(disp,Rm),R0 */
			srcu = (unsigned char __user *)*rm;
			srcu += (instruction & 0x000F) << 1;
			dst = (unsigned char *) &regs->regs[0];
			*(unsigned long *)dst = 0;

#if !defined(__LITTLE_ENDIAN__)
			dst += 2;
#endif
			if (ma->from(dst, srcu, 2))
				goto fetch_fault;
			sign_extend(2, dst);
			ret = 0;
			break;
		}
		break;

	case 9: /* mov.w @(disp,PC),Rn */
		srcu = (unsigned char __user *)regs->pc;
		srcu += 4;
		srcu += (instruction & 0x00FF) << 1;
		dst = (unsigned char *)rn;
		*(unsigned long *)dst = 0;

#if !defined(__LITTLE_ENDIAN__)
		dst += 2;
#endif

		if (ma->from(dst, srcu, 2))
			goto fetch_fault;
		sign_extend(2, dst);
		ret = 0;
		break;

	case 0xd: /* mov.l @(disp,PC),Rn */
		srcu = (unsigned char __user *)(regs->pc & ~0x3);
		srcu += 4;
		srcu += (instruction & 0x00FF) << 2;
		dst = (unsigned char *)rn;
		*(unsigned long *)dst = 0;

		if (ma->from(dst, srcu, 4))
			goto fetch_fault;
		ret = 0;
		break;
	}
	return ret;

 fetch_fault:
	/* Argh. Address not only misaligned but also non-existent.
	 * Raise an EFAULT and see if it's trapped
	 */
	die_if_no_fixup("Fault in unaligned fixup", regs, 0);
	return -EFAULT;
}