[an error occurred while processing this directive]

Beginning Open Firmware debugging techniques, Part II: Hardware identification


"Beginning Open Firmware debugging techniques, Part I: The command line interface" and its prerequisites.

System Requirements:

An Open Firmware equipped computer. Included examples will use Apple hardware.

About this tutorial

This tutorial is the second part of learning beginner debugging techniques in Open Firmware. The first tutorial focused on understanding the use of Forth within Open Firmware, and this tutorial will focus on the hardware as it appears to Open Firmware. A simple exercise of writing to the console in Open Firmware will be explored. Some of the reference material discussed in Part I will be applied to the hardware devices, and Advanced Exercises will continue to offer an opportunity to explore concepts in an independent manner. At the end of this tutorial, the reader should feel comfortable with fundamental concepts in Open Firmware and be prepared to continue on to the next tutorial, which explores the process by which Open Firmware bootstraps an operating system.

This tutorial does not discuss interfacing with the hardware by software.

Hardware engineering - Use of Apple hardware documentation

As has been discussed, the firmware does a great deal of device discovery and early initialization of those devices before the operating system takes over. Understanding how to tie devices and operating systems together requires knowledge of both the physical layout of device connections and their digital representation. For two of the computers used in this tutorial (a 7300 and an AGP G4), Apple has provided Hardware Notes as each model was released. Although both models are end-of-lifed, the place to locate the Hardware Notes is at Apple's Hardware Notes page, which contains all models back to the Classic:


The Manual for the 7500/8500 series has the base description of the 7300 model:




is the update to include the 7300. The manual update for the 7300 doesn't contain the block diagrams, which is why both are needed. Two other hardware notes of interest are for the AGP G4, as it is being used in this series, and the new dual processor G5, out of curiosity and with an eye toward a platform with an IBM 970 CPU:



Advanced Exercise: IBM JS20

IBM has documented much of the IBM 970FX powered JS20 Blade Server in their version of hardware notes, called "Redbooks." These are available online. Locate the Redbook for the JS20.

Identifying hardware components

On page 15 of the hardware note for the 7500/8500 series, Diagram 2-1 shows the layout of computers based on this model.

In the upper right hand is the memory circuitry. Below that is the integrated on-board video system containing the device chaos, which in addition to the VRAM has two devices attached to it, control, the video out system used in the second example earlier of local words (chaos was the first), and Plan B, the video in system. Below this block is the I/O subsystem, including the serial ports and the ADB port. On the left side of the diagram is the Bandit chip, which is a PCI-Host chip linking the I/O subsystem to the CPU and memory.

By comparison, the AGP G4 uses a PCI-PCI Bridge to link the equivalent I/O subsystem to the memory controller and has a separate AGP slot for video. The dual processor G5 uses HyperTransport to link its PCI or PCI-X slots, depending on which model, to the I/O and memory systems, and uses an AGP slot for video. Although the G4 and G5 diagrams initially appear to be simpler, they actually convey less information than the 7500/8500 diagram and belie the increasing complexity of even entry level desktop computers (although the faster dual processor G5 model diagram has a nice tangle of HyperTransport/PCI-X Bridge connections to hint at the complexity). The choice of a 7300 for a demonstration platform is appropriate because the underlying concepts can be explored within a simple environment and later scaled to the more complex platforms.

Advanced Exercise: Scouting for device drivers

The reader should identify the revelevant subsystems for models of interest and note which devices appear in more than one model.

Slash, The Root Node of the Firmware Device Tree

In order to establish an organization of the devices on a computer, Open Firmware implements a hierarchical device tree. Devices are also knowns as "nodes" and the terms can be used interchangeably, although "nodes" encompasses more than just devices. The device tree contains information about the devices, the support packages for bootstrapping, and the commands needed to operate both devices and support packages while bootstrapping. Nodes attach to nodes in a parent-child relationship, or stand alone. Nodes can be parents, children and peers, all at the same time. The device tree is generally representative of the physical interconnections of the electronics, but it is not a perfect one-to-one correlation.

The device tree is utilized much like a file system path. To examine the device tree, first select the root device, which is /:

0 > dev /<return>  ok
0 > _

Simple enough. To verify the device has been selected, look at the path to the device with the pwd word:

0 > pwd<return> / ok
0 > _

Nothing unexpected. The actions selected a device with the dev word, and then reviewed the "path to working device" with the pwd word. Use either show-devs or ls to show what is attached to the currently selected device, in this case the root node (again, this is from a 7300, which has a fairly simple layout):

