/* * Copyright (c) 2012-2014, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* MSM ION Kernel api tests. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ion_test_plan.h" #include "ion_test_utils.h" #ifndef __maybe_unused #define __maybe_unused __attribute__((unused)) #endif #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0])) static struct ion_test_data mm_heap_test = { .align = 0x1000, .size = 0x1000, .heap_id_mask = ION_HEAP(ION_SYSTEM_HEAP_ID), .heap_type_req = SYSTEM_MEM, .flags = ION_FLAG_CACHED, }; static struct ion_test_data phys_heap_test = { .align = 0x1000, .size = 0x1000, .heap_id_mask = ION_HEAP(ION_QSECOM_HEAP_ID), .heap_type_req = DMA, .flags = 0, }; static struct ion_test_data adv_mm_heap_test = { .align = 0x1000, .size = 0xC0000000, .heap_id_mask = ION_HEAP(ION_QSECOM_HEAP_ID), .heap_type_req = CARVEOUT, .flags = 0, }; static struct ion_test_data adv_system_heap_test = { .align = 0x1000, .size = 0x1000, .heap_id_mask = ION_HEAP(ION_SYSTEM_HEAP_ID), .heap_type_req = SYSTEM_MEM, .flags = 0, }; static struct ion_test_data *mm_heap_data_settings[] = { [NOMINAL_TEST] = &mm_heap_test, [ADV_TEST] = &adv_mm_heap_test, }; /* * Kernel ion client create test */ int test_kclient(const char *ion_dev __maybe_unused, const char *msm_ion_dev, struct ion_test_plan *ion_tp __maybe_unused, int test_type __maybe_unused, int *test_skipped) { int ion_kernel_fd, rc; ion_kernel_fd = open(msm_ion_dev, O_RDWR); if (ion_kernel_fd < 0) { debug(ERR, "Failed to open msm ion test device\n"); perror("msm ion"); return -EIO; } rc = ioctl(ion_kernel_fd, IOC_ION_KCLIENT_CREATE, NULL); if (rc < 0) { debug(ERR, "kernel client create failed\n"); return -EIO; } rc = ioctl(ion_kernel_fd, IOC_ION_KCLIENT_DESTROY, NULL); close(ion_kernel_fd); *test_skipped = 0; return 0; } static struct ion_test_plan kclient_test = { .name = "Kernel ion client", .test_type_flags = NOMINAL_TEST, .test_plan_data_len = 0, .test_fn = test_kclient, }; /* * Kernel ion buf alloc * Nominal test: Using mm heap. * ADV test: Invalid client, and erroneous alloc request */ int test_kalloc(const char *ion_dev __maybe_unused, const char *msm_ion_dev, struct ion_test_plan *ion_tp, int test_type, int *test_skipped) { int ion_kernel_fd, rc; struct ion_test_data *test_data; struct ion_test_data **test_data_table = (struct ion_test_data **)ion_tp->test_plan_data; if (test_type == NOMINAL_TEST) test_data = test_data_table[NOMINAL_TEST]; else test_data = test_data_table[ADV_TEST]; *test_skipped = !test_data->valid; if (!test_data->valid) { rc = 0; debug(INFO, "%s was skipped\n",__func__); goto out; } ion_kernel_fd = open(msm_ion_dev, O_RDWR); if (ion_kernel_fd < 0) { debug(ERR, "Failed to open msm ion test device\n"); perror("msm ion"); return -EIO; } rc = ioctl(ion_kernel_fd, IOC_ION_KCLIENT_CREATE, NULL); if (rc < 0) { debug(ERR, "kernel client create failed\n"); close(ion_kernel_fd); return -EIO; } rc = ioctl(ion_kernel_fd, IOC_ION_KALLOC, test_data); if (rc < 0 && test_type == NOMINAL_TEST) { debug(ERR, "Nominal kernel alloc buf failed\n"); goto alloc_err; } else if (rc < 0 && test_type == ADV_TEST) { rc = 0; goto alloc_err; } else if (rc == 0 && test_type == ADV_TEST) { debug(INFO, "erroneous alloc request succeeded\n"); rc = EIO; } ioctl(ion_kernel_fd, IOC_ION_KFREE, NULL); alloc_err: ioctl(ion_kernel_fd, IOC_ION_KCLIENT_DESTROY, NULL); close(ion_kernel_fd); out: return rc; } static struct ion_test_plan kalloc_test = { .name = "Kernel ion alloc buf", .test_plan_data = mm_heap_data_settings, .test_plan_data_len = 3, .test_type_flags = NOMINAL_TEST | ADV_TEST, .test_fn = test_kalloc, }; /* * Kernel ion phys attr * Nominal test: use mm heap settings * Adeveserial test: Invalid handle and client */ int test_kphys(const char *ion_dev __maybe_unused, const char *msm_ion_dev, struct ion_test_plan *ion_tp, int test_type, int *test_skipped) { int ion_kernel_fd, rc; struct ion_test_data *test_data = (struct ion_test_data *)ion_tp->test_plan_data; *test_skipped = !test_data->valid; if (!test_data->valid) { rc = 0; debug(INFO, "%s was skipped\n",__func__); goto out; } ion_kernel_fd = open(msm_ion_dev, O_RDWR); if (ion_kernel_fd < 0) { debug(ERR, "Failed to open msm ion test device\n"); perror("msm ion"); return -EIO; } /* * ADV test 1: invalid client * TODO: Requires fix in ion_phys * Currently causes a creash */ if (test_type == ADV_TEST) { rc = ioctl(ion_kernel_fd, IOC_ION_KPHYS, NULL); if (!rc) { debug(INFO, "phys attr obtained with invalid handle\n"); rc = -EIO; close(ion_kernel_fd); return rc; } } rc = ioctl(ion_kernel_fd, IOC_ION_KCLIENT_CREATE, NULL); if (rc < 0) { debug(ERR, "kernel client create failed\n"); goto n_kphys_kc_err; } /* * ADV test 2: invalid handle * TODO: Requires fix in ion_phys * Currently causes a creash */ if (test_type == ADV_TEST) { rc = ioctl(ion_kernel_fd, IOC_ION_KPHYS, NULL); if (!rc) { debug(INFO, "phys attr obtained with invalid handle\n"); rc = -EIO; goto n_kphys_alloc_err; } } rc = ioctl(ion_kernel_fd, IOC_ION_KALLOC, test_data); if (rc < 0) { debug(ERR, "kernel alloc buf failed\n"); goto n_kphys_alloc_err; } rc = ioctl(ion_kernel_fd, IOC_ION_KPHYS, NULL); ioctl(ion_kernel_fd, IOC_ION_KFREE, NULL); n_kphys_alloc_err: ioctl(ion_kernel_fd, IOC_ION_KCLIENT_DESTROY, NULL); n_kphys_kc_err: close(ion_kernel_fd); out: return rc; } static struct ion_test_plan kphys_test = { .name = "Kernel ion buf phys attr test", .test_plan_data = &phys_heap_test, .test_plan_data_len = 1, /* * TODO: Enable ADV test after ion phys api * can handle erroneous input */ .test_type_flags = NOMINAL_TEST, .test_fn = test_kphys, }; /* * Kernel ion system phys attr * Adeveserial test: get phys attr from system heap */ int test_ksystem_phys(const char *ion_dev __maybe_unused, const char *msm_ion_dev, struct ion_test_plan *ion_tp, int test_type, int *test_skipped) { int ion_kernel_fd, rc; struct ion_test_data *test_data = (struct ion_test_data *)ion_tp->test_plan_data; *test_skipped = !test_data->valid; if (!test_data->valid) { rc = 0; debug(INFO, "%s was skipped\n",__func__); goto out; } ion_kernel_fd = open(msm_ion_dev, O_RDWR); if (ion_kernel_fd < 0) { debug(ERR, "Failed to open msm ion test device\n"); perror("msm ion"); return -EIO; } rc = ioctl(ion_kernel_fd, IOC_ION_KCLIENT_CREATE, NULL); if (rc < 0) { debug(ERR, "kernel client create failed\n"); goto n_kphys_kc_err; } rc = ioctl(ion_kernel_fd, IOC_ION_KALLOC, test_data); if (rc < 0) { debug(ERR, "kernel system alloc buf failed\n"); goto n_kphys_alloc_err; } rc = ioctl(ion_kernel_fd, IOC_ION_KPHYS, NULL); if (rc < 0 && test_type == ADV_TEST) rc = 0; ioctl(ion_kernel_fd, IOC_ION_KFREE, NULL); n_kphys_alloc_err: ioctl(ion_kernel_fd, IOC_ION_KCLIENT_DESTROY, NULL); n_kphys_kc_err: close(ion_kernel_fd); out: return rc; } static struct ion_test_plan ksystem_test = { .name = "Kernel system buf phys attr test", .test_plan_data = &adv_system_heap_test, .test_plan_data_len = 1, .test_type_flags = ADV_TEST, .test_fn = test_ksystem_phys, }; /* * Kernel ion map */ int test_kmap(const char *ion_dev __maybe_unused, const char *msm_ion_dev, struct ion_test_plan *ion_tp, int test_type, int *test_skipped) { *test_skipped = 0; if (test_type == NOMINAL_TEST) { int ion_kernel_fd, rc; struct ion_test_data *test_data = (struct ion_test_data *)ion_tp->test_plan_data; *test_skipped = !test_data->valid; if (!test_data->valid) { rc = 0; debug(INFO, "%s was skipped\n",__func__); goto out; } ion_kernel_fd = open(msm_ion_dev, O_RDWR); if (ion_kernel_fd < 0) { debug(ERR, "Failed to open msm ion test device\n"); perror("msm ion"); return -EIO; } rc = ioctl(ion_kernel_fd, IOC_ION_KCLIENT_CREATE, NULL); if (rc < 0) { debug(ERR, "kernel client create failed\n"); goto n_kmap_kc_err; } rc = ioctl(ion_kernel_fd, IOC_ION_KALLOC, test_data); if (rc < 0) { debug(ERR, "kernel alloc buf failed\n"); goto n_kmap_alloc_err; } rc = ioctl(ion_kernel_fd, IOC_ION_KMAP, NULL); if (!rc) { rc = ioctl(ion_kernel_fd, IOC_ION_WRITE_VERIFY, NULL); if (rc < 0) debug(ERR, "kernel map verification fails\n"); ioctl(ion_kernel_fd, IOC_ION_KUMAP, NULL); } ioctl(ion_kernel_fd, IOC_ION_KFREE, NULL); n_kmap_alloc_err: ioctl(ion_kernel_fd, IOC_ION_KCLIENT_DESTROY, NULL); n_kmap_kc_err: close(ion_kernel_fd); return rc; } out: return 0; } static struct ion_test_plan kmap_test = { .name = "Kernel ion map test", .test_plan_data = &mm_heap_test, .test_plan_data_len = 1, .test_type_flags = NOMINAL_TEST, .test_fn = test_kmap, }; /* * Kernel ion user import test */ int test_kuimport(const char *ion_dev, const char *msm_ion_dev, struct ion_test_plan *ion_tp, int test_type, int *test_skipped) { int ion_kernel_fd, rc, ion_fd, map_fd; struct ion_allocation_data alloc_data; struct ion_fd_data fd_data; unsigned long addr; struct ion_test_data *test_data = (struct ion_test_data *)ion_tp->test_plan_data; *test_skipped = !test_data->valid; if (!test_data->valid) { rc = 0; debug(INFO, "%s was skipped\n",__func__); goto out; } ion_kernel_fd = open(msm_ion_dev, O_RDWR); if (ion_kernel_fd < 0) { debug(ERR, "Failed to open msm ion test device\n"); perror("msm ion"); return -EIO; } rc = ioctl(ion_kernel_fd, IOC_ION_KCLIENT_CREATE, NULL); if (rc < 0) { debug(ERR, "kernel client create failed\n"); close(ion_kernel_fd); return -EIO; } ion_fd = open(ion_dev, O_RDONLY); if (ion_fd < 0) { debug(ERR, "Failed to open ion device\n"); perror("msm ion"); rc = -EIO; goto kuimp_id_err; } if (test_type == ADV_TEST) { rc = ioctl(ion_kernel_fd, IOC_ION_UIMPORT, &ion_fd); if (rc < 0) rc = 0; else rc = -EIO; goto kuimp_adv_test_close; } if (test_type == NOMINAL_TEST) { alloc_data.len = test_data->size; alloc_data.align = test_data->align; alloc_data.heap_id_mask = test_data->heap_id_mask; alloc_data.flags = test_data->flags; rc = ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data); if (rc) { debug(ERR, "Failed to allocate uspace ion buffer\n"); goto kuimp_adv_test_close; } fd_data.handle = alloc_data.handle; rc = ioctl(ion_fd, ION_IOC_MAP, &fd_data); if (rc) { debug(ERR, "Failed to map uspace ion buffer\n"); goto kuimp_map_err; } map_fd = fd_data.fd; addr = (unsigned long)mmap(NULL, alloc_data.len, PROT_READ | PROT_WRITE, MAP_SHARED , map_fd, 0); write_pattern(addr, alloc_data.len); rc = ioctl(ion_fd, ION_IOC_SHARE, &fd_data); if (rc) { debug(ERR, "Failed to share uspace ion buffer fd\n"); goto kuimp_share_err; } test_data->shared_fd = fd_data.fd; rc = ioctl(ion_kernel_fd, IOC_ION_UIMPORT, test_data); if (!rc) { rc = ioctl(ion_kernel_fd, IOC_ION_KMAP, NULL); if (!rc) rc = ioctl(ion_kernel_fd, IOC_ION_VERIFY, NULL); else debug(ERR, "failed to map uimp buffer\n"); ioctl(ion_kernel_fd, IOC_ION_KUMAP, NULL); ioctl(ion_kernel_fd, IOC_ION_KFREE, NULL); } if (rc) debug(ERR, "Failed to import uspace buffer\n"); kuimp_share_err: munmap((void *)addr, alloc_data.len); close(map_fd); kuimp_map_err: ioctl(ion_fd, ION_IOC_FREE, &alloc_data.handle); } kuimp_adv_test_close: close(ion_fd); kuimp_id_err: ioctl(ion_kernel_fd, IOC_ION_KCLIENT_DESTROY, NULL); close(ion_kernel_fd); out: return rc; } static struct ion_test_plan kuimp_test = { .name = "Kernel ion uspace buffer import", .test_plan_data = &mm_heap_test, .test_plan_data_len = 1, .test_type_flags = ADV_TEST, .test_fn = test_kuimport, }; /* * Kernel ion imported user buf attributes test */ int test_ubuf_attr(const char *ion_dev, const char *msm_ion_dev, struct ion_test_plan *ion_tp, int test_type, int *test_skipped) { *test_skipped = 0; if (test_type == NOMINAL_TEST) { int ion_kernel_fd, rc, ion_fd, map_fd; struct ion_allocation_data alloc_data; struct ion_fd_data fd_data; unsigned long addr, flags, size; struct ion_test_data *test_data = (struct ion_test_data *)ion_tp->test_plan_data; *test_skipped = !test_data->valid; if (!test_data->valid) { rc = 0; debug(INFO, "%s was skipped (nom)\n",__func__); goto out; } ion_kernel_fd = open(msm_ion_dev, O_RDWR); if (ion_kernel_fd < 0) { debug(ERR, "Failed to open msm ion test device\n"); perror("msm ion"); return -EIO; } rc = ioctl(ion_kernel_fd, IOC_ION_KCLIENT_CREATE, NULL); if (rc < 0) { debug(ERR, "kernel client create failed\n"); close(ion_kernel_fd); return -EIO; } ion_fd = open(ion_dev, O_RDONLY); if (ion_fd < 0) { debug(ERR, "Failed to open ion device\n"); perror("msm ion"); rc = -EIO; goto kubuf_id_err; } alloc_data.len = test_data->size; alloc_data.align = test_data->align; alloc_data.heap_id_mask = test_data->heap_id_mask; alloc_data.flags = test_data->flags; rc = ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data); if (rc) { debug(ERR, "Failed to allocate uspace ion buffer\n"); goto n_kubuf_alloc_err; } fd_data.handle = alloc_data.handle; rc = ioctl(ion_fd, ION_IOC_SHARE, &fd_data); if (rc) { debug(ERR, "Failed to share uspace ion buffer fd\n"); goto n_kubuf_share_err; } test_data->shared_fd = fd_data.fd; rc = ioctl(ion_kernel_fd, IOC_ION_UIMPORT, test_data); if (!rc) { rc = ioctl(ion_kernel_fd, IOC_ION_UBUF_FLAGS, &flags); if (!rc) rc = ioctl(ion_kernel_fd, IOC_ION_UBUF_SIZE, &size); else if (rc < 0) { debug(ERR, "failed to get ubuf attributes\n"); } else { rc = ((size == alloc_data.len) && (flags == alloc_data.flags)) ? 0 : -EIO; } ioctl(ion_kernel_fd, IOC_ION_KFREE, NULL); } else debug(ERR, "failed to import uspace buf\n"); close(fd_data.fd); n_kubuf_share_err: ioctl(ion_fd, ION_IOC_FREE, &alloc_data.handle); n_kubuf_alloc_err: close(ion_fd); kubuf_id_err: ioctl(ion_kernel_fd, IOC_ION_KCLIENT_DESTROY, NULL); close(ion_kernel_fd); return rc; } else if (test_type == ADV_TEST) { int ion_kernel_fd, rc, ion_fd, map_fd; struct ion_allocation_data alloc_data; struct ion_fd_data fd_data; unsigned long addr, flags, size; struct ion_test_data *test_data = (struct ion_test_data *)ion_tp->test_plan_data; *test_skipped = !test_data->valid; if (!test_data->valid) { rc = 0; debug(INFO, "%s was skipped (adv)\n",__func__); goto out; } ion_kernel_fd = open(msm_ion_dev, O_RDWR); if (ion_kernel_fd < 0) { debug(ERR, "Failed to open msm ion test device\n"); perror("msm ion"); return -EIO; } ion_fd = open(ion_dev, O_RDONLY); if (ion_fd < 0) { debug(ERR, "Failed to open ion device\n"); perror("msm ion"); close(ion_kernel_fd); return -EIO; } alloc_data.len = test_data->size; alloc_data.align = test_data->align; alloc_data.heap_id_mask = test_data->heap_id_mask; alloc_data.flags = test_data->flags; rc = ioctl(ion_fd, ION_IOC_ALLOC, &alloc_data); if (rc) { debug(ERR, "Failed to allocate uspace ion buffer\n"); goto a_kubuf_alloc_err; } fd_data.handle = alloc_data.handle; rc = ioctl(ion_fd, ION_IOC_SHARE, &fd_data); if (rc) { debug(ERR, "Failed to share uspace ion buffer fd\n"); goto a_kubuf_share_err; } test_data->shared_fd = fd_data.fd; rc = ioctl(ion_kernel_fd, IOC_ION_UBUF_FLAGS, &flags); if (rc) rc = ioctl(ion_kernel_fd, IOC_ION_UBUF_SIZE, &size); if (rc == 0) { debug(INFO, "can read ubuf attr with invalid client\n"); goto a_kubuf_attr_client_err; } rc = ioctl(ion_kernel_fd, IOC_ION_KCLIENT_CREATE, NULL); if (rc < 0) { debug(INFO, "kernel client create failed\n"); goto a_kubuf_attr_client_err; } rc = ioctl(ion_kernel_fd, IOC_ION_UBUF_FLAGS, &flags); if (rc) rc = ioctl(ion_kernel_fd, IOC_ION_UBUF_SIZE, &size); if (rc == 0) { debug(INFO, "can read ubuf attr with invalid handle\n"); goto a_kubuf_attr_handle_err; } rc = 0; a_kubuf_attr_handle_err: ioctl(ion_kernel_fd, IOC_ION_KCLIENT_DESTROY, NULL); a_kubuf_attr_client_err: a_kubuf_share_err: ioctl(ion_fd, ION_IOC_FREE, &alloc_data.handle); a_kubuf_alloc_err: close(ion_fd); close(ion_kernel_fd); } out: return 0; } static struct ion_test_plan kubuf_attr_test = { .name = "Kernel ion uspace buffer size and flags", .test_plan_data = &mm_heap_test, .test_plan_data_len = 1, /* TODO: ADV test causes crash in ion. * Needs to be fixed till then ADV test * diabled */ .test_type_flags = NOMINAL_TEST, .test_fn = test_ubuf_attr, }; static struct ion_test_plan *kernel_tests[] = { &kclient_test, &kalloc_test, &kphys_test, &ksystem_test, &kmap_test, &kuimp_test, &kubuf_attr_test, }; struct ion_test_plan **get_kernel_ion_tests(const char *dev, size_t *size) { *size = ARRAY_SIZE(kernel_tests); setup_heaps_for_tests(dev, kernel_tests, *size); return kernel_tests; }