/* Copyright 2017 IBM Corp.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * 	http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __ELF_ABI_H
#define __ELF_ABI_H

#ifndef __ASSEMBLY__

#if defined (_CALL_ELF) && _CALL_ELF == 2
#define ELF_ABI_v2
#else
#define ELF_ABI_v1
#endif

/* From linux/arch/powerpc/include/asm/code-patching.h */
#define OP_RT_RA_MASK   0xffff0000UL
#define LIS_R2          0x3c020000UL
#define ADDIS_R2_R12    0x3c4c0000UL
#define ADDI_R2_R2      0x38420000UL

static inline uint64_t function_entry_address(void *func)
{
#ifdef ELF_ABI_v2
	u32 *insn = func;
	/*
	 * A PPC64 ABIv2 function may have a local and a global entry
	 * point. We use the local entry point for branch tables called
	 * from asm, only a single TOC is used, so identify and step over
	 * the global entry point sequence.
	 *
	 * The global entry point sequence is always of the form:
	 *
	 * addis r2,r12,XXXX
	 * addi  r2,r2,XXXX
	 *
	 * A linker optimisation may convert the addis to lis:
	 *
	 * lis   r2,XXXX
	 * addi  r2,r2,XXXX
	 */
	if ((((*insn & OP_RT_RA_MASK) == ADDIS_R2_R12) ||
	     ((*insn & OP_RT_RA_MASK) == LIS_R2)) &&
	    ((*(insn+1) & OP_RT_RA_MASK) == ADDI_R2_R2))
		return (uint64_t)(insn + 2);
	else
		return (uint64_t)func;
#else
	return *(uint64_t *)func;
#endif
}

#endif /* __ASSEMBLY__ */

#endif /* __COMPILER_H */
