当前位置: 首页 > news >正文

OP-TEE driver(五):libteec库中的接口在驱动中的实现

驱动挂载完成后,CA程序通过调用libteec库中的接口调用OP-TEE驱动来穿透到OP-TEE中,然后调用对应的TA程序。

OP-TEE驱动在挂载完成后会在/dev目录下分别创建两个设备节点,分别为/dev/tee0和/dev/teepriv,对/dev/tee0设备进行相关操作就能够穿透到OP-TEE中实现特定请求的发送。

1、libteec库中的open操作

在libteec库中调用open函数打开/dev/tee0设备时,最终会调用到tee_fops中的open成员指定的函数指针——tee_open,该函数的内容如下:

        static int tee_open(struct inode *inode, struct file *filp)
        {
            struct tee_context *ctx;
            /*  调用container_of函数,获取设备的tee_device变量的内容。该变量对于/dev/tee0和/
            dev/teepriv0设备是不一样的,这点可以在驱动过载的过程中查阅*/
            ctx = teedev_open(container_of(inode->i_cdev, struct tee_device, cdev));
            if (IS_ERR(ctx))
                return PTR_ERR(ctx);
            filp->private_data = ctx;
            return 0;
        }
        static struct tee_context *teedev_open(struct tee_device *teedev)
        {
            int rc;
            struct tee_context *ctx;
            /* 标记该设备的使用者加一 */
            if (! tee_device_get(teedev))
                return ERR_PTR(-EINVAL);
            /* 分配tee_context结构体变量空间 */
            ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
            if (! ctx) {
                rc = -ENOMEM;
                goto err;
            }
            /* 将tee_context结构体中的teedev变量赋值 */
            ctx->teedev = teedev;
            INIT_LIST_HEAD(&ctx->list_shm);
            /*调用设备des中的open执行设备级别的open操作*/
            rc = teedev->desc->ops->open(ctx);
              if (rc)
                  goto err;
              return ctx;
          err:
              kfree(ctx);
              tee_device_put(teedev);
              return ERR_PTR(rc);
          }

对于设备级别(/dev/tee0和/dev/teepriv0),最终会调用到optee_open函数,该函数内容如下:

        static int optee_open(struct tee_context *ctx)
        {
            struct optee_context_data *ctxdata;
            struct tee_device *teedev = ctx->teedev;
            struct optee *optee = tee_get_drvdata(teedev);
            /* 分配optee_context_data结构体变量空间 */
            ctxdata = kzalloc(sizeof(*ctxdata), GFP_KERNEL);
            if (! ctxdata)
                return -ENOMEM;
            /*  通过teedev的值是否为  optee->supp_teedev来判定当前的open操作是打开/dev/tee0设
            备还是/dev/teepriv0设备,如果相等,则表示当前是打开/dev/teepriv0设备 */
            if (teedev == optee->supp_teedev) {
                bool busy = true; //标记/dev/teepriv0正在使用
                mutex_lock(&optee->supp.mutex);
                if (! optee->supp.ctx) {
                    busy = false;
                    optee->supp.ctx = ctx;
                }
                mutex_unlock(&optee->supp.mutex);
                if (busy) {
                    kfree(ctxdata);
                    return -EBUSY;
                }
            }
            /* 初始化互斥体和队列 */
            mutex_init(&ctxdata->mutex);
            INIT_LIST_HEAD(&ctxdata->sess_list);
            /* 赋值 */
            ctx->data = ctxdata;
            return 0;
        }

2、libteec库中的release操作

当libteec库和tee_supplicant打开了对应的设备后,如果需要释放打开的设备,则可调用该设备的release操作来实现,在用户空间调用该操作后,最终会调用到OP-TEE驱动的release成员变量——tee_release。该函数内容如下:

    static int tee_release(struct inode *inode, struct file *filp)
    {
        teedev_close_context(filp->private_data);
        return 0;
    }
    static void teedev_close_context(struct tee_context *ctx)
    {
        struct tee_shm *shm;
        /* 调用/dev/tee0或/dev/teepriv0设备的release操作函数 */
        ctx->teedev->desc->ops->release(ctx);
        mutex_lock(&ctx->teedev->mutex);
        /* 清空设备分配的共享内存,并将其指针指向NULL */
        list_for_each_entry(shm, &ctx->list_shm, link)
            shm->ctx = NULL;
        mutex_unlock(&ctx->teedev->mutex);
        //设备使用者数量减一。如果已经没有使用者,则将desc指向NULL
        tee_device_put(ctx->teedev);
        kfree(ctx);
    }

    ctx->teedev->desc->ops->release(ctx);将会执行optee_release函数,其内容如下:

    static void optee_release(struct tee_context *ctx)
    {
        struct optee_context_data *ctxdata = ctx->data;
        struct tee_device *teedev = ctx->teedev;
        struct optee *optee = tee_get_drvdata(teedev);
        struct tee_shm *shm;
        struct optee_msg_arg *arg = NULL;
        phys_addr_t parg;
        struct optee_session *sess;
        struct optee_session *sess_tmp;
        if (! ctxdata)
            return;
        /* 分配驱动与secure world之间的共享内存 */
        shm = tee_shm_alloc(ctx, sizeof(struct optee_msg_arg), TEE_SHM_MAPPED);
        if (! IS_ERR(shm)) {
            arg = tee_shm_get_va(shm, 0); //获取共享内存的虚拟地址
            if (! IS_ERR(arg))
            //解析共享内存的虚拟地址得到物理地址,存放在parg中
            tee_shm_va2pa(shm, arg, &parg);
        }
        /*遍历存放使用该设备的所有session通知OP-TEE执行关闭session操作*/
        list_for_each_entry_safe(sess, sess_tmp, &ctxdata->sess_list,
                      list_node) {
            list_del(&sess->list_node);
            if (! IS_ERR_OR_NULL(arg)) {
                memset(arg, 0, sizeof(*arg));
                arg->cmd = OPTEE_MSG_CMD_CLOSE_SESSION;
                arg->session = sess->session_id;
                optee_do_call_with_arg(ctx, parg);
                  }
                  kfree(sess);
              }
              kfree(ctxdata);
              /* 释放共享内存 */
              if (! IS_ERR(shm))
                  tee_shm_free(shm);
              ctx->data = NULL;
              /*  如果是对/dev/teepriv0设备进行release操作,则指向optee_supp_release操作,释放
              该设备在使用时建立的各种队列 */
              if (teedev == optee->supp_teedev)
                  optee_supp_release(&optee->supp);
          }

3、libteec执行get_version操作

在libteec库中获取OP-TEE的版本信息,会调用/dev/tee0的TEE_IOC_VERSION类型的ioctl操作,该操作最终会调用到tee_ioctl_version函数来完成获取OP-TEE版本信息的操作,该函数的内容和注释如下:

        static int tee_ioctl_version(struct tee_context *ctx,
                      struct tee_ioctl_version_data __user *uvers)
        {
            struct tee_ioctl_version_data vers;
            /* 调用设备的get_version操作 */
            ctx->teedev->desc->ops->get_version(ctx->teedev, &vers);
            /* 判定该操作是来自于tee_supplicant还是libteec */
            if (ctx->teedev->desc->flags & TEE_DESC_PRIVILEGED)
                vers.gen_caps |= TEE_GEN_CAP_PRIVILEGED;
            /* 将获取到的版本信息数据复制到userspace层面提供的buffer中 */
            if (copy_to_user(uvers, &vers, sizeof(vers)))
                return -EFAULT;
            return 0;
        }
        设备的get_version函数内容如下:
        static void optee_get_version(struct tee_device *teedev,
                      struct tee_ioctl_version_data *vers)
        {
            struct tee_ioctl_version_data v = {
                .impl_id = TEE_IMPL_ID_OPTEE,
                .impl_caps = TEE_OPTEE_CAP_TZ,
                .gen_caps = TEE_GEN_CAP_GP,
            };
            *vers = v;
        }

4、libteec库中的open session操作

当用户调用libteec库中的TEEC_OpenSession接口时会执行OP-TEE驱动中ioctl函数的TEE_IOC_OPEN_SESSION分支去执行tee_ioctl_open_session函数,该函数只会在打开/dev/tee0设备后才能被使用。整个open session的操作流程如图9-3所示。

在这里插入图片描述
调用过程中使用optee_do_call_with_arg函数来完成驱动与OP-TEE之间的交互。该函数的内容和说明如下:

        u32 optee_do_call_with_arg(struct tee_context *ctx, phys_addr_t parg)
        {
            struct optee *optee = tee_get_drvdata(ctx->teedev);
            struct optee_call_waiter w;
            struct optee_rpc_param param = { };
            u32 ret;
            /* 设定触发smc操作的第一个参数a0的值为OPTEE_SMC_CALL_WITH_ARG,通过OPTEE_SMC_
    CALL_WITH_ARG值可以知道,该函数将会执行std的smc操作 */
            param.a0 = OPTEE_SMC_CALL_WITH_ARG;
            reg_pair_from_64(&m.a1, &m.a2, parg);
            /* 初始化调用的等待队列 */
            optee_cq_wait_init(&optee->call_queue, &w);
            /*进入loop循环,触发smc操作并等待secure world的返回*/
            while (true) {
                struct arm_smccc_res res;
                /* 触发smc操作 */
                optee->invoke_fn(param.a0, param.a1, param.a2, param.a3,
                        param.a4, param.a5, param.a6, param.a7,
                            &res);
                  /* 判定secure world是否超时,如果超时,完成一次调用,进入下一次循环直到secure world
      端完成open session请求 */
                  if (res.a0 == OPTEE_SMC_RETURN_ETHREAD_LIMIT) {
                      optee_cq_wait_for_completion(&optee->call_queue, &w);
                  } else if (OPTEE_SMC_RETURN_IS_RPC(res.a0)) {
                      /* 处理rpc操作 */
                      param.a0 = res.a0;
                      param.a1 = res.a1;
                      param.a2 = res.a2;
                      param.a3 = res.a3;
                      optee_handle_rpc(ctx, &m);
                  } else {
                      /* 创建session完成之后跳出loop,并返回a0的值 */
                      ret = res.a0;
                      break;
                  }
              }
              /* 执行等待队列最后完成操作 */
              optee_cq_wait_final(&optee->call_queue, &w);
              return ret;
          }

5、libteec库中的invoke操作

当完成session的打开操作后,用户就可以调用TEEC_InvokeCommand接口来调用对应的TA中特定的操作了,TEEC_InvokeCommand函数最终会调用驱动的tee_ioctl_invoke函数来完成具体的操作。该函数内容如下:

        static int tee_ioctl_invoke(struct tee_context *ctx,
                        struct tee_ioctl_buf_data __user *ubuf)
        {
            int rc;
            size_t n;
            struct tee_ioctl_buf_data buf;
            struct tee_ioctl_invoke_arg __user *uarg;
            struct tee_ioctl_invoke_arg arg;
            struct tee_ioctl_param __user *uparams = NULL;
            struct tee_param *params = NULL;
            /* 参数检查 */
            if (! ctx->teedev->desc->ops->invoke_func)
                return -EINVAL;
            /* 数据复制到kernel space */
            if (copy_from_user(&buf, ubuf, sizeof(buf)))
                return -EFAULT;
            if (buf.buf_len > TEE_MAX_ARG_SIZE ||
                buf.buf_len < sizeof(struct tee_ioctl_invoke_arg))
                return -EINVAL;
              uarg = u64_to_user_ptr(buf.buf_ptr);
              if (copy_from_user(&arg, uarg, sizeof(arg)))
                  return -EFAULT;
              if (sizeof(arg) + TEE_IOCTL_PARAM_SIZE(arg.num_params) ! = buf.buf_len)
                  return -EINVAL;
              /* 组合需要传递到secure world中的参数buffer */
              if (arg.num_params) {
                  params = kcalloc(arg.num_params, sizeof(struct tee_param),
                            GFP_KERNEL);
                  if (! params)
                      return -ENOMEM;
                  uparams = uarg->params;
                  rc = params_from_user(ctx, params, arg.num_params, uparams);
                  if (rc)
                      goto out;
              }
              /* 使用对应的session触发smc操作 */
              rc = ctx->teedev->desc->ops->invoke_func(ctx, &arg, params);
              if (rc)
                  goto out;
              /* 检查和解析返回的数据,并将数据复制到userspace用户体用的buffser中 */
              if (put_user(arg.ret, &uarg->ret) ||
                  put_user(arg.ret_origin, &uarg->ret_origin)) {
                  rc = -EFAULT;
                  goto out;
              }
              rc = params_to_user(uparams, arg.num_params, params);
          out:
              if (params) {
                  /* Decrease ref count for all valid shared memory pointers */
                  for (n = 0; n < arg.num_params; n++)
                      if (tee_param_is_memref(params + n) &&
                          params[n].u.memref.shm)
                          tee_shm_put(params[n].u.memref.shm);
                  kfree(params);
              }
              return rc;
          }

