IOMMU就是所谓的SMMU,它可以将不连续的物理内存组织成一个连续的虚拟内存(这对于很多驱动来说很有意义)。IOMMU把虚拟内存转换成物理内存的逻辑与CPU的MMU一样。
通常,Linux很难分配好几MB大小的连续的物理内存(比如5MB或者10MB)。因为内存在使用当中都被会被碎片化,很难找到这么大且连续的物理内存大小。所以以前通常会在linux内核启动之前,把需要用到的几十MB或者上百MB的内存从初始化的物理内存中挖出去。但这么挖出去之后,会造成内存的浪费,因为这么挖出去的内存无论设备用或者不用都不能被Linux内核所分配使用。
IOMMU既可以让Linux内核不用预留很大的连续的物理内存,又可以保证让Linux内核使用一般的API分配到连续的虚拟内存(不管物理内存是连续的还是不连续的)。
下面来看一下IOMMU在整个架构中的位置。
这个根据芯片厂家的芯片型号有很大关系,但这里就说一下高通平台的几个MSM平台的芯片,比如MSM8960的IOMMU。这些芯片的几个多媒体子系统(multimedia subsystem)都有自己独立的IOMMU,如下图所示。(图中红色的部分为IOMMU)。MSM8960中有12个IOMMU。

从上面图中可以看到,IOMMU位于 multimedia core 和 multimedia FABRIC(MMSS FABRIC) 中间,完成地址转换工作。如果IOMMU不用的,就直接bypass。(这和CPU的MMU都一样)
MSM8960上面说了有12个 IOMMU,每个IOMMU都用于特定的 multimedia core。下面来看一下一个IOMMU部分的详细说明。

上面图中,最上面的Multimedia Core为当前IOMMU的使用者。每个IOMMU的使用者,都有一个或者两个Machine ID(MID),这些ID或者是固定的或者是使用者自己生成的??。
中间的 SMMU_M2VCBMT这个是一个标记(nomenclature)?,可以通过找到相应的Virtual Machine ID(VMID)和相应的context bank number。比如JPEG decoder的IOMMU的VMID和context bank number 可以配置在SMMU_JPEGD_M2VCBRn 寄存器(?)中。
从上图中可以看到,MSM8960的每个IOMMU有两个context buffer(通常是两个,但GFX3D IOMMU有三个context buffer)。
每个MID即使在使用同一个IOMMU,但可以有不同的page table。这给了IOMMU的使用者很大的自由度,比如JPEG decoder可以使用不同的page table去读或者去写。
context bank和VMID值都是由Secure Root of Trust(SRoT)在启动的时候设置并且被锁住的。每个context bank有自己的Translation Table Base Register(TTBR),这个寄存器就像是ARM寄存器中保存一级页表基地址的协处理器中的寄存器一样保存页表的基地址。IOMMU使用的页表的格式也是和ARM架构中的页表的格式是哟模一样的。
IOMMU的Security相关:
The VMID is needed for security. It determines on whose behalf the request is made. The context bank index is
used to de termine which context bank and which page table is used to service this request, but the VMID is
then needed to send the translated request to the rest of the system. If non secure apps are controlling the
client and the IOMMU, the nonsecure apps VMID must be programmed to reflect this so the IOMMU
cannot be used to access memory that does not belong to the nonsecure apps . When the TZ is controlling
the IOMMU, e.g., in secure playback , the VMID must be reprogrammed to allow access to the secured
memory.
Domain

IOMMU中Domain代表虚拟地址的范围。每个Domain在IOMMU map中都有自己的虚拟地址范围。在MSM8960中一共定义了四种IOMMU Domain。这表示每个IOMMU的context bank,必须要map到四个Domain中的一个所代表的虚拟地址范围当中。
enum {
VIDEO_DOMAIN,
CAMERA_DOMAIN,
DISPLAY_READ_DOMAIN,
DISPLAY_WRITE_DOMAIN,
ROTATOR_SRC_DOMAIN,
ROTATOR_DST_DOMAIN,
MAX_DOMAINS
};
一个ion client在配置IOMMU把一段地址从虚拟地址转换成物理地址的时候(就像设置mmu一样),必须要指定一个Domain。例如MDP驱动在做上述的事情的时候,就必须指定DISPLAY_DOMAIN。
int ion_map_iommu(struct ion_client *client, struct ion_handle *handle,
int domain_num, int partition_num, unsigned long align,
unsigned long iova_length, ion_phys_addr_t *iova,
unsigned long *buffer_size,
unsigned long flags, unsigned long iommu_flags)
调用ion_map_iommu()函数的例子,可以看msm_smem.c文件中的get_device_address()函数里,
调用ion_map_iommu()之前的log,显示如下:
<6>[ 40.004803] [0:TimedEventQueue: 818] Calling ion_map_iommu - domain: 5, partition: 1
<6>[ 40.012398] [0:TimedEventQueue: 818] Calling ion_map_iommu - domain: 5, partition: 1
Partitions

