1/* 2 * arch/ubicom32/kernel/flat.c 3 * Ubicom32 architecture flat executable format support. 4 * 5 * (C) Copyright 2009, Ubicom, Inc. 6 * 7 * This file is part of the Ubicom32 Linux Kernel Port. 8 * 9 * The Ubicom32 Linux Kernel Port is free software: you can redistribute 10 * it and/or modify it under the terms of the GNU General Public License 11 * as published by the Free Software Foundation, either version 2 of the 12 * License, or (at your option) any later version. 13 * 14 * The Ubicom32 Linux Kernel Port is distributed in the hope that it 15 * will be useful, but WITHOUT ANY WARRANTY; without even the implied 16 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 17 * the GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with the Ubicom32 Linux Kernel Port. If not, 21 * see <http://www.gnu.org/licenses/>. 22 * 23 * Ubicom32 implementation derived from (with many thanks): 24 * arch/m68knommu 25 * arch/blackfin 26 * arch/parisc 27 */ 28#include <linux/module.h> 29#include <linux/types.h> 30#include <linux/flat.h> 31 32unsigned long ubicom32_flat_get_addr_from_rp(unsigned long *rp, 33 u32_t relval, 34 u32_t flags, 35 unsigned long *persistent) 36{ 37 u32_t relval_reloc_type = relval >> 27; 38 u32_t insn = *rp; 39 40 if (*persistent) { 41 /* 42 * relval holds the relocation that has to be adjusted. 43 */ 44 if (relval == 0) { 45 *persistent = 0; 46 } 47 48 return relval; 49 } 50 51 if (relval_reloc_type == R_UBICOM32_32) { 52 /* 53 * insn holds the relocation 54 */ 55 return insn; 56 } 57 58 /* 59 * We don't know this one. 60 */ 61 return 0; 62} 63 64void ubicom32_flat_put_addr_at_rp(unsigned long *rp, 65 u32_t val, 66 u32_t relval, 67 unsigned long *persistent) 68{ 69 u32_t reloc_type = (relval >> 27) & 0x1f; 70 u32_t insn = *rp; 71 72 /* 73 * If persistent is set then it contains the relocation type. 74 */ 75 if (*persistent) { 76 /* 77 * If persistent is set then it contains the relocation type. 78 */ 79 reloc_type = (*persistent >> 27) & 0x1f; 80 } 81 82 switch (reloc_type) { 83 case R_UBICOM32_32: 84 /* 85 * Store the 32 bits as is. 86 */ 87 *rp = val; 88 break; 89 case R_UBICOM32_HI24: 90 { 91 /* 92 * 24 bit relocation that is part of the MOVEAI 93 * instruction. The 24 bits come from bits 7 - 30 of the 94 * relocation. The 24 bits eventually get split into 2 95 * fields in the instruction encoding. 96 * 97 * - Bits 7 - 27 of the relocation are encoded into bits 98 * 0 - 20 of the instruction. 99 * 100 * - Bits 28 - 30 of the relocation are encoded into bit 101 * 24 - 26 of the instruction. 102 */ 103 u32_t mask = 0x1fffff | (0x7 << 24); 104 u32_t valid24bits = (val >> 7) & 0xffffff; 105 u32_t bot_21 = valid24bits & 0x1fffff; 106 u32_t upper_3_bits = ((valid24bits & 0xe00000) << 3); 107 insn &= ~mask; 108 109 insn |= bot_21; 110 insn |= upper_3_bits; 111 *rp = insn; 112 } 113 break; 114 case R_UBICOM32_LO7_S: 115 case R_UBICOM32_LO7_2_S: 116 case R_UBICOM32_LO7_4_S: 117 { 118 /* 119 * Bits 0 - 6 of the relocation are encoded into the 120 * 7bit unsigned immediate fields of the SOURCE-1 field 121 * of the instruction. The immediate value is left 122 * shifted by (0, 1, 2) based on the operand size. 123 */ 124 u32_t mask = 0x1f | (0x3 << 8); 125 u32_t bottom, top; 126 val &= 0x7f; 127 if (reloc_type == R_UBICOM32_LO7_2_S) { 128 val >>= 1; 129 } else if (reloc_type == R_UBICOM32_LO7_4_S) { 130 val >>= 2; 131 } 132 133 bottom = val & 0x1f; 134 top = val >> 5; 135 insn &= ~mask; 136 insn |= bottom; 137 insn |= (top << 8); 138 BUG_ON(*rp != insn); 139 *rp = insn; 140 break; 141 } 142 case R_UBICOM32_LO7_D: 143 case R_UBICOM32_LO7_2_D: 144 case R_UBICOM32_LO7_4_D: 145 { 146 /* 147 * Bits 0 - 6 of the relocation are encoded into the 148 * 7bit unsigned immediate fields of the DESTINATION 149 * field of the instruction. The immediate value is 150 * left shifted by (0, 1, 2) based on the operand size. 151 */ 152 u32_t mask = (0x1f | (0x3 << 8)) << 16; 153 u32_t bottom, top; 154 val &= 0x7f; 155 if (reloc_type == R_UBICOM32_LO7_2_D) { 156 val >>= 1; 157 } else if (reloc_type == R_UBICOM32_LO7_4_D) { 158 val >>= 2; 159 } 160 bottom = (val & 0x1f) << 16; 161 top = (val >> 5) << 16; 162 insn &= ~mask; 163 insn |= bottom; 164 insn |= (top << 8); 165 BUG_ON(*rp != insn); 166 *rp = insn; 167 break; 168 } 169 case R_UBICOM32_LO7_CALLI: 170 case R_UBICOM32_LO16_CALLI: 171 { 172 /* 173 * Extract the offset for a CALLI instruction. The 174 * offsets can be either 7 bits or 18 bits. Since all 175 * instructions in ubicom32 architecture are at work 176 * aligned addresses the truncated offset is right 177 * shifted by 2 before being encoded in the instruction. 178 */ 179 if (reloc_type == R_UBICOM32_LO7_CALLI) { 180 val &= 0x7f; 181 } else { 182 val &= 0x3ffff; 183 } 184 185 val >>= 2; 186 187 insn &= ~0x071f071f; 188 insn |= (val & 0x1f) << 0; 189 val >>= 5; 190 insn |= (val & 0x07) << 8; 191 val >>= 3; 192 insn |= (val & 0x1f) << 16; 193 val >>= 5; 194 insn |= (val & 0x07) << 24; 195 if (reloc_type == R_UBICOM32_LO7_CALLI) { 196 BUG_ON(*rp != insn); 197 } 198 *rp = insn; 199 } 200 break; 201 } 202 203 if (*persistent) { 204 *persistent = 0; 205 } 206} 207