发现没,咱们在实现CA的十个API调入最后都是调入到driver中了。

相关文章:

  • F-003 FPGA基础配置
  • java-php-python-ssm百分百教育集团教务管理系统设计计算机毕业设计
  • Apple M1 Macos 安装虚拟机软件UTM
  • 167.两数之和II-输入有序数组 || 双指针
  • aspnetcore6.0源代码编译调试
  • 【力扣刷题】Day04——链表专题
  • 云计算以及云计算安全相关的中文概述
  • 【 C++ 】哈希表底层结构剖析
  • Swift 基础语法 - 数据类型
  • js单行代码------对象
  • T1061 求整数的和与均值(信息学一本通C++)
  • Java注解-最通俗易懂的讲解
  • 特殊类设计
  • 【STL***deque容器二】
  • 多测师肖sir_高级讲师_第2个月第8讲解类
  • 【笔记】你不知道的JS读书笔记——Promise
  • 【跃迁之路】【519天】程序员高效学习方法论探索系列(实验阶段276-2018.07.09)...
  • Codepen 每日精选(2018-3-25)
  • Django 博客开发教程 16 - 统计文章阅读量
  • github从入门到放弃(1)
  • PHP面试之三:MySQL数据库
  • Spring Boot MyBatis配置多种数据库
  • spring-boot List转Page
  • vue2.0一起在懵逼的海洋里越陷越深(四)
  • 力扣(LeetCode)22
  • 扑朔迷离的属性和特性【彻底弄清】
  • 前端_面试
  • 前端代码风格自动化系列(二)之Commitlint
  • 前端自动化解决方案
  • 如何使用 OAuth 2.0 将 LinkedIn 集成入 iOS 应用
  • 探索 JS 中的模块化
  • 提升用户体验的利器——使用Vue-Occupy实现占位效果
  • 我与Jetbrains的这些年
  • 阿里云API、SDK和CLI应用实践方案
  • ​queue --- 一个同步的队列类​
  • #include到底该写在哪
  • #设计模式#4.6 Flyweight(享元) 对象结构型模式
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (poj1.2.1)1970(筛选法模拟)
  • (二十四)Flask之flask-session组件
  • (紀錄)[ASP.NET MVC][jQuery]-2 純手工打造屬於自己的 jQuery GridView (含完整程式碼下載)...
  • (一)spring cloud微服务分布式云架构 - Spring Cloud简介
  • (原創) 博客園正式支援VHDL語法著色功能 (SOC) (VHDL)
  • (转载)虚幻引擎3--【UnrealScript教程】章节一:20.location和rotation
  • (自用)learnOpenGL学习总结-高级OpenGL-抗锯齿
  • .[hudsonL@cock.li].mkp勒索病毒数据怎么处理|数据解密恢复
  • .NET 4.0中使用内存映射文件实现进程通讯
  • .NET Core 实现 Redis 批量查询指定格式的Key
  • .net 中viewstate的原理和使用
  • .net操作Excel出错解决
  • .NET构架之我见
  • .NET和.COM和.CN域名区别
  • /deep/和 >>>以及 ::v-deep 三者的区别
  • @hook扩展分析
  • [ HTML + CSS + Javascript ] 复盘尝试制作 2048 小游戏时遇到的问题