e8eb4595f192c73ab2e105e9419d23623637266d
[10.03/openwrt.git] / target / linux / ubicom32 / files / arch / ubicom32 / kernel / flat.c
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
32 unsigned 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
64 void 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 }