mirror of
https://github.com/Fishwaldo/Star64_linux.git
synced 2025-07-23 15:27:29 +00:00
MIPS: Initial implementation of a VDSO
Add an initial implementation of a proper (i.e. an ELF shared library) VDSO. With this commit it does not export any symbols, it only replaces the current signal return trampoline page. A later commit will add user implementations of gettimeofday()/clock_gettime(). To support both new toolchains and old ones which don't generate ABI flags section, we define its content manually and then use a tool (genvdso) to patch up the section to have the correct name and type. genvdso also extracts symbol offsets ({,rt_}sigreturn) needed by the kernel, and generates a C file containing a "struct mips_vdso_image" containing both the VDSO data and these offsets. This C file is compiled into the kernel. On 64-bit kernels we require a different VDSO for each supported ABI, so we may build up to 3 different VDSOs. The VDSO to use is selected by the mips_abi structure. A kernel/user shared data page is created and mapped below the VDSO image. This is currently empty, but will be used by the user time function implementations which are added later. [markos.chandras@imgtec.com: - Add more comments - Move abi detection in genvdso.h since it's the get_symbol function that needs it. - Add an R6 specific way to calculate the base address of VDSO in order to avoid the branch instruction which affects performance. - Do not patch .gnu.attributes since it's not needed for dynamic linking. - Simplify Makefile a little bit. - checkpatch fixes - Restrict VDSO support for binutils < 2.25 for pre-R6 - Include atomic64.h for O32 variant on MIPS64] Signed-off-by: Alex Smith <alex.smith@imgtec.com> Signed-off-by: Markos Chandras <markos.chandras@imgtec.com> Cc: Matthew Fortune <matthew.fortune@imgtec.com> Cc: linux-mips@linux-mips.org Patchwork: https://patchwork.linux-mips.org/patch/11337/ Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
This commit is contained in:
parent
22773aa9b9
commit
ebb5e78cc6
19 changed files with 1113 additions and 123 deletions
|
@ -36,7 +36,6 @@
|
|||
#include <asm/ucontext.h>
|
||||
#include <asm/cpu-features.h>
|
||||
#include <asm/war.h>
|
||||
#include <asm/vdso.h>
|
||||
#include <asm/dsp.h>
|
||||
#include <asm/inst.h>
|
||||
#include <asm/msa.h>
|
||||
|
@ -752,16 +751,15 @@ static int setup_rt_frame(void *sig_return, struct ksignal *ksig,
|
|||
struct mips_abi mips_abi = {
|
||||
#ifdef CONFIG_TRAD_SIGNALS
|
||||
.setup_frame = setup_frame,
|
||||
.signal_return_offset = offsetof(struct mips_vdso, signal_trampoline),
|
||||
#endif
|
||||
.setup_rt_frame = setup_rt_frame,
|
||||
.rt_signal_return_offset =
|
||||
offsetof(struct mips_vdso, rt_signal_trampoline),
|
||||
.restart = __NR_restart_syscall,
|
||||
|
||||
.off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs),
|
||||
.off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr),
|
||||
.off_sc_used_math = offsetof(struct sigcontext, sc_used_math),
|
||||
|
||||
.vdso = &vdso_image,
|
||||
};
|
||||
|
||||
static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
|
||||
|
@ -801,11 +799,11 @@ static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
|
|||
}
|
||||
|
||||
if (sig_uses_siginfo(&ksig->ka))
|
||||
ret = abi->setup_rt_frame(vdso + abi->rt_signal_return_offset,
|
||||
ret = abi->setup_rt_frame(vdso + abi->vdso->off_rt_sigreturn,
|
||||
ksig, regs, oldset);
|
||||
else
|
||||
ret = abi->setup_frame(vdso + abi->signal_return_offset, ksig,
|
||||
regs, oldset);
|
||||
ret = abi->setup_frame(vdso + abi->vdso->off_sigreturn,
|
||||
ksig, regs, oldset);
|
||||
|
||||
signal_setup_done(ret, ksig, 0);
|
||||
}
|
||||
|
|
|
@ -31,7 +31,6 @@
|
|||
#include <asm/ucontext.h>
|
||||
#include <asm/fpu.h>
|
||||
#include <asm/war.h>
|
||||
#include <asm/vdso.h>
|
||||
#include <asm/dsp.h>
|
||||
|
||||
#include "signal-common.h"
|
||||
|
@ -406,14 +405,12 @@ static int setup_rt_frame_32(void *sig_return, struct ksignal *ksig,
|
|||
*/
|
||||
struct mips_abi mips_abi_32 = {
|
||||
.setup_frame = setup_frame_32,
|
||||
.signal_return_offset =
|
||||
offsetof(struct mips_vdso, o32_signal_trampoline),
|
||||
.setup_rt_frame = setup_rt_frame_32,
|
||||
.rt_signal_return_offset =
|
||||
offsetof(struct mips_vdso, o32_rt_signal_trampoline),
|
||||
.restart = __NR_O32_restart_syscall,
|
||||
|
||||
.off_sc_fpregs = offsetof(struct sigcontext32, sc_fpregs),
|
||||
.off_sc_fpc_csr = offsetof(struct sigcontext32, sc_fpc_csr),
|
||||
.off_sc_used_math = offsetof(struct sigcontext32, sc_used_math),
|
||||
|
||||
.vdso = &vdso_image_o32,
|
||||
};
|
||||
|
|
|
@ -38,7 +38,6 @@
|
|||
#include <asm/fpu.h>
|
||||
#include <asm/cpu-features.h>
|
||||
#include <asm/war.h>
|
||||
#include <asm/vdso.h>
|
||||
|
||||
#include "signal-common.h"
|
||||
|
||||
|
@ -151,11 +150,11 @@ static int setup_rt_frame_n32(void *sig_return, struct ksignal *ksig,
|
|||
|
||||
struct mips_abi mips_abi_n32 = {
|
||||
.setup_rt_frame = setup_rt_frame_n32,
|
||||
.rt_signal_return_offset =
|
||||
offsetof(struct mips_vdso, n32_rt_signal_trampoline),
|
||||
.restart = __NR_N32_restart_syscall,
|
||||
|
||||
.off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs),
|
||||
.off_sc_fpc_csr = offsetof(struct sigcontext, sc_fpc_csr),
|
||||
.off_sc_used_math = offsetof(struct sigcontext, sc_used_math),
|
||||
|
||||
.vdso = &vdso_image_n32,
|
||||
};
|
||||
|
|
|
@ -1,122 +1,116 @@
|
|||
/*
|
||||
* This file is subject to the terms and conditions of the GNU General Public
|
||||
* License. See the file "COPYING" in the main directory of this archive
|
||||
* for more details.
|
||||
* Copyright (C) 2015 Imagination Technologies
|
||||
* Author: Alex Smith <alex.smith@imgtec.com>
|
||||
*
|
||||
* Copyright (C) 2009, 2010 Cavium Networks, Inc.
|
||||
* 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.
|
||||
*/
|
||||
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/binfmts.h>
|
||||
#include <linux/elf.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/unistd.h>
|
||||
#include <linux/random.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <asm/abi.h>
|
||||
#include <asm/vdso.h>
|
||||
#include <asm/uasm.h>
|
||||
#include <asm/processor.h>
|
||||
|
||||
/* Kernel-provided data used by the VDSO. */
|
||||
static union mips_vdso_data vdso_data __page_aligned_data;
|
||||
|
||||
/*
|
||||
* Including <asm/unistd.h> would give use the 64-bit syscall numbers ...
|
||||
* Mapping for the VDSO data pages. The real pages are mapped manually, as
|
||||
* what we map and where within the area they are mapped is determined at
|
||||
* runtime.
|
||||
*/
|
||||
#define __NR_O32_sigreturn 4119
|
||||
#define __NR_O32_rt_sigreturn 4193
|
||||
#define __NR_N32_rt_sigreturn 6211
|
||||
static struct page *no_pages[] = { NULL };
|
||||
static struct vm_special_mapping vdso_vvar_mapping = {
|
||||
.name = "[vvar]",
|
||||
.pages = no_pages,
|
||||
};
|
||||
|
||||
static struct page *vdso_page;
|
||||
|
||||
static void __init install_trampoline(u32 *tramp, unsigned int sigreturn)
|
||||
static void __init init_vdso_image(struct mips_vdso_image *image)
|
||||
{
|
||||
uasm_i_addiu(&tramp, 2, 0, sigreturn); /* li v0, sigreturn */
|
||||
uasm_i_syscall(&tramp, 0);
|
||||
unsigned long num_pages, i;
|
||||
|
||||
BUG_ON(!PAGE_ALIGNED(image->data));
|
||||
BUG_ON(!PAGE_ALIGNED(image->size));
|
||||
|
||||
num_pages = image->size / PAGE_SIZE;
|
||||
|
||||
for (i = 0; i < num_pages; i++) {
|
||||
image->mapping.pages[i] =
|
||||
virt_to_page(image->data + (i * PAGE_SIZE));
|
||||
}
|
||||
}
|
||||
|
||||
static int __init init_vdso(void)
|
||||
{
|
||||
struct mips_vdso *vdso;
|
||||
init_vdso_image(&vdso_image);
|
||||
|
||||
vdso_page = alloc_page(GFP_KERNEL);
|
||||
if (!vdso_page)
|
||||
panic("Cannot allocate vdso");
|
||||
|
||||
vdso = vmap(&vdso_page, 1, 0, PAGE_KERNEL);
|
||||
if (!vdso)
|
||||
panic("Cannot map vdso");
|
||||
clear_page(vdso);
|
||||
|
||||
install_trampoline(vdso->rt_signal_trampoline, __NR_rt_sigreturn);
|
||||
#ifdef CONFIG_32BIT
|
||||
install_trampoline(vdso->signal_trampoline, __NR_sigreturn);
|
||||
#else
|
||||
install_trampoline(vdso->n32_rt_signal_trampoline,
|
||||
__NR_N32_rt_sigreturn);
|
||||
install_trampoline(vdso->o32_signal_trampoline, __NR_O32_sigreturn);
|
||||
install_trampoline(vdso->o32_rt_signal_trampoline,
|
||||
__NR_O32_rt_sigreturn);
|
||||
#ifdef CONFIG_MIPS32_O32
|
||||
init_vdso_image(&vdso_image_o32);
|
||||
#endif
|
||||
|
||||
vunmap(vdso);
|
||||
#ifdef CONFIG_MIPS32_N32
|
||||
init_vdso_image(&vdso_image_n32);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
subsys_initcall(init_vdso);
|
||||
|
||||
static unsigned long vdso_addr(unsigned long start)
|
||||
{
|
||||
unsigned long offset = 0UL;
|
||||
|
||||
if (current->flags & PF_RANDOMIZE) {
|
||||
offset = get_random_int();
|
||||
offset <<= PAGE_SHIFT;
|
||||
if (TASK_IS_32BIT_ADDR)
|
||||
offset &= 0xfffffful;
|
||||
else
|
||||
offset &= 0xffffffful;
|
||||
}
|
||||
|
||||
return STACK_TOP + offset;
|
||||
}
|
||||
|
||||
int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
|
||||
{
|
||||
int ret;
|
||||
unsigned long addr;
|
||||
struct mips_vdso_image *image = current->thread.abi->vdso;
|
||||
struct mm_struct *mm = current->mm;
|
||||
unsigned long base, vdso_addr;
|
||||
struct vm_area_struct *vma;
|
||||
int ret;
|
||||
|
||||
down_write(&mm->mmap_sem);
|
||||
|
||||
addr = vdso_addr(mm->start_stack);
|
||||
|
||||
addr = get_unmapped_area(NULL, addr, PAGE_SIZE, 0, 0);
|
||||
if (IS_ERR_VALUE(addr)) {
|
||||
ret = addr;
|
||||
goto up_fail;
|
||||
base = get_unmapped_area(NULL, 0, PAGE_SIZE + image->size, 0, 0);
|
||||
if (IS_ERR_VALUE(base)) {
|
||||
ret = base;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = install_special_mapping(mm, addr, PAGE_SIZE,
|
||||
VM_READ|VM_EXEC|
|
||||
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
|
||||
&vdso_page);
|
||||
vdso_addr = base + PAGE_SIZE;
|
||||
|
||||
vma = _install_special_mapping(mm, base, PAGE_SIZE,
|
||||
VM_READ | VM_MAYREAD,
|
||||
&vdso_vvar_mapping);
|
||||
if (IS_ERR(vma)) {
|
||||
ret = PTR_ERR(vma);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Map data page. */
|
||||
ret = remap_pfn_range(vma, base,
|
||||
virt_to_phys(&vdso_data) >> PAGE_SHIFT,
|
||||
PAGE_SIZE, PAGE_READONLY);
|
||||
if (ret)
|
||||
goto up_fail;
|
||||
goto out;
|
||||
|
||||
mm->context.vdso = (void *)addr;
|
||||
/* Map VDSO image. */
|
||||
vma = _install_special_mapping(mm, vdso_addr, image->size,
|
||||
VM_READ | VM_EXEC |
|
||||
VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
|
||||
&image->mapping);
|
||||
if (IS_ERR(vma)) {
|
||||
ret = PTR_ERR(vma);
|
||||
goto out;
|
||||
}
|
||||
|
||||
up_fail:
|
||||
mm->context.vdso = (void *)vdso_addr;
|
||||
ret = 0;
|
||||
|
||||
out:
|
||||
up_write(&mm->mmap_sem);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *arch_vma_name(struct vm_area_struct *vma)
|
||||
{
|
||||
if (vma->vm_mm && vma->vm_start == (long)vma->vm_mm->context.vdso)
|
||||
return "[vdso]";
|
||||
return NULL;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue