Kernel Device Driver Development

This article is intended to be a quickstart guide for building, loading, and debugging a simple device driver for a 64-bit Windows 7 system.

kernel device driver development

Kernel Drivers specializes in Windows device driver consulting and programming. We create the software that empowers Windows platforms. What can we build for you.

2 Free Electrons. Kernel, drivers and embedded Linux development, consulting, training and support. http//free electrons.com Kernel and device drivers.

/dev/hello_world: A Simple Introduction to Device Drivers under Linux - O'Reilly Media

Chapter 1 An Introduction to Device Drivers Contents: The Role of the Device Driver Splitting the Kernel Classes of Devices and Modules Security Issues.

64-bit Device Driver Development

Class Offerings. Class Schedule. Distance Learning. On-Site. Academic Program. Student Feedback. Certification. Courseware. Linux Device Driver Training.

Linux Device Drivers DOs and DON Ts A guide to writing Robust Linux Device Drivers.

Training sessions for embedded Linux kernel driver and BSP developers. The experience and knowledge you need to quickly get your project started.

By Valerie Henson

07/05/2007

Since the misty days of yore, the first step in learning a new programming language has been writing a program that prints Hello, world. See the Hello World Collection for a list of more than 300 Hello, world. examples. In this article, we will use the same approach to learn how to write simple Linux kernel modules and device drivers. We will learn how to print Hello, world. from a kernel module three different ways: printk, a /proc file, and a device in /dev.

Preparation: Installing Kernel Module Compilation Requirements

For the purposes of this article, a kernel module is a piece of kernel code that can be dynamically loaded and unloaded from the running kernel. Because it runs as part of the kernel and needs to interact closely with it, a kernel module cannot be compiled in a vacuum. It needs, at minimum, the kernel headers and configuration for the kernel it will be loaded into. Compiling a module also requires a set of development tools, such as a compiler. For simplicity, we will briefly describe how to install the requirements to build a kernel module using Debian, Fedora, and the vanilla Linux kernel in tarball form. In all cases, you must compile your module against the source for the running kernel the kernel executing on your system when you load the module into your kernel.

A note on kernel source location, permissions, and privileges: the kernel source customarily used to be located in /usr/src/linux and owned by root. Nowadays, it is recommended that the kernel source be located in a home directory and owned by a non-root user. The commands in this article are all run as a non-root user, using sudo to temporarily gain root privileges only when necessary. To setup sudo, see the sudo 8, visudo 8, and sudoers 5 main pages. Alternatively, become root, and run all the commands as root if desired. Either way, you will need root access to follow the instructions in this article.

Preparation for Compiling Kernel Modules Under Debian

The module-assistant package for Debian installs packages and configures the system to build out-of-kernel modules. Install it with:

sudo apt-get install module-assistant

That s it; you can now compile kernel modules. For further reading, the Debian Linux Kernel Handbook has an in-depth discussion on kernel-related tasks in Debian.

Fedora Kernel Source and Configuration

The kernel-devel package for Fedora has a package that includes all the necessary kernel headers and tools to build an out-of-kernel module for a Fedora-shipped kernel. Install it with:

sudo yum install kernel-devel

Again, that s all it takes; you can now compile kernel modules. Related documentation can be found in the Fedora release notes.

Vanilla Kernel Source and Configuration

If you choose to use the vanilla Linux kernel source, you must configure, compile, install, and reboot into your new vanilla kernel. This is definitely not the easy route and this article will only cover the very basics of working with vanilla kernel source.

The canonical Linux source code is hosted at The most recent stable release is linked to from the front page. Download the full source release, not the patch. For example, the current stable release is located at For faster download, find the closest mirror from the list at and download from there. The easiest way to get the source is using wget in continue mode. HTTP is rarely blocked, and if your download is interrupted, it will continue where it left off.

wget -c

Unpack the kernel source:

tar xjvf linux-.tar.bz2

