csky: Reconstruct VDSO framework

Reconstruct vdso framework to support future vsyscall,
vgettimeofday features. These are very important features to reduce
system calls into the kernel for performance improvement.

The patch is reference RISC-V's

Signed-off-by: Guo Ren <guoren@linux.alibaba.com>
Cc: Palmer Dabbelt <palmerdabbelt@google.com>
This commit is contained in:
Guo Ren 2021-01-04 03:37:07 +00:00
parent e26db7ad9c
commit 87f3248cdb
13 changed files with 269 additions and 89 deletions

View File

@ -1,17 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/uaccess.h>
#ifndef __ABI_CSKY_VDSO_H
#define __ABI_CSKY_VDSO_H
static inline int setup_vdso_page(unsigned short *ptr)
{
int err = 0;
/* movi r1, 127; addi r1, (139 - 127) */
#define SET_SYSCALL_ID .long 0x20b167f1
/* movi r1, 127 */
err |= __put_user(0x67f1, ptr + 0);
/* addi r1, (139 - 127) */
err |= __put_user(0x20b1, ptr + 1);
/* trap 0 */
err |= __put_user(0x0008, ptr + 2);
return err;
}
#endif /* __ABI_CSKY_VDSO_H */

View File

@ -3,21 +3,7 @@
#ifndef __ABI_CSKY_VDSO_H
#define __ABI_CSKY_VDSO_H
#include <linux/uaccess.h>
/* movi r7, 173 */
#define SET_SYSCALL_ID .long 0x008bea07
static inline int setup_vdso_page(unsigned short *ptr)
{
int err = 0;
/* movi r7, 173 */
err |= __put_user(0xea07, ptr);
err |= __put_user(0x008b, ptr+1);
/* trap 0 */
err |= __put_user(0xc000, ptr+2);
err |= __put_user(0x2020, ptr+3);
return err;
}
#endif /* __ABI_CSKY_STRING_H */
#endif /* __ABI_CSKY_VDSO_H */

View File