0 > ls<return>
FF828F80: /PowerPC,604@0
FF829230:   /l2-cache@0,0
FF8299F0: /chosen@0
FF829B20: /memory@0
FF829C68: /openprom@0
FF829D28: /AAPL,ROM@FFC00000
FF829F40: /options@0
FF82A3E0: /aliases@0
FF82A620: /packages@0
FF82A6A8:   /deblocker@0,0
FF82AEA8:   /disk-label@0,0
FF82B3E8:   /obp-tftp@0,0
FF82D828:   /mac-files@0,0
FF82E020:   /mac-parts@0,0
FF82E780:   /aix-boot@0,0
FF82EBF8:   /fat-files@0,0
FF8301C8:   /iso-9660-files@0,0
FF830B10:   /xcoff-loader@0,0
FF8314D0:   /terminal-emulator@0,0
FF831568: /bandit@F2000000
FF832758:   /gc@10
FF832B90:     /53c94@10000
FF834418:       /sd@0,0
FF835048:       /st@0,0
FF835CC0:     /mace@11000
FF836B38:     /escc@13000
FF836C90:       /ch-a@13020
FF837340:       /ch-b@13000
FF8379F0:     /awacs@14000
FF837AD8:     /swim3@15000
FF838BE0:     /via-cuda@16000
FF839770:       /adb@0,0
FF839860:         /keyboard@0,0
FF839FB0:         /mouse@1,0
FF83A060:       /pram@0,0
FF83A110:       /rtc@0,0
FF83A5D8:       /power-mgt@0,0
FF83A6F8:     /mesh@18000
FF83C260:       /sd@0,0
FF83CE90:       /st@0,0
FF83DB98:     /nvram@1D000
FF83F3F8:   /pci106b,1@B
FF83F5D0:   /pci1057,4@D
FF83F880:   /pci128a,3@F
FF83DCD0: /chaos@F0000000
FF83FCC8:   /control@B
FF83EC60: /hammerhead@F8000000
0 > _

Top-most is the CPU device. Four of the next seven nodes (/chosen, /options, /aliases, and /packages) aren't devices, but are for support. The child nodes under /packages are specific support packages necessary for bootstrapping an operating system. The three out of first seven nodes that are devices are the /memory device and the Open Firmware (/openprom) and Apple ROMs (/AAPL,ROM). /bandit, /chaos, and /hammerhead are physical devices, and the first two of the three have devices subattached to them.

The notation "@" is a device identifier. Within a node may be one or more devices. If the @ notation is omitted, OF will locate the first device on the node that name matches, but if the @ notation is present, OF will further submatch the device. SCSI is a good example. If there is more than one hard drive present, the factory hard drive would like be identified as /bandit/gc/mesh/sd@0 and a second hard drive might be /bandit/gc/mesh/sd@6. In this case, the number after the @ denotes the SCSI ID. Other devices will have different ways to identify themselves, such as their region in PCI address space, and this will be discussed later.

If the name is omitted, OF will match the first node under the last node identified. For example, "/bandit/gc/mesh/@6" will match sd@6 instead of st@6.

Correlating the OF device tree to the Hardware Notes

Referring again to page 27 of the PowerMac 7500/8500 Hardware Note, at the bottom of the block diagram is the I/O Subsystem. The block diagram shows three PCI slots on the left and on the right is Grand Central, the I/O controller for SCSI, ADB, sound in and out, serial ports, and ethernet peripherals. The external SCSI, ethernet, and serial ports are combined into one ASIC called Curio. In the OF device tree shown earlier, there is no mention of a Curio ASIC. It is possible to identify the internal SCSI chip "mesh" as it is listed in the block diagram, and escc can be inferred to be the serial ports. Awac is identified in the block diagram as sound in and out, and via-cuda handles ADB and other functions. By process of elimination, SWIMIII in the block diagram is the floppy disk controller. That leaves "53c94" and "mace" as unidentified. On page 31 of the Hardware Note is a discussion of the Curio chip, and "mace" is identified there as the ethernet interface, and that leaves "53c94" as the external SCSI port.

At the time the device tree layout was captured, this particular 7300 had two PCI cards in it. There is always one PCI node present regardless of whether there are cards in place or not. This is the "PCI" side of the PCI/Host chip bandit, and is listed as "pci106b,1@B." The two cards are listed as "pci1057,4@D" and "pci128a,3@F."

In terms of parents, children and peers, the PCI slots and Grand Central (gc) are peers and are children of bandit. gc is parent to Curio, Awac, and SWIMIII, among others. The PCI cards themselves may have children nodes, but the one currently installed do not.

Device Trees in other models

Below is an abbreviated OF device tree, showing only the hardware devices, from an AGP G4.

ff893178: /uni-n@f8000000
ff8933c0:   /i2c@f8001000
ff893c10:     /cereal
ff8942c0: /pci@f0000000
ff8bc6e8:   /uni-north-agp@b
ff8bc958:   /ATY,Rage128Ps@10
ff895368: /pci@f2000000
ff8963d8:   /pci-bridge@d
ff898490:     /mac-io@7
ff8994d0:       /interrupt-controller@40000
ff8996a0:       /gpio@50
ff899788:         /extint-gpio1
ff899920:         /programmer-switch
ff899a58:       /escc-legacy@12000
ff899c50:         /ch-a@12004
ff899dd0:         /ch-b@12000
ff899f50:       /escc@13000
ff89a158:         /ch-a@13020
ff89ab00:         /ch-b@13000
ff89b418:       /davbus@14000
ff89b698:         /sound
ff89bd98:       /timer@15000
ff89bf28:       /via-pmu@16000
ff89f078:         /rtc
ff89f768:         /power-mgt
ff8e56b8:           /usb-power-mgt
ff89f9d0:       /i2c@18000
ff8a0260:         /cereal
ff8a0928:       /ata-4@1f000
ff8a2928:         /disk
ff8a3020:       /ata-3@20000
ff8a5020:         /disk
ff8a5718:       /ata-3@21000
ff8a7718:         /disk
ff8a9218:     /usb@8
ff8e4960:       /mouse@1
ff8aeb88:     /usb@9
ff8e4460:       /hub@1
ff8e45f0:         /keyboard@1
ff8b44f8:     /firewire@a
ff897410: /pci@f4000000
ff8e0708:   /ethernet@f

