首页 > 系统相关 >Linux 64位内核(arm64)驱动兼容32位应用程序(armhf)的ioctl接口

Linux 64位内核(arm64)驱动兼容32位应用程序(armhf)的ioctl接口

时间:2024-04-15 15:22:05浏览次数:26  
标签:compat 32 ioctl 64 armhf bit ptr

最近,公司来了一次硬件升级,开发平台从全志T3(armhf)升级到全志T527(arm64),平台迁移后,想直接使用原来动态库和应用程序从而减少开发量,用户态大部分接口都运行正常,唯独ioctl接口无法调用成功。

如果要成功移植要做到以下几点:

1. 驱动要同时实现 unlocked_ioctl 和 compat_ioctl。

struct file_operations
{
	long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
	long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
}__randomize_layout;

从名字就可以猜测,compat_ioctl就是发生兼容性的场景下要调用的函数,事实也确实如此。

当应用层是32位程序,内核及架构是32位程序,那么驱动的unlocked_ioctl函数被调用。
当应用层是32位程序,内核及架构是64位程序,那么驱动的compat_ioctl函数被调用。
当应用层是64位程序,内核及架构是64位程序,那么驱动的unlocked_ioctl函数被调用。

那为什么一个ioctl()系统调用需要在驱动里面实现2个对应的函数呢?

我们可以看到unlocked_ioctl 和 compat_ioctl这2个函数的最后一个参数是 unsigned long类型的,long类型在不同的架构下面的长度是不同的,在32位平台下是4字节,64位平台下就是8个字节,当32位的应用程序使用ioctl系统调用时,传了4个字节的参数,到驱动中,应该是8个字节,这样就产生了不兼容,为了不影响64位的应用程序,就提供了2个接口来实现。当compat_ioctl被调用时,这个参数就会自动扩展成8个字节的数据,这是比较简单从场景,compat_ioctl可以和unlocked_ioctl使用同样的实现。

还有一种更复杂的场景,那就是用户态ioctl的最后一个参数是一个地址,在驱动中使用copt_from_user去取数据,显然,这种情况下,传来的地址是不能直接使用的,用户态传来的32位的地址对于64位的内核来说,就是个错误的地址,那么就需要一个转换函数,那就是 compat_ptr 。

函数定义在:include/linux/compat.h

/*
 * A pointer passed in from user mode. This should not
 * be used for syscall parameters, just declare them
 * as pointers because the syscall entry code will have
 * appropriately converted them already.
 */
 /*
  * 从用户态传入一个指针。这是不能用作系统调用参数的,仅仅是个声明,
  * 系统调用的入口代码会妥善的转换好他。
  */
#ifndef compat_ptr
static inline void __user *compat_ptr(compat_uptr_t uptr)
{
	return (void __user *)(unsigned long)uptr;
}
#endif

static inline compat_uptr_t ptr_to_compat(void __user *uptr)
{
	return (u32)(unsigned long)uptr;
}

我们看看官方是如何介绍他的,ioctl based interfaces — The Linux Kernel documentation 

32-bit compat mode¶
In order to support 32-bit user space running on a 64-bit machine, each subsystem or driver that implements an ioctl callback handler must also implement the corresponding compat_ioctl handler.

As long as all the rules for data structures are followed, this is as easy as setting the .compat_ioctl pointer to a helper function such as compat_ptr_ioctl() or blkdev_compat_ptr_ioctl().

compat_ptr()
On the s390 architecture, 31-bit user space has ambiguous representations for data pointers, with the upper bit being ignored. When running such a process in compat mode, the compat_ptr() helper must be used to clear the upper bit of a compat_uptr_t and turn it into a valid 64-bit pointer. On other architectures, this macro only performs a cast to a void __user * pointer.

In an compat_ioctl() callback, the last argument is an unsigned long, which can be interpreted as either a pointer or a scalar depending on the command. If it is a scalar, then compat_ptr() must not be used, to ensure that the 64-bit kernel behaves the same way as a 32-bit kernel for arguments with the upper bit set.

The compat_ptr_ioctl() helper can be used in place of a custom compat_ioctl file operation for drivers that only take arguments that are pointers to compatible data structures.

翻译一下:

32位兼容模式
为了支持64位机器上的32位用户空间代码,每个实现了ioctl回调函数的驱动和子系统必需对应的 compat_ioctl 。
只要数据结构遵循一定的规则,这就很容易操作。将.compat_ioctl指针直接赋值为 compat_ptr_ioctl() 或者 blkdev_compat_ptr_ioctl() 这样的辅助函数就可以了。

compat_ptr()
在 s390架构中, 31-bit user space has ambiguous representations for data pointers, with the upper bit being ignored. When running such a process in compat mode, the compat_ptr() helper must be used to clear the upper bit of a compat_uptr_t and turn it into a valid 64-bit pointer. 
在其他架构中,这个宏仅仅是将其转换为void __user * 指针.

在compat_ioctl()回调中,最后一个参数是unsigned long的,根据命令的实际情况,它可能被解释为一个指针或者一个普通变量。如果是一个变量,那么compat_ptr()就不能被使用,这样就确保64-bit kernel和 32-bit kernel表现是相同的。
compat_ptr_ioctl()辅助函数被使用为一个定制的compat_ioctl file operation,仅仅将指针转换成兼容的数据结构。

什么意思呢?意思就是当ioctl的最后一个参数被当成一个值来使用的时候,是不需要使用 compat_ptr转换的,只有用作指针的时候才需要转换。当用作指针的时候,这个指针就可以被转换为兼容的地址。

当最后一个参数用作指针的时候,可以偷个懒,直接使用 compat_ptr_ioctl 用作 compat_ioctl的回调函数就行了。

我们来看下定义 fs/ioctl.c

