]>
Commit | Line | Data |
---|---|---|
ad71ed68 BS |
1 | /* |
2 | * PowerPC exception emulation helpers for QEMU. | |
3 | * | |
4 | * Copyright (c) 2003-2007 Jocelyn Mayer | |
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 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 | #include "cpu.h" | |
ad71ed68 BS |
20 | #include "helper.h" |
21 | ||
22 | #include "helper_regs.h" | |
23 | ||
24 | //#define DEBUG_OP | |
25 | //#define DEBUG_EXCEPTIONS | |
26 | ||
27 | /*****************************************************************************/ | |
28 | /* Exceptions processing helpers */ | |
29 | ||
e5f17ac6 BS |
30 | void helper_raise_exception_err(CPUPPCState *env, uint32_t exception, |
31 | uint32_t error_code) | |
ad71ed68 BS |
32 | { |
33 | #if 0 | |
34 | printf("Raise exception %3x code : %d\n", exception, error_code); | |
35 | #endif | |
36 | env->exception_index = exception; | |
37 | env->error_code = error_code; | |
38 | cpu_loop_exit(env); | |
39 | } | |
40 | ||
e5f17ac6 | 41 | void helper_raise_exception(CPUPPCState *env, uint32_t exception) |
ad71ed68 | 42 | { |
e5f17ac6 | 43 | helper_raise_exception_err(env, exception, 0); |
ad71ed68 BS |
44 | } |
45 | ||
46 | #if !defined(CONFIG_USER_ONLY) | |
e5f17ac6 | 47 | void helper_store_msr(CPUPPCState *env, target_ulong val) |
ad71ed68 BS |
48 | { |
49 | val = hreg_store_msr(env, val, 0); | |
50 | if (val != 0) { | |
51 | env->interrupt_request |= CPU_INTERRUPT_EXITTB; | |
e5f17ac6 | 52 | helper_raise_exception(env, val); |
ad71ed68 BS |
53 | } |
54 | } | |
55 | ||
e5f17ac6 | 56 | static inline void do_rfi(CPUPPCState *env, target_ulong nip, target_ulong msr, |
ad71ed68 BS |
57 | target_ulong msrm, int keep_msrh) |
58 | { | |
59 | #if defined(TARGET_PPC64) | |
60 | if (msr & (1ULL << MSR_SF)) { | |
61 | nip = (uint64_t)nip; | |
62 | msr &= (uint64_t)msrm; | |
63 | } else { | |
64 | nip = (uint32_t)nip; | |
65 | msr = (uint32_t)(msr & msrm); | |
66 | if (keep_msrh) { | |
67 | msr |= env->msr & ~((uint64_t)0xFFFFFFFF); | |
68 | } | |
69 | } | |
70 | #else | |
71 | nip = (uint32_t)nip; | |
72 | msr &= (uint32_t)msrm; | |
73 | #endif | |
74 | /* XXX: beware: this is false if VLE is supported */ | |
75 | env->nip = nip & ~((target_ulong)0x00000003); | |
76 | hreg_store_msr(env, msr, 1); | |
77 | #if defined(DEBUG_OP) | |
78 | cpu_dump_rfi(env->nip, env->msr); | |
79 | #endif | |
80 | /* No need to raise an exception here, | |
81 | * as rfi is always the last insn of a TB | |
82 | */ | |
83 | env->interrupt_request |= CPU_INTERRUPT_EXITTB; | |
84 | } | |
85 | ||
e5f17ac6 | 86 | void helper_rfi(CPUPPCState *env) |
ad71ed68 | 87 | { |
e5f17ac6 | 88 | do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], |
ad71ed68 BS |
89 | ~((target_ulong)0x783F0000), 1); |
90 | } | |
91 | ||
92 | #if defined(TARGET_PPC64) | |
e5f17ac6 | 93 | void helper_rfid(CPUPPCState *env) |
ad71ed68 | 94 | { |
e5f17ac6 | 95 | do_rfi(env, env->spr[SPR_SRR0], env->spr[SPR_SRR1], |
ad71ed68 BS |
96 | ~((target_ulong)0x783F0000), 0); |
97 | } | |
98 | ||
e5f17ac6 | 99 | void helper_hrfid(CPUPPCState *env) |
ad71ed68 | 100 | { |
e5f17ac6 | 101 | do_rfi(env, env->spr[SPR_HSRR0], env->spr[SPR_HSRR1], |
ad71ed68 BS |
102 | ~((target_ulong)0x783F0000), 0); |
103 | } | |
104 | #endif | |
105 | ||
106 | /*****************************************************************************/ | |
107 | /* Embedded PowerPC specific helpers */ | |
e5f17ac6 | 108 | void helper_40x_rfci(CPUPPCState *env) |
ad71ed68 | 109 | { |
e5f17ac6 | 110 | do_rfi(env, env->spr[SPR_40x_SRR2], env->spr[SPR_40x_SRR3], |
ad71ed68 BS |
111 | ~((target_ulong)0xFFFF0000), 0); |
112 | } | |
113 | ||
e5f17ac6 | 114 | void helper_rfci(CPUPPCState *env) |
ad71ed68 | 115 | { |
e5f17ac6 | 116 | do_rfi(env, env->spr[SPR_BOOKE_CSRR0], SPR_BOOKE_CSRR1, |
ad71ed68 BS |
117 | ~((target_ulong)0x3FFF0000), 0); |
118 | } | |
119 | ||
e5f17ac6 | 120 | void helper_rfdi(CPUPPCState *env) |
ad71ed68 | 121 | { |
e5f17ac6 | 122 | do_rfi(env, env->spr[SPR_BOOKE_DSRR0], SPR_BOOKE_DSRR1, |
ad71ed68 BS |
123 | ~((target_ulong)0x3FFF0000), 0); |
124 | } | |
125 | ||
e5f17ac6 | 126 | void helper_rfmci(CPUPPCState *env) |
ad71ed68 | 127 | { |
e5f17ac6 | 128 | do_rfi(env, env->spr[SPR_BOOKE_MCSRR0], SPR_BOOKE_MCSRR1, |
ad71ed68 BS |
129 | ~((target_ulong)0x3FFF0000), 0); |
130 | } | |
131 | #endif | |
132 | ||
e5f17ac6 BS |
133 | void helper_tw(CPUPPCState *env, target_ulong arg1, target_ulong arg2, |
134 | uint32_t flags) | |
ad71ed68 BS |
135 | { |
136 | if (!likely(!(((int32_t)arg1 < (int32_t)arg2 && (flags & 0x10)) || | |
137 | ((int32_t)arg1 > (int32_t)arg2 && (flags & 0x08)) || | |
138 | ((int32_t)arg1 == (int32_t)arg2 && (flags & 0x04)) || | |
139 | ((uint32_t)arg1 < (uint32_t)arg2 && (flags & 0x02)) || | |
140 | ((uint32_t)arg1 > (uint32_t)arg2 && (flags & 0x01))))) { | |
e5f17ac6 BS |
141 | helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, |
142 | POWERPC_EXCP_TRAP); | |
ad71ed68 BS |
143 | } |
144 | } | |
145 | ||
146 | #if defined(TARGET_PPC64) | |
e5f17ac6 BS |
147 | void helper_td(CPUPPCState *env, target_ulong arg1, target_ulong arg2, |
148 | uint32_t flags) | |
ad71ed68 BS |
149 | { |
150 | if (!likely(!(((int64_t)arg1 < (int64_t)arg2 && (flags & 0x10)) || | |
151 | ((int64_t)arg1 > (int64_t)arg2 && (flags & 0x08)) || | |
152 | ((int64_t)arg1 == (int64_t)arg2 && (flags & 0x04)) || | |
153 | ((uint64_t)arg1 < (uint64_t)arg2 && (flags & 0x02)) || | |
154 | ((uint64_t)arg1 > (uint64_t)arg2 && (flags & 0x01))))) { | |
e5f17ac6 BS |
155 | helper_raise_exception_err(env, POWERPC_EXCP_PROGRAM, |
156 | POWERPC_EXCP_TRAP); | |
ad71ed68 BS |
157 | } |
158 | } | |
159 | #endif | |
160 | ||
161 | #if !defined(CONFIG_USER_ONLY) | |
162 | /*****************************************************************************/ | |
163 | /* PowerPC 601 specific instructions (POWER bridge) */ | |
164 | ||
e5f17ac6 | 165 | void helper_rfsvc(CPUPPCState *env) |
ad71ed68 | 166 | { |
e5f17ac6 | 167 | do_rfi(env, env->lr, env->ctr, 0x0000FFFF, 0); |
ad71ed68 BS |
168 | } |
169 | ||
170 | /* Embedded.Processor Control */ | |
171 | static int dbell2irq(target_ulong rb) | |
172 | { | |
173 | int msg = rb & DBELL_TYPE_MASK; | |
174 | int irq = -1; | |
175 | ||
176 | switch (msg) { | |
177 | case DBELL_TYPE_DBELL: | |
178 | irq = PPC_INTERRUPT_DOORBELL; | |
179 | break; | |
180 | case DBELL_TYPE_DBELL_CRIT: | |
181 | irq = PPC_INTERRUPT_CDOORBELL; | |
182 | break; | |
183 | case DBELL_TYPE_G_DBELL: | |
184 | case DBELL_TYPE_G_DBELL_CRIT: | |
185 | case DBELL_TYPE_G_DBELL_MC: | |
186 | /* XXX implement */ | |
187 | default: | |
188 | break; | |
189 | } | |
190 | ||
191 | return irq; | |
192 | } | |
193 | ||
e5f17ac6 | 194 | void helper_msgclr(CPUPPCState *env, target_ulong rb) |
ad71ed68 BS |
195 | { |
196 | int irq = dbell2irq(rb); | |
197 | ||
198 | if (irq < 0) { | |
199 | return; | |
200 | } | |
201 | ||
202 | env->pending_interrupts &= ~(1 << irq); | |
203 | } | |
204 | ||
205 | void helper_msgsnd(target_ulong rb) | |
206 | { | |
207 | int irq = dbell2irq(rb); | |
208 | int pir = rb & DBELL_PIRTAG_MASK; | |
209 | CPUPPCState *cenv; | |
210 | ||
211 | if (irq < 0) { | |
212 | return; | |
213 | } | |
214 | ||
215 | for (cenv = first_cpu; cenv != NULL; cenv = cenv->next_cpu) { | |
216 | if ((rb & DBELL_BRDCAST) || (cenv->spr[SPR_BOOKE_PIR] == pir)) { | |
217 | cenv->pending_interrupts |= 1 << irq; | |
218 | cpu_interrupt(cenv, CPU_INTERRUPT_HARD); | |
219 | } | |
220 | } | |
221 | } | |
222 | #endif |