At the top level are the Uni-North memory controller and three "pci" nodes, with AGP, a PCI-PCI Bridge, and Ethernet under each one, respectively. Firewire, USB, and the I/O subsystem are connected through the PCI-PCI Bridge to the CPU and memory. The block diagram on page 27 of the PowerMac G4 Hardware Note depicts the PCI slots to connection under the PCI-PCI- Bridge, but no cards are present to confirm this and no inference should be made about their attachment location.

Advanced Exercise: Mac Mini Device Tree

Using a web search engine, locate the device tree for Apple' Mac mini. Without the use of a Hardware Note, draw a layout of the likely interconnections of the hardware devices on the Mac Mini.

Advanced Exercise: JS20 Blade Server Block Diagram

Page 24 of the IBM JS20 Blade Server Redbook shows the block diagram for this model. Construct a possible device tree.

.properties of the CPU Node

As not much can be done without a CPU, it is appropriate to begin with this node. The .properties word is to look at some of the characteristics of the device. The following is from a 7300:

0 > dev /PowerPC,604<return>  ok
0 > .properties<return>
name                    PowerPC,604
device_type             cpu
reg                     00000000  00000000
cpu-version             00090202
clock-frequency         0BEBC200
timebase-frequency      00BEBC20
reservation-granularity 00000020
tlb-sets                00000040
tlb-size                00000080
d-cache-size            00008000
i-cache-size            00008000
d-cache-sets            00000080
i-cache-sets            00000080
i-cache-block-size      00000020
d-cache-block-size      00000020
l2-cache                FF829230
existing                00000000 80000000 80000000 80000000
available               00000000 F0000000 F4000000 0B800000 FF900000 00300000
translations            F0000000 00010000 F0000000 00000028 F0800000 00001000 F0800000 00000028
                        F0C00000 00001000 F0C00000 00000028 F2000000 00010000 F2000000 00000028
                        F2800000 00001000 F2800000 00000028 F2C00000 00001000 F2C00000 00000028
                        F3000000 01000000 F3000000 00000028 F8000000 00001000 F8000000 00000028
                        FF800000 00100000 00400000 00000010 FFC00000 00400000 FFC00000 00000000
0> _

The first two are self-explanatory, and the reg property will be discussed at a later time. Examination of cpu node:

"cpu-version" Standard property, encoded as with encode-int, that represents the processor type. This shall be the value obtained by reading the Processor Version Register of the CPU.

The important piece of information, for the moment, is the second sentence that states how this value is obtained, which is from the PVR. One can conclude that at some point OF probed the CPU and obtained this value. A quick look up in various sources says that a PVR of 0x00090202 means that the cpu is 0x9 (604e) and the version is 2.2. Next property of interest is clock-frequency, and the description again comes from the binding:

"clock-frequency" Standard property, encoded as with encode-int, that represents the internal processor speed (in hertz) of this node.

The clock-frequency value is a long integer expression of the CPU's clock speed in hexidecimal.

0 > showstack<return>  ok
0BEBC200 decimal<return>  ok

L1 cache

The binding says the d-cache-size property is the size of the L1 data cache. The first tutorial provided the basic Forth necessary to convert this value to a human readable form:

drop<return>  ok
hex 8000 decimal<return>  ok

The L1 data cache size is 32k, which is consistent with the 604e CPU.

L2 cache

The remaining properties of the cpu node require more extensive knowledge than the tutorial requires one to possess at the moment, so the next examination is of the one device attached to the cpu, the L2 cache. Note that in the device tree, the l2-cache is relative to the cpu node so selecting this does not use the "/" to denote an absolute path. Instead, use a relative path, which is the same as most command line interface directory traversal readers are probably familiar with.

drop dev l2-cache<return> ok
pwd<return> /PowerPC,604@0/l2-cache@0,0 ok

The absolute path reflects the devices and their nodes.

name                    l2-cache
device_type             cache
i-cache-size            00040000
d-cache-size            00040000
i-cache-sets            00002000
d-cache-sets            00002000

ok Empty hex 40000 d# 1024 /<return> ok 100 _ decimal<return> ok 256 _

The above example shows mixing bases during a calculation. The d# word tells OF to treat the next value on the stack as a decimal base number. The motherboard L2 cache is 256k.

Examination of /cpus node on AGP G4

As the CPU node is extremely important, and notable differences exist between the 7300 and the AGP G4 regarding this node, it is appropriate to also examine the AGP G4 at this time (using the first method of displaying the stack for clarity):

0 > dev /<return>

0 > ls<return>

ff83b690: /cpus
ff83b8c0:   /PowerPC,G4@0
ff83bc38:     /l2-cache
ff83c758: /chosen
ff83c8e8: /memory@0
ff83cb00: /openprom
ff83cc30:   /client-services
ff83ddd0: /rom@ff800000
ff83df58:   /boot-rom@fff00000
ff83e0d8:   /macos
ff83e158: /options
ff83e1d8: /aliases
ff83ebf0: /packages
ff83ec58:   /deblocker
ff83f578:   /disk-label
ff83ff78:   /obp-tftp
ff8470a8:   /telnet
ff847928:   /mac-parts
ff848a00:   /mac-files
ff84b810:   /hfs-plus-files
ff850580:   /fat-files
ff8522b0:   /iso-9660-files
ff852eb8:   /bootinfo-loader
ff854b58:   /xcoff-loader
ff855570:   /pe-loader
ff855f48:   /elf-loader
ff857578:   /usb-hid-class
ff8598b8:   /usb-ms-class
ff85bc58:   /sbp2-disk
ff85e220:   /ata-disk
ff85f480:   /atapi-disk
ff8611e0:   /bootpath-search
ff867ad0:   /terminal-emulator
ff867b68: /firewire-disk-mode
ff885348: /psuedo-hid
ff8853d0:   /keyboard
ff885a50:   /mouse
ff885f68: /multiboot
ff892448: /diagnostics
ff8924b0: /rtas
ff8926b0: /nvram@fff04000

