v4l2-uvm-test: support SVP [1/1]

PD#SWPL-20176

Problem:
Secure Video Path test application.

Solution:
a) Use secmem API to copy ES frame into secure dmabuf
b) Use MESON_USE_PROTECTED to allocate secure memory from DRM-GEM.

Verify:
U212 with command "v4l2-uvm-test -f /data/1.mp4 -p 26 -d 0 -s"

Change-Id: Iedb23a374cbd198e85b2678eb5034a2c802ec5bb
Signed-off-by: Song Zhao <song.zhao@amlogic.com>
diff --git a/v4l2-uvm-test/src/Makefile b/v4l2-uvm-test/src/Makefile
index f0e0f67..385f857 100644
--- a/v4l2-uvm-test/src/Makefile
+++ b/v4l2-uvm-test/src/Makefile
@@ -1,11 +1,11 @@
-OBJ = drm.c v4l2-dec.c test.c demux.c
+OBJ = drm.c v4l2-dec.c test.c demux.c secmem.c
 TARGET = v4l2-uvm-test
 
 # rules
 all: $(TARGET)
 
 #TARGET_CFLAGS += -DDEBUG_FRAME
-LD_FLAG = -ldrm -lavformat -lavcodec -lavutil -lm -lpthread -lz -lswresample
+LD_FLAG = -ldrm -lavformat -lavcodec -lavutil -lm -lpthread -lz -lswresample -lsecmem
 
 $(TARGET): $(OBJ)
 	$(CC) $(TARGET_CFLAGS)  -D_FILE_OFFSET_BITS=64 -Wall -I$(STAGING_DIR)/usr/include/ -I$(STAGING_DIR)/usr/include/libdrm/ -L$(STAGING_DIR)/usr/lib $(LD_FLAG) $(OBJ) -o $@
diff --git a/v4l2-uvm-test/src/drm.c b/v4l2-uvm-test/src/drm.c
index a90ef0f..55c1320 100644
--- a/v4l2-uvm-test/src/drm.c
+++ b/v4l2-uvm-test/src/drm.c
@@ -36,6 +36,7 @@
 static char* drm_cli_dev_name = "/dev/dri/renderD128";
 static int drm_fd, drm_cli_fd;
 static int drm_mode_set;
+static int secure_mode;
 extern unsigned int global_plane_id;
 
 struct gem_buffer {
@@ -144,6 +145,8 @@
             buffer->pitches[i] = width*2;
         } else {
             gem_create.flags = MESON_USE_VIDEO_PLANE;
+            if (secure_mode)
+                 gem_create.flags |= MESON_USE_PROTECTED;
             if (i == 0)
                 gem_create.size = width * height;
             else
@@ -657,12 +660,14 @@
     return 0;
 }
 
