Writing Device Drivers

Associating Kernel Memory With User Mappings

Some device drivers may need to allocate kernel memory that is made accessible to user programs by using mmap(2). Examples of this are setting up shared memory for communication between two applications or between driver and application.


Caution - Caution -

MAP_FIXED must not be set in mmap(2) if kernel memory is mapped. Setting MAP_FIXED makes the application non-portable.


In general, the steps for exporting kernel memory to user applications are:

  1. Allocate kernel memory using ddi_umem_alloc(9F).

  2. Export the memory using devmap_umem_setup(9F).

  3. If not needed, free the memory using ddi_umem_free(9F).

Device Mapping Additions to the State Structure

This section adds the following fields to the state structure. See "Software State Structure" for more information.

	void 					*umem;			/* exported kernel memory */
 	ddi_umem_cookie_t					ucookie;			/* kernel memory cookie */

Allocating Kernel Memory for User Access

ddi_umem_alloc(9F) is provided to allocate kernel memory that is exported to applications:

	void *ddi_umem_alloc(size_t size, int flag,
 			ddi_umem_cookie_t *cookiep);			

size is the number of bytes to allocate.

flag is used to determine the sleep conditions and the memory type.

cookiep is a pointer to a kernel memory cookie.

ddi_umem_alloc(9F) allocates page-aligned kernel memory and returns a pointer to the allocated memory. The initial contents of the memory is zero-filled. The number of bytes allocated is a multiple of the system page size (roundup of size). The allocated memory can be used in the kernel and can be exported to applications. cookiep is a pointer to the kernel memory cookie that describes the kernel memory being allocated. It is used in devmap_umem_setup(9F) when the driver exports the kernel memory to a user application.

The flag argument indicates whether ddi_umem_alloc(9F) will block or return immediately, and whether the allocated kernel memory is pageable. Table 11-1 lists the values for flag.

Table 11-1 ddi_umem_alloc(9F) flag Values

Values 

Indicated Action 

DDI_UMEM_NOSLEEP

Driver does not need to wait for memory to become available. Return NULL if memory unavailable. 

DDI_UMEM_SLEEP

Driver can wait indefinitely for memory to become available. 

DDI_UMEM_PAGEABLE

Driver allows memory to be paged out. If not set, the memory will be locked down. 

Example 11-2 shows how to allocate kernel memory for application access. The driver exports one page of kernel memory, which is used by multiple applications as a shared memory area. The memory is allocated in segmap(9E) when an application maps the shared page the first time. An additional page is allocated if the driver has to support multiple application data models (for example a 64-bit driver exporting memory to 64-bit and 32-bit applications). 64-bit applications share the first page, and 32-bit applications share the second page.


Example 11-2 ddi_umem_alloc(9F) Routine

static int
xxsegmap(dev_t dev, off_t off, struct as *asp, caddr_t *addrp,
	off_t len, unsigned int prot, unsigned int maxprot,
	unsigned int flags, cred_t *credp)
{
	int error;
	minor_t instance = getminor(dev);
	struct xxstate *xsp = ddi_get_soft_state(statep, instance);

	mutex_enter(&xsp->mu);
	if (xsp->umem == NULL) {
	   	size_t mem_size;
#ifdef  _MULTI_DATAMODEL
		/* 64-bit driver supports 64-bit and 32-bit applications */
	   	mem_size = ptob(2);
#else
	   	mem_size = ptob(1);
#endif /* _MULTI_DATAMODEL */

	   	/* allocate the shared area as kernel pageable memory */
	   	xsp->umem = ddi_umem_alloc(mem_size,
						DDI_UMEM_SLEEP | DDI_UMEM_PAGEABLE,
						&xsp->ucookie);
	}
	mutex_exit(&xsp->mu);
	/* Set up the user mapping */
	error = devmap_setup(dev, (offset_t)off, asp, addrp, len,
				prot, maxprot, flags, credp);
	
	return (error);
}

Exporting Kernel Memory to Applications

devmap_umem_setup(9F) is provided to export kernel memory to user applications. devmap_umem_setup(9F) must be called from the driver's devmap(9E) entry point:

	int devmap_umem_setup(devmap_cookie_t handle, dev_info_t *dip,
 			struct devmap_callback_ctl *callbackops,
 			ddi_umem_cookie_t cookie, offset_t koff,
 			size_t len, u_int maxprot, u_int flags,
 			ddi_device_acc_attr_t *accattrp);

handle is an opaque structure that the system uses to describe the mapping.

dip is a pointer to the device's dev_info structure.

callbackops is a pointer to a devmap_callback_ctl(9S) structure.

cookie is a kernel memory cookie returned by ddi_umem_alloc(9F).

koff is the offset into the kernel memory specified by cookie.

len is the length in bytes that is exported.

maxprot specifies the maximum protection possible for the exported mapping.

flags must be set to DEVMAP_DEFAULTS.

accattrp is a pointer to a ddi_device_acc_attr(9S) structure.

handle is a device-mapping handle that the system uses to identify the mapping. It is passed in by the devmap(9E) entry point. dip is a pointer to the device's dev_info structure. dip is stored by the driver in its private data structure during attach(9E). callbackops allows the driver to be notified of user events on the mapping. Most drivers will set callbackops to NULL when kernel memory is exported.

koff and len specify a range within the kernel memory allocated by ddi_umem_alloc(9F). This range will be made accessible to the user's application mapping at the offset passed in by the devmap(9E) entry point. Usually the driver will pass the devmap(9E) offset directly to devmap_umem_setup(9F). The return address of mmap(2) will then map to the kernel address returned by ddi_umem_alloc(9F). koff and len must be page aligned.

maxprot enables the driver to specify different protections for different regions within the exported kernel memory. For example, one region might not allow write access by only setting PROT_READ and PROT_USER.

Example 11-3 shows how to export kernel memory to an application. The driver first checks if the requested mapping falls within the allocated kernel memory region. If a 64-bit driver receives a mapping request from a 32-bit application, the request is redirected to the second page of the kernel memory area. This ensures that only applications compiled to the same data model will share the same page.


Example 11-3 devmap_umem_setup(9F) Routine

static int
xxdevmap(dev_t dev, devmap_cookie_t handle, offset_t off,
	size_t len, size_t *maplen, uint_t model)
{
	struct xxstate *xsp;
	int	error;

	/* round up len to a multiple of a page size */
	len = ptob(btopr(len));
	/* check if the requested range is ok */
	if (off + len > ptob(1))
	   	return (ENXIO);
	xsp = ddi_get_soft_state(statep, getminor(dev));
	if (xsp == NULL)
	   	return (ENXIO);
	
#ifdef  _MULTI_DATAMODEL
	if (ddi_model_convert_from(model) == DDI_MODEL_ILP32) {
	   	/* request from 32-bit application. Skip first page */
	   	off += ptob(1);
	}
#endif  /* _MULTI_DATAMODEL */
	/* export the memory to the application */
	error = devmap_umem_setup(handle, xsp->dip, NULL, xsp-
>ucookie,
				off, len, PROT_ALL, DEVMAP_DEFAULTS, NULL);
	*maplen = len;
	return (error);
}

Freeing Kernel Memory Exported for User Access

When the driver is unloaded, the memory must be freed. ddi_umem_free(9F) frees memory that was allocated by ddi_umem_alloc(9F).

	void ddi_umem_free(ddi_umem_cookie_t cookie);

cookie is the kernel memory cookie returned by ddi_umem_alloc(9F).