Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

  • 61acfe9
  • /
  • arch
  • /
  • arc
  • /
  • kernel
  • /
  • disasm.c
Raw File Download
Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
content badge Iframe embedding
swh:1:cnt:3b7cd4864ba20fa67896de08913395d9978a2dd5
directory badge Iframe embedding
swh:1:dir:53dc487de6e820eb3cbd2687198a8f15fb8ba574
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
disasm.c
/*
 * several functions that help interpret ARC instructions
 * used for unaligned accesses, kprobes and kgdb
 *
 * Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
 */

#include <linux/types.h>
#include <linux/kprobes.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <asm/disasm.h>

#if defined(CONFIG_KGDB) || defined(CONFIG_ARC_EMUL_UNALIGNED) || \
	defined(CONFIG_KPROBES)

/* disasm_instr: Analyses instruction at addr, stores
 * findings in *state
 */
void __kprobes disasm_instr(unsigned long addr, struct disasm_state *state,
	int userspace, struct pt_regs *regs, struct callee_regs *cregs)
{
	int fieldA = 0;
	int fieldC = 0, fieldCisReg = 0;
	uint16_t word1 = 0, word0 = 0;
	int subopcode, is_linked, op_format;
	uint16_t *ins_ptr;
	uint16_t ins_buf[4];
	int bytes_not_copied = 0;

	memset(state, 0, sizeof(struct disasm_state));

	/* This fetches the upper part of the 32 bit instruction
	 * in both the cases of Little Endian or Big Endian configurations. */
	if (userspace) {
		bytes_not_copied = copy_from_user(ins_buf,
						(const void __user *) addr, 8);
		if (bytes_not_copied > 6)
			goto fault;
		ins_ptr = ins_buf;
	} else {
		ins_ptr = (uint16_t *) addr;
	}

	word1 = *((uint16_t *)addr);

	state->major_opcode = (word1 >> 11) & 0x1F;

	/* Check if the instruction is 32 bit or 16 bit instruction */
	if (state->major_opcode < 0x0B) {
		if (bytes_not_copied > 4)
			goto fault;
		state->instr_len = 4;
		word0 = *((uint16_t *)(addr+2));
		state->words[0] = (word1 << 16) | word0;
	} else {
		state->instr_len = 2;
		state->words[0] = word1;
	}

	/* Read the second word in case of limm */
	word1 = *((uint16_t *)(addr + state->instr_len));
	word0 = *((uint16_t *)(addr + state->instr_len + 2));
	state->words[1] = (word1 << 16) | word0;

	switch (state->major_opcode) {
	case op_Bcc:
		state->is_branch = 1;

		/* unconditional branch s25, conditional branch s21 */
		fieldA = (IS_BIT(state->words[0], 16)) ?
			FIELD_s25(state->words[0]) :
			FIELD_s21(state->words[0]);

		state->delay_slot = IS_BIT(state->words[0], 5);
		state->target = fieldA + (addr & ~0x3);
		state->flow = direct_jump;
		break;

	case op_BLcc:
		if (IS_BIT(state->words[0], 16)) {
			/* Branch and Link*/
			/* unconditional branch s25, conditional branch s21 */
			fieldA = (IS_BIT(state->words[0], 17)) ?
				(FIELD_s25(state->words[0]) & ~0x3) :
				FIELD_s21(state->words[0]);

			state->flow = direct_call;
		} else {
			/*Branch On Compare */
			fieldA = FIELD_s9(state->words[0]) & ~0x3;
			state->flow = direct_jump;
		}

		state->delay_slot = IS_BIT(state->words[0], 5);
		state->target = fieldA + (addr & ~0x3);
		state->is_branch = 1;
		break;

	case op_LD:  /* LD<zz> a,[b,s9] */
		state->write = 0;
		state->di = BITS(state->words[0], 11, 11);
		if (state->di)
			break;
		state->x = BITS(state->words[0], 6, 6);
		state->zz = BITS(state->words[0], 7, 8);
		state->aa = BITS(state->words[0], 9, 10);
		state->wb_reg = FIELD_B(state->words[0]);
		if (state->wb_reg == REG_LIMM) {
			state->instr_len += 4;
			state->aa = 0;
			state->src1 = state->words[1];
		} else {
			state->src1 = get_reg(state->wb_reg, regs, cregs);
		}
		state->src2 = FIELD_s9(state->words[0]);
		state->dest = FIELD_A(state->words[0]);
		state->pref = (state->dest == REG_LIMM);
		break;

	case op_ST:
		state->write = 1;
		state->di = BITS(state->words[0], 5, 5);
		if (state->di)
			break;
		state->aa = BITS(state->words[0], 3, 4);
		state->zz = BITS(state->words[0], 1, 2);
		state->src1 = FIELD_C(state->words[0]);
		if (state->src1 == REG_LIMM) {
			state->instr_len += 4;
			state->src1 = state->words[1];
		} else {
			state->src1 = get_reg(state->src1, regs, cregs);
		}
		state->wb_reg = FIELD_B(state->words[0]);
		if (state->wb_reg == REG_LIMM) {
			state->aa = 0;
			state->instr_len += 4;
			state->src2 = state->words[1];
		} else {
			state->src2 = get_reg(state->wb_reg, regs, cregs);
		}
		state->src3 = FIELD_s9(state->words[0]);
		break;

	case op_MAJOR_4:
		subopcode = MINOR_OPCODE(state->words[0]);
		switch (subopcode) {
		case 32:	/* Jcc */
		case 33:	/* Jcc.D */
		case 34:	/* JLcc */
		case 35:	/* JLcc.D */
			is_linked = 0;

			if (subopcode == 33 || subopcode == 35)
				state->delay_slot = 1;

			if (subopcode == 34 || subopcode == 35)
				is_linked = 1;

			fieldCisReg = 0;
			op_format = BITS(state->words[0], 22, 23);
			if (op_format == 0 || ((op_format == 3) &&
				(!IS_BIT(state->words[0], 5)))) {
				fieldC = FIELD_C(state->words[0]);

				if (fieldC == REG_LIMM) {
					fieldC = state->words[1];
					state->instr_len += 4;
				} else {
					fieldCisReg = 1;
				}
			} else if (op_format == 1 || ((op_format == 3)
				&& (IS_BIT(state->words[0], 5)))) {
				fieldC = FIELD_C(state->words[0]);
			} else  {
				/* op_format == 2 */
				fieldC = FIELD_s12(state->words[0]);
			}

			if (!fieldCisReg) {
				state->target = fieldC;
				state->flow = is_linked ?
					direct_call : direct_jump;
			} else {
				state->target = get_reg(fieldC, regs, cregs);
				state->flow = is_linked ?
					indirect_call : indirect_jump;
			}
			state->is_branch = 1;
			break;

		case 40:	/* LPcc */
			if (BITS(state->words[0], 22, 23) == 3) {
				/* Conditional LPcc u7 */
				fieldC = FIELD_C(state->words[0]);

				fieldC = fieldC << 1;
				fieldC += (addr & ~0x03);
				state->is_branch = 1;
				state->flow = direct_jump;
				state->target = fieldC;
			}
			/* For Unconditional lp, next pc is the fall through
			 * which is updated */
			break;

		case 48 ... 55:	/* LD a,[b,c] */
			state->di = BITS(state->words[0], 15, 15);
			if (state->di)
				break;
			state->x = BITS(state->words[0], 16, 16);
			state->zz = BITS(state->words[0], 17, 18);
			state->aa = BITS(state->words[0], 22, 23);
			state->wb_reg = FIELD_B(state->words[0]);
			if (state->wb_reg == REG_LIMM) {
				state->instr_len += 4;
				state->src1 = state->words[1];
			} else {
				state->src1 = get_reg(state->wb_reg, regs,
						cregs);
			}
			state->src2 = FIELD_C(state->words[0]);
			if (state->src2 == REG_LIMM) {
				state->instr_len += 4;
				state->src2 = state->words[1];
			} else {
				state->src2 = get_reg(state->src2, regs,
					cregs);
			}
			state->dest = FIELD_A(state->words[0]);
			if (state->dest == REG_LIMM)
				state->pref = 1;
			break;

		case 10:	/* MOV */
			/* still need to check for limm to extract instr len */
			/* MOV is special case because it only takes 2 args */
			switch (BITS(state->words[0], 22, 23)) {
			case 0: /* OP a,b,c */
				if (FIELD_C(state->words[0]) == REG_LIMM)
					state->instr_len += 4;
				break;
			case 1: /* OP a,b,u6 */
				break;
			case 2: /* OP b,b,s12 */
				break;
			case 3: /* OP.cc b,b,c/u6 */
				if ((!IS_BIT(state->words[0], 5)) &&
				    (FIELD_C(state->words[0]) == REG_LIMM))
					state->instr_len += 4;
				break;
			}
			break;


		default:
			/* Not a Load, Jump or Loop instruction */
			/* still need to check for limm to extract instr len */
			switch (BITS(state->words[0], 22, 23)) {
			case 0: /* OP a,b,c */
				if ((FIELD_B(state->words[0]) == REG_LIMM) ||
				    (FIELD_C(state->words[0]) == REG_LIMM))
					state->instr_len += 4;
				break;
			case 1: /* OP a,b,u6 */
				break;
			case 2: /* OP b,b,s12 */
				break;
			case 3: /* OP.cc b,b,c/u6 */
				if ((!IS_BIT(state->words[0], 5)) &&
				   ((FIELD_B(state->words[0]) == REG_LIMM) ||
				    (FIELD_C(state->words[0]) == REG_LIMM)))
					state->instr_len += 4;
				break;
			}
			break;
		}
		break;

	/* 16 Bit Instructions */
	case op_LD_ADD: /* LD_S|LDB_S|LDW_S a,[b,c] */
		state->zz = BITS(state->words[0], 3, 4);
		state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
		state->src2 = get_reg(FIELD_S_C(state->words[0]), regs, cregs);
		state->dest = FIELD_S_A(state->words[0]);
		break;

	case op_ADD_MOV_CMP:
		/* check for limm, ignore mov_s h,b (== mov_s 0,b) */
		if ((BITS(state->words[0], 3, 4) < 3) &&
		    (FIELD_S_H(state->words[0]) == REG_LIMM))
			state->instr_len += 4;
		break;

	case op_S:
		subopcode = BITS(state->words[0], 5, 7);
		switch (subopcode) {
		case 0:	/* j_s */
		case 1:	/* j_s.d */
		case 2:	/* jl_s */
		case 3:	/* jl_s.d */
			state->target = get_reg(FIELD_S_B(state->words[0]),
						regs, cregs);
			state->delay_slot = subopcode & 1;
			state->flow = (subopcode >= 2) ?
				direct_call : indirect_jump;
			break;
		case 7:
			switch (BITS(state->words[0], 8, 10)) {
			case 4:	/* jeq_s [blink] */
			case 5:	/* jne_s [blink] */
			case 6:	/* j_s [blink] */
			case 7:	/* j_s.d [blink] */
				state->delay_slot = (subopcode == 7);
				state->flow = indirect_jump;
				state->target = get_reg(31, regs, cregs);
			default:
				break;
			}
		default:
			break;
		}
		break;

	case op_LD_S:	/* LD_S c, [b, u7] */
		state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
		state->src2 = FIELD_S_u7(state->words[0]);
		state->dest = FIELD_S_C(state->words[0]);
		break;

	case op_LDB_S:
	case op_STB_S:
		/* no further handling required as byte accesses should not
		 * cause an unaligned access exception */
		state->zz = 1;
		break;

	case op_LDWX_S:	/* LDWX_S c, [b, u6] */
		state->x = 1;
		/* intentional fall-through */

	case op_LDW_S:	/* LDW_S c, [b, u6] */
		state->zz = 2;
		state->src1 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
		state->src2 = FIELD_S_u6(state->words[0]);
		state->dest = FIELD_S_C(state->words[0]);
		break;

	case op_ST_S:	/* ST_S c, [b, u7] */
		state->write = 1;
		state->src1 = get_reg(FIELD_S_C(state->words[0]), regs, cregs);
		state->src2 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
		state->src3 = FIELD_S_u7(state->words[0]);
		break;

	case op_STW_S:	/* STW_S c,[b,u6] */
		state->write = 1;
		state->zz = 2;
		state->src1 = get_reg(FIELD_S_C(state->words[0]), regs, cregs);
		state->src2 = get_reg(FIELD_S_B(state->words[0]), regs, cregs);
		state->src3 = FIELD_S_u6(state->words[0]);
		break;

	case op_SP:	/* LD_S|LDB_S b,[sp,u7], ST_S|STB_S b,[sp,u7] */
		/* note: we are ignoring possibility of:
		 * ADD_S, SUB_S, PUSH_S, POP_S as these should not
		 * cause unaliged exception anyway */
		state->write = BITS(state->words[0], 6, 6);
		state->zz = BITS(state->words[0], 5, 5);
		if (state->zz)
			break;	/* byte accesses should not come here */
		if (!state->write) {
			state->src1 = get_reg(28, regs, cregs);
			state->src2 = FIELD_S_u7(state->words[0]);
			state->dest = FIELD_S_B(state->words[0]);
		} else {
			state->src1 = get_reg(FIELD_S_B(state->words[0]), regs,
					cregs);
			state->src2 = get_reg(28, regs, cregs);
			state->src3 = FIELD_S_u7(state->words[0]);
		}
		break;

	case op_GP:	/* LD_S|LDB_S|LDW_S r0,[gp,s11/s9/s10] */
		/* note: ADD_S r0, gp, s11 is ignored */
		state->zz = BITS(state->words[0], 9, 10);
		state->src1 = get_reg(26, regs, cregs);
		state->src2 = state->zz ? FIELD_S_s10(state->words[0]) :
			FIELD_S_s11(state->words[0]);
		state->dest = 0;
		break;

	case op_Pcl:	/* LD_S b,[pcl,u10] */
		state->src1 = regs->ret & ~3;
		state->src2 = FIELD_S_u10(state->words[0]);
		state->dest = FIELD_S_B(state->words[0]);
		break;

	case op_BR_S:
		state->target = FIELD_S_s8(state->words[0]) + (addr & ~0x03);
		state->flow = direct_jump;
		state->is_branch = 1;
		break;

	case op_B_S:
		fieldA = (BITS(state->words[0], 9, 10) == 3) ?
			FIELD_S_s7(state->words[0]) :
			FIELD_S_s10(state->words[0]);
		state->target = fieldA + (addr & ~0x03);
		state->flow = direct_jump;
		state->is_branch = 1;
		break;

	case op_BL_S:
		state->target = FIELD_S_s13(state->words[0]) + (addr & ~0x03);
		state->flow = direct_call;
		state->is_branch = 1;
		break;

	default:
		break;
	}

	if (bytes_not_copied <= (8 - state->instr_len))
		return;

fault:	state->fault = 1;
}

