1/* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "SharedMemory.h" 28 29#include "ArgumentDecoder.h" 30#include "ArgumentEncoder.h" 31#include "Arguments.h" 32#include "MachPort.h" 33#include <mach/mach_error.h> 34#include <mach/mach_port.h> 35#include <mach/mach_vm.h> 36#include <mach/vm_map.h> 37#include <wtf/RefPtr.h> 38 39namespace WebKit { 40 41SharedMemory::Handle::Handle() 42 : m_port(MACH_PORT_NULL) 43 , m_size(0) 44{ 45} 46 47SharedMemory::Handle::~Handle() 48{ 49 clear(); 50} 51 52bool SharedMemory::Handle::isNull() const 53{ 54 return !m_port; 55} 56 57void SharedMemory::Handle::clear() 58{ 59 if (m_port) 60 mach_port_deallocate(mach_task_self(), m_port); 61 62 m_port = MACH_PORT_NULL; 63 m_size = 0; 64} 65 66void SharedMemory::Handle::encode(IPC::ArgumentEncoder& encoder) const 67{ 68 encoder << static_cast<uint64_t>(m_size); 69 encoder << IPC::MachPort(m_port, MACH_MSG_TYPE_MOVE_SEND); 70 m_port = MACH_PORT_NULL; 71} 72 73bool SharedMemory::Handle::decode(IPC::ArgumentDecoder& decoder, Handle& handle) 74{ 75 ASSERT(!handle.m_port); 76 ASSERT(!handle.m_size); 77 78 uint64_t size; 79 if (!decoder.decode(size)) 80 return false; 81 82 IPC::MachPort machPort; 83 if (!decoder.decode(machPort)) 84 return false; 85 86 handle.m_size = size; 87 handle.m_port = machPort.port(); 88 return true; 89} 90 91static inline void* toPointer(mach_vm_address_t address) 92{ 93 return reinterpret_cast<void*>(static_cast<uintptr_t>(address)); 94} 95 96static inline mach_vm_address_t toVMAddress(void* pointer) 97{ 98 return static_cast<mach_vm_address_t>(reinterpret_cast<uintptr_t>(pointer)); 99} 100 101PassRefPtr<SharedMemory> SharedMemory::create(size_t size) 102{ 103 ASSERT(size); 104 105 mach_vm_address_t address; 106 kern_return_t kr = mach_vm_allocate(mach_task_self(), &address, round_page(size), VM_FLAGS_ANYWHERE); 107 if (kr != KERN_SUCCESS) { 108 LOG_ERROR("Failed to allocate mach_vm_allocate shared memory (%zu bytes). %s (%x)", size, mach_error_string(kr), kr); 109 return 0; 110 } 111 112 RefPtr<SharedMemory> sharedMemory = createFromVMBuffer(toPointer(address), size); 113 if (!sharedMemory) { 114 mach_vm_deallocate(mach_task_self(), address, round_page(size)); 115 return 0; 116 } 117 118 sharedMemory->m_shouldVMDeallocateData = true; 119 return sharedMemory.release(); 120} 121 122PassRefPtr<SharedMemory> SharedMemory::createFromVMBuffer(void* data, size_t size) 123{ 124 ASSERT(size); 125 126 // Create a Mach port that represents the shared memory. 127 mach_port_t port; 128 memory_object_size_t memoryObjectSize = round_page(size); 129 kern_return_t kr = mach_make_memory_entry_64(mach_task_self(), &memoryObjectSize, toVMAddress(data), VM_PROT_DEFAULT | VM_PROT_IS_MASK, &port, MACH_PORT_NULL); 130 131 if (kr != KERN_SUCCESS) { 132 LOG_ERROR("Failed to create a mach port for shared memory. %s (%x)", mach_error_string(kr), kr); 133 return 0; 134 } 135 136 if (memoryObjectSize < round_page(size)) { 137 // There is a limit on how large a shared memory object can be (see <rdar://problem/16595870>). 138 LOG_ERROR("Failed to create a mach port for shared memory of size %lu (got %llu bytes).", round_page(size), memoryObjectSize); 139 mach_port_deallocate(mach_task_self(), port); 140 return 0; 141 } 142 143 RefPtr<SharedMemory> sharedMemory(adoptRef(new SharedMemory)); 144 sharedMemory->m_size = size; 145 sharedMemory->m_data = data; 146 sharedMemory->m_shouldVMDeallocateData = false; 147 sharedMemory->m_port = port; 148 149 return sharedMemory.release(); 150} 151 152static inline vm_prot_t machProtection(SharedMemory::Protection protection) 153{ 154 switch (protection) { 155 case SharedMemory::ReadOnly: 156 return VM_PROT_READ; 157 case SharedMemory::ReadWrite: 158 return VM_PROT_READ | VM_PROT_WRITE; 159 } 160 161 ASSERT_NOT_REACHED(); 162 return VM_PROT_NONE; 163} 164 165PassRefPtr<SharedMemory> SharedMemory::create(const Handle& handle, Protection protection) 166{ 167 if (handle.isNull()) 168 return 0; 169 170 ASSERT(round_page(handle.m_size) == handle.m_size); 171 172 // Map the memory. 173 vm_prot_t vmProtection = machProtection(protection); 174 mach_vm_address_t mappedAddress = 0; 175 kern_return_t kr = mach_vm_map(mach_task_self(), &mappedAddress, round_page(handle.m_size), 0, VM_FLAGS_ANYWHERE, handle.m_port, 0, false, vmProtection, vmProtection, VM_INHERIT_NONE); 176 if (kr != KERN_SUCCESS) 177 return 0; 178 179 RefPtr<SharedMemory> sharedMemory(adoptRef(new SharedMemory)); 180 sharedMemory->m_size = handle.m_size; 181 sharedMemory->m_data = toPointer(mappedAddress); 182 sharedMemory->m_shouldVMDeallocateData = true; 183 sharedMemory->m_port = MACH_PORT_NULL; 184 185 return sharedMemory.release(); 186} 187 188SharedMemory::~SharedMemory() 189{ 190 if (m_data && m_shouldVMDeallocateData) { 191 kern_return_t kr = mach_vm_deallocate(mach_task_self(), toVMAddress(m_data), round_page(m_size)); 192 ASSERT_UNUSED(kr, kr == KERN_SUCCESS); 193 } 194 195 if (m_port) { 196 kern_return_t kr = mach_port_deallocate(mach_task_self(), m_port); 197 ASSERT_UNUSED(kr, kr == KERN_SUCCESS); 198 } 199} 200 201bool SharedMemory::createHandle(Handle& handle, Protection protection) 202{ 203 ASSERT(!handle.m_port); 204 ASSERT(!handle.m_size); 205 206 mach_vm_address_t address = toVMAddress(m_data); 207 memory_object_size_t size = round_page(m_size); 208 209 mach_port_t port; 210 211 if (protection == ReadWrite && m_port) { 212 // Just re-use the port we have. 213 port = m_port; 214 if (mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1) != KERN_SUCCESS) 215 return false; 216 } else { 217 // Create a mach port that represents the shared memory. 218 kern_return_t kr = mach_make_memory_entry_64(mach_task_self(), &size, address, machProtection(protection), &port, MACH_PORT_NULL); 219 if (kr != KERN_SUCCESS) 220 return false; 221 222 ASSERT(size >= round_page(m_size)); 223 if (size < round_page(m_size)) { 224 mach_port_deallocate(mach_task_self(), port); 225 return false; 226 } 227 } 228 229 handle.m_port = port; 230 handle.m_size = size; 231 232 return true; 233} 234 235unsigned SharedMemory::systemPageSize() 236{ 237 return vm_page_size; 238} 239 240} // namespace WebKit 241