Q: I'm writing a native driver (a SCSI SIM)
that controls DMA hardware and needs to use partial preparation. How
can I do this without breaking the rules described in Table 9-2 of
"Designing PCI Cards and Drivers for Power Macintosh Computers"?
A: The short answer is that you can't do this
without breaking the rules, but it's OK to break the rules in this
case. A native SCSI SIM on the traditional Mac OS must call
PrepareMemoryForIO at native hardware-interrupt level,
and it is safe to do so. This is obviously going to require some
explanation. Hold on to your hats!
For background information on the problem, you should read the
article
The
New Device Drivers: Memory Matters (develop 24). This article
describes the official algorithm for implementing partial preparation
mechanism in the native driver model. However, there are two problems
with this algorithm:
- It doesn't work (in practice) because it relies on software
interrupts, which are not implemented on the traditional Mac OS.
See
Technote
1104: Interrupt Safe Routines for a discussion of this
restriction.
- It doesn't work (in theory) for devices on the page fault
path. This is because the algorithm requires the use of software
interrupts -- which can cause a page fault -- and a device on the
page fault path is not allowed to cause a page fault. This
restriction is described in the develop
article itself.
So the algorithm described in
The
New Device Drivers: Memory Matters is not valid for SCSI SIMs on
the traditional Mac OS. There is, however, a relatively simple
workaround that does work in these circumstances. In the algorithm
described after Figure 2 of the develop
article, you can compress steps 2, 3 and 4 into one step,
eliminating the use of secondary-interrupt and software-interrupt
level execution. As a result, your SIM calls
PrepareMemoryForIO at native hardware-interrupt time.
This is explicitly outlawed in Table 9-2 of "Designing PCI Cards and
Drivers for Power Macintosh Computers," but is the only way that
works.
The rest of this Q&A is a justification of why this works. To
understand this discussion, you may need to reference
Technote
1094: Virtual Memory Application Compatibility, which contains a
comprehensive explanation of how virtual memory works on the
traditional Mac OS.
On the traditional Mac OS, PrepareMemoryForIO is
actually implemented as a wrapper around standard Virtual Memory
Manager routines. The specific routines of interest are
LockMemory and GetPhysical . The
implementation of PrepareMemoryForIO is, in itself,
interrupt-safe; thus, calling PrepareMemoryForIO is
allowed at any time calling the underlying Virtual Memory
Manager routines is allowed.
Note: On Mac OS 8.5 or higher, PrepareMemoryForIO
will call LockMemory or
LockMemoryForOutput , depending on the direction
of the request, as specified in the options
field of the IOPreparationTable . However, in
this context we can treat LockMemory and
LockMemoryForOutput as synonyms and ignore this
complication.
|
Technote
1104: Interrupt Safe Routines describes the interrupt-safe nature
of GetPhysical and LockMemory . To summarize
the discussion in that Technote, GetPhysical is always
interrupt-safe and LockMemory is interrupt-safe if
either a) paging is safe, or b) you can guarantee that locking the
memory will not cause any page faults.
In the case of a SCSI SIM, the traditional Mac OS guarantees that
any transfer buffer passed to the SIM is held resident in memory.
This guarantee is maintained by two mechanisms:
- The Virtual Memory Manager holds any transfer buffer passed to
a SCSI device driver. The details of this mechanism are covered in
the
VM
Implementation Details section of
Technote
1094.
- The programming rules for SCSI Manager require that any
program making direct SCSI calls must hold all transfer buffers
passed to SCSI Manager. This requirement is explicitly spelt out
in the
Direct
SCSI Manager or ATA Manager Calls section of
Technote
1094.
So, as the author of a SCSI SIM, you are guaranteed that transfer
buffers passed to your SIM are held resident. This implies that it's
always safe to call LockMemory on those transfer
buffers, even at hardware-interrupt time. This, in turn, implies that
it is always safe to prepare those transfer buffers using
PrepareMemoryForIO , even at hardware-interrupt time.
|