1 1.3 mlelstv /* $NetBSD: x86_pte_tester.c,v 1.3 2022/08/21 14:06:42 mlelstv Exp $ */ 2 1.1 maxv 3 1.1 maxv /* 4 1.1 maxv * Copyright (c) 2016 The NetBSD Foundation, Inc. 5 1.1 maxv * All rights reserved. 6 1.1 maxv * 7 1.1 maxv * Redistribution and use in source and binary forms, with or without 8 1.1 maxv * modification, are permitted provided that the following conditions 9 1.1 maxv * are met: 10 1.1 maxv * 1. Redistributions of source code must retain the above copyright 11 1.1 maxv * notice, this list of conditions and the following disclaimer. 12 1.1 maxv * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 maxv * notice, this list of conditions and the following disclaimer in the 14 1.1 maxv * documentation and/or other materials provided with the distribution. 15 1.1 maxv * 16 1.1 maxv * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 maxv * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 maxv * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 maxv * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 maxv * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 maxv * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 maxv * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 maxv * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 maxv * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 maxv * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 maxv * POSSIBILITY OF SUCH DAMAGE. 27 1.1 maxv */ 28 1.1 maxv 29 1.1 maxv #define __HAVE_DIRECT_MAP 30 1.1 maxv #define __HAVE_PCPU_AREA 31 1.1 maxv #define SVS 32 1.1 maxv 33 1.1 maxv #include <sys/cdefs.h> 34 1.1 maxv #include <sys/param.h> 35 1.1 maxv #include <sys/module.h> 36 1.1 maxv #include <sys/proc.h> 37 1.1 maxv #include <sys/sysctl.h> 38 1.1 maxv #include <uvm/uvm.h> 39 1.1 maxv #include <x86/pmap.h> 40 1.1 maxv 41 1.1 maxv #if defined(__x86_64__) 42 1.1 maxv # include <amd64/pmap.h> 43 1.3 mlelstv # include <amd64/pmap_private.h> 44 1.1 maxv # define NLEVEL 4 45 1.1 maxv #else 46 1.1 maxv # error "Unsupported configuration" 47 1.1 maxv #endif 48 1.1 maxv 49 1.1 maxv static struct { 50 1.1 maxv struct sysctllog *ctx_sysctllog; 51 1.1 maxv vaddr_t levels[NLEVEL]; 52 1.1 maxv struct { 53 1.1 maxv size_t l4; 54 1.1 maxv size_t l3; 55 1.1 maxv size_t l2; 56 1.1 maxv size_t l1; 57 1.1 maxv } coord; 58 1.1 maxv struct { 59 1.1 maxv size_t n_rwx; 60 1.2 maxv size_t n_shstk; 61 1.1 maxv bool kernel_map_with_low_ptes; 62 1.1 maxv bool pte_is_user_accessible; 63 1.1 maxv size_t n_user_space_is_kernel; 64 1.1 maxv size_t n_kernel_space_is_user; 65 1.1 maxv size_t n_svs_g_bit_set; 66 1.1 maxv } results; 67 1.1 maxv } tester_ctx; 68 1.1 maxv 69 1.1 maxv typedef enum { 70 1.1 maxv WALK_NEXT, /* go to the next level */ 71 1.1 maxv WALK_SKIP, /* skip the next level, but keep iterating on the current one */ 72 1.1 maxv WALK_STOP /* stop the iteration on the current level */ 73 1.1 maxv } walk_type; 74 1.1 maxv 75 1.1 maxv /* -------------------------------------------------------------------------- */ 76 1.1 maxv 77 1.1 maxv #define is_flag(__ent, __flag) (((__ent) & __flag) != 0) 78 1.1 maxv #define is_valid(__ent) is_flag(__ent, PTE_P) 79 1.1 maxv #define get_pa(__pde) (__pde & PTE_FRAME) 80 1.1 maxv 81 1.1 maxv #define L4_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t)) 82 1.1 maxv #define L3_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t)) 83 1.1 maxv #define L2_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t)) 84 1.1 maxv #define L1_MAX_NENTRIES (PAGE_SIZE / sizeof(pd_entry_t)) 85 1.1 maxv 86 1.1 maxv static void 87 1.1 maxv scan_l1(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl)) 88 1.1 maxv { 89 1.1 maxv pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[0]; 90 1.1 maxv size_t i; 91 1.1 maxv 92 1.1 maxv pmap_kenter_pa(tester_ctx.levels[0], pa, VM_PROT_READ, 0); 93 1.1 maxv pmap_update(pmap_kernel()); 94 1.1 maxv 95 1.1 maxv for (i = 0; i < L1_MAX_NENTRIES; i++) { 96 1.1 maxv tester_ctx.coord.l1 = i; 97 1.1 maxv if (is_valid(pd[i])) { 98 1.1 maxv fn(pd[i], i, 1); 99 1.1 maxv } 100 1.1 maxv } 101 1.1 maxv 102 1.1 maxv pmap_kremove(tester_ctx.levels[0], PAGE_SIZE); 103 1.1 maxv pmap_update(pmap_kernel()); 104 1.1 maxv } 105 1.1 maxv 106 1.1 maxv static void 107 1.1 maxv scan_l2(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl)) 108 1.1 maxv { 109 1.1 maxv pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[1]; 110 1.1 maxv walk_type ret; 111 1.1 maxv size_t i; 112 1.1 maxv 113 1.1 maxv pmap_kenter_pa(tester_ctx.levels[1], pa, VM_PROT_READ, 0); 114 1.1 maxv pmap_update(pmap_kernel()); 115 1.1 maxv 116 1.1 maxv for (i = 0; i < L2_MAX_NENTRIES; i++) { 117 1.1 maxv tester_ctx.coord.l2 = i; 118 1.1 maxv if (!is_valid(pd[i])) 119 1.1 maxv continue; 120 1.1 maxv ret = fn(pd[i], i, 2); 121 1.1 maxv if (ret == WALK_STOP) 122 1.1 maxv break; 123 1.1 maxv if (is_flag(pd[i], PTE_PS)) 124 1.1 maxv continue; 125 1.1 maxv if (ret == WALK_NEXT) 126 1.1 maxv scan_l1(get_pa(pd[i]), fn); 127 1.1 maxv } 128 1.1 maxv 129 1.1 maxv pmap_kremove(tester_ctx.levels[1], PAGE_SIZE); 130 1.1 maxv pmap_update(pmap_kernel()); 131 1.1 maxv } 132 1.1 maxv 133 1.1 maxv static void 134 1.1 maxv scan_l3(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl)) 135 1.1 maxv { 136 1.1 maxv pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[2]; 137 1.1 maxv walk_type ret; 138 1.1 maxv size_t i; 139 1.1 maxv 140 1.1 maxv pmap_kenter_pa(tester_ctx.levels[2], pa, VM_PROT_READ, 0); 141 1.1 maxv pmap_update(pmap_kernel()); 142 1.1 maxv 143 1.1 maxv for (i = 0; i < L3_MAX_NENTRIES; i++) { 144 1.1 maxv tester_ctx.coord.l3 = i; 145 1.1 maxv if (!is_valid(pd[i])) 146 1.1 maxv continue; 147 1.1 maxv ret = fn(pd[i], i, 3); 148 1.1 maxv if (ret == WALK_STOP) 149 1.1 maxv break; 150 1.1 maxv if (is_flag(pd[i], PTE_PS)) 151 1.1 maxv continue; 152 1.1 maxv if (ret == WALK_NEXT) 153 1.1 maxv scan_l2(get_pa(pd[i]), fn); 154 1.1 maxv } 155 1.1 maxv 156 1.1 maxv pmap_kremove(tester_ctx.levels[2], PAGE_SIZE); 157 1.1 maxv pmap_update(pmap_kernel()); 158 1.1 maxv } 159 1.1 maxv 160 1.1 maxv static void 161 1.1 maxv scan_l4(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl)) 162 1.1 maxv { 163 1.1 maxv pd_entry_t *pd = (pd_entry_t *)tester_ctx.levels[3]; 164 1.1 maxv walk_type ret; 165 1.1 maxv size_t i; 166 1.1 maxv 167 1.1 maxv pmap_kenter_pa(tester_ctx.levels[3], pa, VM_PROT_READ, 0); 168 1.1 maxv pmap_update(pmap_kernel()); 169 1.1 maxv 170 1.1 maxv for (i = 0; i < L4_MAX_NENTRIES; i++) { 171 1.1 maxv tester_ctx.coord.l4 = i; 172 1.1 maxv if (!is_valid(pd[i])) 173 1.1 maxv continue; 174 1.1 maxv ret = fn(pd[i], i, 4); 175 1.1 maxv if (ret == WALK_STOP) 176 1.1 maxv break; 177 1.1 maxv if (is_flag(pd[i], PTE_PS)) 178 1.1 maxv continue; 179 1.1 maxv if (ret == WALK_NEXT) 180 1.1 maxv scan_l3(get_pa(pd[i]), fn); 181 1.1 maxv } 182 1.1 maxv 183 1.1 maxv pmap_kremove(tester_ctx.levels[3], PAGE_SIZE); 184 1.1 maxv pmap_update(pmap_kernel()); 185 1.1 maxv } 186 1.1 maxv 187 1.1 maxv static void 188 1.1 maxv scan_tree(paddr_t pa, walk_type (fn)(pd_entry_t pde, size_t slot, int lvl)) 189 1.1 maxv { 190 1.1 maxv scan_l4(pa, fn); 191 1.1 maxv } 192 1.1 maxv 193 1.1 maxv /* -------------------------------------------------------------------------- */ 194 1.1 maxv 195 1.1 maxv /* 196 1.1 maxv * Rule: the number of kernel RWX pages should be zero. 197 1.1 maxv */ 198 1.1 maxv static walk_type 199 1.1 maxv count_krwx(pd_entry_t pde, size_t slot, int lvl) 200 1.1 maxv { 201 1.1 maxv if (lvl == NLEVEL && slot < 256) { 202 1.1 maxv return WALK_SKIP; 203 1.1 maxv } 204 1.1 maxv if (is_flag(pde, PTE_NX) || !is_flag(pde, PTE_W)) { 205 1.1 maxv return WALK_SKIP; 206 1.1 maxv } 207 1.1 maxv if (lvl != 1 && !is_flag(pde, PTE_PS)) { 208 1.1 maxv return WALK_NEXT; 209 1.1 maxv } 210 1.1 maxv 211 1.1 maxv if (lvl == 4) { 212 1.1 maxv tester_ctx.results.n_rwx += (NBPD_L4 / PAGE_SIZE); 213 1.1 maxv } else if (lvl == 3) { 214 1.1 maxv tester_ctx.results.n_rwx += (NBPD_L3 / PAGE_SIZE); 215 1.1 maxv } else if (lvl == 2) { 216 1.1 maxv tester_ctx.results.n_rwx += (NBPD_L2 / PAGE_SIZE); 217 1.1 maxv } else if (lvl == 1) { 218 1.1 maxv tester_ctx.results.n_rwx += (NBPD_L1 / PAGE_SIZE); 219 1.1 maxv } 220 1.1 maxv 221 1.1 maxv return WALK_NEXT; 222 1.1 maxv } 223 1.1 maxv 224 1.1 maxv /* 225 1.2 maxv * Rule: the number of kernel SHSTK pages should be zero. 226 1.2 maxv */ 227 1.2 maxv static walk_type 228 1.2 maxv count_kshstk(pd_entry_t pde, size_t slot, int lvl) 229 1.2 maxv { 230 1.2 maxv if (lvl == NLEVEL && slot < 256) { 231 1.2 maxv return WALK_SKIP; 232 1.2 maxv } 233 1.2 maxv 234 1.2 maxv if (is_flag(pde, PTE_PS) || lvl == 1) { 235 1.2 maxv if (!is_flag(pde, PTE_W) && is_flag(pde, PTE_D)) { 236 1.2 maxv if (lvl == 4) { 237 1.2 maxv tester_ctx.results.n_shstk += (NBPD_L4 / PAGE_SIZE); 238 1.2 maxv } else if (lvl == 3) { 239 1.2 maxv tester_ctx.results.n_shstk += (NBPD_L3 / PAGE_SIZE); 240 1.2 maxv } else if (lvl == 2) { 241 1.2 maxv tester_ctx.results.n_shstk += (NBPD_L2 / PAGE_SIZE); 242 1.2 maxv } else if (lvl == 1) { 243 1.2 maxv tester_ctx.results.n_shstk += (NBPD_L1 / PAGE_SIZE); 244 1.2 maxv } 245 1.2 maxv } 246 1.2 maxv return WALK_SKIP; 247 1.2 maxv } 248 1.2 maxv 249 1.2 maxv if (!is_flag(pde, PTE_W)) { 250 1.2 maxv return WALK_SKIP; 251 1.2 maxv } 252 1.2 maxv 253 1.2 maxv return WALK_NEXT; 254 1.2 maxv } 255 1.2 maxv 256 1.2 maxv /* 257 1.1 maxv * Rule: the lower half of the kernel map must be zero. 258 1.1 maxv */ 259 1.1 maxv static walk_type 260 1.1 maxv check_kernel_map(pd_entry_t pde, size_t slot, int lvl) 261 1.1 maxv { 262 1.1 maxv if (lvl != NLEVEL) { 263 1.1 maxv return WALK_STOP; 264 1.1 maxv } 265 1.1 maxv if (slot >= 256) { 266 1.1 maxv return WALK_SKIP; 267 1.1 maxv } 268 1.1 maxv if (pde != 0) { 269 1.1 maxv tester_ctx.results.kernel_map_with_low_ptes |= true; 270 1.1 maxv } 271 1.1 maxv return WALK_SKIP; 272 1.1 maxv } 273 1.1 maxv 274 1.1 maxv /* 275 1.1 maxv * Rule: the PTE space must not have user permissions. 276 1.1 maxv */ 277 1.1 maxv static walk_type 278 1.1 maxv check_pte_space(pd_entry_t pde, size_t slot, int lvl) 279 1.1 maxv { 280 1.1 maxv if (lvl != NLEVEL) { 281 1.1 maxv return WALK_STOP; 282 1.1 maxv } 283 1.1 maxv if (slot != PDIR_SLOT_PTE) { 284 1.1 maxv return WALK_SKIP; 285 1.1 maxv } 286 1.1 maxv if (is_flag(pde, PTE_U)) { 287 1.1 maxv tester_ctx.results.pte_is_user_accessible |= true; 288 1.1 maxv } 289 1.1 maxv return WALK_SKIP; 290 1.1 maxv } 291 1.1 maxv 292 1.1 maxv /* 293 1.1 maxv * Rule: each page in the lower half must have user permissions. 294 1.1 maxv */ 295 1.1 maxv static walk_type 296 1.1 maxv check_user_space(pd_entry_t pde, size_t slot, int lvl) 297 1.1 maxv { 298 1.1 maxv if (lvl == NLEVEL && slot >= 256) { 299 1.1 maxv return WALK_SKIP; 300 1.1 maxv } 301 1.1 maxv if (!is_flag(pde, PTE_U)) { 302 1.1 maxv tester_ctx.results.n_user_space_is_kernel += 1; 303 1.1 maxv return WALK_SKIP; 304 1.1 maxv } 305 1.1 maxv return WALK_NEXT; 306 1.1 maxv } 307 1.1 maxv 308 1.1 maxv /* 309 1.1 maxv * Rule: each page in the higher half must have kernel permissions. 310 1.1 maxv */ 311 1.1 maxv static walk_type 312 1.1 maxv check_kernel_space(pd_entry_t pde, size_t slot, int lvl) 313 1.1 maxv { 314 1.1 maxv if (lvl == NLEVEL && slot < 256) { 315 1.1 maxv return WALK_SKIP; 316 1.1 maxv } 317 1.1 maxv if (lvl == NLEVEL && slot == PDIR_SLOT_PTE) { 318 1.1 maxv return WALK_SKIP; 319 1.1 maxv } 320 1.1 maxv if (is_flag(pde, PTE_U)) { 321 1.1 maxv tester_ctx.results.n_kernel_space_is_user += 1; 322 1.1 maxv return WALK_SKIP; 323 1.1 maxv } 324 1.1 maxv return WALK_NEXT; 325 1.1 maxv } 326 1.1 maxv 327 1.1 maxv /* 328 1.1 maxv * Rule: the SVS map is allowed to use the G bit only on the PCPU area. 329 1.1 maxv */ 330 1.1 maxv static walk_type 331 1.1 maxv check_svs_g_bit(pd_entry_t pde, size_t slot, int lvl) 332 1.1 maxv { 333 1.1 maxv if (lvl == NLEVEL && slot == PDIR_SLOT_PCPU) { 334 1.1 maxv return WALK_SKIP; 335 1.1 maxv } 336 1.1 maxv if (is_flag(pde, PTE_G)) { 337 1.1 maxv tester_ctx.results.n_svs_g_bit_set += 1; 338 1.1 maxv return WALK_SKIP; 339 1.1 maxv } 340 1.1 maxv return WALK_NEXT; 341 1.1 maxv } 342 1.1 maxv 343 1.1 maxv /* -------------------------------------------------------------------------- */ 344 1.1 maxv 345 1.1 maxv static void 346 1.1 maxv scan_svs(void) 347 1.1 maxv { 348 1.1 maxv extern bool svs_enabled; 349 1.1 maxv paddr_t pa0; 350 1.1 maxv 351 1.1 maxv if (!svs_enabled) { 352 1.1 maxv tester_ctx.results.n_svs_g_bit_set = -1; 353 1.1 maxv return; 354 1.1 maxv } 355 1.1 maxv 356 1.1 maxv kpreempt_disable(); 357 1.1 maxv pa0 = curcpu()->ci_svs_updirpa; 358 1.1 maxv scan_tree(pa0, &check_user_space); 359 1.1 maxv scan_tree(pa0, &check_kernel_space); 360 1.1 maxv scan_tree(pa0, &check_svs_g_bit); 361 1.1 maxv kpreempt_enable(); 362 1.1 maxv } 363 1.1 maxv 364 1.1 maxv static void 365 1.1 maxv scan_proc(struct proc *p) 366 1.1 maxv { 367 1.1 maxv struct pmap *pmap = p->p_vmspace->vm_map.pmap; 368 1.1 maxv paddr_t pa0; 369 1.1 maxv 370 1.1 maxv mutex_enter(&pmap->pm_lock); 371 1.1 maxv 372 1.1 maxv kpreempt_disable(); 373 1.1 maxv pa0 = (paddr_t)pmap->pm_pdirpa[0]; 374 1.1 maxv scan_tree(pa0, &check_user_space); 375 1.1 maxv scan_tree(pa0, &check_kernel_space); 376 1.1 maxv scan_tree(pa0, &check_pte_space); 377 1.1 maxv kpreempt_enable(); 378 1.1 maxv 379 1.1 maxv mutex_exit(&pmap->pm_lock); 380 1.1 maxv } 381 1.1 maxv 382 1.1 maxv static void 383 1.1 maxv x86_pte_run_scans(void) 384 1.1 maxv { 385 1.1 maxv struct pmap *kpm = pmap_kernel(); 386 1.1 maxv paddr_t pa0; 387 1.1 maxv 388 1.1 maxv memset(&tester_ctx.results, 0, sizeof(tester_ctx.results)); 389 1.1 maxv 390 1.1 maxv /* Scan the current user process. */ 391 1.1 maxv scan_proc(curproc); 392 1.1 maxv 393 1.1 maxv /* Scan the SVS mapping. */ 394 1.1 maxv scan_svs(); 395 1.1 maxv 396 1.1 maxv /* Scan the kernel map. */ 397 1.1 maxv pa0 = (paddr_t)kpm->pm_pdirpa[0]; 398 1.1 maxv scan_tree(pa0, &count_krwx); 399 1.2 maxv scan_tree(pa0, &count_kshstk); 400 1.1 maxv scan_tree(pa0, &check_kernel_map); 401 1.1 maxv } 402 1.1 maxv 403 1.1 maxv static void 404 1.1 maxv x86_pte_levels_init(void) 405 1.1 maxv { 406 1.1 maxv size_t i; 407 1.1 maxv for (i = 0; i < NLEVEL; i++) { 408 1.1 maxv tester_ctx.levels[i] = uvm_km_alloc(kernel_map, PAGE_SIZE, 0, 409 1.1 maxv UVM_KMF_VAONLY); 410 1.1 maxv } 411 1.1 maxv } 412 1.1 maxv 413 1.1 maxv static void 414 1.1 maxv x86_pte_levels_destroy(void) 415 1.1 maxv { 416 1.1 maxv size_t i; 417 1.1 maxv for (i = 0; i < NLEVEL; i++) { 418 1.1 maxv uvm_km_free(kernel_map, tester_ctx.levels[i], PAGE_SIZE, 419 1.1 maxv UVM_KMF_VAONLY); 420 1.1 maxv } 421 1.1 maxv } 422 1.1 maxv 423 1.1 maxv /* -------------------------------------------------------------------------- */ 424 1.1 maxv 425 1.1 maxv static int 426 1.1 maxv x86_pte_sysctl_run(SYSCTLFN_ARGS) 427 1.1 maxv { 428 1.1 maxv if (oldlenp == NULL) 429 1.1 maxv return EINVAL; 430 1.1 maxv 431 1.1 maxv x86_pte_run_scans(); 432 1.1 maxv 433 1.1 maxv if (oldp == NULL) { 434 1.1 maxv *oldlenp = sizeof(tester_ctx.results); 435 1.1 maxv return 0; 436 1.1 maxv } 437 1.1 maxv 438 1.1 maxv if (*oldlenp < sizeof(tester_ctx.results)) 439 1.1 maxv return ENOMEM; 440 1.1 maxv 441 1.1 maxv return copyout(&tester_ctx.results, oldp, sizeof(tester_ctx.results)); 442 1.1 maxv } 443 1.1 maxv 444 1.1 maxv static int 445 1.1 maxv x86_pte_sysctl_init(void) 446 1.1 maxv { 447 1.1 maxv struct sysctllog **log = &tester_ctx.ctx_sysctllog; 448 1.1 maxv const struct sysctlnode *rnode, *cnode; 449 1.1 maxv int error; 450 1.1 maxv 451 1.1 maxv error = sysctl_createv(log, 0, NULL, &rnode, CTLFLAG_PERMANENT, 452 1.1 maxv CTLTYPE_NODE, "x86_pte_test", 453 1.1 maxv SYSCTL_DESCR("x86_pte testing interface"), 454 1.1 maxv NULL, 0, NULL, 0, CTL_KERN, CTL_CREATE, CTL_EOL); 455 1.1 maxv if (error) 456 1.1 maxv goto out; 457 1.1 maxv 458 1.1 maxv error = sysctl_createv(log, 0, &rnode, &cnode, CTLFLAG_PERMANENT, 459 1.1 maxv CTLTYPE_STRUCT, "test", 460 1.1 maxv SYSCTL_DESCR("execute a x86_pte test"), 461 1.1 maxv x86_pte_sysctl_run, 0, NULL, 0, CTL_CREATE, CTL_EOL); 462 1.1 maxv 463 1.1 maxv out: 464 1.1 maxv if (error) 465 1.1 maxv sysctl_teardown(log); 466 1.1 maxv return error; 467 1.1 maxv } 468 1.1 maxv 469 1.1 maxv static void 470 1.1 maxv x86_pte_sysctl_destroy(void) 471 1.1 maxv { 472 1.1 maxv sysctl_teardown(&tester_ctx.ctx_sysctllog); 473 1.1 maxv } 474 1.1 maxv 475 1.1 maxv /* -------------------------------------------------------------------------- */ 476 1.1 maxv 477 1.1 maxv MODULE(MODULE_CLASS_MISC, x86_pte_tester, NULL); 478 1.1 maxv 479 1.1 maxv static int 480 1.1 maxv x86_pte_tester_modcmd(modcmd_t cmd, void *arg __unused) 481 1.1 maxv { 482 1.1 maxv int error = 0; 483 1.1 maxv 484 1.1 maxv switch (cmd) { 485 1.1 maxv case MODULE_CMD_INIT: 486 1.1 maxv x86_pte_levels_init(); 487 1.1 maxv error = x86_pte_sysctl_init(); 488 1.1 maxv break; 489 1.1 maxv case MODULE_CMD_FINI: 490 1.1 maxv x86_pte_sysctl_destroy(); 491 1.1 maxv x86_pte_levels_destroy(); 492 1.1 maxv break; 493 1.1 maxv default: 494 1.1 maxv error = ENOTTY; 495 1.1 maxv break; 496 1.1 maxv } 497 1.1 maxv 498 1.1 maxv return error; 499 1.1 maxv } 500