tee driver 对共享内存的管理

本文解析了optee_config_shm_memremap函数如何配置secureworld与normalworld间的共享内存,包括虚拟地址映射、内存池创建及内存分配流程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在driver/tee/optee/core.c中的optee_probe函数中会调用optee_config_shm_memremap 来配置secure world 和 normal world之间的共享内存
static struct tee_shm_pool *
optee_config_shm_memremap(optee_invoke_fn *invoke_fn, void **memremaped_shm)
{
    union {
        struct arm_smccc_res smccc;
        struct optee_smc_get_shm_config_result result;
    } res;
    struct tee_shm_pool *pool;
    unsigned long vaddr;
    phys_addr_t paddr;
    size_t size;
    phys_addr_t begin;
    phys_addr_t end;
    void *va;
    struct tee_shm_pool_mem_info priv_info;
    struct tee_shm_pool_mem_info dmabuf_info;

    invoke_fn(OPTEE_SMC_GET_SHM_CONFIG, 0, 0, 0, 0, 0, 0, 0, &res.smccc);
    if (res.result.status != OPTEE_SMC_RETURN_OK) {
        pr_info("shm service not available\n");
        return ERR_PTR(-ENOENT);
    }

    if (res.result.settings != OPTEE_SMC_SHM_CACHED) {
        pr_err("only normal cached shared memory supported\n");
        return ERR_PTR(-EINVAL);
    }

    begin = roundup(res.result.start, PAGE_SIZE);
    end = rounddown(res.result.start + res.result.size, PAGE_SIZE);
    paddr = begin;
    size = end - begin;

    if (size < 2 * OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE) {
        pr_err("too small shared memory area\n");
        return ERR_PTR(-EINVAL);
    }
//得到共享内存的虚拟地址
    va = memremap(paddr, size, MEMREMAP_WB);
    if (!va) {
        pr_err("shared memory ioremap failed\n");
        return ERR_PTR(-EINVAL);
    }
    vaddr = (unsigned long)va;
//这里的共享内存分为两种,一种是priv_info,一个是dmabuf_info,分别给这两组共享内存赋值后,调用tee_shm_pool_alloc_res_mem
    priv_info.vaddr = vaddr;
    priv_info.paddr = paddr;
    priv_info.size = OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
    dmabuf_info.vaddr = vaddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
    dmabuf_info.paddr = paddr + OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;
    dmabuf_info.size = size - OPTEE_SHM_NUM_PRIV_PAGES * PAGE_SIZE;

    pool = tee_shm_pool_alloc_res_mem(&priv_info, &dmabuf_info);
    if (IS_ERR(pool)) {
        memunmap(va);
        goto out;
    }

    *memremaped_shm = va;
out:
    return pool;
}
在tee_shm_pool_alloc_res_mem 中分别对priv_info和dmabuf_info 调用pool_res_mem_mgr_init
    /*
     * Create the pool for driver private shared memory
     */
    ret = pool_res_mem_mgr_init(&pool->private_mgr, priv_info,
                    3 /* 8 byte aligned */);
    if (ret)
        goto err;

这里以对priv_info为例
static int pool_res_mem_mgr_init(struct tee_shm_pool_mgr *mgr,
                 struct tee_shm_pool_mem_info *info,
                 int min_alloc_order)
{
    size_t page_mask = PAGE_SIZE - 1;
    struct gen_pool *genpool = NULL;
    int rc;

    /*
     * Start and end must be page aligned
     */
//检查是否page size对其。可见share memory 是要按page size对齐的
    if ((info->vaddr & page_mask) || (info->paddr & page_mask) ||
        (info->size & page_mask))
        return -EINVAL;
//通过gen_pool_create 申请一个struct gen_pool *genpool = NULL;其中gen_pool_create是kernel 提供的公共函数,其和tee 无关
    genpool = gen_pool_create(min_alloc_order, -1);
    if (!genpool)
        return -ENOMEM;
//设置这个pool的algo函数,以后tee driver会从这个pool管理的memory中申请内存,这个algo就用于寻找空闲内存的算法,这里也可以知道gen pool是通过chunk 管理memory的,独立与伙伴系统和slab系统.
    gen_pool_set_algo(genpool, gen_pool_best_fit, NULL);
//pool 只是管理chunk的,要调用gen_pool_add_virt 来将前面一定映射好的虚拟内存加到pool中
    rc = gen_pool_add_virt(genpool, info->vaddr, info->paddr, info->size,
                   -1);
    if (rc) {
        gen_pool_destroy(genpool);
        return rc;
    }

    mgr->private_data = genpool;
    mgr->ops = &pool_ops_generic;
    return 0;
}
后面就会通过pool_ops_generic的alloc函数申请内存,通过pool_ops_generic的free函数申请内存。
static int pool_op_gen_alloc(struct tee_shm_pool_mgr *poolm,
                 struct tee_shm *shm, size_t size)
{
    unsigned long va;
这个private_date就是genpool结构,是在pool_res_mem_mgr_init 中赋值的mgr->private_data = genpool;
    struct gen_pool *genpool = poolm->private_data;
    size_t s = roundup(size, 1 << genpool->min_alloc_order);
//申请内存,返回的虚拟地址,因为我加到genpool的时候已经是虚拟地址了
    va = gen_pool_alloc(genpool, s);
    if (!va)
        return -ENOMEM;

    memset((void *)va, 0, s);
    shm->kaddr = (void *)va;
//将虚拟地址转成物理地址
    shm->paddr = gen_pool_virt_to_phys(genpool, va);
    shm->size = s;
    return 0;
}
unsigned long gen_pool_alloc(struct gen_pool *pool, size_t size)
{
    return gen_pool_alloc_algo(pool, size, pool->algo, pool->data);
}
unsigned long gen_pool_alloc_algo(struct gen_pool *pool, size_t size,
        genpool_algo_t algo, void *data)
{
    struct gen_pool_chunk *chunk;
    unsigned long addr = 0;
    int order = pool->min_alloc_order;
    int nbits, start_bit, end_bit, remain;

#ifndef CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG
    BUG_ON(in_nmi());
#endif

    if (size == 0)
        return 0;
//将要申请的size 转成order来申请
    nbits = (size + (1UL << order) - 1) >> order;
    rcu_read_lock();
//遍历pool->chunks,
    list_for_each_entry_rcu(chunk, &pool->chunks, next_chunk) {
        if (size > atomic_read(&chunk->avail))
            continue;

        start_bit = 0;
        end_bit = chunk_size(chunk) >> order;
retry:
//调用algo函数来寻找size 符合nbits的chunk,这里的algo函数为gen_pool_best_fit
        start_bit = algo(chunk->bits, end_bit, start_bit,
                 nbits, data, pool);
        if (start_bit >= end_bit)
            continue;
        remain = bitmap_set_ll(chunk->bits, start_bit, nbits);
        if (remain) {
            remain = bitmap_clear_ll(chunk->bits, start_bit,
                         nbits - remain);
            BUG_ON(remain);
            goto retry;
        }
//得到申请的地址
        addr = chunk->start_addr + ((unsigned long)start_bit << order);
        size = nbits << order;
        atomic_sub(size, &chunk->avail);
        break;
    }
    rcu_read_unlock();
    return addr;
}

<think>我们正在讨论的是i.MX6Q平台上的TEE.bin与Linux内核的协同启动机制。虽然用户提供的引用[1]是关于i.MX8系列的双显示支持,但这与当前问题无关。因此,我们将专注于i.MX6Q的启动流程。在i.MX6Q平台上,TEE.bin(OP-TEE)与Linux内核的协同启动是通过特定的启动顺序和内存隔离实现的。以下是详细机制:###1.**启动顺序**i.MX6Q的启动流程通常遵循以下顺序:-**BootROM**→**Bootloader(U-Boot)**→**TEEOS(tee.bin)**→**LinuxKernel**具体步骤:a.BootROM加载并运行U-Boot。b.U-Boot加载tee.bin到安全内存区域(例如:0x9e000000),并跳转到其入口点执行。c.OP-TEE初始化安全环境(包括安全监视器调用(smc)处理、安全内存分配等)。d.OP-TEE将控制权返回给U-Boot。e.U-Boot加载Linux内核到非安全内存(例如:0x12000000)。f.U-Boot通过`smc`指令触发安全监视器模式,通知OP-TEE将进入Linux内核。g.OP-TEE设置非安全世界的上下文(包括Linux内核启动地址)。h.U-Boot执行`bootm`命令启动Linux内核。###2.**内存隔离**-**安全内存区域**:OP-TEE运行时需要独占一块物理内存(由设备树中的`reserved-memory`节点定义),Linux内核无法访问此区域。-**非安全内存**:Linux内核运行在非安全世界,只能访问非安全内存。设备树示例:```dtsreserved-memory{#address-cells=<1>;#size-cells=<1>;ranges;/*为TEE保留256MB内存*/tee_memory:tee@9e000000{reg=<0x9e0000000x10000000>;no-map;};};```###3.**世界切换机制**-当Linux内核需要执行安全操作(如调用可信应用)时,通过`smc`指令触发安全监视器异常。-OP-TEE接管CPU,执行请求的安全服务,然后返回非安全世界。###4.**Linux内核的TEE驱动支持**Linux内核需要启用`CONFIG_TEE`和`CONFIG_OPTEE`驱动,以便与OP-TEE通信:```bashDeviceDrivers--->TEEsupport---><*>TEEcore<*>OP-TEEdriver```###5.**U-Boot启动命令示例**在U-Boot中加载TEE.bin和Linux内核的命令序列:```bash#加载tee.bin到安全内存fatloadmmc0:10x9e000000tee.bin#加载Linux内核到非安全内存fatloadmmc0:10x12000000zImagefatloadmmc0:10x18000000imx6q-sabresd.dtb#设置启动参数,包括TEE的地址setenvbootargs'console=ttymxc0,115200root=/dev/mmcblk0p2rootwait'setenvtee_addr0x9e000000#启动内核并传递TEE信息bootz0x12000000-0x18000000```###6.**协同启动流程图解```plaintext+----------------++----------------++----------------++----------------+|BootROM|---->|U-Boot|---->|OP-TEE|---->|LinuxKernel|+----------------++----------------++----------------++----------------+|||||加载U-Boot|加载tee.bin|初始化安全世界|运行非安全世界||到安全内存|并返回控制权|通过smc调用TEE服务||||```###验证协同启动-**U-Boot日志**:检查TEE加载地址和状态。-**Linux内核日志**:`dmesg|greptee`应显示OP-TEE驱动初始化成功。-**安全服务测试**:使用`optee_example_hello_world`测试可信应用调用。###常见问题-**TEE加载地址冲突**:确保tee.bin加载地址与设备树中预留的内存区域一致。-**内核未启用TEE驱动**:检查内核配置是否包含`CONFIG_OPTEE`。§§相关问题§§1.i.MX6Q平台上如何调试OP-TEE的启动失败问题?2.如何为i.MX6Q定制OP-TEE的可信应用(TA)?3.i.MX6Q的TEE与Linux内核之间如何实现数据共享?</think>### i.MX6Q平台上TEE.BIN与Linux内核的协同启动机制 在i.MX6Q平台上,TEE.BIN(OP-TEE OS)与Linux内核的协同启动是通过 **ARM TrustZone 技术** 实现的硬件级隔离机制,其核心流程如下: #### 一、启动顺序与角色分工 1. **BootROM** (固化在芯片中) - 首先加载并验证 **U-Boot** 的完整性 - 初始化安全硬件环境[^1] 2. **U-Boot** (Bootloader) ```bash # 典型启动命令序列 fatload mmc 0:1 0x9e000000 tee.bin # 加载TEE到安全内存 fatload mmc 0:1 0x12000000 zImage # 加载Linux内核 bootm 0x12000000 - 0x18000000 # 传递TEE地址给内核 ``` - 关键操作: - 将 `tee.bin` 加载到 **安全内存区域** (如 `0x9e000000`) - 通过设备树向Linux内核传递TEE信息[^2] 3. **OP-TEE (tee.bin)** - 在ARM安全态(Secure World)初始化: - 建立安全监控模式(Monitor Mode) - 划分安全/非安全内存区域 - 初始化加密引擎(如CAAM) 4. **Linux内核** (非安全态) - 启动时检查 `CONFIG_OPTEE` 驱动 - 通过 **SMC (Secure Monitor Call)** 指令与TEE通信 ```c // 内核驱动示例 (drivers/tee/optee/core.c) call_tee_fn(struct tee_context *ctx, u32 cmd, ...) { arm_smccc_smc(...); // 触发安全世界切换 } ``` #### 二、内存隔离机制 采用 **物理地址空间划分**: $$ \begin{cases} \text{安全内存: } & 0x9E000000 \sim 0x9FFFFFFF \\ \text{非安全内存: } & 0x10000000 \sim 0x9DFFFFFF \end{cases} $$ - Linux内核无法直接访问安全内存区域 - 通过 **MMU双页表** 实现硬件级隔离[^1] #### 三、世界切换流程 ```mermaid sequenceDiagram Linux内核->>+OP-TEE: SMC指令(系统调用) OP-TEE-->>安全服务: 处理请求(加密/认证等) OP-TEE->>-Linux内核: 返回结果(通过寄存器) Note right of OP-TEE: 切换时保存/恢复<br>CPU上下文状态 ``` #### 四、关键协同组件 1. **OP-TEE驱动** (Linux内核) - 提供 `/dev/tee0` 字符设备接口 - 实现RPC(远程过程调用)桥接 2. **TEE Supplicant** (用户空间守护进程) - 处理TEE的文件系统访问等请求 ```bash /usr/sbin/tee-supplicant & # 通常由init启动 ``` 3. **共享内存缓冲区** - 通过 `CONFIG_OPTEE_SHMEM_SIZE` 配置 - 使用 **物理连续内存** 避免泄露安全数据 #### 五、启动验证方法 1. U-Boot日志检查: ```bash U-Boot > printenv tee_addr=0x9e000000 # 确认TEE加载地址 ``` 2. Linux内核日志: ```bash dmesg | grep tee # 成功输出: [TEE] OP-TEE driver initialized ``` 3. TEE状态检查: ```bash optee_example_hello_world # 运行测试TA ``` #### 六、常见故障排除 | 故障现象 | 解决方案 | |------------------------|----------------------------| | 内核无法找到TEE | 检查U-Boot的 `bootm` 参数传递 | | SMC调用超时 | 验证内存映射是否冲突 | | 安全服务不可用 | 确认 `tee-supplicant` 运行状态| > ⚠️ 注意:i.MX6Q的 **DDR控制器配置** 必须与TEE内存区域对齐,错误配置会导致启动失败[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值