The point of this change is that g_icycles might not always be the
current time. For example, when g_realtime is true.
process_timers now returns the next time a timer needs to be processed.
Don't subtract time_now, because for realtime mode, the time has changed
since time_now was captured, especially for timers that take a long time
to process.
When there's no timer pending, process_events will return a time of now
+ 0.5 ms for when process_timers should be called again.
This fixes booting Mac OS 8.6 hanging at the Happy Mac on the iMac G3.
During boot, as part of installing the nanokernel, the OS attempts to do
a full TLB flush via a series of `tlbie` instructions. We were trying to
respect the effective address that's specified in the instruction, but
the semantics of how it should be interpreted are different on the 750
CPU - the [user manual](https://www.nxp.com/docs/en/reference-manual/MPC750UM.pdf)
says:
> The `tlbie` instruction invalidates all TLB entries indexed by the EA,
> and operates on both the instruction and data TLBs simultaneously
> invalidating four TLB entries. The index corresponds to bits 14-19 of the EA.
We were not respecting the bits 14-19 part (`TLB_VPS_MASK` matched bits
4-19), and thus not matching all expected entries. The stale entries
would remain, leading to unexpected behavior.
Rather than trying to more precisely model the 750 behavior, we instead
flush all PAT-derived TLB entries. This matches QEMU's approach (see
qemu/qemu@3dcfb74fd4) and is allowed by
[the PowerPC specification](http://refspecs.linux-foundation.org/PPC_hrm.2005mar31.pdf):
> For example, the tlbie instruction may be implemented to purge all TLB
> entries in a congruence class (that is, all TLB entries indexed by the
> specified effective address which can include corresponding entries in
> data and instruction TLBs) **or the entire TLB**.
As of #219 a full flush is not that expensive, thus this does not
materially affect performance. The goal of the guest OS is a full TLB
flush anyway (hence the sweep of `tlbie` instructions), so we don't end
up doing too many extra lookups after they're done.
For #184
XNU frequently changes BAT and PAT mappings for physical-memory access.
Each resulting context synchronization can walk tens of thousands of TLB
entries, which consumes a lot of time.
Record the instruction and data TLB slots populated by BAT or PAT and
invalidate only those slots. Pending invalidations are coalesced by source,
and BAT updates cover PAT-derived entries because a new BAT can shadow a page
mapping.
Saves another 0.7 secondss in the time it takes to boot Mac OS X 10.3 to
the WindowServer (compared to the generational PAT invalidation).
PAT generation counters make invalidation constant-time by adding a
generation check to every TLB lookup, but they added a per-access overhead
to compare generation counters. I am swithcing to an alternate approach
(explicitly tracking populated entries, which can also be used for
BAT-derived entries), and am removing this first to make that diff
easier to read.
This logically reverts the work from #199 (the TLB helper refactoring
from #203 prevents a mechanical revert).
Added controls to increment/decrement icnt_factor and toggle g_realtime
for testing and debugging purposes.
Control Alt Shift = -> increment_icnt_factor();
Control Alt Shift - -> decrement_icnt_factor();
Control Alt r -> toggle_g_realtime();
Co-Authored-By: joevt <joevt@shaw.ca>
The ATI drivers uses dcbz to simulate a write-combining buffer when
sending HOST_DATA payloads.
The intent is something like this (on real hardware):
```
dcbz:
cache line = [0, 0, 0, 0, 0, 0, 0, 0]
ATI sees nothing
following stores:
cache line = [A, B, C, D, E, F, G, H]
ATI still sees nothing
dcbf or eviction:
write back [A, B, C, D, E, F, G, H]
ATI receives one completed cache line
```
With our previous cacheless implementation, the behavior was more like this:
```
dcbz:
ATI immediately receives [0, 0, 0, 0, 0, 0, 0, 0]
following stores:
ATI then receives [A, B, C, D, E, F, G, H]
dcbf:
no-op
```
The zero-ed out values have side effects (32 pixels of black being
written), so the intermediate state was visible.
The truly correct fix is to fully model the data cache, and do an
eventual write back of the modified cache lines with their final values,
but that is very invasive (and likely to be slow).
We instead just make `dcbz` a no-op for MMIO regions, which lets us
avoid that intermediate state and unintended side effects.
We end up flushing all entries very frequently (hundreds of thousands of times
during Mac OS X boot), and were spending a lot of time in `tlb_flush_entries`
walking through the array. We can instead store a generation counter and
make invalidation be a simple increment. This does make validity checks slightly
more expensive, but it appears to be a net win. On my machine it takes booting
the Mac OS X public meta to the Finder from ~58 seconds to ~35 seconds.
`TLBEntry` and its cache constants and flags are used exclusively by
ppcmmu.cpp. Keep them out of the public MMU header so future TLB changes
do not expand its interface.
This required a small fix in dbdma.cpp since it was implicitly relying on
an include that we removed.
Also declare `gTLBFlush*` variables as static to make it explicit that we
don't expect them to be accessed outside of ppcmmu.cpp.
Followup to #186 - select `MSR[POW]` handling mode once during CPU
initialization instead of treating every processor as `HID0`-gated.
We match documented (and QEMU) behavior:
- 603/7xx CPUs require `HID0` doze, nap, or sleep bits before we enter
the emulator sleep loop
- 604-family CPUs use just for `POW` directly
- 601 does not support `POW` at all
xnu-123.5 and similar vintage kernls enter the PowerPC 750 powersave mode
when the scheduler has no runnable work. `machine_idle_ppc` selects HID0
doze or nap, records a nap timestamp, then executes mtmsr with MSR[POW]
set. It stays in a loop there (in case the power saving had no effect
immediately) - it expects execution to resume at the `machine_idle_ret`
label (when an interrupt occurs), which ends up returning from the
function.
We model this state explicitly:
- When mtmsr sets MSR[POW] with the HID0 doze/nap/sleep flags, set a new
EXEF_SLEEP flag.
- When EXEF_SLEEP is set, keep processing host events without advancing
the guest PC.
Towards getting the 10.0 public beta to boot (#154) - before this we
would enter the DPPC debugger shortly after this instruction (due to a
bogus MSR LE flag eventually being set). Now we make more progress and
end up in a "Still waiting for root device" loop (presumably something
at the ATA layer)
===============
Rairii changes:
memctrlbase: The memory controller may have an endian swap mode.
mpc106: Only mmio regions need endian swap and only if PCIR_LE_MODE is
set.
videoctrl: Add framebuffer_in_main_memory flag. Use needs_swap_endian.
debugger: Fix disasm for little endian mode.
ppcmmu: little endian mode requires munging of the data and instruction
addresses, affecting the 3 least significant bits.
ppcexec: pc_real is always recalculated during little endian mode
because instructions are swapped every 8 bytes (due to the address
munging).
==============
joevt changes:
Add SUPPORTS_MEMORY_CTRL_ENDIAN_MODE for memory controller.
Use cmake -DDPPC_SUPPORT_PPC_LE=ON to enable.
Add SUPPORTS_PPC_LITTLE_ENDIAN_MODE for CPU.
Use cmake -DDPPC_SUPPORT_MEM_LE=ON to enable.
Add ppc_change_endian to handle endian switch for MPC601 (uses HID0
instead of MSR) and later PPC CPUs.
Add is_LE flag to ppc_state to indicate little endian mode for all
processor versions.
Create separate ppc_exec_inner template functions for little and big
endian.
Switch endian mode in the ppc_exec outer functions when power_off_reason
is po_endian_switch.
Since MPC601 doesn't have ILE and LE bits, remove that code for that
CPU.
videoctrl: Uses templated methods. See next commit for a fix that this
change introduces.
- Always add a tick to the timeout calculation.
- Make sure value of decrementer is < 0 for timed decrementer exception.
- Don't recalculate decrementer write time stamp in the case of an
immediate decrementer exception.