/* Copyright (C) 2005 David Decotigny This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include #include #include "binfmt_elf32.h" /** * The "C" structure of a user program image in the kernel. Structures * like this are created by the Makefile in the userland/ directory */ struct userprog_entry { const char *name; sos_vaddr_t bottom_vaddr; sos_vaddr_t top_vaddr; }; /** * Symbol marking the start of the userprogs table, as setup by the * ld script in the userland/ directory */ extern char _userprogs_table; /** * Structure of a mapped resource for an ELF32 program (ie a portion * of the kernel space) */ struct elf32_mapped_program { sos_vaddr_t vaddr; sos_size_t size; int ref_cnt; struct sos_umem_vmm_mapped_resource mr; }; /** Called after the virtual region has been inserted inside its address space */ static void elf32prog_ref(struct sos_umem_vmm_vr * vr) { struct elf32_mapped_program * elf32prog_resource; elf32prog_resource = (struct elf32_mapped_program*) sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data; elf32prog_resource->ref_cnt ++; } /** Called when the virtual region is removed from its address space */ static void elf32prog_unref(struct sos_umem_vmm_vr * vr) { struct elf32_mapped_program * elf32prog_resource; elf32prog_resource = (struct elf32_mapped_program*) sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data; elf32prog_resource->ref_cnt --; SOS_ASSERT_FATAL(elf32prog_resource->ref_cnt >= 0); /* Free the resource if it becomes unused */ if (elf32prog_resource->ref_cnt == 0) sos_kfree((sos_vaddr_t)elf32prog_resource); } /** Called when a legitimate page fault is occuring in the VR */ static sos_ret_t elf32prog_page_in(struct sos_umem_vmm_vr * vr, sos_uaddr_t uaddr, sos_bool_t write_access) { struct elf32_mapped_program * elf32prog_resource; sos_ret_t retval = SOS_OK; sos_paddr_t ppage_paddr; sos_uaddr_t upage_uaddr = SOS_PAGE_ALIGN_INF(uaddr); sos_uoffset_t offset_in_prog; sos_size_t size_to_copy; sos_ui32_t access_rights = sos_umem_vmm_get_prot_of_vr(vr); elf32prog_resource = (struct elf32_mapped_program*) sos_umem_vmm_get_mapped_resource_of_vr(vr)->custom_data; /* Compute the offset in program of the page, and the size to copy in user space */ offset_in_prog = upage_uaddr - sos_umem_vmm_get_start_of_vr(vr) + sos_umem_vmm_get_offset_in_resource(vr); size_to_copy = SOS_PAGE_SIZE; if (offset_in_prog + size_to_copy > elf32prog_resource->size) size_to_copy = elf32prog_resource->size - offset_in_prog; /* If the source page is also aligned, simply remap the kernel area into user space */ if (SOS_IS_PAGE_ALIGNED(elf32prog_resource->vaddr + offset_in_prog)) { sos_vaddr_t kern_vaddr = elf32prog_resource->vaddr + offset_in_prog; ppage_paddr = sos_paging_get_paddr(kern_vaddr); /* Remap it in user space, in read-only mode (to force COW) */ retval = sos_paging_map(ppage_paddr, upage_uaddr, TRUE, access_rights & ~SOS_VM_MAP_PROT_WRITE); SOS_ASSERT_FATAL(SOS_OK == retval); } /* Otherwise we need to allocate a new page */ else { /* Allocate a new page that contains the code/data of the program */ ppage_paddr = sos_physmem_ref_physpage_new(FALSE); if (! ppage_paddr) return -SOS_ENOMEM; /* Map it in user space, in read/write mode for the kernel to copy the data in the page */ retval = sos_paging_map(ppage_paddr, upage_uaddr, TRUE, access_rights | SOS_VM_MAP_PROT_WRITE); SOS_ASSERT_FATAL(SOS_OK == retval); sos_physmem_unref_physpage(ppage_paddr); /* Copy the program in it */ memcpy((void*)upage_uaddr, (void*)(elf32prog_resource->vaddr + offset_in_prog), size_to_copy); if (size_to_copy < SOS_PAGE_SIZE) memset((void*)(upage_uaddr + size_to_copy), 0x0, SOS_PAGE_SIZE - size_to_copy); /* Change it read-only if needed */ if (! (access_rights & SOS_VM_MAP_PROT_WRITE)) return sos_paging_set_prot(upage_uaddr, access_rights & ~SOS_VM_MAP_PROT_WRITE); } return retval; } static struct sos_umem_vmm_vr_ops elf32prog_ops = (struct sos_umem_vmm_vr_ops) { .ref = elf32prog_ref, .unref = elf32prog_unref, .page_in = elf32prog_page_in, .unmap = NULL /* ignored */ }; static sos_ret_t elf32prog_mmap(struct sos_umem_vmm_vr *vr) { return sos_umem_vmm_set_ops_of_vr(vr, &elf32prog_ops); } /* * Local functions */ /** * Function to locate the given user program image in the kernel memory */ static struct userprog_entry * lookup_userprog(const char *name); sos_uaddr_t sos_binfmt_elf32_map(struct sos_umem_vmm_as * dest_as, const char * progname) { int i; /** * Typedefs, constants and structure definitions as given by the ELF * standard specifications. */ typedef unsigned long Elf32_Addr; typedef unsigned long Elf32_Word; typedef unsigned short Elf32_Half; typedef unsigned long Elf32_Off; typedef signed long Elf32_Sword; /* Elf identification */ #define EI_NIDENT 16 typedef struct { unsigned char e_ident[EI_NIDENT]; Elf32_Half e_type; Elf32_Half e_machine; Elf32_Word e_version; Elf32_Addr e_entry; Elf32_Off e_phoff; Elf32_Off e_shoff; Elf32_Word e_flags; Elf32_Half e_ehsize; Elf32_Half e_phentsize; Elf32_Half e_phnum; Elf32_Half e_shentsize; Elf32_Half e_shnum; Elf32_Half e_shstrndx; } __attribute__((packed)) Elf32_Ehdr_t; /* e_ident value */ #define ELFMAG0 0x7f #define ELFMAG1 'E' #define ELFMAG2 'L' #define ELFMAG3 'F' /* e_ident offsets */ #define EI_MAG0 0 #define EI_MAG1 1 #define EI_MAG2 2 #define EI_MAG3 3 #define EI_CLASS 4 #define EI_DATA 5 #define EI_VERSION 6 #define EI_PAD 7 /* e_ident[EI_CLASS] */ #define ELFCLASSNONE 0 #define ELFCLASS32 1 #define ELFCLASS64 2 /* e_ident[EI_DATA] */ #define ELFDATANONE 0 #define ELFDATA2LSB 1 #define ELFDATA2MSB 2 /* e_type */ #define ET_NONE 0 /* No file type */ #define ET_REL 1 /* Relocatable file */ #define ET_EXEC 2 /* Executable file */ #define ET_DYN 3 /* Shared object file */ #define ET_CORE 4 /* Core file */ #define ET_LOPROC 0xff00 /* Processor-specific */ #define ET_HIPROC 0xffff /* Processor-specific */ /* e_machine */ #define EM_NONE 0 /* No machine */ #define EM_M32 1 /* AT&T WE 32100 */ #define EM_SPARC 2 /* SPARC */ #define EM_386 3 /* Intel 80386 */ #define EM_68K 4 /* Motorola 68000 */ #define EM_88K 5 /* Motorola 88000 */ #define EM_860 7 /* Intel 80860 */ #define EM_MIPS 8 /* MIPS RS3000 */ /* e_version */ #define EV_NONE 0 /* invalid version */ #define EV_CURRENT 1 /* current version */ typedef struct { Elf32_Word p_type; Elf32_Off p_offset; Elf32_Addr p_vaddr; Elf32_Addr p_paddr; Elf32_Word p_filesz; Elf32_Word p_memsz; Elf32_Word p_flags; Elf32_Word p_align; } __attribute__((packed)) Elf32_Phdr_t; /* Reserved segment types p_type */ #define PT_NULL 0 #define PT_LOAD 1 #define PT_DYNAMIC 2 #define PT_INTERP 3 #define PT_NOTE 4 #define PT_SHLIB 5 #define PT_PHDR 6 #define PT_LOPROC 0x70000000 #define PT_HIPROC 0x7fffffff /* p_flags */ #define PF_X 1 #define PF_W 2 #define PF_R 4 Elf32_Ehdr_t *elf_hdr; Elf32_Phdr_t *elf_phdrs; struct elf32_mapped_program * mapped_prog; struct userprog_entry * prog; sos_uaddr_t prog_top_user_address = 0; mapped_prog = (struct elf32_mapped_program*) sos_kmalloc(sizeof(struct elf32_mapped_program), 0); if (! mapped_prog) return -SOS_ENOMEM; prog = lookup_userprog(progname); if (! prog) { sos_kfree((sos_vaddr_t)mapped_prog); return 0; } /* Initialize mapped resource */ memset(mapped_prog, 0x0, sizeof(*mapped_prog)); mapped_prog->mr.custom_data = mapped_prog; mapped_prog->mr.mmap = elf32prog_mmap; mapped_prog->mr.allowed_access_rights = SOS_VM_MAP_PROT_READ | SOS_VM_MAP_PROT_WRITE | SOS_VM_MAP_PROT_EXEC; mapped_prog->vaddr = prog->bottom_vaddr; mapped_prog->size = prog->top_vaddr - prog->bottom_vaddr; elf_hdr = (Elf32_Ehdr_t*) prog->bottom_vaddr; /* Make sure the image is large enough to contain at least the ELF header */ if (prog->bottom_vaddr + sizeof(Elf32_Ehdr_t) > prog->top_vaddr) { sos_bochs_printf("ELF prog %s: incorrect header\n", prog->name); return 0; } /* Macro to check expected values for some fields in the ELF header */ #define ELF_CHECK(hdr,field,expected_value) \ ({ if ((hdr)->field != (expected_value)) \ { \ sos_bochs_printf("ELF prog %s: for %s, expected %x, got %x\n", \ prog->name, \ #field, \ (unsigned)(expected_value), \ (unsigned)(hdr)->field); \ return 0; \ } \ }) ELF_CHECK(elf_hdr, e_ident[EI_MAG0], ELFMAG0); ELF_CHECK(elf_hdr, e_ident[EI_MAG1], ELFMAG1); ELF_CHECK(elf_hdr, e_ident[EI_MAG2], ELFMAG2); ELF_CHECK(elf_hdr, e_ident[EI_MAG3], ELFMAG3); ELF_CHECK(elf_hdr, e_ident[EI_CLASS], ELFCLASS32); ELF_CHECK(elf_hdr, e_ident[EI_DATA], ELFDATA2LSB); ELF_CHECK(elf_hdr, e_type, ET_EXEC); ELF_CHECK(elf_hdr, e_version, EV_CURRENT); /* Get the begining of the program header table */ elf_phdrs = (Elf32_Phdr_t*) (prog->bottom_vaddr + elf_hdr->e_phoff); /* Map the program segment in R/W mode. To make things clean, we should iterate over the sections, not the program header */ for (i = 0 ; i < elf_hdr->e_phnum ; i++) { sos_ui32_t prot_flags; sos_uaddr_t uaddr; /* Ignore the empty program headers that are not marked "LOAD" */ if (elf_phdrs[i].p_type != PT_LOAD) { if (elf_phdrs[i].p_memsz != 0) { sos_display_fatal_error("ELF: non-empty non-LOAD segments not supported yet"); } continue; } if (! SOS_PAGING_IS_USER_AREA(elf_phdrs[i].p_vaddr, elf_phdrs[i].p_memsz) ) { sos_display_fatal_error("User program has an incorrect address"); } prot_flags = 0; if (elf_phdrs[i].p_flags & SOS_VM_MAP_PROT_READ) prot_flags |= SOS_VM_MAP_PROT_READ; if (elf_phdrs[i].p_flags & SOS_VM_MAP_PROT_WRITE) prot_flags |= SOS_VM_MAP_PROT_WRITE; if (elf_phdrs[i].p_flags & SOS_VM_MAP_PROT_EXEC) prot_flags |= SOS_VM_MAP_PROT_EXEC; uaddr = elf_phdrs[i].p_vaddr; SOS_ASSERT_FATAL(SOS_IS_PAGE_ALIGNED(uaddr)); /* First of all: map the region of the phdr which is also covered by the file */ SOS_ASSERT_FATAL(SOS_OK == sos_umem_vmm_map(dest_as, &uaddr, SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_filesz), prot_flags, /* PRIVATE */ SOS_VR_MAP_FIXED, & mapped_prog->mr, elf_phdrs[i].p_offset)); /* Then map the remaining by a zero resource */ uaddr += SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_filesz); if (SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_filesz) < SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_memsz)) SOS_ASSERT_FATAL(SOS_OK == sos_dev_zero_map(dest_as, &uaddr, SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_memsz) - SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_filesz), prot_flags, /* PRIVATE */ SOS_VR_MAP_FIXED)); if (prog_top_user_address < uaddr + SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_memsz)) prog_top_user_address = uaddr + SOS_PAGE_ALIGN_SUP(elf_phdrs[i].p_memsz); } /* Now prepare the heap */ sos_umem_vmm_init_heap(dest_as, prog_top_user_address); return elf_hdr->e_entry; } /** * Lookup a user program located inside the kernel's image */ static struct userprog_entry * lookup_userprog(const char *name) { struct userprog_entry *prog; if (! name) return NULL; /* Walk through the table of user program description structures to find the user program with the given name */ for (prog = (struct userprog_entry*) & _userprogs_table ; prog && (prog->name != NULL) ; prog++) { if (0 == strcmp(name, prog->name)) /* Found it ! */ return prog; } return NULL; }