1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2010, 2012 Konstantin Belousov <kib@FreeBSD.org> 5 * Copyright (c) 2015 The FreeBSD Foundation 6 * All rights reserved. 7 * 8 * Portions of this software were developed by Konstantin Belousov 9 * under sponsorship from the FreeBSD Foundation. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30 * SUCH DAMAGE. 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include "opt_vm.h" 37 38 #include <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/kernel.h> 41 #include <sys/lock.h> 42 #include <sys/malloc.h> 43 #include <sys/rwlock.h> 44 #include <sys/sysent.h> 45 #include <sys/sysctl.h> 46 #include <sys/vdso.h> 47 48 #include <vm/vm.h> 49 #include <vm/vm_param.h> 50 #include <vm/pmap.h> 51 #include <vm/vm_extern.h> 52 #include <vm/vm_kern.h> 53 #include <vm/vm_map.h> 54 #include <vm/vm_object.h> 55 #include <vm/vm_page.h> 56 #include <vm/vm_pager.h> 57 58 static struct sx shared_page_alloc_sx; 59 static vm_object_t shared_page_obj; 60 static int shared_page_free; 61 char *shared_page_mapping; 62 63 void 64 shared_page_write(int base, int size, const void *data) 65 { 66 67 bcopy(data, shared_page_mapping + base, size); 68 } 69 70 static int 71 shared_page_alloc_locked(int size, int align) 72 { 73 int res; 74 75 res = roundup(shared_page_free, align); 76 if (res + size >= IDX_TO_OFF(shared_page_obj->size)) 77 res = -1; 78 else 79 shared_page_free = res + size; 80 return (res); 81 } 82 83 int 84 shared_page_alloc(int size, int align) 85 { 86 int res; 87 88 sx_xlock(&shared_page_alloc_sx); 89 res = shared_page_alloc_locked(size, align); 90 sx_xunlock(&shared_page_alloc_sx); 91 return (res); 92 } 93 94 int 95 shared_page_fill(int size, int align, const void *data) 96 { 97 int res; 98 99 sx_xlock(&shared_page_alloc_sx); 100 res = shared_page_alloc_locked(size, align); 101 if (res != -1) 102 shared_page_write(res, size, data); 103 sx_xunlock(&shared_page_alloc_sx); 104 return (res); 105 } 106 107 static void 108 shared_page_init(void *dummy __unused) 109 { 110 vm_page_t m; 111 vm_offset_t addr; 112 113 sx_init(&shared_page_alloc_sx, "shpsx"); 114 shared_page_obj = vm_pager_allocate(OBJT_PHYS, 0, PAGE_SIZE, 115 VM_PROT_DEFAULT, 0, NULL); 116 VM_OBJECT_WLOCK(shared_page_obj); 117 m = vm_page_grab(shared_page_obj, 0, VM_ALLOC_NOBUSY | VM_ALLOC_ZERO); 118 m->valid = VM_PAGE_BITS_ALL; 119 VM_OBJECT_WUNLOCK(shared_page_obj); 120 addr = kva_alloc(PAGE_SIZE); 121 pmap_qenter(addr, &m, 1); 122 shared_page_mapping = (char *)addr; 123 } 124 125 SYSINIT(shp, SI_SUB_EXEC, SI_ORDER_FIRST, (sysinit_cfunc_t)shared_page_init, 126 NULL); 127 128 /* 129 * Push the timehands update to the shared page. 130 * 131 * The lockless update scheme is similar to the one used to update the 132 * in-kernel timehands, see sys/kern/kern_tc.c:tc_windup() (which 133 * calls us after the timehands are updated). 134 */ 135 static void 136 timehands_update(struct vdso_sv_tk *svtk) 137 { 138 struct vdso_timehands th; 139 struct vdso_timekeep *tk; 140 uint32_t enabled, idx; 141 142 enabled = tc_fill_vdso_timehands(&th); 143 th.th_gen = 0; 144 idx = svtk->sv_timekeep_curr; 145 if (++idx >= VDSO_TH_NUM) 146 idx = 0; 147 svtk->sv_timekeep_curr = idx; 148 if (++svtk->sv_timekeep_gen == 0) 149 svtk->sv_timekeep_gen = 1; 150 151 tk = (struct vdso_timekeep *)(shared_page_mapping + 152 svtk->sv_timekeep_off); 153 tk->tk_th[idx].th_gen = 0; 154 atomic_thread_fence_rel(); 155 if (enabled) 156 tk->tk_th[idx] = th; 157 atomic_store_rel_32(&tk->tk_th[idx].th_gen, svtk->sv_timekeep_gen); 158 atomic_store_rel_32(&tk->tk_current, idx); 159 160 /* 161 * The ordering of the assignment to tk_enabled relative to 162 * the update of the vdso_timehands is not important. 163 */ 164 tk->tk_enabled = enabled; 165 } 166 167 #ifdef COMPAT_FREEBSD32 168 static void 169 timehands_update32(struct vdso_sv_tk *svtk) 170 { 171 struct vdso_timehands32 th; 172 struct vdso_timekeep32 *tk; 173 uint32_t enabled, idx; 174 175 enabled = tc_fill_vdso_timehands32(&th); 176 th.th_gen = 0; 177 idx = svtk->sv_timekeep_curr; 178 if (++idx >= VDSO_TH_NUM) 179 idx = 0; 180 svtk->sv_timekeep_curr = idx; 181 if (++svtk->sv_timekeep_gen == 0) 182 svtk->sv_timekeep_gen = 1; 183 184 tk = (struct vdso_timekeep32 *)(shared_page_mapping + 185 svtk->sv_timekeep_off); 186 tk->tk_th[idx].th_gen = 0; 187 atomic_thread_fence_rel(); 188 if (enabled) 189 tk->tk_th[idx] = th; 190 atomic_store_rel_32(&tk->tk_th[idx].th_gen, svtk->sv_timekeep_gen); 191 atomic_store_rel_32(&tk->tk_current, idx); 192 tk->tk_enabled = enabled; 193 } 194 #endif 195 196 /* 197 * This is hackish, but easiest way to avoid creating list structures 198 * that needs to be iterated over from the hardclock interrupt 199 * context. 200 */ 201 static struct vdso_sv_tk *host_svtk; 202 #ifdef COMPAT_FREEBSD32 203 static struct vdso_sv_tk *compat32_svtk; 204 #endif 205 206 void 207 timekeep_push_vdso(void) 208 { 209 210 if (host_svtk != NULL) 211 timehands_update(host_svtk); 212 #ifdef COMPAT_FREEBSD32 213 if (compat32_svtk != NULL) 214 timehands_update32(compat32_svtk); 215 #endif 216 } 217 218 struct vdso_sv_tk * 219 alloc_sv_tk(void) 220 { 221 struct vdso_sv_tk *svtk; 222 int tk_base; 223 uint32_t tk_ver; 224 225 tk_ver = VDSO_TK_VER_CURR; 226 svtk = malloc(sizeof(struct vdso_sv_tk), M_TEMP, M_WAITOK | M_ZERO); 227 tk_base = shared_page_alloc(sizeof(struct vdso_timekeep) + 228 sizeof(struct vdso_timehands) * VDSO_TH_NUM, 16); 229 KASSERT(tk_base != -1, ("tk_base -1 for native")); 230 shared_page_write(tk_base + offsetof(struct vdso_timekeep, tk_ver), 231 sizeof(uint32_t), &tk_ver); 232 svtk->sv_timekeep_off = tk_base; 233 timekeep_push_vdso(); 234 return (svtk); 235 } 236 237 #ifdef COMPAT_FREEBSD32 238 struct vdso_sv_tk * 239 alloc_sv_tk_compat32(void) 240 { 241 struct vdso_sv_tk *svtk; 242 int tk_base; 243 uint32_t tk_ver; 244 245 svtk = malloc(sizeof(struct vdso_sv_tk), M_TEMP, M_WAITOK | M_ZERO); 246 tk_ver = VDSO_TK_VER_CURR; 247 tk_base = shared_page_alloc(sizeof(struct vdso_timekeep32) + 248 sizeof(struct vdso_timehands32) * VDSO_TH_NUM, 16); 249 KASSERT(tk_base != -1, ("tk_base -1 for 32bit")); 250 shared_page_write(tk_base + offsetof(struct vdso_timekeep32, 251 tk_ver), sizeof(uint32_t), &tk_ver); 252 svtk->sv_timekeep_off = tk_base; 253 timekeep_push_vdso(); 254 return (svtk); 255 } 256 #endif 257 258 void 259 exec_sysvec_init(void *param) 260 { 261 struct sysentvec *sv; 262 263 sv = (struct sysentvec *)param; 264 if ((sv->sv_flags & SV_SHP) == 0) 265 return; 266 sv->sv_shared_page_obj = shared_page_obj; 267 sv->sv_sigcode_base = sv->sv_shared_page_base + 268 shared_page_fill(*(sv->sv_szsigcode), 16, sv->sv_sigcode); 269 if ((sv->sv_flags & SV_ABI_MASK) != SV_ABI_FREEBSD) 270 return; 271 if ((sv->sv_flags & SV_TIMEKEEP) != 0) { 272 #ifdef COMPAT_FREEBSD32 273 if ((sv->sv_flags & SV_ILP32) != 0) { 274 KASSERT(compat32_svtk == NULL, 275 ("Compat32 already registered")); 276 compat32_svtk = alloc_sv_tk_compat32(); 277 sv->sv_timekeep_base = sv->sv_shared_page_base + 278 compat32_svtk->sv_timekeep_off; 279 } else { 280 #endif 281 KASSERT(host_svtk == NULL, ("Host already registered")); 282 host_svtk = alloc_sv_tk(); 283 sv->sv_timekeep_base = sv->sv_shared_page_base + 284 host_svtk->sv_timekeep_off; 285 #ifdef COMPAT_FREEBSD32 286 } 287 #endif 288 } 289 } 290