(The hardware devices were displayed earlier and are omitted for clarity, but attach after the nvram node as children of the root node /.)

0 > dev /cpus<return>

0 > ls<return>

ff83b8c0: /PowerPC,G4@0
ff83bc38:   /l2-cache

0> dev PowerPC,G4 .properties<return>

name                    PowerPC,G4
device_type             cpu
reg                     00000000  
cpu-version             000c0209 
state                   running
clock-frequency         17d78400 
bus-frequency           05f03e4d 
timebase-frequency      017c0f93 
tlb-sets                00000040 
tlb-size                00000080 
d-cache-size            00008000 
i-cache-size            00008000 
d-cache-sets            00000080 
i-cache-sets            00000080 
i-cache-block-size      00000020 
d-cache-block-size      00000020 
l2-cache                ff83bc38 
l2cr                    b9000000 
existing                00000000 80000000 80000000 80000000 
available               00003000 7fffd000 d0000000 20000000 
translations            00000000 00003000 00000000 00000010 80000000 00080000 80000000 00000028 
                        80080000 00001000 80080000 00000028 80081000 00001000 80081000 00000028 
                        80082000 00001000 80082000 00000028 f0000000 00010000 f0000000 00000028 
                        f0800000 00001000 f0800000 00000028 f0c00000 00001000 f0c00000 00000028 
                        f2000000 00010000 f2000000 00000028 f2800000 00001000 f2800000 00000028 
                        f2c00000 00001000 f2c00000 00000028 f4000000 00010000 f4000000 00000028 
                        f4800000 00001000 f4800000 00000028 f4c00000 00001000 f4c00000 00000028 
                        f5200000 00200000 f5200000 00000028 f5200000 00200000 f5200000 00000028 
                        ... 00000140 bytes total

0 > ls<return>
ff83bc38: /l2-cache
0 > dev l2-cache<return>
0 > .properties<return>
name                    l2-cache
device_type             cache
i-cache-size            00100000 
d-cache-size            00100000 
i-cache-sets            00002000 
d-cache-sets            00002000 
i-cache-line-size       00000040 
d-cache-line-size       00000040 
clock-frequency         0bebc200 

0 > 

An important property of the CPU node is the l2cr. When a CPU with a L2 backside cache cold starts, the cache is not on. It must be enabled during the booting process. Under most circumstances, Open Firmware will examine this property and enable the cache using the l2cr. If this node is not present, as is the case with aftermarket cards that do not contain Open Firmware boot FCode, the cache must be enabled by software. This means that either the software knows the correct value to use, or is able to measure the cache size dynamically.

Advanced Exercise: L2CR calculation

Examine the setting for the L2 cache in l2cr and compare it to the values in the l2-cache node. Note the size of the cache, and additional settings for the L2 cache.

The Memory Node

Returning to examination of the 7300, the next device of interest is /memory:

hex drop dev /memory .properties<return>
name                    memory
device_type             memory
reg                     00000000  08000000
available               00000000 00400000 00500000 07B00000

ok Empty _

XXX The particular property was of interest because the IEEE Std 1275-1994 Standard for Boot (Initialization, Configuration) Firmware, Core Practices and Requirements (also known as "core specification") states that the "reg" property is the physical memory present denoted as a physical "region" of memory (this is not the same definition of "reg" for other nodes, so be aware of this quirk). The upper value is the size of the memory installed, or in this case 128M (refer to Part I for the math calculating the size of the memory).

XXX Need to discuss the format of a region memory (start size)

The other property in /memory is "available" and we see that the memory has some breaks in it. The syntax on this property is (address size) which means that we have a 4M block of memory starting at 0x0, and a 0x7BM block of memory starting at 0x5M. Something is taking a 1M block of memory, starting at 0x4M.

/memory node on AGP G4

As a comparison, here is the output from the AGP G4:

name                    memory
device_type             memory
reg                     00000000  10000000 
                        10000000  04000000 
                        00000000  00000000 
                        00000000  00000000 
slot-names              0000000f
available               00003000 13dfd000 
dimm-info               8008040c 0a024000 01a06000 80080001 8f040601 01000ea0 60000014 10142d20 
                        20102010 ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffff12f4 
                        ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff 
                        ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffffffff ffff64f7 
                        8008040c 09014000 01806000 80080001 8f040601 01000ea0 60000014 14143210 
                        20102010 00000000 00000000 00000000 00000000 00000000 00000000 000012e5 
                        2cffffff ffffffff 04384c53 44543836 3441472d 31304543 37202007 00000d46 
                        00826700 00000000 00000000 00000000 00000000 00000000 00000000 000064af 
                        ... 00000200 bytes total
dimm-types              SDRAM
dimm-speeds             PC100-222S

There is no discontinuity in the available memory regions.

Advanced Exercises: /memory

The available memory in OF 1.0.5 was not contiguous immediately after boot. What would be present in memory, and how large is it?

