]> git.saurik.com Git - apple/xnu.git/blame - osfmk/vm/device_vm.c
xnu-7195.101.1.tar.gz
[apple/xnu.git] / osfmk / vm / device_vm.c
CommitLineData
0b4e3aa0 1/*
f427ee49 2 * Copyright (c) 2000-2020 Apple Inc. All rights reserved.
0b4e3aa0 3 *
2d21ac55 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
0a7de745 5 *
2d21ac55
A
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
0a7de745 14 *
2d21ac55
A
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
0a7de745 17 *
2d21ac55
A
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
8f6c56a5
A
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
2d21ac55
A
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
0a7de745 25 *
2d21ac55 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
0b4e3aa0
A
27 */
28
29#include <sys/errno.h>
91447636 30
0b4e3aa0 31#include <mach/mach_types.h>
0b4e3aa0 32#include <mach/kern_return.h>
91447636 33#include <mach/memory_object_control.h>
0b4e3aa0
A
34#include <mach/memory_object_types.h>
35#include <mach/port.h>
36#include <mach/policy.h>
91447636
A
37#include <mach/upl.h>
38#include <kern/kern_types.h>
39#include <kern/ipc_kobject.h>
40#include <kern/host.h>
41#include <kern/thread.h>
0b4e3aa0
A
42#include <ipc/ipc_port.h>
43#include <ipc/ipc_space.h>
0b4e3aa0 44#include <device/device_port.h>
91447636 45#include <vm/memory_object.h>
0b4e3aa0 46#include <vm/vm_pageout.h>
91447636
A
47#include <vm/vm_map.h>
48#include <vm/vm_kern.h>
49#include <vm/vm_pageout.h>
50#include <vm/vm_protos.h>
5ba3f43e 51#include <mach/sdt.h>
0a7de745 52#include <os/refcnt.h>
91447636 53
0b4e3aa0 54
0b4e3aa0
A
55/* Device VM COMPONENT INTERFACES */
56
57
0a7de745
A
58/*
59 * Device PAGER
0b4e3aa0
A
60 */
61
62
63/* until component support available */
64
65
66
67/* until component support available */
0c530ab8 68const struct memory_object_pager_ops device_pager_ops = {
cb323159
A
69 .memory_object_reference = device_pager_reference,
70 .memory_object_deallocate = device_pager_deallocate,
71 .memory_object_init = device_pager_init,
72 .memory_object_terminate = device_pager_terminate,
73 .memory_object_data_request = device_pager_data_request,
74 .memory_object_data_return = device_pager_data_return,
75 .memory_object_data_initialize = device_pager_data_initialize,
76 .memory_object_data_unlock = device_pager_data_unlock,
77 .memory_object_synchronize = device_pager_synchronize,
78 .memory_object_map = device_pager_map,
79 .memory_object_last_unmap = device_pager_last_unmap,
80 .memory_object_data_reclaim = NULL,
a991bd8d 81 .memory_object_backing_object = NULL,
cb323159 82 .memory_object_pager_name = "device pager"
0c530ab8 83};
0b4e3aa0 84
b0d623f7 85typedef uintptr_t device_port_t;
0b4e3aa0 86
0c530ab8
A
87/*
88 * The start of "struct device_pager" MUST match a "struct memory_object".
89 */
0b4e3aa0 90typedef struct device_pager {
5ba3f43e
A
91 /* mandatory generic header */
92 struct memory_object dev_pgr_hdr;
93
94 /* pager-specific data */
0a7de745 95 lck_mtx_t lock;
0b4e3aa0 96 device_port_t device_handle; /* device_handle */
0a7de745 97 vm_size_t size;
c3c9b80d
A
98#if MEMORY_OBJECT_HAS_REFCOUNT
99#define dev_pgr_hdr_ref dev_pgr_hdr.mo_ref
100#else
101 os_ref_atomic_t dev_pgr_hdr_ref;
102#endif
0a7de745
A
103 int flags;
104 boolean_t is_mapped;
0b4e3aa0
A
105} *device_pager_t;
106
c3c9b80d
A
107__header_always_inline os_ref_count_t
108device_pager_get_refcount(device_pager_t device_object)
109{
110 return os_ref_get_count_raw(&device_object->dev_pgr_hdr_ref);
111}
112
f427ee49 113LCK_GRP_DECLARE(device_pager_lck_grp, "device_pager");
0b4e3aa0 114
f427ee49
A
115ZONE_DECLARE(device_pager_zone, "device node pager structures",
116 sizeof(struct device_pager), ZC_NONE);
117
118#define device_pager_lock_init(pager) \
119 lck_mtx_init(&(pager)->lock, &device_pager_lck_grp, LCK_ATTR_NULL)
120#define device_pager_lock_destroy(pager) \
5ba3f43e
A
121 lck_mtx_destroy(&(pager)->lock, &device_pager_lck_grp)
122#define device_pager_lock(pager) lck_mtx_lock(&(pager)->lock)
123#define device_pager_unlock(pager) lck_mtx_unlock(&(pager)->lock)
0b4e3aa0
A
124
125device_pager_t
0a7de745 126device_pager_lookup( /* forward */
0b4e3aa0
A
127 memory_object_t);
128
129device_pager_t
0a7de745 130device_object_create(void); /* forward */
0b4e3aa0 131
0a7de745 132#define DEVICE_PAGER_NULL ((device_pager_t) 0)
0b4e3aa0 133
0a7de745 134#define MAX_DNODE 10000
0b4e3aa0
A
135
136
0b4e3aa0
A
137/*
138 *
139 */
140memory_object_t
141device_pager_setup(
91447636 142 __unused memory_object_t device,
0a7de745
A
143 uintptr_t device_handle,
144 vm_size_t size,
145 int flags)
0b4e3aa0 146{
0a7de745 147 device_pager_t device_object;
5ba3f43e 148 memory_object_control_t control;
0a7de745 149 vm_object_t object;
0b4e3aa0
A
150
151 device_object = device_object_create();
0a7de745 152 if (device_object == DEVICE_PAGER_NULL) {
0b4e3aa0 153 panic("device_pager_setup: device_object_create() failed");
0a7de745 154 }
0b4e3aa0
A
155
156 device_object->device_handle = device_handle;
157 device_object->size = size;
9bccf70c 158 device_object->flags = flags;
0b4e3aa0 159
5ba3f43e 160 memory_object_create_named((memory_object_t) device_object,
0a7de745
A
161 size,
162 &control);
5ba3f43e
A
163 object = memory_object_control_to_vm_object(control);
164
cb323159
A
165 memory_object_mark_trusted(control);
166
5ba3f43e
A
167 assert(object != VM_OBJECT_NULL);
168 vm_object_lock(object);
169 object->true_share = TRUE;
170 if (object->copy_strategy == MEMORY_OBJECT_COPY_SYMMETRIC) {
171 object->copy_strategy = MEMORY_OBJECT_COPY_DELAY;
172 }
173 vm_object_unlock(object);
174
175 return (memory_object_t)device_object;
0b4e3aa0
A
176}
177
178/*
179 *
180 */
181kern_return_t
182device_pager_populate_object(
0a7de745
A
183 memory_object_t device,
184 memory_object_offset_t offset,
185 ppnum_t page_num,
186 vm_size_t size)
0b4e3aa0 187{
0a7de745
A
188 device_pager_t device_object;
189 vm_object_t vm_object;
190 kern_return_t kr;
191 upl_t upl;
0b4e3aa0
A
192
193 device_object = device_pager_lookup(device);
0a7de745 194 if (device_object == DEVICE_PAGER_NULL) {
0b4e3aa0 195 return KERN_FAILURE;
0a7de745 196 }
0b4e3aa0
A
197
198 vm_object = (vm_object_t)memory_object_control_to_vm_object(
0a7de745
A
199 device_object->dev_pgr_hdr.mo_control);
200 if (vm_object == NULL) {
0b4e3aa0 201 return KERN_FAILURE;
0a7de745 202 }
0b4e3aa0
A
203
204 kr = vm_object_populate_with_private(
0a7de745
A
205 vm_object, offset, page_num, size);
206 if (kr != KERN_SUCCESS) {
0b4e3aa0 207 return kr;
0a7de745 208 }
0b4e3aa0 209
0a7de745 210 if (!vm_object->phys_contiguous) {
0c530ab8 211 unsigned int null_size = 0;
b0d623f7 212 assert((upl_size_t) size == size);
0a7de745
A
213 kr = vm_object_upl_request(vm_object,
214 (vm_object_offset_t)offset,
215 (upl_size_t) size, &upl, NULL,
216 &null_size,
217 (UPL_NO_SYNC | UPL_CLEAN_IN_PLACE),
218 VM_KERN_MEMORY_NONE);
219 if (kr != KERN_SUCCESS) {
0b4e3aa0 220 panic("device_pager_populate_object: list_req failed");
0a7de745 221 }
0b4e3aa0 222
91447636 223 upl_commit(upl, NULL, 0);
0b4e3aa0
A
224 upl_deallocate(upl);
225 }
226
227
228 return kr;
229}
230
231/*
232 *
233 */
234device_pager_t
235device_pager_lookup(
0a7de745 236 memory_object_t mem_obj)
0b4e3aa0 237{
0a7de745 238 device_pager_t device_object;
0b4e3aa0 239
5ba3f43e
A
240 assert(mem_obj->mo_pager_ops == &device_pager_ops);
241 device_object = (device_pager_t)mem_obj;
c3c9b80d 242 assert(device_pager_get_refcount(device_object) > 0);
5ba3f43e 243 return device_object;
0b4e3aa0
A
244}
245
246/*
247 *
248 */
249kern_return_t
91447636 250device_pager_init(
0a7de745
A
251 memory_object_t mem_obj,
252 memory_object_control_t control,
b0d623f7 253 __unused memory_object_cluster_size_t pg_size)
0b4e3aa0
A
254{
255 device_pager_t device_object;
256 kern_return_t kr;
257 memory_object_attr_info_data_t attributes;
258
0a7de745 259 vm_object_t vm_object;
0b4e3aa0
A
260
261
0a7de745 262 if (control == MEMORY_OBJECT_CONTROL_NULL) {
0b4e3aa0 263 return KERN_INVALID_ARGUMENT;
0a7de745 264 }
0b4e3aa0
A
265
266 device_object = device_pager_lookup(mem_obj);
267
268 memory_object_control_reference(control);
5ba3f43e 269 device_object->dev_pgr_hdr.mo_control = control;
0b4e3aa0
A
270
271
272/* The following settings should be done through an expanded change */
273/* attributes call */
274
275 vm_object = (vm_object_t)memory_object_control_to_vm_object(control);
276 vm_object_lock(vm_object);
277 vm_object->private = TRUE;
0a7de745 278 if (device_object->flags & DEVICE_PAGER_CONTIGUOUS) {
0b4e3aa0 279 vm_object->phys_contiguous = TRUE;
0a7de745
A
280 }
281 if (device_object->flags & DEVICE_PAGER_NOPHYSCACHE) {
0b4e3aa0 282 vm_object->nophyscache = TRUE;
0a7de745 283 }
9bccf70c
A
284
285 vm_object->wimg_bits = device_object->flags & VM_WIMG_MASK;
0b4e3aa0
A
286 vm_object_unlock(vm_object);
287
288
289 attributes.copy_strategy = MEMORY_OBJECT_COPY_DELAY;
290 /* attributes.cluster_size = (1 << (CLUSTER_SHIFT + PAGE_SHIFT));*/
291 attributes.cluster_size = (1 << (PAGE_SHIFT));
292 attributes.may_cache_object = FALSE;
293 attributes.temporary = TRUE;
294
295 kr = memory_object_change_attributes(
0a7de745
A
296 control,
297 MEMORY_OBJECT_ATTRIBUTE_INFO,
298 (memory_object_info_t) &attributes,
299 MEMORY_OBJECT_ATTR_INFO_COUNT);
300 if (kr != KERN_SUCCESS) {
0b4e3aa0 301 panic("device_pager_init: memory_object_change_attributes() failed");
0a7de745 302 }
0b4e3aa0 303
0a7de745 304 return KERN_SUCCESS;
0b4e3aa0
A
305}
306
307/*
308 *
309 */
91447636 310/*ARGSUSED6*/
0b4e3aa0
A
311kern_return_t
312device_pager_data_return(
0a7de745
A
313 memory_object_t mem_obj,
314 memory_object_offset_t offset,
315 memory_object_cluster_size_t data_cnt,
316 __unused memory_object_offset_t *resid_offset,
317 __unused int *io_error,
318 __unused boolean_t dirty,
319 __unused boolean_t kernel_copy,
320 __unused int upl_flags)
0b4e3aa0 321{
0a7de745 322 device_pager_t device_object;
0b4e3aa0
A
323
324 device_object = device_pager_lookup(mem_obj);
0a7de745 325 if (device_object == DEVICE_PAGER_NULL) {
0b4e3aa0 326 panic("device_pager_data_return: lookup failed");
0a7de745 327 }
0b4e3aa0 328
3e170ce0 329 __IGNORE_WCASTALIGN(return device_data_action(device_object->device_handle,
0a7de745
A
330 (ipc_port_t) device_object,
331 VM_PROT_READ | VM_PROT_WRITE,
332 offset, data_cnt));
0b4e3aa0
A
333}
334
335/*
336 *
337 */
0a7de745 338kern_return_t
0b4e3aa0 339device_pager_data_request(
0a7de745
A
340 memory_object_t mem_obj,
341 memory_object_offset_t offset,
342 memory_object_cluster_size_t length,
343 __unused vm_prot_t protection_required,
344 __unused memory_object_fault_info_t fault_info)
0b4e3aa0 345{
0a7de745 346 device_pager_t device_object;
0b4e3aa0
A
347
348 device_object = device_pager_lookup(mem_obj);
349
0a7de745 350 if (device_object == DEVICE_PAGER_NULL) {
0b4e3aa0 351 panic("device_pager_data_request: lookup failed");
0a7de745 352 }
0b4e3aa0 353
3e170ce0 354 __IGNORE_WCASTALIGN(device_data_action(device_object->device_handle,
0a7de745
A
355 (ipc_port_t) device_object,
356 VM_PROT_READ, offset, length));
0b4e3aa0
A
357 return KERN_SUCCESS;
358}
359
360/*
361 *
362 */
363void
364device_pager_reference(
0a7de745
A
365 memory_object_t mem_obj)
366{
367 device_pager_t device_object;
0b4e3aa0
A
368
369 device_object = device_pager_lookup(mem_obj);
c3c9b80d 370 os_ref_retain_raw(&device_object->dev_pgr_hdr_ref, NULL);
0a7de745
A
371 DTRACE_VM2(device_pager_reference,
372 device_pager_t, device_object,
c3c9b80d 373 unsigned int, device_pager_get_refcount(device_object));
0b4e3aa0
A
374}
375
376/*
377 *
378 */
379void
380device_pager_deallocate(
0a7de745 381 memory_object_t mem_obj)
0b4e3aa0 382{
0a7de745
A
383 device_pager_t device_object;
384 memory_object_control_t device_control;
c3c9b80d 385 os_ref_count_t ref_count;
0b4e3aa0
A
386
387 device_object = device_pager_lookup(mem_obj);
5ba3f43e
A
388
389 DTRACE_VM2(device_pager_deallocate,
0a7de745 390 device_pager_t, device_object,
c3c9b80d 391 unsigned int, device_pager_get_refcount(device_object));
5ba3f43e 392
c3c9b80d 393 ref_count = os_ref_release_raw(&device_object->dev_pgr_hdr_ref, NULL);
5ba3f43e
A
394
395 if (ref_count == 1) {
396 /*
397 * The last reference is our "named" reference.
398 * Close the device and "destroy" the VM object.
399 */
400
401 DTRACE_VM2(device_pager_destroy,
0a7de745 402 device_pager_t, device_object,
c3c9b80d 403 unsigned int, device_pager_get_refcount(device_object));
0b4e3aa0 404
5ba3f43e 405 assert(device_object->is_mapped == FALSE);
0b4e3aa0
A
406 if (device_object->device_handle != (device_port_t) NULL) {
407 device_close(device_object->device_handle);
91447636 408 device_object->device_handle = (device_port_t) NULL;
0b4e3aa0 409 }
5ba3f43e
A
410 device_control = device_object->dev_pgr_hdr.mo_control;
411 memory_object_destroy(device_control, 0);
412 } else if (ref_count == 0) {
413 /*
414 * No more references: free the pager.
415 */
416 DTRACE_VM2(device_pager_free,
0a7de745 417 device_pager_t, device_object,
c3c9b80d 418 unsigned int, device_pager_get_refcount(device_object));
5ba3f43e 419
c3c9b80d
A
420 device_control = device_object->dev_pgr_hdr.mo_control;
421
422 if (device_control != MEMORY_OBJECT_CONTROL_NULL) {
423 memory_object_control_deallocate(device_control);
424 device_object->dev_pgr_hdr.mo_control = MEMORY_OBJECT_CONTROL_NULL;
425 }
5ba3f43e 426 device_pager_lock_destroy(device_object);
91447636
A
427
428 zfree(device_pager_zone, device_object);
0b4e3aa0
A
429 }
430 return;
431}
432
433kern_return_t
434device_pager_data_initialize(
0a7de745
A
435 __unused memory_object_t mem_obj,
436 __unused memory_object_offset_t offset,
437 __unused memory_object_cluster_size_t data_cnt)
0b4e3aa0 438{
91447636 439 panic("device_pager_data_initialize");
0b4e3aa0
A
440 return KERN_FAILURE;
441}
442
443kern_return_t
444device_pager_data_unlock(
0a7de745
A
445 __unused memory_object_t mem_obj,
446 __unused memory_object_offset_t offset,
447 __unused memory_object_size_t size,
448 __unused vm_prot_t desired_access)
0b4e3aa0
A
449{
450 return KERN_FAILURE;
451}
452
91447636 453kern_return_t
0b4e3aa0 454device_pager_terminate(
0a7de745 455 __unused memory_object_t mem_obj)
0b4e3aa0
A
456{
457 return KERN_SUCCESS;
458}
459
460
461
462/*
463 *
464 */
465kern_return_t
466device_pager_synchronize(
0a7de745
A
467 __unused memory_object_t mem_obj,
468 __unused memory_object_offset_t offset,
469 __unused memory_object_size_t length,
470 __unused vm_sync_t sync_flags)
0b4e3aa0 471{
5ba3f43e
A
472 panic("device_pager_synchronize: memory_object_synchronize no longer supported\n");
473 return KERN_FAILURE;
0b4e3aa0
A
474}
475
476/*
477 *
478 */
479kern_return_t
593a1d5f 480device_pager_map(
0a7de745
A
481 memory_object_t mem_obj,
482 __unused vm_prot_t prot)
593a1d5f 483{
0a7de745 484 device_pager_t device_object;
5ba3f43e
A
485
486 device_object = device_pager_lookup(mem_obj);
487
488 device_pager_lock(device_object);
c3c9b80d 489 assert(device_pager_get_refcount(device_object) > 0);
5ba3f43e
A
490 if (device_object->is_mapped == FALSE) {
491 /*
492 * First mapping of this pager: take an extra reference
493 * that will remain until all the mappings of this pager
494 * are removed.
495 */
496 device_object->is_mapped = TRUE;
497 device_pager_reference(mem_obj);
498 }
499 device_pager_unlock(device_object);
500
593a1d5f
A
501 return KERN_SUCCESS;
502}
503
504kern_return_t
505device_pager_last_unmap(
0a7de745 506 memory_object_t mem_obj)
0b4e3aa0 507{
0a7de745
A
508 device_pager_t device_object;
509 boolean_t drop_ref;
5ba3f43e
A
510
511 device_object = device_pager_lookup(mem_obj);
512
513 device_pager_lock(device_object);
c3c9b80d 514 assert(device_pager_get_refcount(device_object) > 0);
5ba3f43e
A
515 if (device_object->is_mapped) {
516 device_object->is_mapped = FALSE;
517 drop_ref = TRUE;
518 } else {
519 drop_ref = FALSE;
520 }
521 device_pager_unlock(device_object);
522
523 if (drop_ref) {
524 device_pager_deallocate(mem_obj);
525 }
526
0b4e3aa0
A
527 return KERN_SUCCESS;
528}
529
530
531
532/*
533 *
534 */
535device_pager_t
2d21ac55 536device_object_create(void)
0b4e3aa0 537{
39037602 538 device_pager_t device_object;
0b4e3aa0
A
539
540 device_object = (struct device_pager *) zalloc(device_pager_zone);
0a7de745
A
541 if (device_object == DEVICE_PAGER_NULL) {
542 return DEVICE_PAGER_NULL;
543 }
5ba3f43e 544
0a7de745 545 bzero(device_object, sizeof(*device_object));
5ba3f43e
A
546
547 device_object->dev_pgr_hdr.mo_ikot = IKOT_MEMORY_OBJECT;
548 device_object->dev_pgr_hdr.mo_pager_ops = &device_pager_ops;
549 device_object->dev_pgr_hdr.mo_control = MEMORY_OBJECT_CONTROL_NULL;
550
551 device_pager_lock_init(device_object);
c3c9b80d 552 os_ref_init_raw(&device_object->dev_pgr_hdr_ref, NULL);
5ba3f43e 553 device_object->is_mapped = FALSE;
0b4e3aa0 554
5ba3f43e 555 DTRACE_VM2(device_pager_create,
0a7de745 556 device_pager_t, device_object,
c3c9b80d 557 unsigned int, device_pager_get_refcount(device_object));
0b4e3aa0 558
0a7de745 559 return device_object;
0b4e3aa0
A
560}
561
39037602
A
562boolean_t
563is_device_pager_ops(const struct memory_object_pager_ops *pager_ops)
564{
565 if (pager_ops == &device_pager_ops) {
566 return TRUE;
567 }
568 return FALSE;
569}