|
|
This Technote is both a summary and review of existing disk driver
information and a description of disk driver features that
until now have not been generally documented.
This Note is directed at developers of disk drivers and
disk formatting utilities. There is also a section
specifically aimed at application developers who need to
operate on disks directly.
Updated: [Nov 22 1999]
|
Introduction
The Mac OS disk driver architecture has not been
comprehensively documented since Inside Macintosh
II (1985). In the intervening years, disk technology
has changed radically, from 400 KB floppy disks to FireWire,
visiting two different SCSI Managers and four versions of
ATA Manager on the way. Many of these technological changes
have been accompanied by architectural changes for which the
documentation is in obscure
places, was not
generally released, or was just never written.
The technote is an attempt to rectify that oversight. It
serves both to bring together the existing documentation and
to fill in the missing pieces. You can use this technote as
either a reference, an introduction to writing disk
drivers, or just to bring yourself up-to-date on the latest
disk driver advances.
If you are new to Mac OS disk drivers, you should start
with the Disk Driver Basics
section. If you're already familiar with the basics of the
Mac OS disk driver architecture, you may want to start with
the two high-level summaries, one for
disk driver
writers and one for
application developers.
Existing Information
The existing documentation for disk drivers is scattered
through many different Apple documents, interface files, and
code samples. The section
classifies these references based on their
usefulness.
Core References
These large works cover information that you will
definitely need in your driver. Don't start a disk driver
without being familiar with these works:
- Inside Macintosh:
Devices, SCSI
Manager is the core
reference for the classic SCSI Manager programming
interface, introduced with the Mac Plus. It also
describes the Apple partition
map format, used by all
Macintosh computers since the Mac Plus.
- Inside Macintosh:
Devices, SCSI
Manager 4.3 is the core
reference for the SCSI Manager 4.3 programming interface,
introduced with the Quadra 840av. All SCSI drivers
written today should use the SCSI Manager 4.3 programming
interface.
- ATA
Device Software for Macintosh Computers (previously
known as the ATA Device Software Guide) is
the core reference for the ATA Manager, which allows you
to find and control ATA devices connected to the
computer. The "ATA Driver Reference" chapter offers a
useful summary of the Control and Status requests
relevant to a modern Mac OS hard disk driver, although
some of the information is inaccurate and has been
updated in this
document.
- ATA
0/1 Software Developers Guide is a supplement to the
above, and describes the changes required to support
device 0/1 (master/slave) on ATA buses.
- Inside
Macintosh: Files describes the
drive
queue, a key data structure used by all disk drivers.
- Technote 1041,
"Inside
Macintosh: Files Errata" comprises corrections to the
core Inside Macintosh: Files document.
- The Shared Device
Access Protocol specification.
- DTS sample code
RAM
Disk implements the basic framework for a disk
driver. Unfortunately, it does not demonstrate how to
handle requests asynchronously, which is one of the
trickiest things to get right in a disk driver.
- DTS sample code
TradDriverLoaderLib
shows how to correctly install a Mac OS driver
'DRVR'.
- DTS sample code
SCSI
Driver Example demonstrates a fully fledged SCSI
driver that supports both classic SCSI Manager and SCSI
Manager 4.3. It is a useful sample, although it has
decayed a bit in the years since it was last updated
(1994).
- DTS sample code
ATA_Demo demonstrates
how to read blocks from both ATA
and ATAPI disks.
- "DriverGestalt.h" (from the latest
Universal
Interfaces) always contains the most up-to-date list
of Driver Gestalt selectors.
- The MoreDisks module from the DTS sample code library
MoreIsBetter
contains a comprehensive list of all the currently
defined disk driver Control and Status requests, and
where to get more information on how to support them.
Additional Information
These smaller documents contain information that
supplements the above in certain key areas.
- Technote 1098,
"ATA
Device Software Guide: Additions and Corrections" is
the latest errata for the
ATA
Device Software for Macintosh Computers.
- Technote DV 17
Sony
Driver: What your Sony Drives For You documents the
Control and Status requests supported by Apple's standard
floppy disk driver. This is a key reference for disk
driver developers. Floppy disk driver writers should also
read the "MFM Disk Device Driver" chapter of
Apple
Logic Board Design LPX-40 Developer Note (hardware
developer note), which includes information on floppy
disk Control and Status requests that is missing from DV
17.
- Technote DV 22,
"CD-ROM
Driver Calls" documents the Control and Status
requests supported by Apple's standard CD-ROM driver.
This is a key reference for CD-ROM driver developers.
- Technote 1104,
"Interrupt-Safe
Routines" answers the perennial question, can I do X
at interrupt time?
- Technote 1067,
"Traditional
Device Drivers: Sync or Swim" addresses a common
misconception of device driver writers.
- Technote 1040,
"Write
Cache Flushing: Techniques for Properly Handling System
Shutdown" describes how disk drivers should handle
system shutdown.
- Technote ME 09,
"Coping
with VM and Memory Mappings" is probably the best
place for information on ensuring that your device driver is
compatible with virtual memory.
- Technote 1094,
"Virtual
Memory Application Compatibility" contains a description of the Mac
OS VM architecture as a whole, which is useful background
material for device driver writers.
- Designing
PCI Cards and Drivers for Power Macintosh Computers,
pages 110 through 117, documents the Driver Gestalt
mechanism and some new Control requests. This technote
provides clarifications and corrections on
Driver Gestalt and the
mechanism used to boot
from a partition. In addition, the
File Exchange section of this
technote completely replaces the PC Exchange description
in the book.
- Guide
to the File System Manager contains useful background
information about how FSM interacts with disk drivers;
however, the specific recommendations for driver writers
are covered in the File
Exchange section of this technote.
- DTS Q&A OPS 22,
"Notification
Manager Reinitialized During Boot" is an important
tidbit for disk driver developers.
- DTS Q&A DV 34,
"Secondary
Interrupts on the Page Fault Path" describes the
dangers of using secondary interrupts in software that
must service page faults. While the Q&A was written
for SIM developers, its warning is also important for
other page fault path software, such as disk drivers.
Disk drivers must not use secondary interrupts (or,
for that matter, deferred tasks) on the page fault
path.
- Data Structure to
Aid Security and Recovery Software, David Shayer and
Marvin Carlberg, 1991
- The InterruptSafeDebug module
of the DTS sample code library MoreIsBetter can be useful when tracking down nasty
crashing problems in a device driver, especially those
that happen early at startup time.
Obsolete
These documents, as they pertain to disk drivers, are
considered obsolete. This list is provided for completeness only. You
should read the recommended material instead.
- Inside Macintosh
II, "The Disk
Driver", page 211 through 219, documents the basic
interface to a disk driver, include the
kEject (7) Control request, the
kSetTagBuffer (8) Control request, and the
kDriveStatus (8) Status request.
- Inside Macintosh
IV, "The Disk
Driver", page 223 through 224, documents the
kVerify (5), kFormat (6), kTrackCache (9), and kDriveIcon (21) Control requests.
- Inside Macintosh IV, "The SCSI Manager",
page 292 through 293 describes the original partitioning
format used on the Mac Plus and goes on to say, "Since the
driver is called to install itself, it must contain code
to set up its own entry in the unit table and to call its
own Open routine. An example of how to do this can be
obtained from Developer Technical Support." This example
was part of the "SCSI Driver Developer Kit". All of the
information in the kit is available elsewhere. The
specific sample code referenced by the book evolved into
SCSI
Driver Example.
- Inside Macintosh
V, "The Disk Driver",
page 470 through 471, documents the
kDriveIcon (21), kMediaIcon (22), and kDriveInfo (23) Control requests.
- Technote DV 2,
"_AddDrive,
_DrvrInstall, and _DrvrRemove" documented the
AddDrive, DriverInstall, and
DriverRemove system routines. This technote
is now obsolete. AddDrive is documented in
Inside
Macintosh: Files, and DriverInstall, and
DriverRemove are covered by
Inside
Macintosh: Devices, along with
DriverInstallReserveMem. Moreover,
developers of 68K drivers should use
TradDriverLoaderLib to
install their drivers.
- Technote DV 12,
"Our
Checksum Bounced" documents a misfeature of the code
used by the ROM to checksum disk drivers. The technote is
now obsolete. The ROM checksum behavior is described in
Inside
Macintosh: Devices and this technote describes the
checksum algorithm itself.
- Technote DV 13,
"_PBClose
the Barn Door" still contains valid advice for general
device driver writers, although this technote deals with
this topic as it applies to disk
drivers.
- Technote DV 18,
"CD-ROM
Notes (Most Excellent)" contains some interesting
historical information about CD-ROM devices, although
much of the information is now obsolete or covered
elsewhere.
- Power
Macintosh 9500 Computers (hardware developer note)
describes many aspects of the large volume support
(greater than 4 GB support) introduced with that machine.
The large volume support aspects of that developer note
are now obsolete. This technote discusses
large volume support as
it applies to disk drivers. DTS Q&As FL
07
and FL
08
discuss large volume support from the application
perspective.
- The following documents were never released
generally. Their developer-oriented content has been
rolled into this technote.
- "Chainable Drivers and Patches"
- "Ruby Slipper Lite ERS" (large volume support)
- "Bootable CD Developer Kit (Software Developer
Note)"
- "PC Exchange and Large Volume Drivers"
Checklist
for Disk Driver Writers
All of the above is probably overwhelming, so here is a
summary of the most important steps to take to improve the
reliability and compatibility of your disk driver:
- If you do nothing else, you should support
Driver Gestalt.
- You should support the partition map entry features
documented in Secrets
of the Partition Map. Specifically, you should ensure
that your driver is checksummed, supports booting from a
partition, and write your driver signature to the
pmPad field.
- Your driver should support
large volumes,
including booting from large volumes on machines without
large volume support in the ROM by means of the
'ruby' patch.
- You should follow the rules when
installing
and removing your driver and its drive queue
elements. You should also support
close to allow other
developers to remove your driver cleanly.
- If your driver uses
SCSI Manager 4.3 or ATA Manager, it must register itself
with the manager. The documentation for each manager
describes how this is done. If you're using SCSI Manager
4.3, use
SCSICreateRefNumXref.
If you're using ATA Manager, use
kATAMgrDriveRegister.
- You should support the File
Exchange interface. This will allow foreign file
systems to access your disks without any skullduggery.
- You should check that your
private
Control and Status requests follow the rules, both
with respect to Driver Gestalt and virtual memory. This
is harder than you might think.
- You should support
read-verify mode. This
technote explains how to do it easily.
- You may want to support
target
mode in your ATA
driver.
- You may want to support color
icons. Woo hoo!
For Application
Writers
The purpose of a disk driver is to support a generic
interface for accessing block devices. The primary client of
this interface is the File Manager, although it can be used
by other programs. If you're writing a foreign file system,
or just an application that needs something beyond the
standard File Manager programming interface, parts of this
technote may be of interest to you.
- If you need to interrogate a driver about its
capabilities, you should read the section
Driver Gestalt
for Applications.
- If you need to read arbitrary blocks on a volume, you
should read the discussion of the
XIOParam block for
applications, along with the accompanying
hints and
tips.
- If you need to read arbitrary blocks outside of a
volume -- for example, the partition map, or a non-Mac OS
partition -- you should investigate the
File Exchange section of this
technote, especially the section on using the
File Exchange
interface.
- If you need to verify that you have written data to
the disk correctly, you should check out the
read-verify mode section
which describes the easiest way to do this. [Hint: Think
"MoreFiles"!]
- If you need to get color icons for a drive, you can
now call the disk driver to get
them -- although you should probably just call
Icon
Services instead.
In addition, if you're writing a disk formatting utility,
this technote contain invaluable information on the
partition map,
chaining
drivers, patch partitions, and
"hostile" takeovers.
Back to top
Disk Driver Basics
Mac OS communicates with attached devices through
device drivers, which are software plug-ins
that conform to a well-defined structure. The Device Manager
is the original system component used to install, find,
manage, and communicate with device drivers. It exports
routines that can be called by higher level system software,
and by applications. Most of these routines translate
directly into requests to the underlying device driver.
In order to identify different drivers, the Device
Manager assigns each installed driver a unique negative
number, referred to as a driver reference
number. When calling the Device Manager, clients
pass a driver reference number to tell it which driver they
are dealing with.
For a block device to be available to the system, it must
have a disk driver. This is either in the
ROM (for the built-in floppy drive), or loaded at system
startup from a special partition on the disk (SCSI, ATA, and
FireWire devices), or loaded from a system extension (USB
and FireWire devices). In addition, a disk driver can be
loaded when a device is plugged in by either an I/O family
expert (ATA, USB, and FireWire), or by a special utility
program (SCSI). Finally, software can install a disk driver
for a virtual block device which has no obvious physical
presence, such as a RAM disk or disk image. Regardless of
how they are installed, all
disk drivers roughly follow the same rules.
It is important to note the difference between a disk and
a device. A block
device is the entity which reads and writes
data on a disk. A disk is the medium which
actually stores the data. This distinction is unimportant
for fixed disk devices (such as hard disks), but is critical
for removable disk devices (such as floppy drives and
removable cartridge disk devices).
Mac OS always directs block I/O to a software entity
known as a drive. Each disk driver creates
one or more drives and puts them in a system structure called the drive
queue. Each drive queue element
represents a drive, and
contains both the driver reference number and the drive
number. The drive number is a positive
number that uniquely identifies the drive; it is assigned
when the drive is added to the drive queue.
A drive does not necessarily correspond directly to a
given physical device. Rather, the driver decides which
drives to create for the device it controls. In some cases,
there is one drive per physical device. For example, the
built-in floppy disk driver creates a drive for each
attached floppy disk device. However, it is also common for
a driver to create multiple drives for a single device. For
example, the driver for a partitioned hard disk device
creates a drive for each file system partition on the disk.
When the system performs I/O to a drive, it supplies the driver
reference number of the device driver and the drive number
of a drive created by that driver. The Device Manager uses
the driver reference number to find the device driver and
call its entry point. The device driver then uses the drive
number to determine which drive is the target of the I/O
request.
All drive I/O is done is terms of 512-byte logical
blocks. Therefore, all transfers must start at multiple of
512 bytes and be a multiple of 512 bytes long. This is
regardless of the underlying device's block size.
File Manager and Drives
To allow the flexibility of storage required by the user
interface (a hierarchy of folders and files), Mac OS implements another
layer of abstraction, known as the File Manager, on top of the Device Manager
and the drive queue.
A file
system is a mechanism for storing
fine-grained data (files) and meta-data (folders, Finders
attributes, and so on) on a drive. The file system defines
the way this data is stored and the rules for manipulating
it. The File Manager includes built-in support for two file
systems (HFS and HFS Plus) and a plug-in architecture (File
System Manager) for others (AppleShare, DOS FAT, ProDOS ,
UDF, and third-party FSM plug-ins).
The File Manager exports a programming interface defined
in terms of volumes, which
contain directories, files, and meta-data. A
volume is an instance of a file system on a
drive. Each volume is uniquely identified by a negative
volume reference number, which is stored,
along with other data to operate the volume, in a
volume control block (VCB) that is linked
into the system VCB queue. The VCB also
contains the drive number and the driver reference number of
the drive on which the volume is mounted.
The process of making the contents of a drive available
via the File Manager is called mounting a
volume. When the File Manager attempts to mount a volume on
a drive, it calls each of the file systems in turn to determine which one
understands the logical format of the data on the disk in the drive. It then
creates a VCB for that file system on that drive.
The File Manager takes requests to operate on the volume
and passes them to the appropriate plug-in file system,
which reduces them to basic block operations and passes them
to the drive via the Device Manager (using the drive number
and driver reference number stored in the VCB). As far as
the file system is concerned, the drive is its own logical
disk, even though it may only represent a small part of the real
disk.
A drive can exist without having a volume mounted on it.
This happens, for example, if the data format on the drive
is incomprehensible to the installed file systems, or the
volume on the drive has been unmounted. You can still access the data on a
drive that has no volume mounted on it, but only via the
Device Manager interface.
Terminology
In any technical document, it is very important to get
your terminology straight. This is especially important when
talking about disk drivers, where much of the terminology
has been extended over the long, confusing history of the
Mac OS block storage architecture. This technote uses the following terms
throughout.
- disk driver
A software plug-in that implements a hardware
abstraction layer for block devices, like hard disks,
floppy drives, and CD-ROM drives. In Mac OS, a disk
driver must be a Device Manager driver (either a 68K
driver or a native driver).
- 68K driver
A disk driver implemented using the traditional 68K
driver architecture, as documented in
Inside
Macintosh: Devices. A 68K driver is commonly stored
in a resource of type 'DRVR' or in a driver partition.
- native driver
A disk driver implemented using the native
driver model, introduced
with the first generation of PCI Power Macintosh
computers and documented in
Designing
PCI Cards and Drivers for Power Macintosh Computers.
A native driver is commonly stored in a file of type
'ndrv', although native drivers have started
appearing in driver partitions as well.
- driver reference number
An SInt16 that uniquely identifies a
Device Manager driver to the system. Driver reference
numbers are not persistent -- they are assigned when the
driver is added to the unit table -- but some driver
reference numbers are assigned to certain well-known
drivers. Driver reference numbers occupy the same "name
space" as file reference numbers (which identify an open
file). Driver reference numbers are always negative,
while file reference numbers are always positive. Zero is
an invalid driver reference number and an invalid file
reference number.
- unit table
A Device Manager data structure
that lists the installed device drivers (both 68K and
native).
- block device
A block-oriented storage device.
- real block device
A block device that has some obvious physical
presence, such as a floppy drive or a SCSI hard disk
device.
- virtual block device
A block device this has no obvious physical presence,
such as a RAM disk, a disk image, or a network block
device.
- device
Some hardware attached to the computer. In this
context of this technote, this typically means a block
device although, in some
places, the term may be used for any type of
device.
- disk
The actual physical media which holds data. A disk is
made up of blocks, each of which holds a fixed number of
bytes (typically 512). A disk is distinct from a block
device because, in the case of removable disk devices,
the user can insert one of many different disks into the
device.
- disc
A synonym for "disk" that is only used in the context
of CD or DVD discs (where the disk is actually a disc).
- media
See disk.
- drive
A Mac OS software construct used to represent a block
storage entity. A volume is always mounted on a drive.
There may be multiple drives corresponding to a single
disk. Exception: some removable disk devices have been
historically known as drives (for example, floppy drive,
CD-ROM drive). This technote continues to use "drive" in
these contexts, rather than the more cumbersome "floppy
disk device." However, if the word "drive" appears
unqualified, it always refers to the primary definition.
- drive queue
A OS queue which contains all the drive queue
elements known to the system. You can get the head of the
drive queue using the routine GetDrvQHdr.
See
Inside
Macintosh: Files for more details of the
drive
queue and its elements.
- drive queue element
The specific data structure used to represent a
drive. A drive queue element is a structure of type
DrvQEl allocated in the system heap and
placed in the drive queue.
- drive number
An SInt16 which uniquely identifies a
drive. Drive numbers are not persistent; they are
assigned when the drive is added to the drive queue.
Drive numbers occupy the same "name space" as volume
reference numbers. Drive numbers are always positive,
while volume reference numbers are always negative.
- partition
A disk may be divided into a set of contiguous
blocks, each known as a partition. Partitions are
typically either file system partitions (which hold file
system data) or meta-data partitions (which hold
information about the disk, such as the partition map or
the disk's device driver). Not all disks are partitioned,
although a disk must be partitioned to support booting
(except for floppy disks, because the driver for the
built-in floppy disk drive is in the ROM).
- partition map
A data structure, typically at the beginning of the
disk, which describes the partitions on the disk. Most
Mac OS disks are partitioned using the Apple partition
map format, described in
Secrets of the
Partition Map.
- partition map entry
The Apple partition map describes each partition on
the disk using a partition map entry data structure (of
type Partition).
- startup partition
The partition which the user has designated as the
one from which they prefer to boot the system, or the
partition from which the system booted.
- driver partition
A partition which contains a disk driver.
- file system partition
A partition which contains file
system data.
- meta-data partition
A partition which holds information about the disk,
such as the partition map or the disk's device driver.
- partition-based driver
A driver that is loaded from a partition.
- file system-based driver
A driver that is loaded from a file in the file
system, typically in the Extensions folder.
- disk-based driver
Either a partition-based driver or a file
system-based driver. This term is ambiguous and to be
avoided.
- ghost partitioning
A system used on non-512 byte block devices where
partition map entries appear at both 512-byte boundaries
and device block boundaries so that they can be seen by
software using either physical or device blocks.
- I/O family
A component of the Mac OS I/O subsystem that is
responsible for a particular category of devices. A
driver can work within multiple I/O families. Each family
requires certain attributes of the driver (for example,
how it is packaged and the programming interface it
provides to upper layer software) and provides services
for the driver. For example, a FireWire disk driver must
be packaged as a native driver which responds to the
standard disk driver programming interface, and FireWire
provides services to the disk driver, such as SBP-2
utility routines.
- I/O family expert
A component of an I/O family
that seeks out devices of a particular type and registers
them with the I/O family.
- volume
A File Manager software construct that represents a
single, user-visible storage device. Each volume appears
as a icon on the desktop. Each
volume is mounted on a drive, so if the disk has multiple
file system partitions it will also have multiple drives
and hence multiple volumes.
- volume reference number
An SInt16 which uniquely identifies a
volume. Volume reference numbers are not persistent; they
are assigned when the volume is mounted. Volume reference
numbers occupy the same 'name space' as drive numbers.
Drive numbers are always positive, while volume reference
numbers are always negative.
- refNum
This contraction of "reference number" is ambiguous
and is not used in this document. In other documents, it
commonly means either a driver reference number or a file
reference number, depending on context.
- vRefNum
A contraction of volume reference number.
- logical blocks
The block numbering scheme used to access blocks on a
drive. Each logical block contains 512 bytes and the
first block accessible through the drive is block 0. See
Block Translation for
details.
- physical blocks
The block numbering scheme used to access blocks on a
disk. You can derive a physical block number from a
logical block number by adding to
it the start block number of the partition. If the
disk is not partitioned, logical blocks and physical
blocks are identical. Each physical block contains 512
bytes. See Block
Translation for details.
- device blocks
The actual block numbering scheme used by the device
hardware to access data on the disk. Device blocks are
not necessarily 512 bytes big, and the device driver is
responsible for blocking and deblocking to present the
illusion of 512-byte physical blocks to the system. See
Block Translation for
details.
- blocks
When used without qualification in this technote, blocks means
logical blocks.
- sectors
Depending on context, this can
either mean device blocks (for a floppy drive), physical
blocks (for a hard disk device), or logical blocks (in a
volume format specification). To avoid confusion, this
technote avoids the term "sector" in favor of its more
specific synonyms.
- chaining driver
A driver loaded from a partition which performs some
action and then loads the next driver in the driver
chain. The most common chaining driver is Apple's patch
driver.
- driver chain
A sequence of drivers, each in its own driver
partition, that can all be loaded for a particular
expansion bus type (for example,
SCSI or ATA). Each driver chain consists of one or
more chaining drivers and a real driver for the disk. A
disk may contain more than one driver chain if it can be accessed through more
than one expansion bus type.
- patch driver
A chaining driver which applies the patches from a
patch partition and then chains to the next driver.
- patch partition
A meta-data partition containing patches that must be
applied to the system before it can boot. The patches in
the patch partition are applied by the patch driver
before it chains to the real disk driver.
- target mode
PowerBook computers can be placed in target mode,
where the PowerBook's internal hard disk device is
accessible as a hard disk device to other computers on
an expansion bus (typically
SCSI).
- SCSI disk mode
See target mode.
- request
When the Device Manager calls a driver entry point
(Open, Close, Prime, Control, or Status for a 68K driver,
DoDriverIO for native drivers), it passes
the address of a parameter block which describes the
requested operation. This is known as a request. A
request is different from a simple function call in that
the driver may return from this initial call without
completing the request. Specifically, for queued
requests, the request is not complete until the driver
explicitly tells the system so (by calling
IODone for 68K drivers, or by calling
IOCommandIsComplete for native drivers).
- queued request
Synchronous and asynchronous requests are
collectively known as queued requests. This is because
they are queued in the driver's queue (on the
dCtlQHdr) and the driver is marked as busy
while the request is being processed.
- immediate request
Immediate requests are distinct from queued requests
in that they are not placed
in the driver's queue and do not mark the driver as busy.
Back to top
Driver Gestalt
All disk drivers should support Driver Gestalt. Driver
Gestalt is a mechanism whereby the system can query your
driver to determine whether it supports advanced driver
features. In many ways it is similar to the Mac OS Gestalt
Manager, except that the system is querying your driver, not
the other way around.
Your driver should support Driver
Gestalt. If you don't support Driver Gestalt, the
system is in the dark as to which advanced driver features
your driver supports.
Driver Gestalt Reference
The basic reference for Driver Gestalt is
Designing
PCI Cards and Drivers for Power Macintosh Computers,
specifically the "Driver Gestalt" section starting on page
106. However, Driver Gestalt is useful even on non-PCI
computers. Your driver must support Driver Gestalt
regardless of what computer or OS version it is running on.
Designing
PCI Cards and Drivers for Power Macintosh Computers does
not document all of the selectors associated with Driver
Gestalt. The only official, up-to-date list of Driver
Gestalt selectors is the "DriverGestalt.h" header file,
provided as part of Universal Interfaces. When Apple defines
a new Driver Gestalt selector, we add the selector to
"DriverGestalt.h", along with comments that describe how to
implement it.
In the event of a conflict between the written
documentation and "DriverGestalt.h", "DriverGestalt.h" is
correct and the written documentation is wrong. For example,
Designing
PCI Cards and Drivers for Power Macintosh Computers
describes the response of the 'purg' selector
as a Boolean (page 111), whereas
"DriverGestalt.h" correctly describes the response to be of
type DriverGestaltPurgeResponse.
Driver Gestalt Guarantees
By saying that it supports Driver Gestalt, your driver
guarantees certain things to the system, including:
- Your driver will return
controlErr in
response to a Control request with an unrecognized
csCode.
- Your driver will return
statusErr in
response to a Status request with an unrecognized
csCode.
- Your driver will return
controlErr in
response to a Driver Configure request with an
unrecognized selector.
- Your driver will return
statusErr in
response to a Driver Gestalt request with an unrecognized
selector.
- Your driver will not use any
csCodes
below 128 for private Control or Status requests.
Items 3 and 4 in the list above are not documented
clearly in
Designing
PCI Cards and Drivers for Power Macintosh Computers,
although they are implemented by all Apple drivers and are
clearly shown in the various Driver Gestalt samples. This
technote serves to officially document these two additional
requirements.
Driver
Gestalt for Applications
Probably the best way to understand how to issue Driver
Gestalt queries from an application is to look at some
sample code. "Driver Gestalt Demo" is a simple sample that
shows how to issue a few queries. "DriverGestaltExplorer" is
a more comprehensive sample, which is also useful as a
simple test and investigation tool. Both samples are
available as DTS
sample
code.
Summary of Driver Gestalt
All disk drivers should support Driver
Gestalt.
Back to top
Secrets of the Partition Map
A number of features have been added to the Apple partition map since it was
documented in
Inside
Macintosh: Devices. This section describes those
features in detail.
Partition Field Relevance
The description of the
Partition
data type in
Inside
Macintosh: Devices does not explicitly call out that
some fields of the data structure are only relevant for
driver partitions (those whose partition name contains
"Apple" and "Driver"). Specifically, the fields from
pmLgBootStart through to
pmProcessor are only relevant for driver
partitions. Non-driver partitions should set these fields to
zero.
pmParType Possibilities
Inside
Macintosh: Devices documents the
well
known values for the pmParType field of the
partition map entry, namely "Apple_partition_map",
"Apple_Driver", "Apple_Driver43", "Apple_MFS", "Apple_HFS",
"Apple_Unix_SVR2", "Apple_PRODOS", "Apple_Free", and
"Apple_Scratch". This technote describes a number of
additional partition types.
- "Apple_Driver_ATA" -- Holds the device driver for an
ATA device.
- "Apple_Driver_ATAPI" -- Holds the device driver for
an ATAPI device. When it discovers a device on an ATA bus, the ATA
Manager identifies whether a device is ATA or ATAPI and
automatically loads the corresponding driver.
- "Apple_Driver43_CD" -- A SCSI CD-ROM driver suitable
for booting.
- "Apple_FWDriver" -- Holds a FireWire driver for the
device. See Loading
FireWire Drivers for details.
- "Apple_Void" -- A dummy partition map entry, used to
pad out a partition map to ensure the correct alignment
of partition map entries in a
bootable CD-ROM.
- "Apple_Patches" -- Holds a patch partition. The patch
partition architecture is described in
Chaining
Drivers and Patch Partitions.
|
IMPORTANT:
Apple reserves all partition types beginning
with "Apple". Apple expects to add a number of new
partition types in the near future, and your
software should handle these new, reserved partition
types cleanly.
|
pmPartStatus Revealed
Inside
Macintosh: Devices says that the
pmPartStatus field of the
Partition data structure is only used by A/UX,
bits 0 through 7 having a defined meaning and all others
being reserved. This is no longer true.
The following flags are defined in
pmPartStatus field of the
Partition structure. All
bits not defined here are reserved (you should initialize
them to 0 and ignore their value).
enum {
kPartitionAUXIsValid= 0x00000001,
kPartitionAUXIsAllocated = 0x00000002,
kPartitionAUXIsInUse= 0x00000004,
kPartitionAUXIsBootValid = 0x00000008,
kPartitionAUXIsReadable = 0x00000010,
kPartitionAUXIsWriteable = 0x00000020,
kPartitionAUXIsBootCodePositionIndependent = 0x00000040,
kPartitionIsWriteable = 0x00000020,
kPartitionIsMountedAtStartup = 0x40000000,
kPartitionIsStartup = 0x80000000,
kPartitionIsChainCompatible = 0x00000100,
kPartitionIsRealDeviceDriver = 0x00000200,
kPartitionCanChainToNext = 0x00000400,
};
|
Bits 0 through 4 and 6 are still defined as documented in
Inside
Macintosh: Devices. A Mac OS formatting utility should
always set these bit to 1 for file system partitions and
clear them for other partition types.
The second group of bits is used by Apple Mac OS disk
drivers to hold information about file system partitions.
kPartitionIsWriteable
This bit indicates whether the partition is writeable
(1) or write-protected (0). If the bit is clear and your
driver creates a drive queue element to represent this
partition, it should mark the drive queue element as
write-protected. Note that mask has the same value (and
the same semantics) as
kPartitionAUXIsWriteable.
kPartitionIsMountedAtStartup
This bit indicates whether the partition is mounted
at system startup (1) or not (0). If your driver would
otherwise create a drive queue element to represent this
partition at system startup and this bit is clear, it
should not create the drive.
kPartitionIsStartup
This bit indicates whether this is the startup
partition (1) or not (0). This bit must be set for at
most one partition. See
A Partition of Your
Imagination below.
|
Note:
Some third-party disk drivers reverse the sense
of the kPartitionIsMountedAtStartup
bit of pmPartStatus. This is a bug.
Unfortunately, we cannot retroactively fix that bug
on all installed disks, so it is not possible to
look at this flag and determine whether the
partition will be mounted. The most reliable way to
work out whether a partition will be mounted at
startup is by using the
partition
attribute Control and Status requests.
|
The third group of bits provides information about driver
partitions. You may need to read
Chaining
Drivers and Patch Partitions to understand these
descriptions.
kPartitionIsChainCompatible
The driver in this partition supports being loaded by
a chaining driver.
kPartitionIsRealDeviceDriver
This partition contains a driver that actually knows
how to drive the device. Contrast this with the patch
driver, which is chain compatible, but which can only load
patches and then chain to the next driver; it does not
actually contain a disk driver.
kPartitionCanChainToNext
This partition contains a driver that can chain to
another driver. Typically, all drivers in the chain must
have this bit set, except the last one where it is clear.
|
IMPORTANT: Some Apple and most third-party drivers do not
have the chaining flags set correctly, so it is
virtually impossible for your software to rely on
their semantics.
|
Partition Attributes
There are a number of Control and Status requests that
modify the attributes of a partition. A disk driver must
support these requests as described below. A formatting
application can use these requests to modify partition
attributes.
Setting the Startup
Partition
|
Trap |
_Control
|
|
Mode |
Synch, Async |
|
csCode
|
SInt16
|
-> |
kSetStartupPartition (44)
|
|
ioVRefNum
|
SInt16
|
-> |
The drive number of the new startup partition,
or 0 if you wish to specify the startup partition
by block number. |
|
csParam[0..1]
|
UInt32
|
-> |
If ioVRefNum is 0, this is the
physical block number of the partition map entry of
the new startup partition. If
ioVRefNum is not 0, this is ignored. |
|
csParam[2..3]
|
|
-> |
If ioVRefNum is 0, this is the
device containing the new startup partition. This
is in the same format as the SCSIID
field of the
partInfoRec.
If ioVRefNum is not 0, this is
ignored. |
In response to this request, your disk driver must set
the partition described by ioVRefNum and
csParam[0..3] as the startup partition.
Typically this involves setting
kPartitionIsStartup in
pmPartStatus,
which in turn causes your disk driver to place the drive
queue element for this partition first in the drive queue at
system startup.
|
IMPORTANT:
When your driver sets the
kPartitionIsStartup bit for one
partition, it must clear it for all other
partitions. This bit must be set for at most one
partition.
|
Determining Whether a Partition is the Startup Partition
|
Trap
|
_Status
|
|
Mode
|
Synch, Async
|
|
csCode
|
SInt16
|
->
|
kGetStartupStatus (44)
|
|
ioVRefNum
|
SInt16
|
->
|
The drive number of the partition to query, or 0
if you wish to query the partition by block number.
|
|
csParam[0..1]
|
UInt32
|
->
|
If ioVRefNum is 0, this is the
physical block number of the partition map entry of
the partition to query. If ioVRefNum
is not 0, this is ignored.
|
|
csParam[2..3]
|
DeviceIdent
|
->
|
If ioVRefNum is 0, this is
identifies the device containing the partition to
query. This is in the same format as the
SCSIID field of the
partInfoRec.
If ioVRefNum is not 0, this is
ignored.
|
|
csParam[0]
|
UInt16
|
<-
|
Your disk driver must set this to either 0 (this
is not the startup partition) or 1 (this is the
startup partition).
|
In response to this request, your disk driver must set
csParam[0] to
indicate whether the partition described by
ioVRefNum and csParam[0..3] is the
startup partition. Typically this involves testing
kPartitionIsStartup in
pmPartStatus.
The request returns the status that is currently recorded
in the partition map, not whether the system actually
started from this partition.
Specifying That a Partition Should Be Mounted at Startup
|
Trap
|
_Control
|
|
Mode
|
Synch, Async
|
|
csCode
|
SInt16
|
->
|
kSetStartupMount (45)
|
|
ioVRefNum
|
SInt16
|
->
|
The drive number of the partition, or 0 if you
wish to specify the partition by block number.
|
|
csParam[0..1]
|
UInt32
|
->
|
If ioVRefNum is 0, this is the
physical block number of the partition map entry of
the partition. If ioVRefNum is not 0,
this is ignored.
|
|
csParam[2..3]
|
DeviceIdent
|
->
|
If ioVRefNum is 0, this is the
device containing the partition. This is in the
same format as the SCSIID field of the
partInfoRec.
If ioVRefNum is not 0, this is
ignored.
|
In response to this request, your disk driver must set
the partition described by ioVRefNum and
csParam[0..3] to be mounted at startup.
Typically this involves setting
kPartitionIsMountedAtStartup in
pmPartStatus,
which in turn causes your disk driver to place a drive queue
element for this partition in the drive queue at system
startup.
This request modifies the partition map, and hence only
takes effect the next time the system is started. It does
not affect the state of any volume currently mounted on the
partition.
Specifying That a
Partition Should Not Be Mounted at Startup
|
Trap
|
_Control
|
|
Mode
|
Synch, Async
|
|
csCode
|
SInt16
|
->
|
kClearPartitionMount (48)
|
|
ioVRefNum
|
SInt16
|
->
|
The drive number of the partition, or 0 if you
wish to specify the partition by block number.
|
|
csParam[0..1]
|
UInt32
|
->
|
If ioVRefNum is 0, this is the
physical block number of the partition map entry of
the partition. If ioVRefNum is not 0,
this is ignored.
|
|
csParam[2..3]
|
DeviceIdent
|
->
|
If ioVRefNum is 0, this is the
device containing the partition. This is in the
same format as the SCSIID field of the
partInfoRec.
If ioVRefNum is not 0, this is
ignored.
|
In response to this request, your disk driver must set
the partition described by ioVRefNum and
csParam[0..3] to not be mounted at startup.
Typically this involves clearing
kPartitionIsMountedAtStartup in
pmPartStatus,
which in turn causes your disk driver to not place a drive
queue element for this partition in the drive queue at
system startup.
This request modifies the partition map and hence only
takes effect the next time the system is started. It does
not affect the state of any volume currently mounted on the
partition.
Determining Whether a Partition is to be Mounted
|
Trap
|
_Status
|
|
Mode
|
Synch, Async
|
|
csCode
|
SInt16
|
->
|
kGetMountStatus (45)
|
|
ioVRefNum
|
SInt16
|
->
|
The drive number of the partition to query, or 0
if you wish to query the partition by block number.
|
|
csParam[0..1]
|
UInt32
|
->
|
If ioVRefNum is 0, this is the
physical block number of the partition map entry of
the partition to query. If ioVRefNum
is not 0, this is ignored.
|
|
csParam[2..3]
|
DeviceIdent
|
->
|
If ioVRefNum is 0, this is
identifies the device containing the partition to
query. This is in the same format as the
SCSIID field of the
partInfoRec.
If ioVRefNum is not 0, this is
ignored.
|
|
csParam[0]
|
UInt16
|
<-
|
Your disk driver must set this to either 0 (this
partition is not to be mounted) or 1 (this
partition is to be mounted).
|
In response to this request, your disk driver must set
csParam[0] to
indicate whether the partition described by
ioVRefNum and csParam[0..3] is to
be mounted at system startup. Typically this involves
testing kPartitionIsMountedAtStartup in
pmPartStatus.
The request returns the status that is currently recorded
in the partition map, not whether the partition was actually
mounted at startup.
Mounting a Partition Immediately
|
Trap
|
_Control
|
|
Mode
|
Synch, Async
|
|
csCode
|
SInt16
|
->
|
kMountVolume (60)
|
|
ioVRefNum
|
SInt16
|
->
|
The drive number of the partition, or 0 if you
wish to specify the partition by block number.
|
|
csParam[0..1]
|
UInt32
|
->
|
If ioVRefNum is 0, this is the
physical block number of the partition map entry of the
partition. If ioVRefNum is not
0, this is ignored.
|
|
csParam[2..3]
|
DeviceIdent
|
->
|
If ioVRefNum is 0, this is the
device containing the partition. This is in the
same format as the SCSIID field of the
partInfoRec.
If ioVRefNum is not 0, this is
ignored.
|
In response to this request, your disk driver must create
a drive queue element for the partition described by
ioVRefNum and csParam[0..3] (if it
doesn't already have one) and post a "disk inserted" event for
it. It must do this regardless of the state of the
kPartitionIsMountedAtStartup bit in the
partition's
pmPartStatus;
however, the kPartitionIsWriteable bit still
controls whether the drive is writeable.
If there is already a volume mounted on the partition,
the system will ignore the "extra disk inserted" event this
request generates.
Locking a Partition
|
Trap
|
_Control
|
|
Mode
|
Synch, Async
|
|
csCode
|
SInt16
|
->
|
kLockPartition (46)
|
|
ioVRefNum
|
SInt16
|
->
|
The drive number of the partition, or 0 if you
wish to specify the partition by block number.
|
|
csParam[0..1]
|
UInt32
|
->
|
If ioVRefNum is 0, this is the
physical block number of the partition map entry of
the partition. If ioVRefNum is not 0,
this is ignored.
|
|
csParam[2..3]
|
DeviceIdent
|
->
|
If ioVRefNum is 0, this is the
device containing the partition. This is in the
same format as the SCSIID field of the
partInfoRec.
If ioVRefNum is not 0, this is
ignored.
|
In response to this request, your disk driver must lock
the partition described by ioVRefNum and
csParam[0..3]. Typically this involves:
- clearing
kPartitionIsWriteable in
pmPartStatus,
which in turn causes your disk driver to create a
read-only drive queue element for this partition at
system startup, and
- making the drive queue element associated with this
partition read-only. A read-only drive queue element has
bit 7 of the
writeProt field of the drive
queue element set, as described in
Inside
Macintosh: Files,
page
2-85.
Unlocking a Partition
|
Trap
|
_Control
|
|
Mode
|
Synch, Async
|
|
csCode
|
SInt16
|
->
|
kUnlockPartition (49)
|
|
ioVRefNum
|
SInt16
|
->
|
The drive number of the partition, or 0 if you
wish to specify the partition by block number.
|
|
csParam[0..1]
|
UInt32
|
->
|
If ioVRefNum is 0, this is the
physical block number of the partition map entry of
the partition. If ioVRefNum is not 0,
this is ignored.
|
|
csParam[2..3]
|
DeviceIdent
|
->
|
If ioVRefNum is 0, this is the
device containing the partition. This is in the
same format as the SCSIID field of the
partInfoRec.
If ioVRefNum is not 0, this is
ignored.
|
In response to this request, your disk driver must unlock
the partition described by ioVRefNum and
csParam[0..3]. Typically this involves:
- setting
kPartitionIsWriteable in
pmPartStatus,
which in turn causes your disk driver to create a
read/write drive queue element for this partition at
system startup, and
- making the drive queue element associated with this
partition read/write.
Determining Whether a Partition is Locked
|
Trap
|
_Status
|
|
Mode
|
Synch, Async
|
|
csCode
|
SInt16
|
->
|
kGetLockStatus (46)
|
|
ioVRefNum
|
SInt16
|
->
|
The drive number of the partition to query, or 0
if you wish to query the partition by block number.
|
|
csParam[0..1]
|
UInt32
|
->
|
If ioVRefNum is 0, this is the
physical block number of the partition map entry of
the partition to query. If ioVRefNum
is not 0, this is ignored.
|
|
csParam[2..3]
|
DeviceIdent
|
->
|
If ioVRefNum is 0, this is
identifies the device containing the partition to
query. This is in the same format as the
SCSIID field of the
partInfoRec.
If ioVRefNum is not 0, this is
ignored.
|
|
csParam[0]
|
UInt16
|
<-
|
Your disk driver must set this to either 0 (this
partition is not locked) or 1 (this partition is
locked).
|
In response to this request, your disk driver must set
csParam[0] to
indicate whether the partition described by
ioVRefNum and csParam[0..3] is
locked. Typically this involves testing
kPartitionIsWriteable in
pmPartStatus.
|
IMPORTANT:
The polarity of this test is opposite to the
other partition attribute Status requests. If the
partition is locked,
kPartitionIsWriteable is clear in
pmPartStatus.
|
The request returns the status that is currently recorded
in the partition map, not whether the partition was actually
locked at startup. You can determine whether a drive is
currently write-protected by looking at bit 7 of the
writeProt field of the drive queue element, as
described in
Inside
Macintosh: Files,
page
2-85.
pmPad Pearls
A previously undocumented feature of the
Partition structure is the use of the
pmPad field. The first four bytes of this field
is a driver signature, a Mac OS four-
character code that uniquely identifies the driver.
Developers must fill out this field with either a
registered
creator code (which is strongly recommended) or zero.
Drivers that use a registered creator code in this driver
signature field may then use the remainder of
pmPad to hold driver-specific configuration
parameters.
Apple currently uses the following driver signatures:
enum {
kPatchDriverSignature = 'ptDR',
kSCSIDriverSignature = 0x00010600,
kATADriverSignature = 'wiki',
kSCSICDDriverSignature = 'CDvr',
kATAPIDriverSignature = 'ATPI',
kDriveSetupHFSSignature = 'DSU1'
};
|
The values have the following meaning:
kPatchDriverSignature
The Apple patch driver.
kSCSIDriverSignature
The Apple SCSI hard disk driver. [The significance of
this value has been lost in the mists of time.]
kATADriverSignature
The Apple ATA hard disk driver.
kSCSICDDriverSignature
The Apple SCSI CD-ROM driver.
kATAPIDriverSignature
The Apple ATAPI CD-ROM driver.
kDriveSetupHFSSignature
Drive Setup sets the first four bytes of the
pmPad field of
"Apple_HFS" partitions to this value. While this is not,
in the strictest sense, a driver signature, it is
documented here for completeness.
Remember that your disk driver should use its own driver
signature; do not use these values for your own driver.
New Driver Types
Inside
Macintosh: Devices describes how a Mac OS driver is
tagged by having ddType set to 1 in the driver
descriptor map (DDM). There is a constant for this,
sbMac, defined in "SCSI.h". However, there are
other useful constants for this field.
enum {
kDriverTypeMacSCSI = 0x0001,
kDriverTypeMacATA = 0x0701,
kDriverTypeMacSCSIChained = 0xFFFF,
kDriverTypeMacATAChained = 0xF8FF
};
|
The following constants are defined for the
ddType field of the DDM:
kDriverTypeMacSCSI
This is a Mac OS SCSI driver, equivalent to
sbMac. Typically this is only used for the
first driver (the patch driver) in a SCSI driver chain.
kDriverTypeMacATA
This is a Mac OS ATA driver. Typically this is only
used for the first driver (the patch driver) in an ATA
driver chain.
kDriverTypeMacSCSIChained
This is a chained Mac OS SCSI driver. This is used
for the second and subsequent drivers in a driver chain.
kDriverTypeMacATAChained
This is a chained Mac OS
ATA driver. This is used for the second and subsequent
drivers in a driver chain.
The driver type for a chained driver is always the two's
complement of the driver type for the patch driver. For more
information about this relationship, see
Chaining
Drivers and Patch Partitions.
Driver Checksums
Inside Macintosh, Volume V (page 580)
contains an assembly language description of the checksum
algorithm used for the pmBootCksum field of the
partition map, but this algorithm was somehow dropped from
Inside
Macintosh: Devices. As it is now quite difficult to
obtain copies of Inside Macintosh,
Volume V, the algorithm is included below.
; Inputs:
; a0.l -> pointer to driver code
; d1.w -> length of driver code in bytes
; Outputs:
; d0.w -> driver checksum
DoCksum
moveq.l #0,d0 ; initialize sum register
moveq.l #0,d7 ; zero extended byte
bra.s CkDecr; handle 0 bytes
CkLoop
move.b(a0)+,d7 ; get a byte
add.w d7,d0 ; add to checksum
rol.w #1,d0 ; and rotate
CkDecr
dbra d1,CkLoop ; next byte
tst.w d0 ; convert a checksum of 0
bne.s @1 ; into $FFFF
subq.w#1,d0 ;
@1
|
The following is a C equivalent.
static UInt32 ChecksumDriver(void *start, UInt16 bytesToSum)
{
UInt8 *cursor;
UInt16 result;
cursor = (UInt8 *) start;
result = 0;
while ( bytesToSum != 0 ) {
result = result + *cursor;
result = ((result << 1) & 0x0FFFE) |
((result >> 15) & 0x00001);
cursor += 1;
bytesToSum -= 1;
}
if (result == 0) {
result = 0x0FFFF;
}
return result;
}
|
One minor mystery of the pmBootCksum field
is that the field is 32 bits wide but the checksum algorithm
only calculates a 16-bit value. The checksum is always
stored in the least significant 16 bits of
pmBootCksum and the most significant bits are
always set to zero.
Inside Macintosh, Volume V also states that
driver checksumming is only done for if the first four bytes
of the driver's partition map entry pmPartName
field is "Maci". This is only true for SCSI disk drivers.
Other, partition-based disk drivers are always checksummed.
The above algorithm is known as the 16-bit driver
checksum algorithm. This is because the ROM
decrements and tests bytesToSum using a
DBRA instruction (which effectively makes
bytesToSum a UInt16), so only the
first bytesToSum modulo 64 K bytes of the
driver are checksummed. This is not a problem if your driver
is smaller than 64 K bytes. If your driver is larger, you
must be careful for two reasons.
- The code you use to calculate
pmBootCksum must mimic the incorrect
behavior and only checksum your driver up to the driver
size modulo 64 K.
- You may want to include your own checksum in the
driver to ensure that the driver code is intact.
|
Note:
The 16-bit driver checksum algorithm is
identical to the algorithm used by AppleTalk's
Datagram Delivery Protocol (DDP).
In some situations where the ROM loads a driver, it does
not use the 16-bit checksum algorithm. Specifically, later
versions of ATA Manager use a 32-bit driver checksum
algorithm, shown below.
|
static UInt16 ATALoadDoCksum(void *start, UInt32 bytesToSum)
{
UInt8 *startAsBytes;
UInt32 result;
UInt32 i;
startAsBytes = (UInt8 *) start;
result = 0;
for (i = 0; i < bytesToSum; i++) {
result += startAsBytes[i];
result <<= 1;
result |= (result & 0x00010000) ? 1 : 0;
}
return (UInt16) result;
}
|
The key difference is that bytesToSum is now
expressed as a 32-bit quantity, and the algorithm correctly
checksums bytes beyond 64 KB. Further, the 16-bit algorithm
never returns a checksum of 0 (it is mapped to $FFFF), while
the 32-bit algorithm can return a checksum of 0.
Your formatting utility must set pmBootCksum
appropriately, depending on which version of ATA Manager is
loading your driver. Furthermore, the ATA driver loader
mechanism is updated during the system startup process so that
on machines with the old checksum algorithm in ROM, your
driver will need a different checksum depending on whether
it is loaded at start time or after system startup.
Overall, the best solution to this
driver checksum conundrum is:
- make your driver's size less
than 64 KB (if necessary, use a boot strap driver to load
your main driver), and
- if your driver checksums to 0,
add pad bytes until it doesn't.
|
IMPORTANT:
ATA disk drivers are also limited to a size of
255 * block size bytes (just
under 128 KB for 512-byte block devices).
This is because the ROM reads the entire driver
using a single ATA request.
|
A Partition of Your
Imagination
The original Mac Plus SCSI implementation did not allow the user
to specify a startup partition. Obviously this is desired
feature, and disk driver developers came up with a number of
solutions for this problem. Over the years, Apple has
introduced various stages of OS support for booting from a
partition.
Developer-Only Solutions
Prior to Apple providing a solution, developers were
responsible for engineering their own. Developers quickly
noticed that, all things being equal, the Macintosh tends to
boot from the first bootable drive in the drive queue. Therefore,
disk driver writers arranged to add the startup partition's
drive queue element to the drive queue before the non-boot
partitions' element. The disk driver's formatting utility provided
the user interface for specifying the boot partition.
This technique was relatively effective and stimulated
user demand for a reliable mechanism for booting from a
partition.
Partition Attribute Support
Eventually, Apple codified this approach and provided
support for it in the Startup Disk control panel. The
codification came in the form of the
kPartitionIsStartup bit in the
pmPartStatus field of the partition map, along
with a driver Control request,
kSetStartupPartition,
which allows the Startup Disk control panel to instruct the
driver to set that bit.
This standardized the previous non-standard behavior,
although it still is not a perfect solution because of
variances in the way the ROM startup code chooses a drive
from which to start up.
SCSI Manager 4.3
Apple made further refinements to this solution with the
introduction of SCSI Manager 4.3. SCSI Manager 4.3 presented
new problems to the startup code because it allows for
multiple SCSI buses, and it provides full support for SCSI
LUNs. So, when SCSI Manager 4.3
was introduced, Apple also
introduced a new technique for finding the startup
partition, the kdgBoot Driver Gestalt selector.
|
IMPORTANT:
SCSI Manager 4.3 must be in ROM for the
kdgBoot selector to be effective. On
machines, such as the Quadra 700, that can run SCSI
Manager 4.3 but do not have it in ROM, SCSI Manager
4.3 loads out of the System file, too late for it
to affect the startup drive selection.
|
When the user chooses a drive in the Startup Disk control
panel, Startup Disk sends the kdgBoot Driver
Gestalt selector to the disk driver controlling that drive. Startup Disk then records the
response in PRAM. When the Macintosh boots, it iterates
through the drive queue, sending a kdgBoot
request to each drive. When it finds a drive with a value
matching the value in PRAM, it knows that this is the
correct startup drive.
The kdgBoot Driver Gestalt selector is
documented in
Designing
PCI Cards and Drivers for Power Macintosh Computers,
page 113. This documentation is accurate for SCSI drivers.
For ATA drivers, the
DriverGestaltBootResponse response fields
should be set as follows.
extDev
The ATA bus number of the device.
partition
The partition number on the bootable partition on the
device. As described
below, the format of
this field is internal to your disk driver.
SIMSlot
ATA devices must set this to
kDriverGestaltBootATASIMSlot ($20). [This
constant is not currently in Universal Interfaces, Radar
ID 2314693.]
SIMsRSRC
If your driver supports ATA 0/1, you must put 0 or 1
in this field to indicate the number of the device on the
ATA bus. If your driver does not support ATA 0/1, you
must set this to zero. See
ATA
0/1 Software Developers Guide for more details on ATA
0/1 support.
ROM-in-RAM (NewWorld)
The
ROM-in-RAM
architecture, introduced with the iMac, presents new
challenges for the startup device selection process. On a
ROM-in-RAM machine, Open Firmware is responsible for loading
the Mac OS ROM file off the startup partition, and hence
Open Firmware must define the startup partition well before
Mac OS starts to execute. When the Mac OS ROM starts, it
continues booting from the startup partition chosen by Open
Firmware to avoid the potential user confusion of loading
the Mac OS ROM from one disk and the system software from another.
Open Firmware synthesizes the traditional Macintosh
startup process, including:
- Startup drive selection algorithm -- Open Firmware
implements the traditional
startup drive selection algorithm. It turns out that this
algorithm is very complex, although the gist of it is:
- if a "snag" key is held
down, try booting from the corresponding
device,
- try booting from the default drive (if any),
- then try booting from other drives.
- CODS -- Holding down command-option-delete-shift
(CODS) prevents the Open Firmware from booting from the
default drive.
- C for CD-ROM -- Holding down the C key forces Open
Firmware to boot from the CD-ROM device. This was
previously implemented by the
"snag" patch but is
implemented by Open Firmware in ROM-in-RAM computers.
- Flashing question mark -- If no startup device is
available, Open Firmware displays the traditional
"flashing question mark" icon (although, in deference to
the fact that ROM-in-RAM computers do not have floppy
drives, it flashes the question mark inside a folder icon
instead of a floppy disk icon).
On ROM-in-RAM computers, the selected default startup
device is held in an Open Firmware configuration variable
boot-device. This configuration variable holds
an Open Firmware path to the default startup device. The
Startup Disk control panel generates a path based on the disk
driver's response to various Driver Gestalt queries.
It is impossible for Open
Firmware to completely mimic the startup drive selection
algorithm when it comes to selecting a startup partition.
When booting from a partition, boot-device
contains the Open Firmware partition number of the
startup partition. Unfortunately, there is no reliable way
to get this from a disk driver with commonly implemented
Driver Gestalt queries.
|
Note:
You might think that the partition
field of the DriverGestaltBootResponse
would do the trick; however, this field is defined
to be opaque to the system. "Designing PCI Cards
and Drivers for Power Macintosh Computers"
explicitly states:
|
The partition field
enables the selection of a single partition on a
multiply partitioned device as the boot device. It
is not interpreted by the ROM or the Startup Disk
'cdev' [sic], so the driver can choose
a meaning and a value for this field.
It turns out that different disk drivers use
different values for the partition
field. Apple disk
drivers set this to be the block number of the
partition map entry for the partition, but some third-party drivers use
other techniques, such as recording 1 for the first
HFS partition, 2 for the second HFS partition, and
so on. The upshot of this is that Startup Disk is
unable to use this field reliably to set the
partition number in boot-device.
Prior to Mac OS 9.0, the Startup Disk control panel used
tricky heuristics to allow booting from a partition with
Apple disk drivers as a temporary measure to solving this
problem. The long-term solution; however, is for disk
drivers to support a set of new Driver Gestalt queries,
which return exactly the information Startup Disk needs to
set boot-device. The required Driver Gestalt
selectors (kdgDeviceReference,
kdgNameRegistryEntry,
kdgOpenFirmwareBootSupport, and
kdgOpenFirmwareBootingSupport) are described in
"DriverGestalt.h" in Universal Interfaces 3.3.
|
Note:
Your driver only
need support the kdgNameRegistryEntry Driver Gestalt selector if your
device has an obvious Name Registry node. For
devices with no Name Registry node (SCSI), or where
the Name Registry node can be tricky to find (ATA),
it is reasonable to just return statusErr.
|
Back to top
Non-512 Byte Block Devices
The original Mac OS disk driver architecture assumed that
all block devices would use 512-byte blocks. Supporting
block devices with a different block size is relatively
simple, although it gets more complicated if you want to
boot from such a device. Non-512 byte block device support
is most important for CD-ROM drivers, which use a 2-KB block
size.
Just the Basics
The basic rule for supporting non-512 block devices on
Mac OS is that the disk driver is responsible for blocking
and deblocking all I/O requests to a drive. This discussion
assumes that the device block size is an integer multiple of
512, although similar algorithms work for weird device block
sizes.
Block Translation
The File Manager makes an I/O request in terms of
512-byte logical block numbers on a
particular drive. The disk
driver is responsible for translating the logical block
number of the request to an actual block number on the
drive. If the disk is partitioned, the first step of this
translation is to add the offset of the partition to the
logical block number; this generates the physical
block number. If the device uses 512-byte blocks,
the physical block number is the actual block number of the
data on the disk. If the device uses non-512 byte blocks,
the disk driver must do a further translation, converting
the physical block number to a device block
number by dividing the physical block number by the
number of 512-byte blocks in each physical block.
In addition, the disk driver must block/deblock the
request. If the physical block number, or the number of
blocks to transfer, is not evenly divisible by the device
block size, the disk driver must
transfer partial blocks to and from the disk.
The following diagrams shows the entire translation
process for two partitions on a 2 KB block device. All
numbers on the diagram are in the units labeled in the left
column. For example, partition 1 is a 50 MB partition which
extends from 0 to 100 mega logical blocks (512-byte blocks),
40 to 140 mega physical blocks (also 512-byte blocks), and
10 to 35 mega device blocks (2 KB byte blocks).

Implementation Notes
A disk driver typically deblocks a request by breaking it
into three components. The leading
component consists of all the requested physical blocks up
to the first device block boundary. The leading component is
empty if the requested physical blocks start on a device
block boundary.
The main component consists of all the
requested physical blocks which are fully encompassed by
device blocks. The main component may be empty if the
transfer is short. The main component is transferred
directly from between the client buffer and the disk.
Finally, the trailing component consists
of all the requested physical blocks of the transfer which
fall after the last block of the main component. The
trailing component is empty if the physical block number
plus the number of physical blocks to transfer falls on a
device block boundary.
Because you can't transfer a sub-block size request, the
leading and trailing components must be transferred through
a temporary buffer. You should allocate this temporary
buffer when your driver is opened. As the leading and
trailing components are always less than one device block
(otherwise they would be part of the main component), the
temporary buffer need only be as big as a device block. If
your device driver is single threaded, you need only
allocate a single temporary buffer. If your driver is
multi-threaded, you must allocate as many temporary buffers
as you allow threads of execution within your driver, or
internally serialize the use of the temporary buffer.
The leading and trailing components are read by
transferring the device block to the temporary buffer and
then copying the appropriate data out of the temporary
buffer to the client buffer. The leading and trailing
components are written by first reading the current contents
of the device to the temporary buffer, then copying the new
data from the client buffer to the temporary buffer, then
writing the temporary buffer to the device.
The following illustration shows how misaligned read is
transferred to the client buffer:

Performance Considerations
The above algorithm is obviously inefficient if transfers
are misaligned, that is, if the leading and trailing
components are not empty. Misaligned writes are even more
expensive than misaligned reads because the disk driver must
do an extra I/O to pre-fill the temporary buffer with the
existing contents of device block. Worse yet, a misaligned write that has both
leading and trailing components takes five I/O operations (read leading, write leading, write main,
read leading, write leading).
There are a number of ways to avoid misaligned transfers:
- Your formatting utility should always start
partitions (especially file system
partitions) on device block boundaries.
- File system clients can issue a Driver Gestalt
kdgMediaInfo request to determine the device
block size and ensure that transfers are aligned. This is
particularly important for
write requests.
- As a rule, volume formats should use the above
technique to ensure that their allocation blocks are
correctly aligned. At a minimum, volume formats should
align allocation blocks on 2 KB boundaries to accommodate
the most common cases, namely CD-ROM, DVD-ROM/RAM, and
magneto-optical devices.
It is strongly recommended that your disk driver cache at
least one device block. Many Mac OS programs will transfer
data in sequential 512-byte chunks. By caching a single
device block, your driver can radically reduce the average
time taken to service these requests.
Booting From Non-512 Byte
Block Devices
This section is not yet finished and has been omitted in the interests of shipping
an initial version of the technote. A future revision of
this technote will cover booting from a non-512 byte block
device. If you are interested in this topic, please email
DTS and ask for a
prerelease draft of this section.
Back to top
Large Volume Support
When Mac OS originally shipped, it supported volume sizes
up to 2 GB. This limit was shared by a number of system
components, including the File Manager and disk drivers.
Large volume support was introduced in two phases.
- System 7.5 introduced support for volumes larger than
2 GB, up to a size of 4 GB. The semantics of two
programming interfaces were changed to accomplish this.
PBHGetVInfo does not return the true
size of the volumes greater than 2 GB; the volume size
and free space are always clipped to 2 GB or less.
- The
dCtlPosition field of the Device
Control Entry (DCE) was redefined as an unsigned
quantity.
- System 7.5.2 introduced support for volumes larger
than 4 GB, up to a size of a 2 TB. This required two new
programming interfaces.
PBXGetVolInfo returns the volume size
and free space as a 64-bit quantity.
- The I/O parameter block passed to disk drivers was
extended to include a 64-bit field,
ioWPosOffset, which supplants
dCtlPosition.
The changes to the File Manager programming interfaces
are not relevant to this technote; they are documented in
DTS Q&A FL 08,
"Determining
Volume Size." This section describes the changes to the
disk driver interface.
Large Volume Interfaces
Supporting volumes between 2 GB and 4 GB was simply a
matter of redefining the dCtlPosition field of
the DCE and the ioPosOffset field of the
IOParam structure to be unsigned longs
(UInt32).
|
IMPORTANT:
While the semantics of these fields have been
changed to unsigned, Universal Interfaces (as of
the current version, 3.3) still define the fields
as signed. Your code must type cast the fields as
appropriate. |
To support volumes larger than 4 GB, a new extended I/O
parameter block (XIOParam) structure was
defined. The original and extended I/O parameter blocks are
distinguished by the kUseWidePositioning bit of
the ioPosMode field (clear for original, set
for extended).
The C definition of the extended I/O parameter block is
given below. The key difference is the addition of the
ioWPosOffset field, a signed 64-bit quantity
which contains the offset of the request.
|
IMPORTANT:
The extended I/O parameter block must only be
used for _Read or _Write
requests to device drivers. It must not be used for
accessing files. The following description assumes
this restriction to simplify the text.
|
|
Note:
This structure was previously only documented
in the
Power
Macintosh 9500 Computers hardware developer
note. The description here is not only easier to
find, but updated and more accurate.
|
struct XIOParam {
QElemPtr qLink;
shortqType;
shortioTrap;
Ptr ioCmdAddr;
IOCompletionUPP ioCompletion;
OSErrioResult;
StringPtr ioNamePtr;
shortioVRefNum;
shortioRefNum;
SInt8ioVersNum;
SInt8ioPermssn;
Ptr ioMisc;
Ptr ioBuffer;
long ioReqCount;
long ioActCount;
shortioPosMode;
wide ioWPosOffset;
};
typedef struct XIOParam XIOParam;
typedef XIOParam *XIOParamPtr;
|
For software making extended I/O
requests, the fields are defined as follows:
qLink
qType
ioTrap
ioCmdAddr
Used internally by the Device Manager.
ioCompletion
For asynchronous requests, you must either set this
field to zero or set it to a universal procedure pointer
for your completion routine. For synchronous requests,
this field is ignored.
ioResult
On completion this field contains the result of the
request, which is either noErr (0) or a
negative error code. The Device Manager guarantees that
this field will be set to ioInProgress (1)
until the request is complete.
ioNamePtr
Ignored for _Read and
_Write requests.
ioVRefNum
You must set this field to the drive number of the
drive you wish to read or write.
ioRefNum
You must set this field to the driver reference
number of the device driver controlling the drive you
wish to read or write.
ioVersNum
ioPermssn
ioMisc
Ignored for _Read and
_Write requests.
ioBuffer
You must set this to point to a data buffer from
which data is written, or to which data is read.
ioReqCount
You must set this field to the number of bytes you
wish to read or write. For disk driver requests, this
must be a multiple of 512 bytes.
ioActCount
On completion this field contains the number of bytes
of data that were actually
transferred.
ioPosMode
You must set this field to
kUseWidePositioning to indicate that this is
a wide request. All wide requests use a positioning mode
of fsFromStart. You must not specify any
other positioning mode (fsAtMark,
fsFromLEOF, or fsFromMark). You
may also specify
rdVerifyMask for
read-verify mode,
noCacheMask to request that
the data not be placed in the cache, or
pleaseCacheMask to request that data be
placed in the cache.
ioWPosOffset
You must set this field to the offset (in bytes) from
the beginning of the disk where the transfer should
begin. For disk driver requests, this must be a multiple
of 512 bytes.
For disk drivers servicing an extended I/O request, the fields are defined as
follows:
qLink
qType
Used internally by the Device Manager.
ioTrap
Your driver must test bit 0 of this field to
determine whether the request is a _Read
(bit 0 clear) or a _Write (bit 0 set). It
must also test noQueueBit (bit 9) to
determine whether the request is immediate (bit 9 set) or
not. If your driver does not support immediate requests,
it must fail the request with a paramErr.
Your driver must not test
asyncTrpBit (bit 10) to determine whether
the request is synchronous or asynchronous. Instead, it
should handle all requests as if they were made
asynchronously. See Technote 1067
Traditional
Device Drivers: Sync or Swim for details.
ioCmdAddr
Used internally by the Device Manager.
ioCompletion
The Device Manager IODone routine will
do the right thing with this field. Your driver should
ignore this field and handle all requests as if they were
made asynchronously. See Technote 1067
Traditional
Device Drivers: Sync or Swim for details.
ioResult
Your driver must not read or write this field. Your
driver sets this field implicitly when it calls
IODone. When your driver has finished a
queued request, it should call IODone to
signal that the request is complete. IODone
performs a number of actions, one of which is to set this
field to the error status you passed to the routine in
register D0. Your driver must pass a non-positive
error status to IODone.
ioNamePtr
Your driver must ignore this field.
ioVRefNum
Your driver must use this field to determine which
drive is the target of the request. If your driver does
not control a drive with this drive number, it must
complete the request with nsDrvErr.
ioRefNum
Your driver may look at this field to determine the
driver reference number of the request. This may be
useful if the same code is used for multiple device
drivers (see Code Sharing).
ioVersNum
ioPermssn
ioMisc
Your driver must ignore these fields.
ioBuffer
Your driver must transfer data to or from the buffer
pointed to by this field.
ioReqCount
Your driver must attempt to transfer the number of
bytes specified in this field. Your driver may fail a
request (with paramErr) if this is not a
multiple of 512 bytes.
ioActCount
Before completing the request, your driver must set
this field to the number of bytes that were actually
transferred.
ioPosMode
Your driver must test the
kUseWidePositioning bit to determine whether
this is a wide request, as described in the next section.
If it is a wide request, your driver must ignore the
bottom 2 bits of this field (that is,
fsFromStart, fsAtMark,
fsFromLEOF, and fsFromMark) and
use ioWPosOffset to determine the offset
into the drive for the transfer. Your driver may choose
to honor the
rdVerifyMask,
noCacheMask, and
pleaseCacheMask in the traditional way.
ioWPosOffset
Your driver must transfer data from this offset (in
bytes) into the drive. Your driver may fail a request
(with paramErr) if this is not a multiple of
512 bytes. If ioWPosOffset is negative or
ioWPosOffset plus ioReqCount is
beyond the end of the drive, your driver must fail the
request with a paramErr.
Supporting Large Volumes in Your Driver
To support large volumes correctly, your driver must
implement the following:
- Your driver must return true in response to the
kdgWide Driver Gestalt selector. You may
want to use the
GetDriverGestaltBooleanResponse macro to
ensure that you set the correct response byte in the
parameter block.
- When handling all
_Read or
_Write requests, your driver must check
whether the kUseWidePositioning flag is set
in ioPosMode. If it is, you must cast the
parameter block to an XIOParam and do the
I/O at the 64-bit offset specified in
ioWPosOffset. This type of request is known
as a wide request.
- If
kUseWidePositioning is not set, your
driver must do the I/O at the offset specified by
dCtlPosition. You must cast this signed
value to an unsigned quantity (UInt32) to
correctly handle offsets from 2 GB to 4 GB. This type of
request is known as a narrow request.
There are some important caveats of which you should be
aware.
- There is no guarantee that the system will check with
Driver Gestalt before issuing a wide request. The system
expects that any driver controlling a drive larger than 4 GB will
respond to wide request correctly. Similarly, the system
expects that a driver controlling a drive whose size is
between 2 GB and 4 GB is smart enough to treat
dCtlPosition as unsigned.
- There is no guarantee that the system will always use
wide requests when talking to a drive larger than 4 GB.
In fact, the system currently decides on a
request-by-request basis whether to use a wide or a
narrow request, based on the request's offset on the
drive. However, you must not rely on this behavior; you
must handle wide requests to offsets less than 4 GB
correctly.
- The
dCtlPosition field of the DCE is a
32-bit quantity, thus it cannot accurately reflect the
position of the current I/O beyond the 4 GB boundary. You
should ignore dCtlPosition for wide requests
and use it only for narrow requests.
Notes
for Developers Calling Disk Drivers
If you're writing software that issues _Read
or _Write requests to a disk driver, you must
be careful to avoid some common pitfalls. Specifically, you
should follow the recommendations given below.
- You should always use an
ioPosMode of
fsFromStart when calling a disk driver.
Because dCtlPosition cannot accurately
reflect the position beyond 4 GB, other positioning modes
do not work as expected in all cases.
- Before issuing a wide request, you should call Driver
Gestalt to determine whether the driver supports wide
requests.
- If the driver supports wide requests, you may choose
to always use wide requests for that driver. However, for
maximum compatibility, DTS recommends that you take the same approach as the
system by deciding to use a wide or narrow request based
on the offset into the drive.
The following code snippet implements these
recommendations.
static void SetWidePosOffset(UInt32 blockOffset, XIOParamPtr pb)
// Set up ioPosMode and either ioPosOffset or ioWPosOffset for a
// device _Read or _Write.
{
pb->ioWPosOffset.lo = blockOffset << 9; // convert block number
pb->ioWPosOffset.hi = blockOffset >> 23; // to wide byte offset
if ( pb->ioWPosOffset.hi != 0 ) {
// Offset on drive is >= 4G, so use wide positioning mode
pb->ioPosMode = fsFromStart | (1 << kWidePosOffsetBit);
} else {
// Offset on drive is < 4G, so use regular positioning mode,
// and move the offset into ioPosOffset
pb->ioPosMode = fsFromStart;
((IOParam *)pb)->ioPosOffset = pb->ioWPosOffset.lo;
}
}
|
In addition, you should never call
PBReadImmed or PBWriteImmed on a
disk driver unless you know, in advance, that the disk
driver supports such requests. Many disk drivers fail to
handle Immediate requests properly. Because immediate
requests result in the disk driver possibly being reentered,
these problems are hard to detect and debug.
Back to top
How the ROM Loads SCSI and ATA Drivers
This section describes how the ROM loads SCSI and ATA
drivers from a driver partition. Understanding this process
is critical to an understanding of the
chaining driver
architecture, and useful for general disk driver
writers.
|
Note: This discussion only applies to computers with
built-in support for SCSI or
ATA, and the drivers loaded from devices
attached to those buses. It does not apply to the
Macintosh 128 and 512, which can only boot through
the floppy drive interface and do not support
partition-based drivers. Nor does it
apply to drivers for modern I/O buses, such as USB
and FireWire.
|
When a Macintosh boots, code in the ROM scans each SCSI and ATA bus for block devices in
a bus-specific manner. Once it has found a potentially
bootable block device, the ROM attempts to load a driver
from that device. The ROM executes the following procedure
to load a driver.
- It first reads device
block 0 of the disk. This is the driver descriptor map
(DDM) and is structured as the
Block0 data
type defined in "SCSI.h". It checks that block 0 is a
valid DDM by comparing the sbSig field to
sbSIGWord ($4552 or 'ER'). If
the DDM is not valid, the ROM ignores the device.
- It then reads device
block 1 of the disk and looking for the first entry of
the partition map. A partition map entry is represented
by the
Partition data structure in "SCSI.h".
For the partition to be recognized, the
pmSig field must be
newPMSigWord ($5453 or 'PM').
The ROM uses the pmMapBlkCnt field of this
first partition to determine the size of the partition
map as a whole.
- The ROM then searches the DDM for the first driver
that is compatible with this
bootable bus. The DDM contains an array of
DDMap structures. The key field in this
structure is ddType, which identifies the
type of driver defined by the structure. If the device is
attached to a SCSI bus, the ROM looks for a
DDMap whose ddType is
kDriverTypeMacSCSI. If the device is
attached to an ATA bus, the ROM looks for a
DDMap whose ddType is
kDriverTypeMacATA.
- The ROM then searches (by
reading consecutive device blocks) the partition
map for the chosen driver's partition map entry (whose
pmParType starts with
"Apple_Driver" and whose pmPyPartStart
equals the ddBlock |