The memory in OF 3.0 on the AGP G4 has two regions in the "reg" property. Why?

Neither the start nor ending address of the "available" region match the "reg" property. What would be present below the start of the available memory, and what would be present above the available memory?

/aliases node

Not all nodes are hardware devices. Another type of node is a "system node." These are standard nodes that are present as required by IEEE 1275-1994. One important system node is /aliases. These are pathway shortcuts to devices, and can be used by both humans and OF. The aliases present are dependent on both the OF version and the hardware present. Not all devices that have an alias are present, and not all devices have an alias. From an OF 1.0.5 based PowerMac 7300:

dev /aliases .properties<return>
name                    aliases
vci0                    2F636861 6F734046 30303030 30303000
pci1                    2F62616E 64697440 46323030 30303030 00
pci2                    2F62616E 64697440 46343030 30303030 00
fd                      2F62616E 6469742F 67632F73 77696D33 00
kbd                     2F62616E 6469742F 67632F76 69612D63 7564612F 6164622F 6B657962 6F617264 00
ttya                    2F62616E 6469742F 67632F65 7363632F 63682D61 00
ttyb                    2F62616E 6469742F 67632F65 7363632F 63682D62 00
enet                    2F62616E 6469742F 67632F6D 61636500
scsi                    2F62616E 6469742F 67632F35 33633934 00
scsi-int                2F62616E 6469742F 67632F6D 65736800

Part I discussed how to extract C-style ASCII strings. One could (and should, at least once) use those routines to determine the path to the devices. However, an alternative, and easier, way to find the absolute path of a device alias is devalias, another of the Forth words that uses arguments that follow instead of preceed:

devalias kbd<return> /bandit/gc/via-cuda/adb/keyboard ok

One would use an alias in the same manner as any device path:

dev kbd pwd<return> /bandit@F2000000/gc@10/via-cuda@16000/adb@0,0/keyboard@0,0 ok

This only works with devices that have aliases, which is obvious forwards but not backwards, where one would like a device to have an alias but does not and the absolute path must be specified repeatedly. It is important to note that the the syntax when using a device alias is to not specify an absolute path. When Open Firmware examines a device path and sees it does not start with "/" Open Firmware will check the aliases first before using a relative path. Aliases make life simple and reduce errors dramatically, so use them when possible, such as specifying an output device.

Not specifying a device alias for devalias will display all device aliases:

vci0                /chaos@F0000000
pci1                /bandit@F2000000
pci2                /bandit@F4000000
fd                  /bandit/gc/swim3
kbd                 /bandit/gc/via-cuda/adb/keyboard
ttya                /bandit/gc/escc/ch-a
ttyb                /bandit/gc/escc/ch-b
enet                /bandit/gc/mace
scsi                /bandit/gc/53c94
scsi-int            /bandit/gc/mesh 


A third usage of devalias is to create a non-persistent alias, rather useful when an alias does not exist in the OF ROM:

devalias keyboard /bandit@F2000000/gc@10/via-cuda@16000/adb@0,0/keyboard@0,0<return>  ok
vci0                /chaos@F0000000
pci1                /bandit@F2000000
pci2                /bandit@F4000000
fd                  /bandit/gc/swim3
kbd                 /bandit/gc/via-cuda/adb/keyboard
ttya                /bandit/gc/escc/ch-a
ttyb                /bandit/gc/escc/ch-b
enet                /bandit/gc/mace
scsi                /bandit/gc/53c94
scsi-int            /bandit/gc/mesh
keyboard            /bandit@F2000000/gc@10/via-cuda@16000/adb@0,0/keyboard@0,0


Note that the new alias appears at the end of the device aliases list. Making this alias persistent is beyond the scope of this tutorial but it is possible to do so.

The devalias output from an AGP G4:

pci0                /pci@f0000000
agp                 /pci@f0000000
pci1                /pci@f2000000
pci2                /pci@f4000000
bridge              /pci@f2000000/@d
pci                 /pci@f2000000/@d
fwx                 /pci@f2000000/@d/firewire@a
enetx               /pci@f2000000/@d/ethernet@b
enet1               /pci@f2000000/@d/ethernet
fw1                 /pci@f2000000/@d/firewire
cb                  /pci@f2000000/@d/cardbus@1a
magma               /pci@f2000000/@d/cardbus@1a/pci-bridge/pci-bridge
usb0                /pci@f2000000/@d/usb@8
usb1                /pci@f2000000/@d/usb@9
mac-io              /pci@f2000000/@d/mac-io@7
mpic                /pci@f2000000/@d/mac-io@7/interrupt-controller
ide0                /pci@f2000000/@d/mac-io@7/ata-3@20000/disk@0
ide1                /pci@f2000000/@d/mac-io@7/ata-3@20000/disk@1
hd                  /pci@f2000000/@d/mac-io@7/ata-4@1f000/disk@0
cd                  /pci@f2000000/@d/mac-io@7/ata-3@20000/disk@0
zip                 /pci@f2000000/@d/mac-io@7/ata-3@20000/disk@1
ultra0              /pci@f2000000/@d/mac-io@7/ata-4@1f000/disk@0
ultra1              /pci@f2000000/@d/mac-io@7/ata-4@1f000/disk@1
scca                /pci@f2000000/@d/mac-io@7/escc/ch-a
sccb                /pci@f2000000/@d/mac-io@7/escc/ch-b
ki2c                /pci@f2000000/@d/mac-io@7/i2c
ki2c-serial         /pci@f2000000/@d/mac-io@7/i2c/cereal
via-pmu             /pci@f2000000/@d/mac-io@7/via-pmu
rtc                 /pci@f2000000/@d/mac-io@7/via-pmu/rtc
adb                 /pci@f2000000/@d/mac-io@7/via-pmu/adb
adb-keyboard        /pci@f2000000/@d/mac-io@7/via-pmu/adb/keyboard
adb-mouse           /pci@f2000000/@d/mac-io@7/via-pmu/adb/mouse
wireless            /pci@f2000000/@d/mac-io@7/@30000
ui2c                /uni-n/i2c
ui2c-serial         /uni-n/i2c/cereal
enet                /pci@f4000000/ethernet
fw                  /pci@f4000000/firewire
keyboard            /psuedo-hid/keyboard
mouse               /psuedo-hid/mouse
nvram               /nvram
last-boot           /pci@f4000000/ethernet@f
screen              /pci@f0000000/ATY,Rage128Ps@10