Now your kernel is located in linux-/. Change directory into your kernel and configure it:

cd linux-

make menuconfig

A number of really nice make targets exist to automatically build and install a kernel in many forms: Debian package, RPM package, gzipped tar, etc. Ask the make system for help to list them all:

make help

A target that will work on almost every distro is:

make tar-pkg

When finished building, install your new kernel with:

sudo tar -C / -xvf linux-.tar

Then create a symbolic link to the source tree in the standard location:

sudo ln -s /lib/modules/ uname -r /build

Now the kernel source is ready for compiling external modules. Reboot into your new kernel before loading modules compiled against this source tree.

Hello, World. Using printk

For our first module, we ll start with a module that uses the kernel message facility, printk, to print Hello, world.. printk is basically printf for the kernel. The output of printk is printed to the kernel message buffer and copied to /var/log/messages with minor variations depending on how syslogd is configured.

Download the hello_printk module tarball and extract it:

tar xzvf hello_printk.tar.gz

This contains two files: Makefile, which contains instructions for building the module, and hello_printk.c, the module source file. First, we ll briefly review the Makefile.

obj-m : hello_printk.o

obj-m is a list of what kernel modules to build. The. o and other objects will be automatically built from the corresponding. c file no need to list the source files explicitly.

KDIR : /lib/modules/ shell uname -r /build

KDIR is the location of the kernel source. The current standard is to link to the associated source tree from the directory containing the compiled modules.

PWD : shell pwd

PWD is the current working directory and the location of our module source files.

default:

MAKE -C KDIR M PWD modules

default is the default make target; that is, make will execute the rules for this target unless it is told to build another target instead. The rule here says to run make with a working directory of the directory containing the kernel source and compile only the modules in the PWD local directory. This allows us to use all the rules for compiling modules defined in the main kernel source tree.

Now, let s run through the code in hello_printk.c.

include

This includes the header files provided by the kernel that are required for all modules. They include things like the definition of the module_init macro, which we will see later on.

static int __init

hello_init void

printk Hello, world. n ;

return 0;

This is the module initialization function, which is run when the module is first loaded. The __init keyword tells the kernel that this code will only be run once, when the module is loaded. The printk line writes the string Hello, world. to the kernel message buffer. The format of printk arguments is, in most cases, identical to that of printf 3.

module_init hello_init ;

The module_init macro tells the kernel which function to run when the module first starts up. Everything else that happens inside a kernel module is a consequence of what is set up in the module initialization function.

static void __exit

hello_exit void

printk Goodbye, world. n ;

module_exit hello_exit ;

Similarly, the exit function is run once, upon module unloading, and the module_exit macro identifies the exit function. The __exit keyword tells the kernel that this code will only be executed once, on module unloading.

MODULE_LICENSE GPL ;

MODULE_AUTHOR Valerie Henson ;

MODULE_DESCRIPTION Hello, world. minimal module ;

MODULE_VERSION printk ;

MODULE_LICENSE informs the kernel what license the module source code is under, which affects which symbols functions, variables, etc. it may access in the main kernel. A GPLv2 licensed module like this one can access all the symbols. Certain module licenses will taint the kernel, indicating that non-open or untrusted code has been loaded. Modules without a MODULE_LICENSE tag are assumed to be non-GPLv2 and will result in tainting the kernel. Most kernel developers will ignore bug reports from tainted kernels because they do not have access to all the source code, which makes debugging much more difficult. The rest of the MODULE_ macros provide useful identifying information about the module in a standard format.

Pages: 1, 2, 3

Next Page

/dev/hello_world: A Simple Introduction to Device Drivers under Linux

Now, to compile and run the code. Change into the directory and build the module:

cd hello_printk

make

Then, load the module, using insmod, and check that it printed its message, using dmesg, a program that prints out the kernel message buffer:

sudo insmod. /hello_printk.ko

dmesg tail

