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

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

tsar_boot:

  • Displaying Data Bad Virtual Address when exception during preloader.
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 = 0;
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_paddr;
248
249                pseg_end   = elf_phdr_ptr[pseg].p_paddr +
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_paddr  +
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_paddr  +
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_paddr);
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.