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