Booting Mac OS X
This page contains a brief description of the Mac's firmware (analogous to the PC BIOS in many respects), the bootloader, and the typical Mac OS X boot up sequence. There are significant differences between how older (68k, "Old World" PowerMacs) and newer (everything currently, but essentially "New World" machines with Open Firmware 3.x that load ROM from a file) boot. The discussion here applies to the newer systems.
The firmware is not part of Mac OS X, but it plays an important role in the operation of the machine, and is useful in debugging. Hence, we discuss it here.
Open Firmware
Background
Open Firmware (IEEE-1275 Standard for Boot Firmware: Core Requirements and Practices) is a non-proprietary, platform (CPU and system) independent boot firmware. Similar to a PC's BIOS, Open Firmware is stored in ROM and is the first stored program to be executed upon power-up.
An Open Firmware implementation is based on the Forth programming language, in particular, the FCode dialect (FCode is an ANS Forth compliant dialect that supports compilation of FCode source to bytecode). Apple and Sun are two prominent computer system makers that use implementations of Open Firmware in their systems (Sun's trademark is called OpenBoot). The Open Firmware Working Group's home page is hosted at various places, including Apple and Sun.
Thus, the firmware is implemented in Forth, and stored in the ROM as FCode bytecode. Device drivers that are required during system startup are also implemented similarly. Such drivers usually exist in the expansion ROM of expansion cards that are needed before the operating system has loaded.
Interaction
You can enter Open Firmware by pressing the key combination cmd-opt-O-F
just as you power on a Macintosh. The cmd
key is the one with the Apple logo, and the opt
(option) key is the same as the alt
key. You should see a welcome message and some other verbiage, and should be dropped into a prompt like the following:
ok 0 >
You can continue booting the machine by typing mac-boot
, or shut it down by typing shut-down
.
Even though this Forth "shell" supports reasonable (for a BIOS) command line editing (you can use ctrl-a
to go to the beginning of a line, ctrl-e
to go to the end, ctrl-u
to erase a line, the up-arrow key for history, etc.), you would find it more convenient (particularly if you are trying to write any code in the firmware) to access a Mac's Open Firmware from another (arbitrary) computer, over the network. Here is the command sequence to do this (everything is typed at the Open Firmware prompt, unless stated otherwise):
0 > dev /packages/telnet
Note that upon success, Open Firmware prints the string "ok
" on the same line as you press <return>. In the examples on this page, if you see ok
, remember that it is printed by Open Firmware and you are not supposed to type it in (it's not a valid Open Firmware word anyway).
If your Mac's Open Firmware includes the telnet
package, you would see:
0 > dev /packages/telnet ok
If you do get an ok
, you can run a TELNET server on it:
" enet:telnet,10.0.0.1" io
This would run a TELNET server on the machine with IP address 10.0.0.1
(you can and should choose any appropriate address). Thereafter, you can connect to Open Firmware on this machine using a TELNET client - say, from a Windows machine. See The Towers of Hanoi in Open Firmware for a programming example.
Note that current (at least G4 and above) Apple computers come with Ethernet ports that are auto-sensing and self-configuring, so you do not need a cross-over cable to connect it directly to another computer (no hub is required, etc.).
Examples
1. The following command prints the device tree:
0 > dev / ls ff880d90: /cpus ff881068: /PowerPC,750@0 ff881488: /l2-cache ff882148: /chosen ff882388: /memory@0 ff882650: /openprom ff882828: /client-services ... More [<space>,<cr>,q,a] ? _
2. The following command gives you information about installed RAM:
0 > dev /memory .properties ok name memory device_type memory reg 00000000 10000000 10000000 10000000 slot-names 00000003 SODIMM0/J25LOWER SODIMM1/J25UPPER ... dimm-types DDR SDRAM DDR SDRAM dimm-speeds PC2700U-25330 PC2700U-25330 ...
The machine in the above command (a PowerBook G4 15, although that is not relevant) has two PC2700 DDR SDRAM chips installed. The two pairs of numbers against reg
are specify the starting address and size of the chips. Thus, the first RAM chip starts at address 0x0000000
and has a size 0x10000000
(which is 256 MB). The second chip starts at 0x1000000
(256 MB) and has a size 256 MB. The total RAM is thus 512 MB.
If you need to reduce the installed RAM size (as seen by Mac OS X) for any reason, without actually having to remove a RAM stick (or you want to simulate an arbitrary size that's less than the total installed RAM), you can actually delete the reg
entry using the delete-property
command, and specify your own reg
. Referring to the previous example of the 512 MB PowerBook, the following command essentially disables the second RAM stick (note that this change is not written to NVRAM - it is transient - once you reboot, the other chip will be detected and used as before):
0 > " reg" delete-property ok 0 > 0 encode-int 10000000 encode-int encode+ " reg" property ok
It must be kept in mind though that the reg properties can change from machine to machine, or more likely with architectural changes (for example, the format changed with the PowerMac G5). A less adventurous and more appropriate way to limit visible RAM is to use the maxmem
boot argument, such as at the shell prompt:
# nvram boot-args="maxmem=128"
3. The following command sequence shows you various information on the machine's CPU(s):
0 > dev / ok 0 > dev /cpus ok 0 > ls ff886d58: /PowerPC,G4@0 ff8871f8: /l2-cache ok 0 > dev PowerPC,G4@0 ok 0 > .properties name cpu reg 00000000 cpu-version 80020101 state running clock-frequency 4a817c7b bus-frequency 09ef21aa ...
The rest of the output contains various cache sizes, the processor's graphics capabilities (Altivec, support for certain instructions, ...), and so on. You can think of this as analogous to /proc/cpuinfo
on Linux.
4. The following command lists files in the root directory of the disk (partition) referred to by the "alias" hd
0 > dir hd:\ Size/ GMT File/Dir bytes date time Name 6148 12/25/ 3 4:25:25 .DS_Store 156 9/12/ 3 20:41:59 .hidden 589824 12/25/ 3 6:45: 6 .hotfiles.btree ...
5. The following command expands the alias hd
, and gives you the complete path of the device in the tree (type devalias
by itself to see a list of current aliases, along with what they refer to):
0 > devalias hd /pci@f4000000/ata-6@d/disk@0 ok
6. You can load a file (kernel) using the load
command, and boot it using the boot
command. As stated earlier, mac-boot
and shut-down
are predefined to boot the machine normally, or shut it down, respectively. You can get and set variables (options) using the printenv
and setenv
commands. These variables are stored in the non-volatile memory (NVRAM) of Open Firmware. For example, if you want your email address to be used as the "OEM banner", you should do the following:
0 > setenv oem-banner you@your.email.address 0 > setenv oem-banner? true
You do not actually need to drop into Open Firmware to set the NVRAM variables. You can access (get and set) these from within Mac OS X via the nvram
command line utility.
To sum up, Open Firmware is a powerful tool for controlling, debugging, and exploring the computer.
Operation
When an Open Firmware equipped Macintosh (all current Apple systems at the time of this writing) is powered on, hardware is diagnosed (by some POST code) and initialized. The first entity to control the CPU thereafter is the firmware. Open Firmware (which runs with interrupts disabled) builds a device tree, probes slots for devices, queries PCI devices and assigns them address space appropriately, and then looks for the default boot device (unless one was specified explicitly). The following "snag" keys let the user specify a boot device as the system is powered on:
C device referred to by the 'cd' alias, a CD-ROM drive D device referred to by the 'hd' alias, a hard disk drive N device referred to by the 'enet' alias, a network card Z device referred to by the 'zip' alias, a ZIP drive
It is worth noting that pressing T
while your Mac powers on would boot it into what's called the FireWire Target Disk Mode. Essentially, your Mac becomes a fancy external FireWire disk drive.
You can also specify the complete pathname of a device, or have the machine boot over the network using TFTP:
boot enet:<server IP>,<file>,<my IP>;<subnet>,;<gateway IP>
If Open Firmware fails to find a boot device, a blinking folder is displayed.
Open Firmware then loads a file of type tbxi
(ToolBox ROM Image, for historical reasons) from the system partition. Note that this would have been the file called "Mac OS ROM
" in the System Folder on Mac OS 9, while OS X loads /System/Library/CoreServices/BootX
, which is the bootloader as well. BootX is then executed and Control is then passed to it.
Note that Open Firmware can directly load ELF, XCOFF and "bootinfo" (any supported format with an XML header) binaries, but not Mach-O, the native executable format on Mac OS X. BootX can load Mach-O binaries.
Bootloader
BootX (/System/Library/CoreServices/BootX
) is the default bootloader on Mac OS X.
BootX is also the name of an open source bootloader (different from Apple's BootX) that allows dual-booting Mac OS and Linux on "Old World" machines.
BootX can load kernels from various filesystems: HFS+, HFS, UFS, ext2, and TFTP (network, abstracted to look like a filesystem). In addition to Mach-O, BootX can also load ELF kernels, although Mac OS X does not use this feature. To reiterate, BootX can load ELF kernels from an ext2 partition!
The "Old World" Macs had various issues with the implementation of Open Firmware, which in turn caused many booting problems for Apple engineers, and even more problems for the PowerPC Linux port. Now, Apple had access to the firmware's source. They solved most of the problems either via NVRAM patches, or by integrating required changes into BootX itself (in the instances where the changes could not be implemented as patches). As BootX matured, Apple added support for ext2 and ELF with the goal of making the platform more amenable to PowerPC Linux.
The sequence of events when BootX starts executing (after being handed control by Open Firmware) is described below:
- BootX first initializes the Open Firmware client interface (that it would use to talk to the firmware), and retrieves the firmware version.
- It then creates a pseudo-device called
sl_words
('sl' implies secondary loader) in the firmware, and defines various FORTH words in it (it is here that code for the spinning cursor is set up). - BootX looks up the
options
device in the firmware, which contains various variables (that you can see and set using the printenv
and setenv
commands in Open Firmware).
0 > dev /options .properties name options little-endian? false real-mode? false auto-boot? true diag-switch? false ... boot-command mac-boot ...
- BootX looks up the
chosen
device, which contains handles for entities such as the boot input/output devices, memory, the MMU, the PMU, the CPU, the PIC, etc. For example, the following command at the Open Firmware prompt shows you the contents of chosen
:
0 > dev /chosen ok 0 > .properties name chosen stdin ffbc6e40 stdout ffbc6600 memory ffbdd600 mmu ... ...
- BootX initializes handles to the MMU and memory using
chosen
. - BootX initializes handles to the boot display and the keyboard (if present).
- BootX checks if the "security mode" is "none", or
- BootX checks if the "verbose" (
cmd-v
) or "single user" (cmd-s
) flags were specified, and sets the "output level" accordingly. - BootX checks if the system is booting in "Safe Mode".
- BootX claims memory for various purposes.
- BootX finds all displays and sets them up. It does this by searching for nodes of type "display" in the device tree. The primary display is referred to by the
screen
alias. For example, you can try this at the Open Firmware prompt:
0 > dev screen ok 0 > .properties name ATY,Bee_A compatible ATY,Bee width 00000400 height 00000300 linebytes 00000400 depth 00000008 display-type 4c434400 device_type display character-set ISO859-1 ...
- While opening the display(s), BootX also sets the screen color to the familiar whitish gray.
- BootX looks up the boot device, boot arguments, etc., and determines where to get the kernel from (via a network device, from a block device, etc.), whence the path to the kernel file (
mach_kernel
) is constructed. If booting from a block device (which is the usual case), the path to the kext cache (see kextcache(8)
) is calculated, along with the extensions directory (usually /System/Library/Extensions
).
Mac OS X uses a few kinds of "kext" (kernel extension) caches to speed up loading of kexts. Kernel caches are kept in the directory /System/Library/Caches/com.apple.kernelcaches
. The cache files are named kernelcache.XXXXXXXX
, where the suffix is a 32-bit adler
checksum (the same algorithm as used by Gzip).
- At this point, BootX draws the Apple logo splash screen, and starts the spinning cursor. If booting from a network device, a spinning globe is drawn instead.
- Depending on various conditions, BootX tries to retrieve and load the kernel cache file.
- The next step is to "decode" the kernel. If the kernel header indicates a compressed kernel, BootX tries to decompress it (typical LZSS compression is used, as you compress this kind of data once but expand it many times). Since the kernel binary can potentially be a "fat" binary (code for multiple architectures residing in the same binary), BootX checks if it indeed is (fat), and if so, "thins" it (figures out the PowerPC code).
- BootX attempts to decode the file (possibly "thinned") as a Mach-O binary. If this fails, BootX also tries to decode it as ELF.
- If the above fails, BootX gives up, draws the failed boot picture, and goes into an infinite loop.
- If BootX is successful so far, it saves filesystem cache hits, misses and evicts, sets up various boot arguments and values (such as whether this is a graphical or verbose boot, whether there are some flags to be passed to the kernel, the size of installed RAM), and also calls a recursive function to flatten the device tree.
- Finally, BootX "calls" the kernel, immediately before which it "quiesces" Open Firmware, an operation as a result of which any asynchronous tasks in the firmware, timers, or DMA get stopped, etc.
System Startup
Mac OS X user level startup is neither pure BSD style, nor SYSV style, although the presence of /etc/rc
indicates a BSD heritage. In fact, various things are unsurprisingly similar to NEXTSTEP.
The next section, XNU: The Kernel, describes some of the things the kernel does as it comes up. Mac OS X System Startup continues with a description of (mostly) user-level startup.
BootCache
Mac OS X uses a boot-time optimization (effectively a smart readahead) called "BootCache" that monitors the pattern of incoming read requests to a block device (the boot disk), and sorts the pattern into a "playlist" (it also measures the cache hit rate and stores the request pattern into a "history list" for being adaptive in future).
The loadable (sorted) read pattern is stored in /var/db/BootCache.playlist
. Once this is loaded, the cache comes into effect.
Note that this feature requires at least 128 MB of physical RAM before it is enabled (automatically).
/System/Library/Extensions/BootCache.kext
is the location of the kernel extension implementing the cache while Contents/Resources/BootCacheControl
within that directory is the user-level control utility (it lets you load the playlist, among other things). The effectiveness of BootCache can be gauged from the following: in a recent update to "Panther", a reference to BootCacheControl was broken. BootCache is started (via the control utility) in /etc/rc
, and a prefetch tag is inserted (unless the system is booting in safe mode). /etc/rc
looks for BootCacheControl
in the "kext" directory, as well as in /usr/sbin
, and finds it in the former (it doesn't exist in the latter). However, another program (possibly loginwindow.app
) accesses /usr/sbin/BootCacheControl
directly, and does not find it. For what it's worth, making BootCacheControl
available in /usr/sbin
, say via a symlink, reduces the boot time (measured from clicking on the "Restart" confirmation button to the point where absolutely everything has shown up on the system menu) from 135 seconds to 60 seconds on one of my machines!