The Solaris system can run in 64-bit mode on appropriate hardware and provides a 64-bit kernel with a 64-bit address space for applications. To update a device driver to be 64-bit ready, driver writers need to understand the 32-bit and 64-bit C data type models, know how to use the system derived types and the fundamental C data types, and understand specific driver issues, such as how to enable a 64-bit driver and a 32-bit application to share data structures.
For details on making a device driver ready for a 64-bit environment, see Appendix C, Making a Device Driver 64-Bit Ready .
The Solaris kernel is a large collection of code that is compiled in one of two ways; it is either compiled as a 32-bit program that supports solely 32-bit applications, or as a 64-bit program that supports both 32-bit and 64-bit applications. To allow drivers and STREAMS modules to be used on both systems, you must write kernel code that is both portable between these two compilation environments and supportive of 32-bit and 64-bit applications. The resulting code must be compiled in two ways, creating two separate modules: a 32-bit module for the 32-bit kernel, and a 64-bit module for the 64-bit kernel.
Some classes of portability issues can best be solved using the standard derived types, such as size_t
, off_t
, time_t
, and caddr_t
, since these grow and shrink appropriately. To provide better support of 32-bit applications in the 64-bit kernel, fixed-width types corresponding to the sizes expected by 32-bit applications are available in <sys/types32.h>, for example, size32_t
, off32_t
, time32_t
, and caddr32_t
.
Other classes of portability problems, in particular those describing hardware registers or data sent over the wire, are best described using the size-invariant types in <sys/inttypes.h>; for example, uint16_t
, and int64_t
. It also includes the definition of intptr_t
and uintptr_t
.
See the Solaris 64-bit Developer's Guide for the full list of changes to derived types and more information on fixed-width types.
The 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 flags that are associated with every system call, including ioctl(2):
Application data model is ILP32.
Application data model is LP64.
Application data model is native.
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.
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.