You should see Hello, world. in the output from dmesg. Now unload the module, using rmmod, and check for the exit message:

sudo rmmod hello_printk

You have successfully compiled and installed a kernel module.

Hello, World. Using /proc

One of the easiest and most popular ways to communicate between the kernel and user programs is via a file in the /proc file system. /proc is a pseudo-file system, where reads from files return data manufactured by the kernel, and data written to files is read and handled by the kernel. Before /proc, all user-kernel communication had to happen through a system call. Using a system call meant choosing between finding a system call that already behaved the way you needed often not possible, creating a new system call requiring global changes to the kernel, using up a system call number, and generally frowned upon, or using the catch-all ioctl system call, which requires the creation of a special file that the ioctl operates on complex and frequently buggy, and very much frowned upon. /proc provides a simple, predefined way to pass data between the kernel and userspace with just enough framework to be useful, but still enough freedom that kernel modules can do what they need.

For our purposes, we want a file in /proc that will return Hello, world. when read. We ll use /proc/hello_world. Download and extract the hello_proc module tarball. We ll run through the code in hello_proc.c.

This time, we add the header file for procfs, which includes support for registering with the /proc file system.

The next function will be called when a process calls read on the /proc file we will create. It is simpler than a completely generic read system call implementation because we only allow the Hello, world. string to be read all at once.

static int

hello_read_proc char buffer, char start, off_t offset, int size, int eof,

void data

The arguments to this function deserve an explicit explanation. buffer is a pointer to a kernel buffer where we write the output of the read. start is used for more complex /proc files; we ignore it here. offset tells us where to begin reading inside the file ; we only allow an offset of 0 for simplicity. size is the size of the buffer in bytes; we must check that we don t write past the end of the buffer accidentally. eof is a short cut for indicating EOF end of file rather than the usual method of calling read again and getting 0 bytes back. data is again for more complex /proc files and ignored here.

Now, for the body of the function:

char hello_str Hello, world. n ;

int len strlen hello_str ; / Don t include the null byte. /

/

We only support reading the whole string at once.

/

if / size len

return -EINVAL;

If file position is non-zero, then assume the string has

been read and indicate there is no more data to be read.

if offset. 0

We know the buffer is big enough to hold the string.

strcpy buffer, hello_str ;

Signal EOF.

eof 1;

return len;

Next, we need to register with the /proc subsystem in our module initialization function.

Create an entry in /proc named hello_world that calls

hello_read_proc when the file is read.

if create_proc_read_entry hello_world, 0, NULL, hello_read_proc,

NULL 0

printk KERN_ERR

Unable to register Hello, world. proc file n ;

return -ENOMEM;

And unregister when the module unloads if we didn t do this, when a process attempted to read /proc/hello_world, the /proc file system would try to execute a function that no longer existed and the kernel would panic.

remove_proc_entry hello_world, NULL ;

MODULE_VERSION proc ;

Then, we re ready to compile and load the module:

cd hello_proc

sudo insmod. /hello_proc.ko

Now, there is a file named /proc/hello_world that will produce Hello, world. when read:

cat /proc/hello_world

Hello, world.

You can create many more /proc files from the same driver, add routines to allow writing to /proc files, create directories full of /proc files, and more. For anything more complicated than this driver, it is easier and safer to use the seq_file helper routines when writing /proc interface routines. For further reading, see Driver porting: The seq_file interface.

Hello, World. Using /dev/hello_world

Now we will implement Hello, world. using a device file in /dev, /dev/hello_world. Back in the old days, a device file was a special file created by running a crufty old shell script named MAKEDEV which called the mknod command to create every possible file in /dev, regardless of whether the associated device driver would ever run on that system. The next iteration, devfs, created /dev files when they were first accessed, which led to many interesting locking problems and wasteful attempts to open device files to see if the associated device existed. The current version of /dev support is called udev, since it creates /dev links with a userspace program. When kernel modules register devices, they appear in the sysfs file system, mounted on /sys. A userspace program, udev, notices changes in /sys and dynamically creates /dev entries according to a set of rules usually located in /etc/udev/.