Partition是在一个Domain中,虚拟地址的一个窗口。所以不同的partition表示一个在一个4GB大小的虚拟地址空间中的不同的窗口。一个ION Client在请求虚拟地址->物理地址的映射的时候,Client必须指定partition以指定属于哪个虚拟地址空间。
目前有三种partition。每个部分都有如下虚拟地址窗口(开始地址以及大小)。
VIDEO_FIRMWARE_POOL
Virtual addr start = SZ_128K
Size of virtual address window = SZ_16M to SZ_128K
VIDEO_MAIN_POOL
Virtual addr start = SZ_16M
Size of virtual address window = SZ_256M to SZ_16M
GEN_POOL
Virtual addr start = SZ_256M
Size of virtual address window = SZ_2G to SZ_256M
比如,MSM8960的video core,从video core角度看,其firmware下载地址必须小于16MB,这个是其硬件决定的。下面的代码就是保证要求的虚拟地址范围满足其需求的一个例子。上面的三个partition中,只有VIDEO_FIRMWARE_POOL的地址范围在16MB以下,所以传如的partition也是VIDEO_FIRMWARE_POOL。
ion_map_iommu(ddl_context- >video_ion_client, addr ->alloc_handle, VIDEO_DOMAI N,
VIDEO_FIRMWARE_POOL, SZ_4K, 0, &iova, &buffer_size, UNCACHED, 0);
这几个Partition的定义如下:
enum {
VIDEO_FIRMWARE_POOL,
VIDEO_MAIN_POOL,
GEN_POOL,
};
IOMMU Kernel APIs
Ion APIs

前面说过,IOMMU和CPU的MMU一样,都是用于虚拟地址和物理地址之间的转换的。只不过IOMMU是服务于multimedia core。一般IOMMU提供了ion_map_iommu()函数和ion_unmap_iommu()函数完成配置IOMMU已完成虚拟地址到物理地址的转换工作,就像配置MMU一样。
ion_map_iommu()函数的第一个和第二个参数分别是ion clients和ion handle。想要把ion handle表示的ion buffer用IOMMU map起来,就必须要在调用ion_map_iommu()函数之前申请ion buffer。IOMMU在申请这个ion buffer的时候所使用的接口为ion_alloc()函数,且使用的ion heap id是 ION_IOMMU_HEAP_ID。这个在高通平台已经定义成了ION_SYSTEM_HEAP_ID。(msm_ion.h 中 #define ION_IOMMU_HEAP_ID ION_SYSTEM_HEAP_ID )。以前讲过ion memory相关的内容,ION_SYSTEM_HEAP_ID对应的ion heap type定义在了msm89xx-ion.dtsi文件中,根据ion heap type就可以看出来起使用的ion_heap_ops到底是什么。
这里直接讲了,ION_IOMMU_HEAP_ID对应的ion_heap_ops就是
static struct ion_heap_ops system_heap_ops = {
.allocate = ion_system_heap_allocate,
.free = ion_system_heap_free,
.map_dma = ion_system_heap_map_dma,
.unmap_dma = ion_system_heap_unmap_dma,
.map_kernel = ion_heap_map_kernel,
.unmap_kernel = ion_heap_unmap_kernel,
.map_user = ion_heap_map_user,
.shrink = ion_system_heap_shrink,
};
IOMMU相关的内存分配,在ion_alloc()函数调用的时候传ION_SYSTEM_HEAP_ID或者ION_IOMMU_HEAP_ID就会调用上面的alloc对应的函数。在ion_system_heap_allocate()函数分配完内存之后,其信息就会保存到相应的ion_buffer类型的变量中。
struct ion_buffer {

size_t size;
union {
void *priv_virt;
ion_phys_addr_t priv_phys;
};

int kmap_cnt; //number of times the buffer is mapped to the kernel
void *vaddr; //上面kmap_cnt如果不是0,vaddr就表示map到内核的虚拟地址!!
int dmap_cnt; //number of times the buffer is mapped for dma
struct sg_table *sg_table; //the sg table for the buffer if dmap_cnt is not zero!!
struct page **pages;
struct list_head vmas;
}
ion_buffer保存着所有分配到的物理内存的page个数,内存大小。ion_buffer后面会被用来配置IOMMU和CPU的MMU。
system_heap_ops中的map_kernel,应该是用来把分配到的内存再map给kernel的。因为有时候CPU通过MMU也是需要访问这些内存以拷贝一些数据给相应的multimedia core的。
kernel\drivers\iommu\msm_iommu-v1.c
kernel\drivers\iommu\msm_iommu_sec.c
kernel\drivers\iommu\msm_iommu_mapping.c
iommu_ops!!
ion_map_iommu() : 配置IOMMU,完成地址转换(就像配置MMU一样)。

Linux驱动基础:MSM平台IOMMU相关推荐

  1. Linux驱动视频教程推荐,隆重推荐:linux驱动基础开发系列免费教程独家版本

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 相信大家经常网上闲逛,会经常碰到很多的linux免费教程之类得,但是今天我推荐的这个linux驱动基础开发系列免费教程可不是网上可以随意找到得.废话少说: ...

  2. Linux驱动 简单的Linux驱动基础知识

    Linux驱动 简单的Linux驱动基础知识 一.简述         记--Linux驱动学习笔记. Linux驱动程序初始化硬件设备,并提供硬件控制接口给更上一层的应用调用. 例如使用QT应用程序 ...

  3. Linux驱动基础:MSM平台AP/CP通信机制

    点击打开链接 概述 MSM平台AP和CP封装到一个芯片,共享内容.所以之前也说过,高通的MSM解决方案中,CP的代码都是由AP放到指定地址的内存中以供CP运行.那上传完代码,CP开始跑之后,AP/CP ...

  4. Linux驱动基础:msm平台,modem等framework加载

    msm平台,AP和CP封装在一起,公用一块内存.所以AP需要负责把整个modem, TZ , rpm等binary拷贝到内存中以供modem等subsystem去运行.那AP这边是怎么分配这些内存,又 ...

  5. linux驱动基础开发0——linux 设备驱动概述-转

    目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer):       主要利用C库函数和Linux API进行应用 ...

  6. Linux驱动基础开发

    来源:http://www.linuxidc.com/Linux/2011-10/44721.htm Linux 设备驱动概述 目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件 ...

  7. linux驱动基础开发1——linux 设备驱动基本概念-转

    学习linux设备驱动首先我们必须明确以下几个概念,为我们接下来学习linux驱动打下坚实的基础: 应用程序.库.内核.驱动程序的关系 设备类型 设备文件.主设备号与从设备号 驱动程序与应用程序的区别 ...

  8. 通俗易懂,嵌入式Linux驱动基础

    前言 上一篇分享的:<从单片机工程师的角度看嵌入式Linux>中有简单提到Linux的三大类驱动: 我们学习编程的时候都会从hello程序开始.同样的,学习Linux驱动我们也从最简单的h ...

  9. linux 驱动基础知识(2)---设备树

    转载自 http://huaqianlee.me/2015/08/19/Android/高通平台Android源码分析之Linux内核设备树-DT-Device-Tree-dts文件/ 刚开始接触An ...

