-
Notifications
You must be signed in to change notification settings - Fork 1
Emulator Building the HAL
This page owns: getting from a clean checkout to
build/hal.dll, and what the build is doing that an ordinary one does not.
git clone https://github.com/pappadf/powermac-nt-hal.git
cd powermac-nt-hal
makeThat is the whole thing. It takes a few seconds and produces build/hal.dll.
What follows is why that works at all, because building a Windows NT PowerPC binary with an open-source Linux toolchain is not something the toolchain was designed for.
clang-18, lld-18
|
any recent LLVM works |
python3 |
for the three build-time tools |
No Microsoft toolchain. No DDK. No import libraries. That is deliberate: the historical route for building NT PowerPC code involves Microsoft's PowerPC assembler and the NT 4.0 DDK running under Wine, which works but makes the project impossible to contribute to casually. Everything here builds from packages your distribution already has.
The DDK contract — structure layouts, IRQL values, vector numbers — is restated in
include/nt.h in this
project's own words, with _Static_asserts where a wrong layout would otherwise be silent.
src/*.c src/thunk.S
│
│ clang --target=powerpcle-unknown-linux-gnu -mcpu=604
▼
ELF objects ─────────────────┐
│ hal.imports / hal.exports
tools/mkstubs.py ────────────┤ │
│ │ ▼
▼ │ imports.S / exports.S (IAT slots, descriptors, thunks)
ld.lld -T hal.ld --emit-relocs│
▼ │
build/hal.elf ────────────────┘
│
│ tools/elf2pe.py
▼
build/hal.dll ← an NT PowerPC PE
Three things are worth understanding.
NT PowerPC does not call functions by address; it calls them through function descriptors, and
crossing a module boundary means changing the TOC register. mkstubs.py generates the assembly
for both directions from two plain text files:
-
hal.imports— every kernel function the HAL calls. Produces an import-address-table slot and a stub per name. -
hal.exports— every function the HAL publishes. Produces a descriptor per name, and a thunk that gives the kernel a stack frame of its own before calling our C.
That last clause is not optional, and the reason it exists is the most expensive bug this project has had. → The NT PowerPC ABI
lld emits ELF; NT's loader wants a PE with machine type 0x1F0. elf2pe.py converts one to the
other: function descriptors for the exports, an import directory naming ntoskrnl.exe with the
IAT pre-filled the way NT's boot loader requires, and base relocations mapped one-to-one from the
ELF relocation kinds.
The build passes -mllvm -combiner-store-merging=false. That is not tuning — it stops clang
merging adjacent byte stores into a single word store, which faults on a 604 in little-endian
mode at a misaligned address. It is one of three shapes of the same trap, and the other two need
volatile at the point of use rather than a flag.
→ Little-endian PowerPC
Two checks worth running after any non-trivial change.
No byte-reversed accesses. The compiler will happily turn a hand-written endian conversion
back into a single lwbrx, which faults at a misaligned address. This should print nothing:
llvm-objdump -d build/hal.elf | grep -E 'lwbrx|stwbrx|lhbrx|sthbrx'No frame-ownership bugs. Disassemble any exported function and look at the first few
instructions. If one stores to a positive offset from the incoming r1, it is writing into the
caller's frame — which on NT PowerPC is where the caller's saved TOC lives:
llvm-objdump -d --disassemble-symbols=HalGetBusDataByOffset build/hal.elf | headYou should see the C function's own prologue reached through a thunk, never called directly by the kernel.
Two builds of the same tree are byte-identical. The PE timestamp, which would otherwise vary,
comes from SOURCE_DATE_EPOCH and defaults to zero:
make && md5sum build/hal.dll
make clean && make && md5sum build/hal.dll # same
SOURCE_DATE_EPOCH=$(date +%s) make # if you want a real timestampThis matters more than it usually does here, because the HAL's size is load-bearing in two separate places: the CD's directory record carries it for Setup's post-copy checksum, and every driver's load address shifts when it changes. → Making an OEM CD, Running text-mode Setup
So: whenever the HAL changes size, re-run mkoem.py.
src/init.c |
HAL initialisation phases, the configuration tree, memory descriptors |
src/ints.c, irql.c
|
interrupt dispatch and IRQL |
src/clock.c |
the decrementer |
src/pci.c |
Bandit configuration space, bus address translation, resource assignment |
src/disk.c |
the partition-table exports NT puts on the HAL side |
src/misc.c, arc.c
|
the ARC environment, drive letters, the real-time clock |
src/cuda.c |
Cuda transport, for ADB |
src/vga.c, display.c
|
the Cirrus console the HAL draws on |
src/thunk.S |
the ABI glue, in both directions |
include/nt.h |
the DDK contract, restated, with static asserts |
include/ans.h |
the machine-specific addresses |
Most of it is about the TNT chipset rather than the Network Server specifically; the ANS-only part is a short enumerated list — the external interrupt routing, the second Bandit, the two SCSI controllers, the on-board video and the timebase.
- Making an OEM CD — getting the HAL onto installation media.
- Iterating on the HAL — testing a rebuild without reinstalling.
- The HAL contract — what the code has to do.
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