[ALPS05515589] DSO: VDSO COMPACT restore
This reverts commit 71c542a3019152826829b8c95c6ec3e286475ffb. MTK-Commit-Id: 7fd8f55044d2c0fc0a6a6016ec43b58ae63bc435 Change-Id: I86c14d07d3b02f1975a15807247916358c258e65 CR-Id: ALPS05515589 Feature: [Module]Kernel Maintenance Signed-off-by: Bo Ye <bo.ye@mediatek.com>
This commit is contained in:
@@ -100,6 +100,7 @@ config ARM64
|
||||
select GENERIC_STRNCPY_FROM_USER
|
||||
select GENERIC_STRNLEN_USER
|
||||
select GENERIC_TIME_VSYSCALL
|
||||
select GENERIC_GETTIMEOFDAY
|
||||
select HANDLE_DOMAIN_IRQ
|
||||
select HARDIRQS_SW_RESEND
|
||||
select HAVE_ACPI_APEI if (ACPI && EFI)
|
||||
@@ -138,6 +139,8 @@ config ARM64
|
||||
select HAVE_GENERIC_DMA_COHERENT
|
||||
select HAVE_HW_BREAKPOINT if PERF_EVENTS
|
||||
select HAVE_IRQ_TIME_ACCOUNTING
|
||||
select HAVE_KERNEL_GZIP
|
||||
select HAVE_KERNEL_LZ4
|
||||
select HAVE_MEMBLOCK
|
||||
select HAVE_MEMBLOCK_NODE_MAP if NUMA
|
||||
select HAVE_NMI
|
||||
@@ -152,6 +155,7 @@ config ARM64
|
||||
select HAVE_SYSCALL_TRACEPOINTS
|
||||
select HAVE_KPROBES
|
||||
select HAVE_KRETPROBES
|
||||
select HAVE_GENERIC_VDSO
|
||||
select IOMMU_DMA if IOMMU_SUPPORT
|
||||
select IRQ_DOMAIN
|
||||
select IRQ_FORCED_THREADING
|
||||
@@ -504,6 +508,22 @@ config ARM64_ERRATUM_1463225
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config ARM64_ERRATUM_1542419
|
||||
bool "Neoverse-N1: workaround mis-ordering of instruction fetches"
|
||||
default y
|
||||
help
|
||||
This option adds a workaround for ARM Neoverse-N1 erratum
|
||||
1542419.
|
||||
|
||||
Affected Neoverse-N1 cores could execute a stale instruction when
|
||||
modified by another CPU. The workaround depends on a firmware
|
||||
counterpart.
|
||||
|
||||
Workaround the issue by hiding the DIC feature from EL0. This
|
||||
forces user-space to perform cache maintenance.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config CAVIUM_ERRATUM_22375
|
||||
bool "Cavium erratum 22375, 24313"
|
||||
default y
|
||||
@@ -753,6 +773,18 @@ config HOTPLUG_CPU
|
||||
Say Y here to experiment with turning CPUs off and on. CPUs
|
||||
can be controlled through /sys/devices/system/cpu.
|
||||
|
||||
# The GPIO number here must be sorted by descending number. In case of
|
||||
# a multiplatform kernel, we just want the highest value required by the
|
||||
# selected platforms.
|
||||
config ARCH_NR_GPIO
|
||||
int "Number of GPIOs in the system"
|
||||
default 1280 if ARCH_QCOM
|
||||
default 256
|
||||
help
|
||||
Maximum number of GPIOs in the system.
|
||||
|
||||
If unsure, leave the default value.
|
||||
|
||||
# Common NUMA Features
|
||||
config NUMA
|
||||
bool "Numa Memory Allocation and Scheduler Support"
|
||||
@@ -831,6 +863,37 @@ config ARCH_HAS_CACHE_LINE_SIZE
|
||||
config CC_HAVE_SHADOW_CALL_STACK
|
||||
def_bool $(cc-option, -fsanitize=shadow-call-stack -ffixed-x18)
|
||||
|
||||
config ARM64_DMA_USE_IOMMU
|
||||
bool "ARM64 DMA iommu integration"
|
||||
select ARM_HAS_SG_CHAIN
|
||||
select NEED_SG_DMA_LENGTH
|
||||
help
|
||||
Enable using iommu through the standard dma apis.
|
||||
dma_alloc_coherent() will allocate scatter-gather memory
|
||||
which is made virtually contiguous via iommu.
|
||||
Enable if system contains IOMMU hardware.
|
||||
|
||||
if ARM64_DMA_USE_IOMMU
|
||||
|
||||
config ARM64_DMA_IOMMU_ALIGNMENT
|
||||
int "Maximum PAGE_SIZE order of alignment for DMA IOMMU buffers"
|
||||
range 4 9
|
||||
default 9
|
||||
help
|
||||
DMA mapping framework by default aligns all buffers to the smallest
|
||||
PAGE_SIZE order which is greater than or equal to the requested buffer
|
||||
size. This works well for buffers up to a few hundreds kilobytes, but
|
||||
for larger buffers it just a waste of address space. Drivers which has
|
||||
relatively small addressing window (like 64Mib) might run out of
|
||||
virtual space with just a few allocations.
|
||||
|
||||
With this parameter you can specify the maximum PAGE_SIZE order for
|
||||
DMA IOMMU buffers. Larger buffers will be aligned only to this
|
||||
specified order. The order is expressed as a power of two multiplied
|
||||
by the PAGE_SIZE.
|
||||
|
||||
endif
|
||||
|
||||
config SECCOMP
|
||||
bool "Enable seccomp to safely compute untrusted bytecode"
|
||||
---help---
|
||||
@@ -982,6 +1045,19 @@ config ARM64_TAGGED_ADDR_ABI
|
||||
to system calls as pointer arguments. For details, see
|
||||
Documentation/arm64/tagged-address-abi.rst.
|
||||
|
||||
config COMPAT_VDSO
|
||||
bool "Enable vDSO for 32-bit applications"
|
||||
depends on !CPU_BIG_ENDIAN && "$(CROSS_COMPILE_COMPAT)" != ""
|
||||
select GENERIC_COMPAT_VDSO
|
||||
default y
|
||||
help
|
||||
Place in the process address space of 32-bit applications an
|
||||
ELF shared object providing fast implementations of gettimeofday
|
||||
and clock_gettime.
|
||||
|
||||
You must have a 32-bit build of glibc 2.22 or later for programs
|
||||
to seamlessly take advantage of this.
|
||||
|
||||
menuconfig ARMV8_DEPRECATED
|
||||
bool "Emulate deprecated/obsolete ARMv8 instructions"
|
||||
depends on COMPAT
|
||||
@@ -1359,6 +1435,34 @@ config COMPAT
|
||||
|
||||
If you want to execute 32-bit userspace applications, say Y.
|
||||
|
||||
config KUSER_HELPERS
|
||||
bool "Enable kuser helpers page for 32 bit applications."
|
||||
depends on COMPAT
|
||||
default y
|
||||
help
|
||||
Warning: disabling this option may break 32-bit user programs.
|
||||
|
||||
Provide kuser helpers to compat tasks. The kernel provides
|
||||
helper code to userspace in read only form at a fixed location
|
||||
to allow userspace to be independent of the CPU type fitted to
|
||||
the system. This permits binaries to be run on ARMv4 through
|
||||
to ARMv8 without modification.
|
||||
|
||||
See Documentation/arm/kernel_user_helpers.txt for details.
|
||||
|
||||
However, the fixed address nature of these helpers can be used
|
||||
by ROP (return orientated programming) authors when creating
|
||||
exploits.
|
||||
|
||||
If all of the binaries and libraries which run on your platform
|
||||
are built specifically for your platform, and make no use of
|
||||
these helpers, then you can turn this option off to hinder
|
||||
such exploits. However, in that case, if a binary or library
|
||||
relying on those helpers is run, it will not function correctly.
|
||||
|
||||
Say N here only if you are absolutely certain that you do not
|
||||
need these helpers; otherwise, the safe option is to say Y.
|
||||
|
||||
config SYSVIPC_COMPAT
|
||||
def_bool y
|
||||
depends on COMPAT && SYSVIPC
|
||||
|
||||
@@ -49,10 +49,10 @@ $(warning Detected assembler with broken .inst; disassembly will be unreliable)
|
||||
endif
|
||||
endif
|
||||
|
||||
KBUILD_CFLAGS += -mgeneral-regs-only $(lseinstr) $(brokengasinst)
|
||||
KBUILD_CFLAGS += -mgeneral-regs-only $(lseinstr) $(brokengasinst) $(compat_vdso)
|
||||
KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
|
||||
KBUILD_CFLAGS += $(call cc-disable-warning, psabi)
|
||||
KBUILD_AFLAGS += $(lseinstr) $(brokengasinst)
|
||||
KBUILD_AFLAGS += $(lseinstr) $(brokengasinst) $(compat_vdso)
|
||||
|
||||
KBUILD_CFLAGS += $(call cc-option,-mabi=lp64)
|
||||
KBUILD_AFLAGS += $(call cc-option,-mabi=lp64)
|
||||
@@ -174,6 +174,9 @@ ifeq ($(KBUILD_EXTMOD),)
|
||||
prepare: vdso_prepare
|
||||
vdso_prepare: prepare0
|
||||
$(Q)$(MAKE) $(build)=arch/arm64/kernel/vdso include/generated/vdso-offsets.h
|
||||
$(if $(CONFIG_COMPAT_VDSO),$(Q)$(MAKE) \
|
||||
$(build)=arch/arm64/kernel/vdso32 \
|
||||
include/generated/vdso32-offsets.h)
|
||||
endif
|
||||
|
||||
define archhelp
|
||||
|
||||
@@ -209,11 +209,25 @@ typedef compat_elf_greg_t compat_elf_gregset_t[COMPAT_ELF_NGREG];
|
||||
({ \
|
||||
set_thread_flag(TIF_32BIT); \
|
||||
})
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
#define COMPAT_ARCH_DLINFO \
|
||||
do { \
|
||||
/* \
|
||||
* Note that we use Elf64_Off instead of elf_addr_t because \
|
||||
* elf_addr_t in compat is defined as Elf32_Addr and casting \
|
||||
* current->mm->context.vdso to it triggers a cast warning of \
|
||||
* cast from pointer to integer of different size. \
|
||||
*/ \
|
||||
NEW_AUX_ENT(AT_SYSINFO_EHDR, \
|
||||
(Elf64_Off)current->mm->context.vdso); \
|
||||
} while (0)
|
||||
#else
|
||||
#define COMPAT_ARCH_DLINFO
|
||||
extern int aarch32_setup_vectors_page(struct linux_binprm *bprm,
|
||||
int uses_interp);
|
||||
#endif
|
||||
extern int aarch32_setup_additional_pages(struct linux_binprm *bprm,
|
||||
int uses_interp);
|
||||
#define compat_arch_setup_additional_pages \
|
||||
aarch32_setup_vectors_page
|
||||
aarch32_setup_additional_pages
|
||||
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
|
||||
@@ -20,7 +20,51 @@
|
||||
#ifdef CONFIG_COMPAT
|
||||
#include <linux/compat.h>
|
||||
|
||||
#define AARCH32_KERN_SIGRET_CODE_OFFSET 0x500
|
||||
struct compat_sigcontext {
|
||||
/* We always set these two fields to 0 */
|
||||
compat_ulong_t trap_no;
|
||||
compat_ulong_t error_code;
|
||||
|
||||
compat_ulong_t oldmask;
|
||||
compat_ulong_t arm_r0;
|
||||
compat_ulong_t arm_r1;
|
||||
compat_ulong_t arm_r2;
|
||||
compat_ulong_t arm_r3;
|
||||
compat_ulong_t arm_r4;
|
||||
compat_ulong_t arm_r5;
|
||||
compat_ulong_t arm_r6;
|
||||
compat_ulong_t arm_r7;
|
||||
compat_ulong_t arm_r8;
|
||||
compat_ulong_t arm_r9;
|
||||
compat_ulong_t arm_r10;
|
||||
compat_ulong_t arm_fp;
|
||||
compat_ulong_t arm_ip;
|
||||
compat_ulong_t arm_sp;
|
||||
compat_ulong_t arm_lr;
|
||||
compat_ulong_t arm_pc;
|
||||
compat_ulong_t arm_cpsr;
|
||||
compat_ulong_t fault_address;
|
||||
};
|
||||
|
||||
struct compat_ucontext {
|
||||
compat_ulong_t uc_flags;
|
||||
compat_uptr_t uc_link;
|
||||
compat_stack_t uc_stack;
|
||||
struct compat_sigcontext uc_mcontext;
|
||||
compat_sigset_t uc_sigmask;
|
||||
int __unused[32 - (sizeof(compat_sigset_t) / sizeof(int))];
|
||||
compat_ulong_t uc_regspace[128] __attribute__((__aligned__(8)));
|
||||
};
|
||||
|
||||
struct compat_sigframe {
|
||||
struct compat_ucontext uc;
|
||||
compat_ulong_t retcode[2];
|
||||
};
|
||||
|
||||
struct compat_rt_sigframe {
|
||||
struct compat_siginfo info;
|
||||
struct compat_sigframe sig;
|
||||
};
|
||||
|
||||
int compat_setup_frame(int usig, struct ksignal *ksig, sigset_t *set,
|
||||
struct pt_regs *regs);
|
||||
|
||||
@@ -28,6 +28,9 @@
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
#include <generated/vdso-offsets.h>
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
#include <generated/vdso32-offsets.h>
|
||||
#endif
|
||||
|
||||
#define VDSO_SYMBOL(base, name) \
|
||||
({ \
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016 MediaTek Inc.
|
||||
*/
|
||||
#ifndef __ASM_VDSO_DATAPAGE_H
|
||||
#define __ASM_VDSO_DATAPAGE_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
|
||||
struct vdso_data {
|
||||
__u64 cs_cycle_last; /* Timebase at clocksource init */
|
||||
__u64 raw_time_sec; /* Raw time */
|
||||
__u64 raw_time_nsec;
|
||||
__u64 xtime_clock_sec; /* Kernel time */
|
||||
__u64 xtime_clock_nsec;
|
||||
__u64 xtime_coarse_sec; /* Coarse time */
|
||||
__u64 xtime_coarse_nsec;
|
||||
__u64 wtm_clock_sec; /* Wall to monotonic time */
|
||||
__u64 wtm_clock_nsec;
|
||||
__u32 tb_seq_count; /* Timebase sequence counter */
|
||||
/* cs_* members must be adjacent and in this order (ldp accesses) */
|
||||
__u32 cs_mono_mult; /* NTP-adjusted clocksource multiplier */
|
||||
__u32 cs_shift; /* Clocksource shift (mono = raw) */
|
||||
__u32 cs_raw_mult; /* Raw clocksource multiplier */
|
||||
__u32 tz_minuteswest; /* Whacky timezone stuff */
|
||||
__u32 tz_dsttime;
|
||||
__u32 use_syscall;
|
||||
__u32 hrtimer_res;
|
||||
};
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
|
||||
#endif /* __KERNEL__ */
|
||||
|
||||
#endif /* __ASM_VDSO_DATAPAGE_H */
|
||||
@@ -27,8 +27,12 @@ OBJCOPYFLAGS := --prefix-symbols=__efistub_
|
||||
$(obj)/%.stub.o: $(obj)/%.o FORCE
|
||||
$(call if_changed,objcopy)
|
||||
|
||||
arm64-obj-$(CONFIG_COMPAT) += sys32.o kuser32.o signal32.o \
|
||||
arm64-obj-$(CONFIG_COMPAT) += sys32.o signal32.o \
|
||||
sys_compat.o
|
||||
ifneq ($(CONFIG_COMPAT_VDSO), y)
|
||||
arm64-obj-$(CONFIG_COMPAT) += sigreturn32.o
|
||||
endif
|
||||
arm64-obj-$(CONFIG_KUSER_HELPERS) += kuser32.o
|
||||
arm64-obj-$(CONFIG_FUNCTION_TRACER) += ftrace.o entry-ftrace.o
|
||||
arm64-obj-$(CONFIG_MODULES) += arm64ksyms.o module.o
|
||||
arm64-obj-$(CONFIG_ARM64_MODULE_PLTS) += module-plts.o
|
||||
@@ -61,6 +65,7 @@ arm64-obj-$(CONFIG_SHADOW_CALL_STACK) += scs.o
|
||||
|
||||
obj-y += $(arm64-obj-y) vdso/ probes/
|
||||
obj-m += $(arm64-obj-m)
|
||||
obj-$(CONFIG_COMPAT_VDSO) += vdso32/
|
||||
head-y := head.o
|
||||
extra-y += $(head-y) vmlinux.lds
|
||||
|
||||
|
||||
@@ -25,13 +25,14 @@
|
||||
#include <linux/kvm_host.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <linux/suspend.h>
|
||||
#include <vdso/datapage.h>
|
||||
#include <asm/cpufeature.h>
|
||||
#include <asm/fixmap.h>
|
||||
#include <asm/thread_info.h>
|
||||
#include <asm/memory.h>
|
||||
#include <asm/signal32.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/suspend.h>
|
||||
#include <asm/vdso_datapage.h>
|
||||
#include <linux/kbuild.h>
|
||||
#include <linux/arm-smccc.h>
|
||||
|
||||
@@ -84,6 +85,11 @@ int main(void)
|
||||
DEFINE(S_STACKFRAME, offsetof(struct pt_regs, stackframe));
|
||||
DEFINE(S_FRAME_SIZE, sizeof(struct pt_regs));
|
||||
BLANK();
|
||||
#ifdef CONFIG_COMPAT
|
||||
DEFINE(COMPAT_SIGFRAME_REGS_OFFSET, offsetof(struct compat_sigframe, uc.uc_mcontext.arm_r0));
|
||||
DEFINE(COMPAT_RT_SIGFRAME_REGS_OFFSET, offsetof(struct compat_rt_sigframe, sig.uc.uc_mcontext.arm_r0));
|
||||
BLANK();
|
||||
#endif
|
||||
DEFINE(MM_CONTEXT_ID, offsetof(struct mm_struct, context.id.counter));
|
||||
BLANK();
|
||||
DEFINE(VMA_VM_MM, offsetof(struct vm_area_struct, vm_mm));
|
||||
@@ -108,22 +114,28 @@ int main(void)
|
||||
DEFINE(CLOCK_COARSE_RES, LOW_RES_NSEC);
|
||||
DEFINE(NSEC_PER_SEC, NSEC_PER_SEC);
|
||||
BLANK();
|
||||
DEFINE(VDSO_CS_CYCLE_LAST, offsetof(struct vdso_data, cs_cycle_last));
|
||||
DEFINE(VDSO_RAW_TIME_SEC, offsetof(struct vdso_data, raw_time_sec));
|
||||
DEFINE(VDSO_RAW_TIME_NSEC, offsetof(struct vdso_data, raw_time_nsec));
|
||||
DEFINE(VDSO_XTIME_CLK_SEC, offsetof(struct vdso_data, xtime_clock_sec));
|
||||
DEFINE(VDSO_XTIME_CLK_NSEC, offsetof(struct vdso_data, xtime_clock_nsec));
|
||||
DEFINE(VDSO_XTIME_CRS_SEC, offsetof(struct vdso_data, xtime_coarse_sec));
|
||||
DEFINE(VDSO_XTIME_CRS_NSEC, offsetof(struct vdso_data, xtime_coarse_nsec));
|
||||
DEFINE(VDSO_WTM_CLK_SEC, offsetof(struct vdso_data, wtm_clock_sec));
|
||||
DEFINE(VDSO_WTM_CLK_NSEC, offsetof(struct vdso_data, wtm_clock_nsec));
|
||||
DEFINE(VDSO_TB_SEQ_COUNT, offsetof(struct vdso_data, tb_seq_count));
|
||||
DEFINE(VDSO_CS_MONO_MULT, offsetof(struct vdso_data, cs_mono_mult));
|
||||
DEFINE(VDSO_CS_RAW_MULT, offsetof(struct vdso_data, cs_raw_mult));
|
||||
DEFINE(VDSO_CS_SHIFT, offsetof(struct vdso_data, cs_shift));
|
||||
DEFINE(VDSO_SEQ, offsetof(struct vdso_data, seq));
|
||||
DEFINE(VDSO_CLK_MODE, offsetof(struct vdso_data, clock_mode));
|
||||
DEFINE(VDSO_CYCLE_LAST, offsetof(struct vdso_data, cycle_last));
|
||||
DEFINE(VDSO_MASK, offsetof(struct vdso_data, mask));
|
||||
DEFINE(VDSO_MULT, offsetof(struct vdso_data, mult));
|
||||
DEFINE(VDSO_SHIFT, offsetof(struct vdso_data, shift));
|
||||
DEFINE(VDSO_REALTIME_SEC, offsetof(struct vdso_data, basetime[CLOCK_REALTIME].sec));
|
||||
DEFINE(VDSO_REALTIME_NSEC, offsetof(struct vdso_data, basetime[CLOCK_REALTIME].nsec));
|
||||
DEFINE(VDSO_MONO_SEC, offsetof(struct vdso_data, basetime[CLOCK_MONOTONIC].sec));
|
||||
DEFINE(VDSO_MONO_NSEC, offsetof(struct vdso_data, basetime[CLOCK_MONOTONIC].nsec));
|
||||
DEFINE(VDSO_MONO_RAW_SEC, offsetof(struct vdso_data, basetime[CLOCK_MONOTONIC_RAW].sec));
|
||||
DEFINE(VDSO_MONO_RAW_NSEC, offsetof(struct vdso_data, basetime[CLOCK_MONOTONIC_RAW].nsec));
|
||||
DEFINE(VDSO_BOOTTIME_SEC, offsetof(struct vdso_data, basetime[CLOCK_BOOTTIME].sec));
|
||||
DEFINE(VDSO_BOOTTIME_NSEC, offsetof(struct vdso_data, basetime[CLOCK_BOOTTIME].nsec));
|
||||
DEFINE(VDSO_TAI_SEC, offsetof(struct vdso_data, basetime[CLOCK_TAI].sec));
|
||||
DEFINE(VDSO_TAI_NSEC, offsetof(struct vdso_data, basetime[CLOCK_TAI].nsec));
|
||||
DEFINE(VDSO_RT_COARSE_SEC, offsetof(struct vdso_data, basetime[CLOCK_REALTIME_COARSE].sec));
|
||||
DEFINE(VDSO_RT_COARSE_NSEC, offsetof(struct vdso_data, basetime[CLOCK_REALTIME_COARSE].nsec));
|
||||
DEFINE(VDSO_MONO_COARSE_SEC, offsetof(struct vdso_data, basetime[CLOCK_MONOTONIC_COARSE].sec));
|
||||
DEFINE(VDSO_MONO_COARSE_NSEC, offsetof(struct vdso_data, basetime[CLOCK_MONOTONIC_COARSE].nsec));
|
||||
DEFINE(VDSO_TZ_MINWEST, offsetof(struct vdso_data, tz_minuteswest));
|
||||
DEFINE(VDSO_TZ_DSTTIME, offsetof(struct vdso_data, tz_dsttime));
|
||||
DEFINE(VDSO_USE_SYSCALL, offsetof(struct vdso_data, use_syscall));
|
||||
BLANK();
|
||||
DEFINE(TVAL_TV_SEC, offsetof(struct timeval, tv_sec));
|
||||
DEFINE(TVAL_TV_USEC, offsetof(struct timeval, tv_usec));
|
||||
|
||||
@@ -1,29 +1,14 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* Low-level user helpers placed in the vectors page for AArch32.
|
||||
* AArch32 user helpers.
|
||||
* Based on the kuser helpers in arch/arm/kernel/entry-armv.S.
|
||||
*
|
||||
* Copyright (C) 2005-2011 Nicolas Pitre <nico@fluxnic.net>
|
||||
* Copyright (C) 2012 ARM Ltd.
|
||||
* Copyright (C) 2012-2018 ARM Ltd.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* AArch32 user helpers.
|
||||
*
|
||||
* Each segment is 32-byte aligned and will be moved to the top of the high
|
||||
* vector page. New segments (if ever needed) must be added in front of
|
||||
* existing ones. This mechanism should be used only for things that are
|
||||
* really small and justified, and not be abused freely.
|
||||
* The kuser helpers below are mapped at a fixed address by
|
||||
* aarch32_setup_additional_pages() and are provided for compatibility
|
||||
* reasons with 32 bit (aarch32) applications that need them.
|
||||
*
|
||||
* See Documentation/arm/kernel_user_helpers.txt for formal definitions.
|
||||
*/
|
||||
@@ -77,42 +62,3 @@ __kuser_helper_version: // 0xffff0ffc
|
||||
.word ((__kuser_helper_end - __kuser_helper_start) >> 5)
|
||||
.globl __kuser_helper_end
|
||||
__kuser_helper_end:
|
||||
|
||||
/*
|
||||
* AArch32 sigreturn code
|
||||
*
|
||||
* For ARM syscalls, the syscall number has to be loaded into r7.
|
||||
* We do not support an OABI userspace.
|
||||
*
|
||||
* For Thumb syscalls, we also pass the syscall number via r7. We therefore
|
||||
* need two 16-bit instructions.
|
||||
*/
|
||||
.globl __aarch32_sigret_code_start
|
||||
__aarch32_sigret_code_start:
|
||||
|
||||
/*
|
||||
* ARM Code
|
||||
*/
|
||||
.byte __NR_compat_sigreturn, 0x70, 0xa0, 0xe3 // mov r7, #__NR_compat_sigreturn
|
||||
.byte __NR_compat_sigreturn, 0x00, 0x00, 0xef // svc #__NR_compat_sigreturn
|
||||
|
||||
/*
|
||||
* Thumb code
|
||||
*/
|
||||
.byte __NR_compat_sigreturn, 0x27 // svc #__NR_compat_sigreturn
|
||||
.byte __NR_compat_sigreturn, 0xdf // mov r7, #__NR_compat_sigreturn
|
||||
|
||||
/*
|
||||
* ARM code
|
||||
*/
|
||||
.byte __NR_compat_rt_sigreturn, 0x70, 0xa0, 0xe3 // mov r7, #__NR_compat_rt_sigreturn
|
||||
.byte __NR_compat_rt_sigreturn, 0x00, 0x00, 0xef // svc #__NR_compat_rt_sigreturn
|
||||
|
||||
/*
|
||||
* Thumb code
|
||||
*/
|
||||
.byte __NR_compat_rt_sigreturn, 0x27 // svc #__NR_compat_rt_sigreturn
|
||||
.byte __NR_compat_rt_sigreturn, 0xdf // mov r7, #__NR_compat_rt_sigreturn
|
||||
|
||||
.globl __aarch32_sigret_code_end
|
||||
__aarch32_sigret_code_end:
|
||||
|
||||
@@ -29,42 +29,7 @@
|
||||
#include <asm/traps.h>
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
struct compat_sigcontext {
|
||||
/* We always set these two fields to 0 */
|
||||
compat_ulong_t trap_no;
|
||||
compat_ulong_t error_code;
|
||||
|
||||
compat_ulong_t oldmask;
|
||||
compat_ulong_t arm_r0;
|
||||
compat_ulong_t arm_r1;
|
||||
compat_ulong_t arm_r2;
|
||||
compat_ulong_t arm_r3;
|
||||
compat_ulong_t arm_r4;
|
||||
compat_ulong_t arm_r5;
|
||||
compat_ulong_t arm_r6;
|
||||
compat_ulong_t arm_r7;
|
||||
compat_ulong_t arm_r8;
|
||||
compat_ulong_t arm_r9;
|
||||
compat_ulong_t arm_r10;
|
||||
compat_ulong_t arm_fp;
|
||||
compat_ulong_t arm_ip;
|
||||
compat_ulong_t arm_sp;
|
||||
compat_ulong_t arm_lr;
|
||||
compat_ulong_t arm_pc;
|
||||
compat_ulong_t arm_cpsr;
|
||||
compat_ulong_t fault_address;
|
||||
};
|
||||
|
||||
struct compat_ucontext {
|
||||
compat_ulong_t uc_flags;
|
||||
compat_uptr_t uc_link;
|
||||
compat_stack_t uc_stack;
|
||||
struct compat_sigcontext uc_mcontext;
|
||||
compat_sigset_t uc_sigmask;
|
||||
int __unused[32 - (sizeof (compat_sigset_t) / sizeof (int))];
|
||||
compat_ulong_t uc_regspace[128] __attribute__((__aligned__(8)));
|
||||
};
|
||||
#include <asm/vdso.h>
|
||||
|
||||
struct compat_vfp_sigframe {
|
||||
compat_ulong_t magic;
|
||||
@@ -92,16 +57,6 @@ struct compat_aux_sigframe {
|
||||
unsigned long end_magic;
|
||||
} __attribute__((__aligned__(8)));
|
||||
|
||||
struct compat_sigframe {
|
||||
struct compat_ucontext uc;
|
||||
compat_ulong_t retcode[2];
|
||||
};
|
||||
|
||||
struct compat_rt_sigframe {
|
||||
struct compat_siginfo info;
|
||||
struct compat_sigframe sig;
|
||||
};
|
||||
|
||||
#define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP)))
|
||||
|
||||
static inline int put_sigset_t(compat_sigset_t __user *uset, sigset_t *set)
|
||||
@@ -398,14 +353,38 @@ static void compat_setup_return(struct pt_regs *regs, struct k_sigaction *ka,
|
||||
retcode = ptr_to_compat(ka->sa.sa_restorer);
|
||||
} else {
|
||||
/* Set up sigreturn pointer */
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
void *vdso_base = current->mm->context.vdso;
|
||||
void *vdso_trampoline;
|
||||
|
||||
if (ka->sa.sa_flags & SA_SIGINFO) {
|
||||
if (thumb) {
|
||||
vdso_trampoline = VDSO_SYMBOL(vdso_base,
|
||||
compat_rt_sigreturn_thumb);
|
||||
} else {
|
||||
vdso_trampoline = VDSO_SYMBOL(vdso_base,
|
||||
compat_rt_sigreturn_arm);
|
||||
}
|
||||
} else {
|
||||
if (thumb) {
|
||||
vdso_trampoline = VDSO_SYMBOL(vdso_base,
|
||||
compat_sigreturn_thumb);
|
||||
} else {
|
||||
vdso_trampoline = VDSO_SYMBOL(vdso_base,
|
||||
compat_sigreturn_arm);
|
||||
}
|
||||
}
|
||||
|
||||
retcode = ptr_to_compat(vdso_trampoline) + thumb;
|
||||
#else
|
||||
unsigned int idx = thumb << 1;
|
||||
|
||||
if (ka->sa.sa_flags & SA_SIGINFO)
|
||||
idx += 3;
|
||||
|
||||
retcode = AARCH32_VECTORS_BASE +
|
||||
AARCH32_KERN_SIGRET_CODE_OFFSET +
|
||||
retcode = (unsigned long)current->mm->context.vdso +
|
||||
(idx << 2) + thumb;
|
||||
#endif
|
||||
}
|
||||
|
||||
regs->regs[0] = usig;
|
||||
|
||||
46
arch/arm64/kernel/sigreturn32.S
Normal file
46
arch/arm64/kernel/sigreturn32.S
Normal file
@@ -0,0 +1,46 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
/*
|
||||
* AArch32 sigreturn code.
|
||||
* Based on the kuser helpers in arch/arm/kernel/entry-armv.S.
|
||||
*
|
||||
* Copyright (C) 2005-2011 Nicolas Pitre <nico@fluxnic.net>
|
||||
* Copyright (C) 2012-2018 ARM Ltd.
|
||||
*
|
||||
* For ARM syscalls, the syscall number has to be loaded into r7.
|
||||
* We do not support an OABI userspace.
|
||||
*
|
||||
* For Thumb syscalls, we also pass the syscall number via r7. We therefore
|
||||
* need two 16-bit instructions.
|
||||
*/
|
||||
|
||||
#include <asm/unistd.h>
|
||||
|
||||
.globl __aarch32_sigret_code_start
|
||||
__aarch32_sigret_code_start:
|
||||
|
||||
/*
|
||||
* ARM Code
|
||||
*/
|
||||
.byte __NR_compat_sigreturn, 0x70, 0xa0, 0xe3 // mov r7, #__NR_compat_sigreturn
|
||||
.byte __NR_compat_sigreturn, 0x00, 0x00, 0xef // svc #__NR_compat_sigreturn
|
||||
|
||||
/*
|
||||
* Thumb code
|
||||
*/
|
||||
.byte __NR_compat_sigreturn, 0x27 // svc #__NR_compat_sigreturn
|
||||
.byte __NR_compat_sigreturn, 0xdf // mov r7, #__NR_compat_sigreturn
|
||||
|
||||
/*
|
||||
* ARM code
|
||||
*/
|
||||
.byte __NR_compat_rt_sigreturn, 0x70, 0xa0, 0xe3 // mov r7, #__NR_compat_rt_sigreturn
|
||||
.byte __NR_compat_rt_sigreturn, 0x00, 0x00, 0xef // svc #__NR_compat_rt_sigreturn
|
||||
|
||||
/*
|
||||
* Thumb code
|
||||
*/
|
||||
.byte __NR_compat_rt_sigreturn, 0x27 // svc #__NR_compat_rt_sigreturn
|
||||
.byte __NR_compat_rt_sigreturn, 0xdf // mov r7, #__NR_compat_rt_sigreturn
|
||||
|
||||
.globl __aarch32_sigret_code_end
|
||||
__aarch32_sigret_code_end:
|
||||
@@ -19,6 +19,7 @@
|
||||
*/
|
||||
|
||||
#include <linux/compat.h>
|
||||
#include <linux/cpufeature.h>
|
||||
#include <linux/personality.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/sched/signal.h>
|
||||
@@ -28,6 +29,7 @@
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/system_misc.h>
|
||||
#include <asm/tlbflush.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
static long
|
||||
@@ -41,6 +43,15 @@ __do_compat_cache_op(unsigned long start, unsigned long end)
|
||||
if (fatal_signal_pending(current))
|
||||
return 0;
|
||||
|
||||
if (cpus_have_const_cap(ARM64_WORKAROUND_1542419)) {
|
||||
/*
|
||||
* The workaround requires an inner-shareable tlbi.
|
||||
* We pick the reserved-ASID to minimise the impact.
|
||||
*/
|
||||
__tlbi(aside1is, __TLBI_VADDR(0, 0));
|
||||
dsb(ish);
|
||||
}
|
||||
|
||||
ret = __flush_cache_user_range(start, start + chunk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
@@ -1,6 +1,21 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Copyright (c) 2016 MediaTek Inc.
|
||||
* VDSO implementations.
|
||||
*
|
||||
* Copyright (C) 2012 ARM Limited
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Will Deacon <will.deacon@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/cache.h>
|
||||
@@ -16,90 +31,74 @@
|
||||
#include <linux/slab.h>
|
||||
#include <linux/timekeeper_internal.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <vdso/datapage.h>
|
||||
#include <vdso/helpers.h>
|
||||
#include <vdso/vsyscall.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/signal32.h>
|
||||
#include <asm/vdso.h>
|
||||
#include <asm/vdso_datapage.h>
|
||||
|
||||
extern char vdso_start[], vdso_end[];
|
||||
static unsigned long vdso_pages __ro_after_init;
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
extern char vdso32_start[], vdso32_end[];
|
||||
#endif /* CONFIG_COMPAT_VDSO */
|
||||
|
||||
/* vdso_lookup arch_index */
|
||||
enum arch_vdso_type {
|
||||
ARM64_VDSO = 0,
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
ARM64_VDSO32 = 1,
|
||||
#endif /* CONFIG_COMPAT_VDSO */
|
||||
};
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
#define VDSO_TYPES (ARM64_VDSO32 + 1)
|
||||
#else
|
||||
#define VDSO_TYPES (ARM64_VDSO + 1)
|
||||
#endif /* CONFIG_COMPAT_VDSO */
|
||||
|
||||
struct __vdso_abi {
|
||||
const char *name;
|
||||
const char *vdso_code_start;
|
||||
const char *vdso_code_end;
|
||||
unsigned long vdso_pages;
|
||||
/* Data Mapping */
|
||||
struct vm_special_mapping *dm;
|
||||
/* Code Mapping */
|
||||
struct vm_special_mapping *cm;
|
||||
};
|
||||
|
||||
static struct __vdso_abi vdso_lookup[VDSO_TYPES] __ro_after_init = {
|
||||
{
|
||||
.name = "vdso",
|
||||
.vdso_code_start = vdso_start,
|
||||
.vdso_code_end = vdso_end,
|
||||
},
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
{
|
||||
.name = "vdso32",
|
||||
.vdso_code_start = vdso32_start,
|
||||
.vdso_code_end = vdso32_end,
|
||||
},
|
||||
#endif /* CONFIG_COMPAT_VDSO */
|
||||
};
|
||||
|
||||
/*
|
||||
* The vDSO data page.
|
||||
*/
|
||||
static union {
|
||||
struct vdso_data data;
|
||||
struct vdso_data data[CS_BASES];
|
||||
u8 page[PAGE_SIZE];
|
||||
} vdso_data_store __page_aligned_data;
|
||||
struct vdso_data *vdso_data = &vdso_data_store.data;
|
||||
struct vdso_data *vdso_data = vdso_data_store.data;
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
/*
|
||||
* Create and map the vectors page for AArch32 tasks.
|
||||
*/
|
||||
static struct page *vectors_page[1] __ro_after_init;
|
||||
|
||||
static int __init alloc_vectors_page(void)
|
||||
{
|
||||
extern char __kuser_helper_start[], __kuser_helper_end[];
|
||||
extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[];
|
||||
|
||||
int kuser_sz = __kuser_helper_end - __kuser_helper_start;
|
||||
int sigret_sz = __aarch32_sigret_code_end - __aarch32_sigret_code_start;
|
||||
unsigned long vpage;
|
||||
|
||||
vpage = get_zeroed_page(GFP_ATOMIC);
|
||||
|
||||
if (!vpage)
|
||||
return -ENOMEM;
|
||||
|
||||
/* kuser helpers */
|
||||
memcpy((void *)vpage + 0x1000 - kuser_sz, __kuser_helper_start,
|
||||
kuser_sz);
|
||||
|
||||
/* sigreturn code */
|
||||
memcpy((void *)vpage + AARCH32_KERN_SIGRET_CODE_OFFSET,
|
||||
__aarch32_sigret_code_start, sigret_sz);
|
||||
|
||||
flush_icache_range(vpage, vpage + PAGE_SIZE);
|
||||
vectors_page[0] = virt_to_page(vpage);
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(alloc_vectors_page);
|
||||
|
||||
int aarch32_setup_vectors_page(struct linux_binprm *bprm, int uses_interp)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
unsigned long addr = AARCH32_VECTORS_BASE;
|
||||
static const struct vm_special_mapping spec = {
|
||||
.name = "[vectors]",
|
||||
.pages = vectors_page,
|
||||
|
||||
};
|
||||
void *ret;
|
||||
|
||||
if (down_write_killable(&mm->mmap_sem))
|
||||
return -EINTR;
|
||||
current->mm->context.vdso = (void *)addr;
|
||||
|
||||
/* Map vectors page at the high address. */
|
||||
ret = _install_special_mapping(mm, addr, PAGE_SIZE,
|
||||
VM_READ|VM_EXEC|VM_MAYREAD|VM_MAYEXEC,
|
||||
&spec);
|
||||
|
||||
up_write(&mm->mmap_sem);
|
||||
|
||||
return PTR_ERR_OR_ZERO(ret);
|
||||
}
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
static int vdso_mremap(const struct vm_special_mapping *sm,
|
||||
struct vm_area_struct *new_vma)
|
||||
static int __vdso_remap(enum arch_vdso_type arch_index,
|
||||
const struct vm_special_mapping *sm,
|
||||
struct vm_area_struct *new_vma)
|
||||
{
|
||||
unsigned long new_size = new_vma->vm_end - new_vma->vm_start;
|
||||
unsigned long vdso_size = vdso_end - vdso_start;
|
||||
unsigned long vdso_size = vdso_lookup[arch_index].vdso_code_end -
|
||||
vdso_lookup[arch_index].vdso_code_start;
|
||||
|
||||
if (vdso_size != new_size)
|
||||
return -EINVAL;
|
||||
@@ -109,7 +108,303 @@ static int vdso_mremap(const struct vm_special_mapping *sm,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct vm_special_mapping vdso_spec[2] __ro_after_init = {
|
||||
static int __vdso_init(enum arch_vdso_type arch_index)
|
||||
{
|
||||
int i;
|
||||
struct page **vdso_pagelist;
|
||||
unsigned long pfn;
|
||||
|
||||
if (memcmp(vdso_lookup[arch_index].vdso_code_start, "\177ELF", 4)) {
|
||||
pr_err("vDSO is not a valid ELF object!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vdso_lookup[arch_index].vdso_pages = (
|
||||
vdso_lookup[arch_index].vdso_code_end -
|
||||
vdso_lookup[arch_index].vdso_code_start) >>
|
||||
PAGE_SHIFT;
|
||||
|
||||
/* Allocate the vDSO pagelist, plus a page for the data. */
|
||||
vdso_pagelist = kcalloc(vdso_lookup[arch_index].vdso_pages + 1,
|
||||
sizeof(struct page *),
|
||||
GFP_KERNEL);
|
||||
if (vdso_pagelist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Grab the vDSO data page. */
|
||||
vdso_pagelist[0] = phys_to_page(__pa_symbol(vdso_data));
|
||||
|
||||
|
||||
/* Grab the vDSO code pages. */
|
||||
pfn = sym_to_pfn(vdso_lookup[arch_index].vdso_code_start);
|
||||
|
||||
for (i = 0; i < vdso_lookup[arch_index].vdso_pages; i++)
|
||||
vdso_pagelist[i + 1] = pfn_to_page(pfn + i);
|
||||
|
||||
vdso_lookup[arch_index].dm->pages = &vdso_pagelist[0];
|
||||
vdso_lookup[arch_index].cm->pages = &vdso_pagelist[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __setup_additional_pages(enum arch_vdso_type arch_index,
|
||||
struct mm_struct *mm,
|
||||
struct linux_binprm *bprm,
|
||||
int uses_interp)
|
||||
{
|
||||
unsigned long vdso_base, vdso_text_len, vdso_mapping_len;
|
||||
void *ret;
|
||||
|
||||
vdso_text_len = vdso_lookup[arch_index].vdso_pages << PAGE_SHIFT;
|
||||
/* Be sure to map the data page */
|
||||
vdso_mapping_len = vdso_text_len + PAGE_SIZE;
|
||||
|
||||
vdso_base = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0);
|
||||
if (IS_ERR_VALUE(vdso_base)) {
|
||||
ret = ERR_PTR(vdso_base);
|
||||
goto up_fail;
|
||||
}
|
||||
|
||||
ret = _install_special_mapping(mm, vdso_base, PAGE_SIZE,
|
||||
VM_READ|VM_MAYREAD,
|
||||
vdso_lookup[arch_index].dm);
|
||||
if (IS_ERR(ret))
|
||||
goto up_fail;
|
||||
|
||||
vdso_base += PAGE_SIZE;
|
||||
mm->context.vdso = (void *)vdso_base;
|
||||
ret = _install_special_mapping(mm, vdso_base, vdso_text_len,
|
||||
VM_READ|VM_EXEC|
|
||||
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
|
||||
vdso_lookup[arch_index].cm);
|
||||
if (IS_ERR(ret))
|
||||
goto up_fail;
|
||||
|
||||
return 0;
|
||||
|
||||
up_fail:
|
||||
mm->context.vdso = NULL;
|
||||
return PTR_ERR(ret);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT
|
||||
/*
|
||||
* Create and map the vectors page for AArch32 tasks.
|
||||
*/
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
static int aarch32_vdso_mremap(const struct vm_special_mapping *sm,
|
||||
struct vm_area_struct *new_vma)
|
||||
{
|
||||
return __vdso_remap(ARM64_VDSO32, sm, new_vma);
|
||||
}
|
||||
#endif /* CONFIG_COMPAT_VDSO */
|
||||
|
||||
/*
|
||||
* aarch32_vdso_pages:
|
||||
* 0 - kuser helpers
|
||||
* 1 - sigreturn code
|
||||
* or (CONFIG_COMPAT_VDSO):
|
||||
* 0 - kuser helpers
|
||||
* 1 - vdso data
|
||||
* 2 - vdso code
|
||||
*/
|
||||
#define C_VECTORS 0
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
#define C_VVAR 1
|
||||
#define C_VDSO 2
|
||||
#define C_PAGES (C_VDSO + 1)
|
||||
#else
|
||||
#define C_SIGPAGE 1
|
||||
#define C_PAGES (C_SIGPAGE + 1)
|
||||
#endif /* CONFIG_COMPAT_VDSO */
|
||||
static struct page *aarch32_vdso_pages[C_PAGES] __ro_after_init;
|
||||
static struct vm_special_mapping aarch32_vdso_spec[C_PAGES] = {
|
||||
{
|
||||
.name = "[vectors]", /* ABI */
|
||||
.pages = &aarch32_vdso_pages[C_VECTORS],
|
||||
},
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
{
|
||||
.name = "[vvar]",
|
||||
},
|
||||
{
|
||||
.name = "[vdso]",
|
||||
.mremap = aarch32_vdso_mremap,
|
||||
},
|
||||
#else
|
||||
{
|
||||
.name = "[sigpage]", /* ABI */
|
||||
.pages = &aarch32_vdso_pages[C_SIGPAGE],
|
||||
},
|
||||
#endif /* CONFIG_COMPAT_VDSO */
|
||||
};
|
||||
|
||||
static int aarch32_alloc_kuser_vdso_page(void)
|
||||
{
|
||||
extern char __kuser_helper_start[], __kuser_helper_end[];
|
||||
int kuser_sz = __kuser_helper_end - __kuser_helper_start;
|
||||
unsigned long vdso_page;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_KUSER_HELPERS))
|
||||
return 0;
|
||||
|
||||
vdso_page = get_zeroed_page(GFP_ATOMIC);
|
||||
if (!vdso_page)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy((void *)(vdso_page + 0x1000 - kuser_sz), __kuser_helper_start,
|
||||
kuser_sz);
|
||||
aarch32_vdso_pages[C_VECTORS] = virt_to_page(vdso_page);
|
||||
flush_dcache_page(aarch32_vdso_pages[C_VECTORS]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
static int __aarch32_alloc_vdso_pages(void)
|
||||
{
|
||||
int ret;
|
||||
|
||||
vdso_lookup[ARM64_VDSO32].dm = &aarch32_vdso_spec[C_VVAR];
|
||||
vdso_lookup[ARM64_VDSO32].cm = &aarch32_vdso_spec[C_VDSO];
|
||||
|
||||
ret = __vdso_init(ARM64_VDSO32);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = aarch32_alloc_kuser_vdso_page();
|
||||
if (ret) {
|
||||
unsigned long c_vvar =
|
||||
(unsigned long)page_to_virt(aarch32_vdso_pages[C_VVAR]);
|
||||
unsigned long c_vdso =
|
||||
(unsigned long)page_to_virt(aarch32_vdso_pages[C_VDSO]);
|
||||
|
||||
free_page(c_vvar);
|
||||
free_page(c_vdso);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
#else
|
||||
static int __aarch32_alloc_vdso_pages(void)
|
||||
{
|
||||
extern char __aarch32_sigret_code_start[], __aarch32_sigret_code_end[];
|
||||
int sigret_sz = __aarch32_sigret_code_end - __aarch32_sigret_code_start;
|
||||
unsigned long sigpage;
|
||||
int ret;
|
||||
|
||||
sigpage = get_zeroed_page(GFP_ATOMIC);
|
||||
if (!sigpage)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy((void *)sigpage, __aarch32_sigret_code_start, sigret_sz);
|
||||
aarch32_vdso_pages[C_SIGPAGE] = virt_to_page(sigpage);
|
||||
flush_dcache_page(aarch32_vdso_pages[C_SIGPAGE]);
|
||||
|
||||
ret = aarch32_alloc_kuser_vdso_page();
|
||||
if (ret)
|
||||
free_page(sigpage);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_COMPAT_VDSO */
|
||||
|
||||
static int __init aarch32_alloc_vdso_pages(void)
|
||||
{
|
||||
return __aarch32_alloc_vdso_pages();
|
||||
}
|
||||
arch_initcall(aarch32_alloc_vdso_pages);
|
||||
|
||||
static int aarch32_kuser_helpers_setup(struct mm_struct *mm)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_KUSER_HELPERS))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Avoid VM_MAYWRITE for compatibility with arch/arm/, where it's
|
||||
* not safe to CoW the page containing the CPU exception vectors.
|
||||
*/
|
||||
ret = _install_special_mapping(mm, AARCH32_VECTORS_BASE, PAGE_SIZE,
|
||||
VM_READ | VM_EXEC |
|
||||
VM_MAYREAD | VM_MAYEXEC,
|
||||
&aarch32_vdso_spec[C_VECTORS]);
|
||||
|
||||
return PTR_ERR_OR_ZERO(ret);
|
||||
}
|
||||
|
||||
#ifndef CONFIG_COMPAT_VDSO
|
||||
static int aarch32_sigreturn_setup(struct mm_struct *mm)
|
||||
{
|
||||
unsigned long addr;
|
||||
void *ret;
|
||||
|
||||
addr = get_unmapped_area(NULL, 0, PAGE_SIZE, 0, 0);
|
||||
if (IS_ERR_VALUE(addr)) {
|
||||
ret = ERR_PTR(addr);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/*
|
||||
* VM_MAYWRITE is required to allow gdb to Copy-on-Write and
|
||||
* set breakpoints.
|
||||
*/
|
||||
ret = _install_special_mapping(mm, addr, PAGE_SIZE,
|
||||
VM_READ | VM_EXEC | VM_MAYREAD |
|
||||
VM_MAYWRITE | VM_MAYEXEC,
|
||||
&aarch32_vdso_spec[C_SIGPAGE]);
|
||||
if (IS_ERR(ret))
|
||||
goto out;
|
||||
|
||||
mm->context.vdso = (void *)addr;
|
||||
|
||||
out:
|
||||
return PTR_ERR_OR_ZERO(ret);
|
||||
}
|
||||
#endif /* !CONFIG_COMPAT_VDSO */
|
||||
|
||||
int aarch32_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
int ret;
|
||||
|
||||
if (down_write_killable(&mm->mmap_sem))
|
||||
return -EINTR;
|
||||
|
||||
ret = aarch32_kuser_helpers_setup(mm);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
||||
#ifdef CONFIG_COMPAT_VDSO
|
||||
ret = __setup_additional_pages(ARM64_VDSO32,
|
||||
mm,
|
||||
bprm,
|
||||
uses_interp);
|
||||
#else
|
||||
ret = aarch32_sigreturn_setup(mm);
|
||||
#endif /* CONFIG_COMPAT_VDSO */
|
||||
|
||||
out:
|
||||
up_write(&mm->mmap_sem);
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_COMPAT */
|
||||
|
||||
static int vdso_mremap(const struct vm_special_mapping *sm,
|
||||
struct vm_area_struct *new_vma)
|
||||
{
|
||||
return __vdso_remap(ARM64_VDSO, sm, new_vma);
|
||||
}
|
||||
|
||||
/*
|
||||
* aarch64_vdso_pages:
|
||||
* 0 - vvar
|
||||
* 1 - vdso
|
||||
*/
|
||||
#define A_VVAR 0
|
||||
#define A_VDSO 1
|
||||
#define A_PAGES (A_VDSO + 1)
|
||||
static struct vm_special_mapping vdso_spec[A_PAGES] __ro_after_init = {
|
||||
{
|
||||
.name = "[vvar]",
|
||||
},
|
||||
@@ -121,37 +416,10 @@ static struct vm_special_mapping vdso_spec[2] __ro_after_init = {
|
||||
|
||||
static int __init vdso_init(void)
|
||||
{
|
||||
int i;
|
||||
struct page **vdso_pagelist;
|
||||
unsigned long pfn;
|
||||
vdso_lookup[ARM64_VDSO].dm = &vdso_spec[A_VVAR];
|
||||
vdso_lookup[ARM64_VDSO].cm = &vdso_spec[A_VDSO];
|
||||
|
||||
if (memcmp(vdso_start, "\177ELF", 4)) {
|
||||
pr_err("vDSO is not a valid ELF object!\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
vdso_pages = (vdso_end - vdso_start) >> PAGE_SHIFT;
|
||||
|
||||
/* Allocate the vDSO pagelist, plus a page for the data. */
|
||||
vdso_pagelist = kcalloc(vdso_pages + 1, sizeof(struct page *),
|
||||
GFP_KERNEL);
|
||||
if (vdso_pagelist == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Grab the vDSO data page. */
|
||||
vdso_pagelist[0] = phys_to_page(__pa_symbol(vdso_data));
|
||||
|
||||
|
||||
/* Grab the vDSO code pages. */
|
||||
pfn = sym_to_pfn(vdso_start);
|
||||
|
||||
for (i = 0; i < vdso_pages; i++)
|
||||
vdso_pagelist[i + 1] = pfn_to_page(pfn + i);
|
||||
|
||||
vdso_spec[0].pages = &vdso_pagelist[0];
|
||||
vdso_spec[1].pages = &vdso_pagelist[1];
|
||||
|
||||
return 0;
|
||||
return __vdso_init(ARM64_VDSO);
|
||||
}
|
||||
arch_initcall(vdso_init);
|
||||
|
||||
@@ -159,84 +427,17 @@ int arch_setup_additional_pages(struct linux_binprm *bprm,
|
||||
int uses_interp)
|
||||
{
|
||||
struct mm_struct *mm = current->mm;
|
||||
unsigned long vdso_base, vdso_text_len, vdso_mapping_len;
|
||||
void *ret;
|
||||
|
||||
vdso_text_len = vdso_pages << PAGE_SHIFT;
|
||||
/* Be sure to map the data page */
|
||||
vdso_mapping_len = vdso_text_len + PAGE_SIZE;
|
||||
int ret;
|
||||
|
||||
if (down_write_killable(&mm->mmap_sem))
|
||||
return -EINTR;
|
||||
vdso_base = get_unmapped_area(NULL, 0, vdso_mapping_len, 0, 0);
|
||||
if (IS_ERR_VALUE(vdso_base)) {
|
||||
ret = ERR_PTR(vdso_base);
|
||||
goto up_fail;
|
||||
}
|
||||
ret = _install_special_mapping(mm, vdso_base, PAGE_SIZE,
|
||||
VM_READ|VM_MAYREAD,
|
||||
&vdso_spec[0]);
|
||||
if (IS_ERR(ret))
|
||||
goto up_fail;
|
||||
|
||||
vdso_base += PAGE_SIZE;
|
||||
mm->context.vdso = (void *)vdso_base;
|
||||
ret = _install_special_mapping(mm, vdso_base, vdso_text_len,
|
||||
VM_READ|VM_EXEC|
|
||||
VM_MAYREAD|VM_MAYWRITE|VM_MAYEXEC,
|
||||
&vdso_spec[1]);
|
||||
if (IS_ERR(ret))
|
||||
goto up_fail;
|
||||
|
||||
ret = __setup_additional_pages(ARM64_VDSO,
|
||||
mm,
|
||||
bprm,
|
||||
uses_interp);
|
||||
|
||||
up_write(&mm->mmap_sem);
|
||||
return 0;
|
||||
|
||||
up_fail:
|
||||
mm->context.vdso = NULL;
|
||||
up_write(&mm->mmap_sem);
|
||||
return PTR_ERR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* Update the vDSO data page to keep in sync with kernel timekeeping.
|
||||
*/
|
||||
void update_vsyscall(struct timekeeper *tk)
|
||||
{
|
||||
u32 use_syscall = !tk->tkr_mono.clock->archdata.vdso_direct;
|
||||
|
||||
++vdso_data->tb_seq_count;
|
||||
smp_wmb();
|
||||
|
||||
vdso_data->use_syscall = use_syscall;
|
||||
vdso_data->xtime_coarse_sec = tk->xtime_sec;
|
||||
vdso_data->xtime_coarse_nsec = tk->tkr_mono.xtime_nsec >>
|
||||
tk->tkr_mono.shift;
|
||||
vdso_data->wtm_clock_sec = tk->wall_to_monotonic.tv_sec;
|
||||
vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec;
|
||||
|
||||
/* Read without the seqlock held by clock_getres() */
|
||||
WRITE_ONCE(vdso_data->hrtimer_res, hrtimer_resolution);
|
||||
|
||||
if (!use_syscall) {
|
||||
/* tkr_mono.cycle_last == tkr_raw.cycle_last */
|
||||
vdso_data->cs_cycle_last = tk->tkr_mono.cycle_last;
|
||||
vdso_data->raw_time_sec = tk->raw_sec;
|
||||
vdso_data->raw_time_nsec = tk->tkr_raw.xtime_nsec;
|
||||
vdso_data->xtime_clock_sec = tk->xtime_sec;
|
||||
vdso_data->xtime_clock_nsec = tk->tkr_mono.xtime_nsec;
|
||||
vdso_data->cs_mono_mult = tk->tkr_mono.mult;
|
||||
vdso_data->cs_raw_mult = tk->tkr_raw.mult;
|
||||
/* tkr_mono.shift == tkr_raw.shift */
|
||||
vdso_data->cs_shift = tk->tkr_mono.shift;
|
||||
}
|
||||
|
||||
smp_wmb();
|
||||
++vdso_data->tb_seq_count;
|
||||
}
|
||||
|
||||
void update_vsyscall_tz(void)
|
||||
{
|
||||
vdso_data->tz_minuteswest = sys_tz.tz_minuteswest;
|
||||
vdso_data->tz_dsttime = sys_tz.tz_dsttime;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -6,26 +6,49 @@
|
||||
# Heavily based on the vDSO Makefiles for other archs.
|
||||
#
|
||||
|
||||
obj-vdso := gettimeofday.o note.o sigreturn.o
|
||||
# Absolute relocation type $(ARCH_REL_TYPE_ABS) needs to be defined before
|
||||
# the inclusion of generic Makefile.
|
||||
ARCH_REL_TYPE_ABS := R_AARCH64_JUMP_SLOT|R_AARCH64_GLOB_DAT|R_AARCH64_ABS64
|
||||
include $(srctree)/lib/vdso/Makefile
|
||||
|
||||
obj-vdso := vgettimeofday.o note.o sigreturn.o
|
||||
|
||||
# Build rules
|
||||
targets := $(obj-vdso) vdso.so vdso.so.dbg
|
||||
obj-vdso := $(addprefix $(obj)/, $(obj-vdso))
|
||||
|
||||
ccflags-y := -shared -fno-common -fno-builtin
|
||||
ccflags-y += -nostdlib -Wl,-soname=linux-vdso.so.1 \
|
||||
$(call cc-ldoption, -Wl$(comma)--hash-style=sysv)
|
||||
ldflags-y := -shared -nostdlib -soname=linux-vdso.so.1 --hash-style=sysv \
|
||||
--build-id -n -T
|
||||
ccflags-y += $(DISABLE_LTO)
|
||||
|
||||
CFLAGS_REMOVE_vgettimeofday.o += $(CC_FLAGS_SCS)
|
||||
ccflags-y := -fno-common -fno-builtin -fno-stack-protector
|
||||
ccflags-y += -DDISABLE_BRANCH_PROFILING
|
||||
|
||||
VDSO_LDFLAGS := -Bsymbolic
|
||||
|
||||
CFLAGS_REMOVE_vgettimeofday.o = $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS)
|
||||
KBUILD_CFLAGS += $(DISABLE_LTO)
|
||||
KASAN_SANITIZE := n
|
||||
UBSAN_SANITIZE := n
|
||||
OBJECT_FILES_NON_STANDARD := y
|
||||
KCOV_INSTRUMENT := n
|
||||
|
||||
CFLAGS_vgettimeofday.o = -O2 -mcmodel=tiny
|
||||
|
||||
ifneq ($(c-gettimeofday-y),)
|
||||
CFLAGS_vgettimeofday.o += -include $(c-gettimeofday-y)
|
||||
endif
|
||||
|
||||
# Clang versions less than 8 do not support -mcmodel=tiny
|
||||
ifeq ($(CONFIG_CC_IS_CLANG), y)
|
||||
ifeq ($(shell test $(CONFIG_CLANG_VERSION) -lt 80000; echo $$?),0)
|
||||
CFLAGS_REMOVE_vgettimeofday.o += -mcmodel=tiny
|
||||
endif
|
||||
endif
|
||||
|
||||
# Disable gcov profiling for VDSO code
|
||||
GCOV_PROFILE := n
|
||||
|
||||
# Workaround for bare-metal (ELF) toolchains that neglect to pass -shared
|
||||
# down to collect2, resulting in silent corruption of the vDSO image.
|
||||
ccflags-y += -Wl,-shared
|
||||
|
||||
obj-y += vdso.o
|
||||
extra-y += vdso.lds
|
||||
CPPFLAGS_vdso.lds += -P -C -U$(ARCH)
|
||||
@@ -35,7 +58,7 @@ $(obj)/vdso.o : $(obj)/vdso.so
|
||||
|
||||
# Link rule for the .so file, .lds has to be first
|
||||
$(obj)/vdso.so.dbg: $(src)/vdso.lds $(obj-vdso)
|
||||
$(call if_changed,vdsold)
|
||||
$(call if_changed,vdsold_and_vdso_check)
|
||||
|
||||
# Strip rule for the .so file
|
||||
$(obj)/%.so: OBJCOPYFLAGS := -S
|
||||
@@ -52,16 +75,13 @@ endef
|
||||
include/generated/vdso-offsets.h: $(obj)/vdso.so.dbg FORCE
|
||||
$(call if_changed,vdsosym)
|
||||
|
||||
# Assembly rules for the .S files
|
||||
$(obj-vdso): %.o: %.S FORCE
|
||||
$(call if_changed_dep,vdsoas)
|
||||
|
||||
# Actual build commands
|
||||
quiet_cmd_vdsold = VDSOL $@
|
||||
cmd_vdsold = $(CC) $(c_flags) -Wl,-n -Wl,-T $^ -o $@
|
||||
quiet_cmd_vdsoas = VDSOA $@
|
||||
cmd_vdsoas = $(CC) $(a_flags) -c -o $@ $<
|
||||
|
||||
quiet_cmd_vdsold_and_vdso_check = LD $@
|
||||
cmd_vdsold_and_vdso_check = $(cmd_ld); $(cmd_vdso_check)
|
||||
|
||||
# Install commands for the unstripped file
|
||||
quiet_cmd_vdso_install = INSTALL $@
|
||||
cmd_vdso_install = cp $(obj)/$@.dbg $(MODLIB)/vdso/$@
|
||||
|
||||
@@ -1,334 +0,0 @@
|
||||
/*
|
||||
* Userspace implementations of gettimeofday() and friends.
|
||||
*
|
||||
* Copyright (C) 2012 ARM Limited
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* 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, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* Author: Will Deacon <will.deacon@arm.com>
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/asm-offsets.h>
|
||||
#include <asm/unistd.h>
|
||||
|
||||
#define NSEC_PER_SEC_LO16 0xca00
|
||||
#define NSEC_PER_SEC_HI16 0x3b9a
|
||||
|
||||
vdso_data .req x6
|
||||
seqcnt .req w7
|
||||
w_tmp .req w8
|
||||
x_tmp .req x8
|
||||
|
||||
/*
|
||||
* Conventions for macro arguments:
|
||||
* - An argument is write-only if its name starts with "res".
|
||||
* - All other arguments are read-only, unless otherwise specified.
|
||||
*/
|
||||
|
||||
.macro seqcnt_acquire
|
||||
9999: ldr seqcnt, [vdso_data, #VDSO_TB_SEQ_COUNT]
|
||||
tbnz seqcnt, #0, 9999b
|
||||
dmb ishld
|
||||
.endm
|
||||
|
||||
.macro seqcnt_check fail
|
||||
dmb ishld
|
||||
ldr w_tmp, [vdso_data, #VDSO_TB_SEQ_COUNT]
|
||||
cmp w_tmp, seqcnt
|
||||
b.ne \fail
|
||||
.endm
|
||||
|
||||
.macro syscall_check fail
|
||||
ldr w_tmp, [vdso_data, #VDSO_USE_SYSCALL]
|
||||
cbnz w_tmp, \fail
|
||||
.endm
|
||||
|
||||
.macro get_nsec_per_sec res
|
||||
mov \res, #NSEC_PER_SEC_LO16
|
||||
movk \res, #NSEC_PER_SEC_HI16, lsl #16
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Returns the clock delta, in nanoseconds left-shifted by the clock
|
||||
* shift.
|
||||
*/
|
||||
.macro get_clock_shifted_nsec res, cycle_last, mult
|
||||
/* Read the virtual counter. */
|
||||
isb
|
||||
mrs x_tmp, cntvct_el0
|
||||
/* Calculate cycle delta and convert to ns. */
|
||||
sub \res, x_tmp, \cycle_last
|
||||
/* We can only guarantee 56 bits of precision. */
|
||||
movn x_tmp, #0xff00, lsl #48
|
||||
and \res, x_tmp, \res
|
||||
mul \res, \res, \mult
|
||||
/*
|
||||
* Fake address dependency from the value computed from the counter
|
||||
* register to subsequent data page accesses so that the sequence
|
||||
* locking also orders the read of the counter.
|
||||
*/
|
||||
and x_tmp, \res, xzr
|
||||
add vdso_data, vdso_data, x_tmp
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Returns in res_{sec,nsec} the REALTIME timespec, based on the
|
||||
* "wall time" (xtime) and the clock_mono delta.
|
||||
*/
|
||||
.macro get_ts_realtime res_sec, res_nsec, \
|
||||
clock_nsec, xtime_sec, xtime_nsec, nsec_to_sec
|
||||
add \res_nsec, \clock_nsec, \xtime_nsec
|
||||
udiv x_tmp, \res_nsec, \nsec_to_sec
|
||||
add \res_sec, \xtime_sec, x_tmp
|
||||
msub \res_nsec, x_tmp, \nsec_to_sec, \res_nsec
|
||||
.endm
|
||||
|
||||
/*
|
||||
* Returns in res_{sec,nsec} the timespec based on the clock_raw delta,
|
||||
* used for CLOCK_MONOTONIC_RAW.
|
||||
*/
|
||||
.macro get_ts_clock_raw res_sec, res_nsec, clock_nsec, nsec_to_sec
|
||||
udiv \res_sec, \clock_nsec, \nsec_to_sec
|
||||
msub \res_nsec, \res_sec, \nsec_to_sec, \clock_nsec
|
||||
.endm
|
||||
|
||||
/* sec and nsec are modified in place. */
|
||||
.macro add_ts sec, nsec, ts_sec, ts_nsec, nsec_to_sec
|
||||
/* Add timespec. */
|
||||
add \sec, \sec, \ts_sec
|
||||
add \nsec, \nsec, \ts_nsec
|
||||
|
||||
/* Normalise the new timespec. */
|
||||
cmp \nsec, \nsec_to_sec
|
||||
b.lt 9999f
|
||||
sub \nsec, \nsec, \nsec_to_sec
|
||||
add \sec, \sec, #1
|
||||
9999:
|
||||
cmp \nsec, #0
|
||||
b.ge 9998f
|
||||
add \nsec, \nsec, \nsec_to_sec
|
||||
sub \sec, \sec, #1
|
||||
9998:
|
||||
.endm
|
||||
|
||||
.macro clock_gettime_return, shift=0
|
||||
.if \shift == 1
|
||||
lsr x11, x11, x12
|
||||
.endif
|
||||
stp x10, x11, [x1, #TSPEC_TV_SEC]
|
||||
mov x0, xzr
|
||||
ret
|
||||
.endm
|
||||
|
||||
.macro jump_slot jumptable, index, label
|
||||
.if (. - \jumptable) != 4 * (\index)
|
||||
.error "Jump slot index mismatch"
|
||||
.endif
|
||||
b \label
|
||||
.endm
|
||||
|
||||
.text
|
||||
|
||||
/* int __kernel_gettimeofday(struct timeval *tv, struct timezone *tz); */
|
||||
ENTRY(__kernel_gettimeofday)
|
||||
.cfi_startproc
|
||||
adr vdso_data, _vdso_data
|
||||
/* If tv is NULL, skip to the timezone code. */
|
||||
cbz x0, 2f
|
||||
|
||||
/* Compute the time of day. */
|
||||
1: seqcnt_acquire
|
||||
syscall_check fail=4f
|
||||
ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
|
||||
/* w11 = cs_mono_mult, w12 = cs_shift */
|
||||
ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT]
|
||||
ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
|
||||
|
||||
get_nsec_per_sec res=x9
|
||||
lsl x9, x9, x12
|
||||
|
||||
get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
|
||||
seqcnt_check fail=1b
|
||||
get_ts_realtime res_sec=x10, res_nsec=x11, \
|
||||
clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9
|
||||
|
||||
/* Convert ns to us. */
|
||||
mov x13, #1000
|
||||
lsl x13, x13, x12
|
||||
udiv x11, x11, x13
|
||||
stp x10, x11, [x0, #TVAL_TV_SEC]
|
||||
2:
|
||||
/* If tz is NULL, return 0. */
|
||||
cbz x1, 3f
|
||||
ldp w4, w5, [vdso_data, #VDSO_TZ_MINWEST]
|
||||
stp w4, w5, [x1, #TZ_MINWEST]
|
||||
3:
|
||||
mov x0, xzr
|
||||
ret
|
||||
4:
|
||||
/* Syscall fallback. */
|
||||
mov x8, #__NR_gettimeofday
|
||||
svc #0
|
||||
ret
|
||||
.cfi_endproc
|
||||
ENDPROC(__kernel_gettimeofday)
|
||||
|
||||
#define JUMPSLOT_MAX CLOCK_MONOTONIC_COARSE
|
||||
|
||||
/* int __kernel_clock_gettime(clockid_t clock_id, struct timespec *tp); */
|
||||
ENTRY(__kernel_clock_gettime)
|
||||
.cfi_startproc
|
||||
cmp w0, #JUMPSLOT_MAX
|
||||
b.hi syscall
|
||||
adr vdso_data, _vdso_data
|
||||
adr x_tmp, jumptable
|
||||
add x_tmp, x_tmp, w0, uxtw #2
|
||||
br x_tmp
|
||||
|
||||
ALIGN
|
||||
jumptable:
|
||||
jump_slot jumptable, CLOCK_REALTIME, realtime
|
||||
jump_slot jumptable, CLOCK_MONOTONIC, monotonic
|
||||
b syscall
|
||||
b syscall
|
||||
jump_slot jumptable, CLOCK_MONOTONIC_RAW, monotonic_raw
|
||||
jump_slot jumptable, CLOCK_REALTIME_COARSE, realtime_coarse
|
||||
jump_slot jumptable, CLOCK_MONOTONIC_COARSE, monotonic_coarse
|
||||
|
||||
.if (. - jumptable) != 4 * (JUMPSLOT_MAX + 1)
|
||||
.error "Wrong jumptable size"
|
||||
.endif
|
||||
|
||||
ALIGN
|
||||
realtime:
|
||||
seqcnt_acquire
|
||||
syscall_check fail=syscall
|
||||
ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
|
||||
/* w11 = cs_mono_mult, w12 = cs_shift */
|
||||
ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT]
|
||||
ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
|
||||
|
||||
/* All computations are done with left-shifted nsecs. */
|
||||
get_nsec_per_sec res=x9
|
||||
lsl x9, x9, x12
|
||||
|
||||
get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
|
||||
seqcnt_check fail=realtime
|
||||
get_ts_realtime res_sec=x10, res_nsec=x11, \
|
||||
clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9
|
||||
clock_gettime_return, shift=1
|
||||
|
||||
ALIGN
|
||||
monotonic:
|
||||
seqcnt_acquire
|
||||
syscall_check fail=syscall
|
||||
ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
|
||||
/* w11 = cs_mono_mult, w12 = cs_shift */
|
||||
ldp w11, w12, [vdso_data, #VDSO_CS_MONO_MULT]
|
||||
ldp x13, x14, [vdso_data, #VDSO_XTIME_CLK_SEC]
|
||||
ldp x3, x4, [vdso_data, #VDSO_WTM_CLK_SEC]
|
||||
|
||||
/* All computations are done with left-shifted nsecs. */
|
||||
lsl x4, x4, x12
|
||||
get_nsec_per_sec res=x9
|
||||
lsl x9, x9, x12
|
||||
|
||||
get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
|
||||
seqcnt_check fail=monotonic
|
||||
get_ts_realtime res_sec=x10, res_nsec=x11, \
|
||||
clock_nsec=x15, xtime_sec=x13, xtime_nsec=x14, nsec_to_sec=x9
|
||||
|
||||
add_ts sec=x10, nsec=x11, ts_sec=x3, ts_nsec=x4, nsec_to_sec=x9
|
||||
clock_gettime_return, shift=1
|
||||
|
||||
ALIGN
|
||||
monotonic_raw:
|
||||
seqcnt_acquire
|
||||
syscall_check fail=syscall
|
||||
ldr x10, [vdso_data, #VDSO_CS_CYCLE_LAST]
|
||||
/* w11 = cs_raw_mult, w12 = cs_shift */
|
||||
ldp w12, w11, [vdso_data, #VDSO_CS_SHIFT]
|
||||
ldp x13, x14, [vdso_data, #VDSO_RAW_TIME_SEC]
|
||||
|
||||
/* All computations are done with left-shifted nsecs. */
|
||||
get_nsec_per_sec res=x9
|
||||
lsl x9, x9, x12
|
||||
|
||||
get_clock_shifted_nsec res=x15, cycle_last=x10, mult=x11
|
||||
seqcnt_check fail=monotonic_raw
|
||||
get_ts_clock_raw res_sec=x10, res_nsec=x11, \
|
||||
clock_nsec=x15, nsec_to_sec=x9
|
||||
|
||||
add_ts sec=x10, nsec=x11, ts_sec=x13, ts_nsec=x14, nsec_to_sec=x9
|
||||
clock_gettime_return, shift=1
|
||||
|
||||
ALIGN
|
||||
realtime_coarse:
|
||||
seqcnt_acquire
|
||||
ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC]
|
||||
seqcnt_check fail=realtime_coarse
|
||||
clock_gettime_return
|
||||
|
||||
ALIGN
|
||||
monotonic_coarse:
|
||||
seqcnt_acquire
|
||||
ldp x10, x11, [vdso_data, #VDSO_XTIME_CRS_SEC]
|
||||
ldp x13, x14, [vdso_data, #VDSO_WTM_CLK_SEC]
|
||||
seqcnt_check fail=monotonic_coarse
|
||||
|
||||
/* Computations are done in (non-shifted) nsecs. */
|
||||
get_nsec_per_sec res=x9
|
||||
add_ts sec=x10, nsec=x11, ts_sec=x13, ts_nsec=x14, nsec_to_sec=x9
|
||||
clock_gettime_return
|
||||
|
||||
ALIGN
|
||||
syscall: /* Syscall fallback. */
|
||||
mov x8, #__NR_clock_gettime
|
||||
svc #0
|
||||
ret
|
||||
.cfi_endproc
|
||||
ENDPROC(__kernel_clock_gettime)
|
||||
|
||||
/* int __kernel_clock_getres(clockid_t clock_id, struct timespec *res); */
|
||||
ENTRY(__kernel_clock_getres)
|
||||
.cfi_startproc
|
||||
cmp w0, #CLOCK_REALTIME
|
||||
ccmp w0, #CLOCK_MONOTONIC, #0x4, ne
|
||||
ccmp w0, #CLOCK_MONOTONIC_RAW, #0x4, ne
|
||||
b.ne 1f
|
||||
|
||||
adr vdso_data, _vdso_data
|
||||
ldr w2, [vdso_data, #CLOCK_REALTIME_RES]
|
||||
b 2f
|
||||
1:
|
||||
cmp w0, #CLOCK_REALTIME_COARSE
|
||||
ccmp w0, #CLOCK_MONOTONIC_COARSE, #0x4, ne
|
||||
b.ne 4f
|
||||
ldr x2, 5f
|
||||
2:
|
||||
cbz x1, 3f
|
||||
stp xzr, x2, [x1]
|
||||
|
||||
3: /* res == NULL. */
|
||||
mov w0, wzr
|
||||
ret
|
||||
|
||||
4: /* Syscall fallback. */
|
||||
mov x8, #__NR_clock_getres
|
||||
svc #0
|
||||
ret
|
||||
5:
|
||||
.quad CLOCK_COARSE_RES
|
||||
.cfi_endproc
|
||||
ENDPROC(__kernel_clock_getres)
|
||||
25
arch/arm64/kernel/vdso/vgettimeofday.c
Normal file
25
arch/arm64/kernel/vdso/vgettimeofday.c
Normal file
@@ -0,0 +1,25 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* ARM64 userspace implementations of gettimeofday() and similar.
|
||||
*
|
||||
* Copyright (C) 2018 ARM Limited
|
||||
*
|
||||
*/
|
||||
|
||||
int __kernel_clock_gettime(clockid_t clock,
|
||||
struct __kernel_timespec *ts)
|
||||
{
|
||||
return __cvdso_clock_gettime(clock, ts);
|
||||
}
|
||||
|
||||
int __kernel_gettimeofday(struct __kernel_old_timeval *tv,
|
||||
struct timezone *tz)
|
||||
{
|
||||
return __cvdso_gettimeofday(tv, tz);
|
||||
}
|
||||
|
||||
int __kernel_clock_getres(clockid_t clock_id,
|
||||
struct __kernel_timespec *res)
|
||||
{
|
||||
return __cvdso_clock_getres(clock_id, res);
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
ARCH=arm64
|
||||
CLANG_TRIPLE=aarch64-linux-gnu-
|
||||
CROSS_COMPILE_COMPAT=arm-linux-androideabi-
|
||||
CROSS_COMPILE=aarch64-linux-androidkernel-
|
||||
CC=clang
|
||||
LD=ld.lld
|
||||
@@ -11,4 +10,3 @@ OBJCOPY=llvm-objcopy
|
||||
KERNEL_DIR=kernel-4.19
|
||||
CLANG_PREBUILT_BIN=prebuilts/clang/host/linux-x86/clang-r383902/bin
|
||||
LINUX_GCC_CROSS_COMPILE_PREBUILTS_BIN=prebuilts/gcc/linux-x86/aarch64/aarch64-linux-android-4.9/bin
|
||||
LINUX_GCC_CROSS_COMPILE_COMPAT_PREBUILTS_BIN=prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9/bin/
|
||||
|
||||
Reference in New Issue
Block a user