aboutsummaryrefslogtreecommitdiff
path: root/sys/amd64
diff options
context:
space:
mode:
Diffstat (limited to 'sys/amd64')
-rw-r--r--sys/amd64/linux/linux_sysvec.c159
-rw-r--r--sys/amd64/linux/linux_vdso.lds.s12
-rw-r--r--sys/amd64/linux/linux_vdso_gtod.c146
-rw-r--r--sys/amd64/linux32/linux32_locore.asm6
-rw-r--r--sys/amd64/linux32/linux32_sysvec.c158
-rw-r--r--sys/amd64/linux32/linux32_vdso.lds.s24
-rw-r--r--sys/amd64/linux32/linux32_vdso_gtod.c146
7 files changed, 578 insertions, 73 deletions
diff --git a/sys/amd64/linux/linux_sysvec.c b/sys/amd64/linux/linux_sysvec.c
index 2d3406582a33..7be7411a960a 100644
--- a/sys/amd64/linux/linux_sysvec.c
+++ b/sys/amd64/linux/linux_sysvec.c
@@ -50,6 +50,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
+#include <sys/stddef.h>
#include <sys/signalvar.h>
#include <sys/syscallsubr.h>
#include <sys/sysctl.h>
@@ -72,6 +73,7 @@ __FBSDID("$FreeBSD$");
#include <machine/specialreg.h>
#include <machine/trap.h>
+#include <x86/linux/linux_x86.h>
#include <amd64/linux/linux.h>
#include <amd64/linux/linux_proto.h>
#include <compat/linux/linux_emul.h>
@@ -85,11 +87,24 @@ __FBSDID("$FreeBSD$");
MODULE_VERSION(linux64, 1);
+#define LINUX_VDSOPAGE_SIZE PAGE_SIZE * 2
+#define LINUX_VDSOPAGE_LA48 (VM_MAXUSER_ADDRESS_LA48 - \
+ LINUX_VDSOPAGE_SIZE)
+#define LINUX_SHAREDPAGE_LA48 (LINUX_VDSOPAGE_LA48 - PAGE_SIZE)
+ /*
+ * PAGE_SIZE - the size
+ * of the native SHAREDPAGE
+ */
+#define LINUX_USRSTACK_LA48 LINUX_SHAREDPAGE_LA48
+#define LINUX_PS_STRINGS_LA48 (LINUX_USRSTACK_LA48 - \
+ sizeof(struct ps_strings))
+
static int linux_szsigcode;
-static vm_object_t linux_shared_page_obj;
-static char *linux_shared_page_mapping;
-extern char _binary_linux_locore_o_start;
-extern char _binary_linux_locore_o_end;
+static vm_object_t linux_vdso_obj;
+static char *linux_vdso_mapping;
+extern char _binary_linux_vdso_so_o_start;
+extern char _binary_linux_vdso_so_o_end;
+static vm_offset_t linux_vdso_base;
extern struct sysent linux_sysent[LINUX_SYS_MAXSYSCALL];
@@ -102,10 +117,12 @@ static int linux_fixup_elf(uintptr_t *stack_base,
static bool linux_trans_osrel(const Elf_Note *note, int32_t *osrel);
static void linux_vdso_install(void *param);
static void linux_vdso_deinstall(void *param);
+static void linux_vdso_reloc(char *mapping, Elf_Addr offset);
static void linux_set_syscall_retval(struct thread *td, int error);
static int linux_fetch_syscall_args(struct thread *td);
static void linux_exec_setregs(struct thread *td, struct image_params *imgp,
uintptr_t stack);
+static void linux_exec_sysvec_init(void *param);
static int linux_on_exec_vmspace(struct proc *p,
struct image_params *imgp);
static int linux_vsyscall(struct thread *td);
@@ -151,6 +168,8 @@ static int _bsd_to_linux_trapcode[] = {
LINUX_VDSO_SYM_INTPTR(linux_rt_sigcode);
LINUX_VDSO_SYM_CHAR(linux_platform);
+LINUX_VDSO_SYM_INTPTR(kern_timekeep_base);
+LINUX_VDSO_SYM_INTPTR(kern_tsc_selector);
/*
* If FreeBSD & Linux have a difference of opinion about what a trap
@@ -264,8 +283,7 @@ linux_copyout_auxargs(struct image_params *imgp, uintptr_t base)
M_WAITOK | M_ZERO);
issetugid = p->p_flag & P_SUGID ? 1 : 0;
- AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO_EHDR,
- imgp->proc->p_sysent->sv_shared_page_base);
+ AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO_EHDR, linux_vdso_base);
AUXARGS_ENTRY(pos, LINUX_AT_HWCAP, cpu_feature);
AUXARGS_ENTRY(pos, AT_PAGESZ, args->pagesz);
AUXARGS_ENTRY(pos, LINUX_AT_CLKTCK, stclohz);
@@ -739,7 +757,7 @@ struct sysentvec elf_linux_sysvec = {
.sv_transtrap = linux_translate_traps,
.sv_fixup = linux_fixup_elf,
.sv_sendsig = linux_rt_sendsig,
- .sv_sigcode = &_binary_linux_locore_o_start,
+ .sv_sigcode = &_binary_linux_vdso_so_o_start,
.sv_szsigcode = &linux_szsigcode,
.sv_name = "Linux ELF64",
.sv_coredump = elf64_coredump,
@@ -750,8 +768,8 @@ struct sysentvec elf_linux_sysvec = {
.sv_minsigstksz = LINUX_MINSIGSTKSZ,
.sv_minuser = VM_MIN_ADDRESS,
.sv_maxuser = VM_MAXUSER_ADDRESS_LA48,
- .sv_usrstack = USRSTACK_LA48,
- .sv_psstrings = PS_STRINGS_LA48,
+ .sv_usrstack = LINUX_USRSTACK_LA48,
+ .sv_psstrings = LINUX_PS_STRINGS_LA48,
.sv_stackprot = VM_PROT_ALL,
.sv_copyout_auxargs = linux_copyout_auxargs,
.sv_copyout_strings = linux_copyout_strings,
@@ -759,11 +777,11 @@ struct sysentvec elf_linux_sysvec = {
.sv_fixlimit = NULL,
.sv_maxssiz = NULL,
.sv_flags = SV_ABI_LINUX | SV_LP64 | SV_SHP | SV_SIG_DISCIGN |
- SV_SIG_WAITNDQ,
+ SV_SIG_WAITNDQ | SV_TIMEKEEP,
.sv_set_syscall_retval = linux_set_syscall_retval,
.sv_fetch_syscall_args = linux_fetch_syscall_args,
.sv_syscallnames = NULL,
- .sv_shared_page_base = SHAREDPAGE_LA48,
+ .sv_shared_page_base = LINUX_SHAREDPAGE_LA48,
.sv_shared_page_len = PAGE_SIZE,
.sv_schedtail = linux_schedtail,
.sv_thread_detach = linux_thread_detach,
@@ -777,47 +795,130 @@ struct sysentvec elf_linux_sysvec = {
static int
linux_on_exec_vmspace(struct proc *p, struct image_params *imgp)
{
+ int error;
- linux_on_exec(p, imgp);
- return (0);
+ error = linux_map_vdso(p, linux_vdso_obj, linux_vdso_base,
+ LINUX_VDSOPAGE_SIZE, imgp);
+ if (error == 0)
+ linux_on_exec(p, imgp);
+ return (error);
}
static void
-linux_vdso_install(void *param)
+linux_exec_sysvec_init(void *param)
{
+ l_uintptr_t *ktimekeep_base, *ktsc_selector;
+ struct sysentvec *sv;
+ ptrdiff_t tkoff;
+
+ sv = param;
+ amd64_lower_shared_page(sv);
+ /* Fill timekeep_base */
+ exec_sysvec_init(sv);
- amd64_lower_shared_page(&elf_linux_sysvec);
+ tkoff = kern_timekeep_base - linux_vdso_base;
+ ktimekeep_base = (l_uintptr_t *)(linux_vdso_mapping + tkoff);
+ *ktimekeep_base = sv->sv_timekeep_base;
- linux_szsigcode = (&_binary_linux_locore_o_end -
- &_binary_linux_locore_o_start);
+ tkoff = kern_tsc_selector - linux_vdso_base;
+ ktsc_selector = (l_uintptr_t *)(linux_vdso_mapping + tkoff);
+ *ktsc_selector = linux_vdso_tsc_selector_idx();
+ if (bootverbose)
+ printf("Linux x86-64 vDSO tsc_selector: %lu\n", *ktsc_selector);
+}
+SYSINIT(elf_linux_exec_sysvec_init, SI_SUB_EXEC, SI_ORDER_ANY,
+ linux_exec_sysvec_init, &elf_linux_sysvec);
+
+static void
+linux_vdso_install(void *param)
+{
+ char *vdso_start = &_binary_linux_vdso_so_o_start;
+ char *vdso_end = &_binary_linux_vdso_so_o_end;
- if (linux_szsigcode > elf_linux_sysvec.sv_shared_page_len)
- panic("Linux invalid vdso size\n");
+ linux_szsigcode = vdso_end - vdso_start;
+ MPASS(linux_szsigcode <= LINUX_VDSOPAGE_SIZE);
- __elfN(linux_vdso_fixup)(&elf_linux_sysvec);
+ linux_vdso_base = LINUX_VDSOPAGE_LA48;
+ if (hw_lower_amd64_sharedpage != 0)
+ linux_vdso_base -= PAGE_SIZE;
- linux_shared_page_obj = __elfN(linux_shared_page_init)
- (&linux_shared_page_mapping);
+ __elfN(linux_vdso_fixup)(vdso_start, linux_vdso_base);
- __elfN(linux_vdso_reloc)(&elf_linux_sysvec);
+ linux_vdso_obj = __elfN(linux_shared_page_init)
+ (&linux_vdso_mapping, LINUX_VDSOPAGE_SIZE);
+ bcopy(vdso_start, linux_vdso_mapping, linux_szsigcode);
- bcopy(elf_linux_sysvec.sv_sigcode, linux_shared_page_mapping,
- linux_szsigcode);
- elf_linux_sysvec.sv_shared_page_obj = linux_shared_page_obj;
+ linux_vdso_reloc(linux_vdso_mapping, linux_vdso_base);
}
-SYSINIT(elf_linux_vdso_init, SI_SUB_EXEC, SI_ORDER_ANY,
+SYSINIT(elf_linux_vdso_init, SI_SUB_EXEC, SI_ORDER_FIRST,
linux_vdso_install, NULL);
static void
linux_vdso_deinstall(void *param)
{
- __elfN(linux_shared_page_fini)(linux_shared_page_obj,
- linux_shared_page_mapping);
+ __elfN(linux_shared_page_fini)(linux_vdso_obj,
+ linux_vdso_mapping, LINUX_VDSOPAGE_SIZE);
}
SYSUNINIT(elf_linux_vdso_uninit, SI_SUB_EXEC, SI_ORDER_FIRST,
linux_vdso_deinstall, NULL);
+static void
+linux_vdso_reloc(char *mapping, Elf_Addr offset)
+{
+ const Elf_Ehdr *ehdr;
+ const Elf_Shdr *shdr;
+ Elf64_Addr *where, val;
+ Elf_Size rtype, symidx;
+ const Elf_Rela *rela;
+ Elf_Addr addr, addend;
+ int relacnt;
+ int i, j;
+
+ MPASS(offset != 0);
+
+ relacnt = 0;
+ ehdr = (const Elf_Ehdr *)mapping;
+ shdr = (const Elf_Shdr *)(mapping + ehdr->e_shoff);
+ for (i = 0; i < ehdr->e_shnum; i++)
+ {
+ switch (shdr[i].sh_type) {
+ case SHT_REL:
+ printf("Linux x86_64 vDSO: unexpected Rel section\n");
+ break;
+ case SHT_RELA:
+ rela = (const Elf_Rela *)(mapping + shdr[i].sh_offset);
+ relacnt = shdr[i].sh_size / sizeof(*rela);
+ }
+ }
+
+ for (j = 0; j < relacnt; j++, rela++) {
+ where = (Elf_Addr *)(mapping + rela->r_offset);
+ addend = rela->r_addend;
+ rtype = ELF_R_TYPE(rela->r_info);
+ symidx = ELF_R_SYM(rela->r_info);
+
+ switch (rtype) {
+ case R_X86_64_NONE: /* none */
+ break;
+
+ case R_X86_64_RELATIVE: /* B + A */
+ addr = (Elf_Addr)(offset + addend);
+ val = addr;
+ if (*where != val)
+ *where = val;
+ break;
+ case R_X86_64_IRELATIVE:
+ printf("Linux x86_64 vDSO: unexpected ifunc relocation, "
+ "symbol index %ld\n", symidx);
+ break;
+ default:
+ printf("Linux x86_64 vDSO: unexpected relocation type %ld, "
+ "symbol index %ld\n", rtype, symidx);
+ }
+ }
+}
+
static char GNULINUX_ABI_VENDOR[] = "GNU";
static int GNULINUX_ABI_DESC = 0;
diff --git a/sys/amd64/linux/linux_vdso.lds.s b/sys/amd64/linux/linux_vdso.lds.s
index 94f0266095fb..ccf7c80565bb 100644
--- a/sys/amd64/linux/linux_vdso.lds.s
+++ b/sys/amd64/linux/linux_vdso.lds.s
@@ -54,16 +54,20 @@ VERSION
{
LINUX_2.6 {
global:
- time;
__vdso_time;
- gettimeofday;
__vdso_gettimeofday;
- getcpu;
__vdso_getcpu;
- clock_gettime;
__vdso_clock_gettime;
+ __vdso_clock_getres;
+ local: *;
+ };
+
+ LINUX_0.0 {
+ global:
linux_rt_sigcode;
linux_platform;
+ kern_timekeep_base;
+ kern_tsc_selector;
local: *;
};
}
diff --git a/sys/amd64/linux/linux_vdso_gtod.c b/sys/amd64/linux/linux_vdso_gtod.c
new file mode 100644
index 000000000000..ad23dc33575a
--- /dev/null
+++ b/sys/amd64/linux/linux_vdso_gtod.c
@@ -0,0 +1,146 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Dmitry Chagin <dchagin@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/elf.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/stddef.h>
+#define _KERNEL
+#include <sys/vdso.h>
+#undef _KERNEL
+#include <stdbool.h>
+#include <strings.h>
+
+#include <machine/atomic.h>
+#include <machine/stdarg.h>
+
+#include <amd64/linux/linux.h>
+#include <amd64/linux/linux_syscall.h>
+#include <compat/linux/linux_errno.h>
+#include <compat/linux/linux_timer.h>
+
+/* The kernel fixup this at vDSO install */
+uintptr_t *kern_timekeep_base = NULL;
+uint32_t kern_tsc_selector = 0;
+
+#include <x86/linux/linux_vdso_gettc_x86.inc>
+
+/* for debug purpose */
+static int
+write(int fd, const void *buf, size_t size)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "syscall"
+ : "=a"(res)
+ : "a"(LINUX_SYS_write), "D"(fd), "S"(buf), "d"(size)
+ : "cc", "rcx", "r11", "memory"
+ );
+ return (res);
+}
+
+static int
+__vdso_clock_gettime_fallback(clockid_t clock_id, struct l_timespec *ts)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "syscall"
+ : "=a"(res)
+ : "a"(LINUX_SYS_linux_clock_gettime), "D"(clock_id), "S"(ts)
+ : "cc", "rcx", "r11", "memory"
+ );
+ return (res);
+}
+
+static int
+__vdso_gettimeofday_fallback(l_timeval *tv, struct timezone *tz)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "syscall"
+ : "=a"(res)
+ : "a"(LINUX_SYS_gettimeofday), "D"(tv), "S"(tz)
+ : "cc", "rcx", "r11", "memory"
+ );
+ return (res);
+}
+
+static int
+__vdso_clock_getres_fallback(clockid_t clock_id, struct l_timespec *ts)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "syscall"
+ : "=a"(res)
+ : "a"(LINUX_SYS_linux_clock_getres), "D"(clock_id), "S"(ts)
+ : "cc", "rcx", "r11", "memory"
+ );
+ return (res);
+}
+
+static int
+__vdso_getcpu_fallback(uint32_t *cpu, uint32_t *node, void *cache)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "syscall"
+ : "=a"(res)
+ : "a"(LINUX_SYS_linux_getcpu), "D"(cpu), "S"(node), "d"(cache)
+ : "cc", "rcx", "r11", "memory"
+ );
+ return (res);
+}
+
+static int
+__vdso_time_fallback(long *tm)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "syscall"
+ : "=a"(res)
+ : "a"(LINUX_SYS_linux_time), "D"(tm)
+ : "cc", "rcx", "r11", "memory"
+ );
+ return (res);
+}
+
+#include <compat/linux/linux_vdso_gtod.inc>
diff --git a/sys/amd64/linux32/linux32_locore.asm b/sys/amd64/linux32/linux32_locore.asm
index 7dbde5a2c854..f96b3e730f9f 100644
--- a/sys/amd64/linux32/linux32_locore.asm
+++ b/sys/amd64/linux32/linux32_locore.asm
@@ -18,7 +18,7 @@ linux_platform:
* To avoid excess stack frame the signal trampoline code emulates
* the 'call' instruction.
*/
-ENTRY(linux32_sigcode)
+ENTRY(__kernel_sigreturn)
movl %esp, %ebx /* preserve sigframe */
call .getip0
.getip0:
@@ -33,7 +33,7 @@ ENTRY(linux32_sigcode)
.endsigcode:
0: jmp 0b
-ENTRY(linux32_rt_sigcode)
+ENTRY(__kernel_rt_sigreturn)
leal LINUX_RT_SIGF_UC(%esp),%ebx /* linux ucp */
leal LINUX_RT_SIGF_SC(%ebx),%ecx /* linux sigcontext */
movl %esp, %edi
@@ -49,7 +49,7 @@ ENTRY(linux32_rt_sigcode)
.endrtsigcode:
0: jmp 0b
-ENTRY(linux32_vsyscall)
+ENTRY(__kernel_vsyscall)
.startvsyscall:
int $0x80
ret
diff --git a/sys/amd64/linux32/linux32_sysvec.c b/sys/amd64/linux32/linux32_sysvec.c
index 90b6207a50c9..165eb7b2a1d2 100644
--- a/sys/amd64/linux32/linux32_sysvec.c
+++ b/sys/amd64/linux32/linux32_sysvec.c
@@ -56,6 +56,7 @@ __FBSDID("$FreeBSD$");
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/resourcevar.h>
+#include <sys/stddef.h>
#include <sys/signalvar.h>
#include <sys/syscallsubr.h>
#include <sys/sysctl.h>
@@ -78,6 +79,7 @@ __FBSDID("$FreeBSD$");
#include <machine/specialreg.h>
#include <machine/trap.h>
+#include <x86/linux/linux_x86.h>
#include <amd64/linux32/linux.h>
#include <amd64/linux32/linux32_proto.h>
#include <compat/linux/linux_emul.h>
@@ -91,14 +93,21 @@ __FBSDID("$FreeBSD$");
MODULE_VERSION(linux, 1);
#define LINUX32_MAXUSER ((1ul << 32) - PAGE_SIZE)
-#define LINUX32_SHAREDPAGE (LINUX32_MAXUSER - PAGE_SIZE)
+#define LINUX32_VDSOPAGE_SIZE PAGE_SIZE * 2
+#define LINUX32_VDSOPAGE (LINUX32_MAXUSER - LINUX32_VDSOPAGE_SIZE)
+#define LINUX32_SHAREDPAGE (LINUX32_VDSOPAGE - PAGE_SIZE)
+ /*
+ * PAGE_SIZE - the size
+ * of the native SHAREDPAGE
+ */
#define LINUX32_USRSTACK LINUX32_SHAREDPAGE
static int linux_szsigcode;
-static vm_object_t linux_shared_page_obj;
-static char *linux_shared_page_mapping;
-extern char _binary_linux32_locore_o_start;
-extern char _binary_linux32_locore_o_end;
+static vm_object_t linux_vdso_obj;
+static char *linux_vdso_mapping;
+extern char _binary_linux32_vdso_so_o_start;
+extern char _binary_linux32_vdso_so_o_end;
+static vm_offset_t linux_vdso_base;
extern struct sysent linux32_sysent[LINUX32_SYS_MAXSYSCALL];
@@ -111,12 +120,14 @@ static int linux_copyout_strings(struct image_params *imgp,
static void linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask);
static void linux_exec_setregs(struct thread *td,
struct image_params *imgp, uintptr_t stack);
+static void linux_exec_sysvec_init(void *param);
static int linux_on_exec_vmspace(struct proc *p,
struct image_params *imgp);
static void linux32_fixlimit(struct rlimit *rl, int which);
static bool linux32_trans_osrel(const Elf_Note *note, int32_t *osrel);
static void linux_vdso_install(void *param);
static void linux_vdso_deinstall(void *param);
+static void linux_vdso_reloc(char *mapping, Elf_Addr offset);
static void linux32_set_syscall_retval(struct thread *td, int error);
#define LINUX_T_UNKNOWN 255
@@ -167,9 +178,11 @@ struct linux32_ps_strings {
#define LINUX32_PS_STRINGS (LINUX32_USRSTACK - \
sizeof(struct linux32_ps_strings))
-LINUX_VDSO_SYM_INTPTR(linux32_sigcode);
-LINUX_VDSO_SYM_INTPTR(linux32_rt_sigcode);
-LINUX_VDSO_SYM_INTPTR(linux32_vsyscall);
+LINUX_VDSO_SYM_INTPTR(__kernel_vsyscall);
+LINUX_VDSO_SYM_INTPTR(__kernel_sigreturn);
+LINUX_VDSO_SYM_INTPTR(__kernel_rt_sigreturn);
+LINUX_VDSO_SYM_INTPTR(kern_timekeep_base);
+LINUX_VDSO_SYM_INTPTR(kern_tsc_selector);
LINUX_VDSO_SYM_CHAR(linux_platform);
/*
@@ -206,9 +219,8 @@ linux_copyout_auxargs(struct image_params *imgp, uintptr_t base)
M_WAITOK | M_ZERO);
issetugid = imgp->proc->p_flag & P_SUGID ? 1 : 0;
- AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO, linux32_vsyscall);
- AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO_EHDR,
- imgp->proc->p_sysent->sv_shared_page_base);
+ AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO, __kernel_vsyscall);
+ AUXARGS_ENTRY(pos, LINUX_AT_SYSINFO_EHDR, linux_vdso_base);
AUXARGS_ENTRY(pos, LINUX_AT_HWCAP, cpu_feature);
AUXARGS_ENTRY(pos, AT_PAGESZ, args->pagesz);
@@ -354,7 +366,7 @@ linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
/* Build context to run handler in. */
regs->tf_rsp = PTROUT(fp);
- regs->tf_rip = linux32_rt_sigcode;
+ regs->tf_rip = __kernel_rt_sigreturn;
regs->tf_rflags &= ~(PSL_T | PSL_D);
regs->tf_cs = _ucode32sel;
regs->tf_ss = _udatasel;
@@ -460,7 +472,7 @@ linux_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask)
/* Build context to run handler in. */
regs->tf_rsp = PTROUT(fp);
- regs->tf_rip = linux32_sigcode;
+ regs->tf_rip = __kernel_sigreturn;
regs->tf_rflags &= ~(PSL_T | PSL_D);
regs->tf_cs = _ucode32sel;
regs->tf_ss = _udatasel;
@@ -911,7 +923,7 @@ struct sysentvec elf_linux_sysvec = {
.sv_transtrap = linux_translate_traps,
.sv_fixup = linux_fixup_elf,
.sv_sendsig = linux_sendsig,
- .sv_sigcode = &_binary_linux32_locore_o_start,
+ .sv_sigcode = &_binary_linux32_vdso_so_o_start,
.sv_szsigcode = &linux_szsigcode,
.sv_name = "Linux ELF32",
.sv_coredump = elf32_coredump,
@@ -931,7 +943,7 @@ struct sysentvec elf_linux_sysvec = {
.sv_fixlimit = linux32_fixlimit,
.sv_maxssiz = &linux32_maxssiz,
.sv_flags = SV_ABI_LINUX | SV_ILP32 | SV_IA32 | SV_SHP |
- SV_SIG_DISCIGN | SV_SIG_WAITNDQ,
+ SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_TIMEKEEP,
.sv_set_syscall_retval = linux32_set_syscall_retval,
.sv_fetch_syscall_args = linux32_fetch_syscall_args,
.sv_syscallnames = NULL,
@@ -949,45 +961,127 @@ struct sysentvec elf_linux_sysvec = {
static int
linux_on_exec_vmspace(struct proc *p, struct image_params *imgp)
{
+ int error;
- linux_on_exec(p, imgp);
- return (0);
+ error = linux_map_vdso(p, linux_vdso_obj, linux_vdso_base,
+ LINUX32_VDSOPAGE_SIZE, imgp);
+ if (error == 0)
+ linux_on_exec(p, imgp);
+ return (error);
}
static void
-linux_vdso_install(void *param)
+linux_exec_sysvec_init(void *param)
{
+ l_uintptr_t *ktimekeep_base, *ktsc_selector;
+ struct sysentvec *sv;
+ ptrdiff_t tkoff;
- linux_szsigcode = (&_binary_linux32_locore_o_end -
- &_binary_linux32_locore_o_start);
+ sv = param;
+ /* Fill timekeep_base */
+ exec_sysvec_init(sv);
- if (linux_szsigcode > elf_linux_sysvec.sv_shared_page_len)
- panic("Linux invalid vdso size\n");
+ tkoff = kern_timekeep_base - linux_vdso_base;
+ ktimekeep_base = (l_uintptr_t *)(linux_vdso_mapping + tkoff);
+ *ktimekeep_base = sv->sv_timekeep_base;
- __elfN(linux_vdso_fixup)(&elf_linux_sysvec);
+ tkoff = kern_tsc_selector - linux_vdso_base;
+ ktsc_selector = (l_uintptr_t *)(linux_vdso_mapping + tkoff);
+ *ktsc_selector = linux_vdso_tsc_selector_idx();
+ if (bootverbose)
+ printf("Linux i386 vDSO tsc_selector: %u\n", *ktsc_selector);
+}
+SYSINIT(elf_linux_exec_sysvec_init, SI_SUB_EXEC, SI_ORDER_ANY,
+ linux_exec_sysvec_init, &elf_linux_sysvec);
- linux_shared_page_obj = __elfN(linux_shared_page_init)
- (&linux_shared_page_mapping);
+static void
+linux_vdso_install(void *param)
+{
+ char *vdso_start = &_binary_linux32_vdso_so_o_start;
+ char *vdso_end = &_binary_linux32_vdso_so_o_end;
+
+ linux_szsigcode = vdso_end - vdso_start;
+ MPASS(linux_szsigcode <= LINUX32_VDSOPAGE_SIZE);
+
+ linux_vdso_base = LINUX32_VDSOPAGE;
- __elfN(linux_vdso_reloc)(&elf_linux_sysvec);
+ __elfN(linux_vdso_fixup)(vdso_start, linux_vdso_base);
- bcopy(elf_linux_sysvec.sv_sigcode, linux_shared_page_mapping,
- linux_szsigcode);
- elf_linux_sysvec.sv_shared_page_obj = linux_shared_page_obj;
+ linux_vdso_obj = __elfN(linux_shared_page_init)
+ (&linux_vdso_mapping, LINUX32_VDSOPAGE_SIZE);
+ bcopy(vdso_start, linux_vdso_mapping, linux_szsigcode);
+
+ linux_vdso_reloc(linux_vdso_mapping, linux_vdso_base);
}
-SYSINIT(elf_linux_vdso_init, SI_SUB_EXEC, SI_ORDER_ANY,
+SYSINIT(elf_linux_vdso_init, SI_SUB_EXEC, SI_ORDER_FIRST,
linux_vdso_install, NULL);
static void
linux_vdso_deinstall(void *param)
{
- __elfN(linux_shared_page_fini)(linux_shared_page_obj,
- linux_shared_page_mapping);
+ __elfN(linux_shared_page_fini)(linux_vdso_obj,
+ linux_vdso_mapping, LINUX32_VDSOPAGE_SIZE);
}
SYSUNINIT(elf_linux_vdso_uninit, SI_SUB_EXEC, SI_ORDER_FIRST,
linux_vdso_deinstall, NULL);
+static void
+linux_vdso_reloc(char *mapping, Elf_Addr offset)
+{
+ const Elf_Shdr *shdr;
+ const Elf_Rel *rel;
+ const Elf_Ehdr *ehdr;
+ Elf32_Addr *where;
+ Elf_Size rtype, symidx;
+ Elf32_Addr addr, addend;
+ int i, relcnt;
+
+ MPASS(offset != 0);
+
+ relcnt = 0;
+ ehdr = (const Elf_Ehdr *)mapping;
+ shdr = (const Elf_Shdr *)(mapping + ehdr->e_shoff);
+ for (i = 0; i < ehdr->e_shnum; i++)
+ {
+ switch (shdr[i].sh_type) {
+ case SHT_REL:
+ rel = (const Elf_Rel *)(mapping + shdr[i].sh_offset);
+ relcnt = shdr[i].sh_size / sizeof(*rel);
+ break;
+ case SHT_RELA:
+ printf("Linux i386 vDSO: unexpected Rela section\n");
+ break;
+ }
+ }
+
+ for (i = 0; i < relcnt; i++, rel++) {
+ where = (Elf32_Addr *)(mapping + rel->r_offset);
+ addend = *where;
+ rtype = ELF_R_TYPE(rel->r_info);
+ symidx = ELF_R_SYM(rel->r_info);
+
+ switch (rtype) {
+ case R_386_NONE: /* none */
+ break;
+
+ case R_386_RELATIVE: /* B + A */
+ addr = (Elf32_Addr)PTROUT(offset + addend);
+ if (*where != addr)
+ *where = addr;
+ break;
+
+ case R_386_IRELATIVE:
+ printf("Linux i386 vDSO: unexpected ifunc relocation, "
+ "symbol index %ld\n", (intmax_t)symidx);
+ break;
+ default:
+ printf("Linux i386 vDSO: unexpected relocation type %ld, "
+ "symbol index %ld\n", (intmax_t)rtype, (intmax_t)symidx);
+ }
+ }
+}
+
static char GNU_ABI_VENDOR[] = "GNU";
static int GNULINUX_ABI_DESC = 0;
diff --git a/sys/amd64/linux32/linux32_vdso.lds.s b/sys/amd64/linux32/linux32_vdso.lds.s
index a49c209a1ebc..0a392e6380b6 100644
--- a/sys/amd64/linux32/linux32_vdso.lds.s
+++ b/sys/amd64/linux32/linux32_vdso.lds.s
@@ -51,16 +51,30 @@ PHDRS
eh_frame_hdr PT_GNU_EH_FRAME;
}
-ENTRY(linux32_vsyscall);
-
VERSION
{
+ LINUX_2.6 {
+ global:
+ __vdso_clock_gettime;
+ __vdso_gettimeofday;
+ __vdso_time;
+ __vdso_clock_getres;
+ __vdso_clock_gettime64;
+ };
+
LINUX_2.5 {
global:
- linux32_vsyscall;
- linux32_sigcode;
- linux32_rt_sigcode;
+ __kernel_vsyscall;
+ __kernel_sigreturn;
+ __kernel_rt_sigreturn;
+ local: *;
+ };
+
+ LINUX_0.0 {
+ global:
linux_platform;
+ kern_timekeep_base;
+ kern_tsc_selector;
local: *;
};
}
diff --git a/sys/amd64/linux32/linux32_vdso_gtod.c b/sys/amd64/linux32/linux32_vdso_gtod.c
new file mode 100644
index 000000000000..f1573ca3c1b1
--- /dev/null
+++ b/sys/amd64/linux32/linux32_vdso_gtod.c
@@ -0,0 +1,146 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 Dmitry Chagin <dchagin@FreeBSD.org>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/elf.h>
+#include <sys/errno.h>
+#include <sys/proc.h>
+#include <sys/stddef.h>
+#define _KERNEL
+#include <sys/vdso.h>
+#undef _KERNEL
+#include <stdbool.h>
+#include <strings.h>
+
+#include <machine/atomic.h>
+#include <machine/stdarg.h>
+
+#include <amd64/linux32/linux.h>
+#include <amd64/linux32/linux32_syscall.h>
+#include <compat/linux/linux_errno.h>
+#include <compat/linux/linux_timer.h>
+
+/* The kernel fixup this at vDSO install */
+uintptr_t *kern_timekeep_base = NULL;
+uint32_t kern_tsc_selector = 0;
+
+#include <x86/linux/linux_vdso_gettc_x86.inc>
+
+static int
+write(int fd, const void *buf, size_t size)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "int $0x80"
+ : "=a"(res)
+ : "a"(LINUX32_SYS_write), "b"(fd), "c"(buf), "d"(size)
+ : "cc", "memory"
+ );
+ return (res);
+}
+
+static int
+__vdso_clock_gettime_fallback(clockid_t clock_id, struct l_timespec *ts)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "int $0x80"
+ : "=a"(res)
+ : "a"(LINUX32_SYS_linux_clock_gettime), "b"(clock_id), "c"(ts)
+ : "cc", "memory"
+ );
+ return (res);
+}
+
+static int
+__vdso_clock_gettime64_fallback(clockid_t clock_id, struct l_timespec64 *ts)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "int $0x80"
+ : "=a"(res)
+ : "a"(LINUX32_SYS_linux_clock_gettime64), "b"(clock_id), "c"(ts)
+ : "cc", "memory"
+ );
+ return (res);
+}
+
+static int
+__vdso_gettimeofday_fallback(l_timeval *tv, struct timezone *tz)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "int $0x80"
+ : "=a"(res)
+ : "a"(LINUX32_SYS_linux_gettimeofday), "b"(tv), "c"(tz)
+ : "cc", "memory"
+ );
+ return (res);
+}
+
+static int
+__vdso_clock_getres_fallback(clockid_t clock_id, struct l_timespec *ts)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "int $0x80"
+ : "=a"(res)
+ : "a"(LINUX32_SYS_linux_clock_getres), "b"(clock_id), "c"(ts)
+ : "cc", "memory"
+ );
+ return (res);
+}
+
+static int
+__vdso_time_fallback(long *tm)
+{
+ int res;
+
+ __asm__ __volatile__
+ (
+ "int $0x80"
+ : "=a"(res)
+ : "a"(LINUX32_SYS_linux_time), "b"(tm)
+ : "cc", "memory"
+ );
+ return (res);
+}
+
+#include <compat/linux/linux_vdso_gtod.inc>