1 /*
2  * reloc.c
3  *
4  * DSP-BIOS Bridge driver support functions for TI OMAP processors.
5  *
6  * Copyright (C) 2005-2006 Texas Instruments, Inc.
7  *
8  * This package is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License version 2 as
10  * published by the Free Software Foundation.
11  *
12  * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
13  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
14  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
15  */
16 
17 #include "header.h"
18 
19 #if TMS32060
20 /* the magic symbol for the start of BSS */
21 static const char bsssymbol[] = { ".bss" };
22 #endif
23 
24 #if TMS32060
25 #include "reloc_table_c6000.c"
26 #endif
27 
28 #if TMS32060
29 /* From coff.h - ignore these relocation operations */
30 #define R_C60ALIGN     0x76	/* C60: Alignment info for compressor */
31 #define R_C60FPHEAD    0x77	/* C60: Explicit assembly directive */
32 #define R_C60NOCMP    0x100	/* C60: Don't compress this code scn */
33 #endif
34 
35 /**************************************************************************
36  * Procedure dload_unpack
37  *
38  * Parameters:
39  *	data	pointer to storage unit containing lowest host address of
40  *		image data
41  *	fieldsz	Size of bit field, 0 < fieldsz <= sizeof(rvalue)*BITS_PER_AU
42  *	offset	Offset from LSB, 0 <= offset < BITS_PER_AU
43  *	sgn	Signedness of the field (ROP_SGN, ROP_UNS, ROP_MAX, ROP_ANY)
44  *
45  * Effect:
46  *	Extracts the specified field and returns it.
47  ************************************************************************* */
dload_unpack(struct dload_state * dlthis,tgt_au_t * data,int fieldsz,int offset,unsigned sgn)48 rvalue dload_unpack(struct dload_state *dlthis, tgt_au_t * data, int fieldsz,
49 		    int offset, unsigned sgn)
50 {
51 	register rvalue objval;
52 	register int shift, direction;
53 	register tgt_au_t *dp = data;
54 
55 	fieldsz -= 1;	/* avoid nastiness with 32-bit shift of 32-bit value */
56 	/* * collect up enough bits to contain the desired field */
57 	if (TARGET_BIG_ENDIAN) {
58 		dp += (fieldsz + offset) >> LOG_TGTAU_BITS;
59 		direction = -1;
60 	} else
61 		direction = 1;
62 	objval = *dp >> offset;
63 	shift = TGTAU_BITS - offset;
64 	while (shift <= fieldsz) {
65 		dp += direction;
66 		objval += (rvalue) *dp << shift;
67 		shift += TGTAU_BITS;
68 	}
69 
70 	/* * sign or zero extend the value appropriately */
71 	if (sgn == ROP_UNS)
72 		objval &= (2 << fieldsz) - 1;
73 	else {
74 		shift = sizeof(rvalue) * BITS_PER_AU - 1 - fieldsz;
75 		objval = (objval << shift) >> shift;
76 	}
77 
78 	return objval;
79 
80 }				/* dload_unpack */
81 
82 /**************************************************************************
83  * Procedure dload_repack
84  *
85  * Parameters:
86  *	val		Value to insert
87  *	data	Pointer to storage unit containing lowest host address of
88  * 		image data
89  *	fieldsz	Size of bit field, 0 < fieldsz <= sizeof(rvalue)*BITS_PER_AU
90  *	offset	Offset from LSB, 0 <= offset < BITS_PER_AU
91  *	sgn	Signedness of the field (ROP_SGN, ROP_UNS, ROP_MAX, ROP_ANY)
92  *
93  * Effect:
94  *	Stuffs the specified value in the specified field.  Returns 0 for
95  *	success
96  * or 1 if the value will not fit in the specified field according to the
97  * specified signedness rule.
98  ************************************************************************* */
99 static const unsigned char ovf_limit[] = { 1, 2, 2 };
100 
dload_repack(struct dload_state * dlthis,rvalue val,tgt_au_t * data,int fieldsz,int offset,unsigned sgn)101 int dload_repack(struct dload_state *dlthis, rvalue val, tgt_au_t * data,
102 		 int fieldsz, int offset, unsigned sgn)
103 {
104 	register urvalue objval, mask;
105 	register int shift, direction;
106 	register tgt_au_t *dp = data;
107 
108 	fieldsz -= 1;	/* avoid nastiness with 32-bit shift of 32-bit value */
109 	/* clip the bits */
110 	mask = (2UL << fieldsz) - 1;
111 	objval = (val & mask);
112 	/* * store the bits through the specified mask */
113 	if (TARGET_BIG_ENDIAN) {
114 		dp += (fieldsz + offset) >> LOG_TGTAU_BITS;
115 		direction = -1;
116 	} else
117 		direction = 1;
118 
119 	/* insert LSBs */
120 	*dp = (*dp & ~(mask << offset)) + (objval << offset);
121 	shift = TGTAU_BITS - offset;
122 	/* align mask and objval with AU boundary */
123 	objval >>= shift;
124 	mask >>= shift;
125 
126 	while (mask) {
127 		dp += direction;
128 		*dp = (*dp & ~mask) + objval;
129 		objval >>= TGTAU_BITS;
130 		mask >>= TGTAU_BITS;
131 	}
132 
133 	/*
134 	 * check for overflow
135 	 */
136 	if (sgn) {
137 		unsigned tmp = (val >> fieldsz) + (sgn & 0x1);
138 		if (tmp > ovf_limit[sgn - 1])
139 			return 1;
140 	}
141 	return 0;
142 
143 }				/* dload_repack */
144 
145 /* lookup table for the scaling amount in a C6x instruction */
146 #if TMS32060
147 #define SCALE_BITS 4		/* there are 4 bits in the scale field */
148 #define SCALE_MASK 0x7		/* we really only use the bottom 3 bits */
149 static const u8 c60_scale[SCALE_MASK + 1] = {
150 	1, 0, 0, 0, 1, 1, 2, 2
151 };
152 #endif
153 
154 /**************************************************************************
155  * Procedure dload_relocate
156  *
157  * Parameters:
158  *	data	Pointer to base of image data
159  *	rp		Pointer to relocation operation
160  *
161  * Effect:
162  *	Performs the specified relocation operation
163  ************************************************************************* */
dload_relocate(struct dload_state * dlthis,tgt_au_t * data,struct reloc_record_t * rp,bool * tramps_generated,bool second_pass)164 void dload_relocate(struct dload_state *dlthis, tgt_au_t * data,
165 		    struct reloc_record_t *rp, bool *tramps_generated,
166 		    bool second_pass)
167 {
168 	rvalue val, reloc_amt, orig_val = 0;
169 	unsigned int fieldsz = 0;
170 	unsigned int offset = 0;
171 	unsigned int reloc_info = 0;
172 	unsigned int reloc_action = 0;
173 	register int rx = 0;
174 	rvalue *stackp = NULL;
175 	int top;
176 	struct local_symbol *svp = NULL;
177 #ifdef RFV_SCALE
178 	unsigned int scale = 0;
179 #endif
180 	struct image_packet_t *img_pkt = NULL;
181 
182 	/* The image packet data struct is only used during first pass
183 	 * relocation in the event that a trampoline is needed.  2nd pass
184 	 * relocation doesn't guarantee that data is coming from an
185 	 * image_packet_t structure. See cload.c, dload_data for how img_data is
186 	 * set. If that changes this needs to be updated!!! */
187 	if (second_pass == false)
188 		img_pkt = (struct image_packet_t *)((u8 *) data -
189 						    sizeof(struct
190 							   image_packet_t));
191 
192 	rx = HASH_FUNC(rp->TYPE);
193 	while (rop_map1[rx] != rp->TYPE) {
194 		rx = HASH_L(rop_map2[rx]);
195 		if (rx < 0) {
196 #if TMS32060
197 			switch (rp->TYPE) {
198 			case R_C60ALIGN:
199 			case R_C60NOCMP:
200 			case R_C60FPHEAD:
201 				/* Ignore these reloc types and return */
202 				break;
203 			default:
204 				/* Unknown reloc type, print error and return */
205 				dload_error(dlthis, "Bad coff operator 0x%x",
206 					    rp->TYPE);
207 			}
208 #else
209 			dload_error(dlthis, "Bad coff operator 0x%x", rp->TYPE);
210 #endif
211 			return;
212 		}
213 	}
214 	rx = HASH_I(rop_map2[rx]);
215 	if ((rx < (sizeof(rop_action) / sizeof(u16)))
216 	    && (rx < (sizeof(rop_info) / sizeof(u16))) && (rx > 0)) {
217 		reloc_action = rop_action[rx];
218 		reloc_info = rop_info[rx];
219 	} else {
220 		dload_error(dlthis, "Buffer Overflow - Array Index Out "
221 			    "of Bounds");
222 	}
223 
224 	/* Compute the relocation amount for the referenced symbol, if any */
225 	reloc_amt = rp->UVAL;
226 	if (RFV_SYM(reloc_info)) {	/* relocation uses a symbol reference */
227 		/* If this is first pass, use the module local symbol table,
228 		 * else use the trampoline symbol table. */
229 		if (second_pass == false) {
230 			if ((u32) rp->SYMNDX < dlthis->dfile_hdr.df_no_syms) {
231 				/* real symbol reference */
232 				svp = &dlthis->local_symtab[rp->SYMNDX];
233 				reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ?
234 				    svp->delta : svp->value;
235 			}
236 			/* reloc references current section */
237 			else if (rp->SYMNDX == -1) {
238 				reloc_amt = (RFV_SYM(reloc_info) == ROP_SYMD) ?
239 				    dlthis->delta_runaddr :
240 				    dlthis->image_secn->run_addr;
241 			}
242 		}
243 	}
244 	/* relocation uses a symbol reference */
245 	/* Handle stack adjustment */
246 	val = 0;
247 	top = RFV_STK(reloc_info);
248 	if (top) {
249 		top += dlthis->relstkidx - RSTK_UOP;
250 		if (top >= STATIC_EXPR_STK_SIZE) {
251 			dload_error(dlthis,
252 				    "Expression stack overflow in %s at offset "
253 				    FMT_UI32, dlthis->image_secn->name,
254 				    rp->vaddr + dlthis->image_offset);
255 			return;
256 		}
257 		val = dlthis->relstk[dlthis->relstkidx];
258 		dlthis->relstkidx = top;
259 		stackp = &dlthis->relstk[top];
260 	}
261 	/* Derive field position and size, if we need them */
262 	if (reloc_info & ROP_RW) {	/* read or write action in our future */
263 		fieldsz = RFV_WIDTH(reloc_action);
264 		if (fieldsz) {	/* field info from table */
265 			offset = RFV_POSN(reloc_action);
266 			if (TARGET_BIG_ENDIAN)
267 				/* make sure vaddr is the lowest target
268 				 * address containing bits */
269 				rp->vaddr += RFV_BIGOFF(reloc_info);
270 		} else {	/* field info from relocation op */
271 			fieldsz = rp->FIELDSZ;
272 			offset = rp->OFFSET;
273 			if (TARGET_BIG_ENDIAN)
274 				/* make sure vaddr is the lowest target
275 				   address containing bits */
276 				rp->vaddr += (rp->WORDSZ - offset - fieldsz)
277 				    >> LOG_TARGET_AU_BITS;
278 		}
279 		data = (tgt_au_t *) ((char *)data + TADDR_TO_HOST(rp->vaddr));
280 		/* compute lowest host location of referenced data */
281 #if BITS_PER_AU > TARGET_AU_BITS
282 		/* conversion from target address to host address may lose
283 		   address bits; add loss to offset */
284 		if (TARGET_BIG_ENDIAN) {
285 			offset += -((rp->vaddr << LOG_TARGET_AU_BITS) +
286 				    offset + fieldsz) &
287 			    (BITS_PER_AU - TARGET_AU_BITS);
288 		} else {
289 			offset += (rp->vaddr << LOG_TARGET_AU_BITS) &
290 			    (BITS_PER_AU - 1);
291 		}
292 #endif
293 #ifdef RFV_SCALE
294 		scale = RFV_SCALE(reloc_info);
295 #endif
296 	}
297 	/* read the object value from the current image, if so ordered */
298 	if (reloc_info & ROP_R) {
299 		/* relocation reads current image value */
300 		val = dload_unpack(dlthis, data, fieldsz, offset,
301 				   RFV_SIGN(reloc_info));
302 		/* Save off the original value in case the relo overflows and
303 		 * we can trampoline it. */
304 		orig_val = val;
305 
306 #ifdef RFV_SCALE
307 		val <<= scale;
308 #endif
309 	}
310 	/* perform the necessary arithmetic */
311 	switch (RFV_ACTION(reloc_action)) {	/* relocation actions */
312 	case RACT_VAL:
313 		break;
314 	case RACT_ASGN:
315 		val = reloc_amt;
316 		break;
317 	case RACT_ADD:
318 		val += reloc_amt;
319 		break;
320 	case RACT_PCR:
321 		/*-----------------------------------------------------------
322 		 * Handle special cases of jumping from absolute sections
323 		 * (special reloc type) or to absolute destination
324 		 * (symndx == -1).  In either case, set the appropriate
325 		 * relocation amount to 0.
326 		 *----------------------------------------------------------- */
327 		if (rp->SYMNDX == -1)
328 			reloc_amt = 0;
329 		val += reloc_amt - dlthis->delta_runaddr;
330 		break;
331 	case RACT_ADDISP:
332 		val += rp->R_DISP + reloc_amt;
333 		break;
334 	case RACT_ASGPC:
335 		val = dlthis->image_secn->run_addr + reloc_amt;
336 		break;
337 	case RACT_PLUS:
338 		if (stackp != NULL)
339 			val += *stackp;
340 		break;
341 	case RACT_SUB:
342 		if (stackp != NULL)
343 			val = *stackp - val;
344 		break;
345 	case RACT_NEG:
346 		val = -val;
347 		break;
348 	case RACT_MPY:
349 		if (stackp != NULL)
350 			val *= *stackp;
351 		break;
352 	case RACT_DIV:
353 		if (stackp != NULL)
354 			val = *stackp / val;
355 		break;
356 	case RACT_MOD:
357 		if (stackp != NULL)
358 			val = *stackp % val;
359 		break;
360 	case RACT_SR:
361 		if (val >= sizeof(rvalue) * BITS_PER_AU)
362 			val = 0;
363 		else if (stackp != NULL)
364 			val = (urvalue) *stackp >> val;
365 		break;
366 	case RACT_ASR:
367 		if (val >= sizeof(rvalue) * BITS_PER_AU)
368 			val = sizeof(rvalue) * BITS_PER_AU - 1;
369 		else if (stackp != NULL)
370 			val = *stackp >> val;
371 		break;
372 	case RACT_SL:
373 		if (val >= sizeof(rvalue) * BITS_PER_AU)
374 			val = 0;
375 		else if (stackp != NULL)
376 			val = *stackp << val;
377 		break;
378 	case RACT_AND:
379 		if (stackp != NULL)
380 			val &= *stackp;
381 		break;
382 	case RACT_OR:
383 		if (stackp != NULL)
384 			val |= *stackp;
385 		break;
386 	case RACT_XOR:
387 		if (stackp != NULL)
388 			val ^= *stackp;
389 		break;
390 	case RACT_NOT:
391 		val = ~val;
392 		break;
393 #if TMS32060
394 	case RACT_C6SECT:
395 		/* actually needed address of secn containing symbol */
396 		if (svp != NULL) {
397 			if (rp->SYMNDX >= 0)
398 				if (svp->secnn > 0)
399 					reloc_amt = dlthis->ldr_sections
400 					    [svp->secnn - 1].run_addr;
401 		}
402 		/* !!! FALL THRU !!! */
403 	case RACT_C6BASE:
404 		if (dlthis->bss_run_base == 0) {
405 			struct dynload_symbol *symp;
406 			symp = dlthis->mysym->find_matching_symbol
407 			    (dlthis->mysym, bsssymbol);
408 			/* lookup value of global BSS base */
409 			if (symp)
410 				dlthis->bss_run_base = symp->value;
411 			else
412 				dload_error(dlthis,
413 					    "Global BSS base referenced in %s "
414 					    "offset" FMT_UI32 " but not "
415 					    "defined",
416 					    dlthis->image_secn->name,
417 					    rp->vaddr + dlthis->image_offset);
418 		}
419 		reloc_amt -= dlthis->bss_run_base;
420 		/* !!! FALL THRU !!! */
421 	case RACT_C6DSPL:
422 		/* scale factor determined by 3 LSBs of field */
423 		scale = c60_scale[val & SCALE_MASK];
424 		offset += SCALE_BITS;
425 		fieldsz -= SCALE_BITS;
426 		val >>= SCALE_BITS;	/* ignore the scale field hereafter */
427 		val <<= scale;
428 		val += reloc_amt;	/* do the usual relocation */
429 		if (((1 << scale) - 1) & val)
430 			dload_error(dlthis,
431 				    "Unaligned reference in %s offset "
432 				    FMT_UI32, dlthis->image_secn->name,
433 				    rp->vaddr + dlthis->image_offset);
434 		break;
435 #endif
436 	}			/* relocation actions */
437 	/* * Put back result as required */
438 	if (reloc_info & ROP_W) {	/* relocation writes image value */
439 #ifdef RFV_SCALE
440 		val >>= scale;
441 #endif
442 		if (dload_repack(dlthis, val, data, fieldsz, offset,
443 				 RFV_SIGN(reloc_info))) {
444 			/* Check to see if this relo can be trampolined,
445 			 * but only in first phase relocation.  2nd phase
446 			 * relocation cannot trampoline. */
447 			if ((second_pass == false) &&
448 			    (dload_tramp_avail(dlthis, rp) == true)) {
449 
450 				/* Before generating the trampoline, restore
451 				 * the value to its original so the 2nd pass
452 				 *  relo will work. */
453 				dload_repack(dlthis, orig_val, data, fieldsz,
454 					     offset, RFV_SIGN(reloc_info));
455 				if (!dload_tramp_generate(dlthis,
456 							(dlthis->image_secn -
457 							 dlthis->ldr_sections),
458 							 dlthis->image_offset,
459 							 img_pkt, rp)) {
460 					dload_error(dlthis,
461 						    "Failed to "
462 						    "generate trampoline for "
463 						    "bit overflow");
464 					dload_error(dlthis,
465 						    "Relocation val " FMT_UI32
466 						    " overflows %d bits in %s "
467 						    "offset " FMT_UI32, val,
468 						    fieldsz,
469 						    dlthis->image_secn->name,
470 						    dlthis->image_offset +
471 						    rp->vaddr);
472 				} else
473 					*tramps_generated = true;
474 			} else {
475 				dload_error(dlthis, "Relocation value "
476 					    FMT_UI32 " overflows %d bits in %s"
477 					    " offset " FMT_UI32, val, fieldsz,
478 					    dlthis->image_secn->name,
479 					    dlthis->image_offset + rp->vaddr);
480 			}
481 		}
482 	} else if (top)
483 		*stackp = val;
484 }				/* reloc_value */
485