-int display_engine_start()
+int display_engine_start(int smode)
 {
     unsigned int plane_id;
     drmModeModeInfo mode;
     int rc;
 
+    secure_mode = smode;
+
     drm_fd = drmOpen("meson", drm_master_dev_name);
     if (drm_fd < 0) {
         printf("Unable to open DRM node: %s\n",
diff --git a/v4l2-uvm-test/src/secmem.c b/v4l2-uvm-test/src/secmem.c
new file mode 100644
index 0000000..74f93d0
--- /dev/null
+++ b/v4l2-uvm-test/src/secmem.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
+ *
+ * This source code is subject to the terms and conditions defined in the
+ * file 'LICENSE' which is part of this source code package.
+ *
+ * Description:
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "secmem.h"
+#include "secmem_ca.h"
+
+static void* sec_sess;
+
+int secmem_init()
+{
+    unsigned int ret;
+    ret = Secure_V2_SessionCreate(&sec_sess);
+    if (ret) {
+        printf("Secure_V2_SessionCreate fail %x\n", ret);
+        return -1;
+    }
+    ret = Secure_V2_Init(sec_sess, 1, 1, 0, 0);
+    if (ret) {
+        Secure_V2_SessionDestroy(sec_sess);
+        printf("Secure_V2_Init fail %x\n", ret);
+        return -1;
+    }
+    return 0;
+}
+
+void secmem_destroy()
+{
+    if (sec_sess)
+        Secure_V2_SessionDestroy(&sec_sess);
+    sec_sess = NULL;
+}
+
+struct secmem* secmem_alloc(uint32_t size)
+{
+    struct secmem* mem = calloc(1, sizeof(*mem));
+    if (mem) {
+        unsigned int ret;
+        uint32_t maxsize;
+
+        ret = Secure_V2_MemCreate(sec_sess, &mem->handle);
+        if (ret) {
+            printf("Secure_V2_MemCreate fail %x\n", ret);
+            goto error;
+        }
+        ret = Secure_V2_MemAlloc(sec_sess, mem->handle, size, &mem->phyaddr);
+        if (ret) {
+            printf("Secure_V2_MemAlloc failed %x\n",ret);
+            goto error2;
+        }
+        ret = Secure_V2_MemExport(sec_sess, mem->handle, &mem->fd, &maxsize);
+        if (ret) {
+            printf("Secure_V2_MemExport failed %x\n",ret);
+            goto error3;
+        }
+    }
+    return mem;
+error3:
+    Secure_V2_MemFree(sec_sess, mem->handle);
+error2:
+    Secure_V2_MemRelease(sec_sess, mem->handle);
+error:
+    free(mem);
+    return NULL;
+}
+
+void secmem_free(struct secmem * mem)
+{
+    unsigned int ret;
+    close(mem->fd);
+    ret = Secure_V2_MemFree(sec_sess, mem->handle);
+    if (ret)
+        printf("Secure_V2_MemFree fail %x\n", ret);
+    ret = Secure_V2_MemRelease(sec_sess, mem->handle);
+    if (ret)
+        printf("Secure_V2_MemRelease fail %x\n", ret);
+    free(mem);
+}
+
+int secmem_fill(struct secmem* mem, uint8_t* buf, uint32_t offset, uint32_t size)
+{
+    unsigned int ret = 0;
+    ret = Secure_V2_MemFill(sec_sess, mem->handle, offset, buf, size);
+    if (ret) {
+        printf("Secure_V2_MemFill fail %x\n", ret);
+        return -1;
+    }
+    return ret;
+}
diff --git a/v4l2-uvm-test/src/secmem.h b/v4l2-uvm-test/src/secmem.h
new file mode 100644
index 0000000..0345cd8
--- /dev/null
+++ b/v4l2-uvm-test/src/secmem.h
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2019 Amlogic, Inc. All rights reserved.
+ *
+ * This source code is subject to the terms and conditions defined in the
+ * file 'LICENSE' which is part of this source code package.
+ *
+ * Description:
+ */
+
+#ifndef _SECMEMC_H_
+#define _SECMEMC_H_
+
+#include <stdint.h>
+
+struct secmem {
+    uint32_t handle;
+    uint32_t phyaddr;
+    int fd;
+};
+
+int secmem_init();
+void secmem_destroy();
+struct secmem* secmem_alloc(uint32_t size);
+void secmem_free(struct secmem * mem);
+int secmem_fill(struct secmem* mem, uint8_t* buf, uint32_t offset, uint32_t size);
+#endif
diff --git a/v4l2-uvm-test/src/test.c b/v4l2-uvm-test/src/test.c
index 67a5469..9201e0b 100644
--- a/v4l2-uvm-test/src/test.c
+++ b/v4l2-uvm-test/src/test.c
@@ -27,6 +27,7 @@
 static sem_t wait_for_end;
 int global_plane_id;
 int g_dw_mode = 16;
+static int secure_mode = 0;
 int ffmpeg_log = 0;
 
 static void usage(int argc, char **argv)
@@ -44,11 +45,12 @@
                  "-h | --help          Print this message\n"
                  "-p | --plane=id      select display plane. 26[pri] 28[overlay 1] 30[overlay 2] 32[video]\n"
                  "-l | --log           enable more ffmpeg demux log.\n"
+                 "-s | --secure        secure video path.\n"
                  "",
                  argv[0]);
 }
 
-static const char short_options[] = "d:hf:p:l";
+static const char short_options[] = "d:hf:p:ls";
 
 static const struct option
 long_options[] = {
@@ -57,6 +59,7 @@
         { "help",   no_argument,       NULL, 'h' },
         { "plane",  required_argument, NULL, 'p' },
         { "log",  no_argument, NULL, 'l' },
+        { "secure",  no_argument, NULL, 's' },
         { 0, 0, 0, 0 }
 };
 
@@ -68,7 +71,7 @@
 int start_decoder(struct dmx_v_data *v_data)
 {
     int ret;
-    ret = v4l2_dec_init(v_data->type, decode_finish);
+    ret = v4l2_dec_init(v_data->type, secure_mode, decode_finish);
     if (ret) {
         printf("FATAL: start_decoder error:%d\n",ret);
         exit(1);
@@ -120,6 +123,10 @@
                 global_plane_id = atoi(optarg);
                 break;
 
+            case 's':
+                secure_mode = 1;
+                break;
+
             default:
                 usage(argc, argv);
                 return -1;
@@ -149,7 +156,7 @@
     sem_init(&wait_for_end, 0, 0);
 
     display_engine_register_cb(capture_buffer_recycle);
-    rc = display_engine_start();
+    rc = display_engine_start(secure_mode);
     if (rc < 0) {
         printf("Unable to start display engine\n");
         goto error;
diff --git a/v4l2-uvm-test/src/v4l2-dec.c b/v4l2-uvm-test/src/v4l2-dec.c
index bb5c6d0..c15441e 100644
--- a/v4l2-uvm-test/src/v4l2-dec.c
+++ b/v4l2-uvm-test/src/v4l2-dec.c
@@ -26,6 +26,7 @@
 #include "demux.h"
 #include "drm.h"
 #include "v4l2-dec.h"
+#include "secmem.h"
 
 static const char* video_dev_name = "/dev/video26";
 static int video_fd;
@@ -37,10 +38,13 @@
 static bool eos_received;
 extern int g_dw_mode;
 static pthread_mutex_t res_lock;
+static enum v4l2_memory sInMemMode;
+static uint8_t* es_buf;
 //#define DEBUG_FRAME
 #ifdef DEBUG_FRAME
 static int frame_checksum;
 #endif
+#define ES_BUF_SIZE (2*1024*1024)
 
 static pthread_t dec_thread;
 bool quit_thread;
@@ -56,6 +60,8 @@
 
     /* output only */
     uint32_t used;
+    struct secmem* smem;
+
     /* capture only */
     bool free_on_recycle;
     struct drm_frame *drm_frame;
@@ -129,7 +135,7 @@
         req.count = OUTPUT_BUF_CNT;
     }
 
-    req.memory = V4L2_MEMORY_MMAP;
+    req.memory = sInMemMode;
     req.type = output_p.type;
 
     ret = ioctl(fd, VIDIOC_REQBUFS, &req);
@@ -153,7 +159,7 @@
         struct frame_buffer* pb = output_p.buf[i];
         pb->v4lbuf.index = i;
         pb->v4lbuf.type = output_p.type;
-        pb->v4lbuf.memory = V4L2_MEMORY_MMAP;
+        pb->v4lbuf.memory = sInMemMode;
         pb->v4lbuf.length = output_p.plane_num;
         pb->v4lbuf.m.planes = pb->v4lplane;
 
@@ -162,6 +168,9 @@
             printf("VIDIOC_QUERYBUF %dth buf fail ret:%d\n", i, ret);
             return 3;
         }
+
+        if (sInMemMode != V4L2_MEMORY_MMAP)
+            continue;
         for (j = 0; j < output_p.plane_num; j++) {
             void *vaddr;
             vaddr = mmap(NULL, pb->v4lplane[j].length,
@@ -179,13 +188,21 @@
 
     pthread_mutex_init(&output_p.lock, NULL);
     pthread_cond_init(&output_p.wait, NULL);
+
+    if (sInMemMode == V4L2_MEMORY_DMABUF) {
+        es_buf = malloc(ES_BUF_SIZE);
+        if (!es_buf) {
+            printf("%d OOM\n", __LINE__);
+            return 5;
+        }
+    }
     return 0;
 }
 
 static int destroy_output_port(int fd) {
     int i;
     struct v4l2_requestbuffers req = {
-        .memory = V4L2_MEMORY_MMAP,
+        .memory = sInMemMode,
         .type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE,
         .count = 0,
     };
@@ -195,8 +212,9 @@
     for (i = 0 ; i < req.count ; i++) {
         struct frame_buffer *buf = output_p.buf[i];
 
-        /* release GEM buf */
-        buf->drm_frame->destroy(buf->drm_frame);
+        if (sInMemMode == V4L2_MEMORY_DMABUF)
+            secmem_free(buf->smem);
+
         free(buf);
     }
     free(output_p.buf);
@@ -324,8 +342,12 @@
     pthread_mutex_destroy(&capture_p.lock);
     pthread_cond_destroy(&capture_p.wait);
     ioctl(fd, VIDIOC_REQBUFS, &req);
-    for (i = 0 ; i < req.count ; i++)
+    for (i = 0 ; i < req.count ; i++) {
+        struct frame_buffer *buf = capture_p.buf[i];
+        /* release GEM buf */
+        buf->drm_frame->destroy(buf->drm_frame);
         free(capture_p.buf[i]);
+    }
     free(capture_p.buf);
     return 0;
 }
@@ -469,7 +491,7 @@
         if (pfd.revents & (POLLOUT | POLLWRNORM)) {
             memset(&buf, 0, sizeof(buf));
             memset(planes, 0, sizeof(planes));
-            buf.memory = V4L2_MEMORY_MMAP;
+            buf.memory = sInMemMode;
             buf.type = output_p.type;
             buf.length = 2;
             buf.m.planes = planes;
@@ -478,14 +500,18 @@
             if (ret) {
                 printf("output VIDIOC_DQBUF fail %d\n", ret);
             } else {
+                struct frame_buffer *fb = output_p.buf[buf.index];
 #ifdef DEBUG_FRAME
                 printf("dqueue output %d\n", buf.index);
 #endif
                 pthread_mutex_lock(&output_p.lock);
-                output_p.buf[buf.index]->queued = false;
+                fb->queued = false;
                 pthread_mutex_unlock(&output_p.lock);
-                output_p.buf[buf.index]->used = 0;
+                fb->used = 0;
                 d_o_rec_num++;
+                if (sInMemMode == V4L2_MEMORY_DMABUF) {
+                    secmem_free(fb->smem);
+                }
                 pthread_cond_signal(&output_p.wait);
             }
         }
@@ -641,7 +667,7 @@
     return rc;
 }
 
-int v4l2_dec_init(enum vtype type, decode_finish_fn cb)
+int v4l2_dec_init(enum vtype type, int secure, decode_finish_fn cb)
 {
     int ret = -1;
     struct v4l2_capability cap;
@@ -652,6 +678,16 @@
         return 1;
     decode_finish_cb = cb;
 
+    if (secure) {
+        sInMemMode = V4L2_MEMORY_DMABUF;
+        ret = secmem_init();
+        if (ret) {
+            printf("secmem_init fail %d\n",ret);
+            return 1;
+        }
+    } else
+        sInMemMode = V4L2_MEMORY_MMAP;
+
     /* check decoder mode */
     if ((type == VIDEO_TYPE_H264 ||
         type == VIDEO_TYPE_MPEG2) &&
@@ -688,7 +724,7 @@
     }
 
     if (type != VIDEO_TYPE_MPEG2) {
-        ret = v4l2_dec_set_drmmode(false);
+        ret = v4l2_dec_set_drmmode(secure);
         if (ret) {
             printf("aml_v4l2_dec_set_drmmode fail\n");
             goto error;
@@ -745,7 +781,7 @@
 
     output_p.sfmt.fmt.pix_mp.pixelformat = output_p.pixelformat;
     /* 4K frame should fit into 2M */
-    output_p.sfmt.fmt.pix_mp.plane_fmt[0].sizeimage = 2*1024*1024;
+    output_p.sfmt.fmt.pix_mp.plane_fmt[0].sizeimage = ES_BUF_SIZE;
     ret = ioctl(video_fd, VIDIOC_S_FMT, &output_p.sfmt);
     if (ret) {
         printf("VIDIOC_S_FMT 0x%x fail\n", output_p.pixelformat);
@@ -843,6 +879,9 @@
     get_1st_data = false;
     pthread_mutex_destroy(&res_lock);
     close(video_fd);
+    secmem_destroy();
+    if (es_buf)
+        free(es_buf);
     return 0;
 }
 
@@ -896,12 +935,20 @@
     }
 
     p = output_p.buf[cur_output_index];
-    if ((p->used + size) > p->v4lplane[0].length) {
-        printf("fatal frame too big %d > %d\n",
-                size + p->used, p->v4lplane[0].length);
+    if ((sInMemMode == V4L2_MEMORY_MMAP &&
+            (p->used + size) > p->v4lplane[0].length) ||
+        (sInMemMode == V4L2_MEMORY_DMABUF &&
+          (p->used + size) > ES_BUF_SIZE)) {
+        printf("fatal frame too big %d\n",
+                size + p->used);
         return 0;
     }
-    memcpy(p->vaddr[0] + p->used, data, size);
+
+    if (sInMemMode == V4L2_MEMORY_MMAP)
+        memcpy(p->vaddr[0] + p->used, data, size);
+    else
+        memcpy(es_buf + p->used, data, size);
+
     p->used += size;
 
 #ifdef DEBUG_FRAME
@@ -928,6 +975,20 @@
         return 0;
     }
     p = output_p.buf[cur_output_index];
+    if (sInMemMode == V4L2_MEMORY_DMABUF) {
+        /* copy to secmem */
+        p->smem = secmem_alloc(p->used);
+        if (!p->smem) {
+            printf("%s %d oom:%d\n", __func__, __LINE__, p->used);
+            return 0;
+        }
+        ret = secmem_fill(p->smem, es_buf, 0, p->used);
+        if (ret)
+            return 0;
+        p->v4lbuf.m.planes[0].m.fd = p->smem->fd;
+        p->v4lbuf.m.planes[0].length = p->used;
+        p->v4lbuf.m.planes[0].data_offset = 0;
+    }
     p->v4lbuf.m.planes[0].bytesused = p->used;
     pthread_mutex_lock(&output_p.lock);
     p->queued = true;
diff --git a/v4l2-uvm-test/src/v4l2-dec.h b/v4l2-uvm-test/src/v4l2-dec.h
index 8fed35c..d6e2292 100644
--- a/v4l2-uvm-test/src/v4l2-dec.h
+++ b/v4l2-uvm-test/src/v4l2-dec.h
@@ -23,7 +23,7 @@
 
 typedef void (*decode_finish_fn)();
 
-int v4l2_dec_init(enum vtype type, decode_finish_fn);
+int v4l2_dec_init(enum vtype type, int secure, decode_finish_fn);
 int v4l2_dec_destroy();
 
 int v4l2_dec_write_es(const uint8_t *data, int size);
diff --git a/v4l2-uvm-test/v4l2-uvm-test.mk b/v4l2-uvm-test/v4l2-uvm-test.mk
index aae5e95..4c25926 100644
--- a/v4l2-uvm-test/v4l2-uvm-test.mk
+++ b/v4l2-uvm-test/v4l2-uvm-test.mk
@@ -5,6 +5,15 @@
 V4L2_UVM_TEST_SITE = $(TOPDIR)/../multimedia/v4l2-uvm-test/src
 V4L2_UVM_TEST_SITE_METHOD = local
 
+V4L2_UVM_TEST_DEPENDENCIES += ffmpeg
+V4L2_UVM_TEST_DEPENDENCIES += libdrm
+
+ifneq ($(BR2_PACKAGE_LIBSECMEM),y)
+V4L2_UVM_TEST_DEPENDENCIES  += libsecmem-bin
+else
+V4L2_UVM_TEST_DEPENDENCIES  += libsecmem
+endif
+
 define V4L2_UVM_TEST_BUILD_CMDS
 	$(TARGET_CONFIGURE_OPTS) $(MAKE) CC=$(TARGET_CC) -C $(@D)/
 endef