1/*
2 *   Copyright 2013 Google, Inc
3 *
4 *  Licensed under the Apache License, Version 2.0 (the "License");
5 *  you may not use this file except in compliance with the License.
6 *  You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 *  Unless required by applicable law or agreed to in writing, software
11 *  distributed under the License is distributed on an "AS IS" BASIS,
12 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 *  See the License for the specific language governing permissions and
14 *  limitations under the License.
15 */
16
17#include <errno.h>
18#include <fcntl.h>
19#include <getopt.h>
20#include <string.h>
21#include <stdlib.h>
22#include <stdio.h>
23#include <sys/mman.h>
24#include <sys/ioctl.h>
25#include <sys/socket.h>
26#include <sys/stat.h>
27#include <sys/types.h>
28#include <unistd.h>
29
30#include <ion/ion.h>
31#include <linux/ion.h>
32
33size_t len = 1024*1024, align = 0;
34int prot = PROT_READ | PROT_WRITE;
35int map_flags = MAP_SHARED;
36int alloc_flags = 0;
37int heap_mask = 1;
38int test = -1;
39size_t stride;
40
41int _ion_alloc_test(int *fd, ion_user_handle_t *handle)
42{
43    int ret;
44
45    *fd = ion_open();
46    if (*fd < 0)
47        return *fd;
48
49    ret = ion_alloc(*fd, len, align, heap_mask, alloc_flags, handle);
50
51    if (ret)
52        printf("%s failed: %s\n", __func__, strerror(ret));
53    return ret;
54}
55
56void ion_alloc_test()
57{
58    int fd, ret;
59    ion_user_handle_t handle;
60
61    if(_ion_alloc_test(&fd, &handle))
62        return;
63
64    ret = ion_free(fd, handle);
65    if (ret) {
66        printf("%s failed: %s %d\n", __func__, strerror(ret), handle);
67        return;
68    }
69    ion_close(fd);
70    printf("ion alloc test: passed\n");
71}
72
73void ion_map_test()
74{
75    int fd, map_fd, ret;
76    size_t i;
77    ion_user_handle_t handle;
78    unsigned char *ptr;
79
80    if(_ion_alloc_test(&fd, &handle))
81        return;
82
83    ret = ion_map(fd, handle, len, prot, map_flags, 0, &ptr, &map_fd);
84    if (ret)
85        return;
86
87    for (i = 0; i < len; i++) {
88        ptr[i] = (unsigned char)i;
89    }
90    for (i = 0; i < len; i++)
91        if (ptr[i] != (unsigned char)i)
92            printf("%s failed wrote %zu read %d from mapped "
93                   "memory\n", __func__, i, ptr[i]);
94    /* clean up properly */
95    ret = ion_free(fd, handle);
96    ion_close(fd);
97    munmap(ptr, len);
98    close(map_fd);
99
100    _ion_alloc_test(&fd, &handle);
101    close(fd);
102
103#if 0
104    munmap(ptr, len);
105    close(map_fd);
106    ion_close(fd);
107
108    _ion_alloc_test(len, align, flags, &fd, &handle);
109    close(map_fd);
110    ret = ion_map(fd, handle, len, prot, flags, 0, &ptr, &map_fd);
111    /* don't clean up */
112#endif
113}
114
115void ion_share_test()
116
117{
118    ion_user_handle_t handle;
119    int sd[2];
120    int num_fd = 1;
121    struct iovec count_vec = {
122        .iov_base = &num_fd,
123        .iov_len = sizeof num_fd,
124    };
125    char buf[CMSG_SPACE(sizeof(int))];
126    socketpair(AF_UNIX, SOCK_STREAM, 0, sd);
127    if (fork()) {
128        struct msghdr msg = {
129            .msg_control = buf,
130            .msg_controllen = sizeof buf,
131            .msg_iov = &count_vec,
132            .msg_iovlen = 1,
133        };
134
135        struct cmsghdr *cmsg;
136        int fd, share_fd, ret;
137        char *ptr;
138        /* parent */
139        if(_ion_alloc_test(&fd, &handle))
140            return;
141        ret = ion_share(fd, handle, &share_fd);
142        if (ret)
143            printf("share failed %s\n", strerror(errno));
144        ptr = mmap(NULL, len, prot, map_flags, share_fd, 0);
145        if (ptr == MAP_FAILED) {
146            return;
147        }
148        strcpy(ptr, "master");
149        cmsg = CMSG_FIRSTHDR(&msg);
150        cmsg->cmsg_level = SOL_SOCKET;
151        cmsg->cmsg_type = SCM_RIGHTS;
152        cmsg->cmsg_len = CMSG_LEN(sizeof(int));
153        *(int *)CMSG_DATA(cmsg) = share_fd;
154        /* send the fd */
155        printf("master? [%10s] should be [master]\n", ptr);
156        printf("master sending msg 1\n");
157        sendmsg(sd[0], &msg, 0);
158        if (recvmsg(sd[0], &msg, 0) < 0)
159            perror("master recv msg 2");
160        printf("master? [%10s] should be [child]\n", ptr);
161
162        /* send ping */
163        sendmsg(sd[0], &msg, 0);
164        printf("master->master? [%10s]\n", ptr);
165        if (recvmsg(sd[0], &msg, 0) < 0)
166            perror("master recv 1");
167        close(fd);
168        _exit(0);
169    } else {
170        struct cmsghdr *cmsg;
171        char* ptr;
172        int fd, recv_fd;
173        char* child_buf[100];
174        /* child */
175        struct iovec count_vec = {
176            .iov_base = child_buf,
177            .iov_len = sizeof child_buf,
178        };
179
180        struct msghdr child_msg = {
181            .msg_control = buf,
182            .msg_controllen = sizeof buf,
183            .msg_iov = &count_vec,
184            .msg_iovlen = 1,
185        };
186
187        if (recvmsg(sd[1], &child_msg, 0) < 0)
188            perror("child recv msg 1");
189        cmsg = CMSG_FIRSTHDR(&child_msg);
190        if (cmsg == NULL) {
191            printf("no cmsg rcvd in child");
192            return;
193        }
194        recv_fd = *(int*)CMSG_DATA(cmsg);
195        if (recv_fd < 0) {
196            printf("could not get recv_fd from socket");
197            return;
198        }
199        printf("child %d\n", recv_fd);
200        fd = ion_open();
201        ptr = mmap(NULL, len, prot, map_flags, recv_fd, 0);
202        if (ptr == MAP_FAILED) {
203            return;
204        }
205        printf("child? [%10s] should be [master]\n", ptr);
206        strcpy(ptr, "child");
207        printf("child sending msg 2\n");
208        sendmsg(sd[1], &child_msg, 0);
209        close(fd);
210    }
211}
212
213int main(int argc, char* argv[]) {
214    int c;
215    enum tests {
216        ALLOC_TEST = 0, MAP_TEST, SHARE_TEST,
217    };
218
219    while (1) {
220        static struct option opts[] = {
221            {"alloc", no_argument, 0, 'a'},
222            {"alloc_flags", required_argument, 0, 'f'},
223            {"heap_mask", required_argument, 0, 'h'},
224            {"map", no_argument, 0, 'm'},
225            {"share", no_argument, 0, 's'},
226            {"len", required_argument, 0, 'l'},
227            {"align", required_argument, 0, 'g'},
228            {"map_flags", required_argument, 0, 'z'},
229            {"prot", required_argument, 0, 'p'},
230        };
231        int i = 0;
232        c = getopt_long(argc, argv, "af:h:l:mr:st", opts, &i);
233        if (c == -1)
234            break;
235
236        switch (c) {
237        case 'l':
238            len = atol(optarg);
239            break;
240        case 'g':
241            align = atol(optarg);
242            break;
243        case 'z':
244            map_flags = 0;
245            map_flags |= strstr(optarg, "PROT_EXEC") ? PROT_EXEC : 0;
246            map_flags |= strstr(optarg, "PROT_READ") ? PROT_READ: 0;
247            map_flags |= strstr(optarg, "PROT_WRITE") ? PROT_WRITE: 0;
248            map_flags |= strstr(optarg, "PROT_NONE") ? PROT_NONE: 0;
249            break;
250        case 'p':
251            prot = 0;
252            prot |= strstr(optarg, "MAP_PRIVATE") ? MAP_PRIVATE : 0;
253            prot |= strstr(optarg, "MAP_SHARED") ? MAP_PRIVATE : 0;
254            break;
255        case 'f':
256            alloc_flags = atol(optarg);
257            break;
258        case 'h':
259            heap_mask = atol(optarg);
260            break;
261        case 'a':
262            test = ALLOC_TEST;
263            break;
264        case 'm':
265            test = MAP_TEST;
266            break;
267        case 's':
268            test = SHARE_TEST;
269            break;
270        }
271    }
272    printf("test %d, len %zu, align %zu, map_flags %d, prot %d, heap_mask %d,"
273           " alloc_flags %d\n", test, len, align, map_flags, prot,
274           heap_mask, alloc_flags);
275    switch (test) {
276        case ALLOC_TEST:
277            ion_alloc_test();
278            break;
279        case MAP_TEST:
280            ion_map_test();
281            break;
282        case SHARE_TEST:
283            ion_share_test();
284            break;
285        default:
286            printf("must specify a test (alloc, map, share)\n");
287    }
288    return 0;
289}
290