Repository navigation
tezro: add the SGI Tezro, with the V12 and up to four processors - #409
Draft
jamesbraid wants to merge 67 commits into
Draft
jamesbraid wants to merge 67 commits into
jamesbraid wants to merge 67 commits into
Conversation
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. Each head owns a PCI bus, as the Octane's Bridge does: the bus holds the head's functions, decodes their BARs and combines their interrupt pins, and the head supplies only what is its own. That is how a processor access becomes configuration cycles and byte order (from a device window's register, or from Control for an alias), the translation table and DMA, and where an interrupt pin goes. Whole-page BAR runs of the device and alias windows are served from the head's own table, so the bridge shares 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.
The JIT reads code pages, and asks for their generation counters, at plain physical addresses, through the executor's sysad. Everywhere else sysad is also what the cache fills from, so a plain address there is memory. The Fuel's sysad is the uncached connection: it takes bits 58:57 of the address as the uncached attribute and decodes a plain one as Hub-special space, which holds RAM only in its first 256 MiB. Its compile pool was started on the cache's own view of memory instead, so the pool and the CPU's inline compile and page lookup read code through two different objects, and a flush restarted the pool on the CPU's. The executor now keeps one bus for cacheable memory, defaulting to sysad, and the page lookup, the inline compile, a flush's restart and `j2 inline off` all use it. A board whose uncached connection decodes differently gives it the cache's view with set_cached_bus and starts the pool on the same object. No other machine changes: the two are the same object there.
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. 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 CPU's cached bus, the view the cache fills from, 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 the 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" selects the Fuel's r14000_500, and a Fuel config cannot claim another processor. The Octane's R14000 is revision 2.4; the Fuel's is 2.3.
The test slept 150 ms and required the pushing thread to still be unfinished. The board thread's hold was a fixed 4 s sleep, which a loaded host could outlast before the test looked, so the refused store could complete and the assertion fail. The hold now lasts until the test releases it, and the test waits for the push to be refused before it asserts.
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 two choices were hard-wired into the board; they are now one packet sender (the Octane's checks for HEART's widget and register and drives its line, and a packet to anywhere else is refused as before) and an explicit aperture mode chosen at construction. The Octane's constructor and behaviour are unchanged, and nothing on its store path is touched. The debug register dump no longer lists the heart widget, which is the Octane sender's business now. A test routes a packet to a widget other than HEART and requires the sender to get it, not HEART's line.
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 each interface keeps those registers as plain storage. Nothing counts stores or returns credits. 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. The page also says how the PROM's stack in primary data-cache lines is served: by the CPU's staged-line support, with nothing special in the memory controller.
…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 machine 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 machine 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 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 machine type 0. The word at +0x30 is a machine class, not a CPU type. The PROM's start-up check derives its own from the board word's top nibble (0x4 gives 2, 0x8 gives 1, any other 0) and prints "CPU configuration values mismatch" when the flash disagrees. The speed-table rows carry 2, the class of the Fuel's 0x4000 board words; the Tezro's board word 0x2001 gives 0, so the Tezro records author 0, as QEMU's Tezro does. Neither a real Tezro's flash nor its board word has been read, so this keeps the model consistent rather than reproducing a capture. A fresh store can be seeded with environment records at given offsets. The stock PROM treats every empty processor position as a processor that failed to start and power-cycles the module three times for each before writing a Disable record for it. The seed builds those records for the positions a board names, B and D byte for byte as QEMU's two-processor 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 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.
The Tezro's IO9 board carries an IOC4 (PCI revision 0x53) where the Fuel has an IOC3. Its serial console is one of four byte-wide 16550 ports behind it, so the PROM and IRIX cannot reach a prompt without it. The model has the four ports on the shared 16550 core, each with the serial ring engine the IOC3 uses, a 66.67 or 33.33 MHz UART clock selected by the board, and the local interrupt words: eight serial bits per port, a second word for DMA errors, and one PCI pin for both. A failed ring transfer is captured with its address and requester and halts the port until a channel reset. The ATA DMA readback word the PROM diagnostic probes and the PCI status the IP53 policy asks for are latched only for a board that requests them. The DS1744 clock is mapped at 0x80000 to 0x87fff with the calendar at 0x87ff8: byte accesses only, wider ones refused and logged, a chip reset leaves it alone, and an IOC4 with no clock fitted reads zero there. The PS/2 controller sits at 0x200. The chip reads its 32-bit registers in the processor's byte order. PROM 6.210's serial DMA diagnostic timed out on UART A (diag_rc 75) because its writes arrived byte-reversed: port 0 DMA control as 0xff010210 and the TX producer as 0xf8030000 where QEMU's IOC4 on the same PROM records 0x100201ff and 0x3f8. The IO9 PIC carries the processor's bytes in address order and Iris hands a PCI function its BAR value as those bytes read little-endian, while the IOC4 reads them big-endian as the QEMU model's register region does. The UART and clock regions are byte-wide and unaffected. What the guest does that the model takes but cannot make sense of is logged as QEMU logs it: DMA enabled over a misaligned ring base or over PIO data in the FIFOs, DMA disabled with ring work pending, a pair base changed while busy, a PIO transmit while DMA is on, index writes outside the pointer field, receive-timer bits above the reload and control bits stored without effect. Behavior is unchanged by those. The tests port the QEMU model's qtests: the ring registers (4 KiB wrap, retained control bits, index pointer fields, empty-slot entries, timer arm, reload and expiry), receive-ring-full as a level held while ring DMA is on and an entry waits, loopback and overrun cases, the idle status sequence, and the absent clock, calendar window and wide-access refusal. The timer cases check the same transitions at host-time deadlines, since the QEMU file steps virtual time.
The Tezro runs on the same Bedrock node as the Fuel: the same processor views, PIs, MD, L1, boot flash, I/O and local-bus blocks, Hub-special page and CPU and reset wiring. Only the board inputs differ: memory banks, processor population, firmware record, board-id word and L1 identity. All of it lived inside FuelBoard and FuelMachine with the Fuel's values written in, so a second board would have copied several hundred lines that must keep agreeing. Move the node into ip35::node. Node::build takes the board inputs in a NodeConfig. The Fuel's machine file is renamed ip34_machine.rs after its board, as the Octane and O2 are named, since ip35_machine.rs is the name of the software platform the Tezro's IP53 board shares. The widget DMA adapter that turns a board's bus accesses into Bedrock inbound traffic from a widget moves in beside it, so a V12 on the Tezro reaches memory as the Fuel's VPro does. The node also owns the buses a processor reads memory and code through, and wires a processor to them. The PI answered as a node with one processor, with slot A of PI0 present and every other slot absent, and the Fuel machine set CPU 0's enable bit itself. A Tezro tower can hold one, two or four processors, and the PROM reads the present, enable and CPU-number registers to find them. The board now passes a Population: which of the four slots hold a processor, in the board's order PI0/A, PI1/A, PI0/B, PI1/B, so two processors sit one per interface. Every fitted slot reads present; only CPU 0 starts, so only its enable bit is set at power-on and the others are held disabled, which should lead the PROM to report one enabled processor. PROM 6.210 asks the L1 for a power cycle (command 0x2006) after printing "Starting PROM Boot process", to retry the other processor slots at another PLL setting. The L1 reports it as power down, power up and a restart, and the node turned the power down into a machine power-off, so the Tezro stopped at its first line. Only the shutdown is a power-off now and the restart is a reset, as in QEMU.
The IOC4 has the same keyboard and mouse controller at another place in its BAR, and QEMU shares one model between the two chips (sgi-ioc3-kbc). Here it lived inside ioc3.rs as part of the chip's state and could not be reused. Move the register view, the per-port holdings, the clamp rules and the overflow flag to ioc3_kbc.rs. Each call that can bring in a character now says so and the chip latches its own source bit (the IOC3's status bit 22; the IOC4's bit 6 of its other word). The host-input line that pokes the chip moves with it, as KbcOwner and KbcLine. The IOC3 behaves as before and logs what QEMU logs. The IOC4 maps the controller at 0x200 to 0x210 behind an adapter that takes aligned 32-bit accesses only, hides the IOC3-only receive flags, and latches bit 6 of the other interrupt word for every character that arrives, under that word's own enable and write-one acknowledge. Every IO9 brings the keyboard and mouse out; without it the PROM and the OS find no keyboard and the console falls back to the serial line.
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 Tezros whose inventories have been seen are two- and four-processor machines, and their interface board differs from the one-processor one. The tower guide's slot layout gives it four PCI buses (its overview says three) and a second XIO connector, which the QEMU composition models as a second PIC at widget 11 and a V12 slot at widget 12. Accept 1, 2 or 4 in [machine] cpus, with 4 the default for a Tezro: the Tezro used to read its count from [tezro] cpus while the Octane reads it from [machine] cpus, two keys for one setting. Unset, the key is the profile's default (four on a Tezro, one elsewhere), and it is written back only when set so a saved config keeps following its profile's default. Two or four processors fit the second PIC and number the PICs' PI level sources in QEMU's host order. The second PIC is wired to Bedrock IO at widget 11 with nothing on its buses, and its empty devices fail as master timeouts, as on the IO9 PIC.
The Tezro takes an optional VPro V12, but the board fitted no graphics, so [graphics] could only be none. Add board = "v12", on the VPro model the Octane and the Fuel already use. The V12 is attached at widget 12 behind the PXBOW of the PIC that owns widget 0, and Bedrock IO reaches it through that route with widget offsets, as the Fuel's graphics slot does. Each PI keeps its graphics flow-control registers, stored as written as on the Fuel. The board's interrupt packets reach Bedrock IO as the PIC's do, and its DMA enters Bedrock as widget 12's inbound traffic. On the two- and four-processor board widget 12 is QEMU's graphics slot. On the one-processor board the tower guide gives one XIO connector for a VPro board but names neither its widget nor its route, so the same placement there is inferred, and the code says so. PROM 6.210 and the IP35 kernel learn which XIO widgets exist only from widget 0's crossbar: link n is usable when its status word has bit 31 (alive) and its auxiliary status has bit 5 (present). The second PIC was wired straight to Bedrock IO as widget 11, so the PXBOW reported link 11 absent; the PROM never addressed it and IRIX listed only widgets 12 and 15. It is now an endpoint of the IO9 PIC's PXBOW, beside the hub's link 10 and link 15, reached through the switch. Register the V12 with the monitor, as the Fuel does, so the `vpro` commands resolve on a Tezro that has one.
…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. Each CPU's cached view of memory follows the withdrawal: its bitmap loses the region, its host pages stay, and a page it holds there is laid from the bank again when released. 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. 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 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.
…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.
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.
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 carries a Disable environment record for each empty position, B and D on the two-processor board and B, C and D on the one-processor board, so the PROM's first boot goes straight through instead of power-cycling the module three times per empty position to give up on them. On the one-processor board that recovery was nine power cycles, about 30 s of every boot from a fresh flash. The four-processor board has no empty position and seeds 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.
…the guide The one-node network reads and PROM 6.210's crossbar check were answered as the PROM expects but no test ran them on the Tezro board. Add them, and a test 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 the limits of the machine.
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 is 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.
None of iris's PCI functions has an expansion ROM, but dword 12 of the header stored whatever was written to it. A size probe of all ones then read back as a 2 KiB ROM, and IRIX 6.5.30 on the Octane assigns it an address and warns "pciba: ROM is 0x...". A device without a ROM leaves the register unimplemented, and an unimplemented register reads zero and drops writes (PCI Local Bus Specification 2.2, sections 6.1 and 6.2.5.2), which is how firmware learns there is no ROM.
When a guest never finds or never programs a function, the question is which configuration cycles it sent and which of them anything answered. The bus is where every host's cycles meet, so log them there: bus, device, function, register, width, the value, and "no answer" when no function sits at the address. The check is the devlog module's enabled flag, and configuration cycles are firmware and probe traffic, never a fast path.
A driver that gives up between two register accesses on the Octane leaves no trace of what it did in between: it may have programmed the Bridge's direct map or translation table, connected an interrupt through the Bridge and HEART, or tried a DMA that went nowhere. Two log modules show that. bridge logs register reads and writes by name, translation-table writes, a function's transfer that reached no memory (failures only, so successful DMA costs nothing), and each interrupt pin's edges and the HEART vector it sets or clears. heart logs processor-interface and widget register accesses and every interrupt vector raised or lowered. The kernel reads the counter too often to log every read, so heart logs one in 256 with the host clock beside it, which shows the counter's rate. Each check is the module's enabled flag on a register access or an interrupt edge, not on memory or instruction paths.
A bus behind a bridge needs three things the root bus does not: its functions' windows reach the CPU only through the bridge's forwarding windows, their interrupts and DMA go through the bridge, and firmware numbers the bus. PciToPciBridge is a function with a type 1 header (new PciConfig::type1: bus numbers, I/O, memory and prefetchable window registers at their real granularity, no BARs, zero at reset) that owns a secondary PciBus. The caller names its PCI identity; the header is the only part of a bridge chip modelled, and the upper halves of the prefetchable and I/O windows (dwords 10 to 12) read zero, since the lower registers advertise 32-bit prefetchable memory and 16-bit I/O. The type 1 header's expansion ROM base (dword 14) reads zero, as dword 12 does in a type 0 header: no bridge has a ROM. The secondary has no host and no outputs. A pin of device d behind the bridge reaches pin (pin + d) mod 4 of the bridge's device; its DMA is the bridge's, so a function behind a bridge masters as the bridge's root slot. The bus in front merges the BAR regions of the functions behind its bridges whose range lies wholly inside an enabled window (a BAR straddling a window's edge is not forwarded; hardware leaves that undefined), into the same table and the same mappings, inheriting the host window the mapping lies in. A configuration write updates the mappings when it changes the command register, a BAR, or a bridge's bus numbers or windows (dwords 1 and 4 to 9), and a change behind a bridge updates the buses in front of it. Parents hold the bridge function, which holds its bus, and the bus holds its parent weakly. find_bus(n) finds the bus a guest bus number reaches from the bridges' secondary and subordinate numbers, and MACE's CONFIG_ADDR now sends a non-zero bus field there. Raising bridge control bit 6 resets the buses behind the bridge and nothing else, through a new PciFunction::config_written hook that the bus calls when a configuration write changes a dword. The hook sees the write, not the register after the header reset, which clears bit 6. RST# on the primary bus reaches behind the bridge too, as in PCI, so a cold reset resets the functions behind a bridge. Saving a bus saves the headers on the buses behind its bridges. A configuration cycle that reaches the bus behind a bridge and finds no function ends in master abort at the bridge: a read returns all ones (masked to its width), a write is dropped, and the bridge's secondary status records received master abort, write one to clear. Only the root bus leaves an unclaimed cycle to its host. Master-Abort Mode is not modelled; it resets to clear, which is the all-ones behaviour. Nothing machine-level builds a PciToPciBridge yet; it is reachable through the tests and becomes a device type with the device registry.
Each widget's first 2 GB of XIO space also appears at physical 0x8_0000_0000 + w * 2 GB. For the Bridge that window holds the 16 MB small window and, from offset 0x4000_0000, the 32-bit alias of PCI memory space from PCI address 0 (Bridge ASIC specification, p. 52), with the bytes placed by Control's memory swap bit rather than a Device(x) register (p. 55). iris mapped neither, so any PIO there was an unmapped physical access. IRIX 6.5.30 reaches a BAR this way. After a warm reboot it numbered a PCI-to-PCI bridge in slot 5 again, this time with a memory window of 0x80_0000 to 0xff_ffff, and read the NVMe controller behind it at 0xF_C080_0008 (PCI memory 0x80_0008). The read bus-errored and the kernel panicked. The Bridge now publishes the alias as a ninth CPU window, so a BAR it decodes whole becomes a run the processor reaches directly, as the device windows' runs are. Ip30Bus maps the 2 GB window to the Bridge and puts alias runs there instead of in the small window; a widget with no 2 GB window maps no such run. The specification calls 16 MB to 1 GB reserved without saying what an access does, and iris answers it with a bus error. An alias cycle nothing claims is a master time-out whose capture names device 0; the specification does not say which device it names. The address map's section on XIO addressing put widget 15's window at 0xF_0000_0000, against its own physical map and the address IRIX uses; it now says 0xF_8000_0000. The 64-bit alias at 0x8000_0000 and PCI I/O at 4 GB, which only the 64 GB window reaches, are still not modelled.
The PCI layer now has an Octane windows section in docs/pci.md (the Bridge's 2 GB window, what its reserved range answers, and what is not modelled), and the open-items note records what is deferred and what is approximated: restore not being a reset, the AIC-7880 chip reset, the missing external ATE RAM and the per-device reset pins. clanker/plans/pci.md lists what follows this change: the XBridge and PIC moving onto the shared layer with their machines, the XIO PCI carrier, card cage and NVMe in real slots, and the reset work that waits for the machine rework.
jamesbraid
force-pushed
the
tezro
branch
2 times, most recently
from
November 8, 2026 02:24
a4c748a to
1283ac2
Compare
Owner
|
On the "JIT refuses memory above 4 GiB" point in the description: main already handles RAM past 4 GiB in jitv2, so this looks like a property of the Bedrock memory layout in this branch rather than a JIT limit. The PR's fork point includes the Octane 8 GB work (73b589c, b8fca3a, 2026-10-31), where:
Notes: clanker/devices/ip30-memory-past-4gb-takes-a-16gb-window.md and clanker/cpu/mp-llsc-is-a-host-compare-exchange.md. Your own 69261e9 already says a jitv2 build no longer needs memory below 4 GiB, so the PR body is behind the branch there. Two requests:
|
Collaborator
Author
|
ty, will rework it, this PR started out before the work you did as well for >4GB |
The Tezro ships with R16000s at 700, 800, 900 and 1000 MHz, and the part table carried no R16000. Add the family and one Part per shipped clock: r16000_700 (rev 2.1, which the _EARLY const marks so the Fuel's later rev 2.2 700 MHz part keeps the plain name), r16000_800 (2.2), r16000_900 and r16000_1000 (both 3.0), with 4, 4, 8 and 16 MB of secondary cache. hinv prints an R16000's major revision two below the PRId register's major, as the Linux and OpenBSD CPU probes do. Part::prid undoes that in one family match, so 2.1, 2.2 and 3.0 read 0x0f41, 0x0f42 and 0x0f50. 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 three sizes are three aliases of one shadow cache (Config SS codes 3, 4 and 5), and with_cpu! picks the alias from the part's l2_kb 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 lists these parts yet. Tests pin the PRIds, run each alias through the interpreter, the Config, TLB-write and LWR checks and the jitv2 equivalence check, and pin the type with_cpu! builds per part. The cached-view line tests run on the three R16000 types too: a primary data line and a secondary line hold their page in the CPU view, Index Store Data fills it, and dropping the lines ends it.
A CPU's cache, its decode cache and its compiled code reach RAM through ppmem's window, and every CPU and Physical's direct paths share that one window. The R10000-family PROMs use the caches as working memory before memory is set up: lines made valid with Index Store Tag and Index Store Data hold a stack or code while uncached accesses, which bypass the caches (NEC VR10000 user's manual p. 75), see memory. The Tezro's processors hold lines for the same addresses with different contents at once. One shared window has no room for any of that. PpMemSpace::add_cached_view gives a CPU a window of its own. Every mapping laid in the shared window is laid in each view as well, and a view publishes its bitmap to that CPU's inline fields, so the CPU reads its view through the same base pointer and bitmap test it used before. hold_line_page turns a page of a view into private memory, starting as a copy of memory, and release_line_page lays memory back when the last line goes, dropping what the page held. A remap saves and restores the held pages around it. A page with no memory behind it is refused. One in a region the window maps only in part takes the whole region in that view, reading zeros where nothing is mapped, because the region bitmap is all a CPU tests: the O2's POST holds lines for page 0 before CRIME maps the rest of its region. Nothing holds a page yet. The views are not coherent with each other or with DMA. The generic machine, the Octane, the O2 and the IP27 machine give each CPU a view now. Physical and the devices that read the window directly keep the shared one. Nothing on a load, store or fetch changes. The CPU no longer takes over the shared window's bitmap, so set_bitmap_sink and add_bitmap_sink lose their last callers and go.
The Fuel's and the Tezro's PROMs run code from primary instruction lines they make valid with Index Store Tag and Index Store Data, and on iris those words differ from memory once memory is set up there (measured on the model, not on hardware). Compiles read memory and compiled code is shared by every CPU, so code compiled from memory would run in place of the lines. Upstream iris has no machine that runs code from a held page yet: the O2 keeps only a stack in lines and runs POST uncached, and the Octane's lines name no memory. This is for the Fuel and the Tezro. The first CPU to hold a page now puts GEN_HELD on the page's generation counter, which retires what was compiled there, and the last release takes it off with an ordinary bump. A compile that finds the mark publishes no code for that generation and denies every offset, so arrivals interpret the page from the CPU's own view without asking again. The first compile after the release allows every offset once more. The release lowers the counter, so publish treats any change of generation as new bytes, not only a rise. ppmem owns the mark, so it is one mark however many CPUs hold the page, and a remap moves it to the counter that backs the page afterwards. All of it is at compile time. Dispatch and compiled code are unchanged, and every other page stays compiled. Without the denial, four Tezro processors asking for the same held PROM page took 65-77 s to the BASEIO monitor against 38-42 s with it (an earlier version of this change, jitv2 loop-profile builds).
…view The R10000-family PROMs keep stacks and data in cache lines they make valid with Index Store Tag and Index Store Data. The O2's POST tests page 0 uncached while its stack lives in secondary and primary data lines for the same page, the Fuel's and Tezro's stacks sit in primary data lines for 0x100000-0x107fff, and on the Tezro every processor holds those lines at once with its own contents. The shadow cache kept the tags and data arrays for diagnostics only, so a cached access read memory. A tag change that makes a primary data or secondary line valid now holds its page in the CPU's cached view and writes the line's data array into it, since Index Store Tag writes the tag array alone (NEC VR5000/VR10000 instruction manual pp. 24-25). Loads, stores, fetches, the decode cache and compiled code read the view as before and change not at all. Uncached accesses bypass the caches (NEC VR10000 user's manual p. 75) and keep seeing memory, as do other masters, since the views are not coherent. A tag that names another line, or none, ends the line, and the page goes with its last line. Index Store Data into a held line writes through. The tags stay the only line state, plus a held bit. The O2's R5000 POST keeps its stack the same way, in primary data lines for page 0 made valid by Index Store Tag (D) with TagLo 0xc0, and tests page 0 uncached meanwhile. An R5000 primary data tag has the R10000's PTag and PState fields (NEC VR5000 user's manual U11761EJ6V0UM p. 133), and a store hit on a write-back line makes no bus request (p. 243), so a valid one holds its page too. The R5000 has no Index Store Data, so the page starts as memory. Its instruction lines hold nothing: with no Index Store Data to fill one, it could hold only what memory holds. Every line the shipped PROMs stage ends by Index Store Tag, none is written back, and none is read back with Index Load Data while it holds a page, so the writeback and invalidate operations stay as they were. The R12000 zeroing a primary data tag at Index Writeback Invalidate ends a line like any other tag change. A secondary doubleword puts TagLo at the lower address. The manuals fix only that TagHi is the doubleword's high word and TagLo its low word (MIPS R10000 user's manual p. 201, NEC VR10000 user's manual p. 289); which address each pairs with is inferred, not established, from shipped PROMs. PROM 6.210 on the Fuel and the Tezro stages code with the word at A in TagLo and the word at A + 4 in TagHi, 990 pairs in a Tezro boot, and its staged code equals its image only this way. The IP35 boot-flash PROM's staging pairs them the same way: simulated with the other order, 378 of its 562 staged fetches break, every trampoline fetch among them. With the other order the Tezro's PROM falls back to running its cache test uncached, which took twice as long to reach the BASEIO monitor. Limits: a page, not a line, is private, so the rest of a held page shows memory as it was when the page was taken, and nothing is coherent with other CPUs or DMA. A snapshot taken while lines hold pages is refused at restore. Lines that name no memory, like the Octane's tag test patterns, hold nothing. `l2 probe 0` lists the pages held.
Upstream iris has no machine that needs this yet. The Fuel's and the Tezro's PROM 6.210 copies itself to memory from code it stages in primary instruction lines with Index Store Tag and Index Store Data (I) and runs through cached xkphys, and once memory is configured, memory at those addresses holds something else. Without the lines the PROM hangs on iris at "Copying PROM code to memory ..." (measured on the model). A valid primary instruction line now holds its page in the CPU's view like a data line, with each word written there as the instruction it predecodes. A primary instruction word is 36 bits of predecoded instruction (NEC VR10000 user's manual p. 288, established). Neither manual gives the layout, so it is inferred from the words PROM 6.210 stores, and only its eleven sampled classes decode. A word of any other class leaves memory's word in the page, where hardware would run it. Index Store Data (I) into a held line writes through. The PROM gets the predecoded form of other code from the hardware: it runs a secondary line it staged, which refills a primary line, and reads the words back with Index Load Data (I) to stage them itself (measured on the model). The model does not refill on a fetch, so Index Load Data (I) in a set a held secondary line maps to now answers with that line's words predecoded, as if it had been refilled, until Index Store Data (I) writes the set. A guest's own pattern in the set reads back as written (MIPS R10000 user's manual pp. 201-202). An instruction outside the eleven classes comes back in a form only this model reads, bits 35:32 set over the instruction, which takes predecode classes 0x78-0x7f. Nothing on a fetch, load or store, and nothing in the decode cache, changes. A page held only for code also answers cached loads with the code's bytes, where hardware would read memory.
The O2 kept POST's stack in bank 0 and, while a primary data tag for page 0 stood, pointed the decode table at a page of its own for the uncached path, switched by a hook on every Index Store Tag (D). The CPU's cached view holds those lines now, with the bank, the decode table and the rendering engine on memory meanwhile, so the side page, its hook in the shadow cache, the slot-1 mapping it needed at reset and MipsCache::set_pd_tag_store_hook go. A snapshot taken during POST by an older iris carries the side page but no cache lines to rebuild the stack from, and is refused.
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.
The Fuel's and Tezro's PROMs keep their stack in primary data lines for 0x100000-0x107fff and run code from primary instruction lines at the PROM's own addresses before they have configured memory. The cached view refuses a page with no memory behind it, so those lines would hold nothing and the PROM would read the bus. allow_unbacked_lines names ranges where a page may be held with nothing mapped behind it. Such a page starts as what the cache's bus shows there, and its region is the view's while it stands, as for any held page. A remap keeps the page, so memory arriving mid-phase does not lose the stack. jitv2 stays off a held page by the mark ppmem puts on the page's generation counter, but only on ppmem's own counters. The IP35 boards' compiles check other ones: Bedrock's for RAM, and for the reset PROM that the bus maps over memory at 0x1fc00000 the flash's, which is neither ppmem's nor Bedrock's. A page held there carried no mark, so jitv2 could compile the image's bytes for a page whose lines hold other words, as a PROM that stages code in instruction lines at its own address makes it. The cache now puts the same mark on whatever counter its bus gives the page when that is not ppmem's, and takes it off with the page's last line. That runs at CACHE instructions only; loads, stores and fetches are unchanged. Test branch only.
The Fuel's PROM keeps its stack in primary data cache lines for 0x100000-0x107fff before it has configured any memory, so those lines have nothing behind them when they are made valid. The cached view that the CPU reads memory through can hold such lines only in places declared for it. Declare all four bank slots as places lines may be held before memory is mapped. Without the declaration the PROM's first stack access takes a data bus error.
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. Each head owns a shared PciBus and is its PciHost, as the XBridge heads are: it supplies a slot's DMA and takes the level of its eight interrupt inputs, device d's INTA reaching input d - 1, and it publishes its CPU windows (the four short device-I/O windows and the 1 GiB PCI memory window) so a whole BAR page is served from the head's own table. 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, 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. A conventional adapter sees a numeric value at the processor's address with the low two bits corrected, as on MACE; a slot marked self-ordered (IOC4) gets the processor's byte sequence. Which order the silicon gives a 64-bit access to a conventional adapter is not known; it takes word values. 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. The shared bus decides which of two functions that decode the same range answers, as a probing PROM leaves them: the lower device. Real silicon with two matching comparators is undefined, and the QEMU model's newest-wins rule is not carried over. XBridge's whole-page BAR table is shared with the PIC.
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.
A NIC's indirect register and memory windows are configuration dwords the chip computes: a write to the data dword is a register write, and writing the same value twice is two register writes. The header cannot do that, because it stores a value and tells a function about it only when the stored value changed (config_written), and the window dwords have no writable bits to change. config_write is offered a whole-dword write before the header. A function that takes it returns true and the header stores nothing; the default leaves every other function as it was. Narrower writes keep the header's path. The read side already works: a function overrides config_dword.
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: the function answers them from config_dword and config_write, so every bus that attaches it works the same. A slot that drives REQ64# is part of the board's config; the chip samples it as reset ends. 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.
jamesbraid
force-pushed
the
tezro
branch
3 times, most recently
from
November 10, 2026 01:13
934952b to
f4687f4
Compare
At every start the IP35 PROM checks the configuration block in its boot flash against the node board's CPU configuration. It reads that from the board area of the L1's component 0 record. When the area carries none, it checks against a row of its own table chosen by the Bedrock board word. A block that matches neither draws "CPU configuration values mismatch ... reflash the PROM image" before the maintenance menu. The Tezro's board word 0x2001 picks a 400 MHz row, so its 700 MHz flash block failed the check on every boot. Let a board give the L1 a mode word and CPU and Hub clocks. The board area then takes format 2 and the CPU fields after its identity, in the layout QEMU's L1 gives the Origin 350 and Tezro boards and the stock PROM 6.210 accepts. Only Config0 and the two clocks are known, so the other fields stay empty rather than zero. No factory record has been read: the bytes are a software input, not a capture. A board that sets nothing keeps its record byte for byte, so the Fuel is unchanged.
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, with the flash record's mode word and clocks as the node board's CPU configuration. Without that configuration the PROM checks the flash against the 400 MHz table row board word 0x2001 selects and prints "CPU configuration values mismatch" on every boot. 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.
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.
Sits on #368 (Fuel), which sits on #448 and #422.
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. 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, two or four R16000s, 4 GiB by default, the IO9 devices (serial, clock, PS/2, SCSI, gigabit Ethernet) and an optional V12. IRIX 6.5.30 boots to the login prompt on every processor count.
(The screenshots are from earlier heads of this branch.)
What it adds
cpu = "r16000", or nocpukey, selects the 700 MHz part.[machine] cpustakes 1, 2 or 4, with 4 the default for a Tezro.board = "v12"fits a VPro V12 at widget 12, behind the PXBOW.Worth a close look
Shared code:
XtalkHost; an interrupt whose target is not the host goes to the PXBOW packet sink, and the PIC forwards it to its peer. PROM 6.210's crossbar check writes the PXBOW's read-only error address registers, which iris turned into a Data Bus Error; the PXBOW now completes those writes, since an uncached store can't take an error response (NEC U10278EJ4V0UM p.104, table 6-1). A standalone Crossbow, such as the Octane's, still refuses them, and a test covers it.R12000_270. The secondary cache size is a parameter of the shadow cache type, picked once from the part at machine construction; nothing is read or branched on at run time. The R16000 shares the R10000 family's programming model and the R12000's FIR, as a model choice: no R16000 manual was available.PciFunction::config_write. The BCM5701's indirect register and memory windows are configuration dwords where writing the same value twice is two register writes, but the header only reports a write when the stored value changes.config_writeis offered a whole-dword write first; the default declines, so every other function is unchanged.FETCH_ALIAS, R16000 caches only. The IP35 PROM fetches its own code uncached at 0x1fcxxxxx. Once a RAM bank covers that window, the compile pool's cached view answered RAM there, which the PROM's memory test zeroes, so the JIT compiled NOPs in place of PROM routines. An uncached fetch of the reset window now goes to the bus at an address of its own. Ungated, the check cost the R14000 interpreter benchmark about half a percent of its host instructions, so only the R16000 model takes it.Approximations the commits state:
Testing
On main 79bd1ce, on the Fuel branch at 1c16d9c; head f4687f4, 22 commits.
cargo test --workspace --no-run --features jitv2andcargo check --bins --tests.cargo test --lib, release and debug: default features 3433 passed,jitv23953 passed. The only failure isexit_persistence_tests::no_process_exit_bypasses_the_flush, which fails on main too.cargo check -p iris-guiis clean.--validate: the default profile is ok;r16000_800with 4 CPUs is ok and with 2 is refused;r16000_1000is ok; an unknown part is refused with the list of valid ones.halt -p, holds no cache lines at login, and logs no media errors.jitv2hinvshows 1, 2 and 4 "700 MHZ IP35 Processors": R16000 rev 2.1, 4 MB secondary cache, 4096 MB of memory.Networking: the BCM5701 (
tg0) resolves ARP, receives with no errors, and a telnet through the port forward returns the IRIX banner. An earlier version of the BCM5701 commit swapped the halves of its host-address register pairs, so the first transmit fetched its send ring from the wrong address andtg0sent nothing.IRIX's
olperiFPU diagnostic does not pass yet on the Tezro or the Fuel: it depends on a separate fix for the upper word of FP registers after single-precision results.Not run on this head: an
iris-benchcell, the OpenGL tests andol*diagnostics, boots of the 800 and 900 MHz parts,jitv2with one processor or the 1 GHz part, macOS and Windows.