source: trunk/softs/tsar_boot/src/boot_elf_loader.c @ 438

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

Modifications in tsar_boot:

  • Creating new files boot_utils.[c h] containing the memcpy, memset and some ELF format debug functions
  • Introducing assert in the boot_elf_loader to show an error when some of segments to load conflicts with some of the pre-loader segments
  • Cosmetic changes in boot_elf_loader to improve code readibility
  • Fixing bug in dcache_buf_invalidate function used by boot_ioc_read when cache coherence not supported. The condition in the for loop was erroneous.
  • Modification in Makefile: The SYSCLK_FREQ parameter is not passed anymore

as a Makefile parameter but it is definesd in the defs_platform.h file

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