|
CHAPTER 1
Kernel Programming
Application Data Model and ioctl- Data Model Concepts- What`s so Hard About ioctl? - ioctl Specifics - Example - ioctl() and copyout()
Data Model ConceptsThe term data model is used here to describe the model for addresses and data that is used by the kernel and applications.On the 32-bit kernel, the same data model is used by both kernel and applications: ILP32. There is no 64-bit application support on the 32-bit kernel. On the 64-bit kernel, two different kinds of applications are supported concurrently: 32-bit applications using the ILP32 data model, and 64-bit applications using the LP64 data model. The 64-bit kernel itself uses the LP64 data model. These concepts are captured in three ags that are associated with every system call, including ioctl(2):
At first sight, the most useful question to answer about the application invoking the kernel is: "Is it ILP32 or LP64?" However, a better test is: "Is the application using the same model as the kernel, or a different model?" The concept of native data model serves to answer that question; it is conditionally defined to match the data model of the kernel implementation. This approach enables you to write substantially cleaner code.
What's so Hard About ioctl?Most driver entry points are managed by the 32-bit and 64-bit system caller handlers in the kernel in such a way that a driver does not need to be concerned about whether it is performing an operation on behalf of a 32-bit or a 64-bit application.However, ioctl(9E) offers a direct connection between applications and the kernel. It enables a driver to implement device-specific operations. That is, it can cause the driver to perform a device-specific command or to pass arbitrary data between the driver and the application. The third argument to ioctl() is either a simple integral value or a pointer to some other value, typically a data structure. The data structure might be different in size and alignment between a 32-bit and a 64-bit application. Because the form of the interface between the driver and application is generally a private agreement between the driver and the application, the kernel cannot intervene to automatically translate the data structures. It cannot even tell whether the argument is an integer or a pointer. Therefore, drivers and STREAMS modules need to know how to interpret the data structures passed in from an application.
ioctl SpecificsThe data model is passed to the ioctl(9E) routine using the mode field or ags argument. The ag will be set to one of:
with FNATIVE conditionally defined to match the data model of the kernel implementation. The ag should be extracted using the FMODELS mask. The driver can then determine the data model explicitly to work out how to copy the application data structure. The DDI function ddi_model_convert_from(9F) is a convenience routine that can assist some drivers with their ioctl() calls. The function takes the data type model of the user application as an argument and returns one of the following values:
DDI_MODEL_NONE is returned if no data conversion is necessary. This is the case when application and driver have the same data model. DDI_MODEL_ILP32 is returned if the driver is compiled to the LP64 data model and is communicating with a 32-bit application.
ExampleSuppose we have an ioctl() call on a regular character device that copies a data structure into the kernel; it might look like this:
struct opdata {
size_t size;
uint_t flag;
};
You can also define a shadow data structure that describes what the ioctl() would look like from a 32-bit application using fixed-width types:
struct opdata32 {
size32_t size;
uint32_t flag;
};
In this example, the driver ioctl() routine accepts only one command called OPONE, and after checking the parameters passed in from the user for errors, it does most of the work of the ioctl routine in a separate routine called xxdowork(). The driver can arrange to copy in the correct amount of data, and translate it to native form for use by xxdowork(), as shown in Code Example 1-1.
CODE EXAMPLE 1-1
int
xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *cr, int *rval_p)
{
struct opdata op;
if (cmd != OPONE)
return (ENOTTY);
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32: {
struct opdata32 op32;
if (copyin((void *)arg, &op32, sizeof (op32)))
return (EFAULT);
op.size = (size_t)op32.size;
op.flag = (uint_t)op32.flag;
} break;
case DDI_MODEL_NONE:
if (copyin((void *)arg, &op, sizeof (op)))
return (EFAULT);
break;
default:
return (EINVAL);
}
if (op.flag != XXACTIVE || op.size > XXSIZE)
return (EINVAL);
xxdowork(device_state, op.size);
return (0);
}
Notice that this structure allows the same driver code to be compiled either as a 32-bit or a 64-bit driver while still functioning correctly. If needed, you can use a preprocessor symbol to isolate code that is only used for handling multiple data model conversions. If the pre-processor symbol _MULTI_DATAMODEL is defined, then the driver should be prepared to handle more than just native applications.
ioctl() and copyout()So far, this discussion has considered only data coming into the driver. What happens when data must be copied out of the driver? Frequently this can be handled in the same way as shown in Code Example 1-1.However, sometimes a driver needs to copy out a native quantity that no longer fits in the 32-bit sized structure. In this case, the driver should return EOVERFLOW to the caller as an indication that the data type in the interface is too small to hold the value to be returned, as shown in Code Example 1-2.
CODE EXAMPLE 1-2
int
xxioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *cr, int *rval_p)
{
struct resdata res;
switch (ddi_model_convert_from(mode & FMODELS)) {
case DDI_MODEL_ILP32: {
struct resdata32 res32;
if (res.size > UINT_MAX)
return (EOVERFLOW);
res32.size = (size32_t)res.size;
res32.flag = res.flag;
if (copyout(&res32,
(void *)arg, sizeof (res32)))
return (EFAULT);
} break;
case DDI_MODEL_NONE:
if (copyout(&res, (void *)arg, sizeof (res)))
return (EFAULT);
break;
}
return (0);
}
| ||||||||||
|
| ||||||||||||