On Wed, Jul 10, 2024 at 12:58 PM segaloco via TUHS <tuhs(a)tuhs.org> wrote:
On Wednesday, July 10th, 2024 at 7:12 AM, Marc Donner
<marc.donner(a)gmail.com> wrote:
The basic technique was to load pages of 360
machine code unmodified into Power PC memory, marking the pages as dirty. If the machine
branched into one of these pages the VM trap handler would take the page and translate all
of its code from 370 instructions to Power PC instructions. Once that was done the branch,
suitably recalculated, was reinstated.
Was this under the assumption everything else going on (bus architecture, memory map,
exception/trap handling, etc.) was exactly the same or was the MMU for instance in the
picture translating from 370 addresses to their equivalents on whatever PowerPC machine
was in play?
That or was this only expected to work on PIC, user-level code and anything touching for
instance privilege escalation or with explicit address pointers, I/O port access, etc.
just understood to not be applicable to such a direct translation? To tie it back to
UNIX, for instance, was this thing also able to detect that a UNIX system call was being
made and translate the 370 syscall mechanism into the PowerPC syscall mechanism?
My main experience with emulation is console video games so in that realm, you're
dealing not only with a different CPU architecture but system bus, memory map, peripheral
controllers, etc. Essentially you're not just speaking another language, you're
on another planet entirely. This has lead to a entrenched belief in my mind that this
sort of "direct translation" of CPU operations is a non-starter in emulation
hence my curiosity. If there's some historic approach to emulation efficiently
handling system architecture diversity beyond just CPU instructions, I'm certainly
interested!
Surely things like this have some limitations. Self-modifying code,
for example, would be more challenging (though not impossible) to
implement. But for _most_ programs, where text and data are mapped
separately, it wouldn't be that bad; if data formats are common across
ISAs and the instruction sets are similar-ish, it wouldn't be that
bad. For the mainframe to PowerPC, I imagine floating point would be
the sticky wicket.
P.S. Feeling trepid about continuing this thread on
TUHS rather than COFF, but resisting the urge to bump it (again). To keep it on topic,
I'd mostly be interested in how this sort of thing would handle the UNIX system call
mechanism, because that would certainly illuminate the general idea of translating
something more complicated than an algorithm.
In some sense, this is one of the easiest cases to support. The
system call mechanism necessarily needs some method to trap into the
kernel; the kernel, in turn, needs to look at the state of the user
program to see what it should do in response to that trap. In the
case of going across an ISA, the kernel's job is marginally harder in
that it has to respond to having multiple ways to end up at the system
call handler, but that's not so much different than supporting
multiple system call conventions on a single system. For example, on
x86, we have the SYSCALL instruction, but also SYSENTER and older
software might initiate a syscall by means of the INT instruction
(e.g., INT 0x80 or INT 0x40). Across ISAs, one might enter the kernel
on an illegal instruction trap, in which case the trap handler will
have access to the faulting address and can examine what's there; if
it's e.g. a 370 instruction, one could interpret it and figure out
what to do from there.
Usually system calls are made by calling a stub function in, say, the
C library. That stub then sets up state in whatever manner the system
expects, such as poking the system call number and arguments into
registers or putting them on the stack in a specific order or
whatever; the stub then traps, at which point the kernel takes over,
services the syscall, and (usually) returns control to userspace, the
return value will be examined, errno might be set, and the stub
"returns" to calling code. Of course, the process might have invoked
`exit` or something, in which case control won't be returned to
userspace. Or the call can block, context switches might happen, the
arguments might be bad, the thing can be killed, etc, etc. Anyway, in
the context of this kind of ISA translation, presumably the stubs
would be interpreted and the interpreter would be smart enough to
recognize the trap, in which case it might rewrite to a native trap of
some kind, instead of relying on the illegal instruction trick.
These techniques are rather old, and I think go back much further than
we're suggesting. Knuth mentions nested translations in TAOCP (Volume
1, I believe), suggesting the technique was well-known as early as the
mid-1960s.
- Dan C.