/**
 * compat_ptr_ioctl - generic implementation of .compat_ioctl file operation
 *
 * This is not normally called as a function, but instead set in struct
 * file_operations as
 *
 *     .compat_ioctl = compat_ptr_ioctl,
 *
 * On most architectures, the compat_ptr_ioctl() just passes all arguments
 * to the corresponding ->ioctl handler. The exception is arch/s390, where
 * compat_ptr() clears the top bit of a 32-bit pointer value, so user space
 * pointers to the second 2GB alias the first 2GB, as is the case for
 * native 32-bit s390 user space.
 *
 * The compat_ptr_ioctl() function must therefore be used only with ioctl
 * functions that either ignore the argument or pass a pointer to a
 * compatible data type.
 *
 * If any ioctl command handled by fops->unlocked_ioctl passes a plain
 * integer instead of a pointer, or any of the passed data types
 * is incompatible between 32-bit and 64-bit architectures, a proper
 * handler is required instead of compat_ptr_ioctl.
 */
long compat_ptr_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
	if (!file->f_op->unlocked_ioctl)
		return -ENOIOCTLCMD;

	return file->f_op->unlocked_ioctl(file, cmd, (unsigned long)compat_ptr(arg));
}
EXPORT_SYMBOL(compat_ptr_ioctl);

可以看到,就是直接转换了一下,就调用unlocded_ioctl了。

标签:compat,32,ioctl,64,armhf,bit,ptr
From: https://www.cnblogs.com/riveruns/p/18136042

相关文章

  • 1326德永
    实验一-密码引擎-3-加密API研究研究以上API接口,总结他们的异同,并以龙脉GM3000Key为例,写出调用不同接口的代码,提交博客链接和代码链接。内容:0查找各种标准的原始文档,研究学习(至少包含CryptoAPI,PKCS#11,GMT0016-2012,GMT0018-2012)(5分)1总结这些API在编程中的使用方式(5分)2......
  • 2321. 拼接数组的最大分数(leetcode)
    https://leetcode.cn/problems/maximum-score-of-spliced-array/description/这一题应该算一个连续最大子数组思维题,要点是根据差数组去做,然后求最值classSolution{public:intmaximumsSplicedArray(vector<int>&nums1,vector<int>&nums2){//f[i]表示以......
  • 20240323 专项训练
    random给出一个有向无环的连通图。小A需要从\(1\)号点走到\(n\)号点。保证图里所有的点都能够到达\(N\)号点。小A每次会等概率的随机一个能直接走到的节点走过去。求小A从一号点走到\(n\)号点期望需要经过多长的路径。对于\(30\%\)的数据,保证\(1\len,m\le......
  • 4-WIFI&蓝牙(ESP32)转CAN或RS485总线&串口TTL模块-CSDK-设备作为TCP客户端,实现上位机
    <p><iframename="ifd"src="https://mnifdv.cn/resource/cnblogs/ESP32_CAN"frameborder="0"scrolling="auto"width="100%"height="1500"></iframe></p> 说明这节设备作为TCP客户端,连接上位......
  • 【Nano Framework ESP32 篇】刷入 nanoCLR 固件以及相关问题
    老周在几个世纪前曾写过树莓派相关的iOT水文,之所以没写NanoFramework相关的内容,是因为那时候这货还不成熟,可玩性不高。不过,这货现在已经相对完善,老周都把它用在项目上了——第一个是自制的智能插座,这个某宝上50多块可以买到,搜“esp32插座”就能找到。一种是86型盒子的,带屏......
  • 3-WIFI&蓝牙(ESP32)转CAN或RS485总线&串口TTL模块-CSDK--设备作为TCP服务器,实现上位
    <p><iframename="ifd"src="https://mnifdv.cn/resource/cnblogs/ESP32_CAN"frameborder="0"scrolling="auto"width="100%"height="1500"></iframe></p> 说明这节设备作为TCP服务器,上位机T......
  • ESP32 Arduino开发 MQTT
    ESP32Arduino开发MQTT目录ESP32Arduino开发MQTT1.安装程序库2.编写相关程序2.1.引入头文件2.2.定义MQTT相关参数2.3.创建对象2.4.连接网络2.5.连接MQTT服务器2.6.MQTT回调函数3.完整的代码例程4.MQTT连接测试1.安装程序库打开库管理工具工具->管理库.........
  • STM32F10系列开发板的GPIO介绍
    1.GPIO介绍GPIO是控制或者采集外部器件的信息的外设,即负责输入输出。它按组分配,每组16个IO口,组数视芯片而定。STM32F103ZET6芯片是144脚的芯片,具有GPIOA、GPIOB、GPIOC、GPIOD、GPIOE、GPIOF和GPIOG七组GPIO口,共有112个IO口可供我们编程使用2.GPIO八种功能模......
  • 暖风机/寻线仪防干扰/防静电LCD液晶段码屏驱动芯片VK1C21A/B/C/D/DA/E/EA具备显示效果
    概述:VK1C21A/B是一个点阵式存储映射的LCD驱动器,可支持最大128点(32SEGx4COM)的LCD屏,也支持2COM和3COM的LCD屏。单片机可通过3/4个通信脚配置显示参数和发送显示数据,也可通过指令进入省电模式。具备高抗干扰,显示效果好,静电耐压高等优良特性,可替代市面上大部分LCD驱动芯片。 特点:......
  • WinDbg分析32位应用程序dump
    使用Windbg对转储文件进行分析的时候,需要注意:1.使用64位的Windbg对64位的进程DUMP进行分析。2.使用32位的Windbg对32位的进程DUMP进行分析。特别对于32位的进程,抓DUMP的时候,需要使用32位的任务管理器进行转储文件创建。32位任务管理器路径:C:\Windows\SysWOW64\Taskmgr.exe,这个......