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

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

preloader: when loading LINUX, copy the DTB in low memory addresses

File size: 4.1 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
18extern int blk_buf_idx;
19extern int dtb_start, dtb_end;
20
21addr_t dtb_addr;
22
23///////////////////////////////////////////////////////////////////////////////
24void * reset_elf_loader(size_t lba)
25///////////////////////////////////////////////////////////////////////////////
26{
27    size_t file_offset = lba * BLOCK_SIZE;
28
29    reset_puts("\n[RESET] Start reset_elf_loader at cycle ");
30    reset_putd( proctime() );
31    reset_puts("\n");
32
33    /*
34     * Init the cache block index
35     */
36    blk_buf_idx = -1;
37
38    /*
39     * Load ELF HEADER
40     */
41    Elf32_Ehdr elf_header;
42    if (pread(file_offset, (void*)&elf_header, sizeof(Elf32_Ehdr), 0) < 0) {
43        goto error;
44    }
45    check_elf_header(&elf_header);
46
47    /*
48     * Load ELF PROGRAM HEADER TABLE
49     */
50    Elf32_Phdr elf_pht[RESET_PHDR_ARRAY_SIZE];
51    size_t phdr_nbyte = sizeof(Elf32_Phdr) * elf_header.e_phnum;
52    size_t phdr_off = elf_header.e_phoff;
53    if (pread(file_offset, (void*)&elf_pht, phdr_nbyte, phdr_off) < 0) {
54        goto error;
55    }
56
57    /*
58     * Search for loadable segments in the ELF file
59     */
60    int pseg;
61    for (pseg = 0; pseg < elf_header.e_phnum; pseg++) {
62        if(elf_pht[pseg].p_type != PT_LOAD) continue;
63
64#if (RESET_DEBUG == 1)
65        reset_puts("[RESET DEBUG] Loadable segment found:\n");
66        reset_print_elf_phdr(&elf_pht[pseg]);
67#endif
68
69        addr_t p_paddr = elf_pht[pseg].p_paddr;
70        size_t p_filesz = elf_pht[pseg].p_filesz;
71        size_t p_memsz = elf_pht[pseg].p_memsz;
72        size_t p_offset = elf_pht[pseg].p_offset;
73
74        /*
75         * Copy program segment from ELF executable into corresponding physical
76         * address
77         */
78        if (pread(file_offset, (void*)p_paddr, p_filesz, p_offset) < 0) {
79            goto error;
80        }
81
82        /*
83         * Fill remaining bytes with zero (filesz < memsz)
84         */
85        char* pseg_ptr = (char*)p_paddr;
86        memset((void*)&pseg_ptr[p_filesz], 0, (p_memsz - p_filesz));
87
88        reset_puts("\n[RESET] Segment loaded : address = ");
89        reset_putx(p_paddr);
90        reset_puts(" / size = ");
91        reset_putx(p_filesz);
92        reset_puts("\n");
93    }
94
95    if (pseg == 0) {
96        reset_puts("\n[RESET ERROR] No loadable segments");
97        goto error;
98    }
99
100    /* By default, the used device tree is the one in the ROM */
101    dtb_addr = (addr_t)&dtb_start;
102
103#if OS_LINUX
104    /* When loading a Linux kernel, the device tree should be located in low
105     * memory addresses before the kernel itself.
106     * - When the ROM-contained DTB is located before the kernel no need to copy
107     *   the DTB elsewhere.
108     * - When the ROM-contained DTB is located after the kernel the DTB is
109     *   copied before the kernel. */
110    const int copy_dtb = ((addr_t)&dtb_start > elf_pht[0].p_paddr);
111    if (copy_dtb) {
112        size_t dtb_size = (size_t)&dtb_end - (size_t)&dtb_start;
113        dtb_addr = SEG_RAM_BASE;
114        if ((dtb_addr + (addr_t)dtb_size) >= elf_pht[0].p_paddr) {
115            reset_puts("\n[RESET ERROR] Insufficient space to copy the DTB");
116            goto error;
117        }
118
119        memcpy((void*)dtb_addr, (void*)&dtb_start, dtb_size);
120        reset_puts("\n[RESET] Device tree blob copied / address = ");
121        reset_putx(dtb_addr);
122        reset_puts(" / size = ");
123        reset_putx(dtb_size);
124    }
125#endif
126
127    reset_puts("\n[RESET] Complete reset_elf_loader at cycle ");
128    reset_putd(proctime());
129    reset_puts(" / boot entry = ");
130    reset_putx((addr_t)elf_header.e_entry);
131    reset_puts("\n");
132
133    return ((void*)elf_header.e_entry);
134
135error:
136    reset_puts("\n[RESET ERROR] Error while loading ELF file");
137    reset_exit();
138    return 0;
139}
140
141// vim: tabstop=4 : softtabstop=4 : shiftwidth=4 : expandtab
Note: See TracBrowser for help on using the repository browser.