1 | /* |
2 | * QEMU Sun4u/Sun4v System Emulator common routines |
3 | * |
4 | * Copyright (c) 2005 Fabrice Bellard |
5 | * |
6 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
7 | * of this software and associated documentation files (the "Software"), to deal |
8 | * in the Software without restriction, including without limitation the rights |
9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
10 | * copies of the Software, and to permit persons to whom the Software is |
11 | * furnished to do so, subject to the following conditions: |
12 | * |
13 | * The above copyright notice and this permission notice shall be included in |
14 | * all copies or substantial portions of the Software. |
15 | * |
16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
19 | * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN |
22 | * THE SOFTWARE. |
23 | */ |
24 | |
25 | |
26 | #include "qemu/osdep.h" |
27 | #include "cpu.h" |
28 | #include "hw/char/serial.h" |
29 | #include "hw/sparc/sparc64.h" |
30 | #include "qemu/timer.h" |
31 | #include "sysemu/reset.h" |
32 | #include "trace.h" |
33 | |
34 | |
35 | #define TICK_MAX 0x7fffffffffffffffULL |
36 | |
37 | void cpu_check_irqs(CPUSPARCState *env) |
38 | { |
39 | CPUState *cs; |
40 | uint32_t pil = env->pil_in | |
41 | (env->softint & ~(SOFTINT_TIMER | SOFTINT_STIMER)); |
42 | |
43 | /* We should be holding the BQL before we mess with IRQs */ |
44 | g_assert(qemu_mutex_iothread_locked()); |
45 | |
46 | /* TT_IVEC has a higher priority (16) than TT_EXTINT (31..17) */ |
47 | if (env->ivec_status & 0x20) { |
48 | return; |
49 | } |
50 | cs = env_cpu(env); |
51 | /* check if TM or SM in SOFTINT are set |
52 | setting these also causes interrupt 14 */ |
53 | if (env->softint & (SOFTINT_TIMER | SOFTINT_STIMER)) { |
54 | pil |= 1 << 14; |
55 | } |
56 | |
57 | /* The bit corresponding to psrpil is (1<< psrpil), the next bit |
58 | is (2 << psrpil). */ |
59 | if (pil < (2 << env->psrpil)) { |
60 | if (cs->interrupt_request & CPU_INTERRUPT_HARD) { |
61 | trace_sparc64_cpu_check_irqs_reset_irq(env->interrupt_index); |
62 | env->interrupt_index = 0; |
63 | cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); |
64 | } |
65 | return; |
66 | } |
67 | |
68 | if (cpu_interrupts_enabled(env)) { |
69 | |
70 | unsigned int i; |
71 | |
72 | for (i = 15; i > env->psrpil; i--) { |
73 | if (pil & (1 << i)) { |
74 | int old_interrupt = env->interrupt_index; |
75 | int new_interrupt = TT_EXTINT | i; |
76 | |
77 | if (unlikely(env->tl > 0 && cpu_tsptr(env)->tt > new_interrupt |
78 | && ((cpu_tsptr(env)->tt & 0x1f0) == TT_EXTINT))) { |
79 | trace_sparc64_cpu_check_irqs_noset_irq(env->tl, |
80 | cpu_tsptr(env)->tt, |
81 | new_interrupt); |
82 | } else if (old_interrupt != new_interrupt) { |
83 | env->interrupt_index = new_interrupt; |
84 | trace_sparc64_cpu_check_irqs_set_irq(i, old_interrupt, |
85 | new_interrupt); |
86 | cpu_interrupt(cs, CPU_INTERRUPT_HARD); |
87 | } |
88 | break; |
89 | } |
90 | } |
91 | } else if (cs->interrupt_request & CPU_INTERRUPT_HARD) { |
92 | trace_sparc64_cpu_check_irqs_disabled(pil, env->pil_in, env->softint, |
93 | env->interrupt_index); |
94 | env->interrupt_index = 0; |
95 | cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); |
96 | } |
97 | } |
98 | |
99 | static void cpu_kick_irq(SPARCCPU *cpu) |
100 | { |
101 | CPUState *cs = CPU(cpu); |
102 | CPUSPARCState *env = &cpu->env; |
103 | |
104 | cs->halted = 0; |
105 | cpu_check_irqs(env); |
106 | qemu_cpu_kick(cs); |
107 | } |
108 | |
109 | void sparc64_cpu_set_ivec_irq(void *opaque, int irq, int level) |
110 | { |
111 | SPARCCPU *cpu = opaque; |
112 | CPUSPARCState *env = &cpu->env; |
113 | CPUState *cs; |
114 | |
115 | if (level) { |
116 | if (!(env->ivec_status & 0x20)) { |
117 | trace_sparc64_cpu_ivec_raise_irq(irq); |
118 | cs = CPU(cpu); |
119 | cs->halted = 0; |
120 | env->interrupt_index = TT_IVEC; |
121 | env->ivec_status |= 0x20; |
122 | env->ivec_data[0] = (0x1f << 6) | irq; |
123 | env->ivec_data[1] = 0; |
124 | env->ivec_data[2] = 0; |
125 | cpu_interrupt(cs, CPU_INTERRUPT_HARD); |
126 | } |
127 | } else { |
128 | if (env->ivec_status & 0x20) { |
129 | trace_sparc64_cpu_ivec_lower_irq(irq); |
130 | cs = CPU(cpu); |
131 | env->ivec_status &= ~0x20; |
132 | cpu_reset_interrupt(cs, CPU_INTERRUPT_HARD); |
133 | } |
134 | } |
135 | } |
136 | |
137 | typedef struct ResetData { |
138 | SPARCCPU *cpu; |
139 | uint64_t prom_addr; |
140 | } ResetData; |
141 | |
142 | static CPUTimer *cpu_timer_create(const char *name, SPARCCPU *cpu, |
143 | QEMUBHFunc *cb, uint32_t frequency, |
144 | uint64_t disabled_mask, uint64_t npt_mask) |
145 | { |
146 | CPUTimer *timer = g_malloc0(sizeof(CPUTimer)); |
147 | |
148 | timer->name = name; |
149 | timer->frequency = frequency; |
150 | timer->disabled_mask = disabled_mask; |
151 | timer->npt_mask = npt_mask; |
152 | |
153 | timer->disabled = 1; |
154 | timer->npt = 1; |
155 | timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
156 | |
157 | timer->qtimer = timer_new_ns(QEMU_CLOCK_VIRTUAL, cb, cpu); |
158 | |
159 | return timer; |
160 | } |
161 | |
162 | static void cpu_timer_reset(CPUTimer *timer) |
163 | { |
164 | timer->disabled = 1; |
165 | timer->clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
166 | |
167 | timer_del(timer->qtimer); |
168 | } |
169 | |
170 | static void main_cpu_reset(void *opaque) |
171 | { |
172 | ResetData *s = (ResetData *)opaque; |
173 | CPUSPARCState *env = &s->cpu->env; |
174 | static unsigned int nr_resets; |
175 | |
176 | cpu_reset(CPU(s->cpu)); |
177 | |
178 | cpu_timer_reset(env->tick); |
179 | cpu_timer_reset(env->stick); |
180 | cpu_timer_reset(env->hstick); |
181 | |
182 | env->gregs[1] = 0; /* Memory start */ |
183 | env->gregs[2] = ram_size; /* Memory size */ |
184 | env->gregs[3] = 0; /* Machine description XXX */ |
185 | if (nr_resets++ == 0) { |
186 | /* Power on reset */ |
187 | env->pc = s->prom_addr + 0x20ULL; |
188 | } else { |
189 | env->pc = s->prom_addr + 0x40ULL; |
190 | } |
191 | env->npc = env->pc + 4; |
192 | } |
193 | |
194 | static void tick_irq(void *opaque) |
195 | { |
196 | SPARCCPU *cpu = opaque; |
197 | CPUSPARCState *env = &cpu->env; |
198 | |
199 | CPUTimer *timer = env->tick; |
200 | |
201 | if (timer->disabled) { |
202 | trace_sparc64_cpu_tick_irq_disabled(); |
203 | return; |
204 | } else { |
205 | trace_sparc64_cpu_tick_irq_fire(); |
206 | } |
207 | |
208 | env->softint |= SOFTINT_TIMER; |
209 | cpu_kick_irq(cpu); |
210 | } |
211 | |
212 | static void stick_irq(void *opaque) |
213 | { |
214 | SPARCCPU *cpu = opaque; |
215 | CPUSPARCState *env = &cpu->env; |
216 | |
217 | CPUTimer *timer = env->stick; |
218 | |
219 | if (timer->disabled) { |
220 | trace_sparc64_cpu_stick_irq_disabled(); |
221 | return; |
222 | } else { |
223 | trace_sparc64_cpu_stick_irq_fire(); |
224 | } |
225 | |
226 | env->softint |= SOFTINT_STIMER; |
227 | cpu_kick_irq(cpu); |
228 | } |
229 | |
230 | static void hstick_irq(void *opaque) |
231 | { |
232 | SPARCCPU *cpu = opaque; |
233 | CPUSPARCState *env = &cpu->env; |
234 | |
235 | CPUTimer *timer = env->hstick; |
236 | |
237 | if (timer->disabled) { |
238 | trace_sparc64_cpu_hstick_irq_disabled(); |
239 | return; |
240 | } else { |
241 | trace_sparc64_cpu_hstick_irq_fire(); |
242 | } |
243 | |
244 | env->softint |= SOFTINT_STIMER; |
245 | cpu_kick_irq(cpu); |
246 | } |
247 | |
248 | static int64_t cpu_to_timer_ticks(int64_t cpu_ticks, uint32_t frequency) |
249 | { |
250 | return muldiv64(cpu_ticks, NANOSECONDS_PER_SECOND, frequency); |
251 | } |
252 | |
253 | static uint64_t timer_to_cpu_ticks(int64_t timer_ticks, uint32_t frequency) |
254 | { |
255 | return muldiv64(timer_ticks, frequency, NANOSECONDS_PER_SECOND); |
256 | } |
257 | |
258 | void cpu_tick_set_count(CPUTimer *timer, uint64_t count) |
259 | { |
260 | uint64_t real_count = count & ~timer->npt_mask; |
261 | uint64_t npt_bit = count & timer->npt_mask; |
262 | |
263 | int64_t vm_clock_offset = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - |
264 | cpu_to_timer_ticks(real_count, timer->frequency); |
265 | |
266 | trace_sparc64_cpu_tick_set_count(timer->name, real_count, |
267 | timer->npt ? "disabled" : "enabled" , |
268 | timer); |
269 | |
270 | timer->npt = npt_bit ? 1 : 0; |
271 | timer->clock_offset = vm_clock_offset; |
272 | } |
273 | |
274 | uint64_t cpu_tick_get_count(CPUTimer *timer) |
275 | { |
276 | uint64_t real_count = timer_to_cpu_ticks( |
277 | qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) - timer->clock_offset, |
278 | timer->frequency); |
279 | |
280 | trace_sparc64_cpu_tick_get_count(timer->name, real_count, |
281 | timer->npt ? "disabled" : "enabled" , |
282 | timer); |
283 | |
284 | if (timer->npt) { |
285 | real_count |= timer->npt_mask; |
286 | } |
287 | |
288 | return real_count; |
289 | } |
290 | |
291 | void cpu_tick_set_limit(CPUTimer *timer, uint64_t limit) |
292 | { |
293 | int64_t now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL); |
294 | |
295 | uint64_t real_limit = limit & ~timer->disabled_mask; |
296 | timer->disabled = (limit & timer->disabled_mask) ? 1 : 0; |
297 | |
298 | int64_t expires = cpu_to_timer_ticks(real_limit, timer->frequency) + |
299 | timer->clock_offset; |
300 | |
301 | if (expires < now) { |
302 | expires = now + 1; |
303 | } |
304 | |
305 | trace_sparc64_cpu_tick_set_limit(timer->name, real_limit, |
306 | timer->disabled ? "disabled" : "enabled" , |
307 | timer, limit, |
308 | timer_to_cpu_ticks( |
309 | now - timer->clock_offset, |
310 | timer->frequency |
311 | ), |
312 | timer_to_cpu_ticks( |
313 | expires - now, timer->frequency |
314 | )); |
315 | |
316 | if (!real_limit) { |
317 | trace_sparc64_cpu_tick_set_limit_zero(timer->name); |
318 | timer_del(timer->qtimer); |
319 | } else if (timer->disabled) { |
320 | timer_del(timer->qtimer); |
321 | } else { |
322 | timer_mod(timer->qtimer, expires); |
323 | } |
324 | } |
325 | |
326 | SPARCCPU *sparc64_cpu_devinit(const char *cpu_type, uint64_t prom_addr) |
327 | { |
328 | SPARCCPU *cpu; |
329 | CPUSPARCState *env; |
330 | ResetData *reset_info; |
331 | |
332 | uint32_t tick_frequency = 100 * 1000000; |
333 | uint32_t stick_frequency = 100 * 1000000; |
334 | uint32_t hstick_frequency = 100 * 1000000; |
335 | |
336 | cpu = SPARC_CPU(cpu_create(cpu_type)); |
337 | qdev_init_gpio_in_named(DEVICE(cpu), sparc64_cpu_set_ivec_irq, |
338 | "ivec-irq" , IVEC_MAX); |
339 | env = &cpu->env; |
340 | |
341 | env->tick = cpu_timer_create("tick" , cpu, tick_irq, |
342 | tick_frequency, TICK_INT_DIS, |
343 | TICK_NPT_MASK); |
344 | |
345 | env->stick = cpu_timer_create("stick" , cpu, stick_irq, |
346 | stick_frequency, TICK_INT_DIS, |
347 | TICK_NPT_MASK); |
348 | |
349 | env->hstick = cpu_timer_create("hstick" , cpu, hstick_irq, |
350 | hstick_frequency, TICK_INT_DIS, |
351 | TICK_NPT_MASK); |
352 | |
353 | reset_info = g_malloc0(sizeof(ResetData)); |
354 | reset_info->cpu = cpu; |
355 | reset_info->prom_addr = prom_addr; |
356 | qemu_register_reset(main_cpu_reset, reset_info); |
357 | |
358 | return cpu; |
359 | } |
360 | |