162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * uClinux flat-format executables 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2005 John Williams <jwilliams@itee.uq.edu.au> 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#ifndef _ASM_MICROBLAZE_FLAT_H 962306a36Sopenharmony_ci#define _ASM_MICROBLAZE_FLAT_H 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/unaligned.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci/* 1462306a36Sopenharmony_ci * Microblaze works a little differently from other arches, because 1562306a36Sopenharmony_ci * of the MICROBLAZE_64 reloc type. Here, a 32 bit address is split 1662306a36Sopenharmony_ci * over two instructions, an 'imm' instruction which provides the top 1762306a36Sopenharmony_ci * 16 bits, then the instruction "proper" which provides the low 16 1862306a36Sopenharmony_ci * bits. 1962306a36Sopenharmony_ci */ 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * Crack open a symbol reference and extract the address to be 2362306a36Sopenharmony_ci * relocated. rp is a potentially unaligned pointer to the 2462306a36Sopenharmony_ci * reference 2562306a36Sopenharmony_ci */ 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_cistatic inline int flat_get_addr_from_rp(u32 __user *rp, u32 relval, u32 flags, 2862306a36Sopenharmony_ci u32 *addr) 2962306a36Sopenharmony_ci{ 3062306a36Sopenharmony_ci u32 *p = (__force u32 *)rp; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci /* Is it a split 64/32 reference? */ 3362306a36Sopenharmony_ci if (relval & 0x80000000) { 3462306a36Sopenharmony_ci /* Grab the two halves of the reference */ 3562306a36Sopenharmony_ci u32 val_hi, val_lo; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci val_hi = get_unaligned(p); 3862306a36Sopenharmony_ci val_lo = get_unaligned(p+1); 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci /* Crack the address out */ 4162306a36Sopenharmony_ci *addr = ((val_hi & 0xffff) << 16) + (val_lo & 0xffff); 4262306a36Sopenharmony_ci } else { 4362306a36Sopenharmony_ci /* Get the address straight out */ 4462306a36Sopenharmony_ci *addr = get_unaligned(p); 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci return 0; 4862306a36Sopenharmony_ci} 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * Insert an address into the symbol reference at rp. rp is potentially 5262306a36Sopenharmony_ci * unaligned. 5362306a36Sopenharmony_ci */ 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_cistatic inline int 5662306a36Sopenharmony_ciflat_put_addr_at_rp(u32 __user *rp, u32 addr, u32 relval) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci u32 *p = (__force u32 *)rp; 5962306a36Sopenharmony_ci /* Is this a split 64/32 reloc? */ 6062306a36Sopenharmony_ci if (relval & 0x80000000) { 6162306a36Sopenharmony_ci /* Get the two "halves" */ 6262306a36Sopenharmony_ci unsigned long val_hi = get_unaligned(p); 6362306a36Sopenharmony_ci unsigned long val_lo = get_unaligned(p + 1); 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* insert the address */ 6662306a36Sopenharmony_ci val_hi = (val_hi & 0xffff0000) | addr >> 16; 6762306a36Sopenharmony_ci val_lo = (val_lo & 0xffff0000) | (addr & 0xffff); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* store the two halves back into memory */ 7062306a36Sopenharmony_ci put_unaligned(val_hi, p); 7162306a36Sopenharmony_ci put_unaligned(val_lo, p+1); 7262306a36Sopenharmony_ci } else { 7362306a36Sopenharmony_ci /* Put it straight in, no messing around */ 7462306a36Sopenharmony_ci put_unaligned(addr, p); 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci return 0; 7762306a36Sopenharmony_ci} 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define flat_get_relocate_addr(rel) (rel & 0x7fffffff) 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#endif /* _ASM_MICROBLAZE_FLAT_H */ 82