long __kprobes get_reg(int reg, struct pt_regs *regs,
		       struct callee_regs *cregs)
{
	long *p;

	if (reg <= 12) {
		p = &regs->r0;
		return p[-reg];
	}

	if (cregs && (reg <= 25)) {
		p = &cregs->r13;
		return p[13-reg];
	}

	if (reg == 26)
		return regs->r26;
	if (reg == 27)
		return regs->fp;
	if (reg == 28)
		return regs->sp;
	if (reg == 31)
		return regs->blink;

	return 0;
}

void __kprobes set_reg(int reg, long val, struct pt_regs *regs,
		struct callee_regs *cregs)
{
	long *p;

	switch (reg) {
	case 0 ... 12:
		p = &regs->r0;
		p[-reg] = val;
		break;
	case 13 ... 25:
		if (cregs) {
			p = &cregs->r13;
			p[13-reg] = val;
		}
		break;
	case 26:
		regs->r26 = val;
		break;
	case 27:
		regs->fp = val;
		break;
	case 28:
		regs->sp = val;
		break;
	case 31:
		regs->blink = val;
		break;
	default:
		break;
	}
}

/*
 * Disassembles the insn at @pc and sets @next_pc to next PC (which could be
 * @pc +2/4/6 (ARCompact ISA allows free intermixing of 16/32 bit insns).
 *
 * If @pc is a branch
 *	-@tgt_if_br is set to branch target.
 *	-If branch has delay slot, @next_pc updated with actual next PC.
 */
int __kprobes disasm_next_pc(unsigned long pc, struct pt_regs *regs,
			     struct callee_regs *cregs,
			     unsigned long *next_pc, unsigned long *tgt_if_br)
{
	struct disasm_state instr;

	memset(&instr, 0, sizeof(struct disasm_state));
	disasm_instr(pc, &instr, 0, regs, cregs);

	*next_pc = pc + instr.instr_len;

	/* Instruction with possible two targets branch, jump and loop */
	if (instr.is_branch)
		*tgt_if_br = instr.target;

	/* For the instructions with delay slots, the fall through is the
	 * instruction following the instruction in delay slot.
	 */
	 if (instr.delay_slot) {
		struct disasm_state instr_d;

		disasm_instr(*next_pc, &instr_d, 0, regs, cregs);

		*next_pc += instr_d.instr_len;
	 }

	 /* Zero Overhead Loop - end of the loop */
	if (!(regs->status32 & STATUS32_L) && (*next_pc == regs->lp_end)
		&& (regs->lp_count > 1)) {
		*next_pc = regs->lp_start;
	}

	return instr.is_branch;
}

#endif /* CONFIG_KGDB || CONFIG_ARC_EMUL_UNALIGNED || CONFIG_KPROBES */

back to top

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API