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

Last change on this file since 570 was 570, checked in by cfuguet, 10 years ago

Modifications in tsar_boot:

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