"Beginning Open Firmware debugging techniques, Part I: The command line interface" and its prerequisites.
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:
http://developer.apple.com/documentation/Hardware/Developer_Notes/Macintosh_CPUs-G5/PowerMacG5/PowerMacG5.pdfAdvanced 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
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
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 ok 0 > _
Top-most is the CPU device. Four of the next seven nodes (
/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 (
/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
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 ok
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 ok 0> _
The first two are self-explanatory, and the reg property will be discussed at a later time. Examination of
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:
Standard property, encoded as with encode-int, that represents the internal processor speed (in hertz) of this node.
clock-frequency value is a long integer expression of the CPU's clock speed in hexidecimal.
0 > showstack<return> ok Empty 0BEBC200 decimal<return> ok 200000000 _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:
200000000 _ drop<return> ok Empty _ hex 8000 decimal<return> ok 32768 _
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.
32768 _ drop dev l2-cache<return> ok Empty _ pwd<return> /PowerPC,604@0/l2-cache@0,0 ok Empty _
The absolute path reflects the devices and their nodes.
Empty _ .properties<return> name l2-cache device_type cache i-cache-size 00040000 d-cache-size 00040000 i-cache-sets 00002000 d-cache-sets 00002000 cache-unified
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> ok 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> ok 0 > ls<return> ff83b8c0: /PowerPC,G4@0 ff83bc38: /l2-cache ok 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 reservation-granule-size00000020 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 graphics performance-monitor altivec data-streams 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 ok 0 > ls<return> ff83bc38: /l2-cache ok 0 > dev l2-cache<return> ok 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 cache-unified clock-frequency 0bebc200 ok 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:
262144 _ 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 DIMM0/J21 DIMM1/J22 DIMM2/J23 DIMM3/J24 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 SDRAM dimm-speeds PC100-222S 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:
Empty _ 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 ok Empty _
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:
Empty _ devalias kbd<return> /bandit/gc/via-cuda/adb/keyboard ok Empty _
One would use an alias in the same manner as any device path:
Empty _ dev kbd pwd<return> /bandit@F2000000/gc@10/via-cuda@16000/adb@0,0/keyboard@0,0 ok Empty _
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:
Empty _ devalias<return> 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 ok Empty _
A third usage of
devalias is to create a non-persistent alias, rather useful when an alias does not exist in the OF ROM:
Empty _ devalias keyboard /bandit@F2000000/gc@10/via-cuda@16000/adb@0,0/keyboard@0,0<return> ok Empty _ devalias<return> 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 ok Empty _
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.
devalias output from an AGP G4:
Empty _ devalias<return> 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 ok Empty _
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.
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:
Empty _ dev /chaos/control words<return> close restore draw-logo write open read-rectangle fill-rectangle draw-rectangle get-colors set-colors color! color@ control# ok 0> _
Notice there is no "read." Of course, for an output device, it doesn't make sense. However, consider:
Empty _ dev ttya words<return> write read read-ahead close open ok Empty _
A serial port needs to be bidirectional.
Empty _ dev kbd .properties<return> words name keyboard device_type serial reg 00000000 00000000 00000000 reset get-key-map read close open ok Empty _ 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 openAdvanced 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.
Empty _ 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 ok Empty _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
Empty _ 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 iso6429-1983-colors assigned-addresses 82015814 00000000 94000000 00000000 00001000 82015818 00000000 90000000 00000000 04000000 ok Empty _
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.
open-dev have slightly different functionalities. The former selects the device for the active device, while the latter opens the package without selecting it.
Empty _ dev ttya<return> ok Empty " testing" write<return> testing ok 7 _
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.
7 _ drop<return> ok Empty _ unselect-dev<return> ok Empty _ pwd<return> no active package ok Empty _ " testing" " write" " ttya" open-dev $call-method<return> ttyaing ok 7 _The example shows
unselect-devunselecting any device as the active device, and then using
$call-methodto call the instance created by open-dev. Here is a strong example of how stack contents are manipulated:
7 _ drop<return> ok Empty _ " 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 _ clear Empty _ 7 buffer: myBuf<return> ok Empty _
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.
myBufis in fact a value - the memory location of that buffer.
Empty _ myBuf<return> ok -728440 _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
Empty " 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 7 _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
type<return> testing ok Empty _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
dup. In practice, one would use the Forth word
value (x "new-name< >" -- )to store the value of a number. For example:
Empty _ " testing" value len<return> ok -7F1610 _ 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 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
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 _ clear<return> _ Empty _ myBuf len " write" " ttya" open-dev $call-method<return> testing ok 7 _
While in a telnet or serial session, send text to the display device.
The only quantitative exercise from the last tutorial was formatting a Forth chain to display opcodes around a particular address:
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 (
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 | 38210010Advanced 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.
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.
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).
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