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

Last change on this file since 554 was 554, checked in by bouyer, 11 years ago

Make sure the boot loader won't try to write to the ROM.
Store read-write data in BSS along with the stack at seg_stack_base.

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 const * const 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.