Improved reproting of kernel return codes.
[WebKit-https.git] / Source / WebKit2 / Platform / mac / SharedMemoryMac.cpp
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_port.h>
34 #include <mach/mach_vm.h>
35 #include <mach/vm_map.h>
36 #include <wtf/RefPtr.h>
37
38 namespace WebKit {
39
40 SharedMemory::Handle::Handle()
41     : m_port(MACH_PORT_NULL)
42     , m_size(0)
43 {
44 }
45
46 SharedMemory::Handle::~Handle()
47 {
48     if (m_port)
49         mach_port_deallocate(mach_task_self(), m_port);
50 }
51
52 bool SharedMemory::Handle::isNull() const
53 {
54     return !m_port;
55 }
56
57 void SharedMemory::Handle::encode(CoreIPC::ArgumentEncoder* encoder) const
58 {
59     encoder->encodeUInt64(m_size);
60     encoder->encode(CoreIPC::MachPort(m_port, MACH_MSG_TYPE_MOVE_SEND));
61     m_port = MACH_PORT_NULL;
62 }
63
64 bool SharedMemory::Handle::decode(CoreIPC::ArgumentDecoder* decoder, Handle& handle)
65 {
66     ASSERT(!handle.m_port);
67     ASSERT(!handle.m_size);
68
69     uint64_t size;
70     if (!decoder->decodeUInt64(size))
71         return false;
72
73     CoreIPC::MachPort machPort;
74     if (!decoder->decode(CoreIPC::Out(machPort)))
75         return false;
76     
77     handle.m_size = size;
78     handle.m_port = machPort.port();
79     return true;
80 }
81
82 static inline void* toPointer(mach_vm_address_t address)
83 {
84     return reinterpret_cast<void*>(static_cast<uintptr_t>(address));
85 }
86
87 static inline mach_vm_address_t toVMAddress(void* pointer)
88 {
89     return static_cast<mach_vm_address_t>(reinterpret_cast<uintptr_t>(pointer));
90 }
91     
92 PassRefPtr<SharedMemory> SharedMemory::create(size_t size)
93 {
94     ASSERT(size);
95
96     mach_vm_address_t address;
97     kern_return_t kr = mach_vm_allocate(mach_task_self(), &address, round_page(size), VM_FLAGS_ANYWHERE);
98     if (kr != KERN_SUCCESS) {
99         LOG_ERROR("Failed to allocate mach_vm_allocate shared memory (%zu bytes). %s (%x)", size, mach_error_string(kr), kr); 
100         return 0;
101     }
102
103     // Create a Mach port that represents the shared memory.
104     mach_port_t port;
105     memory_object_size_t memoryObjectSize = round_page(size);
106     kr = mach_make_memory_entry_64(mach_task_self(), &memoryObjectSize, address, VM_PROT_DEFAULT, &port, MACH_PORT_NULL);
107
108     if (kr != KERN_SUCCESS) {
109         LOG_ERROR("Failed to create a mach port for shared memory. %s (%x)", mach_error_string(kr), kr);
110         mach_vm_deallocate(mach_task_self(), address, round_page(size));
111         return 0;
112     }
113
114     ASSERT(memoryObjectSize >= round_page(size));
115
116     RefPtr<SharedMemory> sharedMemory(adoptRef(new SharedMemory));
117     sharedMemory->m_size = size;
118     sharedMemory->m_data = toPointer(address);
119     sharedMemory->m_port = port;
120
121     return sharedMemory.release();
122 }
123
124 static inline vm_prot_t machProtection(SharedMemory::Protection protection)
125 {
126     switch (protection) {
127     case SharedMemory::ReadOnly:
128         return VM_PROT_READ;
129     case SharedMemory::ReadWrite:
130         return VM_PROT_READ | VM_PROT_WRITE;
131     }
132
133     ASSERT_NOT_REACHED();
134     return VM_PROT_NONE;    
135 }
136
137 PassRefPtr<SharedMemory> SharedMemory::create(const Handle& handle, Protection protection)
138 {
139     if (handle.isNull())
140         return 0;
141     
142     // Map the memory.
143     vm_prot_t vmProtection = machProtection(protection);
144     mach_vm_address_t mappedAddress = 0;
145     kern_return_t kr = mach_vm_map(mach_task_self(), &mappedAddress, handle.m_size, 0, VM_FLAGS_ANYWHERE, handle.m_port, 0, false, vmProtection, vmProtection, VM_INHERIT_NONE);
146     if (kr != KERN_SUCCESS)
147         return 0;
148
149     RefPtr<SharedMemory> sharedMemory(adoptRef(new SharedMemory));
150     sharedMemory->m_size = handle.m_size;
151     sharedMemory->m_data = toPointer(mappedAddress);
152     sharedMemory->m_port = MACH_PORT_NULL;
153
154     return sharedMemory.release();
155 }
156
157 SharedMemory::~SharedMemory()
158 {
159     if (m_data) {
160         kern_return_t kr = mach_vm_deallocate(mach_task_self(), toVMAddress(m_data), round_page(m_size));
161         ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
162     }
163
164     if (m_port) {
165         kern_return_t kr = mach_port_deallocate(mach_task_self(), m_port);
166         ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
167     }        
168 }
169     
170 bool SharedMemory::createHandle(Handle& handle, Protection protection)
171 {
172     ASSERT(!handle.m_port);
173     ASSERT(!handle.m_size);
174
175     mach_vm_address_t address = toVMAddress(m_data);
176     memory_object_size_t size = round_page(m_size);
177
178     mach_port_t port;
179
180     if (protection == ReadWrite && m_port) {
181         // Just re-use the port we have.
182         port = m_port;
183         if (mach_port_mod_refs(mach_task_self(), port, MACH_PORT_RIGHT_SEND, 1) != KERN_SUCCESS)
184             return false;
185     } else {
186         // Create a mach port that represents the shared memory.
187         kern_return_t kr = mach_make_memory_entry_64(mach_task_self(), &size, address, machProtection(protection), &port, MACH_PORT_NULL);
188         if (kr != KERN_SUCCESS)
189             return false;
190
191         ASSERT(size >= round_page(m_size));
192     }
193
194     handle.m_port = port;
195     handle.m_size = size;
196
197     return true;
198 }
199
200 unsigned SharedMemory::systemPageSize()
201 {
202     return vm_page_size;
203 }
204
205 } // namespace WebKit