Forth Lesson 9

From OLPC
Jump to: navigation, search
Mitch Bradley's Forth and
Open Firmware Lessons:

Review

In the previous lesson, we learned:

  • How to display strings with ."
  • How to enter literal strings with "
  • How to allocate string buffers with buffer: and alloc-mem
  • Various operators that work on strings

Open Firmware

In this lesson we will switch gears and talk about Open Firmware. Heretofore we have been looking at Forth language stuff that is largely independent of Open Firmware.

Open Firmware (OFW) is an application written in Forth which includes a live Forth programming environment. It can be viewed as a Forth system that has been extended with additional words to make it useful for a special purpose. That purpose is boot firmware - initializing and testing a computer system from the ground up, then loading and executing an operating system. In the PC world, the conventional name for such firmware is BIOS, but I don't like to use that name for Open Firmware, because the "flavor" of OFW is very much different from conventional BIOS. OFW has interactive debugging capabilities that far exceed those of the usual PC BIOS.

OFW interacts with the computer's hardware - CPU and I/O devices - without help from the operating system. The purpose is to test that hardware, to load the operating system, and to perform system maintenance functions such as installing new versions of the OS.

Device Tree

Since OFW is focused on computer hardware, particularly I/O devices, it needs a framework for naming such devices. That framework is the "device tree", a hierarchical representation of the computer's hardware.

The device tree is a collection of "device nodes", objects that contain information ("properties") describing the associated device and bits of code ("methods") for using that device.

The device tree structure closely mimics the computer's physical addressing structure. The top level (root) of the tree represents the physical bus that connects one or more CPUs directly to the top level of I/O devices. That top level, devices that connect directly to the root level, often consists of memory, AGP ports or other high-speed graphics devices, and bridges to other buses, for example PCI host bridges.

A device can either be an I/O function (for example a sound card) or a bridge to another bus (for example a PCI host bridge). Bridge devices create additional levels of the tree. Within the tree, the children of bridge devices are the devices that are plugged-in to that bridge. They may in turn be bridges themselves, creating additional levels of the tree. For example, a USB host bridge might be connected to a PCI host bridge.

Device Specifiers

Device nodes are named by text strings called "device specifiers". A device specifier is either an explicit "pathname" or a shorthand "devalias".

Pathnames

A device node can be named by a path that tells how to get to that device from the root of the tree. For example,

 /memory

is a simple path from the root of the tree ("/") to the "memory" node underneath.

 /pci/display

is a path from the root, through the "pci" node (a PCI host bridge), to a "display" device on that PCI bus.

What if there were two display devices on that PCI bus? They can be distinguished as follows:

 /pci/display@6,1
 /pci/display@f,0

The first PCI display device is at PCI device #6, function #1, the second at device #f, function #0. The second one could have been written as

 /pci/display@f

as the default function number is 0.

The stuff after the '@' corresponds to a physical address within the address space defined by the parent node. In the example, the parent node is a PCI bridge, so the address space is a slot address in PCI configuration space. For any given parent, the exact form of the physical address depends on the way the particular bus selects devices underneath it at the hardware level. The physical address is usually written in the form "number" or "number,number", but that is not a hard and fast rule. A bus node can define an almost-arbitrary "address name" space for its children.

Any component, not just the last one, can specifiy a physical address, as in:

 /pci/usb@f,5/scsi/disk@1
Devaliases

The pathname syntax lets you select any device unambiguously, even in the face of very large systems with many levels of bus bridging and numerous I/O devices.

Most systems, however, have only a few devices, and even in large systems, there are a few devices that a user is especially likely to want to name - for example the primary keyboard and primary display. For commonly-used devices, OFW provides a "shorthand" way to name a device. That shorthand name is called a "devalias". A devalias is just a simple name with no slashes.

  ok devalias screen /pci/display@6,1

would make it possible to write "screen" instead of "/pci/display@6,1". A devalias can be used in place of a pathname in any context.

Systems usually come with a set of predefined aliases for important devices. You can see the current list of devaliases with:

  ok devalias

You can search the list for the first match:

  ok devalias screen

Some predefined devaliases point to specific devices that are known when the OFW is compiled. Others may point to devices that dynamically created at start-up according to some rule. For example, a typical rule is: If a USB keyboard is attached, the "keyboard" devalias refers to it, otherwise "keyboard" refers to the PS/2 keyboard. Similar rules often select the default display device ("screen" devalias) or default boot disk ("disk" devalias).

Ambiguous Pathnames

Suppose that there are two PCI display devices, but you say only

 /pci/display

That is ambiguous, because it is not clear which of the two you mean. In this case, OFW will choose the first one that it encounters in its search. In FirmWorks OFW implementation, searches start with the most recently created device, so the most recent one (perhaps the last one to be discovered) will be found.

Lazy Pathnames

Suppose that you have a really complicated pathname, like

  /pci/pci/usb/scsi/disk

It's usually unnecessary to type the whole thing, even if you don't have a convenient devalias for that path. You could type just:

  /disk

OFW would search the device tree through several levels to locate a match. In the common case where there are no duplicate names, this usually finds the device you want. When in doubt, you can supply the full path, but the lazy approach is a great time-saver in the many instances when it works.

Browsing Device Nodes

You can get a list of the system's device nodes with:

  ok show-devs

To restrict the display to just a portion of the tree, try, for example:

  ok show-devs /pci

You can inspect a device node with, for example:

  ok dev /memory
  ok .properties

The "dev" command changes the Forth interpreter context so that the contents of a device node are visible, making that node the "active package". ".properties" shows the properties of the active package.

The properties represent all of the static information that is known about the node. There are several standard properties that are common to all nodes or to all nodes of a given class. For example, the standard "name" property is the string that identifies the node in a pathname. The standard "reg" property contains a representation of the device's physical address within its parent's address space, including the part that matches the address after '@' in pathnames.

In addition to its standard properties, a device node may contain additional device-specific properties to specify arbitrary information about the device. The Open Firmware standard defines properties that apply to all OFW systems. Supplementary "binding" documents define additional properties for special circumstances.

Use "dev" and ".properties" to inspect some device nodes.

Use "dev ..", "dev /", "ls" and "pwd" to navigate the device tree.

To return to the normal Forth context, in which no particular device node is the active package, type:

  ok device-end

or the abbreviated form

  ok dend

Thus endeth the lesson.

Next Lesson: Device Nodes