No description
  • C++ 80.8%
  • Shell 9.8%
  • C 7.7%
  • Python 1.2%
  • CMake 0.5%
Find a file
Repository files (latest commit first)
Filename Latest commit message Latest commit date
zclaude 5437f10226 Merge wi-cpring: a big-endian host's ring is byte-swapped on fetch, and the CP is told so
Half of what makes Linux's radeon DRM reach hardware direct rendering on the emulated
XVR-100. The other half is softsparc's, and the two are a PAIR -- either one alone
leaves the ring test failing with the identical symptom.

THE BUG. CP_RB_CNTL bits [17:16] are a byte-swap field. radeon_reg.h defines exactly one
value for it, RADEON_BUF_SWAP_32BIT == (2 << 16) (radeon_reg.h:3302), and r100_cp_init
(r100.c:1198-1204) is the ONLY place in the entire radeon driver that ever sets it for
this chip family:

        tmp = (REG_SET(RADEON_RB_BUFSZ, rb_bufsz) | ...);
    #ifdef __BIG_ENDIAN
        tmp |= RADEON_BUF_SWAP_32BIT;
    #endif
        WREG32(RADEON_CP_RB_CNTL, tmp | RADEON_RB_NO_UPDATE);

A grep of drivers/gpu/drm/radeon for __BIG_ENDIAN returns exactly one hit in r100.c --
that one. Every other hit is r300+, ATOM or fbdev. So this bit, and nothing else, is the
whole big-endian story for an R100.

WHY IT MATTERS. A driver builds the ring with plain CPU stores: radeon_ring_write() is
`ring->ring[ring->wptr++] = v` into an ordinary kmap of the GTT buffer object, and an IB
is filled the same way -- userspace's own native stores, copied in verbatim by the CS
ioctl, with radeon_get_ib_value() reading them back as a plain `ib[idx]` and no
byte-order helper anywhere. On a big-endian host every one of those dwords lands in
memory MSB-first, and the CP is told to byte-reverse each as it fetches it. Unmodelled,
a sparc64 guest's very first ring packet decodes as garbage: PACKET0(0x15e4, 0) ==
0x00000579 is fetched as 0x79050000, a PACKET1, and the CP halts on ring dword 0. That
is precisely the guest symptom -- r100_ring_test() failing with the scratch register
still holding its 0xCAFEDEAD sentinel.

read_cmd_dword() is now the single point every CP fetch of DRIVER-AUTHORED data goes
through: ring, IB, vertex and index.

DELIBERATELY NOT APPLIED to the GART page table, which keeps read_le32():
r100_pci_gart_set_page() writes those entries with an explicit cpu_to_le32()
(r100.c:709-714), so they are little-endian on the wire on every host regardless of this
bit. Getting that distinction wrong would have broken the GART walk instead.

Only field value 2 is decoded. Values 1 and 3 exist but the driver never emits them and
no fetched source states what they mean, so they are treated as no-swap and recorded
under "Not implemented" rather than guessed at.

TESTS, and the counter-arm is the point. New scenario 1b reproduces the guest's symptom
as a unit test -- against unmodified cp.cpp it fails with the scratch register reading
0xcafedead, the CP halted, and RPTR stuck at 1. Scenario 1c is the arm that keeps the
fix honest: swap bit CLEAR plus a big-endian ring must NOT swap, must halt, and must
record header 0x79050000. It fails if the swap is ever made unconditional.

Verified: rv100 ctest 15/15, and 15/15 again cross-compiled BIG-ENDIAN (s390x + qemu-user
via rig/run-ctest-bigendian.sh, with readelf -h confirming "2's complement, big endian" /
"IBM S/390") -- which is the right way to test a byte-order fix.

UNCERTAIN, recorded rather than smoothed over: the swap is applied to vertex and index
fetches too, which is inferred from this being the driver's only big-endian
accommodation for the chip, not measured -- no big-endian 3D draw has been run. Pixel
and surface data (framebuffer, textures, depth) are untouched here; that is
RADEON_SURFACE_CNTL's business and is unexercised big-endian.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FHazRUnQUw1PpsqVjzcH7K
2026-09-07 04:29:10 -04:00
docs docs: rewrite the handoff, and correct two M5 sections the merge invalidated 2026-09-04 14:34:19 -04:00
frontend/qemu frontend/qemu: tick each head at its own derived refresh period 2026-09-04 14:06:31 -04:00
include/rv100 dev: an access can now report that the device did not respond 2026-09-05 11:48:49 -04:00
rig rig: make run-sparc64.sh honest about what an empty trace proves 2026-09-04 13:53:24 -04:00
roms docs: VBIOS evidence, and the inventory rows the VGA work earns 2026-09-04 11:03:29 -04:00
src cp: a big-endian host's ring is byte-swapped on fetch, and the CP is told so 2026-09-07 01:03:58 -04:00
tests cp: a big-endian host's ring is byte-swapped on fetch, and the CP is told so 2026-09-07 01:03:58 -04:00
tools roms: record verified provenance for both ROM images; checksum x86 images 2026-09-01 20:31:49 -04:00
.gitignore rig: run the whole suite big-endian, and fix the three test bugs it found 2026-09-04 12:08:01 -04:00
CMakeLists.txt dev: an access can now report that the device did not respond 2026-09-05 11:48:49 -04:00
HANDOFF_2026-09-05.md handoff: a screendump can be of the wrong display device 2026-09-04 14:47:56 -04:00
README.md M0 scaffold: RV100 device model, register table, PCI config space 2026-09-01 20:30:21 -04:00
ROADMAP.md glxgears: the gears workload renders, with no library change 2026-09-03 09:15:06 -04:00

rv100 — a device model of the ATI RV100 (Radeon 7000 / Sun XVR-100)

An architecturally-exact, host-independent emulation of the ATI RV100 graphics chip: PCI config space, MMIO register file, memory controller, CRTC/scanout, the CP command processor, the 2D engine and the fixed-function 3D pipeline.

The chip shipped under several names. They are the same silicon, PCI ID 1002:5159 (RV100 "QY"):

  • ATI Radeon 7000 / Radeon VE — the retail x86 card.
  • Sun XVR-100 (part 375-3290) — the SPARC workstation card, whose only difference is the expansion ROM: an IEEE-1275 FCode image instead of an x86 BIOS. Confirmed by dumping the ROM off a real Sun Ultra 25: one image, PCIR code type 1, SUNW,XVR-100.

Why this chip

RV100 is the last generation before programmable shading. It has no transform-and-lighting unit at all (the T&L block the full R100 carried is fused off), so the driver submits vertices already in screen space, and the fragment stage is a configurable texture-combiner state machine rather than a shader core. That makes it small enough to model exactly while still sharing the CP PACKET0/PACKET3 command model and much of the register layout with every later Radeon up to R5xx.

Not tied to one host

The core is a plain library with no knowledge of any emulator. It reaches the outside world only through HostOps callbacks (system-memory read/write for DMA, interrupt raise, scanout notify). Front-ends planned:

  • softsparc — the Sun Ultra 25 emulator, the original motivation.
  • QEMU — a PCI device model, which is also how we drive a real Linux guest with the real radeon kernel driver and Mesa Amber's r100.
  • standalone — command-stream replay for deterministic regression tests.

Build

cmake -B build -S .
cmake --build build -j
ctest --test-dir build --output-on-failure