A "seminal work" for writing solaris device drivers, is called, oddly enough, "Writing Device Drivers". It has been around for a long time, but is still highly valid. Now available on the oracle site, as http://download.oracle.com/docs/cd/E18752_01/html/816-4854/index.html (2010 version, Solaris 10) There are older versions referenced from the developers.sun.com page, above.
There is also the more basic Device Driver Tutorial. Oddly, this seems to have a different docID as the "device driver tutorial" higher up, but looks to have the same table of contents. Very different formatting though, so pick which one you prefer.
And yet another driver oriented primer, Solaris (...) Driver writer orientation.
Since there are many different types of device driver, and a few different ways of thinking about them, you may well benefit from reading all of the above, rather than just picking one. Some may cover particular areas better than others.
A quickie "right mindset" tip to start off: If you run into a bug (either in software or hardware) and things blow up because your error checking is inadequate: do NOT fix the bug yet! You're getting a free "test harness" here, so use it to improve your error checking routines. Once they are bulletproof, THEN go back and fix/work around the error that triggered the problem. This is driver writing, not WIMP programming. Your code *must* continue to work, or at minimum not crash, when EVERYTHING ELSE dies!!!
If you think you're being paranoid about error checking -- you're not being paranoid enough.
Normally, if things completely blow up, you'll have to boot off cdrom. But for a quicker resolution, you can prepare a backup copy of the usual driver environment that you can use. (Assuming your crash hasnt corrupted your filesystems). To make the emergency area, do
# cd /platform/`uname -m` # cp -r kernel safetyYou now have a directory tree starting at
At boot-time, the OS automatically goes into the
part. So when you need to , you can choose to do the appropriate alternate
# Intel b safety/unix # sparc boot safety/unix
This assumes that you install your driver into
/platform/`uname -m`/kernel/drv. If you put it in
/kernel/drv, or /usr/kernel/drv, you're up the creek without a paddle.
The Makefile for my PCIbase driver skeleton does
this by default.
If you're not feeling THAT paranoid, then at minimum, do
# cd / ; ufsdump 0f /var/root.dump etc dev devicesThis way, rather than restore/reinstall your ENTIRE SYSTEM if something goes wrong, you can probably boot off cdrom, mount your root filesystem, and just use ufsrestore to get back those most critical directories.
== If you've forgotten to do this,and your system blows up, and goes into a loop... Rather than do a reinstall, you might try removing that hardware device, if it is indeed removable. That way, maybe your driver wont attach, and thus wont corrupt the system.
But be warned, your system might be corrupted so much you may have to reinstall anyway.
You can use the "printregs" script here, to look at
the registers for your hardware. printregs is used in relation to a
specific device, so you first need to find the pci ID for the device you
PCI IDs look like [vendornumber],[cardnumber]
(OoOOLD school solaris x86) These are present on the old DCA (boot floppy) if you poke around on it. Otherwise, on a running system, you can look at the output of
prtconf -pv== from the command line.
"printregs" actually uses that same output, but automatically splits out the register information for you into slightly more readable form.
Intel's vendor ID is 8086. So the pci identifier for a particular revision of Intel Pro100 Fast Ethernet card happens to be "8086,c".
# printregs 8086,c
on a system with that card install, gives the following output:
Looking for device 'pci8086,c' We will print the possible register mappings, that can be used with ddi_regs_map_setup, and also any physical mappings. The physical mappings are only to help you FIGURE OUT what they are !!!! DO NOT TRY TO ACCESS THAT ADDRESS DIRECTLY !!!!! ------------------------- register set 1 has tag 02006810 and length 00001000 register set 2 has tag 01006814 and length 00000040 register set 3 has tag 02006818 and length 00100000 register set 4 has tag 02006830 and length 00100000 tag 82006810 is mapped at addr ed100000 tag 81006814 is mapped at addr 0000c800 tag 82006818 is mapped at addr ed000000 tag 82006830 is mapped at addr ea000000If you ignore the leading '8' for the second set of tags, you can match them up to realize that
register set 1 is mapped at physaddr ed100000 for length 0x1000 register set 2 is mapped at physaddr 0000c800 for length 0x0040 register set 3 is mapped at physaddr ed000000 for length 0x00100000 register set 4 is mapped at physaddr ea000000 for length 0x00100000But as the warnings in the output say, do NOT try to directly do in your driver code,
ddi_regs_map_setup(dip, 2, ®2_ptr, 0, 0x40, &attrptr, &handleptr);to map register set 2 into your own kernel memory. You will then have reg2_ptr pointing to the start of it.
But even then, you should not normally directly assign values to the pointer!! (There are some occasions where you can get away with this. But at least for initial testing, ...) You should use one of ddi_put8/ddi_put16/ddi_put32/ddi_put64 to change the memory contents of a mapped register
"man ddi_regs_map_setup" for more details on things like attrptr
************************************************************** * "full" memory checking == 0xf ; "light" checking == 0x100 * CF: /usr/include/sys/kmem_impl.h set kmem_flags=0x1f * To auto-generate a coredump on a locked system. * This is the "deadman kernel" enabler. Allegedly. * It doesnt work under x86 very well, if at all. set snooping=1 ***************************************************************When you do have a kernel panic, you can find out the routine in which it crashed by doing
# adb -k /var/crash/`uname -n`/unix.0 /var/crash/`uname -n`/vmcore.0 $CYes, type dollarsign, C. You wont have a prompt, but just type it anyway. (You can also use mdb the same way)
If you want lots and LOTS of info about the kernelstate, at the time of a crash, you can use
$<threadlistto look at every single kernel thread.
$<panicbufwill tell you messages from when the system paniced
::findleaksin mdb, if you had kmem_flags = 0xf set, will help you with memory leaks. You might also be interested in
*panicstr/sshould print the "panic string" from a kernel panic, if available.
If you have started the system with "b kadb", there are some other options. [To drop down to debugger in x86, use control-alt-D]
18::more Turns on screen paging (like running "more") :c Continue running UNIX lbolt/X Shows "system tick" counter (system "clock" tick,64bit needs /J) freemem/X Shows number of free memory pages (64bit needs /J) $<cpus Shows thread id on each cpu $<kmastat Shows free memory (::kmastat in mdb) [ Like "next" in gdb ] Like "step" in gdb module#f_name:b Set a "breakpoint" at function f_name(), kernel module 'module' === General Memory examination, mdb or adb or kadb == addrname/D Print 4byte int, decimal format addrname/E Print 8byte unsigned int, decimal format addrname/X Print 4byte unsigned int, hex format addrname/J Print 8byte unsigned int, hex formatNote that "man mdb" gives other modifiers to print memory, under the "Formatting dcmds" section.
Unfortunately, the $C trick gives you a location in a format like:
functionname:+0x104as the place where it crashed, and you're wondering what the heck line that is. But you CAN FIND OUT, if you grab the GNU binutils, and just install "objdump" from it.
Assuming you compiled your driver with -g,
objdump -d -S yourdriverwill give you some assembly output, along with your C code in comments, AND hex offsets!
If there is a variable you'd like to adjust on the fly, on a running system, you can use either adb or mdb, to change it. Example:
adb -kw (or mdb -kw) > my_var?W1sets the contacts of the 4 bytes at symbolic address "my_var" to be '1'.
However, sometimes, you have a non-unique variable name, and thats where mdb
comes in handy.
First, use it to find the address of all the symbols in a specific driver. Then you can adjust the address directly. Example:
mdb -kw > ::nm wacom 0x00000000|0x00000000|NOTY |LOCL |0x0 |UNDEF | 0xfea08754|0x00000073|FUNC |LOCL |0x0 |1 |wacom_detach [....] 0xfed0a50c|0x00000004|OBJT |LOCL |0x0 |4 |debug_level [....]
Oh, look! There's my debug_level variable, at address 0xfed0a50c. It's a 4byte value, with current value 0x0. But I want to tweak up the debug_level, without having to reload my driver. No problem..
and I've just changed the 4byte value to be 1. Let the debug messages roll!
If you need to write an 8byte value, use Z instead of W