Advanced Search
Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

ADC Home > Reference Library > Technical Notes > Legacy Documents > Hardware & Drivers >

Legacy Documentclose button

Important: This document is part of the Legacy section of the ADC Reference Library. This information should not be used for new development.

Current information on this Reference Library topic can be found here:

The Monster Disk Driver Technote

CONTENTS

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.

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:

  1. Your driver will return controlErr in response to a Control request with an unrecognized csCode.
  2. Your driver will return statusErr in response to a Status request with an unrecognized csCode.
  3. Your driver will return controlErr in response to a Driver Configure request with an unrecognized selector.
  4. Your driver will return statusErr in response to a Driver Gestalt request with an unrecognized selector.
  5. 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.



Note:
Many of these Control and Status requests were previously documented in Designing PCI Cards and Drivers for Power Macintosh Computers, page 113 through 114, and ATA Device Software for Macintosh Computers. The description herein replaces both of these documents. The old documents fail to describe the DeviceIdent parameter to these routines, nor do they clarify that csParam[0..1] is a partition map entry address.



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.

  1. 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.
  2. 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:
    1. if a "snag" key is held down, try booting from the corresponding device,
    2. try booting from the default drive (if any),
    3. 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).

tn1189.gif.1

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:

tn1189.gif.2

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.

  1. 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.
  2. 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.

  1. 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.
  2. 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.
  3. 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.
  4. 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