Download the hello world module tarball. We ll go through hello_dev.c.

As we can see from looking at the necessary header files, creating a device requires quite a bit more kernel support than our previous methods. fs.h includes the definitions for a file operations structure, which we must fill out and attach to our /dev file. miscdevice.h includes support for registering a miscellaneous device file. asm/uaccess.h includes functions for testing whether we can read or write to userspace memory without violating permissions.

hello_read is the function called when a process calls read on /dev/hello. It writes Hello, world. to the buffer passed in the read call.

static ssize_t hello_read struct file file, char buf,

size_t count, loff_t ppos

if count len

if ppos. 0

Besides copying the string to the user provided buffer,

this function also checks that the user has permission to

write to the buffer, that it is mapped, etc.

if copy_to_user buf, hello_str, len

Tell the user how much data we wrote.

ppos len;

Next, we create the file operations struct defining what actions to take when the file is accessed. The only file operation we care about is read.

static const struct file_operations hello_fops

. owner THIS_MODULE,

. read hello_read,

;

Now, create the structure containing the information needed to register a miscellaneous device with the kernel.

static struct miscdevice hello_dev

We don t care what minor number we end up with, so tell the

kernel to just pick one.

MISC_DYNAMIC_MINOR,

Name ourselves /dev/hello.

hello,

What functions to call when a program performs file

operations on the device.

hello_fops

As usual, we register the device in the module s initialization function.

int ret;

Create the hello device in the /sys/class/misc directory.

Udev will automatically create the /dev/hello device using

the default rules.

ret misc_register hello_dev ;

if ret

Unable to register Hello, world. misc device n ;

return ret;

And remember to unregister the device in the exit function.

misc_deregister hello_dev ;

MODULE_VERSION dev ;

Compile and load the module:

cd hello_dev

sudo insmod. /hello_dev.ko

Now there is a device named /dev/hello that will produce Hello, world. when read by root:

sudo cat /dev/hello

But we can t read it as a regular user:

cat /dev/hello

cat: /dev/hello: Permission denied

ls -l /dev/hello

crw-rw---- 1 root root 10, 61 2007-06-20 /dev/hello

This is what happens with the default udev rule, which says that when a miscellaneous device appears, create a file named /dev/ and give it permissions 0660 owner and group have read-write access, everyone else has no access. We would really like instead for the device be readable by regular users and have a link to it named /dev/hello_world. In order to do this, we ll write a udev rule.

The udev rule has to do two things: create a symbolic link and change the permissions on device to make world readable. The rule that accomplishes this is:

KERNEL hello, SYMLINK hello_world, MODE 0444

We ll break the rule down into parts and explain each part.

KERNEL hello says to execute the rest of the rule when a device with a name the same as this string the operator means comparison appears in /sys. The hello device appeared when we called misc_register with a structure containing the device name hello. See the result for yourself in /sys:

ls -d /sys/class/misc/hello/

/sys/class/misc/hello/

SYMLINK hello_world says to add the operator means append hello_world to the list of symbolic links that should be created when the device appears. In our case, we know this is the only symbolic link in the list, but other devices may have multiple udev rules that create multiple different symbolic links, so it is good practice add to the list instead of assigning to it.

MODE 0444 says to set the permissions of the original device file to the 0444 mode, which allows owner, group, and world all to read the file.

In general, it is very important to use the correct operator , , or , or unexpected things will happen.

Now that we understand what the rule does, let s install it in the /etc/udev directory. Udev rules files are arranged in much the same manner as the System V init scripts in /etc/init.d/. Udev executes every script the udev rules directory, /etc/udev/rules.d, in alphabetical/numerical order. Like System V init scripts, the files in the /etc/udev/rules.d directory are usually symbolic links to the real rules files, with the symbolic links named so that the rules will be executed in the correct order.

