1 1.5 andvar /* $NetBSD: subr_physmap.c,v 1.5 2021/09/06 20:55:08 andvar Exp $ */ 2 1.2 rmind 3 1.1 matt /*- 4 1.1 matt * Copyright (c) 2013 The NetBSD Foundation, Inc. 5 1.1 matt * All rights reserved. 6 1.1 matt * 7 1.1 matt * This code is derived from software contributed to The NetBSD Foundation 8 1.1 matt * by Matt Thomas of 3am Software Foundry. 9 1.1 matt * 10 1.1 matt * Redistribution and use in source and binary forms, with or without 11 1.1 matt * modification, are permitted provided that the following conditions 12 1.1 matt * are met: 13 1.1 matt * 1. Redistributions of source code must retain the above copyright 14 1.1 matt * notice, this list of conditions and the following disclaimer. 15 1.1 matt * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 matt * notice, this list of conditions and the following disclaimer in the 17 1.1 matt * documentation and/or other materials provided with the distribution. 18 1.1 matt * 19 1.1 matt * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 1.1 matt * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 1.1 matt * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 1.1 matt * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 1.1 matt * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 1.1 matt * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 1.1 matt * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 1.1 matt * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 1.1 matt * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 1.1 matt * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 1.1 matt * POSSIBILITY OF SUCH DAMAGE. 30 1.1 matt */ 31 1.1 matt 32 1.1 matt #include <sys/cdefs.h> 33 1.5 andvar __KERNEL_RCSID(1, "$NetBSD: subr_physmap.c,v 1.5 2021/09/06 20:55:08 andvar Exp $"); 34 1.1 matt 35 1.1 matt #include <sys/param.h> 36 1.1 matt #include <sys/physmap.h> 37 1.1 matt #include <sys/kmem.h> 38 1.1 matt 39 1.4 riastrad #include <uvm/uvm_extern.h> 40 1.4 riastrad #include <uvm/uvm_page.h> 41 1.4 riastrad 42 1.1 matt #include <dev/mm.h> 43 1.1 matt 44 1.1 matt /* 45 1.1 matt * This file contain support routines used to create and destroy lists of 46 1.1 matt * physical pages from lists of pages or ranges of virtual address. By using 47 1.1 matt * these physical maps, the kernel can avoid mapping physical I/O in the 48 1.1 matt * kernel's address space in most cases. 49 1.1 matt */ 50 1.1 matt 51 1.1 matt typedef struct { 52 1.1 matt physmap_t *pc_physmap; 53 1.1 matt physmap_segment_t *pc_segs; 54 1.1 matt vsize_t pc_offset; 55 1.1 matt vsize_t pc_klen; 56 1.1 matt vaddr_t pc_kva; 57 1.1 matt u_int pc_nsegs; 58 1.1 matt vm_prot_t pc_prot; 59 1.1 matt bool pc_direct_mapped; 60 1.1 matt } physmap_cookie_t; 61 1.1 matt 62 1.1 matt /* 63 1.1 matt * Allocate a physmap structure that requires "maxsegs" segments. 64 1.1 matt */ 65 1.1 matt static physmap_t * 66 1.1 matt physmap_alloc(size_t maxsegs) 67 1.1 matt { 68 1.1 matt const size_t mapsize = offsetof(physmap_t, pm_segs[maxsegs]); 69 1.1 matt 70 1.1 matt KASSERT(maxsegs > 0); 71 1.1 matt 72 1.1 matt physmap_t * const map = kmem_zalloc(mapsize, KM_SLEEP); 73 1.1 matt map->pm_maxsegs = maxsegs; 74 1.1 matt 75 1.1 matt return map; 76 1.1 matt } 77 1.1 matt 78 1.1 matt static int 79 1.1 matt physmap_fill(physmap_t *map, pmap_t pmap, vaddr_t va, vsize_t len) 80 1.1 matt { 81 1.1 matt size_t nsegs = map->pm_nsegs; 82 1.1 matt physmap_segment_t *ps = &map->pm_segs[nsegs]; 83 1.1 matt vsize_t offset = va - trunc_page(va); 84 1.1 matt 85 1.1 matt if (nsegs == 0) { 86 1.1 matt if (!pmap_extract(pmap, va, &ps->ps_addr)) { 87 1.1 matt return EFAULT; 88 1.1 matt } 89 1.2 rmind ps->ps_len = MIN(len, PAGE_SIZE - offset); 90 1.1 matt if (ps->ps_len == len) { 91 1.1 matt map->pm_nsegs = 1; 92 1.1 matt return 0; 93 1.1 matt } 94 1.1 matt offset = 0; 95 1.1 matt } else { 96 1.1 matt /* 97 1.1 matt * Backup to the last segment since we have to see if we can 98 1.1 matt * merge virtual addresses that are physically contiguous into 99 1.1 matt * as few segments as possible. 100 1.1 matt */ 101 1.1 matt ps--; 102 1.1 matt nsegs--; 103 1.1 matt } 104 1.1 matt 105 1.1 matt paddr_t lastaddr = ps->ps_addr + ps->ps_len; 106 1.1 matt for (;;) { 107 1.1 matt paddr_t curaddr; 108 1.1 matt if (!pmap_extract(pmap, va, &curaddr)) { 109 1.1 matt return EFAULT; 110 1.1 matt } 111 1.1 matt if (curaddr != lastaddr) { 112 1.1 matt ps++; 113 1.1 matt nsegs++; 114 1.1 matt KASSERT(nsegs < map->pm_maxsegs); 115 1.1 matt ps->ps_addr = curaddr; 116 1.1 matt lastaddr = curaddr; 117 1.1 matt } 118 1.1 matt if (offset + len > PAGE_SIZE) { 119 1.1 matt ps->ps_len += PAGE_SIZE - offset; 120 1.1 matt lastaddr = ps->ps_addr + ps->ps_len; 121 1.1 matt len -= PAGE_SIZE - offset; 122 1.1 matt lastaddr += PAGE_SIZE - offset; 123 1.1 matt offset = 0; 124 1.1 matt } else { 125 1.1 matt ps->ps_len += len; 126 1.1 matt map->pm_nsegs = nsegs + 1; 127 1.1 matt return 0; 128 1.1 matt } 129 1.1 matt } 130 1.1 matt } 131 1.1 matt 132 1.1 matt /* 133 1.1 matt * Create a physmap and populate it with the pages that are used to mapped 134 1.1 matt * linear range of virtual addresses. It is assumed that uvm_vslock has been 135 1.1 matt * called to lock these pages into memory. 136 1.1 matt */ 137 1.1 matt int 138 1.1 matt physmap_create_linear(physmap_t **map_p, const struct vmspace *vs, vaddr_t va, 139 1.1 matt vsize_t len) 140 1.1 matt { 141 1.1 matt const size_t maxsegs = atop(round_page(va + len) - trunc_page(va)); 142 1.1 matt physmap_t * const map = physmap_alloc(maxsegs); 143 1.1 matt int error = physmap_fill(map, vs->vm_map.pmap, va, len); 144 1.1 matt if (error) { 145 1.1 matt physmap_destroy(map); 146 1.1 matt *map_p = NULL; 147 1.1 matt return error; 148 1.1 matt } 149 1.1 matt *map_p = map; 150 1.1 matt return 0; 151 1.1 matt } 152 1.1 matt 153 1.1 matt /* 154 1.1 matt * Create a physmap and populate it with the pages that are contained in an 155 1.1 matt * iovec array. It is assumed that uvm_vslock has been called to lock these 156 1.1 matt * pages into memory. 157 1.1 matt */ 158 1.1 matt int 159 1.1 matt physmap_create_iov(physmap_t **map_p, const struct vmspace *vs, 160 1.1 matt struct iovec *iov, size_t iovlen) 161 1.1 matt { 162 1.1 matt size_t maxsegs = 0; 163 1.1 matt for (size_t i = 0; i < iovlen; i++) { 164 1.1 matt const vaddr_t start = (vaddr_t) iov[i].iov_base; 165 1.1 matt const vaddr_t end = start + iov[i].iov_len; 166 1.1 matt maxsegs += atop(round_page(end) - trunc_page(start)); 167 1.1 matt } 168 1.1 matt physmap_t * const map = physmap_alloc(maxsegs); 169 1.1 matt 170 1.1 matt for (size_t i = 0; i < iovlen; i++) { 171 1.1 matt int error = physmap_fill(map, vs->vm_map.pmap, 172 1.1 matt (vaddr_t) iov[i].iov_base, iov[i].iov_len); 173 1.1 matt if (error) { 174 1.1 matt physmap_destroy(map); 175 1.1 matt *map_p = NULL; 176 1.1 matt return error; 177 1.1 matt } 178 1.1 matt } 179 1.1 matt *map_p = map; 180 1.1 matt return 0; 181 1.1 matt } 182 1.1 matt 183 1.1 matt /* 184 1.1 matt * This uses a list of vm_page structure to create a physmap. 185 1.1 matt */ 186 1.1 matt physmap_t * 187 1.1 matt physmap_create_pagelist(struct vm_page **pgs, size_t npgs) 188 1.1 matt { 189 1.1 matt physmap_t * const map = physmap_alloc(npgs); 190 1.1 matt 191 1.1 matt physmap_segment_t *ps = map->pm_segs; 192 1.1 matt 193 1.1 matt /* 194 1.1 matt * Initialize the first segment. 195 1.1 matt */ 196 1.1 matt paddr_t lastaddr = VM_PAGE_TO_PHYS(pgs[0]); 197 1.1 matt ps->ps_addr = lastaddr; 198 1.1 matt ps->ps_len = PAGE_SIZE; 199 1.1 matt 200 1.1 matt for (pgs++; npgs-- > 1; pgs++) { 201 1.1 matt /* 202 1.1 matt * lastaddr needs to be increased by a page. 203 1.1 matt */ 204 1.1 matt lastaddr += PAGE_SIZE; 205 1.1 matt paddr_t curaddr = VM_PAGE_TO_PHYS(*pgs); 206 1.1 matt if (curaddr != lastaddr) { 207 1.1 matt /* 208 1.1 matt * If the addresses are not the same, we need to use 209 1.5 andvar * a new segment. Set its address and update lastaddr. 210 1.1 matt */ 211 1.1 matt ps++; 212 1.1 matt ps->ps_addr = curaddr; 213 1.1 matt lastaddr = curaddr; 214 1.1 matt } 215 1.1 matt /* 216 1.1 matt * Increase this segment's length by a page 217 1.1 matt */ 218 1.1 matt ps->ps_len += PAGE_SIZE; 219 1.1 matt } 220 1.1 matt 221 1.1 matt map->pm_nsegs = ps + 1 - map->pm_segs; 222 1.1 matt return map; 223 1.1 matt } 224 1.1 matt 225 1.1 matt void 226 1.1 matt physmap_destroy(physmap_t *map) 227 1.1 matt { 228 1.1 matt const size_t mapsize = offsetof(physmap_t, pm_segs[map->pm_maxsegs]); 229 1.1 matt 230 1.1 matt kmem_free(map, mapsize); 231 1.1 matt } 232 1.1 matt 233 1.1 matt void * 234 1.1 matt physmap_map_init(physmap_t *map, size_t offset, vm_prot_t prot) 235 1.1 matt { 236 1.1 matt physmap_cookie_t * const pc = kmem_zalloc(sizeof(*pc), KM_SLEEP); 237 1.1 matt 238 1.1 matt KASSERT(prot == VM_PROT_READ || prot == (VM_PROT_READ|VM_PROT_WRITE)); 239 1.1 matt 240 1.1 matt pc->pc_physmap = map; 241 1.1 matt pc->pc_segs = map->pm_segs; 242 1.1 matt pc->pc_nsegs = map->pm_nsegs; 243 1.1 matt pc->pc_prot = prot; 244 1.1 matt pc->pc_klen = 0; 245 1.1 matt pc->pc_kva = 0; 246 1.1 matt pc->pc_direct_mapped = false; 247 1.1 matt 248 1.1 matt /* 249 1.1 matt * Skip to the first segment we are interested in. 250 1.1 matt */ 251 1.1 matt while (offset >= pc->pc_segs->ps_len) { 252 1.1 matt offset -= pc->pc_segs->ps_len; 253 1.1 matt pc->pc_segs++; 254 1.1 matt pc->pc_nsegs--; 255 1.1 matt } 256 1.1 matt 257 1.1 matt pc->pc_offset = offset; 258 1.1 matt 259 1.1 matt return pc; 260 1.1 matt } 261 1.1 matt 262 1.1 matt size_t 263 1.1 matt physmap_map(void *cookie, vaddr_t *kvap) 264 1.1 matt { 265 1.1 matt physmap_cookie_t * const pc = cookie; 266 1.1 matt 267 1.1 matt /* 268 1.1 matt * If there is currently a non-direct mapped KVA region allocated, 269 1.1 matt * free it now. 270 1.1 matt */ 271 1.1 matt if (pc->pc_kva != 0 && !pc->pc_direct_mapped) { 272 1.1 matt pmap_kremove(pc->pc_kva, pc->pc_klen); 273 1.2 rmind pmap_update(pmap_kernel()); 274 1.1 matt uvm_km_free(kernel_map, pc->pc_kva, pc->pc_klen, 275 1.1 matt UVM_KMF_VAONLY); 276 1.1 matt } 277 1.1 matt 278 1.1 matt /* 279 1.1 matt * If there are no more segments to process, return 0 indicating 280 1.1 matt * we are done. 281 1.1 matt */ 282 1.1 matt if (pc->pc_nsegs == 0) { 283 1.1 matt return 0; 284 1.1 matt } 285 1.1 matt 286 1.1 matt /* 287 1.1 matt * Get starting physical address of this segment and its length. 288 1.1 matt */ 289 1.1 matt paddr_t pa = pc->pc_segs->ps_addr + pc->pc_offset; 290 1.1 matt const size_t koff = pa & PAGE_MASK; 291 1.1 matt const size_t len = pc->pc_segs->ps_len - pc->pc_offset; 292 1.1 matt 293 1.1 matt /* 294 1.1 matt * Now that we have the starting offset in the page, reset to the 295 1.1 matt * beginning of the page. 296 1.1 matt */ 297 1.1 matt pa = trunc_page(pa); 298 1.1 matt 299 1.1 matt /* 300 1.1 matt * We are now done with this segment; advance to the next one. 301 1.1 matt */ 302 1.1 matt pc->pc_segs++; 303 1.1 matt pc->pc_nsegs--; 304 1.1 matt pc->pc_offset = 0; 305 1.1 matt 306 1.1 matt /* 307 1.1 matt * Find out how many pages we are mapping. 308 1.1 matt */ 309 1.1 matt pc->pc_klen = round_page(len); 310 1.1 matt #ifdef __HAVE_MM_MD_DIRECT_MAPPED_PHYS 311 1.1 matt /* 312 1.1 matt * Always try to direct map it since that's nearly zero cost. 313 1.1 matt */ 314 1.1 matt pc->pc_direct_mapped = mm_md_direct_mapped_phys(pa, &pc->pc_kva); 315 1.1 matt #endif 316 1.1 matt if (!pc->pc_direct_mapped) { 317 1.1 matt /* 318 1.1 matt * If we can't direct map it, we have to allocate some KVA 319 1.1 matt * so we map it via the kernel_map. 320 1.1 matt */ 321 1.1 matt pc->pc_kva = uvm_km_alloc(kernel_map, pc->pc_klen, 322 1.3 ad atop(pa) & uvmexp.colormask, 323 1.1 matt UVM_KMF_VAONLY | UVM_KMF_WAITVA | UVM_KMF_COLORMATCH); 324 1.1 matt KASSERT(pc->pc_kva != 0); 325 1.1 matt 326 1.1 matt /* 327 1.1 matt * Setup mappings for this segment. 328 1.1 matt */ 329 1.1 matt for (size_t poff = 0; poff < pc->pc_klen; poff += PAGE_SIZE) { 330 1.1 matt pmap_kenter_pa(pc->pc_kva + poff, pa + poff, 331 1.1 matt pc->pc_prot, 0); 332 1.1 matt } 333 1.1 matt /* 334 1.1 matt * Make them real. 335 1.1 matt */ 336 1.1 matt pmap_update(pmap_kernel()); 337 1.1 matt } 338 1.1 matt /* 339 1.1 matt * Return the starting KVA (including offset into the page) and 340 1.1 matt * the length of this segment. 341 1.1 matt */ 342 1.1 matt *kvap = pc->pc_kva + koff; 343 1.1 matt return len; 344 1.1 matt } 345 1.1 matt 346 1.1 matt void 347 1.1 matt physmap_map_fini(void *cookie) 348 1.1 matt { 349 1.1 matt physmap_cookie_t * const pc = cookie; 350 1.1 matt 351 1.1 matt /* 352 1.1 matt * If there is currently a non-direct mapped KVA region allocated, 353 1.1 matt * free it now. 354 1.1 matt */ 355 1.1 matt if (pc->pc_kva != 0 && !pc->pc_direct_mapped) { 356 1.1 matt pmap_kremove(pc->pc_kva, pc->pc_klen); 357 1.2 rmind pmap_update(pmap_kernel()); 358 1.1 matt uvm_km_free(kernel_map, pc->pc_kva, pc->pc_klen, 359 1.1 matt UVM_KMF_VAONLY); 360 1.1 matt } 361 1.1 matt 362 1.1 matt /* 363 1.1 matt * Free the cookie. 364 1.1 matt */ 365 1.1 matt kmem_free(pc, sizeof(*pc)); 366 1.1 matt } 367 1.1 matt 368 1.1 matt /* 369 1.1 matt * genio needs to zero pages past the EOF or without backing storage (think 370 1.1 matt * sparse files). But since we are using physmaps, there is no kva to use with 371 1.1 matt * memset so we need a helper to obtain a kva and memset the desired memory. 372 1.1 matt */ 373 1.1 matt void 374 1.1 matt physmap_zero(physmap_t *map, size_t offset, size_t len) 375 1.1 matt { 376 1.1 matt void * const cookie = physmap_map_init(map, offset, 377 1.1 matt VM_PROT_READ|VM_PROT_WRITE); 378 1.1 matt 379 1.1 matt for (;;) { 380 1.1 matt vaddr_t kva; 381 1.1 matt size_t seglen = physmap_map(cookie, &kva); 382 1.1 matt KASSERT(seglen != 0); 383 1.1 matt if (seglen > len) 384 1.1 matt seglen = len; 385 1.1 matt memset((void *)kva, 0, seglen); 386 1.1 matt if (seglen == len) 387 1.1 matt break; 388 1.1 matt } 389 1.1 matt 390 1.1 matt physmap_map_fini(cookie); 391 1.1 matt } 392