1 /* 2 * Copyright (c) 2025 Apple Inc. All rights reserved. 3 * 4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@ 5 * 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. 14 * 15 * Please obtain a copy of the License at 16 * http://www.opensource.apple.com/apsl/ and read it before using this file. 17 * 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 20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 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. 25 * 26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@ 27 */ 28 #include <darwintest/darwintest.h> 29 #include <mach/error.h> 30 #include <mach/mach.h> 31 #include <mach/mach_vm.h> 32 #include <mach/memory_entry.h> 33 #include <mach/memory_object_types.h> 34 #include <mach/vm_page_size.h> 35 #include <string.h> 36 #include <sys/mman.h> 37 #include <sys/sysctl.h> 38 #include <unistd.h> 39 40 T_GLOBAL_META( 41 T_META_NAMESPACE("xnu.vm"), 42 T_META_RADAR_COMPONENT_NAME("xnu"), 43 T_META_RADAR_COMPONENT_VERSION("VM"), 44 T_META_TAG_VM_PREFERRED); 45 46 47 T_DECL(memory_entry_page_counts, 48 "Test that page counts are computed correctly for memory entries") 49 { 50 mach_error_t err; 51 int ret; 52 uint64_t pages = 1024; 53 mach_vm_size_t size = pages * vm_page_size; 54 mach_port_t memory_entry = MACH_PORT_NULL; 55 uint64_t resident, dirty, swapped; 56 57 T_LOG("Creating memory entry"); 58 err = mach_make_memory_entry_64(mach_task_self(), &size, 59 (memory_object_offset_t)0, 60 (MAP_MEM_NAMED_CREATE | MAP_MEM_LEDGER_TAGGED | VM_PROT_DEFAULT), 61 &memory_entry, MEMORY_OBJECT_NULL); 62 T_QUIET; T_ASSERT_MACH_SUCCESS(err, "mach_make_memory_entry()"); 63 T_QUIET; T_ASSERT_NE(memory_entry, MACH_PORT_NULL, "memory entry is non-null"); 64 65 T_LOG("Mapping memory entry"); 66 mach_vm_address_t addr; 67 err = mach_vm_map(mach_task_self(), &addr, size, 0, 68 VM_FLAGS_ANYWHERE, memory_entry, 0, FALSE, 69 VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_NONE); 70 T_QUIET; T_ASSERT_MACH_SUCCESS(err, "mach_vm_map()"); 71 72 T_LOG("Querying page counts"); 73 ret = mach_memory_entry_get_page_counts(memory_entry, &resident, &dirty, &swapped); 74 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "mach_memory_entry_get_page_counts()"); 75 76 T_EXPECT_EQ(resident, 0ull, "Entry should have no resident pages"); 77 T_EXPECT_EQ(dirty, 0ull, "Entry should have no dirty pages"); 78 T_EXPECT_EQ(swapped, 0ull, "Entry should have no swapped pages"); 79 80 T_LOG("Faulting mapping"); 81 memset((void *)addr, 0xAB, size); 82 83 T_LOG("Wiring mapping"); 84 ret = mlock((void *)addr, size); 85 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "mlock()"); 86 87 T_LOG("Querying page counts"); 88 ret = mach_memory_entry_get_page_counts(memory_entry, &resident, &dirty, &swapped); 89 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "mach_memory_entry_get_page_counts()"); 90 91 T_EXPECT_EQ(resident, pages, "Entry should have all resident pages"); 92 T_EXPECT_EQ(dirty, pages, "Entry should have all dirty pages"); 93 T_EXPECT_EQ(swapped, 0ull, "Entry should have no swapped pages"); 94 95 T_LOG("Un-wiring mapping"); 96 ret = munlock((void *)addr, size); 97 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "munlock()"); 98 99 T_LOG("Evicting backing pages..."); 100 ret = madvise((void *)addr, size, MADV_PAGEOUT); 101 T_QUIET; T_ASSERT_POSIX_SUCCESS(ret, "madvise()"); 102 103 /* MADV_PAGEOUT is asynchronous */ 104 sleep(1); 105 106 T_LOG("Querying page counts"); 107 ret = mach_memory_entry_get_page_counts(memory_entry, &resident, &dirty, &swapped); 108 T_QUIET; T_ASSERT_MACH_SUCCESS(ret, "mach_memory_entry_get_page_counts()"); 109 110 T_EXPECT_EQ(resident, 0ull, "Entry should have no resident pages"); 111 T_EXPECT_EQ(dirty, 0ull, "Entry should have no dirty pages"); 112 T_EXPECT_EQ(swapped, pages, "Entry should have all swapped pages"); 113 114 err = mach_vm_deallocate(mach_task_self(), addr, size); 115 T_QUIET; T_ASSERT_MACH_SUCCESS(err, "mach_vm_deallocate()"); 116 117 err = mach_port_deallocate(mach_task_self(), memory_entry); 118 T_QUIET; T_ASSERT_MACH_SUCCESS(err, "mach_port_deallocate()"); 119 } 120 121 T_DECL(memory_entry_partial_fixed_mapping, 122 "Test a fixed mapping of a subset of a memory entry") 123 { 124 mach_error_t err; 125 mach_vm_address_t map_addr; 126 uint64_t map_pages = 5; 127 mach_vm_size_t map_size = map_pages * vm_page_size; 128 uint64_t me_pages = 3; 129 mach_vm_size_t me_size = me_pages * vm_page_size; 130 mach_port_t memory_entry = MACH_PORT_NULL; 131 int i; 132 133 T_LOG("Creating mapping"); 134 map_addr = 0; 135 err = mach_vm_allocate(mach_task_self(), &map_addr, map_size, VM_FLAGS_ANYWHERE); 136 T_QUIET; T_ASSERT_MACH_SUCCESS(err, "mach_vm_allocate()"); 137 for (i = 0; i < map_pages; i++) { 138 memset(&((char *)map_addr)[i * vm_page_size], 'a' + i, vm_page_size); 139 T_LOG("map_addr[0x%lx] = '%c' expected '%c'", i * vm_page_size, ((unsigned char *)map_addr)[i * vm_page_size], 'a' + i); 140 T_QUIET; T_ASSERT_EQ(((char *)map_addr)[i * vm_page_size], 'a' + i, "mapping contents are correct"); 141 } 142 143 T_LOG("Creating memory entry"); 144 err = mach_make_memory_entry_64(mach_task_self(), &me_size, 145 map_addr, 146 (MAP_MEM_VM_SHARE | VM_PROT_DEFAULT), 147 &memory_entry, MEMORY_OBJECT_NULL); 148 T_QUIET; T_ASSERT_MACH_SUCCESS(err, "mach_make_memory_entry()"); 149 T_QUIET; T_ASSERT_NE(memory_entry, MACH_PORT_NULL, "memory entry is non-null"); 150 151 T_LOG("Mapping partial memory entry over original mapping"); 152 mach_vm_address_t remap_addr; 153 int remap_offset = 2; 154 int bad_contents = 0; 155 remap_addr = map_addr + (remap_offset * vm_page_size); 156 err = mach_vm_map(mach_task_self(), &remap_addr, vm_page_size, 0, 157 VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE, memory_entry, vm_page_size, FALSE, 158 VM_PROT_DEFAULT, VM_PROT_DEFAULT, VM_INHERIT_NONE); 159 T_QUIET; T_ASSERT_EQ(remap_addr, map_addr + (2 * vm_page_size), "fixed address"); 160 161 if (err == KERN_INVALID_ARGUMENT) { 162 /* failing is OK; contents should be unchanged */ 163 for (i = 0; i < map_pages; i++) { 164 unsigned char expected = 'a' + i; 165 T_LOG("map_addr[0x%lx] = '%c' expected '%c'", i * vm_page_size, ((unsigned char *)map_addr)[i * vm_page_size], expected); 166 if (((char *)map_addr)[i * vm_page_size] != expected) { 167 bad_contents++; 168 } 169 } 170 T_ASSERT_EQ(bad_contents, 0, "new contents correct"); 171 T_ASSERT_MACH_ERROR(err, KERN_INVALID_ARGUMENT, "mach_vm_map() was rejected"); 172 } else { 173 /* if it succeeds, the contents should be as follows */ 174 for (i = 0; i < map_pages; i++) { 175 unsigned char expected = 'a' + i; 176 if (i == remap_offset) { 177 expected = 'b'; 178 } 179 T_LOG("map_addr[0x%lx] = '%c' expected '%c'", i * vm_page_size, ((unsigned char *)map_addr)[i * vm_page_size], expected); 180 if (((char *)map_addr)[i * vm_page_size] != expected) { 181 bad_contents++; 182 } 183 } 184 T_ASSERT_EQ(bad_contents, 0, "new contents correct"); 185 } 186 187 /* cleanup */ 188 err = mach_vm_deallocate(mach_task_self(), map_addr, map_size); 189 T_QUIET; T_ASSERT_MACH_SUCCESS(err, "mach_vm_deallocate()"); 190 191 err = mach_port_deallocate(mach_task_self(), memory_entry); 192 T_QUIET; T_ASSERT_MACH_SUCCESS(err, "mach_port_deallocate()"); 193 } 194