diff options
Diffstat (limited to 'qemu/roms/openbios/arch/sparc64/call-client.S')
-rw-r--r-- | qemu/roms/openbios/arch/sparc64/call-client.S | 236 |
1 files changed, 236 insertions, 0 deletions
diff --git a/qemu/roms/openbios/arch/sparc64/call-client.S b/qemu/roms/openbios/arch/sparc64/call-client.S new file mode 100644 index 000000000..f365e3cb1 --- /dev/null +++ b/qemu/roms/openbios/arch/sparc64/call-client.S @@ -0,0 +1,236 @@ + .globl sparc64_of_client_interface, client_tba + + +/* + * SAVE_WINDOW_STATE and RESTORE_WINDOW_STATE are used to ensure + * that the CPU window state is preserved across CIF calls. This is + * to workaround a *BSD restriction that window fill/spill traps must + * be minimised during trap table takeover, and likely emulates the + * behaviour of OBP. + */ + +#define SAVE_WINDOW_STATE(type) \ + setx client_window, %g6, %g1; \ + rdpr %cwp, %g7; \ + stx %g7, [%g1]; \ + rdpr %cansave, %g7; \ + stx %g7, [%g1 + 0x8]; \ + rdpr %canrestore, %g7; \ + stx %g7, [%g1 + 0x10]; \ + rdpr %otherwin, %g7; \ + stx %g7, [%g1 + 0x18]; \ + rdpr %wstate, %g7; \ + stx %g7, [%g1 + 0x20]; \ + rdpr %cleanwin, %g7; \ + stx %g7, [%g1 + 0x28]; \ + \ + stx %o0, [%g1 + 0x30]; \ + stx %o1, [%g1 + 0x38]; \ + stx %o2, [%g1 + 0x40]; \ + stx %o3, [%g1 + 0x48]; \ + stx %o4, [%g1 + 0x50]; \ + stx %o5, [%g1 + 0x58]; \ + stx %o6, [%g1 + 0x60]; \ + stx %o7, [%g1 + 0x68]; \ + \ + rdpr %pstate, %g7; \ + stx %g7, [%g1 + 0x70]; \ + rd %y, %g7; \ + stx %g7, [%g1 + 0x78]; \ + rd %fprs, %g7; \ + stx %g7, [%g1 + 0x80]; \ + \ + /* Now iterate through all of the windows saving all l and i registers */ \ + add %g1, 0x90, %g5; \ + \ + /* Get the number of windows in %g6 */ \ + rdpr %ver, %g6; \ + and %g6, 0xf, %g6; \ + inc %g6; \ + \ +save_cpu_window_##type: \ + deccc %g6; \ + wrpr %g6, %cwp; \ + stx %l0, [%g5]; \ + stx %l1, [%g5 + 0x8]; \ + stx %l2, [%g5 + 0x10]; \ + stx %l3, [%g5 + 0x18]; \ + stx %l4, [%g5 + 0x20]; \ + stx %l5, [%g5 + 0x28]; \ + stx %l6, [%g5 + 0x30]; \ + stx %l7, [%g5 + 0x38]; \ + stx %i0, [%g5 + 0x40]; \ + stx %i1, [%g5 + 0x48]; \ + stx %i2, [%g5 + 0x50]; \ + stx %i3, [%g5 + 0x58]; \ + stx %i4, [%g5 + 0x60]; \ + stx %i5, [%g5 + 0x68]; \ + stx %i6, [%g5 + 0x70]; \ + stx %i7, [%g5 + 0x78]; \ + bne save_cpu_window_##type; \ + add %g5, 0x80, %g5; \ + \ + /* For 8 windows with 16 registers to save in the window, memory required \ + is 16*8*8 = 0x400 bytes */ \ + \ + /* Now we should be in window 0 so update the other window registers */ \ + rdpr %ver, %g6; \ + and %g6, 0xf, %g6; \ + dec %g6; \ + wrpr %g6, %cansave; \ + \ + wrpr %g0, %cleanwin; \ + wrpr %g0, %canrestore; \ + wrpr %g0, %otherwin; + + +#define RESTORE_WINDOW_STATE(type) \ + setx client_window, %g6, %g1; \ + \ + /* Get the number of windows in %g6 */ \ + rdpr %ver, %g6; \ + and %g6, 0xf, %g6; \ + inc %g6; \ + \ + /* Now iterate through all of the windows restoring all l and i registers */ \ + add %g1, 0x90, %g5; \ + \ +restore_cpu_window_##type: \ + deccc %g6; \ + wrpr %g6, %cwp; \ + ldx [%g5], %l0; \ + ldx [%g5 + 0x8], %l1; \ + ldx [%g5 + 0x10], %l2; \ + ldx [%g5 + 0x18], %l3; \ + ldx [%g5 + 0x20], %l4; \ + ldx [%g5 + 0x28], %l5; \ + ldx [%g5 + 0x30], %l6; \ + ldx [%g5 + 0x38], %l7; \ + ldx [%g5 + 0x40], %i0; \ + ldx [%g5 + 0x48], %i1; \ + ldx [%g5 + 0x50], %i2; \ + ldx [%g5 + 0x58], %i3; \ + ldx [%g5 + 0x60], %i4; \ + ldx [%g5 + 0x68], %i5; \ + ldx [%g5 + 0x70], %i6; \ + ldx [%g5 + 0x78], %i7; \ + bne restore_cpu_window_##type; \ + add %g5, 0x80, %g5; \ + \ + /* Restore the window registers to their original value */ \ + ldx [%g1], %g7; \ + wrpr %g7, %cwp; \ + ldx [%g1 + 0x8], %g7; \ + wrpr %g7, %cansave; \ + ldx [%g1 + 0x10], %g7; \ + wrpr %g7, %canrestore; \ + ldx [%g1 + 0x18], %g7; \ + wrpr %g7, %otherwin; \ + ldx [%g1 + 0x20], %g7; \ + wrpr %g7, %wstate; \ + ldx [%g1 + 0x28], %g7; \ + wrpr %g7, %cleanwin; \ + \ + ldx [%g1 + 0x30], %o0; \ + ldx [%g1 + 0x38], %o1; \ + ldx [%g1 + 0x40], %o2; \ + ldx [%g1 + 0x48], %o3; \ + ldx [%g1 + 0x50], %o4; \ + ldx [%g1 + 0x58], %o5; \ + ldx [%g1 + 0x60], %o6; \ + ldx [%g1 + 0x68], %o7; \ + \ + ldx [%g1 + 0x70], %g7; \ + wrpr %g7, %pstate; \ + ldx [%g1 + 0x78], %g7; \ + wr %g7, 0, %y; \ + ldx [%g1 + 0x80], %g7; \ + wr %g7, 0, %fprs + + + .data + .align 8 + + .skip 16384 +openbios_stack: + +client_stack: + .xword 0 +client_tba: + .xword 0 +client_window: + .skip 2048 + + + .text + .align 4 + .register %g2, #scratch + .register %g3, #scratch + .register %g6, #scratch + .register %g7, #scratch +/* + make some more space on stack since linux kernel only provides 128 bytes + without memory to spill registers (used by gcc in -O0 mode) +*/ + +sparc64_of_client_interface: + + /* Save globals on callers stack */ + add %sp, -56, %sp + + stx %g1, [%sp + 2047 + 0] + stx %g2, [%sp + 2047 + 8] + stx %g3, [%sp + 2047 + 16] + stx %g4, [%sp + 2047 + 24] + stx %g5, [%sp + 2047 + 32] + stx %g6, [%sp + 2047 + 40] + stx %g7, [%sp + 2047 + 48] + + /* Save client trap table */ + setx client_tba, %g6, %g7 + rdpr %tba, %g6 + stx %g6, [%g7] + + /* Save existing stack */ + setx client_stack, %g6, %g7 + stx %sp, [%g7] + + /* Save windows */ + SAVE_WINDOW_STATE(cif) + + /* Move to OpenBIOS stack */ + setx openbios_stack - 2047 - 192, %g6, %g7 + mov %g7, %sp + + /* Call client inteface */ + call of_client_interface + ldx [%g1 + 0x30], %o0 + + setx client_window, %g6, %g1 + stx %o0, [%g1 + 0x30] + + /* Restore windows */ + RESTORE_WINDOW_STATE(cif) + + /* Restore stack */ + setx client_stack, %g6, %g7 + ldx [%g7], %sp + + /* Restore client trap table */ + setx client_tba, %g6, %g7 + ldx [%g7], %g6 + wrpr %g6, %tba + + /* Restore globals */ + ldx [%sp + 2047 + 0], %g1 + ldx [%sp + 2047 + 8], %g2 + ldx [%sp + 2047 + 16], %g3 + ldx [%sp + 2047 + 24], %g4 + ldx [%sp + 2047 + 32], %g5 + ldx [%sp + 2047 + 40], %g6 + ldx [%sp + 2047 + 48], %g7 + + add %sp, 56, %sp + + jmp %o7+8 + nop |