Use device aliases whenever possible, and if one is repeatedly using a device path that has no alias, create one.

Open Firmware compliant hardware devices - Five device types

As discussed in Technote 1044, for a device to be Open Firmware compliant, it must meet several specifications that depend on the device type. There are five basic device types defined: display, block, byte, network, and serial. For a device to be recognized and useable by Open Firmware, it must identify itself as one of these types and self-contain the required Forth words for that device type. A display device must be able to display graphics. Block and byte devices are used for loading the operating system and therefore must contain the ability to be read from. Serial devices must be able to do bi-directional character based communication. Network devices must be able to send and receive network packets, and can also be used for loading the operating system.

With each device type there are support packages provided by Open Firmware that are designed to utilize the hardware device. These support packages handle much of the formatting required to interface to the hardware device, as long as that hardware device has implemented the required Forth words. If the device meets 1275-1994 compliance, the underlying details of the hardware device become transparent to the package that needs the device. While the expectation of transparency may appear to be obvious, keep in mind that if any device that implements device requirements can be used by any support package that uses those requirements. A serial device can become indistinguishable from a network device.

Support packages reside at the /packages node and the next tutorial in this series will have an in-depth discussion of these. The next tutorial will also discuss block devices at length.

Open Firmware input and output devices

The first tutorial briefly discussed input and output devices and provided a link to a good tutorial on locating an appropriate one for booting an operating system. In debugging with Open Firmware, which occurs before the operating system debugger can take over, one useful technique is to print debug messages to a different output device than the console. In particular, on hardware that does not have a serial port, session capture can be difficult. The user typically boots an operating system with the keyboard and monitor attached to the computer. Anything that happens before the operating system is able to locate and mount a file system is difficult to record. Being able to direct debugging messages, or duplicate booting messages, to a second output device is very useful.

In order to be able to determine possible output devices that can support this, one must understand the Open Firmware definition of an output device. There is more than one type of output device recognized by OF. The most obvious is the video out system. Not all video out devices qualify as output devices to OF. OF can only use video out devices that meet the requirements for a "display" type device. The IEEE 1275-1995 specification requires that display device types support the four (Forth) words "open," "close," "write," and "draw-logo." Additionally, it should support "restore," in case of unexpected problems. Part I showed the words the video out, /chaos/control, for a 7300:

dev /chaos/control words<return>
close           restore         draw-logo       write           open            read-rectangle
fill-rectangle  draw-rectangle  get-colors      set-colors      color!          color@
0> _

Notice there is no "read." Of course, for an output device, it doesn't make sense. However, consider:

dev ttya words<return>
write           read            read-ahead      close           open

A serial port needs to be bidirectional.

dev kbd .properties<return> words
name                    keyboard
device_type             serial
reg                     00000000 00000000  00000000

reset           get-key-map     read            close           open
dev enet .properties<return> words
name                    mace
device_type             network
AAPL,connector          ethernet
reg                     00011000  00001000
                        00008200  00000100
                        00008300  00000100
AAPL,interrupts         0000000E 00000002 00000003
local-mac-address       00A04067 7A7C
address-bits            00000030
max-frame-size          00000800

dma-free        dma-alloc       load            write           read            close

Advanced Exercise: split input and output

Using input, output and io, have input go to one console device and output go to another. For example, on Apple hardware with serial ports, have the output go to the monitor and the input come from the serial port. On Apple hardware without serial ports, have output go to the monitor but the input come from a telnet session.

devalias ttya<return> /bandit/gc/escc/ch-a ok
dev ttya .properties<return>
name                    ch-a
device_type             serial
AAPL,connector          modem
reg                     00013020  00000020
                        00008400  00000100
                        00008500  00000100
AAPL,interrupts         0000000F 00000004 00000005

Locating the video subsystem

On page 15 of the hardware note for the 7500/8500 series, Diagram 2-1 shows the layout of this computer.

FF83DCD0: /chaos@F0000000
FF83FCC8:   /control@B

This device matches the block diagram.

Locating devices on AGP G4 (Advanced)

It is left to the reader to locate appropriate documentation for the AGP G4 (or their own model) and identify the appropriate input and output devices, and then compare them to the device aliases present.

OPen Firmware Display devices

dev /chaos/control .properties<return>
vendor-id               0000106B
device-id               00000003
revision-id             00000000
class-code              00000000
min-grant               00000000
max-latency             00000000
devsel-speed            00000000
AAPL,interrupts         0000001A
name                    control
device_type             display
model                   AAPL,343S1154
AAPL,connector          monitor
reg                     00015800 00000000 00000000  00000000 00000000
                        02015818 00000000 00000000  00000000 04000000
                        02015814 00000000 00000000  00000000 00001000
