|
Please post any updates, additions, or questions on the developer forums at
http://forum.sun.com
Note that some information may apply to earlier releases.
1. Reporting bugs and general information
1.1
How do I report bugs on this web site?
1.2
What do I do if I find a bug/omission in the DDI/DKI or the documentation?
2. DDI / DKI
2.1
What is the Solaris 2.x DDI/DKI?
2.2
What does it mean if a driver is not Solaris DDI-compliant?
2.3
How can I tell if a driver is not DDI compliant?
3. Structures, Variables, Files, and Operating System Information
3.1
What are the definitions of and the relationships between instances, device numbers, minor nodes, major numbers, and minor numbers?
3.2
Should I acquire a mutex around any manipulations of a state structure element?
3.3
Can & Should I only have one mutex for each instance?
3.4
What structures can my driver access?
3.5
Can I access the user or proc structure?
3.6
I need to prevent user memory from being paged out, so my driver can use DMA to access it. Can I call as_fault()?
3.7
Why is there no man page for printf() or panic()?
3.8
What ddi_iblock_cookie_t should I pass mutex_init(9F) for a mutex used with a timeout(9F) routine?
3.9
I`m using the ddi_soft_state routines to allocate per-instance state structures. Using the global pointer they require, in this case "statep", how can I access unit n`s state structure in kadb?
3.10
Periodically, in situations where only one "xx " card is in a machine, /dev/xx1 is created instead of /dev/xx0. boot -r does not help. At one point there were two "xx " cards in this machine, and one was removed. Why isn`t the remaining card now /dev/xx0?
3.11
What is a "watchdog reset "?
3.12
Is there a naming convention for I/O controls?
3.13
What are the different address spaces that a PCI driver can access ?
3.14
I am trying to access my PCI device IO registers through the driver using DDI calls. The driver is mapping the device IO space. I read all zeros, but I was able to read the configuration space and read the contents. Any clues why IO map is not working ?
3.15
I can reliably crash the system if I write zero to the command register on my PCI adapter. The same code doesn`t crash the x86 machine running Solaris 2.5 . The panic message is as follows. panic[cpu0]/thread=0x503f6c60: CPU0 Privileged Timeout Error:
3.16
Is it possible to map-in and use both the Base Address Register (BAR) and the expansion ROM from the PCI configuration space?
3.17
When I tried to load my driver using add_drv(1M) it fails with the following message "driver loaded successfully but failed to attach ". Looking through debug messages, I don`t see my attach routine in the driver called at all!
3.18
I have three Base Address Registers (BAR) allocated on my PCI device. How do I know what index to use to map into each of these registers using ddi_regs_map_setup(9f) in my device driver?
3.19
How can a simple character device driver be developed for the Solaris OS? How can a new device driver for the Solaris OS be installed and uninstalled? UPDATED
3.20
We are having problems loading our driver in the Solaris 2.9 OS, SPARC Platform Edition. UPDATED
3.21
I wrote a new driver for the SCSI HBA. I have not defined a bus_ops function, nor have I referred to the driver after loading, but I cannot unload the driver. UPDATED
3.22
Is there any way to keep scheduled events generated by timeout(9F) going during a panic dump? UPDATED
3.23
If not, is there a way for our HBA driver to find out if a panic dump has been initiated? UPDATED
3.24
How can I access the physical device once the new driver is loaded? UPDATED
3.25
Is it possible for two different drivers to create variables that they can share? In other words, can we define a global variable, something similar to kernel variables maxphys or scsi_options? UPDATED
3.26
What are the different address spaces a PCI driver can access? UPDATED
3.27
A developer puts a new developed device driver under /usr/kernel, and it causes a system crash while the system boots. How can one avoid loading this buggy driver in the next boot? UPDATED
3.28
Under certain conditions on the Solaris OS, a device driver cannot be unloaded. What are those conditions? UPDATED
3.29
In drivers we get the instance number in attach/detach routines, and we get the device (*dev) in open routine. But there is no DDI/DKI call to get the instance number for the given device number in the open routine. UPDATED
3.30
I have just written a new driver and am ready to test it. How do you create the new device links so the driver can be loaded and tested? UPDATED
3.31
How can I copy the data from the kernel memory space to the user memory space? UPDATED
3.32
I want to manage multiple instances in my Solaris device driver.
My device needs four-channel communication. I call ddi_create_minor_node() four times from the attach() routine. But inside the ioctl() routine, I cannot get the minor number. How can I achieve this? UPDATED
4. About Drivers
4.1
What is a nexus driver and how do I write one?
4.2
Can I write a driver for a SCSI Host Bus Adapter? That`s a nexus driver, isn`t it?
4.3
Is there a way to send signals to user threads from a device driver?
4.4
What is a layered driver?
4.5
What does self-identifying means? UPDATED
4.6
What properties for devices that are not self-identifying must be described? UPDATED
4.7
What are the contents of the "xx.conf" file? UPDATED
4.8
Can you name some self-identifying devices? UPDATED
4.9
My driver panics the system in ddi_create_minor_node(9F). Is this a known bug in the kernel?
4.10
Do self-identifying devices require a configuration file?
4.11
I want to have two drivers in one module. It doesn`t seem to work by putting multiple entries in the modlinkage structure What do I need to do?.
4.12
How does a driver retrieve the hostid?
4.13
How do I exercise my driver`s aread(9E) and awrite(9E) routines?
4.14
When does the kernel unload loadable modules?
4.15
How do I make my driver support PCI Hotplug?
4.16
How do I make my driver support SCSI Hotplug?
4.17
Is D_HOTPLUG necessary?
4.18
How do I set breakpoints to stop at the beginning of my driver`s probe?
4.19 Solaris Device Driver Packaging and Installation
4.19.1. Should I get the "Solaris Ready" brand?
4.19.2. Where can I find general information about packaging for Solaris software and device drivers in particular?
4.19.3. Where should drivers be installed on the system?
4.19.4. Do drivers for Solaris need to be uniquely named?
4.19.5. How do I add a new device driver to the system?
4.19.6. Why isn`t there a common location for drivers to be installed on the system?
4.19.7. How should I name the software package that contains my driver?
4.19.8. If I am allowed to put my driver package on the Solaris operating environment distribution media, do I have to do anything different in my driver package?
5. SunOS 4.x to SunOS 5.x
5.1
I used to pass flags to my driver with the `flags` keyword in SunOS 4.x. How do I do that in SunOS 5.x?
5.2
Is there a SunOS 5.x replacement for the SunOS 4.x dev_info command?
5.3
My driver needs to keep track of all the processes using it. This can be done in SunOS 4.x by looking at proc structure information in open(9E), but I can`t "forget" about processes that are no longer using it since close(9E) is only called when the last reference goes away. How can I do this?
6. FCode
6.1
General FCode FAQs
6.2
PCI FCode FAQs
7. Compiler and Development Environment Questions
7.1
What compiler can I use to compile device drivers? UPDATED!
7.2
What compiler do I use to build 64-bit applications? UPDATED!
7.3
Which option should I provide the compiler to generate 64-bit code? What is the difference between
-xarch=v9, -xarch=v9a, and -xarch=v9b?
7.4
Why do I get "/kernel/drv/sparcv9/mumble: undefined symbol" when I try to load my 64-bit driver?
7.5
Do I need to recompile my Solaris 7 driver to be able to run on Solaris 8?
7.6
The Forte Developer compiler specifies the -xchip=ultra3 / xtarget-ultra3 options. Do I need to recompile for the UltraSPARCIII processor?
7.7
Can I use the Forte C compiler -fast option to optimize the driver performance?
7.8
What compiler options should I typically use?
7.9
Can I write a device driver in C++?
7.10
When using the 3.x SunPro C compiler on a SCSI driver, why are all the SCSI symbols undefined?
8. System Messages
8.1
What does "panic: Lock Held and only one CPU" mean?
8.2
What does it mean when prtconf(1M) returns "(no driver)"?
8.3
What does this message mean: "panic: assertion failed: ibc<(void*) KERNELBASE, file: ../../common/mutex.c, line 427"?
9. Solaris OS on x86 Platforms
9.1
How do I boot with kadb on the Solaris OS for x86 Platforms?
9.2
How can I make a boot floppy that boots right into the Solaris OS on x86 Platforms?
9.3
How does one mount a dos IDE HD on the Solaris OS on x86 Platforms?
9.4
How do I allocate and access physical memory for the Solaris OS on x86 Platforms?
9.4.1 Solaris OS on x86 Platforms: How do I allocate physically contiguous memory?
9.4.2 Solaris OS on x86 Platforms: Why does ddi_iopb_alloc() fail with larger allocations?
9.4.3 Solaris OS on x86 Platforms: How do I convert a kernel virtual address to a physical address?
9.5
How can the kernel recognize a non-self-identifying device?
9.6
How do I make an x86 device bootable?
10. Hardware related questions
11. PCI Overview
Questions and Answers
1. Reporting bugs and general information
1.1 How do I report bugs on this web site?
Report any problems with the web site by using the feedback link present at the bottom of all pages on this site.
1.2 What do I do if I find a bug/omission in the DDI/DKI or the documentation?
To report a bug use the feedback link present at the bottom of all pages on this site.
Back to Top
2.1 What is the Solaris 2.x DDI/DKI?
In System V Release 4 (SVR4), the interface between device drivers and the
rest of the UNIX kernel has been standardized and documented in section
9 of the Solaris man pages. The reference manual documents driver entry
points, driver callable functions and kernel data structures used by device
drivers. These interfaces, known collectively as the Device Driver Interface/Driver-Kernel Interface (DDI/DKI) consist of several sections:
-
DDI/DKI Architecture Independent: These interfaces are supported on all implementations of SVR 4 and will be supported in future releases of System V.
-
DKI-only: These interfaces are part of SVR4 and may not be supported in future releases of System V. There are only two interfaces in this class,
segmap(9E) and hat_getkpfnum(9F).
-
Solaris DDI: These interfaces specific to Solaris and will be supported in future releases of Solaris 2.x.
-
Solaris DDI for SPARC based systems: These interfaces are specific to the SPARC
processor and may not be available on other processors supported by Solaris.
-
Solaris DDI for x86 based systems: These interfaces are specific to the x86 processor and may not be available on other processors supported by Solaris.
For more information about the DDI/DKI, see Intro(9) in the Solaris man pages.
The Solaris 2.x DDI/DKI, like its SVR4 counterpart, is intended to standardize
and document all interfaces between device drivers and the SunOS kernel.
In addition, the Solaris 2.x DDI/DKI is designed to allow source compatibility
for drivers on any SunOS 5.x-based machine, regardless of the machine architecture.
It is also intended to provide binary compatibility for drivers running
on the same processor that supports the Solaris 2.x DDI/DKI, regardless
of the specific machine architecture. Drivers that only use kernel facilities
that are part of the Solaris 2.x DDI/DKI are known as "Solaris 2.x DDI/DKI-compliant
device drivers" or "Solaris DDI-compliant" device drivers.
Note:
Suns implementation of the Solaris 2.x DDI/DKI was designed to provide
binary compatibility for third party device drivers across currently supported
hardware platforms across minor releases of the operating system. Currently
there is no validation process for supported hardware platforms.
However, unforeseen technical issues may force changes to the binary interface of
the Solaris 2.x DDI/DKI. Sun cannot therefore promise or in any way assure
that Solaris 2.x DDI/DKI-compliant device drivers will continue to operate
correctly on future releases of Solaris Operating Environment.
Furthermore, future releases may contain additions to the Solaris 2.x DDI/DKI to support future platforms.
At that time, device drivers may require these additions
to operate across the new set of supported platforms.
2.2 What does it mean if a driver is not Solaris DDI-compliant?
It simply means that your driver may not work (or compile) in later releases.
Functions and structures not documented in the DDI/DKI are subject to change/removal at any time.
2.3 How can I tell if a driver is not DDI compliant?
You can tell if your driver is compliant by running the ddict tool or by comparing
what the driver uses with section 9 of the Solaris man pages. If the driver is using an interface
not in the section 9 man pages, it is non-compliant.
Back to Top
3. Structures, Variables, Files, and Operating System Information
3.1 What are the definitions of and the relationships between instances, device numbers, minor nodes,
major numbers, and minor numbers?
"Instance numbers" are part of the "shorthand" name for "dev_info" nodes owned by a particular driver.
These nodes are usually associated one-to-one with a particular hardware instance: the two 85C30 chips in a SPARCstation
each have a dev_info node called instance zero or "zs" and instance one. Instance numbers are assigned (and owned) by the
system, and returned to the driver by ddi_get_instance(9F).
Instance numbers and "shorthand names" are not normally visible outside
the kernel.
host% ls -l /devices/pseudo/mm:zero crw-rw-rw- 1 root
13, 12 Oct 11 02:20 /devices/pseudo/mm:zero
"Minor nodes" consist of all the information that is held about a user-accessible device that is the minor
number (exported by the special file), part of the name of the special file (the 'zero' in "mm:zero", minor number
12 of the "mm" driver), and whether it is block or character. See ddi_create_minor_node(9F) for
how to build one.
"Device numbers" are contained in an opaque type "dev_t". They consist of a major
number and a minor number.
The "major number" is an internal magic number used by the system to bind special files (such as
/devices/pseudo/mm:zero) to device drivers.
In the example above, the "major number" is 13. There is usually no reason for the driver to care what its major
number is as they are assigned by the system.
The "minor number" is a component of the device number. It's meaning is entirely up to the device driver,
and it is associated with a special file by calling ddi_create_minor_node(9F).
In the above example, the minor number is 12. Minor names are associated
with minor numbers, as the part following the colon in the name of the
special file ('zero' in this case).
3.2 Should I acquire a mutex around any manipulations of a state structure element?
It depends. If it is an element you only initialize in attach then read everywhere
else, then there is no need. If it is an element of the data structure
that's shared-writable, then yes.
In general though, you should probably start by protecting everything, and
relax the protection as you get more confident and a better understanding
of your driver and the multithreaded world.
3.3 Can and should I only have one mutex for each instance?
You can, and it often makes sense, but you do not have to. However if you wish
to support DR on the Enterprise platforms you will need to have a mutex
for each instance.
3.4 What structures can my driver access?
Only those defined in section 9S of the manual pages. Note that these structures
may change size, so you should never declare one. For example, if you need
a buf(9S) structure, declare a pointer and dynamically allocate one with
getrbuf(9F).
3.5 Can I access the user or proc structure?
No.
3.6 I need to prevent user memory from being paged out, so my driver can do
DMA to it. Can I call as_fault()?
No, as_fault() is not part of the DDI/DKI. What it does and how it does it may change
more or less arbitrarily from minor release to minor release. The only DDI-compliant way to lock down
memory is with physio(9F)
3.7 Why is there no man page for printf() or panic() ?
Drivers should not normally print messages. However, though printf()
exists in the kernel, if messages need to be printed to the console (and/or
the kernel message buffer) drivers should use cmn_err(9F).
There is no equivalent to the SunOS 4.x uprintf() routine.
cmn_err() also allows the driver to panic the system. It is unlikely that drivers will need to do so, however.
3.8 What ddi_iblock_cookie_t should I pass mutex_init(9F)
for a mutex used with a timeout(9F)?
NULL is fine for mutexes that are not used in interrupt routines.
3.9 I'm using the ddi_soft_state routines to allocate per-instance state structures.
Using the global pointer they require, in this case 'statep', how can I access unit n's state
structure in kadb?
Use:
*(*(*statep)+4n)/mX
For, instance #3 (starting at #0), longs #0 through #3 (starting at #0):
in this case, '4n' is 4 times 3 = 0xc in hex.
So to look at the first four longs of instance #3's soft state structure, use:
*(*(*statep)+c)/4X
To just get the address, use:
*(*(*statep)+c)=X
For SunOS 5.0 and 5.1, you have to add x10 to the base address, like this:
*(*(*statep+10)+4n)/mX
Back to Top
3.10 Periodically, in situations where only one "xx" card is in a machine, /dev/xx1 is
created instead of /dev/xx0. boot -r does not help. At one point there were two "xx" cards in this
machine, and one was removed. Why isn't the remaining card now /dev/xx0?
When you move a card from one slot to another, or remove a card, Solaris remembers
the old card. This is what makes instance numbers and dev_t's stick and solves
the sliding controller number problems that have always existed.
The device Solaris probed first (ever) should be instance #0. Look in /etc/path_to_inst
and you should see which device path name is instance #0.
3.11 What is a 'watchdog reset'?
A watchdog reset occurs when a synchronous trap condition occurs with traps disabled. Since traps are
disabled, the processor can't take a trap but it can't
continue executing either. The chip then enters error mode and logically
raises an error_mode signal. This signal is logically wired back to the
watchdog_reset_in pin.
One common way that a new driver can watchdog the system is by using too much
stack space (commonly, too many large local variables).
Since the kernel stack doesn't grow, but has red-zones, and the first access
into the red-zone could be with traps off from the trap handlers, that
could cause a watchdog.
Note:
Some chips don't have external error_mode signal, nor external watchdog_reset_in
pins and this process all occurs inside the chip.
3.12 Is there a naming convention for I/O controls?
The convention is to pick a letter (say 'M'), and left-shift its ASCII value
8 bits. Then, number your I/O controls in the lower eight bits.
For example:
#define MYIOC ('M' << 8)
#define MYIOCTL_1 (MYIOC | 1)
#define MYIOCTL_2 (MYIOC | 2)
There are two issues to be aware of:
- source (namespace pollution):
Choose a descriptive prefix that does not conflict with any system macros. For example, TIOC is a
particularly dangerous one to use, as the identifier is already defined in <
sys/termios.h,
and there are already a number of TIO* ioctl values. TIO* is sort of "reserved"
for terminal stuff via historical precedent.
-
Binary:
In principle, USL is supposed to keep a registry of ioctl numbers so that
a driver can easily flag when it is being given an inappropriate ioctl.
In practice, this is very broken as there are not enough letters in the
alphabet to deal with all the possible values.
Note:
An ioctl command number can be any 32-bit value - the ioctl command numbers
do not have to be crunched into 16 bits. Unfortunately an ioctl number
registry doesn't really work unless everyone uses it.
3.13 What are the different address spaces that a PCI driver can access?
The PCI address domain consists of three distinct address spaces, as we know,
the memory space, the IO space and the configuration space.
A driver can access all three address spaces, by setting up appropriate
mappings and using the memory, I/O or configuration space access routines
provided in the DDI/DKI framework. As a common example, during
the driver attach routine the driver could read the configuration space
by setting up a mapping via the DDI call pci_config_setup(9f),
read the configuration registers using the pci_config_get* routines
and write to the configuration registers using pci_config_put *
routines.
3.14 I am trying to access my PCI device IO registers through the driver using DDI calls.
The driver is mapping the device IO space. I read all zeros, but I was able to read the configuration space
and read the contents. Any clues why IO map is not working?
First, check that IO_ENABLE bit is set in the device command register. This will
permit access to the device IO space. Although the PCI nexus driver sets these modes by default, the device
driver must agree to these settings and if not, it should set proper operation modes.
If the command register shows proper settings, you could be doing something
wrong in the mapping operation. For example, perhaps a wrong argument like
rnumber is being passed to the mapping routine ddi_regs_map_setup(9f). Check all your arguments.
In addition, it is good programming practice not to map the device memory while the PCI configuration space is mapped
for modifications. For example, If you called pci_config_setup(9f) to map and modify the configuration space,
make sure pci_teardown(9f) has been called before mapping the device memory.
3.15 I can reliably crash the system if I write zero to the command register on my PCI adapter. The
same code doesn't crash the x86 machine running Solaris 2.5 . The panic message is as follows:
panic[cpu0]/thread=0x503f6c60: CPU0 Privileged Timeout Error:
On any system, writing a zero to the command register disables access to I/O
and memory space, and the device fails to respond to accesses.
How a failure is treated is dependent on the platform. On current SPARC platforms, it causes a trap.
On current x86 platforms, writes are dropped and reads return garbage.
The firmware and the framework do write the command register with the desired
value, although a driver is free to map and write it as well. (This is
not advisable unless the driver disagrees and wants to write a different
value.) At no point should the driver disable access to the address space
in which it is operating.
3.16 Is it possible to map-in and use both the Base Address Register (BAR) as well as the expansion
ROM from the PCI configuration space?
According to section 6.2.5.2 of the PCI 2.1 specifications, there are
interactions between the command register MEMORY ENABLE bit and the expansion
ROM register EXPANSION ROM ENABLE bit. First, the device will not respond
to any memory accesses, whether to registers or to its ROM, unless
the MEMORY ENABLE bit is set. Second, the device is allowed to share an
address decoder between register accesses and expansion ROM accesses. These
result in the following interactions:
|
MEMORY ENABLE
|
ROM ENABLE
|
EFFECT
|
|
0
|
0
|
All accesses disabled.
|
|
0
|
1
|
All accesses disabled.
|
|
1
|
0
|
Register accesses enabled.
|
|
1
|
1
|
ROM accesses enabled. [*]
|
|
[*] The status of register accesses is device-specific when both the MEMORY ENABLE
and ROM ENABLE bits are set.
On a device that shares an address decoder, register accesses are not allowed.
On a device with independent decoders for register and ROM accesses, register
accesses are allowed.
3.17 When I tried to load my driver using add_drv(1M) it fails with
the following message "driver loaded successfully but failed to attach". In look through the debug
messages, I don't see my attach routine in the driver called at all!
The failure could be occurring in probe(9e) routine. Typically
a PCI device driver will not have a probe routine, in which case
this explanation is applicable. During the driver load operation, the operating
system has to know if the device is present in the system. When add_drv(1M) is called, it checks the "name" property of each device in the device tree, that matches the specified driver name. If there is no match, then the "compatible" property is checked. If that match also fails, the driver loading mechanism fails, because the system could not find a device to
attach the driver to. So if the "name" or "compatible" property does not match your driver name, the system has no way of knowing which device in
the system the driver is used controls. In order to specify the device
in such cases, you must use add_drv(1M) with the -i (alias)
option, which would specifics the device ID and vendor ID combination string
"pciVVVV,DDDD" for your device. For example, If your driver name is "xxx"
for a PCI device with a Vendor ID 1234 and Device ID 56, then you would use add_drv -i
'"pci1234,56"' xxx. This will result in driver xxx being loaded on your device.
Please note that if the Subsystem ID field in the configuration registers for this device is non-zero,
VVVV,DDDD should be the Subsystem Vendor ID and the Subsystem ID respectively. Please refer to the PCI
Bus bindings (IEEE-1275) document for further information.
3.18 I have three Base Address Registers (BAR) allocated on my PCI device. How do I know what index
to use, to map into each of these registers using ddi_regs_map_setup(9f) in my device driver?
Determine which register index in "reg" property (IEEE 1275) corresponds to your BAR. Use this index value as 'i
number' to ddi_regs_map_setup(9f). You must be familiar with the "reg" property format
in order to do this. It is explained in the IEEEE-1275 PCI bus bindings document. The rules defined by this document
for a non-FCode device state that rnumber=0 refers to the device's configuration space, rnumber=1
refers to the first "active" BAR, if any, and rnumber=2 refers to the second "active" BAR, if any. The
expansion ROM and any "hard decode" entries come after the active BARS; see the binding document for details. If the
device has FCode, consult the device manual for correlation between the "reg" entries and device registers. You would
also need the size of the register for ddi_regs_map_setup(9f) which can be retrieved using
ddi_dev_regsize(9f)
3.19 How can a simple character device driver be developed for the Solaris OS? How can a new device driver for the Solaris OS be installed and uninstalled?
Many sample drivers are available in the Solaris Driver Development area of the Sun Developer site: see code samples.
However, these are full-blown drivers and cannot be easily
stripped down to a basic driver. To this end, we provide here a bare-bones skeleton
driver (called as skelton.c below) that demonstrates the basic structure of a device driver. It can be run as is, and it is intended for quick testing and also as a framework that you can extend. Refer to this program and follow the comments in it.
/************************************************************
Character Driver Skeleton Program (skelton.c)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
o how to create a test driver (skelton)
% cc -c -D_KERNEL -xarch=v9 skelton.c
% ld -r -o skelton skelton.o
o how to configure
# cp skelton /tmp
# cat skelton.conf
name="skelton" parent="pseudo" instance=0;
# cp skelton.conf /usr/kernel/drv
# ln -s /tmp/skelton /usr/kernel/drv/sparcv9/skelton
o how to load the test driver
# add_drv skelton
o how to confirm if the driver is loaded/unloaded or not
# modinfo
or
# modinfo | grep skelton
o how to unload the test driver
# rem_drv skelton
If you insert cmn_err(9F) into this skelton, you will
be able to see how each entry point is called by
watching the console output.
This sample test driver was tested under Solaris 8 and 9.
************************************************************/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define FALSE 0
#define TRUE (!FALSE)
#define SKELTON_NAME "skelton"
#define SKELTON_DIDNODES 0x04
#define SKELTON_DIDZALLOC 0x08
static int skel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
static int skel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
static int skel_open(dev_t *devp, int flag, int otyp, cred_t *cred);
static int skel_close(dev_t dev, int flag, int otyp, cred_t *cred);
static int skel_read(dev_t dev, struct uio *uiop, cred_t *credp);
static int skel_write(dev_t dev, struct uio *uiop, cred_t *credp);
static int skel_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg,
void **resultp);
static int skel_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp);
/* ----- Device Access cb_ops Structure [start] -----
The data structure (cb_ops(9S)) MUST be provided and initialized
correctly.
*/
static struct cb_ops skel_cb_ops = {
skel_open,
skel_close,
nodev,
nodev,
nodev,
skel_read,
skel_write,
skel_ioctl,
nodev,
nodev,
nodev,
nochpoll,
ddi_prop_op,
NULL,
D_NEW | D_MP
};
/* ----- Device Access cb_ops Structure [end] ----- */
/* ----- Autoconfiguration Structure [start] -----
The data structures (modlinkage(9S),modldrv(9S),mod_driverops
and
dev_ops(9S)) MUST be provided and initialized correctly.
*/
static struct dev_ops skel_ops = {
DEVO_REV,
0, /* reference count */
skel_getinfo,
nulldev,
nulldev,
skel_attach,
skel_detach,
nodev,
&skel_cb_ops,
(struct bus_ops *)NULL
};
extern struct mod_ops mod_driverops;
static struct modldrv md = {
&mod_driverops, /* Type of module. This is a driver */
"skel driver", /* Name of the module */
&skel_ops,
};
static struct modlinkage ml = {
MODREV_1,
&md,
NULL
};
/* ----- Autoconfiguration Structure [end] ----- */
/* ----- State Pointer [start] -----
The driver MUST provide a state pointer, which is used by the
soft
state system to create the list of memory items.
*/
struct skel_state {
int instance; /* instance number */
dev_info_t *dip; /* dev_info structure */
};
static void *statep;
/* ----- State Pointer [end] ----- */
/* ----- Loadable Module Routines [start] -----
All drivers MUST implement the _init(9E),_fini(9E) and _info(9E)
entry points to load,unload and report information about the
driver module.
*/
static int
_init(void)
{
int error;
error = ddi_soft_state_init(&statep, sizeof(struct skel_state),
1);
if (error)
return error;
error = mod_install(&ml);
if (error) {
ddi_soft_state_fini(&statep);
return error;
}
return 0;
}
static int
_info(struct modinfo *modinfop)
{
return mod_info(&ml, modinfop);
}
static int
_fini(void)
{
int error;
error = mod_remove(&ml);
if (error)
return error;
ddi_soft_state_fini(&statep);
return 0;
}
/* ----- Loadable Module Routines [end] ----- */
/* ----- Autoconfiguration Entry Point [start] ----- */
static int
skel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance;
struct skel_state *bs;
int flags;
switch(cmd) {
case DDI_ATTACH:
instance = ddi_get_instance(dip);
if (ddi_soft_state_zalloc(statep, instance) !=
DDI_SUCCESS) {
return DDI_FAILURE;
}
flags = 0;
bs = ddi_get_soft_state(statep, instance);
if (bs == NULL) {
goto out;
}
flags |= SKELTON_DIDZALLOC;
if (ddi_create_minor_node(dip, "0", S_IFCHR, instance,
DDI_PSEUDO,0) != DDI_SUCCESS) {
goto out;
}
flags |= SKELTON_DIDNODES;
ddi_set_driver_private(dip, (caddr_t)flags);
ddi_report_dev(dip);
return DDI_SUCCESS;
default:
return DDI_FAILURE;
}
out: /* Undo */
ddi_set_driver_private(dip, (caddr_t)flags);
skel_detach(dip, DDI_DETACH);
return DDI_FAILURE;
}
static int
skel_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
{
int instance, flags;
struct skel_state *bs;
switch(cmd) {
case DDI_DETACH:
instance = ddi_get_instance(dip);
bs = ddi_get_soft_state(statep, instance);
if (bs == NULL) {
return DDI_FAILURE;
}
flags = (int)ddi_get_driver_private(dip);
if (flags & SKELTON_DIDNODES)
ddi_remove_minor_node(dip, NULL);
if (flags & SKELTON_DIDZALLOC)
ddi_soft_state_free(statep, instance);
return DDI_SUCCESS;
default:
return DDI_FAILURE;
}
}
static int
skel_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void
**resultp)
{
dev_t dev;
int instance, rval = DDI_FAILURE;
struct skel_state *bs;
switch(cmd) {
case DDI_INFO_DEVT2DEVINFO: /* seek device's dev_info
pointer */
dev = (dev_t)arg;
instance = getminor(dev);
bs = ddi_get_soft_state(statep, instance);
if (bs != NULL) {
*resultp = bs->dip;
rval = DDI_SUCCESS;
} else {
*resultp = NULL;
}
break;
case DDI_INFO_DEVT2INSTANCE: /* seek device's instance number
*/
dev = (dev_t)arg;
instance = getminor(dev);
*resultp = (void *)instance;
rval = DDI_SUCCESS;
break;
default:
break;
}
return rval;
}
/* ----- Autoconfiguration Entry Point [end] ----- */
/* ----- Character Driver Entry Point [start] ----- */
static int
skel_open(dev_t *devp, int flag, int otyp, cred_t *cred)
{
return 0;
}
static int
skel_close(dev_t dev, int flag, int otyp, cred_t *cred)
{
return 0;
}
static int
skel_read(dev_t dev, struct uio *uiop, cred_t *credp)
{
return 0;
}
static int
skel_write(dev_t dev, struct uio *uiop, cred_t *credp)
{
return 0;
}
static int
skel_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp)
{
return 0;
}
/* ----- Character Driver Entry Point [end] ----- */
3.20 We are having problems loading our driver in the Solaris 2.9 OS, SPARC Platform Edition.
Description:
When we use add_drv, the error message is:
devfsadm: driver failed to attach: ccmux
exit status = 11
Warning: Driver (ccmux) successfully added to system but failed to attach.
Driver (ccmux) installed.
Our driver is trying to push itself between TCP and IP stream layers, and
register a /dev/ccmux character device (in that order). It doesn't seem to get to the _init portion of our code.
The OS is in 64-bit mode, running IPv4.
Solution:
Your error is typical of trying to load a 32-bit device driver
into a 64-bit OS. A 64-bit kernel can run 32- and 64-bit binaries but can load
only 64-bit kernel modules.
In short, you cannot run a 32-bit driver with a 64-bit OS. Since the Solaris 7 release, Solaris has been able to run as either 32-bit mode or 64-bit mode. A 32-bit driver cannot run with a 64-bit mode Solaris release. Ideally, you should compile both 32- and 64-bit versions of the driver, so the OS can pick the right driver depending on the version of the OS that is running. The size of a pointer is different in the two operating systems. The distinction has to occur at compile time. isainfo -kv tells which kernel modules you can load.
# isainfo -kv
64-bit sparcv9 kernel modules
Back to Top
3.21 I wrote a new driver for the SCSI HBA. I have not defined a bus_ops function, nor have I referred to the driver after loading, but I cannot unload the driver.
The following is the dev_ops structure of the driver:
static struct dev_ops star_ops = {
DEVO_REV, /* rev, */
0, /* refcnt */
star_info, /* getinfo */
star_identify, /* identify */
nulldev, /* probe */
star_attach, /* attach */
star_detach, /* detach */
nodev, /* reset */
&star_cb_ops, /* char/block ops */
NULL /* bus ops */
};
The driver is not unloadable if its reference count is more than zero
or if it is a nexus driver. The driver is considered a nexus driver if
it has a bus_ops structure defined in the dev_ops structure.
Verify the structure's current value using adb as follows:
# adb -k /dev/ksyms /dev/mem
physmem 3e42
star_ops$<dev_ops
star_ops:
star_ops: rev refcnt getinfo
3 0 star_info
star_ops+0xc: identify probe attach
star_identify nulldev star_attach
star_ops+0x18: detach reset cb_ops
star_detach nodev star_cb_ops
star_ops+0x24: bus_ops power
scsi_hba_busops 0
The preceding structure shows that refcnt is 0, so that is not why the driver cannot be unloaded. The structure values, however, show that
bus_ops is not 0; it has some function, making the driver a nexus
driver. Therefore, it is not unloadable.
Though bus_ops is not defined in the dev_ops structure, the Solaris OS treats SCSI HBA drivers as nexus drivers. The _init entry point of this driver (SCSI HBA) has the scsi_hba_init function, which internally changes the dev_ops->bus_ops value and makes it a nexus driver.
3.22 Is there any way to keep scheduled events generated by timeout(9F) going during a panic dump?
When the system is panicking, the calls of functions scheduled by timeout(9F) and ddi_trigger_softintr(9F) will not occur.
3.23 If not, is there a way for our HBA driver to find out if a panic dump has been initiated?
Yes, there is a DDI named as ddi_in_panic(9F), which can be used in a driver to check for the panic state. It returns 1 if the system is in panic, and 0 if it is not in panic.
3.24 How can I access the physical device once the new driver is loaded?
Do a reconfigure reboot. This should create the entries in the /devices
directory and the symbolic links in the /dev directory.
3.25 Is it possible for two different drivers to create variables that they
can share? In other words, can we define a global variable, something
similar to kernel variables maxphys or scsi_options?
You can achieve this functionality by having a misc module export
this global variable and other modules referencing the same (the
misc/scsi module defines scsi_options). Other driver modules can
reference this variable by specifying the dependencies in the driver through
the -N option of link-editor ld(1M):
ld -N misc/scsi -o xx xx.o
Refer to the
Declarations and Data Structures section of Sun Product Documentation for more information.
3.26 What are the different address spaces a PCI driver can access?
A driver can access all three address spaces by setting up appropriate
mappings and using the memory, I/O, or configuration space access routines
provided in the DDI/DKI framework.
A common example is during the driver attach routine: the driver could
read the configuration space by setting up a mapping via the DDI call
pci_config_setup(9f), and read/write to the configuration registers via
the pci_config_get*/pci_config_put* routines.
3.27 A developer puts a new developed device driver under /usr/kernel, and it causes a system crash while the system boots. How can one avoid loading this buggy driver in the next boot?
The solution is to use boot -a in the OK prompt. boot -a will boot the system interactively. This allows the developer to
specify the default directory for modules to be loaded. At the prompt Enter default directory for modules:[/platform/SUNW,Ultra-1 /kernel /usr/kernel], the developer could specify the module directory without /usr/kernel.
So the system will not load the drivers under /usr/kernel/drv. It therefore avoids loading the the buggy device driver and causing a system crash
again.
3.28 Under certain conditions on the Solaris OS, a device driver cannot be unloaded.
What are those conditions?
- If a driver has no detach routine (
nodev is specified in the driver's dev_ops(9S) for its detach routine).
- If EBUSY is returned from the driver's
_fini() routine.
- If the driver has a positive number of references to it.
- If the driver has a
bus_ops structure specified in its
dev_ops structure, and it has children attached to it
(therefore it is a nexus driver).
- If the driver has no
dev_ops at all!
3.29 In drivers we get the instance number in attach/detach routines,
and we get the device (*dev) in open routine. But there is no DDI/DKI call
to get the instance number for the given device number in the open routine.
Please use the following Solaris DDI driver entry point in the open routine.
getinfo(9E):
int prefixgetinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp);
When cmd is set to DDI_INFO_DEVT2INSTANCE, getinfo()
returns the instance number associated with the dev_t arg.
The instance number will be returned in the field pointed to by resultp.
3.30 I have just written a new driver and am ready to test it. How do you create
the new device links so the driver can be loaded and tested?
Method #1:
At the boot menu, enter b -r, which is the same as the boot
-r you would use on a SPARC system.
Method #2:
As root:
# touch /reconfigure
# init 6
You can also use the reboot command in place of init 6.
Method #3:
As root:
# _INIT_RECONFIG=1 ; export _INIT_RECONFIG
# /etc/init.d/drvconfig
# /etc/init.d/devlinks
Method #1 and #2 are the preferred ones, but #3 can be used in case you
forgot when rebooting the system after adding new hardware and you don't
want to bring it down again.
If you have removed hardware since your last reconfiguration, it is best
to use one of the methods that involves a reboot of the system.
This is to ensure that everything is closed down properly and the system does not access invalid device nodes that represent missing hardware. In this
case, use either method #1 or #2.
3.31 How can I copy the data from the kernel memory space to the user
memory space?
Use ddi_copyout(9F), which enables us to copy some data in the kernel
memory space to our application program.
For example, when we want to obtain the configuration data within the
device driver, ddi_copyout(9F) is useful.
The sample program is shown below. This is the extention of the skelton
driver -- refer to 3.19 for Character Driver Skelton Program (skelton.c).
1) The user application program opens the device and call ioctl(2).
#include
#include
#include
#include "skelton.h"
main() {
int fd;
struct my_skelton_info skelton_info_copy;
fd = open ("/devices/pseudo/skelton@0:0", O_RDONLY);
if (fd == -1) {
perror ("open");
exit (errno);
}
if (ioctl(fd,SKELTON_DUMMY,NULL) == -1) {
perror("ioctl");
exit(errno);
}
if (ioctl(fd,SKELTON_GETINFO,&skelton_info_copy) == -1) {
perror("ioctl");
exit(errno);
}
printf("my_skelton_info venid = %d\n",skelton_info_copy.venid);
printf("my_skelton_info devid = %d\n",skelton_info_copy.devid);
printf("my_skelton_info revid = %d\n",skelton_info_copy.revid);
}
2) The following sample driver will respond to the application.
o header file (skelton.h)
#ifndef SKELTON_H
#define SKELTON_H
#define SKELTON_DUMMY _IO('m',1)
#define SKELTON_GETINFO _IO('m',2)
struct my_skelton_info {
int venid; /* Let's suppose this is the vendor id. */
int devid; /* Let's suppose this is the device id. */
short revid; /* Let's suppose this is the revision id. */
};
#endif SKELTON_H
o extended skelton driver (skelton.c)
:
:
#include "skelton.h"
:
:
struct skel_state {
int instance; /* instance number */
dev_info_t *dip; /* dev_info structure */
struct my_skelton_info info; /* additional info */
};
:
:
static int
skel_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance;
struct skel_state *bs;
int flags;
switch(cmd) {
case DDI_ATTACH:
:
:
bs = ddi_get_soft_state(statep, instance);
:
:
/* SET VALUE IN THE KERNEL SPACE */
bs->info.venid = 1234; /* Let's suppose this is the vendor id
value. */
bs->info.devid = 5678; /* Let's suppose this is the device id
value. */
bs->info.revid = 99; /* Let's suppose this is the revision id
value. */
return DDI_SUCCESS;
default:
return DDI_FAILURE;
}
:
:
}
static int
skel_ioctl(dev_t dev, int cmd, intptr_t arg, int mode,
cred_t *credp, int *rvalp)
{
int instance;
struct skel_state *rs;
instance = getminor(dev);
rs = ddi_get_soft_state(statep, instance);
if(rs == NULL) {
return ENXIO;
};
switch (cmd) {
case SKELTON_GETINFO:
/* COPY DATA FROM KERNEL SPACE TO USER SPACE */
if(ddi_copyout((void *)&rs->info, (void *)arg,
sizeof(struct my_skelton_info), mode) != 0) {
return EFAULT;
}
return 0;
case SKELTON_DUMMY:
cmn_err(CE_NOTE, "_ioctl called (SKELTON_TEST)\n");
return 0;
default:
return ENOTTY;
}
}
3) The result will be as follows.
You will see the following output. This is the result:
values are copied from the kernel memory space to the user
memory space.
# ./skelApp
my_skelton_info venid = 1234
my_skelton_info devid = 5678
my_skelton_info revid = 99
3.32 I want to manage multiple instances in my Solaris device driver.
My device needs four-channel communication. I call ddi_create_minor_node() four times from the attach() routine. But inside the ioctl() routine, I cannot get the minor number. How can I achieve this?
The trick commonly used is to combine the instance number with a driver-specific number (for example, the slice number) to create a minor number. Then split it back up into two, when you need it.
A code example follows at the end of this note. Look at how MINOR_NUM
macro achieves this.
For more information, visit the Sun Developer site
and navigate to the Solaris Driver Development area. There are a number of sample drivers with sources.
In particular, the 'bst' driver uses this technique.
/*
* Create the minor number by combining the instance number
* with the slice number.
*/
#define MINOR_NUM(i, s) ((i) << 4 | (s))
int
xkdiskattach(dev_info_t *dip, ddi_attach_cmd_t cmd)
{
int instance, slice;
char name[8];
/* other stuff in attach... */
instance = ddi_get_instance(dip);
for (slice = 0; slice < V_NUMPAR; slice++) {
sprintf(name, "%c", slice + 'a');
ddi_create_minor_node(dip, name, S_IFBLK,
MINOR_NUM(instance, slice), DDI_NT_BLOCK_CHAN, 0);
/* Other stuff ... */
}
}
Back to Top
4. About Drivers
4.1 What is a nexus driver and how do I write one?
Nexus drivers handle bus-specific operations.
You cannot currently write one. The nexus interfaces are in a continual state
of change and so they are not documented.
4.2 Can I write a driver for a SCSI Host Bus Adapter? That's a nexus driver, isn't it?
Starting in Solaris 2.4, you can write DDI-compliant SCSI Host Bus Adapter (HBA)
drivers.
Yes, they used to be implemented as nexus drivers, but a set of interfaces were
added to the DDI/DKI in Solaris 2.4 to hide many of the changing details
of nexus drivers from HBA drivers.
The man pages for the interfaces are in section 9 of the man pages.
4.3 Is there a way to send signals to user threads from a device driver?
Starting in Solaris 2.4, there is. You can use proc_signal(9F).
Note:
To send signals from interrupt handlers, you have to get a reliable handle
on the process that can be used even if the process exits before the handler
sends the signal, which is one reason proc_ref(9F),proc_unref(9F),
and proc_signal(9F) were introduced in Solaris 2.4.
You can also look at the chpoll(9E) entry point. This is a documented, DDI-compliant
way for non-STREAMS drivers to implement polling. In many cases, this is what you really want. Applications
can express an interest in a set of file descriptors and be notified by the driver that something has changed.
See the man pages for chpoll(9E) and poll(2). poll(2) replaces the SunOS 4.x
select(2) system call.
4.4 What is a layered driver?
A layered driver is a driver that calls another driver's routines.
An example might be a pseudo device driver that provides a pseudo disk consisting
of several real disks. This driver translates strategy(9E) requests into calls to the real disk driver
strategy(9E) routines.
There is no way to write a Solaris DDI-compliant driver that performs this layering. However, drivers must be aware
that they may be used by a layered driver
such as in open(9E), close(9E), andioctl(9E).
4.5 What does self-identifying means?
Self-identifying devices devices are those that are recognized by the
PROMs/BIOS when the system boots. This is specifically for PCI devices. For
devices that do not identify themselves to PROM/BIOS, the OS has no
information about them and hence has to be specified in conf file. For
example, "driver.conf" is a must for a pseudo device.
4.6 What properties for devices that are not self-identifying must be
described?
For non-self-identifying devices, you must describe basic properties for
devices that are not self-identifying, such as disks, tapes, older ISA
devices, and VME devices, in the driver.conf file.
4.7 What are the contents of the xx.conf file?
This file contains any expected SCSI ID or combination of reg, interrupts,
and other basic properties. See the driver.conf(4) man
page for more information.
4.8 Can you name some self-identifying devices?
Examples of self-identifying devices are all PCI devices, SBus devices,
and PnP ISA devices.
4.9 My driver panics the system in ddi_create_minor_node(9F). Is this a known bug in the kernel?
No, it's probably a bug in the driver. Check your getinfo(9E) routine and be sure that it is doing the right
thing. See the getinfo(9E) man page and the Writing Device Drivers book in the
documentation section of this site for the proper form of using this interface.
Back to Top
4.10 Do self-identifying devices require a configuration file?
No, however if you need to supply driver private properties, they are specified
in the configuration file. See driver.conf(4) and pci(4) for more information.
In order for the hwconf file to augment the properties of a hardware dev_info node, it must match the
"name" property, the "parent" property, and the first "reg" property. If they don't match, you'll get two dev_info
nodes - one from the hardware (PROM) and one from the driver.conf file.
4.11 I want to have two drivers in one module. It doesn't seem to work by putting multiple entries
in the modlinkage structure.
Including two drivers in a single loadable module is not supported.
However, it can be done in a non-compliant way by making three loadable modules.
Put all the driver code in one common module and write two small wrapper modules
for each driver. The small wrappers need _init, _fini, _info,
dev_ops/cb_ops structures for the driver and a line so the two small driver wrappers
depend on the main module and get linked with its symbols.
Each mini-driver stub should include:
char _depends_on[] = "misc/foo";
Where "misc/foo" is the name of the common code module.
The common module would be a "misc." module, with it's own _init, _fini and
_info functions. The module ops would be of the "misc" type using a modlmisc structure
(look this up in <sys/modctl.h>).
The underlying driver just includes the common shared code.
The mini-driver stubs each have their own separate dev_ops structures and can
be made unloadable. The misc module with the common driver code will be
automatically held via the driver stub modules depending on them.
Note that this method, and using modlmisc, are not DDI-compliant, and so may
not work in future releases of Solaris.
4.12 How does a driver retrieve the hostid?
There is no DDI-compliant way to retrieve it, though applications may retrieve
it with the sysinfo(SI_HW_SERIAL) system call.
4.13 How do I exercise my driver's aread(9E) and awrite(9E) routines?
Drivers which provide aread(9E) and awrite(9E) entry points provide asynchronous
I/O capabilities. The DDS web site contains
example drivers (see for example bst and sst) which provide these entry points.
To exercise them, explicit calls to aioread(3) and aiowrite(3) must be made.
Consult the aioread(3) man
page for information on the libaio library, entry points, and the SIGIO
asynchronous I/O notification signal.
Standard read(2) and write(2) system calls do not use these entry points.
4.14 When does the kernel unload loadable modules?
Auto unloading is triggered when the system is so short of memory that it needs
to swap one or more processes out of memory. The precise condition involves
comparing 'freemem' with various combinations of VM tuning parameters,
though it's all a bit arbitrary really. In practice it doesn't happen very
often.
When auto unloading, the kernel tries to unload every module in the system by
calling the _fini(9E) routine of every module that is known to be not in use.
One simple way to prevent a module from unloading (without changing the source
code) is to ensure that it is manually loaded using 'modload' for example:
# modload /kernel/drv/st
The system will not auto-unload a manually loaded module, for more details
see modunload(1M). Another way to prevent a module unloading
is to start a background process that will use the module in some harmless
way:
# sleep 10000 < /dev/foo &
Though of course open(9E)isn't always without side effects. Device drivers can also fail the
_fini(9E) or detach(9E) the request to prevent unloading.
Note: Part of testing a new unloadable device driver should be to put the system
into a fairly tight loop, loading and unloading the driver to check that
there aren't any resource leaks, for example:
#!/bin/sh
>while (/bin/true) do
# use the device in some way which will cause it to load
and attach
sleep 3 < /dev/foo
# tell everything auto-unloadable to auto-unload
modunload -i 0
# verify that it really went away
modinfo | fgrep foo
done
These tests should be run on an otherwise unloaded machine running and your script
should execute the load/unload loop at least for a thousand iterations
while checking on VM resources using vmstat(1M) and crash(1M).
DMA and interrupt resource leaks and double-frees are much more likely to lead
to kernel panics or attach failures and are thus easier to find. Memory
leaks will show up as reducing quantities of free memory.
Freeing memory already freed is more difficult to find without using a debug kernel. However even without a debug kernel, running a stress test (e.g. sundiag)
at the same time as running the above unloading test is a good way to verify
that a driver's load and unload routines are working correctly.
Also note, that if you do get a panic, and the backtrace includes 'kmem*', don't
assume that the problem is in the kernel memory allocator. The overwhelming
majority of 'kernel memory allocator' bugs are caused by double-frees or
misaligned or missized kmem_frees in some other part of the kernel or in your device driver.
4.15 How Do I make my driver support PCI Hotplug?
See the PCI Hot Plug White Paper.
4.16 How do I make my driver support SCSI Hotplug?
See the SCSI Hotplug information page.
4.17 Is D_HOTPLUG necessary?
Solaris 7 requires drivers to set D_HOTPLUG in cb_flag
(see cb_ops(9S))
if the driver supports detaching individual instances with DDI_DETACH.
In Solaris 8 it is assumed that all drivers provide that functionality.
Beginning in Solaris 8, it does not matter whether D_HOTPLUG is
set or not.
4.18 How do I set breakpoints to stop at the beginning of my driver's probe?
Please see the Deferred Breakpoint Feature documented in the kadb(1M)
man page. An excerpt:
Deferred Breakpoint Feature
Since the kernel is dynamically loaded, not all modules may be loaded
when a breakpoint is set. kadb can set deferred breakpoints which will be dynamically
inserted when the corresponding module is loaded. The corresponding module and the location must both be
specified when referring to a deferred breakpoint, as follows:
module_name#location:b
4.19 Solaris Driver Installation and Packaging
4.19.1. Should I get the "Solaris Ready" brand?
IHVs should be getting the "Solaris Ready" brand as applicable.
4.19.2. Where can I find general information about packaging
for Solaris software and device drivers in particular?
See the Application Packaging Developer`s Guide at http://docs.sun.com.
The book has a case study on "Installing and Removing a Driver With Procedure Scripts".
Back to Top
4.19.3. Where should drivers be installed on the system?
The preferred location for drivers depends on the extent of platform
support the driver provides. There are a number of possible locations to
install drivers. Some locations are in the default kernel modules search path
(see kernel(1M)
and boot(1M)
for details).
| Maintenance Commands |
kernel(1M)
|
| |
...
The moddir variable contains a colon-separated list of directories
that the kernel searches for modules. moddir can be set in the
/etc/system file. The minimal default is
/platform/platform-name/kernel:/kernel:/usr/kernel, but this
default may be overridden by a specific platform. It is common for many
systems to override the default path with /platform/platform-name/kernel:/platform/hardware-class-
name/kernel:/kernel:/usr/kernel, where platform-name
can be
found using the -i option of uname(1), and hardware-class-name
can be found using the -m option of uname(1).
The kernel configuration can be controlled using the /etc/system
file (see system(4)). |
|
Here are some guidelines for placing drivers in any of the default kernel
modules search path directories:
/platform/`uname -i`/kernel/drv/
Driver required for booting and designed, tested, and supported for
a specific hardware implementation (e.g., "SUNW,Ultra-1", "Compaq Proliant
6000")
/platform/`uname -i`/kernel/drv/sparcv9/
64-bit driver required for booting and designed, tested, and supported
for a specific 64-bit hardware implementation (e.g., `isalist` includes
"sparcv9")
/platform/`uname -m`/kernel/drv/
Driver required for booting and designed, tested, and supported for
a given hardware class (e.g., "sun4m", "sun4u")
/platform/`uname -m`/kernel/drv/sparcv9/
64-bit driver required for booting and designed, tested, and supported
for a given hardware class that supports a 64-bit native instruction set
(e.g., `isalist` includes "sparcv9")
/kernel/drv/
Driver required for booting and designed, tested, and supported for
all systems of a given instruction set architecture or processor type (`uname
-p`)
/kernel/drv/sparvc9/
64-bit driver required for booting and designed, tested, and supported
for all systems of a given native instruction set (e.g., `isalist` includes
"sparcv9")
/usr/kernel/drv/
Driver that can be loaded after initial boot and designed, tested,
and supported for all systems of a given instruction set architecture or
processor type (`uname -p`)
Another approach is to define an IHV-unique directory to install the
drivers and modify /etc/system accordingly to tell the kernel
to look in the directory to load the driver module (see system(4),
drv
and moddir namespaces), for example:
"IHVdrvr" package prototype(4)
file:
i pkginfo
i depend
i copyright
d usr ? ? ?
d usr/IHVname ...
d usr/IHVname/drv ...
f usr/IHVname/drv/IHV,driver_name ...
i checkinstall
i postinstall
i preremove
e etcsystem etc/system ...
i i.etcsystem
i r.etcsystem
where "IHV,driver_name" is the file name of the driver, the
"checkinstall" procedure scripts see whether the driver is already installed
and/or applicable to the target system, the "postinstall" procedure script
does the add_drv, the "preremove" procedure script does the rem_drv,
and the "etcsystem" class defines the necessary modifications to /etc/system
for driver addition (i.etcsystem) and removal (r.etcsystem) via class action
scripts. See the Application Packaging Developer's Guide at docs.sun.com
for further information.
4.19.4. Do drivers for Solaris need to be uniquely named?
Yes. The driver name, aliases, name-to-major entries, and so on, must be guaranteed
to be unique in the Solaris distribution, for all ISAs (SPARC and IA).
This is necessary to avoid namespace collisions on servers supporting (SPARC
and/or IA) clients as an exported service. When the driver
package is installed on the server, there is only one (1) flat namespace
to record the driver name, aliases, class, minor node, and major number:
/etc/name_to_major
/etc/minor_perm
/etc/driver_aliases
/etc/driver_classes
See also the add_drv bug noted in add_drv(1M).
A common naming convention is <IHV_name>,<driver_name>,
where <IHV_name> can be a stock market identifier.
4.19.5. How do I add a new device driver to the system?
All drivers must be added using add_drv(1M),
instead of directly modifying the device registration files using procedure
scripts. This approach will dynamically create major and minor nodes. However,
since the full path name to the driver is not registered with the system,
your driver name and alias(es) need to be unique to the system.
| Maintenance Commands |
add_drv(1M)
|
| |
...
BUGS
add_drv will accept a full pathname for device_driver.
However, the kernel does not use the full pathname; it only uses the final
component and searches the internal driver search path for the driver.
This can lead to the kernel loading a different driver than expected.
For this reason, it is not recommended that you use add_drv
with a full pathname. See kernel(1M) for more information on the
driver search path.
|
|
In your driver package, this is done using the procedure scripts 'postinstall'
and 'preremove' (or 'postremove'). Ideally, the package would use the "checkinstall"
packaging script to check if the driver is already installed. See the Application
Packaging Developer's Guide at docs.sun.com
for further information.
Back to Top
4.19.6. Why isn't there a common location for drivers to be installed
on the system?
Ideally, there would be a unique location in the kernel search path
for IHV/OEM drivers to distinguish them from Solaris software-delivered
platform independent, hardware-class dependent, and platform-specific
drivers, but that is not defined. If you see this as a problem, please call your Sun Software Support Provider and ask to escalate Solaris BugID #1226960.
4.19.7. How should I name the software package that contains my driver?
The software package (see pkgmk(1M))
abbreviation (see pkginfo(4),
PKG parameter description) naming heuristic is the prefix "SUNW" if Solaris software is providing
first-line support or doing the packaging of the driver.
If this is not the case, use your vendor stock ID or similar unique prefix
for the first three or four characters in the name:
PKG=<COMPANY_ID><pkg_id>
where:
<COMPANY_ID>=: 3 or 4-character uppercase alphanumeric
company identifier; company stock ID is preferred
<pkg_id>=: 5 or 6-character alphanumeric package identifier
4.19.8. If I am allowed to put my driver package on the Solaris operating
environment distribution media, do I have to do anything different in my
driver package?
For driver packages being distributed on the Solaris operating environment
media, the driver package may have a dynamic clustertoc test defined (see
parse_dynamic_clustertoc(1M)
and clustertoc(4)
for details). Using a dynamic clustertoc test makes the package available
for installation only on machines with the appropriate hardware attached.
Only the SUNWCXall metacluster should be static, and all the other metaclusters
should have dynamic entries. The SUNWCXall metacluster is used to install
everything on a server necessary to support the clients of any hardware
configuration.
5. SunOS 4.x to SunOS 5.x
5.1 I used to pass flags to my driver with the 'flags' keyword in SunOS 4.x. How do I do this in
SunOS 5.x?
You can create arbitrary properties in your driver.conf(4)
file.
These can be retrieved with one of the ddi_getprop(9F) routines in the driver, which is the preferred way to get information to
the driver.
5.2 Is there a SunOS 5.x replacement for the SunOS 4.x dev_info
command?
Similar functionality is provided by prtconf(1M).
5.3 My driver needs to keep track of all the processes using it. This can be done in SunOS 4.x by looking
at proc structure information in open(9E), but I can't "forget" about processes that are no longer using
it since close(9E) is only called when the last reference goes away. How can I do this?
You can't look at the proc structure in SunOS 5.x, so you might consider using
the clone mechanism. Essentially, this allows you to allocate an "available" minor device on each open(9E),
and since each open(9E) results in a new minor number, you get one close per process. This usually works
fine, though processes that fork(2), dup(2), or send file descriptors to other processes
may be problems.
There are two ways to do this:
-
STREAMS drivers can set the
is_clone argument of ddi_create_minor_node(9F) to CLONE_DEV.
When the node is opened, the clone driver calls the open(9E)
routine of the driver with the CLONEOPEN flag set.
-
All drivers can decide to clone by changing the minor number of the
dev_t pointer passed to them.
Back to Top
7. Compiler and Development Environment Questions
7.1 What compiler can I use to compile device drivers?
When you build Solaris kernel modules use the same compiler
that Sun used to build the OS. SunPro C is the only supported compiler.
The following table gives the SunPro C compiler versions used to compile Solaris releases (in descending chronological order).
|
Solaris Release
|
Compiler for SPARC
|
Compiler for x86
|
|
2.4
|
2.0.1
+patch
100961-01
|
2.0.1
+patch
101205-01
|
|
2.5
|
2.0.1
+patch
100961-08
|
2.0.1
+patch
101205-08
|
|
2.5.1
|
2.0.1
+patch
100961-08
|
2.0.1
+patch
101205-08
|
|
2.6
|
4.2
|
4.2
|
|
S7 (32 bit) FCS and quarterly updates
|
4.2
|
4.2
|
|
S7 (64 bit) FCS and quarterly updates
|
5.0
Alpha 06-Apr-98
|
N/A
|
|
Solaris 8
|
5.0
+patches
107311-03
107357-02
107390-02
Forte 6
+patches
109482-03
109491-02
109578-01
|
5.0
+patches
107311-05
107357-02
Forte 6
+patches
109492-01
109477-01
|
|
Solaris 9 (64-bit)
|
Forte 6
+ patches
111679-14
115117-01
5.0
|
N/A
5.0
|
|
Solaris 9 (32-bit)
|
Sun Studio 8
Forte 6
+ patches
111679-14
115117-01
5.0
|
Sun Studio 8
Forte 6
+ patches
115976-01
5.0
|
|
Solaris Express (64-bit)
|
Sun Studio 8
Forte 6
|
TBD
|
|
Solaris Express (32-bit)
|
Sun Studio 8
Forte 6
|
Sun Studio 8
Forte 6
|
|
The SunPro compiler does not work in compliant mode (-Xc), because the system
header files are not fully compliant.
Note: To ensure that compilers do not optimize away accesses to device
registers, you must use volatile properly.
7.2 What compiler do I use to build 64-bit applications?
Sun WorkShop 5.0 was the first compiler from Sun to generate 64-bit
code. The latest WorkShop compiler is now part of the Sun Studio line of developer tools.
7.3 Which option should I provide the compiler to generate 64-bit code? What is the difference between
-xarch=v9, -xarch=v9a, and -xarch=v9b?
Using the Forte (WorkShop) C compiler, you specify the instruction set architecture
(ISA) using -xarch.
-
-xarch=v9 will generate 64-bit code.
-
-xarch=v9a adds the Visual Instruction
Set (VIS) and extensions specific to UltraSPARC processors to the SPARC-V9 ISA, and enables the compiler
to generate code for good performance on the V9 SPARC architecture.
-
-xarch=v9b adds UltraSPARC-III extensions and VIS
version 2.0 to the V9a version of the SPARC-V9 ISA. Compiling with this
option uses the best instruction set for good performance in a Solaris
UltraSPARC-III environment.
Consult the Forte (WorkShop) documentation for detailed information.
Warning: Code compiled with -xarch=v9b
will generate instructions unique to the UltraSPARC-III platform and thus
cause errors on the UltraSPARCII platform. The recommended option for generation
of a 64-bit driver is -xarch=v9.
7.4 Why do I get "/kernel/drv/sparcv9/mumble: undefined symbol" when I try to
load my 64-bit driver?
You should use the
-xregs=no%appl compiler option to avoid this. This problem was introduced in Solaris 7 and has been fixed in Solaris 8.
7.5 Do I need to recompile my Solaris 7 driver to be able to run on Solaris 8?
No. However, do a sanity check and verify that the driver runs properly under Solaris 8.
7.6 The Forte Developer compiler specifies the -xchip=ultra3 / xtarget-ultra3 options. Do I need to
recompile for the UltraSPARCIII processor?
32-bit drivers
The UltraSPARCIII processor only runs a 64-bit Solaris kernel. If your driver
is a 32-bit driver, you will need to recompile your code to produce a 64-bit
driver to be able to run on an UltraSPARCIII based machine. Several whitepapers
are available with information on writing 64-bit drivers.
64-bit drivers
You do NOT need to recompile your 64-bit driver code to run on an UltraSPARCIII processor.
Because the bus architecture of UltraSPARCIII based systems has changed, verify that the driver
operates correctly on an UltraSPARCIII system.
Platform-specific compiler options
The Forte Developer Compiler provides several options which allow you to accurately describe the target
computer hardware (-xtarget, -xchip, -xcache, etc). When program performance is critical,
the proper specification of the target hardware could be very important. However, for most programs and
older SPARC processors, the performance gain is negligible. For portability reasons, do not use
platform-specific options.
7.7 Can I use the Forte C compiler -fast option to optimize the driver performance?
The -fast option is really a macro option that invokes several different options. Among them are
-native, which includes -xchip, -xcache, and -xarch. For portability reasons, compile
with as few platform-dependent options as possible. However, for performance reasons, it might be worthwhile to
experiment with the -fast option. In such a case, overwrite the -xarch option with the
preferred instruction set architecture (ISA) setting (e.g. -xarch=v8plusa for 32-bit drivers;
-xarch=v9 for 64-bit drivers) because the -native setting depends on the version of the compiler.
7.8 What compiler options should I typically use?
32-bit drivers
-cc -D_KERNEL -xarch=v8plusa -x02
-
_KERNEL : produces kernel (driver) code
-
-cc -D_KERNEL -xarch=v8plusa : produces 32-bit code, taking advantage of UltraSPARC features
-
-x02 : basic local and global optimization
64-bit drivers
-cc -D_KERNEL -xarch=v9 -x02
-
_KERNEL : produces kernel (driver) code
-
-cc -D_KERNEL -xarch=v9 : produces 64-bit code that will run on all UltraSPARC processors
-
-x02 : basic local and global optimization
7.9 Can I write a device driver in C++?
You might be able to, but it isn't supported. The system header
files are not guaranteed to work with both __cplusplus and _KERNEL set.
Also, unlike C, C++ requires significant runtime support. As libc
isn't available in the kernel, you can't use object initialization, object
constructors, and object destructors. You can circumvent the libc requirement
by avoiding most uses of objects, but if you do that, why use C++ at all?
Back to Top
7.10 When using the 3.x SunPro C compiler on a SCSI driver, why are all the SCSI
symbols undefined?
The problem is that the driver is defining _depends_on
like this:
static char _depends_on[] = "misc/scsi";
_depends_on is used by the kernel to determine that this driver requires another module to be loaded into memory.
The 3.x SunPro C compiler determines that since the driver does not reference _depends_on,
and it is static, it is not needed in the executable. Since the compiler
removes the _depends_on string, the kernel doesn't load the misc/scsi module, and the driver fails to load.
There are two solutions to this problem:
-
Don't use the 3.x compiler. Sun only supports using the 2.x and 4.x compilers for device drivers at this time.
-
Remove the 'static' from the definition of
_depends_on, to make it a global symbol. This prevents the
compiler from removing it, regardless of the compiler used.
The documentation has been changed to indicate that _depends_on
should not be static.
Note
that _depends_on is not a general mechanism to support loading other modules. It can only be used in
certain well-defined areas, such as in SCSI drivers.
8. System Messages
8.1 What does "panic: Lock Held and only one CPU" mean?
This only occurs on a uniprocessor. It means that a spin mutex is held and it
will spin forever because there is no other CPU to release it. This could
be because the driver did not release the mutex on one code path, or blocked
while holding it.
A common cause of this panic is incorrect usage of a spin-type mutex used by the
device's high-level interrupt handler. If the interrupt is high-level (see
ddi_intr_hilevel(9F) and Intro(9F)), the mutex is a spin-type mutex
which blocks the interrupt while held. The interrupt handler cannot use an adaptive mutex or a
condition variable since these might effect dispatcher state, and the dispatcher doesn't block
high-level interrupts. If the spin-type mutex is held while calling a routine that blocks via
cv_wait(9F) or via mutex_enter(9F) on an adaptive (normal non-high-level
driver mutex), then the interrupt could occur during that time and spin forever on the mutex
(on an MP) or get this panic on a uniprocessor.
8.2 What does it mean when prtconf(1M) returns "(no driver)"?
I loaded the driver with modload(1M), and prtconf(1M) said the same thing.
Referencing the driver by opening it and sending it data causes prtconf(1M) to remove the "(no driver)".
At some point, the installation of modules was decoupled from the loading
of modules, which was a good idea. modload(1M) just loads a module and does not install it. Basically,
if you use this command, you are just wasting memory. The command does have other valid uses.
If you want to load and install your driver, there are two ways to do it.
-
Reference the device. (
echo < /dev/<device_name> or /devices/...)
and the system framework will autoload and install your driver.
-
drvconfig -i <name> loads and installs the named driver and build the device nodes for the driver.
You'll have to use the second method if the device nodes for the device are not
yet in the file system. add_drv(1M) does a drvconfig(1M)
on your behalf.
Also note that the notion of the module subsystem installing a driver is slightly
different than the notion of the driver/system frameworks installing a
driver. drvconfig makes sure to do both.
8.3 What does this message mean: "panic: assertion failed: ibc<(void*) KERNELBASE,
file: ../../common/mutex.c, line 427?
A common coding error is to pass the address of an iblock cookie to mutex_init rather than
the value (possibly casted to (void*)). If you are running a DEBUG kernel, you will get this panic when you
pass the address of the cookie to mutex_init.
The fix is to change:
mutex_init (..., &cookie);
to
mutex_init (...,(void*)cookie);
If you are running a non-DEBUG kernel (which is most likely the case), your driver
will fail in unexpected ways. You may get the wrong type of mutex or assign
the mutex the wrong priority.
9. Solaris OS on x86 Platforms
9.1 How do I boot with kadb on the Solaris OS for x86 Platforms?
General information on the x86 booting process can be found at the
http://docs.sun.com
online documentation site, searching for
boot kadb
or
"b kadb"
The boot(1m), and eeprom(1m) commands also provide descriptive information;
in general x86 and SPARC boot options are handled similarly. Here is some further clarification:
Specifically, the file the secondary boot loads can be controlled by setting of the 'boot-file' property,
and by command line override.
During Solaris x86 boot, the /etc/bootrc file interprets user input. 'b
kadb [boot-flags]' is the typical syntax for entering commands.
At the install prompt, typing 'kadb' is interpreted as a shorthand for 'boot
kadb -d'.
Other alternatives effecting kadb boot are the setting of the 'boot-file' property,
via either the devconf boot diskette menu system (see the Help Menu for
the boot-properties editing menu), the eeprom(1m) command, or by direct
editing of the bootenv.rc file, which stores the pseudo-prom properties
which cannot actually be stored in non-volatile ram on the x86 platform.
NOTE:
the '-D' boot option is not yet supported on the Solaris OS for x86 platforms.
9.2 How can I make a boot floppy that boots right into the Solaris OS on x86 Platforms?
Searching for 'auto-boot' at the http://docs.sun.com site brings up general information on control of
automatic booting of Solaris (mostly related to SPARC). The 'auto-boot?' property (see the eeprom(1m)
man page) generally controls whether Solaris automatically boots.
Additional properties introduced in the Solaris x86 boot process allow choosing
a timeout value for allowing the menu system to be entered, and to select
a 'Saved Configuration' (see the self documenting boot diskette menu subsystem
for more details). Here are typical
default settings:
setprop auto-boot? true
setprop auto-boot-cfg-num -1
setprop auto-boot-timeout 5
These properties can be edited via either the devconf boot diskette menu system (see the Help Menu for the
boot-tasks and boot-properties editing menus), the eeprom(1m) command, or by direct editing of the bootenv.rc
file, which stores the pseudo-prom properties which cannot actually be stored in non-volatile ram on the x86
platform.
In summary, the boot diskette menu system for the Solaris OS on x86 Platforms can be used to control
auto-booting, via these properties, via either the explicit menu to select the boot device, or the general
property editing menu. The the boot diskette menu system for the Solaris OS on x86 Platforms is largely self
documenting, with a context-sensitive help system.
9.3 How does one mount a dos IDE HD on the Solaris OS on x86 Platforms?
Searching for 'pcfs' at the http://docs.sun.com
online documentation site provides general information on the pcfs file
system and mounting pcfs filesystems.
Specifically, the pcfs(7FS) manpage describes the special syntax required to mount dos filesystem
partitions on the Solaris OS on SPARC and x86 Platforms. On the intel platform, dos logical partition qualifiers are required on the
mount command. From the man page:
F File
Systems
pcfs(7FS)
x86: pcfs is mounted from the
hard disk with the command:
mount -F
pcfdevice-special:logical-drive directory-name
NOTE: support for ide removable drives such as zip drives is not complete
in Solaris at the time of this writing.
Back to Top
9.4 How do I allocate and access physical memory for the Solaris OS on x86 Platforms?
9.4.1 Solaris OS on x86 Platforms: How do I allocate physically contiguous memory?
Solaris provides several DDI/DKI routines prefaced with ddi_dma_ which
abstract the characteristics of DMA, such that a single driver can be written
to work across all Solaris supported platforms.
Searching for
alloc handle dma
at http://docs.sun.com, or this site, will
locate information on the Solaris DMA Model. See the Writing Device Drivers
book for detailed information.
Specifically, ddi_dma_mem_alloc(9F) is the preferred routine to allocate driver memory suitable for
DMA. The ddi_dma_attr(9S) structure controls the allocation of memory. Note that setting the dma_attr_sgllen
to a length of 1 informs the DDI framework that physically contiguous memory is required. On Solaris Intel, each
ddi_dma_cookie(9S) structure returned when the allocated memory is bound with the various ddi_dma*_bind(9F)
contains the physical address of the memory, suitable for use in constructing scatter-gather lists. See the
example isp(7D) device driver source, available for download
for examples of using various ddi_dma_ prefixed routines.
NOTE: some versions of the DDI framework may not handle all rounding and
size issues. In general, it's better to allocate the minimal number of
contiguous pages required, in sizes falling on page boundaries.
Other allocators: on the Solaris OS on x86 Platforms, ddi_iopb_alloc(9F) by default allocates from a a very restricted
pool of pages allocated at boot time. It's best not to use this allocator unless necessary (note that
most modern adapters can perform DMA into the entire 32- and/or 64-bit
address range). Also note that the kernel virtual memory allocated by kmem_alloc(9F) can be bound using
ddi_dma_addr_bind_handle(9F),
but this memory is not physically contiguous, and may appear above 32-bits on 64-bit or PAE enabled systems.
ddi_dma_mem_alloc(9F) is a recommended interface to use.
TIP: allocating very large blocks of physically contiguous memory:
The Solaris kernel memory model does not support large blocks of physically
contiguous memory: as virtual/paged memory usage fragments the physical
memory, there is no management performed to defragment physical memory.
Also, various kernel data structures are sized dynamically at boot time,
to match the system's memory size.
(One method to allocate a large physically contiguous memory block is to perform
the allocation very early (when the driver is first attached), as memory
resources are relatively unfragmented at this time. Note that
kmem_alloc(9F) will typically also return memory which is physically
contiguous early in system boot).
9.4.2 Solaris OS on x86 Platforms: Why does ddi_iopb_alloc() fail with larger allocations?
On the Solaris OS on x86 Platforms, ddi_iopb_alloc(9F) by default allocates from a a very restricted pool of pages
allocated at boot time. It's best not to use this allocator unless necessary (note that most modern adapters can
perform DMA into the entire 32- and/or 64-bit address range).
It is possible to increase the number of pages available for use by ddi_iopb_alloc(9F) by placing
the following kernel variable edit into /etc/system:
set lomempages=140
However it is preferable to use the new ddi_dma_ DMA Model routines.
9.4.3 Solaris OS on x86 Platforms: How do I convert a kernel virtual address to a physical address?
The ddi_dma_addr_bind_handle(9F) and ddi_dma_buf_bind_handle(9F) routines provide the
method to allocate DMA resources for kernel buffers. Note that on the Solaris OS for x86 Platforms, each ddi_dma_cookie(9S)
returned for each DMA window, will contain a physical address suitable for use as in constructing a scatter-gather list.
9.5 How can the kernel recognize a non-self-identifying device?
You need to write a realmode driver. See the realmode environment sources and
White Papers ("Solaris Realmode Environment", "Realmode Drivers", and "External
BEF Interface Specification for x86 Realmode DDI").
9.6 How do I make an x86 device bootable?
You need to write a realmode driver. See the realmode environment
sources and White Papers ("Solaris Realmode Environment", "Realmode Drivers", and "External BEF Interface Specification
for x86 Realmode DDI").
Back to Top
|