Solaris 内核在适当的硬件上以 64 位模式运行,既支持 32 位应用程序,也支持 64 位应用程序。要求 64 位设备驱动程序同时支持来自这两种处理能力的程序的 I/O 控制命令。32 位程序与 64 位程序的差异在于 C 语言类型模型。32 位程序是 ILP32,而 64 位程序是 LP64。有关 C 数据类型模型的信息,请参见附录 C。
如果程序和内核之间传输的数据具有不同的格式,则驱动程序必须能够处理这种模型不匹配。处理模型不匹配需要对数据进行适当的调整。
要确定是否存在模型不匹配,ioctl(9E) 模式参数会将数据模型位传递到驱动程序。如示例 15–14 中所示,该模式参数随后会被传递到 ddi_model_convert_from(9F),以确定是否有必要进行模型转换。
模式参数的标志子字段用于将数据模型传递到 ioctl(9E) 例程中。可以将此标志设置为以下值之一:
DATAMODEL_ILP32
DATAMODEL_LP64
可以有条件地定义 FNATIVE,以匹配内核实现的数据模型。应使用 FMODELS 掩码来提取 mode 参数的标志。驱动程序随后会对数据模型进行明确检查,以确定如何复制应用程序的数据结构。
DDI 函数 ddi_model_convert_from(9F) 是一个公用例程,可帮助一些驱动程序完成它们的 ioctl() 调用。该函数将用户应用程序的数据类型模型用作参数,并返回下列值之一:
DDI_MODEL_ILP32-从 ILP32 应用程序进行转换
DDI_MODEL_NONE-无需转换
如果不必进行数据转换,则会返回 DDI_MODEL_NONE。当应用程序和驱动程序具有相同的数据模型时,便会发生这种情况。DDI_MODEL_ILP32 将返回到被编译为 LP64 模式而且能够与 32 位应用程序进行通信的驱动程序。
在以下示例中,驱动程序复制了包含用户地址的数据结构。数据结构的处理能力从 ILP32 更改为 LP64。相应地,此 64 位驱动程序在与 32 位应用程序进行通信时使用 32 位版本的结构。
struct args32 { uint32_t addr; /* 32-bit address in LP64 */ int len; } struct args { caddr_t addr; /* 64-bit address in LP64 */ int len; } static int xxioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *credp, int *rvalp) { struct xxstate *xsp; struct args a; xsp = ddi_get_soft_state(statep, getminor(dev)); if (xsp == NULL) { return (ENXIO); } switch (cmd) { case XX_COPYIN_DATA: switch(ddi_model_convert_from(mode)) { case DDI_MODEL_ILP32: { struct args32 a32; /* copy 32-bit args data shape */ if (ddi_copyin((void *)arg, &a32, sizeof (struct args32), mode) != 0) { return (EFAULT); } /* convert 32-bit to 64-bit args data shape */ a.addr = a32.addr; a.len = a32.len; break; } case DDI_MODEL_NONE: /* application and driver have same data model. */ if (ddi_copyin((void *)arg, &a, sizeof (struct args), mode) != 0) { return (EFAULT); } } /* continue using data shape in native driver data model. */ break; case XX_COPYOUT_DATA: /* copyout handling */ break; default: /* generic "ioctl unknown" error */ return (ENOTTY); } return (0); }