Repository navigation
tezro: add the SGI Tezro, with the V12 and up to four processors - #409
Draft
jamesbraid wants to merge 62 commits into
Draft
jamesbraid wants to merge 62 commits into
jamesbraid wants to merge 62 commits into
Conversation
EntryHi, the TLB match and the xuseg bounds check all stopped at VA bit 39, but the R10000 family implements 44 bits: VPN2 is 43:13 and the user segment is 16 TiB (R10000 UM pp. 219, 288). A 64-bit kernel can put an n64 user stack above 40 bits (0x0fff_ffff_aee4, for one). EntryHi dropped VA[43:40] and such an address took an Address Error. The width now comes from the part's VA_BITS in each place: the EntryHi write mask, the TLB's 64-bit compare (Tlb::set_va_bits, installed by the executor beside set_cache_attrs), the xuseg bounds check, the micro-TLB's cacheable segment test and the EntryHi value a TLB exception loads. The mask was spelled three times and is now one const fn, mips_tlb::vpn2_mask. All of it is a const of the CPU model or set once at construction; the TLB lookup is unchanged. This reverses a comment in the executor (b7d2a18) that said widening the EntryHi mask breaks the IP28 and Octane kernels. Those two machines and an O2 set to the R10000 now see 44 bits. The R4400 and R5000 (Indy, Indigo2, O2 with an R5000) stay at 40. One test maps a page at 0x123_4000_0000 on the R10000 and the R4400: the R10000 keeps EntryHi and reads the page, the R4400 masks it and takes Address Error.
A PROM's cache walk reads and writes the primary caches with Index Load Data and Index Store Data. Only the secondary cache had a data array, so an Index Load Data on a primary cache always returned zero and an Index Store Data was dropped. Give the primary caches data arrays and mask each store to its diagnostic field (R10000 UM pp. 262, 271-272): 36 data bits and 1 parity bit for the instruction cache, 32 and 4 for the data cache, 64 and 10 for the secondary. An Index Store Data to the secondary cache writes a quadword, so the other doubleword becomes zero and takes the same check bits (p. 91). A mapped primary Index Load Data or Index Store Data still takes TLB Refill and Invalid, so CACHE now translates the operand for those two; the other index operations still do not. The secondary data slot is now the address >> 3 where it was >> 4, so the walk gets one slot per doubleword instead of two doublewords sharing one. This affects the Octane, the IP28 and an O2 with an R10000. Tests cover a primary round trip and the quadword store. Index Store Data is a diagnostic walk's operation, so it runs out of line in a cold function, and CACHE tests the primary-cache pair against a bit mask. Written inline they grew cache_op and exec_cache in every CPU model and moved the interpreter's hot loops, which cost sys/llsc about 2% on a ship build.
Fuel uses two independent SCSI buses behind one ISP12160 function. Reuse the ISP1040 command engine and let the part trait own the firmware, register and channel differences. A bus reset reaches every attached target as unit attention rather than a media change. Keep the reset attention on the shared target interface so a controller can notify each device through the same bus. The ISP12160 is its only caller: the WD33C93A, AIC-7880 and ISP1040 models do not call it, so their targets behave as before. A bus reset restores each disk's write cache bit per channel: a reset of one channel leaves the other's disks alone, and a chip or soft reset restores both. The engine changes reach the Octane's ISP1040: mailbox commands now run with dispatch held, and the target interface gains the bus reset. The Octane's IRIX 6.5.30 SCSI disk (Octane2, R10000, 256 MiB, V12, jitv2, release build, headless, NAT) boots to the login prompt and runs commands on this branch's tip and on main c6dea8c: login after 126 s on the branch and 109 s on main in one run each (host load makes the times indicative only), the same `hinv` listing of both integral controllers and the disk, and the same command output.
Fuel fits a DP83843 at MII management address 1, where the Octane has an ICS1890. The IOC3 Ethernet controller needs the same packet and link machinery with either PHY, so keep that machinery shared: move the Octane's ICS1890 register model behind a PHY interface, and supply the DP83843's register and negotiation behavior through it. The MAC keeps its existing packet path. The interface carries the snapshot hooks too: the ICS1890 keeps the register save and load it had in the IOC3 model, and the DP83843 saves its registers and negotiation timers the same way, so the shared Ethernet block saves whichever PHY it holds.
Fuel stores its clock and nonvolatile configuration in an M48T35 on the IOC3 byte bus. Keep the register overlay and battery-backed bytes together so latching and persistence share one owner.
Fuel attaches an M48T35 and has different PCI header policy from the Octane. Carry those choices in the population configuration and wire the timekeeper once so byte-bus accesses need no mutable attachment slot. The Fuel's onboard IOC3 also carries the PS/2 keyboard and mouse controller: OpenBSD configures a keyboard controller through the onboard IOC3 (pci/ioc.c, dev/iockbc.c), and the controller core is the one the Octane already has. Turn the controller on in the Fuel's population, as the Octane's is, and test it by sending a host key through the IOC3 registers: it latches the keyboard interrupt and arrives in the K_RX register, and a mouse command answers from its own port, both before and after a chip reset.
Fuel boot flash uses AMD unlock, program and erase commands. Keep that state machine separate from array storage and bus decode so its command handling can serve each fitted flash array. The Intel and AMD command sets need the same rules for seeding and writing flash images, so share the file storage while each command engine keeps its own command state.
Fuel PROM 6.210's memory test fails when uncached SB and SH reach the bus carrying only their low bits. Its L1 console shows: mtest_bank_main failed: Walking Address RSLT mtest_bank_mai FAIL diag_rc = 43 Walking Address In both memory banks the expected halfwords 0xa55a, 0x5aa5, 0x9999 and 0x6666 read back from the ECC check-bit view as 0x0000. The PROM stores each check word with SH from a register whose low 16 bits are zero; the word sits higher in the register, at a position that depends on the tested address. With the whole register visible to the ECC view and the same PROM configuration it reports ALL DIAGS PASSED and enables all 512 MiB. Add BusDevice::write_narrow_from, whose default stores the architectural byte or halfword exactly as write8/write16 do, and call it for every uncached SB and SH. Ordinary devices therefore still see the low bits of the register, and only a device that models a path observing the other lanes overrides it; Bedrock's ECC view does, in its own commit. Which lanes real hardware drives for a narrow store is undefined. Debug writes keep the ordinary-width path, and the JIT callouts pass the same source register. The default adds one virtual call to a path that already makes one.
I/O (bedrock_io): Fuel needs Bedrock I/O to route programmed PIO windows to PCI widgets, check inbound DMA permissions and run its two block-transfer engines. Fault reporting and interrupt delivery stay with the block that owns the state, and unsupported accesses are reported. Register map and behaviour are ported from the QEMU model. Node routing (bedrock_ni): Fuel runs as one node with no NUMAlink peer, but the guest still reads and programs these local control words during machine setup. Retain the writes and keep the network fabric out of the model. Crossbar (bedrock_xb): Record the fabric error queues and let software service them, with the outputs available to the Bedrock processor interface. Unsupported queue reads return zero and writes are logged and ignored. Local bus (bedrock_lb): Add the node and region registers, atomic scratch aliases, error and vector status, and the MicroLAN master that board identity parts sit on. The tests drive it together with the I/O, node-interface and crossbar blocks it reports through, which land in this same commit.
The Fuel CPU receives interrupts and timing events through Bedrock PI. Model the fitted processor slot and its node callbacks while keeping unconnected graphics control explicit.
Fuel reaches its AMD flash through executable PROM aliases and programming windows. Share the array bytes and persistent store while preserving the command and erase state of each flash part. The configuration block's CPU type word is 1, the R14000, as the IP35 PROM's own configuration editor lists it (0 for the R10000 and R12000, 2 for the R16000); a test pins it.
Fuel routes PCI configuration, DMA and interrupt packets through two XBridge heads. Keep small sparse PCI decode in window lists so the bridge can share the machine memory map without allocating more Physical tables. A board attached to one of the crossbar's external ports raises its interrupts as crosstalk packets, the same way the PCI heads do. Route them through the same sender, so the Fuel's graphics board reaches the Bedrock's processor interface with no path of its own, and say whether the destination took the packet. The crossbar answers with the Crossbow's widget 0 register map, so it takes its offsets from the Octane's Crossbow model instead of restating them. What stays separate is behavior that differs between the two: the XBridge crossbar's status register carries the requesting port, wide and malformed accesses set the access-error status and answer zero, its interrupts go out as crosstalk packets to any destination, and it resets the heads and ports its fences select.
The Fuel console first passes through the L1 system controller. Reuse the shared 16550 core and keep controller framing, power commands and identity records in its protocol model.
Fuel enumerates a Lucent OHCI controller on PCI. Provide its registers and descriptor scheduling through the USB function interface while leaving devices unattached in the machine configuration. The frame counter follows the machine clock the board hands the controller, and every register read and write first delivers the frame boundaries that clock has passed. Advancing it only from the board's poll, which runs between CPU batches, left HcFmNumber at zero after the guest's own 1 ms wait had expired, and the Fuel PROM reported that SOF did not start within 2 ms. The sync holds one lock from reading the clock to applying the frames, so a timer thread cannot leave a concurrent reader behind it. Entering UsbOperational from Reset or Suspend re-anchors the frame: it loads HcFmRemaining from HcFmInterval, toggle included, and increments HcFmNumber, with the first SOF one frame later (OpenHCI 1.0a 6.2.1, 7.3.2, 7.3.3). The tests cover that entry with frame numbers at and around the overflow flag. Add two low-speed USB functions for the OHCI root ports: a HID boot keyboard and a HID boot mouse, each with one configuration, one boot interface and one interrupt-IN endpoint. They answer the standard requests (descriptors, SET_ADDRESS taking effect after its status stage, SET_CONFIGURATION, status and interface queries) and the HID class requests (GET/SET_REPORT, GET/SET_IDLE, GET/SET_PROTOCOL), and report host keys and pointer moves as boot reports. Reports queue one per change so a key tapped between two polls still shows both edges. Written from the USB 1.1 and HID 1.11 specifications (boot descriptors from appendix B) and the HID Usage Tables, with no code from another emulator. The wheel, remote wakeup and the host layout translation that the PS/2 path has are left out. Ohci::attach loses its test-only gate so a machine can plug the devices in.
Build the Fuel IP34 board around its R14000, Bedrock, XBridge, IOC3 and dual-channel ISP12160. Use one Physical memory table with sparse processor and peripheral views, and retain the CPU memory fast paths after the PROM startup store retires. Uncached byte and halfword stores reach a device through BusDevice::write_narrow_from, which each window forwards at the device-local address; only the memory controller's ECC view reads more than the low bits of the register. With the jitv2 feature, start the compile pool after the memory windows are published, as the Octane and O2 do. Each worker copies the cache geometry once as it starts, and without the windows that geometry has no inline load or store path. Workers read code through the cached plane, which serves plain physical addresses; the uncached views would decode them as node registers. The machine describes where its memory can answer to the CPU: the four 1 GiB slots Bedrock's controller places its two 256 MiB banks in, and the reset PROM. Its disks on both ISP12160 channels are flushed at power-off, quit and exit like the other machines', so a copy-on-write overlay keeps its record of held sectors after a guest `halt -p`. The processor is R14000FuelShadowCache, the Octane's R14000 at revision 2.3 (PRId 0x0f23), the revision a public software inventory of a Fuel prints, where the Octane's part reports 2.4. The raw CP0 word, the FIR and the 128-byte lines are taken from the Octane part, not read from a Fuel. Like the Octane's R14000 it reports "R12000" as its name. The machine loads its PROM through prom_image. Feed the IOC3 PS/2 controller from the window, VNC and CI input, and let a USB keyboard on root port 0 and a USB mouse on port 1 share it. The Fuel's manual shows PS/2 as the standard keyboard and mouse, so ps2_input under [fuel] defaults on and usb_input defaults off; setting usb_input fits the USB pair, and ps2_input = false leaves the PS/2 pair out. The monitor's ps2 command reaches the keyboard and mouse too. PROM loader (prom_image): Add a device-independent loader for PROM images. It accepts a raw payload or a version-six stored or gzip container, decodes the recognized containers explicitly and reports malformed inputs with their path before any bytes reach guest memory. Config profile (config): The Fuel is a new machine profile with two memory banks, wide SCSI buses, no Ethernet adapter choice beyond its built-in one, and no graphics board by default. The [fuel] section selects the serial console (the L1 controller or either IOC3 port), the L1 TCP port and log, and the persistent boot flash store. l1_revision and brick_serial replace the L1's reported firmware revision (default 1.48.1) and brick serial (default EMUN00). ps2_input defaults on and usb_input off, as the Fuel's manual shows PS/2 as the standard keyboard and mouse; setting usb_input fits a USB keyboard and mouse, and ps2_input = false leaves the PS/2 pair out. The key names the part: machine.cpu = "r14000" (r10000, the family, is still accepted), and a Fuel config cannot claim another processor. The Octane's R14000 is revision 2.4; the Fuel's is 2.3.
Index Store Tag stored all 64 bits of a secondary-cache tag, so a walking-1s test over the tag read back bits the R10000 defines as reserved. The manual says they return zero (R10000 UM p. 270, figure 11-31). Keep MRU, the physical tag, state, virtual index and ECC, which assembled is 0x8000_000f_ffff_cdff, and drop the rest. The mask is CpuModel::SECONDARY_TAG_MASK, all ones except on the R10000 family. MRU stays because the PROM writes it as TagHi bit 31 and reads it back. The later parts share the tag layout (the R12000 manual agrees; the R14000 is taken to match), and a PROM's tag test on the R14000 expects the reserved bits to read as zero. Until the R12000 and later models exist here, the family is just the R10000. Tests: an all-ones store on the R10000 and R10000 O2 reads back the mask, an R4400 control keeps TagLo bits 12 and 13, and the walking-1s round trip expects the reserved bits as zero.
The Octane shipped with an R12000 or R14000 and the O2 with an R12000,
but `cpu` took only r4400, r5000 and r10000. An Octane hinv could not
show the later parts, and CP0 Config reported 1 MB of secondary cache
for every R10000-family part, so a 2 MB model printed "1 Mbyte".
Add r12000 and r14000. They share the R10000's programming model
(44-bit virtual, 40-bit physical, the R10000 cache operations) through
model::r10k_family. Config SS is now log2(secondary size / 512 KB). The
Octane's R14000 is the R12000's 2 MB type with its PRId set at run time,
as the Octane R10000 already does, so only two new executors are
compiled.
Octane r10000, r12000, r14000 (195, 360, 600 MHz by default)
O2 r5000, r10000, r12000 (400 MHz; 64-byte secondary lines,
because with 128 the O2 PROM panics before its banner)
IP28 r10000 only
iris-bench gets r12000 and r14000 cells. The bench harness names an
implementation 0x0f CPU an R16000 above major revision 2, and the
prebuilt guest image is refreshed for it.
On the R12000 and later, Index Writeback Invalidate (D) also clears the
line's primary data tag, as Index Load Tag then shows; the R10000 keeps it.
The new parts also keep the upper word on a partial LWR, as the R10000 does;
for the R12000 and later that is a model choice.
Every Fuel has a VPro graphics slot: a Fuel IRIX inventory lists its board under xtalk/13 and OpenBSD finds Odyssey at crossbar widget 13. Build the board there when the machine is given a V12 or a V10, through the widget-offset constructor, and keep none, the serial machine, as the default. The XBridge now reports link 13 alive, the board's DMA enters Bedrock's inbound path as widget 13, and a link reset of port 13 resets it. Its interrupt packets cross the crossbar to Bedrock like the PCI heads'. The status bar's activity (SCSI, IOC3 and Ethernet) and the CPU's Compare ticks reach its LEDs as on the Octane. IRIX's VPro driver writes each processor interface's graphics flow-control registers at attach and tears the board down when the credit counter reads zero, so the interfaces keep those registers; they count no stores and return no credits, so no processor interface is linked to the board's credit accounting. With PROM 6.210 the board passes the graphics diagnostic, and IRIX 6.5.30 with the VPro driver lists Graphics board: V12 and draws the X login screen on it. Input comes from the PS/2 pair the Fuel already has. Configuration accepts the board on the Fuel too, see the config note below. Tests build a Fuel board with each board and check that the processor reaches the board at widget 13 and finds its link alive, that its interrupt packets set and clear a processor interface's pending bit, that a link reset of its port resets it, that a headless Fuel has nothing there, and that the processor interfaces keep their graphics registers. Config (config): A Fuel is reported with both the V12 and the V10, so take either with [graphics] board; the other boards stay refused, with a message that names the Fuel's choices, and none, the serial machine, stays the default. The doc comments for the two boards now say they fit the Fuel as well as the Octane. The Fuel's validation case now accepts both boards and refuses the V6.
Document the supported serial configuration, image inputs, the host input options and the explicit limits for graphics, USB devices and snapshots, in the feature list, hacking notes, debug notes, the sample config and a Fuel page. Startup storage remains a PROM-specific compatibility approximation.
FrameMask (CP0 register 21) is new with the R10000. Bits 15:0 clear EntryLo bits 33:18 as a TLB write passes them on, and the rest of the register reads as zero (R10000 UM v2.0 p. 260). iris had no register 21: mfc0 read zero and mtc0 was dropped, so a kernel that sets FrameMask to keep software flags out of the physical address got no effect. The core holds the register; the executor sets which bits are writable, all 16 on the R10000 and none on the R4400 and R5000. TLBWI and TLBWR pass the masked EntryLo values, and TLBR returns the masked entry. The register is saved with the rest of CP0 and loads as zero from an older save. CpuModel::R10K_FAMILY says a part is in the R10000 family; for now that is the R10000. Nothing changes while FrameMask is zero, its reset value, and the write path runs only on a TLB write. Tests write FrameMask on the R10000 and the R4400, map a page and check the physical address and the readback; a second writes with TLBWR as well as TLBWI and reads the entry back with TLBR.
In 64-bit mode a partial LWR (offset 0 to 2) sign-extended the merged word on every CPU. The manual calls the upper word of a partial LWR implementation specific (NEC U12754E pp. 125-126), and the reference results observed for the R10000 keep the old bits 63:32 at all three partial offsets. With sign extension a test that compares those results miscompares. The interpreter and the JIT now merge the old upper word on the R10000; a full word (offset 3) still sign-extends. The upper word is kept only when 64-bit operations are enabled for the current mode, matching QEMU; otherwise it sign-extends. The later parts are not covered, since nothing shows what they do. The choice is a JitConsts field set from CpuModel::PARTIAL_LWR_KEEPS_UPPER, so compile workers see it. Whether 64-bit operations are on is a byte in the core that the executor sets wherever it re-derives the privilege state, so a compiled LWR spends a byte load and a compare on it rather than decoding Status; the merge runs on the whole register and needs no more work than the old sign-extending sequence. The code-cache fingerprint gains lwr_upper=1 only when it is set, so the R4400 and R5000 machines keep their warm caches. Behaviour changes on the Octane, IP28 and an O2 with the R10000. The R4400 and R5000 are unchanged. Tests run LWR at every offset in the interpreter on the R4400, R5000 and R10000 in kernel, supervisor and user mode with the mode's own 64-bit bit set and clear, run the same offsets as compiled code on the R10000 and R5000 and compare with the interpreter, and check the fingerprint.
On the R10000 EntryLo holds a 28-bit PFN in bits 33:6, a 40-bit physical address, and the two-bit uncached attribute in bits 63:62; 61:34 are reserved (R10000 UM v2.0 pp. 239-240). iris kept the R4400's 30 bits, so the attribute and PFN bits 33:30 read back as zero and never reached the TLB. The core now has an EntryLo mask that the executor sets per part: the R4400 and R5000 keep 0x3FFF_FFFF, the R10000 gets 0xC000_0003_FFFF_FFFF. The uncached attribute is stored and read back only; it is not carried to the bus. Bits 33:32 are physical 39:38, where the Octane's guest keeps software flags; FrameMask (previous commit) clears them on a TLB write, so the page lands at its real address. A TLB write also clears the PFN bits below the page size, so TLBR does not return what was written to them (R10000 UM v2.0 pp. 299-300). The manual documents this for the R10000 only; the later parts follow it as a model choice. The R4400 and R5000 keep the whole PFN. Translation already ignored those bits, so only TLBR changes. The Octane PROM's tlb diagnostic writes 0xa5a5a5a4 and 0x5a5a5a58 with 16 KB pages and expects 0xa5a5a524 and 0x5a5a5a18 back; it reported 320 compare errors. On the Octane, the IP28 and an O2 set to the R10000, mfc0 of EntryLo now returns bits 63:62 and PFN bits 33:30 if the guest wrote them. The R4400 and R5000 are unchanged. Tests check the readback on the R10000 and the R4400, that a page with PFN bits 33:32 set maps to physical 39:38 and is brought back by FrameMask, and the diagnostic's patterns through TLBWI and TLBWR.
…sses The R10000 drives a two-bit uncached attribute on the system bus. For an unmapped xkphys access it is address bits 58:57; for a mapped reference it comes from the TLB's uncached-attribute field, which EntryLo0/1 bits 63:62 load (R10000 User's Manual 007-2490-001). EntryLo already keeps those bits on the family, and a TLB entry holds them, but the entry's physical base dropped them, so a mapped uncached page always reached attribute zero. Carry them to physical bits 58:57 only for pages the model's C table decodes as uncached, so cached mappings are unchanged. The TLB already holds that table for its shadow entries; reading it there, and recomputing each entry's base when a table is installed, avoids a second decode of C. An entry with no attribute bits set, the usual case, skips the C table. The attribute reaches the bus only on a board that decodes physical bits 58:57. The Octane, Indigo2 and O2 mask the physical address to 40, 32 and 32 bits before the bus, which folds it away, and a test shows that at the executor. The R4400 and R5000 never store the bits. Tests cover the attribute and the board masks, and the C table (a value the model decodes as cached carries nothing; a table installed after the entry was written decides). The uncached-attribute comments cite NEC U10278EJ4V0UM, the fourth edition of the VR10000/VR12000 user's manual, section 11.3, by document number; the manual itself is not added to the repository.
Bedrock memory configuration changes the RAM decode. Keep ordinary CPU and DMA memory on Physical directly, divert diagnostic pages through MD, and update their generation state when memory configuration or ECC changes. The ECC view overrides write_narrow_from to take an uncached SH's check word from the whole source register, at the position Fuel PROM 6.210's memory test uses. Every other view stores the architectural low bits.
The flash seeded its configuration block with the Fuel's values: boot mode word 0x549aa985, 500 MHz and CPU type 2, and the machine kept the matching CP0 Config as a separate constant. A Tezro needs the word of its own chip and a Config whose mode bits agree, or the PROM stops in its error routine before it sets up GP. The board passes a FirmwareRecord: the PROM's boot-mode word, the reset mode bits the CPU latches into Config, the clock and the CPU type. The record yields the Config the CPU resets to, so the flash and the CPU read one source. A reopened image gets the opening board's record and the block's byte sum is still preserved. The record also carries a header policy: the Fuel keeps its refreshed, sum-preserving fields, while the Tezro writes a whole 0x50-byte block when a store is formatted and keeps a stored block on open, so a flash update the PROM wrote survives. There is a record for each Tezro chip, 700, 800, 900 and 1000 MHz. The first three mode words are rows of the stock IP35 PROM's CPU speed table (0x589bad85, 0x549baf85 and 0x569cb185). The table stops at 900 MHz, so the 1 GHz word 0x4aedb385 is built from the formulas the PROM's own configuration editor uses for the other rows and from the cache clock and tap its inventories print; it is approximated, and the doc comment says how each field was derived. The block's layout and its checksum rule are the editor's too: it writes CPU type 2 for an R16000, and sets the compensation byte so the new block sums to what the block it replaces did, which keeps the whole image summing to zero. The seed computes that byte instead of carrying a constant that only reproduced for CPU type 0. A fresh store can be seeded with environment records at given offsets. On the two-processor board the stock PROM treats the empty B and D positions as processors that failed to start and power-cycles the module six times before writing DisableB and DisableD; the two-processor records are provided byte for byte as QEMU's boot tests write them, so the first boot runs through. That the board ships so is a model policy, not a recovered factory image. A store that already exists keeps what it holds, and the Fuel seeds nothing. Ported from tests/functional/mips64/lib_origin300.py in jamesbraid/qemu-sgi-origami.
The Tezro's IO9 carries a 32 KiB timekeeping SRAM with a BCD calendar in its last eight bytes. The Fuel's M48T35 model has the same geometry and clock offsets but not the same registers: run against the DS1744 contract it reads zero from the control register where the part shows the century, 0x40 where the part shows 0x60 after a read request, has no weekday, and takes a month of 0x13 as a wrapped date where the part refuses the commit. That is a different calendar, so the DS1744 is its own model rather than a flag on the Timekeeper. The control register carries the century beside the write and read bits, the weekday advances with the date, the oscillator-stop bit rides in the seconds, a commit that is not a date is refused, and test mode is modelled. The clock is ported from QEMU's ds1742 and the two have to keep in step, so the tests replay the QEMU model's register sequences as one table.
The Tezro tower's PROM asks the L1 for its two port-speed integers and prints a complaint about the link running at 400 MHz instead of 600 when the first reads zero. The L1 answered [0, 0] unconditionally. The first integer selects a PROM display branch rather than naming a speed, so make it a setting (zero or one, as the model it is ported from allows) with the default at zero, which keeps the Fuel's reply unchanged. Add the router command interpreter's brick request for the Chimera brick types, off by default. It arrives as 0x3001, 0x3002 and 0x3003 chunks on one of 32 subchannels; each subchannel reassembles its own message and a new start replaces a pending one. A single-fragment request whose descriptor fields match the capture is answered with the rack, slot, partition, brick type name and serial, in 113-byte chunks; a malformed request, any other interpreter command, a brick type with no name and a message of more than one fragment are logged and get no reply, because the descriptor layout for the longer messages is not known. The model's second UART transport is not present here, so only transport 0 exists. The tests port the port-speed case and the single-transport router cases from sn1-l1-test.c, including the Chimera WS identity the Tezro uses. Ported from hw/misc/sgi-l1.c in jamesbraid/qemu-sgi-origami.
Behind the PIC that carries IO9, widget 0 is a PXBOW and the processor reaches the other widgets of the board through it. The Crossbow model already has the register map, so PXBOW is that model with part 0xd100 and manufacturer 0 instead of a second copy. It selects revision 5 behavior, which adds the reset fences and the resource lock the Octane's revision 4 leaves reserved, but reports revision 3 in its identification word (0x3d100001), as the Tezro, Origin 350 and Onyx 4 PROM inventories print it; part and manufacturer remain inferred, and a 2009 OpenBSD dmesg that names the part Bedrock is an open conflict. The Crossbow also reports a source widget in its status reads and sends its own interrupt to a packet sink when the target is not HEART; PXBOW's target is the host, so the PIC forwards it to its peer. Pxbow::route gives the machine a widget as the processor sees it through the switch. Link resets drop the link for the two core cycles QEMU models (20 ns on the machine clock, ended by the next register read or widget query) and a warm reset takes the first reset in 1024 core cycles and ignores its echoes, each destination's fence deciding whether it admits the source. The Octane, with no clock, restores at once as before. PROM 6.210's crossbar check writes 0xdeadbeef and 0 to the PXBOW's read-only error address registers. Iris turned the refusal into a Data Bus Error and the PROM reported an exception in its xbow_sanity diagnostic. The Crossbow specification makes such a write an error condition reported in widget 0's status bit 5 and its interrupt, and an uncached store cannot be answered with an error response (the R10000 accepts no completion response for it, NEC U10278EJ4V0UM p.104, table 6-1), so the PXBOW completes both writes, as QEMU's complete-posted-error-address-writes policy does. A standalone Crossbow, such as the Octane's, keeps refusing both; a test covers the upper register and both revisions.
…e and timers between IOC chips The IOC4 puts the same ring-DMA engine in front of its four 16550s that the IOC3 has. Copying it would leave two walks and two copies of the timer code to keep in step, and the two chips are meant to track the QEMU models, so the engine moves out of ioc3.rs into ioc_serial_dma.rs under QEMU's names (SerialDma and friends, sgi-ioc-serial-dma there) and reaches the chip around it through a host trait: the 16550s, the interrupt bits, the bus master and the error report. The IOC3 keeps its behavior; two parameters cover what the chips do differently, and a chip can halt a port on a failed transfer until a channel reset and receive the failed address. The host-time timer slots (one pending one-shot per deadline kind, replaced when its deadline moves, removed under the lock that keeps a late arm from outliving a stop) are one DeadlineTimers both chips use. The engine follows QEMU's timing. It writes a character only when the 16550 can take it and resumes when transmit-ready rises; the 16550 holds a byte its backend refuses, reads busy, retries as the backend drains and after two seconds gives up and treats the backend as disconnected until it takes a byte again (SerialBackend::try_send_byte defaults to sending, so other backends are unchanged). Before, a full socket lost bytes or stalled the whole emulator. A looped-back character takes its character time on the line, about a millisecond at the 9600 baud reset rate, and the engine wakes from a timer if nothing touches the chip meanwhile; the IOC3 ring tests that read straight after the doorbell wait out the line. Arbiter-idle (IOC3) and serial-idle (IOC4) read busy only while one of the engine's own bus transfers is under way. Engine tests drive a stand-in host.
The Tezro's IO9 board carries a Broadcom BCM5701 gigabit Ethernet controller (PCI 14e4:1645) next to its IOC4 and SCSI controller, and nothing in Iris implemented one. The PROM and the IRIX driver both bring the chip up through its registers and then move frames through its rings. BAR0 is 64 KiB: the register file, whose first 256 bytes alias the PCI configuration registers, and a window onto 32 KiB of on-chip SRAM. A driver reaches the same state three ways: the BAR, an indirect register window and an indirect memory window in configuration space. The last two are registers the chip computes, which a plain configuration header cannot, so PciConfig gains an optional hook the function installs; every bridge already goes through PciConfig::read and write_masked, so none needs to know. The model has the chip id in MISC_CTL, the global reset with the bootcode's firmware-ready handshake, the self-clearing mode resets, the buffer manager's delayed enable, the station address the bootcode leaves in SRAM and filter slot 0, the interrupt mailbox and global mask with PCISTATE.INTR_NOT_ACTIVE, the MII management path to the copper PHY, and the serial EEPROM controller over a backing the caller supplies. Nothing synthesises an EEPROM image: no complete board image is established. Anything unmodelled (a write to an unmodelled register, an undefined MII opcode, direct EEPROM pin control, a BAR access of a width the chip refuses) is stored or dropped but counted in Bcm5701::refused and logged on first sight. The send ring is walked from the chip's consumer to the producer the driver writes to the doorbell mailbox. A frame's descriptors are gathered, source-address replacement and checksum offload are applied if asked, and a large TCP buffer with a segment size is cut in software. A doorbell that ends inside a frame, or a descriptor or buffer the bus cannot reach, rolls back to the start of that frame so a later doorbell retries it. The standard, jumbo and return rings carry received frames: the ring is chosen by size, a frame with no posted buffer is discarded and counted when the other ring could still take traffic and otherwise left at the head of the backend's queue. The status block and coalescing follow the register contract: update criteria are a descriptor count or a microsecond budget per direction, an expired budget reports the next event at once, zero is off, and an update the bus refused is retried. A completion is held for a 200 microsecond minimum arrival latency so a reply never overtakes the transmit that provoked it. Descriptors, the status block and frame data go through the chip's byte-swap controls as lane permutations; the bridge's own byte order sits behind PciDma. Frames use the shared network link as the IOC3 Ethernet does. Timers fire at their own deadlines, earliest first with the clock set to each one's deadline, so a timer that re-arms another counts from its expiry. The PHY settles after 100 ms and the buffer manager enables after 8 ms. A chip or machine reset purges the frames the backend still holds, so one queued before the reset cannot land in rings set up after it. The tests port the plain-memory cases from bcm570x-test.c: registers, descriptors, status block, coalescing, interrupts, receive, the receive queue reset and the checksum report for every malformed header. Cases needing virtio-net headers, QEMU's user-mode network, a TAP, the dual-port BCM5704 or migration have no counterpart; the bridge byte-order cases belong with the PIC. Ported from hw/net/bcm570x.c and hw/net/bcm570x_phy.c in jamesbraid/qemu-sgi-origami.
…r parity A primary instruction-cache entry is the instruction predecoded into 36 bits with an even parity bit. Fetches from staged lines took TagLo as the instruction, which is wrong for the words the Fuel and Tezro PROMs stage, so serving had to be off and those PROMs ran from flash instead. A fetch hit now decodes the stored word with a layout inferred from the words a shipped PROM stages: addiu $4,$0,2 is 0x3_4420_0004. Only the sampled class is known, so a word of any other class is refused. It is reported once on stderr and the fetch is treated as a miss that reads the secondary cache or memory. A Cache Error would stop a guest the hardware runs, and memory holds the instruction a word was built from in all but the few stale entries. The Fuel PROM stages seven unsampled classes and boots on that fallback. Parity is checked on a fetch hit and a bad word posts a Cache Error unless Status.DE or ERL is set, as the manual says. Index Load Data (I) returns word and parity as stored, so a readback stored back verbatim round-trips. A word with p[35:32] = 0xf is the model's own raw encoding, a model choice with unknown hardware contents, so words outside the sample can round-trip too. A refill is not modelled. IRIS_STAGED_L1I is gone and serving is on. Tests cover the decode and each field, refusal, the parity error and its Status.DE exception, the round trip for sampled and raw words, and a staged word differing from memory. Breaking a decode field or the parity check fails them.
…ache from staged lines The predecoded primary instruction word has 11 known classes (all inferred): nop, dsll/dsll32, or, addi/addiu/ori/lui, daddiu, cache, ld, mtc0, jal, jr/j and REGIMM. A word decodes only if its class, p[28:26] and, for the SPECIAL classes, the function field name an instruction in the table. Anything else is still refused, reported and read from memory. The Fuel PROM now boots with no refusals. A cached fetch that misses the primary I cache and hits a staged secondary line refills the 64-byte primary block from it (NEC U10278EJ4V0UM pp.87-88), as the Tezro PROM expects: it invalidates a way, fetches through a staged line, reads the refilled way back with Index Load Data (I) and stores the words into another line. The victim is an Invalid way before a Valid one and way 0 if both are valid. The manual states no victim rule, so that is a model choice. Words are stored in the raw encoding, the architectural word standing in for the predecoded form hardware stores, a model choice that lets a verbatim readback execute. Ending a secondary block now also invalidates the primary I lines inside it, as the manual says. Tests decode one example per class, refuse a known class with a foreign instruction, and cover the refill into the invalid way, Index Load Tag/Data, the readback round trip, and that no refill happens without a staged hit. Dropping the refill or always using way 0 fails them.
The IP35 PROM fetches its own code uncached at plain physical 0x1fcxxxxx. Bedrock serves that from the reset PROM, but the compile pool read through the cached view, and once a memory bank covers the window the cached view answers RAM there. The memory test zeroes it, so the JIT compiled pages of NOPs in place of PROM routines and the PROM faulted (an instruction fetch bus error, or endless refills). Code pages are keyed by physical frame, so the cached and uncached fetches of that address were one page. Make them two addresses, the way the board decodes them. An uncached instruction fetch of the reset window is now presented to the bus at the same PROM at an address of its own (MipsExecutor::set_uncached_fetch_alias, called by the Fuel; RESET_WINDOW_FETCH, above every RAM slot). The JIT stays as it is: it keys, reads and tracks that page by address like any other, the flash device's generation counter invalidates it, and a store to RAM over the window never does. The alias wraps the translation the CPU installs when it is set, so a cached fetch, a data access and every other machine's fetch path are untouched. The evidence is partial. Which space a request reaches follows the processor's uncached attribute (ip35-address-map.md, processor-side address spaces, established), so an uncached and a cached access of one address are different requests. What attribute 0 returns at 0x1fc00000 is not established: no public source says how the reset request reaches flash, and the cached side there is open too. The split rests on what PROM 6.210's memory test needs, and the code and CHANGELOG say so. The compile pool restarted after a page-pool flush used the CPU's uncached bus instead of the cached plane it was started with; the executor now keeps the compile bus (set_jitv2_code_bus) and passes it to every restart and to the inline compile. The monitor uses it too: `j2 inline off`, `dumppcp`, `corpus` and `j2 html` read pages through it. Tests: an uncached reset-vector fetch compiles the PROM's ADDIU and the cached frame compiles RAM's boundary word, and a flash write invalidates only the PROM's page; a pool restart after a flush keeps the compile bus; a cached fetch and an unaliased executor key the frame they name; uncached attributes 1 to 3 and addresses past the end of memory get no code page.
…unting The board assumed the Octane: its interrupt packets went only to HEART at widget 8, and it took the processor's addresses in the IP30 widget windows. A Fuel's processor reaches widget 13 through Bedrock, which hands the board widget offsets, and the board's packets cross the XBridge's crossbar to Bedrock like any widget's. Add a constructor for such a machine: it takes a packet sender for the interrupts and decodes every address as an aperture offset. The Octane's constructor and behaviour are unchanged. A test routes a packet to a widget other than HEART and requires the sender to get it, not HEART's line. Graphics credits (odyssey): A Bedrock or Hub processor interface counts graphics credits: each counted store to a graphics head takes one, and the head returns it as the store leaves its command FIFO. The Octane's HEART has no such block, so the board left the accounting out. Bring it back for a machine that links a processor interface: the PiGfx register block and the PiGfxLink trait (return, owe and repay credits), a counted and an owed mark on each queued store, the credit loop in retire and clear, the one-store runs while a counted store waits, and the processor's one-shot store mark. With no link, as on the Octane, every credit call is a no-op. How long a V12 takes to return a credit is not published. The host's credit window is 100 doublewords (IP35) or 256 (IP30), a credit is inferred to come back when the board takes the doubleword into its FIFO, and the FIFO must hold more than the window. The model credits a store as it enters while fewer than 16,384 stores wait (cfifo::DEPTH). The initialise call stores its start-up program, about 12,291 words, with no level check before the low-water interrupt is routed; with a depth of 512 two boots in five timed out with "CFIFO timeout" and no screens, and 16,384 is the smallest power of two that covers the program even at one word per store. The depth is a model choice derived from that program. A deeper store owes its credit and hurries the board thread; a reset refunds what the abandoned stores owed. Inside the depth the board thread takes full runs, so a credited backlog does not drain one store at a time. The board thread reads the count of counted stores once per run taken and once per run retired, on every machine. Both reads are Relaxed: a run's length is only a hint, a stale count costs a longer run and never a lost credit, and the retire reads it under the lock a counted push takes to change it. Nothing else on the Octane's store path changed. The tests drive the credit rules with a stand-in link: a counted store is credited on entry, a reset cancels owed credits, the mark counts exactly one store, a widget that runs out of credits starves, the FIFO set-up words do not widen the credit depth, and an owed credit hurries the board thread. Others fill the largest host window plus one with the board taking nothing and require every credit back with none owed, and post 16,384 counted stores and require each credited at entry and the next one owed. The refused-push test used to sleep 150 ms and require a push thread to be unfinished, which a loaded host could break once the board thread's timed hold ran out first. The hold now lasts until the test releases it, and the test waits for the push to be refused before it asserts.
The Bedrock network-interface port status word read 0 on a node with no cable. The IRIX 6.5.30 bus-error handler starts with a network-interface check that prints "Detected a link reset due to other partition reset" when reset enable is 0 and port status bits 1:0 are 0, so every faulting widget probe printed it. Read bits 1:0 = 1 instead: link up (bit 1) clear and bit 0 set. The value is inferred, not measured: bit 1 follows a real Tezro PROM reporting its NUMAlink down, and bit 0 follows a real one-node Fuel taking a PIO read bus error with no link message. Nothing is recorded in the processor-interface error records. The offset-0 register is renamed from NODE_ID to PORT_STATUS, which is what it reports. This block is shared with the Fuel board, so QEMU needs the matching change.
Tezro's interface board puts its PCI buses behind the PIC, not the XBridge Fuel uses. One chip fills one XIO widget with two heads, at offset 0 and 0x800000, and the two- and four-processor board fits a second PIC at widget 11 beside the one at widget 15, so a Pic is a self-contained device the machine builds once per widget. The model is the revision 3 profile the QEMU model selects for Tezro: identity with bit 0 set, the control, Device and DIR_MAP masks, the CPU word view of the eight-byte registers, the processor's byte-lane placement for PCI cycles, direct-map and direct64 DMA, and the invalid address interrupt. Translated DMA and PCI-X errors stay unsupported and observable. PCI slots are hooks only; the machine attaches its functions. Where two functions decode the same range, as a probing PROM leaves them, the BAR mapped last answers, as on QEMU's bus where overlapping BARs have equal priority and the newest sits on top. A function receives each slot's REQ64# as reset ends (the BCM5701 samples it to report a 64-bit bus). A configuration read of an unclaimed slot fails as a master timeout instead of returning all ones: PROM 6.210 reads interrupt status bit 11 after each probe, and an all-ones header recorded two phantom components per empty device, enough with a second PIC to exceed the PROM's 66-component limit and lose its CPU records. The error view at 0x170 records the timeout, the status only while enable bit 11 is set, and the error interrupt follows. That fault and its enable gating are inferred. Configuration reads reach a function's hook only as whole dwords, as writes already did, since the BCM5701's indirect windows are computed by the chip and a narrow read returned part of a window base.
A mapped page whose physical address has a bit set above bit 31 could reach the PCI window instead of RAM on the O2. The O2 aliases RAM at 0x8000_0000, and fold_phys tested that window on the full translated address before cutting it to the 32 bits the board decodes. IRIX keeps software bits in its page-table entries just above the PFN, so such an address missed the window, then lost the high bit in the mask and landed on the PCI range at 0x8000_0000. Cut to the decoded bits first, then apply the alias, then cut its target. The window lies within the decoded bits, so a page already inside it lands where it did. The extra AND runs only on an alias hit. Only the O2 has a physical alias; the Indy, Indigo2 and Octane are unchanged. A test maps a page at 0x1_8000_0000 with the O2's mask and window and expects RAM at 0. It reads 0x8000_0000 without the fix.
The IP35 PROM brings a node's processors up assuming they left reset on one clock edge and have run in step since: four of its early handshakes order one processor's stores before another's reads only because both run at hardware speed. Each processor here is a host thread that can fall far behind, as under QEMU's multi-threaded TCG, so a processor that reaches the rendezvous early is held until the others catch up. This ports QEMU's four holds: the reset barrier on LB scratch 1, the pair liveness post in the processor's own real-time compare register, the presence publisher at its release of the hub lock, and the partner at its real-time counter read. The hold has its own reason in the processor control so that its release does not end a world stop or HEART's disable, and the monitor can tell the holds apart. The LB takes optional hooks for scratch 1 and 3 and Bedrock IO reports each store to scratch 0 once it has landed, with every hook running with the block's lock dropped; nothing connects them on the Fuel. A processor on PI1 reads its CPU number and programs its own interrupts through the XIO local register form at offset 0, as QEMU's IP35 node overlays PI1 there for every processor sitting on it (OpenBSD expects the local form to select the requester's interface). That the chip answers PI1 there is an approximation pending a hardware read. A processor running with others makes SC atomic through its cache's compare-and-swap, which the shadow cache does itself for RAM in its direct window and hands anything else to the bus; on the IP35 boards that bus had no swap, so an SC to a bank above 4 GiB or to a checked page became an ordinary store. The cached view now passes the swap to MD's DMA view, which swaps in the bank and advances the page's logical counter. A node restart from the LB soft reset, the NI local reset or an L1 event, and each processor's startup window starting and ending, are logged. Fuel is unchanged. Ported from hw/misc/sgi-bedrock.c, hw/misc/sgi-bedrock-lb.c, hw/misc/sgi-bedrock-io.c and hw/mips/sgi/sn1-node.c in jamesbraid/qemu-sgi-origami.
A Bedrock node can hold four processors, and the PI only knew how to run the one in PI0 slot A. The board now names its running slots beside the fitted ones. A running slot starts with its CPU-enable bit set, its interrupts, timers and enable writes are no longer refused, and the CPU-number register answers the reading processor's own slice, slot in bit 0 and interface in bit 1, as QEMU's PI does from the issuing processor. A reader that is no processor reads slot A, QEMU's behavior for an access with no requester. A fitted slot that does not run is held disabled; a guest write that enables it is stored and reaches the node hooks, but no processor starts. CPU enable stays stored only: QEMU grants every fitted slot at reset and wires the register to nothing, an approximation since no cold-reset value is known. Clearing a running slot's bit is recorded as a refusal, and an NMI is refused unless the node reports it taken, so an NMI to a running processor, which has no NMI entry, is logged instead of dropped. The PI therefore records a stop or an NMI it cannot carry out, and the Tezro's node hooks need no refusal counters of their own. The graphics slot stops below its bias, as the Bedrock does: the store that crosses below raises the interrupt and the slot's next counted store waits until the processor takes the interrupt, software writes the counter, bias or interrupt count, or returned credits lift the counter above the bias. QEMU follows the SN1 header's credit-counter comment here; its Hub only raises the interrupt once, so a writer the board cannot keep up with drove the counter without bound. Fuel and the one-processor Tezro run PI0/A alone. Tests follow sn1-cpu-identity-test four-processors-know-their-slots and sn1-bedrock-pi-test cpu-number-fallback, cpu-enable-lines and nmi. Ported from hw/misc/sgi-bedrock-pi.c in jamesbraid/qemu-sgi-origami.
Iris had every Tezro device and the Fuel's Bedrock node but no machine that put them together. Add the tezro profile, configured by the shared keys and a [tezro] section, with the board the QEMU one-processor composition describes. The node is the Fuel's, given the Tezro's inputs: an R16000 with reset Config 0x6c9bad85, the Tezro flash record, Bedrock board word 0x2001, memory from 512 and 1024 MiB banks with the 4 GiB layout as the default, and an L1 that reports brick type 0x39, revision 1.48.1. The PIC carries IO9's IOC4, the ISP12160 SCSI controller and the BCM5701 behind a PXBOW, with the DS1744 clock and the shared IOC4 console. Graphics is not attached. The profile lists the four R16000 chips the Tezro shipped with, the 700 MHz one first, so a bare cpu = "r16000" and a config with no cpu both select it. A chip the Tezro lacks is refused by the generic message that names the machine and its chips. The chips differ in what the guest and the PROM read: revision 2.1 at 700 MHz, 2.2 at 800 MHz and 3.0 at 900 MHz and 1 GHz, with a secondary cache of 4, 4, 8 and 16 MB. The board keeps what is its own beside the chip: the flash record whose mode word gives the Config the processor resets with, and the processor counts the chip is fitted with (the 700 MHz chip one, two or four; the others four only). The comment on that table grades each value as established, inferred or approximated and names the evidence. The cache type comes from with_cpu!, so the board adds no run-time size field or branch. The poll on the CPU thread asks the memory controller to return lifted checked pages to the direct window every 100 ms of board time, so a lifted page does not keep its 64 MiB region on the slower decode path.
The two- and four-processor boards fitted their processors but ran only CPU 0. Each fitted slot now gets its own R16000, numbered in the board's slot order (CPU 1 in PI1 slot A, CPUs 2 and 3 in the B slots), all starting together from reset as in QEMU's IP35 node. Each has its CPU number, its share of one compile pool and page set, its direct window bitmaps, its slot's interrupt lines from its own PI, its own status bar counters and a place in the monitor. The board runs the PROM's boot rendezvous across them, fed from the LB scratch registers, II scratch 0 and each PI's real-time registers. A fresh flash on the two-processor board carries the DisableB and DisableD environment records, so the PROM's first boot goes straight through instead of power-cycling the module six times to give up on the empty positions. The one- and four-processor boards leave no empty position the PROM recovers and seed nothing. A flash file that already exists keeps what it holds. The node passes the board's records to its flash; the Fuel passes none. Board composition ported from hw/mips/sgi/sn1-node.c in jamesbraid/qemu-sgi-origami.
…de reads, update the guide The PROM fetches its code uncached at plain physical 0x1fcxxxxx, and Bedrock serves that from the reset PROM. Once a memory bank covers the window the cached plane answers RAM there, so the JIT could compile memory's bytes in place of the PROM's. The Fuel presents an uncached instruction fetch of the window at the PROM's own address (set_uncached_fetch_alias); the node builder both boards share maps the PROM there, so give the Tezro's processors the same alias and start the compile pool on the board's code bus. The alias is keyed by address like any other page, so a jitv2 build no longer needs memory below 4 GiB and takes the default six banks. Tests cover the one-node network reads and the PROM's crossbar check, which the model already answered as the PROM expects but no test ran on the Tezro board, and that flushed copy-on-write overlay writes survive a power-off. The guide now describes the second PIC on link 11 of the IO9 PIC's PXBOW, the PROM disabling every processor but CPU 0, the R16000 policies left out, and how far the machine gets.
The decode cache reads a RAM page's words straight out of ppmem's window. A page the shadow cache serves partly from lines the guest staged has words the window does not hold, so it has to be read word by word through the cache instead. Add set_bus_only to mark such a page. It drops the page's decoded slots and the page then has no window pointer, so it misses the fetch fast path and takes the slow path, which re-reads and compares every word. Pages not marked pay nothing.
The R10000 picks the TLB refill vector from the address space the missing address lies in and that space's Status bit: user addresses follow UX, xsseg SX, and kseg3, sseg/ksseg and xkseg KX. The current mode only decides which space an address is in (R10000 UM v2.0 pp. 333-334, 342). The manual contrasts this with the R4400, which picks by the current mode's bit. iris did the R4400 thing on every model. So a 64-bit kernel (KX=1, UX=0) missing on a kuseg address went to the XTLB vector at 0x080 on the R10000, where the part uses 0x000. The Octane PROM's utlb diagnostic loads VA 0 in exactly that state and waits at the 32-bit vector. It got "Read TLB miss exception" from the XTLB vector instead and failed. On the R10000 family a refill miss now reads the region of the address, sign-extended from 32 bits in 32-bit mode, and tests UX, SX or KX. Only the miss path changes. The TLB compare width still follows the mode. The test misses in each space with each bit on the R10000, and checks the R4400 keeps choosing by mode.
The R10000 and R12000 do not shut down on a multiple match. A TLB write that would let one lookup match two entries first invalidates every entry it overlaps, the wired ones included, then writes the new entry and sets Status.TS (NEC U10278EJ4V0UM, Status register TS bit p.223 and "Avoiding TLB Conflict" p.300). iris stored the entry and left the duplicates matchable, so which one answered depended on how the lookup map happened to be rebuilt. On the two-processor Tezro the firmware flushes the TLB with 64 invalid entries on the kernel's page number, and the kernel then writes the valid global 16 MiB entry over them; a later write elsewhere left an invalid duplicate answering, and the fetch took TLB Invalid. The R14000 gets the same rule as a model choice; no manual for it was read. The R4400 and R5000 keep their behaviour. An overlap is two entries a lookup could match together: same region and page number under the wider of the two page masks, and equal ASIDs or either entry global. Invalid entries count. The removed entry reads back as an unwritten slot except for its PageMask: the kernel TLBRs its wired sentinel entries and reuses PageMask for the next write, and a zeroed mask made it write a per-CPU mapping as 4 KiB. The cost is confined to TLB writes on these models. An entry that overlaps the new one covers a lookup-map slot the new one covers, so a write whose slots are all empty or its own skips the scan. A PageMask that is not a run of ones, which software can write, defeats the map's ranges and turns the shortcut off. A refill that replaces a one-page entry nothing else covers skips the walk over the other entries. TLBWI and TLBWR are never compiled by jitv2, so both reach the same write and set TS in the interpreter. Tests cover the Tezro sequence, wired entries, PageMask kept on a read-back, entries that cannot match together, and TS from TLBWI and TLBWR. They fail with the rule off, and the R4400 and R5000 test fails if the rule is enabled for them.
A store that nothing answers raised a synchronous Data Bus Error with a
precise PC on every CPU. The R10000 cannot do that. It sends an uncached
store as a write request, and a write request gets no response; Bus Error
comes only from an error response to a read or upgrade request, or from
uncorrectable read data (R10000 UM pp. 104, 316). The processor never
learns that a store failed.
The R12000 manual documents the same no-response write path, and the later
parts are taken to match, so the test is the R10000 family and not the R10000
alone.
Add CpuModel::POSTS_UNCACHED_STORES, true for the R10000 family's caches. On such a
model a refused store logs and completes in write_data_impl and in the
masked-store path (SWL, SWR, SDL, SDR), which the interpreter and the
compiled code both go through. An uncached SC or SCD that is refused
completes and succeeds (rt = 1); that is a model choice, since the manual
says nothing about uncached store-conditional. Loads, instruction fetches,
cached accesses and the R4400 and R5000 keep the exception, and device
return values are untouched because BUS_ERR also tells non-CPU callers
that a request was declined.
The error goes no further. Nothing in iris raises an interrupt or sets an
error register for a store the Octane's bus refuses. The IP22/IP28 bus
already completes a write to an unanswered address, latching MC
CPU_ERROR/GIO_ERROR and raising no interrupt, so the stores that change
here are the ones a device refuses itself.
The log line stays visible ("Unsupported uncached write ... posted,
completes") but a probe loop must not flood stderr: it prints the first
four refused stores and then each power of two, with the running count.
The Octane's ide diagnostics store 0x591204 and then 0x4 to physical
0x1e130400, where nothing answers. Before, the first store took a Data Bus
Error; now both print one line and the run carries on.
Tests: the interpreter on the R10000 (full and shadow caches) and the
R5000 for SW, SWL and SC plus the load that still faults; the JIT against
the interpreter for SW and SWL on the R10000 and for the load and the
R5000 store that fault; the report schedule. cargo test --release
--features jitv2 --lib: 2422 passed, 0 failed.
jamesbraid
force-pushed
the
tezro
branch
8 times, most recently
from
November 6, 2026 05:21
a3f8b4f to
15c6ad5
Compare
The Tezro ships with R16000s at 700, 800, 900 and 1000 MHz, and the CPU stack no longer carried the family. Add one CpuModel chip per shipped clock, named like the R12000 and R14000 chips: r16000_700, r16000_800, r16000_900 and r16000_1000. Each carries its clock, display name and the PRId its inventory revision gives (2.1, 2.2, 3.0, 3.0). The 1 GHz value is printed by the PROM banner; the others are inferred from inventories alone, and the comment on CpuModel::prid says which is which. The secondary-cache size is a parameter of the shadow cache type, so the executor, the translator and the cache know it when they are built. The 700 and 800 MHz chips have 4 MB, the 900 MHz chip 8 MB and the 1 GHz chip 16 MB, which are three aliases of one shadow cache (Config SS codes 3, 4 and 5). with_cpu! picks the alias once at machine construction, so nothing is read or branched on at run time. The R16000 shares the R10000 family's programming model, as a model choice, since no manual for it was retained; its FIR is the R12000's 0x900 for the same reason. No machine accepts these chips yet. The tests run each alias through the interpreter, the Config and EntryLo checks and the jitv2 equivalence check.
…the board The Bedrock memory model hard-wired the Fuel's two 256 MiB banks: the bank size was a constant in the decode, in the ECC, directory and protection keys, in the self-test and in the processor bus's test for where RAM can be. A Tezro fits up to six banks of 1024, 1024 and four of 512 MiB, the last two at 4 to 6 GiB, outside ppmem's 4 GiB direct window. The board now passes the fitted bank sizes and they are checked: at most eight banks, each a whole number of direct-window regions up to the 1 GiB the largest size code names. Decode, self-test and the sparse ECC, directory and protection state read the size of the bank they touch and key it by where the bank starts, so banks of different sizes never share a key. The processor bus asks a RamExtent where RAM can answer, allowing for the bank-select swizzle; generation counters cover every slot that can hold RAM. Banks above 4 GiB answer through Physical's decode table with no direct fast path. MemoryPopulation is the typed form a Tezro board uses: 512 or 1024 MiB DIMMs, one per bank, at least 512 MB for one processor and 1 GB for two or four. The default is the 4 GiB layout (1024, 1024 and four 512 MiB), which matches an observed four-processor Tezro; the 1 GiB population stays constructible, and nothing claims the PROM or IRIX boot at it. The Fuel passes [256, 256] and behaves as before. A checked (ECC or directory marked) page no longer rebuilds the whole memory map. The PROM's memory test marks and lifts a few hundred doublewords thousands of times, which the QEMU model counted as 6630 placements. A mark now takes one decode-table entry, and the first mark in a 64 MiB region withdraws that region from the direct bitmap without unmapping its host pages, so a processor that read the old bitmap an instant earlier still reaches the bank's bytes. A lifted mark changes nothing; lifted pages return to their banks together, when 1024 have piled up, after a second of controller time, on a zero self-test, or when the board calls retire_lifted_marks. Per added page the Fuel went from 15 mappings, 3 table rewrites and 393,216 counters to one mapping at most, one rewrite and 2 counters (32,768 for a region's first mark), and the Tezro from 95, 7 and 2,621,440 likewise. Whole-map rebuilds still scrub, so they run inside a guard the board supplies that parks the other processors. Each processor keeps its own startup window, the 32 KiB page at 0x100000 the PROM uses as stack and data in primary-cache lines before memory is set up. Each processor's cache holds its own lines, so a node-wide page let a second processor overwrite the first one's stack. Window start and end are approximated from the tag stores; private per-processor storage is a model policy, as in QEMU. Bulk writes (reset_memory, load_bank, restore_bank) advance every logical counter, since code pages follow the logical counters and a bulk write through Physical bumped only the banks' own. Tests port the qtest cases for memory banks, size boundaries, bank select, directory, self-test and lifted marks (ecc-lifted-mark-keeps-overlay, ecc-lifted-mark-over-marked- line), and cover cached and uncached stores of every width to banks above 4 GiB, a store after a bank swap, and the reset PROM at 0x1fc00000 inside fitted RAM.
The R10000 Indigo2 is the IP28 board: Linux builds the IP22 sources for the R4X00 and R5000 and the IP28 sources only for the R10000. iris let every Indy and Indigo2 profile take any of the three models, so a config could pair an R10000 with the IP22 memory controller and PROM, and an IP28 with the default R4400. cpus() now lists R4400 and R5000 for the Indy and indigo2_ip22 and the R10000 alone for indigo2_ip28, and accepts_cpu refuses anything outside it with a message naming the line to change. validate() and the front ends' pickers read the same list. A config with profile = "indigo2_ip28" and the default cpu now fails validation; ip28.toml.example, the GUI and the IP28 guide already set the R10000. The benchmark machine is an Indy, so iris-bench and the GUI benchmark panel now refuse the R10000 and list only the R4400 and R5000. The bare-metal suite does not run on the IP28's memory map, so it cannot move to that board. The bare-metal suite's r10000, r12000 and r14000 cells in iris-bench go for the same reason: the machine they run on is an Indy, which no longer takes those parts.
Each machine kept its own facts about the CPU: the Octane had three Count constants and a PRId, the O2 two Count rates, the IP28 another, and machine.rs, ip30_machine.rs and ip32_machine.rs each matched on the CPU type to pick a cache model. A new part meant editing all of them. CpuModel now has one variant per chip configuration seen in real hinv dumps (r4400_150, r5000_180, r10000_250, r10000_195, r12000_360, r12000_400, r14000_600; the comments cite the dump counts). The cache types already carry the PRId and cache geometry, so CpuModel adds only what they lack: the clock, a display name and a PRId for the chip's revision, which MachineProfile::cpu_prid overrides where a machine's differs (the IP28's R10000 is rev 2.5, the Octane's 2.7). CP0 Count is half the clock. with_cpu! is the one place a chip becomes a cache and executor type; each machine lists the chips it takes in CPUS, first is the default, and a config with no `cpu` takes it. `cpu = "r14000"` still works: a bare family name means the machine's chip of that family. A full chip name must be in the machine's list. [clock] fixed_mhz overrides Count as before. Guest-visible changes: the Indy and Indigo2 R4400 is rev 5.0 at 150 MHz (Count 75 MHz; was rev 4.0 and 33) and their R5000 rev 1.0 at 180 MHz (was rev 2.1); the O2 R10000 is rev 3.4 at 250 MHz (was rev 2.5 at 180). The Octane and IP28 report what they did. validate() also refuses an O2 R4400, which only the constructor did.
PCI functions need different writable and write-one-to-clear header fields. Describe those fields in one construction policy while keeping the current header defaults for existing functions. Forward narrow Bridge configuration writes with their byte enables so write-one-to-clear bits are handled correctly when a function supplies them. Existing Octane functions do not use that status policy, so their behavior stays unchanged.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Iris runs the Octane and the Fuel. I wanted it to run the Fuel's big brother, the Tezro, so this adds it.
It's a port of my QEMU work. jamesbraid/qemu-sgi-origami already runs the Tezro with up to four processors, and the notes and tools are in jamesbraid/origami. Each ported file says which QEMU file it came from. The Tezro shares the Fuel's Bedrock node, so most of it reuses the Fuel's code. You get one to four R16000s, 4 GiB by default, the IO9 devices (serial, clock, PS/2, SCSI, gigabit Ethernet) and a V12. IRIX 6.5.30 boots to the desktop and runs OpenGL.
This sits on the Fuel (#368) and the CPU changes (#403 to #406), so until those land the branch carries their commits too. The Tezro's own are the
tezro:commits plus theip35:and Bedrock ones for the extra processors.Two changes touch files outside the Tezro, each in its own commit:
Things to look at:
cpus = 2or4the real PROM and kernel start the other processors. Nothing is faked. The PROM expects every processor to leave reset together and our threads don't, so four waits cover for it. They're ported from QEMU, each is labelled an approximation, and each has a test.idle-pausefeature, which only dist builds turn on. I left that alone.r16000_700is the default CPU: 700 MHz, revision 2.1.r16000is 800 MHz, revision 2.2, and four processors only, as in QEMU. The 800 MHz 8 MB part is refused because the cache size is fixed at compile time. FIR 0x900 is a model choice. Nobody knows the real value for any R16000.banks = [1024, 1024, 1024, 1024].Tested:
hinvshows the right count and X answers after login. A config with nocpukey gets the 700 MHz part. A 2.5 second stall of one processor during the PROM's start-up wait still finds all four.pixtest323 passed and 57 failed,glstate410 and 5, same failing tests.blendtest,seltest,toktest, the X tests,ivview,perflyand Quake run.olvst,olperi,oldiskandolmempass.olmemtakes about two hours on the interpreter.jitv2.cargo test --release --lib -- --test-threads=1gives 3052 passed and 0 failed, and 3562 passed and 0 failed withjitv2. The GUI crate checks too.I ran the OpenGL tests, the
ol*diagnostics and the benchmarks on earlier heads of this branch. Since then I rebased it five times onto the changing Fuel and CPU PRs and fixed three build breaks along the way. On the current head I re-ran the build, the unit tests and the boots above, but not the OpenGL tests or the diagnostics.One four-processor boot in about 19 found only three processors, on a very loaded host. I couldn't reproduce it or find the cause. A starved processor dropping out of the PROM's count is what the wait change fixes. With the fix, 4 clean boots and 6 injected stalls of 2.5 and 10 seconds all found four, and the stall at one slot found four again after the rebases. I can't show it was the original failure.
Not run: macOS and Windows.