source: trunk/softs/tsar_boot/src/reset_elf_loader.c @ 666

Last change on this file since 666 was 591, checked in by cfuguet, 11 years ago

When the file size of an program segment is smaller than
its mem size, the remainder must be set to 0. This is for
sections as .bss which are not present in the ELF file.

File size: 10.7 KB
Line 
1/**
2 * \file    : reset_elf_loader.c
3 * \date    : August 2012
4 * \author  : Cesar Fuguet
5 *
6 * This file defines an elf file loader which reads an executable .elf file
7 * starting at a sector passed as argument on a disk and copy the different
8 * ELF program segments in the appropriate memory address using as information
9 * the virtual address read from the .elf file.
10 */
11
12#include <reset_ioc.h>
13#include <elf-types.h>
14#include <reset_tty.h>
15#include <reset_utils.h>
16#include <defs.h>
17
18#if (RESET_DEBUG == 1)
19static char const * const init_state_str[] = 
20{
21    "ELF_HEADER_STATE",
22    "ELF_PROGRAM_HEADER_STATE",
23    "ELF_OFFSET_STATE",
24    "ELF_SEGMENT_STATE",
25    "ELF_END_STATE"
26};
27#endif
28
29unsigned char reset_elf_loader_buffer[512] __attribute__((aligned(CACHE_LINE_SIZE)));
30
31/////////////////////////////////////////////////////////////////////////////////////
32void * reset_elf_loader(unsigned int lba)
33/////////////////////////////////////////////////////////////////////////////////////
34{
35    /*
36     * Temporary variables used by the loader
37     */
38    Elf32_Ehdr      elf_header;
39    Elf32_Phdr      elf_pht[PHDR_ARRAY_SIZE];
40
41    unsigned char * buffer_ptr;
42    Elf32_Ehdr    * elf_ehdr_ptr;
43    Elf32_Phdr    * elf_phdr_ptr;
44
45    unsigned int nb_available;
46    unsigned int nb_rest;
47    unsigned int nb_read;
48    unsigned int nb_block;
49    unsigned int offset;
50
51    unsigned char * pseg_ptr;
52    unsigned int pseg_start;
53    unsigned int pseg_end;
54    unsigned int pseg_remainder;
55    unsigned int pseg;
56
57    /*
58     * Loader state machine definition
59     */
60    typedef enum
61    {
62        ELF_HEADER_STATE,
63        ELF_PROGRAM_HEADER_STATE,
64        ELF_OFFSET_STATE,
65        ELF_SEGMENT_STATE,
66        ELF_END_STATE
67    } elf_loader_t;
68
69    elf_loader_t init_state;
70    init_state = ELF_HEADER_STATE;
71
72#if (RESET_DEBUG == 1)
73    elf_loader_t init_state_debug;
74    init_state_debug = ELF_END_STATE;
75#endif
76
77    reset_puts("\n[RESET] Start reset_elf_loader at cycle ");
78    reset_putd( proctime() );
79    reset_puts("\n");
80
81    nb_block     = lba;
82    nb_available = 0;
83    nb_rest      = sizeof(Elf32_Ehdr);
84    pseg         = 0;
85    offset       = 0;
86    elf_ehdr_ptr = (Elf32_Ehdr *) &elf_header;
87    elf_phdr_ptr = (Elf32_Phdr *) &elf_pht[0];
88
89    while(init_state != ELF_END_STATE)
90    {
91        if (nb_available == 0 )
92        {
93            buffer_ptr = &reset_elf_loader_buffer[0];
94
95            if (reset_ioc_read(nb_block , buffer_ptr, 1))
96            {
97                reset_puts ("[RESET ERROR] reset_ioc_read() failed\n");
98                reset_exit();
99            }
100
101            nb_block    += 1;
102            nb_available = 512;
103        }
104
105        nb_read  = (nb_rest <= nb_available) ? nb_rest : nb_available;
106        offset  +=  nb_read;
107
108#if (RESET_DEBUG == 1)
109        if (init_state != init_state_debug)
110        {
111            reset_puts("\ninit_state = ");
112            reset_puts(init_state_str[init_state]);
113            reset_puts("\n");
114            init_state_debug = init_state;
115        }
116#endif
117
118        switch(init_state)
119        {
120            /*
121             * Reading ELF executable header
122             */
123            case ELF_HEADER_STATE:
124                memcpy(elf_ehdr_ptr, buffer_ptr, nb_read);
125
126                nb_rest -= nb_read;
127
128                if(nb_rest == 0)
129                {
130                    nb_rest = elf_ehdr_ptr->e_phnum * elf_ehdr_ptr->e_phentsize;
131                    /*
132                     * Verification of ELF Magic Number
133                     */
134                    if ( (elf_ehdr_ptr->e_ident[EI_MAG0] != ELFMAG0) ||
135                         (elf_ehdr_ptr->e_ident[EI_MAG1] != ELFMAG1) ||
136                         (elf_ehdr_ptr->e_ident[EI_MAG2] != ELFMAG2) ||
137                         (elf_ehdr_ptr->e_ident[EI_MAG3] != ELFMAG3) )
138                    {
139                        reset_puts("[RESET ERROR] boot-loader file is not an ELF format\n");
140                        reset_exit();
141                    }
142
143                    /*
144                     * Verification of Program Headers table size. It must be
145                     * smaller than the work size allocated for the
146                     * elf_pht[PHDR_ARRAY_SIZE] array
147                     */
148                    if (elf_ehdr_ptr->e_phnum > PHDR_ARRAY_SIZE)
149                    {
150                        reset_puts("[RESET ERROR] ELF PHDR table size too large\n");
151                        reset_exit();
152                    }
153
154                    init_state = ELF_PROGRAM_HEADER_STATE;
155                }
156
157                break;
158
159            /*
160             * Reading ELF program headers
161             */
162            case ELF_PROGRAM_HEADER_STATE:
163                memcpy(elf_phdr_ptr, buffer_ptr, nb_read);
164
165                elf_phdr_ptr = 
166                    (Elf32_Phdr *)((unsigned char *) elf_phdr_ptr + nb_read);
167
168                nb_rest -= nb_read;
169
170                if(nb_rest == 0)
171                {
172                    elf_phdr_ptr = (Elf32_Phdr *) &elf_pht[0];
173
174                    /*
175                     * Search the first not NULL segment in the ELF file
176                     */
177                    for (pseg = 0; pseg < elf_ehdr_ptr->e_phnum; pseg++)
178                    {
179                        if(elf_phdr_ptr[pseg].p_type == PT_LOAD)
180                        {
181#if (RESET_DEBUG == 1)
182                            reset_puts("loadable segment found:\n");
183                            reset_print_elf_phdr(&elf_phdr_ptr[pseg]);
184#endif
185                            if (elf_phdr_ptr[pseg].p_offset < offset)
186                            {
187                                /*
188                                 * Case where the segment to load includes the
189                                 * elf and program headers
190                                 */
191                                nb_rest = elf_phdr_ptr[pseg].p_filesz - offset;
192                                init_state = ELF_SEGMENT_STATE;
193                            }
194                            else
195                            {
196                                /*
197                                 * Segment to load is further away in ELF file
198                                 */
199                                nb_rest = elf_phdr_ptr[pseg].p_offset - offset;
200                                init_state = ELF_OFFSET_STATE;
201                            }
202                            break;
203                        }
204                    }
205
206                    if (pseg == elf_ehdr_ptr->e_phnum)
207                    {
208                        reset_puts("[RESET ERROR] No PT_LOAD found\n");
209                        reset_exit();
210                    }
211
212                }
213
214                break;
215
216            /*
217             * Go to the offset of the first not null program segment in the
218             * ELF file
219             *
220             * TODO:
221             * No need to read from the disk the useless bytes. Try to compute
222             * the next usefull lba
223             */
224            case ELF_OFFSET_STATE:
225                nb_rest -= nb_read;
226
227                if (nb_rest == 0)
228                {
229                    nb_rest    = elf_phdr_ptr[pseg].p_filesz;
230                    init_state = ELF_SEGMENT_STATE;
231                }
232
233                break;
234
235            /*
236             * Reading ELF segments
237             *
238             * TODO:
239             * Do not pass by block buffer but write directly in target memory
240             * address
241             */
242            case ELF_SEGMENT_STATE:
243                /*
244                 * Verify that loadable segment does not conflict with
245                 * pre-loader memory space
246                 */
247                pseg_start = elf_phdr_ptr[pseg].p_vaddr;
248
249                pseg_end   = elf_phdr_ptr[pseg].p_vaddr +
250                             elf_phdr_ptr[pseg].p_memsz;
251
252                if ((pseg_start >= 0xBFC00000 && pseg_start <= 0xBFC10000) ||
253                    (pseg_end   >= 0xBFC00000 && pseg_end   <= 0xBFC10000) ||
254                    (pseg_start <  0xBFC00000 && pseg_end   >  0xBFC10000))
255                {
256                    reset_puts("[RESET ERROR] conflict with pre-loader memory space\n");
257                    reset_exit();
258                }
259
260                /*
261                 * Copy the ELF segment data in memory using the
262                 * virtual address obtained from the ELF file
263                 */
264                pseg_ptr = (unsigned char *)
265                    elf_phdr_ptr[pseg].p_vaddr  +
266                    elf_phdr_ptr[pseg].p_filesz -
267                    nb_rest;
268
269                memcpy(pseg_ptr, buffer_ptr, nb_read);
270
271                nb_rest -= nb_read;
272
273                if (nb_rest == 0)
274                {
275                    /*
276                     * Fill remaining bytes with zeros (filesz < memsz)
277                     */
278                    pseg_remainder =
279                        elf_phdr_ptr[pseg].p_memsz  -
280                        elf_phdr_ptr[pseg].p_filesz ;
281
282                    pseg_ptr = (unsigned char *)
283                        elf_phdr_ptr[pseg].p_vaddr  +
284                        elf_phdr_ptr[pseg].p_filesz ;
285
286                    memset(pseg_ptr, 0, pseg_remainder);
287
288                    reset_puts("\n[RESET] Segment loaded : address = ");
289                    reset_putx(elf_phdr_ptr[pseg].p_vaddr);
290                    reset_puts(" / size = ");
291                    reset_putx(elf_phdr_ptr[pseg].p_filesz);
292                    reset_puts("\n");
293
294                    /*
295                     * Search the next first not NULL segment in the ELF file
296                     */
297                    for (pseg += 1; pseg < elf_ehdr_ptr->e_phnum; pseg++)
298                    {
299                        if(elf_phdr_ptr[pseg].p_type == PT_LOAD)
300                        {
301#if (RESET_DEBUG == 1)
302                            reset_puts("loadable segment found:\n");
303                            reset_print_elf_phdr(&elf_phdr_ptr[pseg]);
304#endif
305                            nb_rest = elf_phdr_ptr[pseg].p_offset - offset;
306                            break;
307                        }
308                    }
309
310                    /*
311                     * Program loading finished
312                     */
313                    if(pseg == elf_ehdr_ptr->e_phnum)
314                    {
315                        init_state = ELF_END_STATE;
316                        break;
317                    }
318
319                    init_state = ELF_OFFSET_STATE;
320                }
321                break;
322
323            default:
324                break;
325        }
326
327        buffer_ptr   += nb_read;
328        nb_available -= nb_read;
329    }
330
331    reset_puts("\n[RESET] Complete reset_elf_loader at cycle ");
332    reset_putd( proctime() );
333    reset_puts(" / boot entry = ");
334    reset_putx( (unsigned int)(elf_ehdr_ptr->e_entry) );
335    reset_puts("\n");
336
337    return ((void *) elf_ehdr_ptr->e_entry);
338}
339
340// vim: tabstop=4 : softtabstop=4 : shiftwidth=4 : expandtab
Note: See TracBrowser for help on using the repository browser.