1*6777b538SAndroid Build Coastguard Worker /*
2*6777b538SAndroid Build Coastguard Worker * Copyright (C) 2008 The Android Open Source Project
3*6777b538SAndroid Build Coastguard Worker *
4*6777b538SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*6777b538SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*6777b538SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*6777b538SAndroid Build Coastguard Worker *
8*6777b538SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*6777b538SAndroid Build Coastguard Worker *
10*6777b538SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*6777b538SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*6777b538SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*6777b538SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*6777b538SAndroid Build Coastguard Worker * limitations under the License.
15*6777b538SAndroid Build Coastguard Worker */
16*6777b538SAndroid Build Coastguard Worker
17*6777b538SAndroid Build Coastguard Worker #include "ashmem.h"
18*6777b538SAndroid Build Coastguard Worker
19*6777b538SAndroid Build Coastguard Worker #include <dlfcn.h>
20*6777b538SAndroid Build Coastguard Worker #include <errno.h>
21*6777b538SAndroid Build Coastguard Worker #include <pthread.h>
22*6777b538SAndroid Build Coastguard Worker #include <unistd.h>
23*6777b538SAndroid Build Coastguard Worker #include <stdlib.h>
24*6777b538SAndroid Build Coastguard Worker #include <string.h>
25*6777b538SAndroid Build Coastguard Worker #include <sys/mman.h>
26*6777b538SAndroid Build Coastguard Worker #include <sys/types.h>
27*6777b538SAndroid Build Coastguard Worker #include <sys/stat.h>
28*6777b538SAndroid Build Coastguard Worker #include <sys/ioctl.h>
29*6777b538SAndroid Build Coastguard Worker #include <sys/stat.h> /* for fdstat() */
30*6777b538SAndroid Build Coastguard Worker #include <fcntl.h>
31*6777b538SAndroid Build Coastguard Worker
32*6777b538SAndroid Build Coastguard Worker #include <linux/ashmem.h>
33*6777b538SAndroid Build Coastguard Worker #include <sys/system_properties.h>
34*6777b538SAndroid Build Coastguard Worker
35*6777b538SAndroid Build Coastguard Worker #define ASHMEM_DEVICE "/dev/ashmem"
36*6777b538SAndroid Build Coastguard Worker
37*6777b538SAndroid Build Coastguard Worker /* Technical note regarding reading system properties.
38*6777b538SAndroid Build Coastguard Worker *
39*6777b538SAndroid Build Coastguard Worker * Try to use the new __system_property_read_callback API that appeared in
40*6777b538SAndroid Build Coastguard Worker * Android O / API level 26 when available. Otherwise use the deprecated
41*6777b538SAndroid Build Coastguard Worker * __system_property_get function.
42*6777b538SAndroid Build Coastguard Worker *
43*6777b538SAndroid Build Coastguard Worker * For more technical details from an NDK maintainer, see:
44*6777b538SAndroid Build Coastguard Worker * https://bugs.chromium.org/p/chromium/issues/detail?id=392191#c17
45*6777b538SAndroid Build Coastguard Worker */
46*6777b538SAndroid Build Coastguard Worker
47*6777b538SAndroid Build Coastguard Worker /* Weak symbol import */
48*6777b538SAndroid Build Coastguard Worker void __system_property_read_callback(
49*6777b538SAndroid Build Coastguard Worker const prop_info* info,
50*6777b538SAndroid Build Coastguard Worker void (*callback)(
51*6777b538SAndroid Build Coastguard Worker void* cookie, const char* name, const char* value, uint32_t serial),
52*6777b538SAndroid Build Coastguard Worker void* cookie) __attribute__((weak));
53*6777b538SAndroid Build Coastguard Worker
54*6777b538SAndroid Build Coastguard Worker /* Callback used with __system_property_read_callback. */
prop_read_int(void * cookie,const char * name,const char * value,uint32_t serial)55*6777b538SAndroid Build Coastguard Worker static void prop_read_int(void* cookie,
56*6777b538SAndroid Build Coastguard Worker const char* name,
57*6777b538SAndroid Build Coastguard Worker const char* value,
58*6777b538SAndroid Build Coastguard Worker uint32_t serial) {
59*6777b538SAndroid Build Coastguard Worker *(int *)cookie = atoi(value);
60*6777b538SAndroid Build Coastguard Worker (void)name;
61*6777b538SAndroid Build Coastguard Worker (void)serial;
62*6777b538SAndroid Build Coastguard Worker }
63*6777b538SAndroid Build Coastguard Worker
system_property_get_int(const char * name)64*6777b538SAndroid Build Coastguard Worker static int system_property_get_int(const char* name) {
65*6777b538SAndroid Build Coastguard Worker int result = 0;
66*6777b538SAndroid Build Coastguard Worker if (__system_property_read_callback) {
67*6777b538SAndroid Build Coastguard Worker const prop_info* info = __system_property_find(name);
68*6777b538SAndroid Build Coastguard Worker if (info)
69*6777b538SAndroid Build Coastguard Worker __system_property_read_callback(info, &prop_read_int, &result);
70*6777b538SAndroid Build Coastguard Worker } else {
71*6777b538SAndroid Build Coastguard Worker char value[PROP_VALUE_MAX] = {};
72*6777b538SAndroid Build Coastguard Worker if (__system_property_get(name, value) >= 1)
73*6777b538SAndroid Build Coastguard Worker result = atoi(value);
74*6777b538SAndroid Build Coastguard Worker }
75*6777b538SAndroid Build Coastguard Worker return result;
76*6777b538SAndroid Build Coastguard Worker }
77*6777b538SAndroid Build Coastguard Worker
device_api_level()78*6777b538SAndroid Build Coastguard Worker static int device_api_level() {
79*6777b538SAndroid Build Coastguard Worker static int s_api_level = -1;
80*6777b538SAndroid Build Coastguard Worker if (s_api_level < 0)
81*6777b538SAndroid Build Coastguard Worker s_api_level = system_property_get_int("ro.build.version.sdk");
82*6777b538SAndroid Build Coastguard Worker return s_api_level;
83*6777b538SAndroid Build Coastguard Worker }
84*6777b538SAndroid Build Coastguard Worker
85*6777b538SAndroid Build Coastguard Worker typedef enum {
86*6777b538SAndroid Build Coastguard Worker ASHMEM_STATUS_INIT,
87*6777b538SAndroid Build Coastguard Worker ASHMEM_STATUS_NOT_SUPPORTED,
88*6777b538SAndroid Build Coastguard Worker ASHMEM_STATUS_SUPPORTED,
89*6777b538SAndroid Build Coastguard Worker } AshmemStatus;
90*6777b538SAndroid Build Coastguard Worker
91*6777b538SAndroid Build Coastguard Worker static AshmemStatus s_ashmem_status = ASHMEM_STATUS_INIT;
92*6777b538SAndroid Build Coastguard Worker static dev_t s_ashmem_dev;
93*6777b538SAndroid Build Coastguard Worker
94*6777b538SAndroid Build Coastguard Worker /* Return the dev_t of a given file path, or 0 if not available, */
ashmem_find_dev(const char * path)95*6777b538SAndroid Build Coastguard Worker static dev_t ashmem_find_dev(const char* path) {
96*6777b538SAndroid Build Coastguard Worker struct stat st;
97*6777b538SAndroid Build Coastguard Worker dev_t result = 0;
98*6777b538SAndroid Build Coastguard Worker if (stat(path, &st) == 0 && S_ISCHR(st.st_mode))
99*6777b538SAndroid Build Coastguard Worker result = st.st_dev;
100*6777b538SAndroid Build Coastguard Worker return result;
101*6777b538SAndroid Build Coastguard Worker }
102*6777b538SAndroid Build Coastguard Worker
ashmem_get_status(void)103*6777b538SAndroid Build Coastguard Worker static AshmemStatus ashmem_get_status(void) {
104*6777b538SAndroid Build Coastguard Worker /* NOTE: No need to make this thread-safe, assuming that
105*6777b538SAndroid Build Coastguard Worker * all threads will find the same value. */
106*6777b538SAndroid Build Coastguard Worker if (s_ashmem_status != ASHMEM_STATUS_INIT)
107*6777b538SAndroid Build Coastguard Worker return s_ashmem_status;
108*6777b538SAndroid Build Coastguard Worker
109*6777b538SAndroid Build Coastguard Worker s_ashmem_dev = ashmem_find_dev(ASHMEM_DEVICE);
110*6777b538SAndroid Build Coastguard Worker s_ashmem_status = (s_ashmem_dev == 0) ? ASHMEM_STATUS_NOT_SUPPORTED
111*6777b538SAndroid Build Coastguard Worker : ASHMEM_STATUS_SUPPORTED;
112*6777b538SAndroid Build Coastguard Worker return s_ashmem_status;
113*6777b538SAndroid Build Coastguard Worker }
114*6777b538SAndroid Build Coastguard Worker
115*6777b538SAndroid Build Coastguard Worker /* Returns true iff the ashmem device ioctl should be used for a given fd.
116*6777b538SAndroid Build Coastguard Worker * NOTE: Try not to use fstat() when possible to avoid performance issues. */
ashmem_dev_fd_check(int fd)117*6777b538SAndroid Build Coastguard Worker static int ashmem_dev_fd_check(int fd) {
118*6777b538SAndroid Build Coastguard Worker if (device_api_level() <= __ANDROID_API_O_MR1__)
119*6777b538SAndroid Build Coastguard Worker return 1;
120*6777b538SAndroid Build Coastguard Worker if (ashmem_get_status() == ASHMEM_STATUS_SUPPORTED) {
121*6777b538SAndroid Build Coastguard Worker struct stat st;
122*6777b538SAndroid Build Coastguard Worker return (fstat(fd, &st) == 0 && S_ISCHR(st.st_mode) &&
123*6777b538SAndroid Build Coastguard Worker st.st_dev != 0 && st.st_dev == s_ashmem_dev);
124*6777b538SAndroid Build Coastguard Worker }
125*6777b538SAndroid Build Coastguard Worker return 0;
126*6777b538SAndroid Build Coastguard Worker }
127*6777b538SAndroid Build Coastguard Worker
128*6777b538SAndroid Build Coastguard Worker /*
129*6777b538SAndroid Build Coastguard Worker * ashmem_create_region - creates a new ashmem region and returns the file
130*6777b538SAndroid Build Coastguard Worker * descriptor, or <0 on error
131*6777b538SAndroid Build Coastguard Worker *
132*6777b538SAndroid Build Coastguard Worker * `name' is an optional label to give the region (visible in /proc/pid/maps)
133*6777b538SAndroid Build Coastguard Worker * `size' is the size of the region, in page-aligned bytes
134*6777b538SAndroid Build Coastguard Worker */
ashmem_dev_create_region(const char * name,size_t size)135*6777b538SAndroid Build Coastguard Worker static int ashmem_dev_create_region(const char *name, size_t size) {
136*6777b538SAndroid Build Coastguard Worker int fd = open(ASHMEM_DEVICE, O_RDWR);
137*6777b538SAndroid Build Coastguard Worker if (fd < 0)
138*6777b538SAndroid Build Coastguard Worker return fd;
139*6777b538SAndroid Build Coastguard Worker
140*6777b538SAndroid Build Coastguard Worker int ret;
141*6777b538SAndroid Build Coastguard Worker if (name) {
142*6777b538SAndroid Build Coastguard Worker char buf[ASHMEM_NAME_LEN];
143*6777b538SAndroid Build Coastguard Worker strlcpy(buf, name, sizeof(buf));
144*6777b538SAndroid Build Coastguard Worker ret = ioctl(fd, ASHMEM_SET_NAME, buf);
145*6777b538SAndroid Build Coastguard Worker if (ret < 0)
146*6777b538SAndroid Build Coastguard Worker goto error;
147*6777b538SAndroid Build Coastguard Worker }
148*6777b538SAndroid Build Coastguard Worker ret = ioctl(fd, ASHMEM_SET_SIZE, size);
149*6777b538SAndroid Build Coastguard Worker if (ret < 0)
150*6777b538SAndroid Build Coastguard Worker goto error;
151*6777b538SAndroid Build Coastguard Worker
152*6777b538SAndroid Build Coastguard Worker return fd;
153*6777b538SAndroid Build Coastguard Worker
154*6777b538SAndroid Build Coastguard Worker error:
155*6777b538SAndroid Build Coastguard Worker close(fd);
156*6777b538SAndroid Build Coastguard Worker return ret;
157*6777b538SAndroid Build Coastguard Worker }
158*6777b538SAndroid Build Coastguard Worker
ashmem_dev_set_prot_region(int fd,int prot)159*6777b538SAndroid Build Coastguard Worker static int ashmem_dev_set_prot_region(int fd, int prot) {
160*6777b538SAndroid Build Coastguard Worker return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
161*6777b538SAndroid Build Coastguard Worker }
162*6777b538SAndroid Build Coastguard Worker
ashmem_dev_get_prot_region(int fd)163*6777b538SAndroid Build Coastguard Worker static int ashmem_dev_get_prot_region(int fd) {
164*6777b538SAndroid Build Coastguard Worker return ioctl(fd, ASHMEM_GET_PROT_MASK);
165*6777b538SAndroid Build Coastguard Worker }
166*6777b538SAndroid Build Coastguard Worker
ashmem_dev_pin_region(int fd,size_t offset,size_t len)167*6777b538SAndroid Build Coastguard Worker static int ashmem_dev_pin_region(int fd, size_t offset, size_t len) {
168*6777b538SAndroid Build Coastguard Worker struct ashmem_pin pin = { offset, len };
169*6777b538SAndroid Build Coastguard Worker return ioctl(fd, ASHMEM_PIN, &pin);
170*6777b538SAndroid Build Coastguard Worker }
171*6777b538SAndroid Build Coastguard Worker
ashmem_dev_unpin_region(int fd,size_t offset,size_t len)172*6777b538SAndroid Build Coastguard Worker static int ashmem_dev_unpin_region(int fd, size_t offset, size_t len) {
173*6777b538SAndroid Build Coastguard Worker struct ashmem_pin pin = { offset, len };
174*6777b538SAndroid Build Coastguard Worker return ioctl(fd, ASHMEM_UNPIN, &pin);
175*6777b538SAndroid Build Coastguard Worker }
176*6777b538SAndroid Build Coastguard Worker
ashmem_dev_get_size_region(int fd)177*6777b538SAndroid Build Coastguard Worker static size_t ashmem_dev_get_size_region(int fd) {
178*6777b538SAndroid Build Coastguard Worker return ioctl(fd, ASHMEM_GET_SIZE, NULL);
179*6777b538SAndroid Build Coastguard Worker }
180*6777b538SAndroid Build Coastguard Worker
181*6777b538SAndroid Build Coastguard Worker // Starting with API level 26, the following functions from
182*6777b538SAndroid Build Coastguard Worker // libandroid.so should be used to create shared memory regions.
183*6777b538SAndroid Build Coastguard Worker typedef int(*ASharedMemory_createFunc)(const char*, size_t);
184*6777b538SAndroid Build Coastguard Worker typedef size_t(*ASharedMemory_getSizeFunc)(int fd);
185*6777b538SAndroid Build Coastguard Worker typedef int(*ASharedMemory_setProtFunc)(int fd, int prot);
186*6777b538SAndroid Build Coastguard Worker
187*6777b538SAndroid Build Coastguard Worker // Function pointers to shared memory functions.
188*6777b538SAndroid Build Coastguard Worker typedef struct {
189*6777b538SAndroid Build Coastguard Worker ASharedMemory_createFunc create;
190*6777b538SAndroid Build Coastguard Worker ASharedMemory_getSizeFunc getSize;
191*6777b538SAndroid Build Coastguard Worker ASharedMemory_setProtFunc setProt;
192*6777b538SAndroid Build Coastguard Worker } ASharedMemoryFuncs;
193*6777b538SAndroid Build Coastguard Worker
194*6777b538SAndroid Build Coastguard Worker static ASharedMemoryFuncs s_ashmem_funcs = {};
195*6777b538SAndroid Build Coastguard Worker static pthread_once_t s_ashmem_funcs_once = PTHREAD_ONCE_INIT;
196*6777b538SAndroid Build Coastguard Worker
ashmem_init_funcs()197*6777b538SAndroid Build Coastguard Worker static void ashmem_init_funcs() {
198*6777b538SAndroid Build Coastguard Worker ASharedMemoryFuncs* funcs = &s_ashmem_funcs;
199*6777b538SAndroid Build Coastguard Worker if (device_api_level() >= __ANDROID_API_O__) {
200*6777b538SAndroid Build Coastguard Worker /* Leaked intentionally! */
201*6777b538SAndroid Build Coastguard Worker void* lib = dlopen("libandroid.so", RTLD_NOW);
202*6777b538SAndroid Build Coastguard Worker funcs->create =
203*6777b538SAndroid Build Coastguard Worker (ASharedMemory_createFunc)dlsym(lib, "ASharedMemory_create");
204*6777b538SAndroid Build Coastguard Worker funcs->getSize =
205*6777b538SAndroid Build Coastguard Worker (ASharedMemory_getSizeFunc)dlsym(lib, "ASharedMemory_getSize");
206*6777b538SAndroid Build Coastguard Worker funcs->setProt =
207*6777b538SAndroid Build Coastguard Worker (ASharedMemory_setProtFunc)dlsym(lib, "ASharedMemory_setProt");
208*6777b538SAndroid Build Coastguard Worker } else {
209*6777b538SAndroid Build Coastguard Worker funcs->create = &ashmem_dev_create_region;
210*6777b538SAndroid Build Coastguard Worker funcs->getSize = &ashmem_dev_get_size_region;
211*6777b538SAndroid Build Coastguard Worker funcs->setProt = &ashmem_dev_set_prot_region;
212*6777b538SAndroid Build Coastguard Worker }
213*6777b538SAndroid Build Coastguard Worker }
214*6777b538SAndroid Build Coastguard Worker
ashmem_get_funcs()215*6777b538SAndroid Build Coastguard Worker static const ASharedMemoryFuncs* ashmem_get_funcs() {
216*6777b538SAndroid Build Coastguard Worker pthread_once(&s_ashmem_funcs_once, ashmem_init_funcs);
217*6777b538SAndroid Build Coastguard Worker return &s_ashmem_funcs;
218*6777b538SAndroid Build Coastguard Worker }
219*6777b538SAndroid Build Coastguard Worker
ashmem_create_region(const char * name,size_t size)220*6777b538SAndroid Build Coastguard Worker int ashmem_create_region(const char* name, size_t size) {
221*6777b538SAndroid Build Coastguard Worker return ashmem_get_funcs()->create(name, size);
222*6777b538SAndroid Build Coastguard Worker }
223*6777b538SAndroid Build Coastguard Worker
ashmem_set_prot_region(int fd,int prot)224*6777b538SAndroid Build Coastguard Worker int ashmem_set_prot_region(int fd, int prot) {
225*6777b538SAndroid Build Coastguard Worker return ashmem_get_funcs()->setProt(fd, prot);
226*6777b538SAndroid Build Coastguard Worker }
227*6777b538SAndroid Build Coastguard Worker
ashmem_get_prot_region(int fd)228*6777b538SAndroid Build Coastguard Worker int ashmem_get_prot_region(int fd) {
229*6777b538SAndroid Build Coastguard Worker if (ashmem_dev_fd_check(fd))
230*6777b538SAndroid Build Coastguard Worker return ashmem_dev_get_prot_region(fd);
231*6777b538SAndroid Build Coastguard Worker /* There are only two practical values to return here: either
232*6777b538SAndroid Build Coastguard Worker * PROT_READ|PROT_WRITE or just PROT_READ, so try to determine
233*6777b538SAndroid Build Coastguard Worker * the flags by trying to mmap() the region read-write first.
234*6777b538SAndroid Build Coastguard Worker */
235*6777b538SAndroid Build Coastguard Worker int result = PROT_READ;
236*6777b538SAndroid Build Coastguard Worker const size_t page_size = (size_t)sysconf(_SC_PAGESIZE);
237*6777b538SAndroid Build Coastguard Worker void* m = mmap(NULL, page_size, PROT_READ|PROT_WRITE,
238*6777b538SAndroid Build Coastguard Worker MAP_SHARED, fd, 0);
239*6777b538SAndroid Build Coastguard Worker if (m != MAP_FAILED) {
240*6777b538SAndroid Build Coastguard Worker munmap(m, page_size);
241*6777b538SAndroid Build Coastguard Worker result = PROT_READ|PROT_WRITE;
242*6777b538SAndroid Build Coastguard Worker }
243*6777b538SAndroid Build Coastguard Worker return result;
244*6777b538SAndroid Build Coastguard Worker }
245*6777b538SAndroid Build Coastguard Worker
ashmem_pin_region(int fd,size_t offset,size_t len)246*6777b538SAndroid Build Coastguard Worker int ashmem_pin_region(int fd, size_t offset, size_t len) {
247*6777b538SAndroid Build Coastguard Worker if (ashmem_dev_fd_check(fd))
248*6777b538SAndroid Build Coastguard Worker return ashmem_dev_pin_region(fd, offset, len);
249*6777b538SAndroid Build Coastguard Worker return ASHMEM_NOT_PURGED;
250*6777b538SAndroid Build Coastguard Worker }
251*6777b538SAndroid Build Coastguard Worker
ashmem_unpin_region(int fd,size_t offset,size_t len)252*6777b538SAndroid Build Coastguard Worker int ashmem_unpin_region(int fd, size_t offset, size_t len) {
253*6777b538SAndroid Build Coastguard Worker if (ashmem_dev_fd_check(fd))
254*6777b538SAndroid Build Coastguard Worker return ashmem_dev_unpin_region(fd, offset, len);
255*6777b538SAndroid Build Coastguard Worker /* NOTE: It is not possible to use madvise() here because it requires a
256*6777b538SAndroid Build Coastguard Worker * memory address. This could be done in the caller though, instead of
257*6777b538SAndroid Build Coastguard Worker * this function. */
258*6777b538SAndroid Build Coastguard Worker return 0;
259*6777b538SAndroid Build Coastguard Worker }
260*6777b538SAndroid Build Coastguard Worker
ashmem_get_size_region(int fd)261*6777b538SAndroid Build Coastguard Worker int ashmem_get_size_region(int fd) {
262*6777b538SAndroid Build Coastguard Worker /* NOTE: Original API returns an int. Avoid breaking it. */
263*6777b538SAndroid Build Coastguard Worker return (int)ashmem_get_funcs()->getSize(fd);
264*6777b538SAndroid Build Coastguard Worker }
265*6777b538SAndroid Build Coastguard Worker
ashmem_device_is_supported(void)266*6777b538SAndroid Build Coastguard Worker int ashmem_device_is_supported(void) {
267*6777b538SAndroid Build Coastguard Worker return ashmem_get_status() == ASHMEM_STATUS_SUPPORTED;
268*6777b538SAndroid Build Coastguard Worker }
269