問題描述
來自 32 位用戶代碼的 64 位系統中的 ioctl 命令錯誤 (ioctl command wrong in 64 bit system from 32 bit user code)
I'm upgrading a device driver from a 32bit RHEL 2.6.32 to 64bit RHEL 2.6.33.9.
I have a program that talks to that driver using ioctl. It works perfectly when both the driver and the program are either 64bit or 32bit. But when the driver is 64bit, and my program is 32 bit, the ioctl command received by the driver (in compat_ioctl) does not match the values defined by the _IOR and _IOW macros.
In my driver's switch statement, the default case prints out the values of all the valid commands, which are 1-12. The 32bit ioctl command is nowhere near those values.
Can someone tell me what would cause the command from a 32bit user program to be messed up when received in a 64bit driver?
Here's some of the code: I had to type it in; the code is on a secured system without internet access, so please forgive any typos. It actually does comile and run!
// IOCTL commands from the include file - most omitted
// ...
#define PORTIO_GET_IRQ_CNT_CMD 10
#define PORTIO_CLR_IRQ_CNT_CMD 11
#define PORTIO_GET_IRQ_TIME_CMD 12
#define PORTIO_IOCTL 'k' // magic number for ioctl
// IOCTL Macros
#define PORTIO_GET_IRQ_CNT_IOCTL _IOR(PORTIO_IOCTL, PORTIO_GET_IRQ_CNT_CMD, unsigned long)
#define PORTIO_CLR_IRQ_CNT_IOCTL _IOR(PORTIO_IOCTL, PORTIO_CLR_IRQ_CNT_CMD, unsigned long)
#define PORTIO_GET_IRQ_TIME_IOCTL _IOR(PORTIO_IOCTL, PORTIO_GET_IRQ_TIME_CMD, unsigned long)
Here's the 32 bit compatible IOCTL routine, from portio.c. I've confirmed that this is being called only when my program is compiled as 32bit, and the driver is 64bit.
static long portio_compat_ioctl( struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
{
unsigned char cmd_number;
int cmd_size=0;
//...
cmd_number = _IOC_NR( cmd );
cmd_size = _IOC_SIZE( cmd );
printk( KERN_ALERT "Portio Compat IOCTL number,size = %d,%d, cmd_number, cmd_size );
//... Switch statement and cases, based on cmd_number
}
The output looks like this:
Portio Compat IOTCL number,size = 224,3157
Of course, the code expects IOCTL numbers from 1-12, and sizes around 4 or 8. That's exactly what comes back when the code and driver are both either 64bit or 32bit.
參考解法
方法 1:
It seems to me that your function compat_ioctl
takes too many parameters. Look at other definitions in the Linux kernel:
long compat_blkdev_ioctl(struct file *file, unsigned cmd, unsigned long arg)
http://lxr.linux.no/#linux+v3.5.3/block/compat_ioctl.c#L654
方法 2:
#define PORTIO_GET_IRQ_CNT_IOCTL _IOR(PORTIO_IOCTL, PORTIO_GET_IRQ_CNT_CMD, unsigned long)
#define PORTIO_CLR_IRQ_CNT_IOCTL _IOR(PORTIO_IOCTL, PORTIO_CLR_IRQ_CNT_CMD, unsigned long)
#define PORTIO_GET_IRQ_TIME_IOCTL _IOR(PORTIO_IOCTL, PORTIO_GET_IRQ_TIME_CMD, unsigned long)
change unsigned long to unit64_t (fixed data type) remove all pointer from ioctl macro arguments, have :
.compat_ioctl
.unlocked_ioctl in kernel pointing to same function.
(by Steve Sibert、user1202136、ashish)