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/elf.h>
  21#include <asm/pgtable.h>
  22#include <asm/pgalloc.h>
  23#include <asm/sections.h>
  24#include <asm/vdso.h>
  25#include <arch/sim.h>
  26
  27/* Notify a running simulator, if any, that an exec just occurred. */
  28static void sim_notify_exec(const char *binary_name)
  29{
  30        unsigned char c;
  31        do {
  32                c = *binary_name++;
  33                __insn_mtspr(SPR_SIM_CONTROL,
  34                             (SIM_CONTROL_OS_EXEC
  35                              | (c << _SIM_CONTROL_OPERATOR_BITS)));
  36
  37        } while (c);
  38}
  39
  40static int notify_exec(struct mm_struct *mm)
  41{
  42        char *buf, *path;
  43        struct vm_area_struct *vma;
  44
  45        if (!sim_is_simulator())
  46                return 1;
  47
  48        if (mm->exe_file == NULL)
  49                return 0;
  50
  51        for (vma = current->mm->mmap; ; vma = vma->vm_next) {
  52                if (vma == NULL)
  53                        return 0;
  54                if (vma->vm_file == mm->exe_file)
  55                        break;
  56        }
  57
  58        buf = (char *) __get_free_page(GFP_KERNEL);
  59        if (buf == NULL)
  60                return 0;
  61
  62        path = d_path(&mm->exe_file->f_path, buf, PAGE_SIZE);
  63        if (IS_ERR(path)) {
  64                free_page((unsigned long)buf);
  65                return 0;
  66        }
  67
  68        /*
  69         * Notify simulator of an ET_DYN object so we know the load address.
  70         * The somewhat cryptic overuse of SIM_CONTROL_DLOPEN allows us
  71         * to be backward-compatible with older simulator releases.
  72         */
  73        if (vma->vm_start == (ELF_ET_DYN_BASE & PAGE_MASK)) {
  74                char buf[64];
  75                int i;
  76
  77                snprintf(buf, sizeof(buf), "0x%lx:@", vma->vm_start);
  78                for (i = 0; ; ++i) {
  79                        char c = buf[i];
  80                        __insn_mtspr(SPR_SIM_CONTROL,
  81                                     (SIM_CONTROL_DLOPEN
  82                                      | (c << _SIM_CONTROL_OPERATOR_BITS)));
  83                        if (c == '\0')
  84                                break;
  85                }
  86        }
  87
  88        sim_notify_exec(path);
  89        free_page((unsigned long)buf);
  90        return 1;
  91}
  92
  93/* Notify a running simulator, if any, that we loaded an interpreter. */
  94static void sim_notify_interp(unsigned long load_addr)
  95{
  96        size_t i;
  97        for (i = 0; i < sizeof(load_addr); i++) {
  98                unsigned char c = load_addr >> (i * 8);
  99                __insn_mtspr(SPR_SIM_CONTROL,
 100                             (SIM_CONTROL_OS_INTERP
 101                              | (c << _SIM_CONTROL_OPERATOR_BITS)));
 102        }
 103}
 104
 105
 106int arch_setup_additional_pages(struct linux_binprm *bprm,
 107                                int executable_stack)
 108{
 109        struct mm_struct *mm = current->mm;
 110        int retval = 0;
 111
 112        down_write(&mm->mmap_sem);
 113
 114        /*
 115         * Notify the simulator that an exec just occurred.
 116         * If we can't find the filename of the mapping, just use
 117         * whatever was passed as the linux_binprm filename.
 118         */
 119        if (!notify_exec(mm))
 120                sim_notify_exec(bprm->filename);
 121
 122        retval = setup_vdso_pages();
 123
 124#ifndef __tilegx__
 125        /*
 126         * Set up a user-interrupt mapping here; the user can't
 127         * create one themselves since it is above TASK_SIZE.
 128         * We make it unwritable by default, so the model for adding
 129         * interrupt vectors always involves an mprotect.
 130         */
 131        if (!retval) {
 132                unsigned long addr = MEM_USER_INTRPT;
 133                addr = mmap_region(NULL, addr, INTRPT_SIZE,
 134                                   VM_READ|VM_EXEC|
 135                                   VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC, 0);
 136                if (addr > (unsigned long) -PAGE_SIZE)
 137                        retval = (int) addr;
 138        }
 139#endif
 140
 141        up_write(&mm->mmap_sem);
 142
 143        return retval;
 144}
 145
 146
 147void elf_plat_init(struct pt_regs *regs, unsigned long load_addr)
 148{
 149        /* Zero all registers. */
 150        memset(regs, 0, sizeof(*regs));
 151
 152        /* Report the interpreter's load address. */
 153        sim_notify_interp(load_addr);
 154}
 155