Copy the hello.rules file from the hello_dev directory into the /etc/udev/ directory and create a link to it that will be executed before any other rules file:

sudo cp hello.rules /etc/udev/

sudo ln -s. ./hello.rules /etc/udev/rules.d/010_hello.rules

Now, reload the hello world driver and look at the new /dev entries:

sudo rmmod hello_dev

ls -l /dev/hello

cr--r--r-- 1 root root 10, 61 2007-06-19 /dev/hello

lrwxrwxrwx 1 root root 5 2007-06-19 /dev/hello_world - hello

Now we have /dev/hello_world. Finally, check that you can read the Hello, world. devices as a normal user:

cat /dev/hello_world

For more details on writing udev rules, see Writing udev rules, by Daniel Drake.

Valerie Henson

is a Linux consultant specializing in file systems, and maintainer of the TCP/IP Drinking Game.

Return to LinuxDevCenter.com.

Comments on this article

1 to 7 of 7

udev rules

2010-06-18 :27 

Helmik

View

Too many

2009-04-08 :39 

jsnx

Error during insmod hello_proc

2007-11-30 :54 

georgeneil

Small problem in hello_proc.c.

2007-07-08 :15 

stderr.dk

2007-07-08 :27 

valhenson

Thanks

2007-07-07 :06 

bartvan deenen

2009-12-13 :31 

noway2

When insmod. /hello_world.ko, I receive Segmentation Fault

2007-07-06 :35 

mvd

2007-07-06 :19 

No mention of the freely available LDD3 online.

2007-07-06 :27 

EdLolington

1 to 7 of 7.

This article is intended to be a quickstart guide for building, loading, and debugging a simple device driver for a 64-bit Windows 7 system. Basic familiarity with device driver development and kernel debugging is assumed. For more of an introduction I recommend Toby Opferman s articles at CodeProject which are still mostly relevant despite being several years old. Also, as a security professional my interest and expertise with device drivers is limited to the use of kernel-mode code to modify operating system behavior, rather than interfacing with actual hardware devices.

Instructions are provided for using WinDbg for remote kernel debugging to a VirtualBox virtual machine. The procedure is similar for using VMware and other virtualization software. Note that running a 64-bit guest operating system in a virtual machine requires a CPU which supports hardware-assisted virtualization Intel-VT or AMD-V. I ended up having to upgrade the processor in one of my systems after finding out that it lacked Intel-VT support. It also may be necessary to go into the BIOS setup and ensure that the hardware virtualization is enabled.

Differences

There are several differences from earlier versions of Windows affecting device driver developers:

Driver Signing. 64-bit Windows systems will not allow drivers to be loaded unless they have a valid digital signature, which requires a signing key to be purchased from Microsoft. This is only made available to approved vendors and is not really an option for the hobbyist or researcher. For development and testing there is an option that can be selected from the boot menu to disable driver signing requirements. This cannot be made the default and must be manually selected at every boot. This is a nuisance for driver developers but is generally a good thing as it makes it harder for malware writers to install kernel rootkits.

Debugging Message Filtering. Beginning with Vista including 32-bit versions, all debugging messages printed by drivers are not necessarily displayed in the kernel debugger. Rather, the system must be configured to display messages matching certain criteria.

Kernel Patch Protection PatchGuard. Modifying certain kernel data structures is no longer allowed, again in an effort to crack down on kernel rootkits. Drivers that rely on certain techniques for hooking system calls may need to use an alternate approach. For details see Microsoft s Patching Policy for x64-Based Systems.

Sample Code

In addition to the minimal DriverEntry and DriverUnload routines, this sample driver also implements a Device I/O Control IOCTL interface for communicating from user to kernel mode.

//testdrv.c

include

define DEVICE_NAME L Device Testdrv

