-
Notifications
You must be signed in to change notification settings - Fork 1
Theory Open Firmware
This page owns: Open Firmware as NT meets it — the device tree, the client interface, and the path grammar that decides whether you get a file or raw sectors.
This is not a specification primer. IEEE 1275 is a large document and most of it does not come up here. What follows is the part of Open Firmware that a Windows NT boot actually touches, written from the behaviour of Apple's real firmware on a real machine rather than from the standard.
Open Firmware is the boot firmware Apple used on PowerPC Macs, standardised as IEEE 1275. Two things distinguish it from a PC BIOS, and both matter here.
It is a Forth interpreter. The firmware is written in Forth, keeps a Forth dictionary, and will happily give you an interactive prompt on a serial console. Typing at that prompt is a first-class way to find things out, and this project has used it to settle questions that would otherwise have taken days.
It describes the machine as a tree, not as a set of fixed addresses. There is no "the keyboard controller is at port 0x60". There is a node, with properties, at a path.
On the Network Server it identifies itself as Open Firmware 2.26NT — a version Apple built with NT in mind, which is why the machine has any chance at all.
The machine is a tree of nodes. Each has a name, properties, and sometimes methods.
/
├── cpus/
├── memory@0
├── bandit@F2000000 ← the first PCI host bridge
│ ├── 53c825@11 ← a SCSI controller
│ ├── 53c825@12 ← the other one
│ │ └── sd@0,0 ← a disk on it
│ ├── gc@10 ← Grand Central
│ │ ├── escc@13000
│ │ │ └── ch-a@13020 ← the serial console
│ │ └── via-cuda@16000
│ └── 54m30@F ← the video card
├── bandit@F4000000 ← the second bridge
└── chosen ← "what was selected", including bootpath
A path such as /bandit@F2000000/53c825@12/sd@0,0 names a node. The @ part is a unit
address, interpreted by the parent — for a PCI bridge it is a device number, for a SCSI
controller a target and LUN.
/chosen is worth knowing by name: it is where the firmware records what it selected,
including bootpath — the device it is going to boot from. Redirecting a boot is largely a matter
of setting that property, and this project does exactly that to point at a hard disk rather than
the CD.
A node's behaviour comes from a package: a set of methods (open, close, read, write,
seek, load) written in Forth. Opening a device creates an instance of the package, and
you talk to the instance.
Two consequences that come up constantly:
Instances are a finite resource. Every open must be matched by a close. The veneer had a bug where every close failed, twelve instances leaked, and the CD simply stopped opening — a failure with no message that looked like a media fault.
A package can be layered. When you open a disk with a filename, the firmware instantiates a filesystem package on top of the block device. When you open it without one, it does not. That distinction is the subject of §5, and it is the single most consequential detail on this page.
A program the firmware has loaded — the veneer, in our case — does not call Forth words directly. It calls the client interface: a single entry point taking a small argument array, with services named by string.
| Service | What it does |
|---|---|
finddevice |
path → phandle (a node) |
getprop |
read a property |
open |
path → ihandle (an instance) |
read, write, seek
|
on an instance |
close |
release an instance |
claim, release
|
memory |
call-method |
invoke a package method by name |
exit |
give up and return to the prompt |
Everything the veneer does for NT is built from these. When NT's loader asks ARC to read a sector,
that becomes a seek and a read on an ihandle, several layers down.
→ The veneer
Here is where the subtleties live.
An Open Firmware path may carry arguments after a colon:
/bandit@F2000000/53c825@12/sd@0,0 : 1 , \os\winnt40\osloader.exe
└──────── the device ───────────┘ │ └── a file on it
└────── which partition
| Form | What you get |
|---|---|
sd@0,0 |
the whole disk, raw |
sd@0,0:0 |
the whole disk, raw (partition 0 conventionally means "all of it") |
sd@0,0:1 |
partition 1, raw — reads are relative to its start |
sd@0,0:1,\path\file |
a file inside partition 1's filesystem |
Why NT needs the raw partition form to work. NT's loader carries its own FAT, NTFS, HPFS and CDFS readers. Before it can open a file it opens the partition and reads sector 0 to work out what filesystem is there. If a raw open silently hands back the whole disk instead of the partition, the loader reads the MBR where it expected a BIOS parameter block, recognises nothing, and reports an error about disk partition tables that has no connection to partition tables.
Apple's firmware handles this correctly on a hard disk. Driven by hand at the prompt, :1 and
:2 return each partition's boot sector and :0 returns the MBR — exactly as they should.
On a CD it does not. Asked for :N on an ISO, Apple's disk-label package hands back the
root directory as a file. Asking for sector 0 therefore returns a directory record; a checksum
over it fails; and NT concludes its boot device is inaccessible. The workaround — forcing raw
opens down the whole-device route — is correct for the CD and exactly wrong for a hard disk,
which is ledger row 4 and cost this project two separate walls.
The firmware prompt is 0 >, on the serial console. It is worth being comfortable there, because
it answers questions about the machine that no amount of reading will.
dev /bandit@F2000000/53c825@12 \ make a node current
ls \ list its children
.properties \ show the current node's properties
words \ list the current package's methodsOpening a device and reading from it directly — this is how the :N behaviour in §5 was settled,
rather than inferred:
variable ih
" /bandit@F2000000/53c825@12/sd@0,0:1" open-dev ih !
600000 200 " read" ih @ $call-method .
600000 10 dump
ih @ close-devTwo practical notes from doing a lot of this:
- The serial input FIFO is small — around sixteen characters. A long line typed in one burst loses its head, silently, and you get a baffling error about a word that is half a word. Feed long lines in chunks.
-
Not all memory is mapped.
dumpat an address the firmware has not claimed throwsDEFAULT CATCH!. Usealloc-memto get a buffer it knows about.
" /bandit@F2000000/53c825@12/sd@0,0" encode-string " bootpath" _chosen (property)That sets /chosen's bootpath property — how this project redirects a boot from the CD to a
hard disk. encode-string turns a Forth string into a property value; _chosen pushes the node;
(property) writes it.
nvram holds settings that survive a reboot — little-endian?, boot-device, and the rest. Two
things to know: it is Apple's format, not ARC's, so NT's boot configuration cannot live there
without a translation layer that does not exist; and little-endian? true followed by a reboot is
what puts the machine into the mode NT runs in.
For completeness, here is what actually happens before NT is involved at all:
-
little-endian? true, then reboot the firmware into little-endian mode. - Load
VENEER.EXEoff a staging disk with the ROM'spe-loaderpackage, laid out at0x50000. - Apply the veneer patches (the ledger, rows 1–5).
- Point
/chosen'sbootpathat the device to boot from. -
go.
From step 5 onward the veneer is in charge and the machine is pretending to be ARC.
- The veneer — what sits on top of all this.
- ARC — what it has to pretend to be.
- Little-endian PowerPC — what step 1 above actually does.
-
The NT boot chain — what happens after
go.
Corrections welcome — this wiki is edited directly, so nothing here has had a review. Repository · STORY.md · GPL-2.0-only
Start here
Theory
- Why NT on a Power Mac is hard
- Open Firmware
- ARC
- The veneer
- The NT boot chain
- The HAL contract
- The NT PowerPC ABI
- Little-endian PowerPC
- How Setup chooses a HAL
- The NT video stack
Machines
Emulator
- Getting Granny Smith
- Media you must supply
- Building the HAL
- The boot floppy
- Preparing disks
- Running text-mode Setup
- Capturing the installed image
- Booting the installed system
- Iterating on the HAL
- Checkpoints and deltas
- Making an OEM CD (retired)
Real hardware
Debugging
- The emulator shell
- Reading NT binaries
- Decoding a bugcheck
- When your instrumentation lies
- Debugging recipes
Reference
- HAL exports
- ARC environment variables
- The veneer's VrDebug bitmask
- Veneer patch catalogue
- Address and interrupt map
- Error codes seen
Project