linux/arch/tile/mm/elf.c
<<
>>
Prefs
   1/*
   2 * Copyright 2010 Tilera Corporation. All Rights Reserved.
   3 *
   4 *   This program is free software; you can redistribute it and/or
   5 *   modify it under the terms of the GNU General Public License
   6 *   as published by the Free Software Foundation, version 2.
   7 *
   8 *   This program is distributed in the hope that it will be useful, but
   9 *   WITHOUT ANY WARRANTY; without even the implied warranty of
  10 *   MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  11 *   NON INFRINGEMENT.  See the GNU General Public License for
  12 *   more details.
  13 */
  14
  15#include <linux/mm.h>
  16#include <linux/pagemap.h>
  17#include <linux/binfmts.h>
  18#include <linux/compat.h>
  19#include <linux/mman.h>
  20#include <linux/file.h>
  21#include <linux/elf.h>
  22#include <asm/pgtable.h>
  23#include <asm/pgalloc.h>
  24#include <asm/sections.h>
  25#include <asm/vdso.h>
  26#include <arch/sim.h>
  27
  28/* Notify a running simulator, if any, that an exec just occurred. */
  29static void sim_notify_exec(const char *binary_name)
  30{
  31        unsigned char c;
  32        do {
  33                c = *binary_name++;
  34                __insn_mtspr(SPR_SIM_CONTROL,
  35                             (SIM_CONTROL_OS_EXEC
  36                              | (c << _SIM_CONTROL_OPERATOR_BITS)));
  37
  38        } while (c);
  39}
  40
  41static int notify_exec(struct mm_struct *mm)
  42{
  43        int ret = 0;
  44        char *buf, *path;
  45        struct vm_area_struct *vma;
  46        struct file *exe_file;
  47
  48        if (!sim_is_simulator())
  49                return 1;
  50
  51        buf = (char *) __get_free_page(GFP_KERNEL);
  52        if (buf == NULL)
  53                return 0;
  54
  55        exe_file = get_mm_exe_file(mm);
  56        if (exe_file == NULL)
  57                goto done_free;
  58
  59        path = file_path(exe_file, buf, PAGE_SIZE);
  60        if (IS_ERR(path))
  61                goto done_put;
  62
  63        down_read(&mm->mmap_sem);
  64        for (vma = current->mm->mmap; ; vma = vma->vm_next) {
  65                if (vma == NULL) {
  66                        up_read(&mm->mmap_sem);
  67                        goto done_put;
  68                }
  69                if (vma->vm_file == exe_file)
  70                        break;
  71        }
  72
  73        /*
  74         * Notify simulator of an ET_DYN object so we know the load address.
  75         * The somewhat cryptic overuse of SIM_CONTROL_DLOPEN allows us
  76         * to be backward-compatible with older simulator releases.
  77         */
  78        if (vma->vm_start == (ELF_ET_DYN_BASE & PAGE_MASK)) {
  79                char buf[64];
  80                int i;
  81
  82                snprintf(buf, sizeof(buf), "0x%lx:@", vma->vm_start);
  83                for (i = 0; ; ++i) {
  84                        char c = buf[i];
  85                        __insn_mtspr(SPR_SIM_CONTROL,
  86                                     (SIM_CONTROL_DLOPEN
  87                                      | (c << _SIM_CONTROL_OPERATOR_BITS)));
  88                        if (c == '\0') {
  89                                ret = 1; /* success */
  90                                break;
  91                        }
  92                }
  93        }
  94        up_read(&mm->mmap_sem);
  95
  96        sim_notify_exec(path);
  97done_put:
  98        fput(exe_file);
  99done_free:
 100        free_page((unsigned long)buf);
 101        return ret;
 102}
 103
 104/* Notify a running simulator, if any, that we loaded an interpreter. */
 105static void sim_notify_interp(unsigned long load_addr)
 106{
 107        size_t i;
 108        for (i = 0; i < sizeof(load_addr); i++) {
 109                unsigned char c = load_addr >> (i * 8);
 110                __insn_mtspr(SPR_SIM_CONTROL,
 111                             (SIM_CONTROL_OS_INTERP
 112                              | (c << _SIM_CONTROL_OPERATOR_BITS)));
 113        }
 114}
 115
 116
 117int arch_setup_additional_pages(struct linux_binprm *bprm,
 118                                int executable_stack)
 119{
 120        struct mm_struct *mm = current->mm;
 121        int retval = 0;
 122
 123        /*
 124         * Notify the simulator that an exec just occurred.
 125         * If we can't find the filename of the mapping, just use
 126         * whatever was passed as the linux_binprm filename.
 127         */
 128        if (!notify_exec(mm))
 129                sim_notify_exec(bprm->filename);
 130
 131        down_write(&mm->mmap_sem);
 132
 133        retval = setup_vdso_pages();
 134
 135#ifndef __tilegx__
 136        /*
 137         * Set up a user-interrupt mapping here; the user can't
 138         * create one themselves since it is above TASK_SIZE.
 139         * We make it unwritable by default, so the model for adding
 140         * interrupt vectors always involves an mprotect.
 141         */
 142        if (!retval) {
 143                unsigned long addr = MEM_USER_INTRPT;
 144                addr = mmap_region(NULL, addr, INTRPT_SIZE,
 145                                   VM_READ|VM_EXEC|
 146                                   VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, 0, NULL);
 147                if (addr > (unsigned long) -PAGE_SIZE)
 148                        retval = (int) addr;
 149        }
 150#endif
 151
 152        up_write(&mm->mmap_sem);
 153
 154        return retval;
 155}
 156
 157
 158void elf_plat_init(struct pt_regs *regs, unsigned long load_addr)
 159{
 160        /* Zero all registers. */
 161        memset(regs, 0, sizeof(*regs));
 162
 163        /* Report the interpreter's load address. */
 164        sim_notify_interp(load_addr);
 165}
 166