/** * \file : boot_loader_entry.c * \date : August 2012 * \author : Cesar Fuguet * * This file defines an elf file loader which reads an executable elf file * starting at a sector passed as argument of a disk and copy the different * ELF program segments in the appropriate memory address using as information * the virtual address read from the elf file. */ #include #include #include #include #include void * boot_elf_loader(unsigned int lba) { /** * Temporary variables used by the boot loader */ unsigned char boot_elf_loader_buffer[512]; Elf32_Ehdr elf_header; Elf32_Phdr elf_pht[PHDR_ARRAY_SIZE]; unsigned char * buffer_ptr; Elf32_Ehdr * elf_header_ptr; Elf32_Phdr * elf_pht_ptr; unsigned int nb_available; unsigned int nb_rest; unsigned int nb_read; unsigned int nb_block; unsigned int offset; unsigned int pseg; unsigned int i; unsigned int segment_req; /* * Loader state machine definition */ enum { ELF_HEADER_STATE, ELF_PROGRAM_HEADER_STATE, ELF_OFFSET_STATE, ELF_SEGMENT_STATE, ELF_END_STATE } init_state; boot_puts("Starting boot_elf_loader function...\n\r"); nb_block = lba; pseg = 0; nb_available = 0; nb_rest = sizeof(Elf32_Ehdr); offset = 0; elf_header_ptr = (Elf32_Ehdr *) &elf_header; elf_pht_ptr = (Elf32_Phdr *) &elf_pht[0]; init_state = ELF_HEADER_STATE; while(init_state != ELF_END_STATE) { if (nb_available == 0) { buffer_ptr = &boot_elf_loader_buffer[0]; if ( boot_ioc_read(nb_block , buffer_ptr, 1) ) { boot_puts ( "ERROR: " "IOC_FAILED" "\n" ); boot_exit(); } nb_block += 1; nb_available = 512; } nb_read = (nb_rest <= nb_available) ? nb_rest : nb_available; offset += nb_read; switch(init_state) { /** * Reading ELF executable header */ case ELF_HEADER_STATE: boot_memcpy(elf_header_ptr, buffer_ptr, nb_read); nb_rest -= nb_read; if(nb_rest == 0) { nb_rest = elf_header_ptr->e_phnum * elf_header_ptr->e_phentsize; /* Verification of ELF Magic Number */ if ( (elf_header_ptr->e_ident[EI_MAG0] != ELFMAG0) || (elf_header_ptr->e_ident[EI_MAG1] != ELFMAG1) || (elf_header_ptr->e_ident[EI_MAG2] != ELFMAG2) || (elf_header_ptr->e_ident[EI_MAG3] != ELFMAG3) ) { boot_puts( "ERROR: " "Input file does not use ELF format" "\n" ); boot_exit(); } /* * Verification of Program Headers table size. It must be * smaller than the work size allocated for the * elf_pht[PHDR_ARRAY_SIZE] array **/ if (elf_header_ptr->e_phnum > PHDR_ARRAY_SIZE) { boot_puts( "ERROR: " "ELF PHDR table size is bigger than " "the allocated work space" "\n" ); boot_exit(); } init_state = ELF_PROGRAM_HEADER_STATE; } break; /** * Reading ELF program headers */ case ELF_PROGRAM_HEADER_STATE: boot_memcpy(elf_pht_ptr, buffer_ptr, nb_read); elf_pht_ptr = (Elf32_Phdr *)((unsigned char *) elf_pht_ptr + nb_read); nb_rest -= nb_read; if(nb_rest == 0) { elf_pht_ptr = (Elf32_Phdr *) &elf_pht[0]; /* * Search the first not NULL segment in the ELF file */ for (pseg = 0; pseg < elf_header_ptr->e_phnum; pseg++) { if(elf_pht_ptr[pseg].p_type == PT_LOAD) { nb_rest = elf_pht_ptr[pseg].p_offset - offset; break; } } init_state = ELF_OFFSET_STATE; } break; /** * Go to the offset of the first not null program segment in the ELF file */ case ELF_OFFSET_STATE: nb_rest -= nb_read; if (nb_rest == 0) { nb_rest = elf_pht_ptr[pseg].p_filesz; init_state = ELF_SEGMENT_STATE; } break; /** * Reading ELF segments */ case ELF_SEGMENT_STATE: /** * Copying ELF segment data in memory segments using the virtual * address got from the ELF file */ segment_req = ((elf_pht_ptr[pseg].p_vaddr & 0xBFC00000) != 0xBFC00000); if ( segment_req ) { boot_memcpy((unsigned char *) elf_pht_ptr[pseg].p_vaddr + (elf_pht_ptr[pseg].p_filesz - nb_rest), buffer_ptr, nb_read); } nb_rest -= nb_read; if ( nb_rest == 0 ) { if ( segment_req ) { boot_puts("Copied segment at address "); boot_putx(elf_pht_ptr[pseg].p_vaddr); boot_puts("\n"); /* * Fill remaining bytes with zeros (filesz < memsz) */ for ( i = 0 ; i < (elf_pht_ptr[pseg].p_memsz - elf_pht_ptr[pseg].p_filesz) ; i-- ) { *(unsigned char *) (elf_pht_ptr[pseg].p_vaddr + elf_pht_ptr[pseg].p_filesz + i) = 0; } } /* * Search the first not NULL segment in the ELF file */ for ( pseg = pseg + 1; pseg < elf_header_ptr->e_phnum; pseg++) { if(elf_pht_ptr[pseg].p_type == PT_LOAD) { nb_rest = elf_pht_ptr[pseg].p_offset - offset; break; } } /* * Program loading finished */ if(pseg == elf_header_ptr->e_phnum) { init_state = ELF_END_STATE; break; } init_state = ELF_OFFSET_STATE; } break; default: break; } buffer_ptr += nb_read; nb_available -= nb_read; } boot_puts ( "Finishing boot_elf_loader function.\n" "Entry point address: " ); boot_putx(elf_header_ptr->e_entry); boot_puts("\n"); return ((void *) elf_header_ptr->e_entry); }