define DOS_DEVICE_NAME L DosDevices Testdrv

//numeric value 0x22a001

define IOCTL_TESTDRV CTL_CODE FILE_DEVICE_UNKNOWN, 0x800, METHOD_IN_DIRECT, FILE_WRITE_DATA

//macros for OACR

DRIVER_INITIALIZE DriverEntry;

DRIVER_UNLOAD TestdrvUnload;

__drv_dispatchType IRP_MJ_CREATE

__drv_dispatchType IRP_MJ_CLOSE

__drv_dispatchType IRP_MJ_DEVICE_CONTROL

DRIVER_DISPATCH TestdrvDispatch;

pragma alloc_text INIT, DriverEntry

pragma alloc_text PAGE, TestdrvUnload

pragma alloc_text PAGE, TestdrvDispatch

//log message with filter mask IHVDRIVER and severity level INFO

void DebugInfo char str

DbgPrintEx DPFLTR_IHVDRIVER_ID, DPFLTR_INFO_LEVEL, testdrv: s n, str ;

NTSTATUS TestdrvDispatch PDEVICE_OBJECT DeviceObject, PIRP Irp

PIO_STACK_LOCATION iostack;

NTSTATUS status STATUS_NOT_SUPPORTED;

PCHAR buf;

ULONG len;

PAGED_CODE ;

iostack IoGetCurrentIrpStackLocation Irp ;

switch iostack- MajorFunction

case IRP_MJ_CREATE:

case IRP_MJ_CLOSE:

status STATUS_SUCCESS;

break;

case IRP_MJ_DEVICE_CONTROL:

if iostack- Parameters.DeviceIoControl.IoControlCode IOCTL_TESTDRV

len iostack- Parameters.DeviceIoControl.InputBufferLength;

buf PCHAR Irp- AssociatedIrp.SystemBuffer;

//verify null-terminated and print string to debugger

if buf len-1 0

DebugInfo buf ;

else

status STATUS_INVALID_PARAMETER;

default:

status STATUS_INVALID_DEVICE_REQUEST;

Irp- IoStatus.Status status;

Irp- IoStatus.Information 0;

IoCompleteRequest Irp, IO_NO_INCREMENT ;

return status;

VOID TestdrvUnload PDRIVER_OBJECT DriverObject

UNICODE_STRING dosdev;

DebugInfo driver unloading ;

RtlInitUnicodeString dosdev, DOS_DEVICE_NAME ;

IoDeleteSymbolicLink dosdev ;

IoDeleteDevice DriverObject- DeviceObject ;

NTSTATUS DriverEntry PDRIVER_OBJECT DriverObject,

PUNICODE_STRING RegistryPath

UNICODE_STRING devname, dosname;

PDEVICE_OBJECT devobj;

NTSTATUS status;

DebugInfo driver initializing ;

DriverObject- MajorFunction IRP_MJ_CREATE TestdrvDispatch;

DriverObject- MajorFunction IRP_MJ_CLOSE TestdrvDispatch;

DriverObject- MajorFunction IRP_MJ_DEVICE_CONTROL TestdrvDispatch;

DriverObject- DriverUnload TestdrvUnload;

RtlInitUnicodeString devname, DEVICE_NAME ;

RtlInitUnicodeString dosname, DOS_DEVICE_NAME ;

status IoCreateDevice DriverObject, 0, devname, FILE_DEVICE_UNKNOWN,

FILE_DEVICE_SECURE_OPEN, FALSE, devobj ;

if . NT_SUCCESS status

DebugInfo error creating device ;

status IoCreateSymbolicLink dosname, devname ;

DebugInfo error creating symbolic link ;

return STATUS_SUCCESS;

Building

There are two additional files required to build a device driver: sources and makefile the file names do not have extensions. sources should contain the following lines:

TARGETNAME testdrv

TARGETTYPE DRIVER

SOURCES testdrv.c

makefile only needs to contain this one line:

.INCLUDE NTMAKEENV makefile.def

Build the driver as follows:

Of course the Windows Driver Kit WDK must be installed. The WDK also includes the Debugging Tools for Windows WinDbg.

From the Windows Driver Kit start menu group, launch the command-line build environment for the desired target platform and architecture, e.g. Windows 7 x64 Checked Build Environment.

Change to the directory containing  testdrv.c, sources, and makefile, and enter the command build.

If all goes well the driver file testdrv.sys will be built in a subdirectory named according the selected platform and architecture, e.g. objchk_win7_amd64.

By default the WDK will start a program called OACR Auto Code Review that will display an icon in the taskbar and automatically detect when a driver is being built and analyze the code for common programming errors. There are some declarations in the testdrv.c source code for preventing OACR warning messages. For details see Microsoft Auto Code Review.

Debugging Message Filtering

Note in the sample source code that the DbgPrintEx function is used with additional parameters, as opposed to the standard DbgPrint. The additional parameters are the Component ID and Severity Level. Only the component IDs defined in dpfilter.h that start with DPFLTR_IHV_ should be used by driver developers. IHV stands for Independent Hardware Vendor, i.e. any third-party non-Microsoft driver.

This example uses the generic DPFLTR_IHVDRIVER but other more specific IDs are available for video/audio/network/etc. drivers. The severity level can technically be any 32-bit number but standard constants exist for ERROR, WARNING, TRACE, and INFO levels corresponding to the numeric values 0, 1, 2, and 3, respectively. ERROR should only be used to indicate a truly serious error, but otherwise it is a matter of preference. This example logs everything at INFO level.

For each Component ID, the system maintains a kernel variable named Kd__Mask which is a 32-bit number representing a bit mask for the severity levels of debugging messages that should be displayed. For example, if bit 3 in this variable is set to 1, then INFO level messages will come through. The full list of these variables can be seen in WinDbg with this command:

kd x nt.Kd_ _Mask

fffff800 02e03198 nt.Kd_LDR_Mask

fffff800 02e031ec nt.Kd_WDI_Mask

fffff800 02e0304c nt.Kd_SETUP_Mask

fffff800 02e03150 nt.Kd_DMIO_Mask

The current value of the IHVDRIVER mask can be displayed as follows it should be zero by default :

kd dd nt.Kd_IHVDRIVER_Mask L1

fffff800 02e03178  00000000

This value can be modified directly in the debugger and will take effect immediately and persist until the system is rebooted. For example, to set the lowest 4 bits and therefore enable debugging messages for levels ERROR thru INFO, set the value to hex 0x0000000f:

kd ed nt.Kd_IHVDRIVER_Mask f

To make this change permanent, create a registry value IHVDRIVER must be uppercase in HKEY_LOCAL_MACHINE SYSTEM CurrentControlSet Control Session Manager Debug Print Filter. The value should be a DWORD corresponding to the desired mask, e.g. 0x0000000f. The change will not take effect until the next reboot.

Refer to Reading and Filtering Debugging Messages for more information.

Note that debug messages will not display in the kernel debugger during local kernel debugging no matter what you do. The only options for seeing messages in local kernel debugging are to execute the. dbgprint command periodically or use the DebugView tool from SysInternals.

Test Program

The following user-mode application can be used to invoke the DeviceIoControl IOCTL interface and pass data into the driver.

//ioctltest.c

define UNICODE

define _UNICODE

define DEVICE_NAME L. Testdrv

define IOCTL_CODE 0x22a001

const char message Greetings from user mode ;

void debug char text

LPTSTR msgbuf;

//get error message string for last error

FormatMessage FORMAT_MESSAGE_ALLOCATE_BUFFER FORMAT_MESSAGE_FROM_SYSTEM,

NULL, GetLastError, LANG_USER_DEFAULT,

LPTSTR msgbuf, 0, NULL ;

