1/*
2 * getsection.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 <dspbridge/getsection.h>
18#include "header.h"
19
20/*
21 * Error strings
22 */
23static const char readstrm[] = { "Error reading %s from input stream" };
24static const char seek[] = { "Set file position to %d failed" };
25static const char isiz[] = { "Bad image packet size %d" };
26static const char err_checksum[] = { "Checksum failed on %s" };
27
28static const char err_reloc[] = { "dload_get_section unable to read"
29	    "sections containing relocation entries"
30};
31
32#if BITS_PER_AU > BITS_PER_BYTE
33static const char err_alloc[] = { "Syms->dload_allocate( %d ) failed" };
34static const char stbl[] = { "Bad string table offset " FMT_UI32 };
35#endif
36
37/************************************************************** */
38/********************* SUPPORT FUNCTIONS ********************** */
39/************************************************************** */
40
41#if BITS_PER_AU > BITS_PER_BYTE
42/**************************************************************************
43 * Procedure unpack_sec_name
44 *
45 * Parameters:
46 *  dlthis		Handle from dload_module_open for this module
47 *	soffset	    Byte offset into the string table
48 *  dst         Place to store the expanded string
49 *
50 * Effect:
51 *	Stores a string from the string table into the destination, expanding
52 * it in the process.  Returns a pointer just past the end of the stored
53 * string on success, or NULL on failure.
54 *
55 ************************************************************************ */
56static char *unpack_sec_name(struct dload_state *dlthis, u32 soffset, char *dst)
57{
58	u8 tmp, *src;
59
60	if (soffset >= dlthis->dfile_hdr.df_scn_name_size) {
61		dload_error(dlthis, stbl, soffset);
62		return NULL;
63	}
64	src = (u8 *) dlthis->str_head +
65	    (soffset >> (LOG_BITS_PER_AU - LOG_BITS_PER_BYTE));
66	if (soffset & 1)
67		*dst++ = *src++;	/* only 1 character in first word */
68	do {
69		tmp = *src++;
70		*dst = (tmp >> BITS_PER_BYTE)
71		    if (!(*dst++))
72			break;
73	} while ((*dst++ = tmp & BYTE_MASK));
74
75	return dst;
76}
77
78/**************************************************************************
79 * Procedure expand_sec_names
80 *
81 * Parameters:
82 *  dlthis		Handle from dload_module_open for this module
83 *
84 * Effect:
85 *    Allocates a buffer, unpacks and copies strings from string table into it.
86 * Stores a pointer to the buffer into a state variable.
87 ************************************************************************* */
88static void expand_sec_names(struct dload_state *dlthis)
89{
90	char *xstrings, *curr, *next;
91	u32 xsize;
92	u16 sec;
93	struct ldr_section_info *shp;
94	/* assume worst-case size requirement */
95	xsize = dlthis->dfile_hdr.df_max_str_len * dlthis->dfile_hdr.df_no_scns;
96	xstrings = (char *)dlthis->mysym->dload_allocate(dlthis->mysym, xsize);
97	if (xstrings == NULL) {
98		dload_error(dlthis, err_alloc, xsize);
99		return;
100	}
101	dlthis->xstrings = xstrings;
102	/* For each sec, copy and expand its name */
103	curr = xstrings;
104	for (sec = 0; sec < dlthis->dfile_hdr.df_no_scns; sec++) {
105		shp = (struct ldr_section_info *)&dlthis->sect_hdrs[sec];
106		next = unpack_sec_name(dlthis, *(u32 *) &shp->name, curr);
107		if (next == NULL)
108			break;	/* error */
109		shp->name = curr;
110		curr = next;
111	}
112}
113
114#endif
115
116/************************************************************** */
117/********************* EXPORTED FUNCTIONS ********************* */
118/************************************************************** */
119
120/**************************************************************************
121 * Procedure dload_module_open
122 *
123 * Parameters:
124 *	module	The input stream that supplies the module image
125 *	syms	Host-side malloc/free and error reporting functions.
126 *			Other methods are unused.
127 *
128 * Effect:
129 *	Reads header information from a dynamic loader module using the
130    specified
131 * stream object, and returns a handle for the module information.  This
132 * handle may be used in subsequent query calls to obtain information
133 * contained in the module.
134 *
135 * Returns:
136 *	NULL if an error is encountered, otherwise a module handle for use
137 * in subsequent operations.
138 ************************************************************************* */
139void *dload_module_open(struct dynamic_loader_stream *module,
140				    struct dynamic_loader_sym *syms)
141{
142	struct dload_state *dlthis;	/* internal state for this call */
143	unsigned *dp, sz;
144	u32 sec_start;
145#if BITS_PER_AU <= BITS_PER_BYTE
146	u16 sec;
147#endif
148
149	/* Check that mandatory arguments are present */
150	if (!module || !syms) {
151		if (syms != NULL)
152			dload_syms_error(syms, "Required parameter is NULL");
153
154		return NULL;
155	}
156
157	dlthis = (struct dload_state *)
158	    syms->dload_allocate(syms, sizeof(struct dload_state));
159	if (!dlthis) {
160		/* not enough storage */
161		dload_syms_error(syms, "Can't allocate module info");
162		return NULL;
163	}
164
165	/* clear our internal state */
166	dp = (unsigned *)dlthis;
167	for (sz = sizeof(struct dload_state) / sizeof(unsigned);
168	     sz > 0; sz -= 1)
169		*dp++ = 0;
170
171	dlthis->strm = module;
172	dlthis->mysym = syms;
173
174	/* read in the doff image and store in our state variable */
175	dload_headers(dlthis);
176
177	if (!dlthis->dload_errcount)
178		dload_strings(dlthis, true);
179
180	/* skip ahead past the unread portion of the string table */
181	sec_start = sizeof(struct doff_filehdr_t) +
182	    sizeof(struct doff_verify_rec_t) +
183	    BYTE_TO_HOST(DOFF_ALIGN(dlthis->dfile_hdr.df_strtab_size));
184
185	if (dlthis->strm->set_file_posn(dlthis->strm, sec_start) != 0) {
186		dload_error(dlthis, seek, sec_start);
187		return NULL;
188	}
189
190	if (!dlthis->dload_errcount)
191		dload_sections(dlthis);
192
193	if (dlthis->dload_errcount) {
194		dload_module_close(dlthis);	/* errors, blow off our state */
195		dlthis = NULL;
196		return NULL;
197	}
198#if BITS_PER_AU > BITS_PER_BYTE
199	/* Expand all section names from the string table into the */
200	/* state variable, and convert section names from a relative */
201	/* string table offset to a pointers to the expanded string. */
202	expand_sec_names(dlthis);
203#else
204	/* Convert section names from a relative string table offset */
205	/* to a pointer into the string table. */
206	for (sec = 0; sec < dlthis->dfile_hdr.df_no_scns; sec++) {
207		struct ldr_section_info *shp =
208		    (struct ldr_section_info *)&dlthis->sect_hdrs[sec];
209		shp->name = dlthis->str_head + *(u32 *) &shp->name;
210	}
211#endif
212
213	return dlthis;
214}
215
216/***************************************************************************
217 * Procedure dload_get_section_info
218 *
219 * Parameters:
220 *  minfo		Handle from dload_module_open for this module
221 *	section_name	Pointer to the string name of the section desired
222 *	section_info	Address of a section info structure pointer to be
223 *			initialized
224 *
225 * Effect:
226 *	Finds the specified section in the module information, and initializes
227 * the provided struct ldr_section_info pointer.
228 *
229 * Returns:
230 *	true for success, false for section not found
231 ************************************************************************* */
232int dload_get_section_info(void *minfo, const char *section_name,
233			   const struct ldr_section_info **const section_info)
234{
235	struct dload_state *dlthis;
236	struct ldr_section_info *shp;
237	u16 sec;
238
239	dlthis = (struct dload_state *)minfo;
240	if (!dlthis)
241		return false;
242
243	for (sec = 0; sec < dlthis->dfile_hdr.df_no_scns; sec++) {
244		shp = (struct ldr_section_info *)&dlthis->sect_hdrs[sec];
245		if (strcmp(section_name, shp->name) == 0) {
246			*section_info = shp;
247			return true;
248		}
249	}
250
251	return false;
252}
253
254#define IPH_SIZE (sizeof(struct image_packet_t) - sizeof(u32))
255
256/**************************************************************************
257 * Procedure dload_get_section
258 *
259 * Parameters:
260 *  minfo		Handle from dload_module_open for this module
261 *	section_info	Pointer to a section info structure for the desired
262 *			section
263 *	section_data	Buffer to contain the section initialized data
264 *
265 * Effect:
266 *	Copies the initialized data for the specified section into the
267 * supplied buffer.
268 *
269 * Returns:
270 *	true for success, false for section not found
271 ************************************************************************* */
272int dload_get_section(void *minfo,
273		      const struct ldr_section_info *section_info,
274		      void *section_data)
275{
276	struct dload_state *dlthis;
277	u32 pos;
278	struct doff_scnhdr_t *sptr = NULL;
279	s32 nip;
280	struct image_packet_t ipacket;
281	s32 ipsize;
282	u32 checks;
283	s8 *dest = (s8 *) section_data;
284
285	dlthis = (struct dload_state *)minfo;
286	if (!dlthis)
287		return false;
288	sptr = (struct doff_scnhdr_t *)section_info;
289	if (sptr == NULL)
290		return false;
291
292	/* skip ahead to the start of the first packet */
293	pos = BYTE_TO_HOST(DOFF_ALIGN((u32) sptr->ds_first_pkt_offset));
294	if (dlthis->strm->set_file_posn(dlthis->strm, pos) != 0) {
295		dload_error(dlthis, seek, pos);
296		return false;
297	}
298
299	nip = sptr->ds_nipacks;
300	while ((nip -= 1) >= 0) {	/* for each packet */
301		/* get the fixed header bits */
302		if (dlthis->strm->read_buffer(dlthis->strm, &ipacket,
303					      IPH_SIZE) != IPH_SIZE) {
304			dload_error(dlthis, readstrm, "image packet");
305			return false;
306		}
307		/* reorder the header if need be */
308		if (dlthis->reorder_map)
309			dload_reorder(&ipacket, IPH_SIZE, dlthis->reorder_map);
310
311		/* Now read the packet image bits. Note: round the size up to
312		 * the next multiple of 4 bytes; this is what checksum
313		 * routines want. */
314		ipsize = BYTE_TO_HOST(DOFF_ALIGN(ipacket.packet_size));
315		if (ipsize > BYTE_TO_HOST(IMAGE_PACKET_SIZE)) {
316			dload_error(dlthis, isiz, ipsize);
317			return false;
318		}
319		if (dlthis->strm->read_buffer
320		    (dlthis->strm, dest, ipsize) != ipsize) {
321			dload_error(dlthis, readstrm, "image packet");
322			return false;
323		}
324		/* reorder the bytes if need be */
325#if !defined(_BIG_ENDIAN) || (TARGET_AU_BITS > 16)
326		if (dlthis->reorder_map)
327			dload_reorder(dest, ipsize, dlthis->reorder_map);
328
329		checks = dload_checksum(dest, ipsize);
330#else
331		if (dlthis->dfile_hdr.df_byte_reshuffle !=
332		    TARGET_ORDER(REORDER_MAP(BYTE_RESHUFFLE_VALUE))) {
333			/* put image bytes in big-endian order, not PC order */
334			dload_reorder(dest, ipsize,
335				      TARGET_ORDER(dlthis->
336						dfile_hdr.df_byte_reshuffle));
337		}
338#if TARGET_AU_BITS > 8
339		checks = dload_reverse_checksum16(dest, ipsize);
340#else
341		checks = dload_reverse_checksum(dest, ipsize);
342#endif
343#endif
344		checks += dload_checksum(&ipacket, IPH_SIZE);
345
346		/* NYI: unable to handle relocation entries here.  Reloc
347		 * entries referring to fields that span the packet boundaries
348		 * may result in packets of sizes that are not multiple of
349		 * 4 bytes. Our checksum implementation works on 32-bit words
350		 * only. */
351		if (ipacket.num_relocs != 0) {
352			dload_error(dlthis, err_reloc, ipsize);
353			return false;
354		}
355
356		if (~checks) {
357			dload_error(dlthis, err_checksum, "image packet");
358			return false;
359		}
360
361		/*Advance destination ptr by the size of the just-read packet */
362		dest += ipsize;
363	}
364
365	return true;
366}
367
368/***************************************************************************
369 * Procedure dload_module_close
370 *
371 * Parameters:
372 *  minfo		Handle from dload_module_open for this module
373 *
374 * Effect:
375 *	Releases any storage associated with the module handle.  On return,
376 * the module handle is invalid.
377 *
378 * Returns:
379 *	Zero for success. On error, the number of errors detected is returned.
380 * Individual errors are reported using syms->error_report(), where syms was
381 * an argument to dload_module_open
382 ************************************************************************* */
383void dload_module_close(void *minfo)
384{
385	struct dload_state *dlthis;
386
387	dlthis = (struct dload_state *)minfo;
388	if (!dlthis)
389		return;
390
391	if (dlthis->str_head)
392		dlthis->mysym->dload_deallocate(dlthis->mysym,
393						dlthis->str_head);
394
395	if (dlthis->sect_hdrs)
396		dlthis->mysym->dload_deallocate(dlthis->mysym,
397						dlthis->sect_hdrs);
398
399#if BITS_PER_AU > BITS_PER_BYTE
400	if (dlthis->xstrings)
401		dlthis->mysym->dload_deallocate(dlthis->mysym,
402						dlthis->xstrings);
403
404#endif
405
406	dlthis->mysym->dload_deallocate(dlthis->mysym, dlthis);
407}
408