diff --git a/sys/arm64/arm64/elf_machdep.c b/sys/arm64/arm64/elf_machdep.c index 6ca8a711839f..970dba0ca7d9 100644 --- a/sys/arm64/arm64/elf_machdep.c +++ b/sys/arm64/arm64/elf_machdep.c @@ -1,388 +1,392 @@ /*- * Copyright (c) 2014, 2015 The FreeBSD Foundation. * Copyright (c) 2014 Andrew Turner. * All rights reserved. * * This software was developed by Andrew Turner under * sponsorship from the FreeBSD Foundation. * * Portions of this software were developed by Konstantin Belousov * under sponsorship from the FreeBSD Foundation. * * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "linker_if.h" u_long __read_frequently elf_hwcap; u_long __read_frequently elf_hwcap2; +u_long __read_frequently elf_hwcap3; +u_long __read_frequently elf_hwcap4; /* TODO: Move to a better location */ u_long __read_frequently linux_elf_hwcap; u_long __read_frequently linux_elf_hwcap2; +u_long __read_frequently linux_elf_hwcap3; +u_long __read_frequently linux_elf_hwcap4; struct arm64_addr_mask elf64_addr_mask; static void arm64_exec_protect(struct image_params *, int); static struct sysentvec elf64_freebsd_sysvec = { .sv_size = SYS_MAXSYSCALL, .sv_table = sysent, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = sendsig, .sv_sigcode = sigcode, .sv_szsigcode = &szsigcode, .sv_name = "FreeBSD ELF64", .sv_coredump = __elfN(coredump), .sv_elf_core_osabi = ELFOSABI_FREEBSD, .sv_elf_core_abi_vendor = FREEBSD_ABI_VENDOR, .sv_elf_core_prepare_notes = __elfN(prepare_notes), .sv_minsigstksz = MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = USRSTACK, .sv_psstrings = PS_STRINGS, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_READ | VM_PROT_WRITE, .sv_copyout_auxargs = __elfN(freebsd_copyout_auxargs), .sv_copyout_strings = exec_copyout_strings, .sv_setregs = exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_SHP | SV_TIMEKEEP | SV_ABI_FREEBSD | SV_LP64 | SV_ASLR | SV_RNG_SEED_VER | SV_SIGSYS, .sv_set_syscall_retval = cpu_set_syscall_retval, .sv_fetch_syscall_args = cpu_fetch_syscall_args, .sv_syscallnames = syscallnames, .sv_shared_page_base = SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = NULL, .sv_thread_detach = NULL, .sv_trap = NULL, .sv_hwcap = &elf_hwcap, .sv_hwcap2 = &elf_hwcap2, - .sv_hwcap3 = NULL, - .sv_hwcap4 = NULL, + .sv_hwcap3 = &elf_hwcap3, + .sv_hwcap4 = &elf_hwcap4, .sv_onexec_old = exec_onexec_old, .sv_protect = arm64_exec_protect, .sv_onexit = exit_onexit, .sv_regset_begin = SET_BEGIN(__elfN(regset)), .sv_regset_end = SET_LIMIT(__elfN(regset)), }; INIT_SYSENTVEC(elf64_sysvec, &elf64_freebsd_sysvec); static Elf64_Brandinfo freebsd_brand_info = { .brand = ELFOSABI_FREEBSD, .machine = EM_AARCH64, .compat_3_brand = "FreeBSD", .interp_path = "/libexec/ld-elf.so.1", .sysvec = &elf64_freebsd_sysvec, .interp_newpath = NULL, .brand_note = &elf64_freebsd_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; SYSINIT(elf64, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t)elf64_insert_brand_entry, &freebsd_brand_info); static bool get_arm64_addr_mask(struct regset *rs, struct thread *td, void *buf, size_t *sizep) { if (buf != NULL) { KASSERT(*sizep == sizeof(elf64_addr_mask), ("%s: invalid size", __func__)); memcpy(buf, &elf64_addr_mask, sizeof(elf64_addr_mask)); } *sizep = sizeof(elf64_addr_mask); return (true); } static struct regset regset_arm64_addr_mask = { .note = NT_ARM_ADDR_MASK, .size = sizeof(struct arm64_addr_mask), .get = get_arm64_addr_mask, }; ELF_REGSET(regset_arm64_addr_mask); void elf64_dump_thread(struct thread *td __unused, void *dst __unused, size_t *off __unused) { } bool elf_is_ifunc_reloc(Elf_Size r_info __unused) { return (ELF_R_TYPE(r_info) == R_AARCH64_IRELATIVE); } static int reloc_instr_imm(Elf32_Addr *where, Elf_Addr val, u_int msb, u_int lsb) { /* Check bounds: upper bits must be all ones or all zeros. */ if ((uint64_t)((int64_t)val >> (msb + 1)) + 1 > 1) return (-1); val >>= lsb; val &= (1 << (msb - lsb + 1)) - 1; *where |= (Elf32_Addr)val; return (0); } /* * Process a relocation. Support for some static relocations is required * in order for the -zifunc-noplt optimization to work. */ static int elf_reloc_internal(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, int flags, elf_lookup_fn lookup) { #define ARM64_ELF_RELOC_LOCAL (1 << 0) #define ARM64_ELF_RELOC_LATE_IFUNC (1 << 1) Elf_Addr *where, addr, addend, val; Elf_Word rtype, symidx; const Elf_Rel *rel; const Elf_Rela *rela; int error; switch (type) { case ELF_RELOC_REL: rel = (const Elf_Rel *)data; where = (Elf_Addr *) (relocbase + rel->r_offset); addend = *where; rtype = ELF_R_TYPE(rel->r_info); symidx = ELF_R_SYM(rel->r_info); break; case ELF_RELOC_RELA: rela = (const Elf_Rela *)data; where = (Elf_Addr *) (relocbase + rela->r_offset); addend = rela->r_addend; rtype = ELF_R_TYPE(rela->r_info); symidx = ELF_R_SYM(rela->r_info); break; default: panic("unknown reloc type %d\n", type); } if ((flags & ARM64_ELF_RELOC_LATE_IFUNC) != 0) { KASSERT(type == ELF_RELOC_RELA, ("Only RELA ifunc relocations are supported")); if (rtype != R_AARCH64_IRELATIVE) return (0); } if ((flags & ARM64_ELF_RELOC_LOCAL) != 0) { if (rtype == R_AARCH64_RELATIVE) *where = elf_relocaddr(lf, relocbase + addend); return (0); } error = 0; switch (rtype) { case R_AARCH64_NONE: case R_AARCH64_RELATIVE: break; case R_AARCH64_TSTBR14: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); error = reloc_instr_imm((Elf32_Addr *)where, addr + addend - (Elf_Addr)where, 15, 2); break; case R_AARCH64_CONDBR19: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); error = reloc_instr_imm((Elf32_Addr *)where, addr + addend - (Elf_Addr)where, 20, 2); break; case R_AARCH64_JUMP26: case R_AARCH64_CALL26: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); error = reloc_instr_imm((Elf32_Addr *)where, addr + addend - (Elf_Addr)where, 27, 2); break; case R_AARCH64_ABS64: case R_AARCH64_GLOB_DAT: case R_AARCH64_JUMP_SLOT: error = lookup(lf, symidx, 1, &addr); if (error != 0) return (-1); *where = addr + addend; break; case R_AARCH64_IRELATIVE: addr = relocbase + addend; val = ((Elf64_Addr (*)(void))addr)(); if (*where != val) *where = val; break; default: printf("kldload: unexpected relocation type %d, " "symbol index %d\n", rtype, symidx); return (-1); } return (error); } int elf_reloc_local(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, ARM64_ELF_RELOC_LOCAL, lookup)); } /* Process one elf relocation with addend. */ int elf_reloc(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, 0, lookup)); } int elf_reloc_late(linker_file_t lf, Elf_Addr relocbase, const void *data, int type, elf_lookup_fn lookup) { return (elf_reloc_internal(lf, relocbase, data, type, ARM64_ELF_RELOC_LATE_IFUNC, lookup)); } int elf_cpu_load_file(linker_file_t lf) { if (lf->id != 1) cpu_icache_sync_range(lf->address, lf->size); return (0); } int elf_cpu_unload_file(linker_file_t lf __unused) { return (0); } int elf_cpu_parse_dynamic(caddr_t loadbase __unused, Elf_Dyn *dynamic __unused) { return (0); } static Elf_Note gnu_property_note = { .n_namesz = sizeof(GNU_ABI_VENDOR), .n_descsz = 16, .n_type = NT_GNU_PROPERTY_TYPE_0, }; static bool gnu_property_cb(const Elf_Note *note, void *arg0, bool *res) { const uint32_t *data; uintptr_t p; *res = false; p = (uintptr_t)(note + 1); p += roundup2(note->n_namesz, 4); data = (const uint32_t *)p; if (data[0] != GNU_PROPERTY_AARCH64_FEATURE_1_AND) return (false); /* * The data length should be at least the size of a uint32, and be * a multiple of uint32_t's */ if (data[1] < sizeof(uint32_t) || (data[1] % sizeof(uint32_t)) != 0) return (false); if ((data[2] & GNU_PROPERTY_AARCH64_FEATURE_1_BTI) != 0) *res = true; return (true); } static void arm64_exec_protect(struct image_params *imgp, int flags __unused) { const Elf_Ehdr *hdr; const Elf_Phdr *phdr; vm_offset_t sva, eva; int i; bool found; /* Skip if BTI is not supported */ if ((elf_hwcap2 & HWCAP2_BTI) == 0) return; hdr = (const Elf_Ehdr *)imgp->image_header; phdr = (const Elf_Phdr *)(imgp->image_header + hdr->e_phoff); found = false; for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type == PT_NOTE && __elfN(parse_notes)(imgp, &gnu_property_note, GNU_ABI_VENDOR, &phdr[i], gnu_property_cb, NULL)) { found = true; break; } } if (!found) return; for (i = 0; i < hdr->e_phnum; i++) { if (phdr[i].p_type != PT_LOAD || phdr[i].p_memsz == 0) continue; sva = phdr[i].p_vaddr + imgp->et_dyn_addr; eva = sva + phdr[i].p_memsz; pmap_bti_set(vmspace_pmap(imgp->proc->p_vmspace), sva, eva); } } diff --git a/sys/arm64/include/md_var.h b/sys/arm64/include/md_var.h index f9aaaeba7306..da136ff091db 100644 --- a/sys/arm64/include/md_var.h +++ b/sys/arm64/include/md_var.h @@ -1,72 +1,76 @@ /*- * Copyright (c) 1995 Bruce D. Evans. * All rights reserved. * * 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. * 3. Neither the name of the author nor the names of contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * 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. * * from: FreeBSD: src/sys/i386/include/md_var.h,v 1.40 2001/07/12 */ #ifndef _MACHINE_MD_VAR_H_ #define _MACHINE_MD_VAR_H_ extern long Maxmem; extern char sigcode[]; extern int szsigcode; extern u_long elf_hwcap; extern u_long elf_hwcap2; +extern u_long elf_hwcap3; +extern u_long elf_hwcap4; extern u_long linux_elf_hwcap; extern u_long linux_elf_hwcap2; +extern u_long linux_elf_hwcap3; +extern u_long linux_elf_hwcap4; #ifdef COMPAT_FREEBSD32 extern u_long elf32_hwcap; extern u_long elf32_hwcap2; #endif struct dumperinfo; struct minidumpstate; int cpu_minidumpsys(struct dumperinfo *, const struct minidumpstate *); void generic_bs_fault(void) __asm(__STRING(generic_bs_fault)); void generic_bs_peek_1(void) __asm(__STRING(generic_bs_peek_1)); void generic_bs_peek_2(void) __asm(__STRING(generic_bs_peek_2)); void generic_bs_peek_4(void) __asm(__STRING(generic_bs_peek_4)); void generic_bs_peek_8(void) __asm(__STRING(generic_bs_peek_8)); void generic_bs_poke_1(void) __asm(__STRING(generic_bs_poke_1)); void generic_bs_poke_2(void) __asm(__STRING(generic_bs_poke_2)); void generic_bs_poke_4(void) __asm(__STRING(generic_bs_poke_4)); void generic_bs_poke_8(void) __asm(__STRING(generic_bs_poke_8)); #ifdef _MD_WANT_SWAPWORD /* * XXX These are implemented primarily for swp/swpb emulation at the moment, and * should be used sparingly with consideration -- they aren't implemented for * any other platform. If we use them anywhere else, at a minimum they need * KASAN/KMSAN interceptors added. */ int swapueword8(volatile uint8_t *base, uint8_t *val); int swapueword32(volatile uint32_t *base, uint32_t *val); #endif #endif /* !_MACHINE_MD_VAR_H_ */ diff --git a/sys/arm64/linux/linux.h b/sys/arm64/linux/linux.h index d612ba8e5d9e..00a70fabc54f 100644 --- a/sys/arm64/linux/linux.h +++ b/sys/arm64/linux/linux.h @@ -1,210 +1,210 @@ /*- * Copyright (c) 1994-1996 Søren Schmidt * Copyright (c) 2013 Dmitry Chagin * Copyright (c) 2018 Turing Robotic Industries Inc. * * 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. */ #ifndef _ARM64_LINUX_H_ #define _ARM64_LINUX_H_ #include #include #include #define LINUX_DTRACE linuxulator /* Provide a separate set of types for the Linux types */ typedef int32_t l_int; typedef int64_t l_long; typedef int16_t l_short; typedef uint32_t l_uint; typedef uint64_t l_ulong; typedef uint16_t l_ushort; typedef l_ulong l_uintptr_t; typedef l_long l_clock_t; typedef l_int l_daddr_t; typedef l_uint l_gid_t; typedef l_ushort l_gid16_t; /* XXX */ typedef l_uint l_uid_t; typedef l_ushort l_uid16_t; /* XXX */ typedef l_ulong l_ino_t; typedef l_int l_key_t; typedef l_long l_loff_t; typedef l_uint l_mode_t; typedef l_long l_off_t; typedef l_int l_pid_t; typedef l_ulong l_size_t; typedef l_long l_suseconds_t; typedef l_long l_time_t; typedef l_int l_timer_t; /* XXX */ typedef l_int l_mqd_t; typedef l_ulong l_fd_mask; #include typedef struct { l_int val[2]; } l_fsid_t; typedef struct { l_time_t tv_sec; l_suseconds_t tv_usec; } l_timeval; #define l_fd_set fd_set /* Miscellaneous */ -#define LINUX_AT_COUNT 21 /* Count of used aux entry types. +#define LINUX_AT_COUNT 23 /* Count of used aux entry types. * Keep this synchronized with * linux_copyout_auxargs() code. */ struct l___sysctl_args { l_uintptr_t name; l_int nlen; l_uintptr_t oldval; l_uintptr_t oldlenp; l_uintptr_t newval; l_uintptr_t newlen; l_ulong __spare[4]; }; /* Resource limits */ #define LINUX_RLIMIT_CPU 0 #define LINUX_RLIMIT_FSIZE 1 #define LINUX_RLIMIT_DATA 2 #define LINUX_RLIMIT_STACK 3 #define LINUX_RLIMIT_CORE 4 #define LINUX_RLIMIT_RSS 5 #define LINUX_RLIMIT_NPROC 6 #define LINUX_RLIMIT_NOFILE 7 #define LINUX_RLIMIT_MEMLOCK 8 #define LINUX_RLIMIT_AS 9 /* Address space limit */ #define LINUX_RLIM_NLIMITS 10 struct l_rlimit { l_ulong rlim_cur; l_ulong rlim_max; }; /* stat family of syscalls */ struct l_timespec { l_time_t tv_sec; l_long tv_nsec; }; #define LINUX_O_DIRECTORY 000040000 /* Must be a directory */ #define LINUX_O_NOFOLLOW 000100000 /* Do not follow links */ #define LINUX_O_DIRECT 000200000 /* Direct disk access hint */ #define LINUX_O_LARGEFILE 000400000 struct l_newstat { l_ulong st_dev; l_ino_t st_ino; l_uint st_mode; l_uint st_nlink; l_uid_t st_uid; l_gid_t st_gid; l_ulong st_rdev; l_ulong __st_pad1; l_off_t st_size; l_int st_blksize; l_int __st_pad2; l_long st_blocks; struct l_timespec st_atim; struct l_timespec st_mtim; struct l_timespec st_ctim; l_uint __unused1; l_uint __unused2; }; /* sigaction flags */ #define LINUX_SA_NOCLDSTOP 0x00000001 #define LINUX_SA_NOCLDWAIT 0x00000002 #define LINUX_SA_SIGINFO 0x00000004 #define LINUX_SA_RESTORER 0x04000000 #define LINUX_SA_ONSTACK 0x08000000 #define LINUX_SA_RESTART 0x10000000 #define LINUX_SA_INTERRUPT 0x20000000 /* XXX */ #define LINUX_SA_NOMASK 0x40000000 /* SA_NODEFER */ #define LINUX_SA_ONESHOT 0x80000000 /* SA_RESETHAND */ typedef void (*l_handler_t)(l_int); typedef struct { l_handler_t lsa_handler; l_ulong lsa_flags; l_uintptr_t lsa_restorer; l_sigset_t lsa_mask; } l_sigaction_t; /* XXX */ typedef struct { l_uintptr_t ss_sp; l_int ss_flags; l_size_t ss_size; } l_stack_t; union l_semun { l_int val; l_uintptr_t buf; l_uintptr_t array; l_uintptr_t __buf; l_uintptr_t __pad; }; #define linux_copyout_rusage(r, u) copyout(r, u, sizeof(*r)) struct linux_pt_regset { l_ulong x[31]; l_ulong sp; l_ulong pc; l_ulong cpsr; }; #ifdef _KERNEL struct reg; struct syscall_info; void bsd_to_linux_regset(const struct reg *b_reg, struct linux_pt_regset *l_regset); void linux_to_bsd_regset(struct reg *b_reg, const struct linux_pt_regset *l_regset); void linux_ptrace_get_syscall_info_machdep(const struct reg *reg, struct syscall_info *si); int linux_ptrace_getregs_machdep(struct thread *td, pid_t pid, struct linux_pt_regset *l_regset); int linux_ptrace_peekuser(struct thread *td, pid_t pid, void *addr, void *data); int linux_ptrace_pokeuser(struct thread *td, pid_t pid, void *addr, void *data); #endif /* _KERNEL */ #endif /* _ARM64_LINUX_H_ */ diff --git a/sys/arm64/linux/linux_sysvec.c b/sys/arm64/linux/linux_sysvec.c index 13c42e061094..084b7a11b01f 100644 --- a/sys/arm64/linux/linux_sysvec.c +++ b/sys/arm64/linux/linux_sysvec.c @@ -1,665 +1,667 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 1994-1996 Søren Schmidt * Copyright (c) 2018 Turing Robotic Industries Inc. * * 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. */ #define __ELF_WORD_SIZE 64 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef VFP #include #endif MODULE_VERSION(linux64elf, 1); #define LINUX_VDSOPAGE_SIZE PAGE_SIZE * 2 #define LINUX_VDSOPAGE (VM_MAXUSER_ADDRESS - \ LINUX_VDSOPAGE_SIZE) #define LINUX_SHAREDPAGE (LINUX_VDSOPAGE - PAGE_SIZE) /* * PAGE_SIZE - the size * of the native SHAREDPAGE */ #define LINUX_USRSTACK LINUX_SHAREDPAGE #define LINUX_PS_STRINGS (LINUX_USRSTACK - \ sizeof(struct ps_strings)) static int linux_szsigcode; 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]; extern const char *linux_syscallnames[]; SET_DECLARE(linux_ioctl_handler_set, struct linux_ioctl_handler); static void linux_vdso_install(const void *param); static void linux_vdso_deinstall(const 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); LINUX_VDSO_SYM_CHAR(linux_platform); LINUX_VDSO_SYM_INTPTR(kern_timekeep_base); LINUX_VDSO_SYM_INTPTR(__user_rt_sigreturn); static int linux_fetch_syscall_args(struct thread *td) { struct proc *p; struct syscall_args *sa; register_t *ap; p = td->td_proc; ap = td->td_frame->tf_x; sa = &td->td_sa; sa->code = td->td_frame->tf_x[8]; sa->original_code = sa->code; if (sa->code >= p->p_sysent->sv_size) sa->callp = &nosys_sysent; else sa->callp = &p->p_sysent->sv_table[sa->code]; if (sa->callp->sy_narg > nitems(sa->args)) panic("ARM64TODO: Could we have more than %zu args?", nitems(sa->args)); memcpy(sa->args, ap, nitems(sa->args) * sizeof(register_t)); td->td_retval[0] = 0; return (0); } static void linux_set_syscall_retval(struct thread *td, int error) { td->td_retval[1] = td->td_frame->tf_x[1]; cpu_set_syscall_retval(td, error); if (__predict_false(error != 0)) { if (error != ERESTART && error != EJUSTRETURN) td->td_frame->tf_x[0] = bsd_to_linux_errno(error); } } void linux64_arch_copyout_auxargs(struct image_params *imgp, Elf_Auxinfo **pos) { AUXARGS_ENTRY((*pos), LINUX_AT_SYSINFO_EHDR, linux_vdso_base); AUXARGS_ENTRY((*pos), LINUX_AT_HWCAP, *imgp->sysent->sv_hwcap); AUXARGS_ENTRY((*pos), LINUX_AT_HWCAP2, *imgp->sysent->sv_hwcap2); + AUXARGS_ENTRY((*pos), LINUX_AT_HWCAP3, *imgp->sysent->sv_hwcap3); + AUXARGS_ENTRY((*pos), LINUX_AT_HWCAP4, *imgp->sysent->sv_hwcap4); AUXARGS_ENTRY((*pos), LINUX_AT_PLATFORM, PTROUT(linux_platform)); } /* * Reset registers to default values on exec. */ static void linux_exec_setregs(struct thread *td, struct image_params *imgp, uintptr_t stack) { struct trapframe *regs = td->td_frame; struct pcb *pcb = td->td_pcb; memset(regs, 0, sizeof(*regs)); regs->tf_sp = stack; regs->tf_elr = imgp->entry_addr; pcb->pcb_tpidr_el0 = 0; pcb->pcb_tpidrro_el0 = 0; WRITE_SPECIALREG(tpidrro_el0, 0); WRITE_SPECIALREG(tpidr_el0, 0); #ifdef VFP vfp_reset_state(td, pcb); #endif /* * Clear debug register state. It is not applicable to the new process. */ bzero(&pcb->pcb_dbg_regs, sizeof(pcb->pcb_dbg_regs)); } static bool linux_parse_sigreturn_ctx(struct thread *td, struct l_sigcontext *sc) { struct l_fpsimd_context *fpsimd; struct _l_aarch64_ctx *ctx; int offset; offset = 0; while (1) { /* The offset must be 16 byte aligned */ if ((offset & 15) != 0) return (false); /* Check for buffer overflow of the ctx */ if ((offset + sizeof(*ctx)) > sizeof(sc->__reserved)) return (false); ctx = (struct _l_aarch64_ctx *)&sc->__reserved[offset]; /* Check for buffer overflow of the data */ if ((offset + ctx->size) > sizeof(sc->__reserved)) return (false); switch(ctx->magic) { case 0: if (ctx->size != 0) return (false); return (true); case L_ESR_MAGIC: /* Ignore */ break; #ifdef VFP case L_FPSIMD_MAGIC: fpsimd = (struct l_fpsimd_context *)ctx; /* * Discard any vfp state for the current thread, we * are about to override it. */ critical_enter(); vfp_discard(td); critical_exit(); td->td_pcb->pcb_fpustate.vfp_fpcr = fpsimd->fpcr; td->td_pcb->pcb_fpustate.vfp_fpsr = fpsimd->fpsr; memcpy(td->td_pcb->pcb_fpustate.vfp_regs, fpsimd->vregs, sizeof(fpsimd->vregs)); break; #endif default: return (false); } offset += ctx->size; } } int linux_rt_sigreturn(struct thread *td, struct linux_rt_sigreturn_args *args) { struct l_rt_sigframe *sf; struct l_sigframe *frame; struct trapframe *tf; sigset_t bmask; int error; sf = malloc(sizeof(*sf), M_LINUX, M_WAITOK | M_ZERO); tf = td->td_frame; frame = (struct l_sigframe *)tf->tf_sp; error = copyin((void *)&frame->sf, sf, sizeof(*sf)); if (error != 0) { free(sf, M_LINUX); return (error); } memcpy(tf->tf_x, sf->sf_uc.uc_sc.regs, sizeof(tf->tf_x)); tf->tf_lr = sf->sf_uc.uc_sc.regs[30]; tf->tf_sp = sf->sf_uc.uc_sc.sp; tf->tf_elr = sf->sf_uc.uc_sc.pc; if ((sf->sf_uc.uc_sc.pstate & PSR_M_MASK) != PSR_M_EL0t || (sf->sf_uc.uc_sc.pstate & PSR_AARCH32) != 0 || (sf->sf_uc.uc_sc.pstate & PSR_DAIF) != (td->td_frame->tf_spsr & PSR_DAIF)) goto einval; tf->tf_spsr = sf->sf_uc.uc_sc.pstate; if (!linux_parse_sigreturn_ctx(td, &sf->sf_uc.uc_sc)) goto einval; /* Restore signal mask. */ linux_to_bsd_sigset(&sf->sf_uc.uc_sigmask, &bmask); kern_sigprocmask(td, SIG_SETMASK, &bmask, NULL, 0); free(sf, M_LINUX); return (EJUSTRETURN); einval: free(sf, M_LINUX); return (EINVAL); } static void linux_rt_sendsig(sig_t catcher, ksiginfo_t *ksi, sigset_t *mask) { struct thread *td; struct proc *p; struct trapframe *tf; struct l_sigframe *fp, *frame; struct l_fpsimd_context *fpsimd; struct l_esr_context *esr; l_stack_t uc_stack; ucontext_t uc; uint8_t *scr; struct sigacts *psp; int onstack, sig, issiginfo; td = curthread; p = td->td_proc; PROC_LOCK_ASSERT(p, MA_OWNED); sig = ksi->ksi_signo; psp = p->p_sigacts; mtx_assert(&psp->ps_mtx, MA_OWNED); tf = td->td_frame; onstack = sigonstack(tf->tf_sp); issiginfo = SIGISMEMBER(psp->ps_siginfo, sig); CTR4(KTR_SIG, "sendsig: td=%p (%s) catcher=%p sig=%d", td, p->p_comm, catcher, sig); /* Allocate and validate space for the signal handler context. */ if ((td->td_pflags & TDP_ALTSTACK) != 0 && !onstack && SIGISMEMBER(psp->ps_sigonstack, sig)) { fp = (struct l_sigframe *)((uintptr_t)td->td_sigstk.ss_sp + td->td_sigstk.ss_size); #if defined(COMPAT_43) td->td_sigstk.ss_flags |= SS_ONSTACK; #endif } else { fp = (struct l_sigframe *)td->td_frame->tf_sp; } /* Make room, keeping the stack aligned */ fp--; fp = (struct l_sigframe *)STACKALIGN(fp); get_mcontext(td, &uc.uc_mcontext, 0); uc.uc_sigmask = *mask; uc_stack.ss_sp = PTROUT(td->td_sigstk.ss_sp); uc_stack.ss_size = td->td_sigstk.ss_size; uc_stack.ss_flags = (td->td_pflags & TDP_ALTSTACK) != 0 ? (onstack ? LINUX_SS_ONSTACK : 0) : LINUX_SS_DISABLE; mtx_unlock(&psp->ps_mtx); PROC_UNLOCK(td->td_proc); /* Fill in the frame to copy out */ frame = malloc(sizeof(*frame), M_LINUX, M_WAITOK | M_ZERO); memcpy(&frame->sf.sf_uc.uc_sc.regs, tf->tf_x, sizeof(tf->tf_x)); frame->sf.sf_uc.uc_sc.regs[30] = tf->tf_lr; frame->sf.sf_uc.uc_sc.sp = tf->tf_sp; frame->sf.sf_uc.uc_sc.pc = tf->tf_elr; frame->sf.sf_uc.uc_sc.pstate = tf->tf_spsr; frame->sf.sf_uc.uc_sc.fault_address = (register_t)ksi->ksi_addr; /* Stack frame for unwinding */ frame->fp = tf->tf_x[29]; frame->lr = tf->tf_elr; /* Translate the signal. */ sig = bsd_to_linux_signal(sig); siginfo_to_lsiginfo(&ksi->ksi_info, &frame->sf.sf_si, sig); bsd_to_linux_sigset(mask, &frame->sf.sf_uc.uc_sigmask); /* * Prepare fpsimd & esr. Does not check sizes, as * __reserved is big enougth. */ scr = (uint8_t *)&frame->sf.sf_uc.uc_sc.__reserved; #ifdef VFP fpsimd = (struct l_fpsimd_context *) scr; fpsimd->head.magic = L_FPSIMD_MAGIC; fpsimd->head.size = sizeof(struct l_fpsimd_context); fpsimd->fpsr = uc.uc_mcontext.mc_fpregs.fp_sr; fpsimd->fpcr = uc.uc_mcontext.mc_fpregs.fp_cr; memcpy(fpsimd->vregs, &uc.uc_mcontext.mc_fpregs.fp_q, sizeof(uc.uc_mcontext.mc_fpregs.fp_q)); scr += roundup(sizeof(struct l_fpsimd_context), 16); #endif if (ksi->ksi_addr != 0) { esr = (struct l_esr_context *) scr; esr->head.magic = L_ESR_MAGIC; esr->head.size = sizeof(struct l_esr_context); esr->esr = tf->tf_esr; } memcpy(&frame->sf.sf_uc.uc_stack, &uc_stack, sizeof(uc_stack)); /* Copy the sigframe out to the user's stack. */ if (copyout(frame, fp, sizeof(*fp)) != 0) { /* Process has trashed its stack. Kill it. */ free(frame, M_LINUX); CTR2(KTR_SIG, "sendsig: sigexit td=%p fp=%p", td, fp); PROC_LOCK(p); sigexit(td, SIGILL); } free(frame, M_LINUX); tf->tf_x[0]= sig; if (issiginfo) { tf->tf_x[1] = (register_t)&fp->sf.sf_si; tf->tf_x[2] = (register_t)&fp->sf.sf_uc; } else { tf->tf_x[1] = 0; tf->tf_x[2] = 0; } tf->tf_x[29] = (register_t)&fp->fp; tf->tf_elr = (register_t)catcher; tf->tf_sp = (register_t)fp; tf->tf_lr = (register_t)__user_rt_sigreturn; CTR3(KTR_SIG, "sendsig: return td=%p pc=%#x sp=%#x", td, tf->tf_elr, tf->tf_sp); PROC_LOCK(p); mtx_lock(&psp->ps_mtx); } struct sysentvec elf_linux_sysvec = { .sv_size = LINUX_SYS_MAXSYSCALL, .sv_table = linux_sysent, .sv_fixup = __elfN(freebsd_fixup), .sv_sendsig = linux_rt_sendsig, .sv_sigcode = &_binary_linux_vdso_so_o_start, .sv_szsigcode = &linux_szsigcode, .sv_name = "Linux ELF64", .sv_coredump = elf64_coredump, .sv_elf_core_osabi = ELFOSABI_NONE, .sv_elf_core_abi_vendor = LINUX_ABI_VENDOR, .sv_elf_core_prepare_notes = linux64_prepare_notes, .sv_minsigstksz = LINUX_MINSIGSTKSZ, .sv_minuser = VM_MIN_ADDRESS, .sv_maxuser = VM_MAXUSER_ADDRESS, .sv_usrstack = LINUX_USRSTACK, .sv_psstrings = LINUX_PS_STRINGS, .sv_psstringssz = sizeof(struct ps_strings), .sv_stackprot = VM_PROT_READ | VM_PROT_WRITE, .sv_copyout_auxargs = __linuxN(copyout_auxargs), .sv_copyout_strings = __linuxN(copyout_strings), .sv_setregs = linux_exec_setregs, .sv_fixlimit = NULL, .sv_maxssiz = NULL, .sv_flags = SV_ABI_LINUX | SV_LP64 | SV_SHP | SV_SIG_DISCIGN | SV_SIG_WAITNDQ | SV_TIMEKEEP, .sv_set_syscall_retval = linux_set_syscall_retval, .sv_fetch_syscall_args = linux_fetch_syscall_args, .sv_syscallnames = linux_syscallnames, .sv_shared_page_base = LINUX_SHAREDPAGE, .sv_shared_page_len = PAGE_SIZE, .sv_schedtail = linux_schedtail, .sv_thread_detach = linux_thread_detach, .sv_trap = NULL, .sv_hwcap = &linux_elf_hwcap, .sv_hwcap2 = &linux_elf_hwcap2, - .sv_hwcap3 = NULL, - .sv_hwcap4 = NULL, + .sv_hwcap3 = &linux_elf_hwcap3, + .sv_hwcap4 = &linux_elf_hwcap4, .sv_onexec = linux_on_exec_vmspace, .sv_onexit = linux_on_exit, .sv_ontdexit = linux_thread_dtor, .sv_setid_allowed = &linux_setid_allowed_query, }; static int linux_on_exec_vmspace(struct proc *p, struct image_params *imgp) { int error; error = linux_map_vdso(p, linux_vdso_obj, linux_vdso_base, LINUX_VDSOPAGE_SIZE, imgp); if (error == 0) error = linux_on_exec(p, imgp); return (error); } /* * linux_vdso_install() and linux_exec_sysvec_init() must be called * after exec_sysvec_init() which is SI_SUB_EXEC (SI_ORDER_ANY). */ static void linux_exec_sysvec_init(void *param) { l_uintptr_t *ktimekeep_base; struct sysentvec *sv; ptrdiff_t tkoff; sv = param; /* Fill timekeep_base */ exec_sysvec_init(sv); tkoff = kern_timekeep_base - linux_vdso_base; ktimekeep_base = (l_uintptr_t *)(linux_vdso_mapping + tkoff); *ktimekeep_base = sv->sv_shared_page_base + sv->sv_timekeep_offset; } SYSINIT(elf_linux_exec_sysvec_init, SI_SUB_EXEC + 1, SI_ORDER_ANY, linux_exec_sysvec_init, &elf_linux_sysvec); static void linux_vdso_install(const void *param) { char *vdso_start = &_binary_linux_vdso_so_o_start; char *vdso_end = &_binary_linux_vdso_so_o_end; linux_szsigcode = vdso_end - vdso_start; MPASS(linux_szsigcode <= LINUX_VDSOPAGE_SIZE); linux_vdso_base = LINUX_VDSOPAGE; __elfN(linux_vdso_fixup)(vdso_start, linux_vdso_base); linux_vdso_obj = __elfN(linux_shared_page_init) (&linux_vdso_mapping, LINUX_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 + 1, SI_ORDER_FIRST, linux_vdso_install, NULL); static void linux_vdso_deinstall(const void *param) { __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) { Elf_Size rtype, symidx; const Elf_Rela *rela; const Elf_Shdr *shdr; const Elf_Ehdr *ehdr; Elf_Addr *where; Elf_Addr addr, addend; int i, relacnt; 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 Aarch64 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 (i = 0; i < relacnt; i++, 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_AARCH64_NONE: /* none */ break; case R_AARCH64_RELATIVE: /* B + A */ addr = (Elf_Addr)(mapping + addend); if (*where != addr) *where = addr; break; default: printf("Linux Aarch64 vDSO: unexpected relocation type %ld, " "symbol index %ld\n", rtype, symidx); } } } static Elf_Brandnote linux64_brandnote = { .hdr.n_namesz = sizeof(GNU_ABI_VENDOR), .hdr.n_descsz = 16, .hdr.n_type = 1, .vendor = GNU_ABI_VENDOR, .flags = BN_TRANSLATE_OSREL, .trans_osrel = linux_trans_osrel }; static Elf64_Brandinfo linux_glibc2brand = { .brand = ELFOSABI_LINUX, .machine = EM_AARCH64, .compat_3_brand = "Linux", .interp_path = "/lib64/ld-linux-x86-64.so.2", .sysvec = &elf_linux_sysvec, .interp_newpath = NULL, .brand_note = &linux64_brandnote, .flags = BI_CAN_EXEC_DYN | BI_BRAND_NOTE }; Elf64_Brandinfo *linux_brandlist[] = { &linux_glibc2brand, NULL }; static int linux64_elf_modevent(module_t mod, int type, void *data) { Elf64_Brandinfo **brandinfo; struct linux_ioctl_handler**lihp; int error; error = 0; switch(type) { case MOD_LOAD: for (brandinfo = &linux_brandlist[0]; *brandinfo != NULL; ++brandinfo) if (elf64_insert_brand_entry(*brandinfo) < 0) error = EINVAL; if (error == 0) { SET_FOREACH(lihp, linux_ioctl_handler_set) linux_ioctl_register_handler(*lihp); stclohz = (stathz ? stathz : hz); if (bootverbose) printf("Linux arm64 ELF exec handler installed\n"); } break; case MOD_UNLOAD: for (brandinfo = &linux_brandlist[0]; *brandinfo != NULL; ++brandinfo) if (elf64_brand_inuse(*brandinfo)) error = EBUSY; if (error == 0) { for (brandinfo = &linux_brandlist[0]; *brandinfo != NULL; ++brandinfo) if (elf64_remove_brand_entry(*brandinfo) < 0) error = EINVAL; } if (error == 0) { SET_FOREACH(lihp, linux_ioctl_handler_set) linux_ioctl_unregister_handler(*lihp); if (bootverbose) printf("Linux arm64 ELF exec handler removed\n"); } else printf("Could not deinstall Linux arm64 ELF interpreter entry\n"); break; default: return (EOPNOTSUPP); } return (error); } static moduledata_t linux64_elf_mod = { "linux64elf", linux64_elf_modevent, 0 }; DECLARE_MODULE_TIED(linux64elf, linux64_elf_mod, SI_SUB_EXEC, SI_ORDER_ANY); MODULE_DEPEND(linux64elf, linux_common, 1, 1, 1); FEATURE(linux64, "AArch64 Linux 64bit support");