最新文章

  1. AI总监王长虎被曝离职,字节跳动AI Lab 再失一将!
  2. Pandas中map,applymap和apply方法之间的区别
  3. matlab建模实例运筹学,matlab数学建模实例与编程教程
  4. 用python实现复选框树_如何使用Python中的复选框创建树视图
  5. RecyclerView的下拉刷新和加载更多 动画
  6. C的动态优化 约瑟夫环
  7. 关于 SAP 电商云 Spartacus UI SSR 的 state transfer 问题
  8. mysqli 扩展_MySQLi的优势
  9. 您不想错过的十大Java书籍
  10. 第六十四期:聊聊原子变量、锁、内存屏障那点事
  11. 2017.7.10 Redis报错:DENIED Redis is running in protected mode
  12. JQM页面跳转,多种效果
  13. 在Filfter中使用注解了spring 的bean,报javax.naming.NamingException: Cannot create resource instance...
  14. NDK-JNI实战教程(二) JNI官方中文资料
  15. 苹果耳机无线真假测试软件,苹果耳机“真伪”终极鉴别来了,原来,区别如此大!...
  16. 微信小程序PNG图片去白底
  17. STM32单片机开发板 定制
  18. qq群api协议,非官方协议,抓包获取的QQ群操作协议
  19. Python 学习笔记 英语默写软件
  20. lol 8.21服务器维护,《LOL》8.16版本维护到几点 8月21日更新维护内容汇总

热门文章

  1. heic图片转换jpg教程
  2. vue 下拉刷新 组件 伸手党福利
  3. 编写微信小程序时出现的错误
  4. iOS中 视频直播功能-流媒体的使用
  5. word删除分节符后之前的格式乱了_很实用的14个word小技巧,能够让你在制作标书,和排版时如有神助...
  6. 如本科技亮相ITES工业展3D视觉软硬件产品引人驻足
  7. action中实现对批量文件上传的封装
  8. el-dialog遮罩层遮挡问题
  9. Excel实现数据转置,很方便操作
  10. Windows中快速在指定文件打开命令行