具体细节:
定义了以下结构体
typedef struct {
void *pInBuf;
int InSize;
void *pOutBuf;
int OutSize;
} sParams;
android hidl 层 定义了参数 sParams tmpBuf, pInBuf以及pOutBuf 保存缓存数据的地址,InSize以及OutSize保存缓存数据的大小 例:
int InVal = 25;
int OutVal[3] = {0,1,2};
sParams tmpBuf;
tmpBuf.pInBuf = &InVal;
tmpBuf.InSize = sizeof(InVal);
tmpBuf.pOutBuf = OutVal;
tmpBuf.OutSize = sizeof(OutVal);
android hidl 通过 ioctl(fd, cmd , &tmpBuf) 把 tmpBuf 参数的地址传入内核
内核驱动的 ioctl的内部实现 通过 copy_from_user 拿到tmpBuf的数据,再通过pInBuf以及pOutBuf的地址调用 copy_from_user 拿到缓存对应的数据 例:
内核的 ioctl内部实现:
custom_ioctl(struct file *filp , unsigned int cmd, unsigned long arg)
{
void *pInBuf;
void *pOutBuf;
sParams tmp;
copy_from_user((void*)&tmp, (void*)arg, sizeof(sParams));
//获取上层tmpBuf 参数初始化 参数:tmp
pInBuf = tmp.pInBuf;
pOutBuf = tmp.pOutBuf;
tmp.pInBuf = kzalloc(tmp.InSize, GFP_KERNEL);
copy_from_user(tmp.pInBuf, pInBuf, tmp.InSize);
//获取缓存pInBuf:InVal的数据
tmp.pOutBuf = kzalloc(tmp.OutSize, GFP_KERNEL);
copy_from_user(tmp.pOutBuf, pOutBuf, tmp.OutSize);
//获取缓存pOutBuf:OutVal[3] 的数据
}
异常点:
1.copy_from_user((void*)&tmp, (void*)arg, sizeof(sParams));
内核 64位系统而android是32位系统 指针的大小是不一致的导致sizeof(sParams)的大小与 android hidl 层 sizeof(sParams)的大小不一致,arg参数就算是上层tmpBuf的地址,但是由于对指针的大小计算不一致会导致 copy_from_user的数据出异常
解决办法:
typedef struct {
uint32_t pInBuf;
int InSize;
uint32_t pOutBuf;
int OutSize;
} sParams;
地址 void* 改为 uint32_t (android 系统32位)hidl 在对tmpBuf赋值时修改为:
tmpBuf.pInBuf = (uint32_t) &InVal,tmpBuf.pOutBuf = (uint32_t)(&OutVal[0]);
驱动层在调用
copy_from_user((void*)&tmp, (void*)arg, sizeof(sParams));
这个时候的sizeof(sParams)大小与 android 32位系统的计算大小一致,拷贝就不会出问题
2 拿到 缓存地址 pInBuf以及pOutBuf 后copy_from_user异常
修复异常1的问题后驱动层可以正确拿到 android hidl层通过ioctl 传递的tmpBuf的数据,
tmp.pInBuf 与 tmpBuf.pInBuf ,tmp.pOutBuf与 tmpBuf.pOutBuf的数值一致,但是
以下拷贝缓存数据的操作仍存在异常
tmp.pInBuf = kzalloc(tmp.InSize, GFP_KERNEL);
//在内核申请缓存用于保存上层缓存数据
copy_from_user(tmp.pInBuf, pInBuf, tmp.InSize);
//获取缓存pInBuf:InVal的数据
android的tmpBuf.pInBuf 通过copy_from_user 赋值给了内核的tmp.pInBuf,但是tmp.pInBuf是uint32_t类型 kernel 层的指针是 64的,数据拷贝时需做以下改动:
void *pInBuf;
void *pOutBuf;
pInBuf = (void*)(long)tmp.pInBuf;
pOutBuf = (void*)(long)tmp.pOutBuf;
tmp.pInBuf = kzalloc(tmp.InSize, GFP_KERNEL);
copy_from_user(tmp.pInBuf, pInBuf, tmp.InSize);
//获取缓存pInBuf:InVal的数据
tmp.pOutBuf = kzalloc(tmp.OutSize, GFP_KERNEL);
copy_from_user(tmp.pOutBuf, pOutBuf, tmp.OutSize);
//获取缓存pOutBuf:OutVal[3] 的数据
(void*)(long)tmp.pInBuf 先通过 (long )让32位变为64位,再通过 (void*)转为地址,
最终copy_from_user 可以正确拿到android的 InVal以及OutVal[3]的所有数据