assigned-addresses      82015814 00000000 94000000  00000000 00001000
                        82015818 00000000 90000000  00000000 04000000


This device has identified itself as a "display" type device. Note that this is only for Open Firmware purposes and is not the same as the PCI class "Display Devices."

Advanced Exercise: Changing screen resolution

While this feature is not present in OF 1.0.5 equipped Macs with the stock /chaos/control display, later versions such as the AGP G4 do possess the ability to change the screen resolution used for the output device of the console. It is left to the reader to examine the video display on their own model and change the screen resolution within Open Firmware.

Opening devices - dev and open-dev

open-dev have slightly different functionalities. The former selects the device for the active device, while the latter opens the package without selecting it.

dev ttya<return>   ok
" testing" write<return> testing ok

This can be repeated with the screen device as well.

$call-method and open-dev

It is possible to write to a device when it is not selected. $call-method is used to call the method named by the string on the stack, but also requires a reference to the package containing the method.

drop<return>  ok
unselect-dev<return>  ok
pwd<return> no active package
" testing" " write" " ttya" open-dev $call-method<return> ttyaing ok
The example shows unselect-dev unselecting any device as the active device, and then using $call-method to call the instance created by open-dev. Here is a strong example of how stack contents are manipulated:

drop<return>  ok
" testing"<return>  ok
-7F1710 7
" write"<return>  ok
-7F1710 7 -7F1610 5
" ttya"<return>  ok
-7F1710 7 -7F1610 5 -7F1710 4

On the third time creating an encoded string in memory, the first memory address was repeated, and by inference "test" was overwritten by "ttya." This is not a bug (although one hesitates to call this a "feature"). Consider the 1275-1994 specification for the word " when in interpret mode:

Interpretation: ( [text<">< >] -- text-str text-len ) Parse text delimited by ", with hex-sequence handling as described below. Store the resulting string text-str text-len at the next available temporary location. The length of the temporary buffer is implementation-dependent but shall be no less than 80 characters. At least two such temporary buffers shall be provided, using the buffers alternately for successive uses of " in interpretation state.

In other words, this implementation met the letter of the specification by providing two and only two temporary buffers. In order to create a chain using more than two strings, at least one string will have to be relocated to another buffer. To create a buffer, logically enough, one must use the word buffer:. This word introduces a mix of stack location for the arguments; one before the word, and one after it. The syntax:

( len "new-name< >" -- )
indicates that the size of the buffer is placed on the stack before the buffer: word and the string naming the buffer is placed after it. For example:
-7F1710 7 -7F1610 5 -7F1710 4
7 buffer: myBuf<return>  ok
buffer: does not return anything on the stack. It is up to the user to keep track of one's buffers. The resulting named buffer, however, can be treated just like any other Forth word or value. myBuf is in fact a value - the memory location of that buffer.
myBuf<return>  ok
The next step is to move a value into that buffer and, with a tip of the hat to those who dislike obfuscation in their programming environment just to save keystrokes, the Forth word to do this is

move ( src-addr dest-addr len -- )

Consider the syntax on the aforementioned " word. It leaves the address and length on the stack, with the length on top. For move, the destination address, in this case the name of the buffer, must come between the source address, which is the start of the string, and the length. Some argument location manipulation must occur. There are two approaches. One is swapping arguments, and one is rotating arguments. With swapping, arguments exchange locations. With rotating, the entire stack shifts.

[graphic here showing examples of rot, -rot, 2rot, swap, and 2swap, syntax included]

1) encode the string

" testing"<return>  ok
-7F1710 7
2) duplicate the length of the string
dup<return>  ok
-7F1710 7 7
3) using the topmost argument, create the buffer
buffer: myBuf<return>  ok
-7F1710 7
4) Again duplicate the length
dup<return>  ok
-7F1710 7 7
5) move the topmost argument to the back to preserve it
-rot<return>  ok
7 -7F1710 7
6) push the destination address myBuf onto the stack
myBuf<return>  ok
7 -7F1710 7 -725BC0
7) swap top two; the top three arguments are now: (src-addr dest-addr length -- )
swap<return>  ok
7 -7F1710 -725BC0 7
8) move the string into the buffer (move does not return any arguments)
move<return>  ok
9) put the address myBuf back on the stack
myBuf<return>  ok
7 -725BC0
10) swap to create: (text-str text-len -- )
swap<return>  ok
-725BC0 7
11) examine the contents of myBuf with type:
type<return> testing ok
At no time above was the length of the string manually entered, which required the extra duplication in order to preserve the value. This approach was chosen to show a few simplified examples of using swap, rot, and dup. In practice, one would use the Forth word value (x "new-name< >" -- ) to store the value of a number. For example:

" testing" value len<return>  ok
len<return>  ok
-7F1610 7

The above snippet stored the string "testing" into a buffer and stored the length of the string into the variable len. Pushing len onto the stack placed the number 7 there. Changing the value of len is done with the word to (param "old-name< >" -- ). The destination variable must already exist before invoking to.

Advanced exercise: Repeat the process of moving a string into a buffer, using value to store the string length.

Repeat the process of moving a string into a buffer, using over ( x1 x2 -- x1 x2 x1 ).

