Writing Device Drivers

I/O Control Support for 64-Bit Capable Device Drivers

The Solaris kernel runs in 64-bit mode on suitable hardware and supports both 32-bit and 64-bit applications. A 64-bit device driver is required to support I/O control commands from 32-bit and 64-bit user mode programs. The difference between a 32-bit program and a 64-bit program is its C language type model: a 32-bit program is ILP32 and a 64-bit program is LP64. See Appendix F, Making a Device Driver 64-Bit Ready , for information on C data type models.

Any data that flows between programs and the kernel and vice versa (for example using ddi_copyin(9F) or ddi_copyout(9F)) will either need to be identical in format regardless of the type model of the kernel and application, or the device driver should be able to handle a model mismatch between it and the application and adjust the data format accordingly.

To determine if there is a model mismatch, the ioctl(9E) mode parameter passes the data model bits to the driver. As Example 9-15 shows, the mode parameter is then passed to ddi_model_convert_from(9F) to determine if any model conversion is necessary.

In the following example, the driver copies a data structure which contains a user address. Because the data structure changes size from ILP32 to LP64, the 64-bit driver uses a 32-bit version of the structure when communicating with a 32-bit application.


Example 9-15 ioctl(9E) Routine to Support 32-bit and 64-bit Applications

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 & FMODELS)) {
		   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);
}