printf s: S n, text, msgbuf ;

int wmain int argc, wchar_t argv

HANDLE hDevice;

char outBuf 16 ;

DWORD returned;

hDevice CreateFile DEVICE_NAME, GENERIC_READ GENERIC_WRITE,

FILE_SHARE_READ FILE_SHARE_WRITE, NULL, OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL, NULL ;

if hDevice INVALID_HANDLE_VALUE

debug error opening device ;

if 0 DeviceIoControl hDevice, IOCTL_CODE, message, sizeof message,

outBuf, sizeof outBuf, returned, NULL

debug ioctl error ;

CloseHandle hDevice ;

return 0;

Build with:

cl ioctltest.c

Virtual Machine Configuration

This section assumes that Windows 7 64-bit has been installed in a VirtualBox virtual machine. The following steps will prepare the virtual system for driver loading and kernel debugging. First, with the virtual machine powered off, configure the virtual serial port to point to a named pipe for the kernel debugger to connect to:

Next, enable kernel debugging in the Boot Configuration Database using the built-in bcdedit command line tool, and also configure the boot menu to always display at boot. There is also a nice free GUI tool EasyBCD available for modifying these settings. Start a command prompt with administrator privileges and run the following commands:

bcdedit /set current debug yes

bcdedit /set bootmgr displaybootmenu yes

Run bcdedit with no parameters to confirm the current configuration.

Finally, reboot the virtual machine. When the boot menu is displayed, press F8 and then select Disable Driver Signature Enforcement from the menu.

Attaching Debugger

This section assumes that debugging symbols have already been configured in WinDbg on the host system. For more information see Debugging Tools and Symbols on MSDN. Launch WinDbg on the host system, select File- Kernel Debug and specify the following settings:

Press Ctrl-Break or select Debug- Break from the menu and wait patiently. Kernel debugging over the virtual serial port is SLOW – it may take several seconds before any feedback is received at all from the Break request, and may take 30 seconds or more for the prompt to display:

You are seeing this message because you pressed either

CTRL C if you run kd.exe or,

CTRL BREAK if you run WinDBG,

on your debugger machine s keyboard.

THIS IS NOT A BUG OR A SYSTEM CRASH

If you did not intend to break into the debugger, press the g key, then

press the Enter key now. This message might immediately reappear. If it

does, press g and Enter again.

nt.DbgBreakPointWithStatus:

fffff800 026c87a0 cc int 3

This confirms that kernel debugging is working. If nothing happens, double-check the virtual serial port settings, debug boot configuration, and WinDbg connection settings.

Configure the debugging message filter mask as previously discussed:

Finally, enter g at the prompt to resume execution of the virtual machine.

Testing driver

Copy the testdrv.sys driver to the virtual machine using the drag-and-drop or shared folder support. Also copy over the test program ioctltest.exe.

The driver can be installed using the built-in Windows command-line tool sc.exe. This can also be done using the GUI tool OSRLoader available from OSR Online free registration required. To use sc.exe, start a command prompt with administrative privileges and run the following commands replacing c: testdrv.sys with the path where testdrv.sys is located on the virtual machine :

sc create testdrv binPath c: testdrv.sys type kernel

Note that the spaces after the equals signs are required. A pop-up warning may be received about the driver not having a valid digital signature, which can be ignored. The sc create command only needs to be run once, and from then on the driver can be just be started and stopped as desired. All the sc create command does is create the necessary registry entries under HKLM System CurrentControlSet Services testdrv. The registry entries can be removed if needed with sc delete testdrv.

To load the driver:

sc start testdrv

If the driver starts successfully, the following message should be seen in the kernel debugger if the message is not seen, double-check the filter mask settings :

testdrv: driver initializing

Next, run ioctltest.exe and the following output should be seen in the debugger:

testdrv: Greetings from user mode

The driver can be unloaded with sc stop and the following message should be seen:

testdrv: driver unloading.