With the string " testing" now stored in myBuf and the length of the string stored in len, the earlier attempt to write "testing" to the chosen output device can now be accomplished with only two buffers available to the word ".

-7F1610 7
myBuf len " write" " ttya" open-dev $call-method<return> testing ok

Advanced Exercise:
While in a telnet or serial session, send text to the display device.

Answer to Last Tutorial's Advanced Exercise - Displaying opcodes around an address location

The only quantitative exercise from the last tutorial was formatting a Forth chain to display opcodes around a particular address:

Exercise: It is often useful to display opcodes around an instruction at a known offset from the start of a file. If dis displays PowerPC instructions starting from an address passed to it, the executable file starts in memory at 0x600000, and the target instruction offset is 0x200, formulate a Forth chain that will display the four instructions before the target address.


0 > 600000 200 + 10 - dis<return>
006001F0: 3BE9200C
006001F4: 3D200003
006001F8: 80093488
006001FC: 907F000C
00600200: 7FE3FB78
00600204: 7C0803A6
00600208: 4E800021
0060020C: 2C03FFFF
00600210: 3860FFFF
00600214: 41820008
00600218: 807F0010
0060021C: 80010014
00600220: 7C0803A6
00600224: 83E1000C
00600228: 38210010
0060022C: 4E800020
00600230: 9421FFF0
00600234: 7C0802A6
00600238: 93E1000C
More [,,q] ? _ q<return>  ok
0 > _

The target address is 0x600200, or 0x200 bytes into the file loaded at 0x600000. This address was composed by the 600000 200 + portion of the chain. Each PowerPC instruction is four bytes, so four instructions requires sixteen bytes. To begin disassembly four instructions before the target address, substract 0x10 from the target address (10 -).

Running this through an object code disassembler yields:

Disassembling PowerPC code from 0434BFE0
  No procedure name
            0434BFE0   addi       r31,r9,0x200C                           | 3BE9200C
            0434BFE4   lis        r9,0x0003                               | 3D200003
            0434BFE8   lwz        r0,0x3488(r9)                           | 80093488
            0434BFEC   stw        r3,0x000C(r31)                          | 907F000C
            0434BFF0   mr         r3,r31                                  | 7FE3FB78
            0434BFF4   mtlr       r0                        ; LR = 0x0008 | 7C0803A6
            0434BFF8   blrl                                               | 4E800021
            0434BFFC   cmpwi      r3,-0x0001                              | 2C03FFFF
            0434C000   li         r3,-0x0001                              | 3860FFFF
            0434C004   beq        $+0x0008                  ; 0x0434C00C  | 41820008
            0434C008   lwz        r3,0x0010(r31)                          | 807F0010
            0434C00C   lwz        r0,0x0014(SP)                           | 80010014
            0434C010   mtlr       r0                        ; LR = 0x0008 | 7C0803A6
            0434C014   lwz        r31,0x000C(SP)                          | 83E1000C
            0434C018   addi       SP,SP,0x0010                            | 38210010

Advanced Exercises

For the disassembly, the author, working in MacOS 9.2.2, pasted the output from OF into BBEdit, removed the addresses, pasted the hexcodes into a data fork of an empty file using Resourcerer 1.2.5, and used its ability to switch to MacsBug to display the opcodes, logging the results. Using the operating system and tools of the reader's choice, duplicate the author's efforts.


In conclusion

This tutorial wandered over fields and streams, showing seemingly unrelated aspects of Forth. However, with only a small amount of thought, it should be clear that the reader is now equipped with a good starting point for debugging in Open Firmware. Should it ever get written, Part III will cover bootstrapping an operating system beginning with the bootloader. However, a placeholder has been created with the answers to this tutorial's Advanced Exercises.

About the author

Gregory T. (tim) Kelly began interfacing software to hardware in the late-90s while pursuing a graduate degree in Physics he never finished, and the time since has been marked by wide ranging interests, jobs and adventures. He can be reached at gtkelly at this domain (dialectronics.com).

Resources, organized by topic

The references are in addition to those in Part I.

http://developer.apple.com/documentation/Hardware/hardware2.html - Hardware Notes for all Macintosh models

http://developer.apple.com/documentation/Hardware/Developer_Notes/Macintosh_CPUs-PPC_Desktop/PPC_7500_8500.pdf - base Hardware note for 7500/8500 models

http://developer.apple.com/documentation/Hardware/Developer_Notes/Macintosh_CPUs-PPC_Desktop/PPC_7300_7600_8600_9600.pdf - suppliment covering the updated versions of the 7500/8500 series

http://developer.apple.com/documentation/Hardware/Developer_Notes/Macintosh_CPUs-G4/PowerMac_G4_16Feb00/PowerMacG4.pdf - Hardware note for AGP G4

http://developer.apple.com/documentation/Hardware/Developer_Notes/Macintosh_CPUs-G5/PowerMacG5/PowerMacG5.pdf - Hardware Note for dual processor G5

http://developer.apple.com/technotes/tn/tn1044.html - Fundamentals of Open Firmware, Part III: Understanding PCI Expansion ROM Choices for Mac OS

IEEE Std 1275-1994 Standard for Boot (Initialization, Configuration) Firmware, Core Practices and Requirements



http://developer.apple.com/technotes/tn/tn2004.html - Debugging Open Firmware Using Telnet

http://developer.apple.com/technotes/tn/tn2023.html - Open Firmware Ethernet Debugging II: Telnet downloading

[an error occurred while processing this directive]