1a665ed98SKonstantin Belousov /*- 28a36da99SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 38a36da99SPedro F. Giffuni * 4a665ed98SKonstantin Belousov * Copyright (c) 2010, 2012 Konstantin Belousov <kib@FreeBSD.org> 55e27d793SKonstantin Belousov * Copyright (c) 2015 The FreeBSD Foundation 6a665ed98SKonstantin Belousov * All rights reserved. 7a665ed98SKonstantin Belousov * 85e27d793SKonstantin Belousov * Portions of this software were developed by Konstantin Belousov 95e27d793SKonstantin Belousov * under sponsorship from the FreeBSD Foundation. 105e27d793SKonstantin Belousov * 11a665ed98SKonstantin Belousov * Redistribution and use in source and binary forms, with or without 12a665ed98SKonstantin Belousov * modification, are permitted provided that the following conditions 13a665ed98SKonstantin Belousov * are met: 14a665ed98SKonstantin Belousov * 1. Redistributions of source code must retain the above copyright 15a665ed98SKonstantin Belousov * notice, this list of conditions and the following disclaimer. 16a665ed98SKonstantin Belousov * 2. Redistributions in binary form must reproduce the above copyright 17a665ed98SKonstantin Belousov * notice, this list of conditions and the following disclaimer in the 18a665ed98SKonstantin Belousov * documentation and/or other materials provided with the distribution. 19a665ed98SKonstantin Belousov * 20a665ed98SKonstantin Belousov * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21a665ed98SKonstantin Belousov * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22a665ed98SKonstantin Belousov * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23a665ed98SKonstantin Belousov * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24a665ed98SKonstantin Belousov * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25a665ed98SKonstantin Belousov * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26a665ed98SKonstantin Belousov * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27a665ed98SKonstantin Belousov * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28a665ed98SKonstantin Belousov * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29a665ed98SKonstantin Belousov * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30a665ed98SKonstantin Belousov * SUCH DAMAGE. 31a665ed98SKonstantin Belousov */ 32a665ed98SKonstantin Belousov 33a665ed98SKonstantin Belousov #include <sys/cdefs.h> 34a665ed98SKonstantin Belousov __FBSDID("$FreeBSD$"); 35a665ed98SKonstantin Belousov 36a665ed98SKonstantin Belousov #include "opt_vm.h" 37a665ed98SKonstantin Belousov 38a665ed98SKonstantin Belousov #include <sys/param.h> 39a665ed98SKonstantin Belousov #include <sys/systm.h> 40a665ed98SKonstantin Belousov #include <sys/kernel.h> 41a665ed98SKonstantin Belousov #include <sys/lock.h> 425e27d793SKonstantin Belousov #include <sys/malloc.h> 4389f6b863SAttilio Rao #include <sys/rwlock.h> 44a665ed98SKonstantin Belousov #include <sys/sysent.h> 45a665ed98SKonstantin Belousov #include <sys/sysctl.h> 46a665ed98SKonstantin Belousov #include <sys/vdso.h> 47a665ed98SKonstantin Belousov 48a665ed98SKonstantin Belousov #include <vm/vm.h> 49a665ed98SKonstantin Belousov #include <vm/vm_param.h> 50a665ed98SKonstantin Belousov #include <vm/pmap.h> 51a665ed98SKonstantin Belousov #include <vm/vm_extern.h> 52a665ed98SKonstantin Belousov #include <vm/vm_kern.h> 53a665ed98SKonstantin Belousov #include <vm/vm_map.h> 54a665ed98SKonstantin Belousov #include <vm/vm_object.h> 55a665ed98SKonstantin Belousov #include <vm/vm_page.h> 56a665ed98SKonstantin Belousov #include <vm/vm_pager.h> 57a665ed98SKonstantin Belousov 58a665ed98SKonstantin Belousov static struct sx shared_page_alloc_sx; 59a665ed98SKonstantin Belousov static vm_object_t shared_page_obj; 60a665ed98SKonstantin Belousov static int shared_page_free; 61a665ed98SKonstantin Belousov char *shared_page_mapping; 62a665ed98SKonstantin Belousov 63a665ed98SKonstantin Belousov void 64a665ed98SKonstantin Belousov shared_page_write(int base, int size, const void *data) 65a665ed98SKonstantin Belousov { 66a665ed98SKonstantin Belousov 67a665ed98SKonstantin Belousov bcopy(data, shared_page_mapping + base, size); 68a665ed98SKonstantin Belousov } 69a665ed98SKonstantin Belousov 70a665ed98SKonstantin Belousov static int 71a665ed98SKonstantin Belousov shared_page_alloc_locked(int size, int align) 72a665ed98SKonstantin Belousov { 73a665ed98SKonstantin Belousov int res; 74a665ed98SKonstantin Belousov 75a665ed98SKonstantin Belousov res = roundup(shared_page_free, align); 76a665ed98SKonstantin Belousov if (res + size >= IDX_TO_OFF(shared_page_obj->size)) 77a665ed98SKonstantin Belousov res = -1; 78a665ed98SKonstantin Belousov else 79a665ed98SKonstantin Belousov shared_page_free = res + size; 80a665ed98SKonstantin Belousov return (res); 81a665ed98SKonstantin Belousov } 82a665ed98SKonstantin Belousov 83a665ed98SKonstantin Belousov int 84a665ed98SKonstantin Belousov shared_page_alloc(int size, int align) 85a665ed98SKonstantin Belousov { 86a665ed98SKonstantin Belousov int res; 87a665ed98SKonstantin Belousov 88a665ed98SKonstantin Belousov sx_xlock(&shared_page_alloc_sx); 89a665ed98SKonstantin Belousov res = shared_page_alloc_locked(size, align); 90a665ed98SKonstantin Belousov sx_xunlock(&shared_page_alloc_sx); 91a665ed98SKonstantin Belousov return (res); 92a665ed98SKonstantin Belousov } 93a665ed98SKonstantin Belousov 94a665ed98SKonstantin Belousov int 95a665ed98SKonstantin Belousov shared_page_fill(int size, int align, const void *data) 96a665ed98SKonstantin Belousov { 97a665ed98SKonstantin Belousov int res; 98a665ed98SKonstantin Belousov 99a665ed98SKonstantin Belousov sx_xlock(&shared_page_alloc_sx); 100a665ed98SKonstantin Belousov res = shared_page_alloc_locked(size, align); 101a665ed98SKonstantin Belousov if (res != -1) 102a665ed98SKonstantin Belousov shared_page_write(res, size, data); 103a665ed98SKonstantin Belousov sx_xunlock(&shared_page_alloc_sx); 104a665ed98SKonstantin Belousov return (res); 105a665ed98SKonstantin Belousov } 106a665ed98SKonstantin Belousov 107a665ed98SKonstantin Belousov static void 108a665ed98SKonstantin Belousov shared_page_init(void *dummy __unused) 109a665ed98SKonstantin Belousov { 110a665ed98SKonstantin Belousov vm_page_t m; 111a665ed98SKonstantin Belousov vm_offset_t addr; 112a665ed98SKonstantin Belousov 113a665ed98SKonstantin Belousov sx_init(&shared_page_alloc_sx, "shpsx"); 114a665ed98SKonstantin Belousov shared_page_obj = vm_pager_allocate(OBJT_PHYS, 0, PAGE_SIZE, 115a665ed98SKonstantin Belousov VM_PROT_DEFAULT, 0, NULL); 11689f6b863SAttilio Rao VM_OBJECT_WLOCK(shared_page_obj); 1174504268aSJeff Roberson m = vm_page_grab(shared_page_obj, 0, VM_ALLOC_ZERO); 11889f6b863SAttilio Rao VM_OBJECT_WUNLOCK(shared_page_obj); 1194504268aSJeff Roberson vm_page_valid(m); 1204504268aSJeff Roberson vm_page_xunbusy(m); 1215df87b21SJeff Roberson addr = kva_alloc(PAGE_SIZE); 122a665ed98SKonstantin Belousov pmap_qenter(addr, &m, 1); 123a665ed98SKonstantin Belousov shared_page_mapping = (char *)addr; 124a665ed98SKonstantin Belousov } 125a665ed98SKonstantin Belousov 126a665ed98SKonstantin Belousov SYSINIT(shp, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t)shared_page_init, 127a665ed98SKonstantin Belousov NULL); 128a665ed98SKonstantin Belousov 12935dfc644SKonstantin Belousov /* 13035dfc644SKonstantin Belousov * Push the timehands update to the shared page. 13135dfc644SKonstantin Belousov * 13235dfc644SKonstantin Belousov * The lockless update scheme is similar to the one used to update the 13335dfc644SKonstantin Belousov * in-kernel timehands, see sys/kern/kern_tc.c:tc_windup() (which 13435dfc644SKonstantin Belousov * calls us after the timehands are updated). 13535dfc644SKonstantin Belousov */ 136a665ed98SKonstantin Belousov static void 1375e27d793SKonstantin Belousov timehands_update(struct vdso_sv_tk *svtk) 138a665ed98SKonstantin Belousov { 139a665ed98SKonstantin Belousov struct vdso_timehands th; 140a665ed98SKonstantin Belousov struct vdso_timekeep *tk; 141a665ed98SKonstantin Belousov uint32_t enabled, idx; 142a665ed98SKonstantin Belousov 143a665ed98SKonstantin Belousov enabled = tc_fill_vdso_timehands(&th); 14435dfc644SKonstantin Belousov th.th_gen = 0; 1455e27d793SKonstantin Belousov idx = svtk->sv_timekeep_curr; 146a665ed98SKonstantin Belousov if (++idx >= VDSO_TH_NUM) 147a665ed98SKonstantin Belousov idx = 0; 1485e27d793SKonstantin Belousov svtk->sv_timekeep_curr = idx; 1495e27d793SKonstantin Belousov if (++svtk->sv_timekeep_gen == 0) 1505e27d793SKonstantin Belousov svtk->sv_timekeep_gen = 1; 15135dfc644SKonstantin Belousov 15235dfc644SKonstantin Belousov tk = (struct vdso_timekeep *)(shared_page_mapping + 1535e27d793SKonstantin Belousov svtk->sv_timekeep_off); 15435dfc644SKonstantin Belousov tk->tk_th[idx].th_gen = 0; 15535dfc644SKonstantin Belousov atomic_thread_fence_rel(); 156a665ed98SKonstantin Belousov if (enabled) 157a665ed98SKonstantin Belousov tk->tk_th[idx] = th; 1585e27d793SKonstantin Belousov atomic_store_rel_32(&tk->tk_th[idx].th_gen, svtk->sv_timekeep_gen); 15935dfc644SKonstantin Belousov atomic_store_rel_32(&tk->tk_current, idx); 16035dfc644SKonstantin Belousov 16135dfc644SKonstantin Belousov /* 16235dfc644SKonstantin Belousov * The ordering of the assignment to tk_enabled relative to 16335dfc644SKonstantin Belousov * the update of the vdso_timehands is not important. 16435dfc644SKonstantin Belousov */ 16535dfc644SKonstantin Belousov tk->tk_enabled = enabled; 166a665ed98SKonstantin Belousov } 167a665ed98SKonstantin Belousov 168a665ed98SKonstantin Belousov #ifdef COMPAT_FREEBSD32 169a665ed98SKonstantin Belousov static void 1705e27d793SKonstantin Belousov timehands_update32(struct vdso_sv_tk *svtk) 171a665ed98SKonstantin Belousov { 172a665ed98SKonstantin Belousov struct vdso_timehands32 th; 17335dfc644SKonstantin Belousov struct vdso_timekeep32 *tk; 174a665ed98SKonstantin Belousov uint32_t enabled, idx; 175a665ed98SKonstantin Belousov 176a665ed98SKonstantin Belousov enabled = tc_fill_vdso_timehands32(&th); 17735dfc644SKonstantin Belousov th.th_gen = 0; 1785e27d793SKonstantin Belousov idx = svtk->sv_timekeep_curr; 179a665ed98SKonstantin Belousov if (++idx >= VDSO_TH_NUM) 180a665ed98SKonstantin Belousov idx = 0; 1815e27d793SKonstantin Belousov svtk->sv_timekeep_curr = idx; 1825e27d793SKonstantin Belousov if (++svtk->sv_timekeep_gen == 0) 1835e27d793SKonstantin Belousov svtk->sv_timekeep_gen = 1; 18435dfc644SKonstantin Belousov 18535dfc644SKonstantin Belousov tk = (struct vdso_timekeep32 *)(shared_page_mapping + 1865e27d793SKonstantin Belousov svtk->sv_timekeep_off); 18735dfc644SKonstantin Belousov tk->tk_th[idx].th_gen = 0; 18835dfc644SKonstantin Belousov atomic_thread_fence_rel(); 189a665ed98SKonstantin Belousov if (enabled) 190a665ed98SKonstantin Belousov tk->tk_th[idx] = th; 1915e27d793SKonstantin Belousov atomic_store_rel_32(&tk->tk_th[idx].th_gen, svtk->sv_timekeep_gen); 19235dfc644SKonstantin Belousov atomic_store_rel_32(&tk->tk_current, idx); 19335dfc644SKonstantin Belousov tk->tk_enabled = enabled; 194a665ed98SKonstantin Belousov } 195a665ed98SKonstantin Belousov #endif 196a665ed98SKonstantin Belousov 197a665ed98SKonstantin Belousov /* 198a665ed98SKonstantin Belousov * This is hackish, but easiest way to avoid creating list structures 199a665ed98SKonstantin Belousov * that needs to be iterated over from the hardclock interrupt 200a665ed98SKonstantin Belousov * context. 201a665ed98SKonstantin Belousov */ 2025e27d793SKonstantin Belousov static struct vdso_sv_tk *host_svtk; 203a665ed98SKonstantin Belousov #ifdef COMPAT_FREEBSD32 2045e27d793SKonstantin Belousov static struct vdso_sv_tk *compat32_svtk; 205a665ed98SKonstantin Belousov #endif 206a665ed98SKonstantin Belousov 207a665ed98SKonstantin Belousov void 208a665ed98SKonstantin Belousov timekeep_push_vdso(void) 209a665ed98SKonstantin Belousov { 210a665ed98SKonstantin Belousov 2115e27d793SKonstantin Belousov if (host_svtk != NULL) 2125e27d793SKonstantin Belousov timehands_update(host_svtk); 213a665ed98SKonstantin Belousov #ifdef COMPAT_FREEBSD32 2145e27d793SKonstantin Belousov if (compat32_svtk != NULL) 2155e27d793SKonstantin Belousov timehands_update32(compat32_svtk); 216a665ed98SKonstantin Belousov #endif 217a665ed98SKonstantin Belousov } 218a665ed98SKonstantin Belousov 2195e27d793SKonstantin Belousov struct vdso_sv_tk * 2205e27d793SKonstantin Belousov alloc_sv_tk(void) 2215e27d793SKonstantin Belousov { 2225e27d793SKonstantin Belousov struct vdso_sv_tk *svtk; 2235e27d793SKonstantin Belousov int tk_base; 2245e27d793SKonstantin Belousov uint32_t tk_ver; 2255e27d793SKonstantin Belousov 2265e27d793SKonstantin Belousov tk_ver = VDSO_TK_VER_CURR; 2275e27d793SKonstantin Belousov svtk = malloc(sizeof(struct vdso_sv_tk), M_TEMP, M_WAITOK | M_ZERO); 2285e27d793SKonstantin Belousov tk_base = shared_page_alloc(sizeof(struct vdso_timekeep) + 2295e27d793SKonstantin Belousov sizeof(struct vdso_timehands) * VDSO_TH_NUM, 16); 2305e27d793SKonstantin Belousov KASSERT(tk_base != -1, ("tk_base -1 for native")); 2315e27d793SKonstantin Belousov shared_page_write(tk_base + offsetof(struct vdso_timekeep, tk_ver), 2325e27d793SKonstantin Belousov sizeof(uint32_t), &tk_ver); 2335e27d793SKonstantin Belousov svtk->sv_timekeep_off = tk_base; 2345e27d793SKonstantin Belousov timekeep_push_vdso(); 2355e27d793SKonstantin Belousov return (svtk); 2365e27d793SKonstantin Belousov } 2375e27d793SKonstantin Belousov 2385e27d793SKonstantin Belousov #ifdef COMPAT_FREEBSD32 2395e27d793SKonstantin Belousov struct vdso_sv_tk * 2405e27d793SKonstantin Belousov alloc_sv_tk_compat32(void) 2415e27d793SKonstantin Belousov { 2425e27d793SKonstantin Belousov struct vdso_sv_tk *svtk; 2435e27d793SKonstantin Belousov int tk_base; 2445e27d793SKonstantin Belousov uint32_t tk_ver; 2455e27d793SKonstantin Belousov 2465e27d793SKonstantin Belousov svtk = malloc(sizeof(struct vdso_sv_tk), M_TEMP, M_WAITOK | M_ZERO); 2475e27d793SKonstantin Belousov tk_ver = VDSO_TK_VER_CURR; 2485e27d793SKonstantin Belousov tk_base = shared_page_alloc(sizeof(struct vdso_timekeep32) + 2495e27d793SKonstantin Belousov sizeof(struct vdso_timehands32) * VDSO_TH_NUM, 16); 2505e27d793SKonstantin Belousov KASSERT(tk_base != -1, ("tk_base -1 for 32bit")); 2515e27d793SKonstantin Belousov shared_page_write(tk_base + offsetof(struct vdso_timekeep32, 2525e27d793SKonstantin Belousov tk_ver), sizeof(uint32_t), &tk_ver); 2535e27d793SKonstantin Belousov svtk->sv_timekeep_off = tk_base; 2545e27d793SKonstantin Belousov timekeep_push_vdso(); 2555e27d793SKonstantin Belousov return (svtk); 2565e27d793SKonstantin Belousov } 2575e27d793SKonstantin Belousov #endif 2585e27d793SKonstantin Belousov 259a665ed98SKonstantin Belousov void 260a665ed98SKonstantin Belousov exec_sysvec_init(void *param) 261a665ed98SKonstantin Belousov { 262a665ed98SKonstantin Belousov struct sysentvec *sv; 263a665ed98SKonstantin Belousov 264a665ed98SKonstantin Belousov sv = (struct sysentvec *)param; 265a665ed98SKonstantin Belousov if ((sv->sv_flags & SV_SHP) == 0) 266a665ed98SKonstantin Belousov return; 267a665ed98SKonstantin Belousov sv->sv_shared_page_obj = shared_page_obj; 268a665ed98SKonstantin Belousov sv->sv_sigcode_base = sv->sv_shared_page_base + 269a665ed98SKonstantin Belousov shared_page_fill(*(sv->sv_szsigcode), 16, sv->sv_sigcode); 270a665ed98SKonstantin Belousov if ((sv->sv_flags & SV_ABI_MASK) != SV_ABI_FREEBSD) 271a665ed98SKonstantin Belousov return; 2725e27d793SKonstantin Belousov if ((sv->sv_flags & SV_TIMEKEEP) != 0) { 273a665ed98SKonstantin Belousov #ifdef COMPAT_FREEBSD32 274a665ed98SKonstantin Belousov if ((sv->sv_flags & SV_ILP32) != 0) { 2755e27d793SKonstantin Belousov KASSERT(compat32_svtk == NULL, 2765e27d793SKonstantin Belousov ("Compat32 already registered")); 2775e27d793SKonstantin Belousov compat32_svtk = alloc_sv_tk_compat32(); 2785e27d793SKonstantin Belousov sv->sv_timekeep_base = sv->sv_shared_page_base + 2795e27d793SKonstantin Belousov compat32_svtk->sv_timekeep_off; 280a665ed98SKonstantin Belousov } else { 281a665ed98SKonstantin Belousov #endif 2825e27d793SKonstantin Belousov KASSERT(host_svtk == NULL, ("Host already registered")); 2835e27d793SKonstantin Belousov host_svtk = alloc_sv_tk(); 2845e27d793SKonstantin Belousov sv->sv_timekeep_base = sv->sv_shared_page_base + 2855e27d793SKonstantin Belousov host_svtk->sv_timekeep_off; 286a665ed98SKonstantin Belousov #ifdef COMPAT_FREEBSD32 287a665ed98SKonstantin Belousov } 288a665ed98SKonstantin Belousov #endif 2895e27d793SKonstantin Belousov } 290a665ed98SKonstantin Belousov } 291*2b313da3SKonstantin Belousov 292*2b313da3SKonstantin Belousov void 293*2b313da3SKonstantin Belousov exec_sysvec_init_secondary(struct sysentvec *sv, struct sysentvec *sv2) 294*2b313da3SKonstantin Belousov { 295*2b313da3SKonstantin Belousov MPASS((sv2->sv_flags & SV_ABI_MASK) == (sv->sv_flags & SV_ABI_MASK)); 296*2b313da3SKonstantin Belousov MPASS((sv2->sv_flags & SV_TIMEKEEP) == (sv->sv_flags & SV_TIMEKEEP)); 297*2b313da3SKonstantin Belousov MPASS((sv2->sv_flags & SV_SHP) != 0 && (sv->sv_flags & SV_SHP) != 0); 298*2b313da3SKonstantin Belousov 299*2b313da3SKonstantin Belousov sv2->sv_shared_page_obj = sv->sv_shared_page_obj; 300*2b313da3SKonstantin Belousov sv2->sv_sigcode_base = sv2->sv_shared_page_base + 301*2b313da3SKonstantin Belousov (sv->sv_sigcode_base - sv->sv_shared_page_base); 302*2b313da3SKonstantin Belousov if ((sv2->sv_flags & SV_ABI_MASK) != SV_ABI_FREEBSD) 303*2b313da3SKonstantin Belousov return; 304*2b313da3SKonstantin Belousov if ((sv2->sv_flags & SV_TIMEKEEP) != 0) { 305*2b313da3SKonstantin Belousov sv2->sv_timekeep_base = sv2->sv_shared_page_base + 306*2b313da3SKonstantin Belousov (sv->sv_timekeep_base - sv->sv_shared_page_base); 307*2b313da3SKonstantin Belousov } 308*2b313da3SKonstantin Belousov } 309