I'm actually porting a driver for PCI cards from Linux to FreeBSD.
As usual, the user space API calls used to configure the PCI card send IOCTL
calls to the kernel driver which actually accesses the PCI card. Most of
the configuration data is held in predefined structures of fixed size, and
the associated IOCTL codes are defined including a read/write flag, and
including the data type, e.g.:
typedef struct
{
...
} PCPS_DEV;
#define IOCTL_GET_PCPS_DEV _IOR( 'Z', 0x01, PCPS_DEV )
The user space code which implements the IOCTL code is:
PCPS_DEV dev_info;
rc = ioctl( fd, IOCTL_GET_PCPS_DEV, &dev_info )
The kernel module implements the IOCTL handler:
PCPS_DEV krnl_dev_info = ...;
int drvr_ioctl( struct cdev *dev, u_long cmd, caddr_t data,
int32_t flag, struct thread *td )
{
switch ( cmd )
{
case IOCTL_GET_PCPS_DEV:
memcpy( data, &krnl_var, sizeof( krnl_var ) );
break;
...
}
return 0;
}
The interesting thing here is that this code works correctly, even though
the kernel driver simply uses memcpy() to copy the contents of the kernel
space buffer to the buffer the pointer of which is passed down by the IOCTL
call.
In the Linux driver I have to use the special function copy_to_user() to
pass the kernel space data to the user space buffer. FreeBSD's "man 9
copyout" suggests I'd also have to use copyout() instead of memcpy() in
FreeBSD, but copyout() always returned EFAULT and no data is returned,
whereas memcpy() works.
I'm actually assuming this is because the kernel analyzes the IOCTL code and
cares about copying from kernel to user space based on the read flag and
the data size encoded in the IOCTL command.
The problem is there are also a few IOCTL calls required where the data size
can vary, in which case it is not possible to encode the data size into the
IOCTL code. If my assumption is correct that the kernel cares about copying
from kernel to user space in the case above then I'd expect I had to use
copyout() in case of variable data size. However, I have tried 2
definitions for IOCTL codes with variable data size:
#define IOCTL_GET_VAR_DATA_1 _IO( 'Z', 0x02 )
#define IOCTL_GET_VAR_DATA_2 _IOR( 'Z', 0x03, 0 )
The first one encodes neither the read flag nor a data size, and the second
one encodes a read flag, but data size 0. However, neither of the
approaches above works as expected. memcpy() does not work in this case,
i.e. the copied data does not arrive in user space, and copyout() always
returns EFAULT.
Can someone shed some light on this and give me a hint what I'm missing?
Thanks in advance,
Martin
--
Martin Burnicki
Meinberg Funkuhren
Bad Pyrmont
Germany
> I'm actually porting a driver for PCI cards from Linux to FreeBSD.
> The interesting thing here is that this code works correctly, even though
> the kernel driver simply uses memcpy() to copy the contents of the kernel
> space buffer to the buffer the pointer of which is passed down by the IOCTL
> call.
>
> I'm actually assuming this is because the kernel analyzes the IOCTL code and
> cares about copying from kernel to user space based on the read flag and
> the data size encoded in the IOCTL command.
See <sys/ioccom.h> and ioctl() in src/sys/kern/sys_generic.c for
how this is implemented.
> The problem is there are also a few IOCTL calls required where the data size
> can vary, in which case it is not possible to encode the data size into the
> IOCTL code.
Use a buffer of the maximum data size possible and encode that
length.
Alternatively, you can also pass a pointer as parameter to ioctl()...
$ find /sys/ -name \*.h | xargs egrep '_IO(R|W|WR)\(.*[^/]\*[^/]'
... although more typically people pass a struct that contains a
pointer among other things. The kernel will copy the pointer itself
into the kernel address space and you use copyout(9) when dereferencing
the pointer.
--
Christian "naddy" Weisgerber na...@mips.inka.de
Thanks for these hints.
I finally ended up doing the latter, i.e. for IOCTLs using input/output
buffers of variable sizes pass down a generic IOCTL request structure with
pointers to the user space buffers and associated buffer sizes. The size of
this request structure is constant and thus can be coded into the IOCTL
codes, and the buffers passed down can be read or filled up using copyin or
copyout.
Thanks again!