| 1 | /* |
| 2 | * Helpers for HPPA system instructions. |
| 3 | * |
| 4 | * Copyright (c) 2016 Richard Henderson <rth@twiddle.net> |
| 5 | * |
| 6 | * This library is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU Lesser General Public |
| 8 | * License as published by the Free Software Foundation; either |
| 9 | * version 2.1 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * This library is distributed in the hope that it will be useful, |
| 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 14 | * Lesser General Public License for more details. |
| 15 | * |
| 16 | * You should have received a copy of the GNU Lesser General Public |
| 17 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| 18 | */ |
| 19 | |
| 20 | #include "qemu/osdep.h" |
| 21 | #include "qemu/log.h" |
| 22 | #include "cpu.h" |
| 23 | #include "exec/helper-proto.h" |
| 24 | #include "qemu/timer.h" |
| 25 | #include "system/runstate.h" |
| 26 | #include "system/system.h" |
| 27 | #include "chardev/char-fe.h" |
| 28 | |
| 29 | void HELPER(write_interval_timer)(CPUHPPAState *env, target_ulong val) |
| 30 | { |
| 31 | HPPACPU *cpu = env_archcpu(env); |
| 32 | uint64_t current = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
| 33 | uint64_t timeout; |
| 34 | |
| 35 | /* |
| 36 | * Even in 64-bit mode, the comparator is always 32-bit. But the |
| 37 | * value we expose to the guest is 1/4 of the speed of the clock, |
| 38 | * so moosh in 34 bits. |
| 39 | */ |
| 40 | timeout = deposit64(current, 0, 34, (uint64_t)val << 2); |
| 41 | |
| 42 | /* If the mooshing puts the clock in the past, advance to next round. */ |
| 43 | if (timeout < current + 1000) { |
| 44 | timeout += 1ULL << 34; |
| 45 | } |
| 46 | |
| 47 | cpu->env.cr[CR_IT] = timeout; |
| 48 | timer_mod(cpu->alarm_timer, timeout); |
| 49 | } |
| 50 | |
| 51 | void HELPER(halt)(CPUHPPAState *env) |
| 52 | { |
| 53 | qemu_system_shutdown_request(SHUTDOWN_CAUSE_GUEST_SHUTDOWN); |
| 54 | helper_excp(env, EXCP_HLT); |
| 55 | } |
| 56 | |
| 57 | void HELPER(reset)(CPUHPPAState *env) |
| 58 | { |
| 59 | qemu_system_reset_request(SHUTDOWN_CAUSE_GUEST_RESET); |
| 60 | helper_excp(env, EXCP_HLT); |
| 61 | } |
| 62 | |
| 63 | target_ulong HELPER(swap_system_mask)(CPUHPPAState *env, target_ulong nsm) |
| 64 | { |
| 65 | target_ulong psw = env->psw; |
| 66 | /* |
| 67 | * Setting the PSW Q bit to 1, if it was not already 1, is an |
| 68 | * undefined operation. |
| 69 | * |
| 70 | * However, HP-UX 10.20 does this with the SSM instruction. |
| 71 | * Tested this on HP9000/712 and HP9000/785/C3750 and both |
| 72 | * machines set the Q bit from 0 to 1 without an exception, |
| 73 | * so let this go without comment. |
| 74 | */ |
| 75 | cpu_hppa_put_psw(env, (psw & ~PSW_SM) | (nsm & PSW_SM)); |
| 76 | return psw & PSW_SM; |
| 77 | } |
| 78 | |
| 79 | void HELPER(rfi)(CPUHPPAState *env) |
| 80 | { |
| 81 | uint64_t mask; |
| 82 | |
| 83 | cpu_hppa_put_psw(env, env->cr[CR_IPSW]); |
| 84 | |
| 85 | /* |
| 86 | * For pa2.0, IIASQ is the top bits of the virtual address. |
| 87 | * To recreate the space identifier, remove the offset bits. |
| 88 | * For pa1.x, the mask reduces to no change to space. |
| 89 | */ |
| 90 | mask = env->gva_offset_mask; |
| 91 | |
| 92 | env->iaoq_f = env->cr[CR_IIAOQ]; |
| 93 | env->iaoq_b = env->cr_back[1]; |
| 94 | env->iasq_f = (env->cr[CR_IIASQ] << 32) & ~(env->iaoq_f & mask); |
| 95 | env->iasq_b = (env->cr_back[0] << 32) & ~(env->iaoq_b & mask); |
| 96 | |
| 97 | if (qemu_loglevel_mask(CPU_LOG_INT)) { |
| 98 | FILE *logfile = qemu_log_trylock(); |
| 99 | if (logfile) { |
| 100 | CPUState *cs = env_cpu(env); |
| 101 | |
| 102 | fprintf(logfile, "RFI: cpu %d\n", cs->cpu_index); |
| 103 | hppa_cpu_dump_state(cs, logfile, 0); |
| 104 | qemu_log_unlock(logfile); |
| 105 | } |
| 106 | } |
| 107 | } |
| 108 | |
| 109 | static void getshadowregs(CPUHPPAState *env) |
| 110 | { |
| 111 | env->gr[1] = env->shadow[0]; |
| 112 | env->gr[8] = env->shadow[1]; |
| 113 | env->gr[9] = env->shadow[2]; |
| 114 | env->gr[16] = env->shadow[3]; |
| 115 | env->gr[17] = env->shadow[4]; |
| 116 | env->gr[24] = env->shadow[5]; |
| 117 | env->gr[25] = env->shadow[6]; |
| 118 | } |
| 119 | |
| 120 | void HELPER(rfi_r)(CPUHPPAState *env) |
| 121 | { |
| 122 | getshadowregs(env); |
| 123 | helper_rfi(env); |
| 124 | } |
| 125 | |
| 126 | #ifndef CONFIG_USER_ONLY |
| 127 | /* |
| 128 | * diag_console_output() is a helper function used during the initial bootup |
| 129 | * process of the SeaBIOS-hppa firmware. During the bootup phase, addresses of |
| 130 | * serial ports on e.g. PCI busses are unknown and most other devices haven't |
| 131 | * been initialized and configured yet. With help of a simple "diag" assembler |
| 132 | * instruction and an ASCII character code in register %r26 firmware can easily |
| 133 | * print debug output without any dependencies to the first serial port and use |
| 134 | * that as serial console. |
| 135 | */ |
| 136 | void HELPER(diag_console_output)(CPUHPPAState *env) |
| 137 | { |
| 138 | CharFrontend *fe; |
| 139 | Chardev *serial_port; |
| 140 | unsigned char c; |
| 141 | |
| 142 | /* find first serial port */ |
| 143 | serial_port = serial_hd(0); |
| 144 | if (!serial_port) { |
| 145 | return; |
| 146 | } |
| 147 | |
| 148 | /* get the frontend for the serial port */ |
| 149 | fe = serial_port->fe; |
| 150 | if (!fe || |
| 151 | !qemu_chr_fe_backend_connected(fe)) { |
| 152 | return; |
| 153 | } |
| 154 | |
| 155 | c = (unsigned char)env->gr[26]; |
| 156 | qemu_chr_fe_write(fe, &c, sizeof(c)); |
| 157 | } |
| 158 | #endif |