@ -3,10 +3,23 @@
#ifndef __ASM_CSKY_VDSO_H
#define __ASM_CSKY_VDSO_H
#include <abi/vdso.h>
#include <linux/types.h>
struct csky_vdso {
unsigned short rt_signal_retcode[4];
struct vdso_data {
};
/*
* The VDSO symbols are mapped into Linux so we can just use regular symbol
* addressing to get their offsets in userspace. The symbols are mapped at an
* offset of 0, but since the linker must support setting weak undefined
* symbols to the absolute address 0 it also happens to support other low
* addresses even when the code model suggests those low addresses would not
* otherwise be availiable.
*/
#define VDSO_SYMBOL(base, name) \
({ \
extern const char __vdso_##name[]; \
(void __user *)((unsigned long)(base) + __vdso_##name); \
})
#endif /* __ASM_CSKY_VDSO_H */

View File

@ -1,7 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
extra-y := head.o vmlinux.lds
obj-y += entry.o atomic.o signal.o traps.o irq.o time.o vdso.o
obj-y += entry.o atomic.o signal.o traps.o irq.o time.o vdso.o vdso/
obj-y += power.o syscall.o syscall_table.o setup.o
obj-y += process.o cpu-probe.o ptrace.o stacktrace.o
obj-y += probes/

View File

@ -134,7 +134,6 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
{
struct rt_sigframe *frame;
int err = 0;
struct csky_vdso *vdso = current->mm->context.vdso;
frame = get_sigframe(ksig, regs, sizeof(*frame));
if (!access_ok(frame, sizeof(*frame)))
@ -152,7 +151,8 @@ setup_rt_frame(struct ksignal *ksig, sigset_t *set, struct pt_regs *regs)
return -EFAULT;
/* Set up to return from userspace. */
regs->lr = (unsigned long)(vdso->rt_signal_retcode);
regs->lr = (unsigned long)VDSO_SYMBOL(
current->mm->context.vdso, rt_sigreturn);
/*
* Set up registers for signal handler.

View File

@ -1,86 +1,103 @@
// SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2018 Hangzhou C-SKY Microsystems co.,ltd.
#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/uaccess.h>
#include <linux/err.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <asm/page.h>
#include <asm/vdso.h>
#include <asm/cacheflush.h>
static struct page *vdso_page;
extern char vdso_start[], vdso_end[];
static int __init init_vdso(void)
static unsigned int vdso_pages;
static struct page **vdso_pagelist;
/*
* The vDSO data page.
*/
static union {
struct vdso_data data;
u8 page[PAGE_SIZE];
} vdso_data_store __page_aligned_data;
struct vdso_data *vdso_data = &vdso_data_store.data;
static int __init vdso_init(void)
{
struct csky_vdso *vdso;
int err = 0;
unsigned int i;
vdso_page = alloc_page(GFP_KERNEL);
if (!vdso_page)
panic("Cannot allocate vdso");
vdso_pages = (vdso_end - vdso_start) >> PAGE_SHIFT;
vdso_pagelist =
kcalloc(vdso_pages + 1, sizeof(struct page *), GFP_KERNEL);
if (unlikely(vdso_pagelist == NULL)) {
pr_err("vdso: pagelist allocation failed\n");
return -ENOMEM;
}
vdso = vmap(&vdso_page, 1, 0, PAGE_KERNEL);
if (!vdso)
panic("Cannot map vdso");
for (i = 0; i < vdso_pages; i++) {
struct page *pg;
clear_page(vdso);
err = setup_vdso_page(vdso->rt_signal_retcode);
if (err)
panic("Cannot set signal return code, err: %x.", err);
dcache_wb_range((unsigned long)vdso, (unsigned long)vdso + 16);
vunmap(vdso);
pg = virt_to_page(vdso_start + (i << PAGE_SHIFT));
vdso_pagelist[i] = pg;
}
vdso_pagelist[i] = virt_to_page(vdso_data);
return 0;
}
subsys_initcall(init_vdso);
arch_initcall(vdso_init);
int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
int arch_setup_additional_pages(struct linux_binprm *bprm,
int uses_interp)
{
int ret;
unsigned long addr;
struct mm_struct *mm = current->mm;
unsigned long vdso_base, vdso_len;
int ret;
vdso_len = (vdso_pages + 1) << PAGE_SHIFT;
mmap_write_lock(mm);
addr = get_unmapped_area(NULL, STACK_TOP, PAGE_SIZE, 0, 0);
if (IS_ERR_VALUE(addr)) {
ret = addr;
goto up_fail;
vdso_base = get_unmapped_area(NULL, 0, vdso_len, 0, 0);
if (IS_ERR_VALUE(vdso_base)) {
ret = vdso_base;
goto end;
}
ret = install_special_mapping(
mm,
addr,
PAGE_SIZE,
VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
&vdso_page);
if (ret)
goto up_fail;
/*
* Put vDSO base into mm struct. We need to do this before calling
* install_special_mapping or the perf counter mmap tracking code
* will fail to recognise it as a vDSO (since arch_vma_name fails).
*/
mm->context.vdso = (void *)vdso_base;
mm->context.vdso = (void *)addr;
ret =
install_special_mapping(mm, vdso_base, vdso_pages << PAGE_SHIFT,
(VM_READ | VM_EXEC | VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC),
vdso_pagelist);
up_fail:
if (unlikely(ret)) {
mm->context.vdso = NULL;
goto end;
}
vdso_base += (vdso_pages << PAGE_SHIFT);
ret = install_special_mapping(mm, vdso_base, PAGE_SIZE,
(VM_READ | VM_MAYREAD), &vdso_pagelist[vdso_pages]);
if (unlikely(ret))
mm->context.vdso = NULL;
end:
mmap_write_unlock(mm);
return ret;
}
const char *arch_vma_name(struct vm_area_struct *vma)
{
if (vma->vm_mm == NULL)
return NULL;
if (vma->vm_start == (long)vma->vm_mm->context.vdso)
if (vma->vm_mm && (vma->vm_start == (long)vma->vm_mm->context.vdso))
return "[vdso]";
else
return NULL;
if (vma->vm_mm && (vma->vm_start ==
(long)vma->vm_mm->context.vdso + PAGE_SIZE))
return "[vdso_data]";
return NULL;
}

4
arch/csky/kernel/vdso/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
vdso.lds
*.tmp
vdso-syms.S

View File

@ -0,0 +1,67 @@
# SPDX-License-Identifier: GPL-2.0-only
# Absolute relocation type $(ARCH_REL_TYPE_ABS) needs to be defined before
# the inclusion of generic Makefile.
ARCH_REL_TYPE_ABS := R_CKCORE_ADDR32|R_CKCORE_JUMP_SLOT
include $(srctree)/lib/vdso/Makefile
# Symbols present in the vdso
vdso-syms = rt_sigreturn
# Files to link into the vdso
obj-vdso = $(patsubst %, %.o, $(vdso-syms)) note.o
ccflags-y := -fno-stack-protector
# Build rules
targets := $(obj-vdso) vdso.so vdso.so.dbg vdso.lds vdso-dummy.o
obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
obj-y += vdso.o vdso-syms.o
CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
# Disable gcov profiling for VDSO code
GCOV_PROFILE := n
KCOV_INSTRUMENT := n
# Force dependency
$(obj)/vdso.o: $(obj)/vdso.so
SYSCFLAGS_vdso.so.dbg = $(c_flags)
$(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso) FORCE
$(call if_changed,vdsold)
SYSCFLAGS_vdso.so.dbg = -shared -s -Wl,-soname=linux-vdso.so.1 \
-Wl,--build-id=sha1 -Wl,--hash-style=both
$(obj)/vdso-syms.S: $(obj)/vdso.so FORCE
$(call if_changed,so2s)
# strip rule for the .so file
$(obj)/%.so: OBJCOPYFLAGS := -S
$(obj)/%.so: $(obj)/%.so.dbg FORCE
$(call if_changed,objcopy)
# actual build commands
# The DSO images are built using a special linker script
# Make sure only to export the intended __vdso_xxx symbol offsets.
quiet_cmd_vdsold = VDSOLD $@
cmd_vdsold = $(CC) $(KBUILD_CFLAGS) $(call cc-option, -no-pie) -nostdlib -nostartfiles $(SYSCFLAGS_$(@F)) \
-Wl,-T,$(filter-out FORCE,$^) -o $@.tmp && \
$(CROSS_COMPILE)objcopy \
$(patsubst %, -G __vdso_%, $(vdso-syms)) $@.tmp $@ && \
rm $@.tmp
# Extracts symbol offsets from the VDSO, converting them into an assembly file
# that contains the same symbols at the same offsets.
quiet_cmd_so2s = SO2S $@
cmd_so2s = $(NM) -D $< | $(srctree)/$(src)/so2s.sh > $@
# install commands for the unstripped file
quiet_cmd_vdso_install = INSTALL $@
cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
vdso.so: $(obj)/vdso.so.dbg
@mkdir -p $(MODLIB)/vdso
$(call cmd,vdso_install)
vdso_install: vdso.so

View File

@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* This supplies .note.* sections to go into the PT_NOTE inside the vDSO text.
* Here we can supply some information useful to userland.
*/
#include <linux/elfnote.h>
#include <linux/version.h>
ELFNOTE_START(Linux, 0, "a")
.long LINUX_VERSION_CODE
ELFNOTE_END

View File

@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <linux/linkage.h>
#include <asm/unistd.h>
#include <abi/vdso.h>
.text
ENTRY(__vdso_rt_sigreturn)
.cfi_startproc
.cfi_signal_frame
SET_SYSCALL_ID
trap 0
.cfi_endproc
ENDPROC(__vdso_rt_sigreturn)

5
arch/csky/kernel/vdso/so2s.sh Executable file
View File

@ -0,0 +1,5 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0+
sed 's!\([0-9a-f]*\) T \([a-z0-9_]*\)\(@@LINUX_5.10\)*!.global \2\n.set \2,0x\1!' \
| grep '^\.'

View File

@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <linux/init.h>
#include <linux/linkage.h>
#include <asm/page.h>
__PAGE_ALIGNED_DATA
.globl vdso_start, vdso_end
.balign PAGE_SIZE
vdso_start:
.incbin "arch/csky/kernel/vdso/vdso.so"
.balign PAGE_SIZE
vdso_end:
.previous

View File

@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#include <asm/page.h>
OUTPUT_ARCH(csky)
SECTIONS
{
PROVIDE(_vdso_data = . + PAGE_SIZE);
. = SIZEOF_HEADERS;
.hash : { *(.hash) } :text
.gnu.hash : { *(.gnu.hash) }
.dynsym : { *(.dynsym) }
.dynstr : { *(.dynstr) }
.gnu.version : { *(.gnu.version) }
.gnu.version_d : { *(.gnu.version_d) }
.gnu.version_r : { *(.gnu.version_r) }
.note : { *(.note.*) } :text :note
.dynamic : { *(.dynamic) } :text :dynamic
.eh_frame_hdr : { *(.eh_frame_hdr) } :text :eh_frame_hdr
.eh_frame : { KEEP (*(.eh_frame)) } :text
.rodata : { *(.rodata .rodata.* .gnu.linkonce.r.*) }
. = 0x800;
.text : { *(.text .text.*) } :text
.data : {
*(.got.plt) *(.got)
*(.data .data.* .gnu.linkonce.d.*)
*(.dynbss)
*(.bss .bss.* .gnu.linkonce.b.*)
}
}
PHDRS
{
text PT_LOAD FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
dynamic PT_DYNAMIC FLAGS(4); /* PF_R */
note PT_NOTE FLAGS(4); /* PF_R */
eh_frame_hdr PT_GNU_EH_FRAME;
}
VERSION
{
LINUX_5.10 {
global:
__vdso_rt_sigreturn;
local: *;
};
}