xref: /openbmc/qemu/target/ppc/timebase_helper.c (revision 78be3218)
1 /*
2  *  PowerPC 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.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 #include "qemu/osdep.h"
20 #include "cpu.h"
21 #include "hw/ppc/ppc.h"
22 #include "exec/helper-proto.h"
23 #include "exec/exec-all.h"
24 #include "qemu/log.h"
25 #include "qemu/main-loop.h"
26 
27 /*****************************************************************************/
28 /* SPR accesses */
29 
helper_load_tbl(CPUPPCState * env)30 target_ulong helper_load_tbl(CPUPPCState *env)
31 {
32     return (target_ulong)cpu_ppc_load_tbl(env);
33 }
34 
helper_load_tbu(CPUPPCState * env)35 target_ulong helper_load_tbu(CPUPPCState *env)
36 {
37     return cpu_ppc_load_tbu(env);
38 }
39 
helper_load_atbl(CPUPPCState * env)40 target_ulong helper_load_atbl(CPUPPCState *env)
41 {
42     return (target_ulong)cpu_ppc_load_atbl(env);
43 }
44 
helper_load_atbu(CPUPPCState * env)45 target_ulong helper_load_atbu(CPUPPCState *env)
46 {
47     return cpu_ppc_load_atbu(env);
48 }
49 
helper_load_vtb(CPUPPCState * env)50 target_ulong helper_load_vtb(CPUPPCState *env)
51 {
52     return cpu_ppc_load_vtb(env);
53 }
54 
55 #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
helper_load_purr(CPUPPCState * env)56 target_ulong helper_load_purr(CPUPPCState *env)
57 {
58     return (target_ulong)cpu_ppc_load_purr(env);
59 }
60 
helper_store_purr(CPUPPCState * env,target_ulong val)61 void helper_store_purr(CPUPPCState *env, target_ulong val)
62 {
63     CPUState *cs = env_cpu(env);
64     CPUState *ccs;
65 
66     if (ppc_cpu_lpar_single_threaded(cs)) {
67         cpu_ppc_store_purr(env, val);
68         return;
69     }
70 
71     THREAD_SIBLING_FOREACH(cs, ccs) {
72         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
73         cpu_ppc_store_purr(cenv, val);
74     }
75 }
76 #endif
77 
78 #if !defined(CONFIG_USER_ONLY)
helper_store_tbl(CPUPPCState * env,target_ulong val)79 void helper_store_tbl(CPUPPCState *env, target_ulong val)
80 {
81     CPUState *cs = env_cpu(env);
82     CPUState *ccs;
83 
84     if (ppc_cpu_lpar_single_threaded(cs)) {
85         cpu_ppc_store_tbl(env, val);
86         return;
87     }
88 
89     THREAD_SIBLING_FOREACH(cs, ccs) {
90         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
91         cpu_ppc_store_tbl(cenv, val);
92     }
93 }
94 
helper_store_tbu(CPUPPCState * env,target_ulong val)95 void helper_store_tbu(CPUPPCState *env, target_ulong val)
96 {
97     CPUState *cs = env_cpu(env);
98     CPUState *ccs;
99 
100     if (ppc_cpu_lpar_single_threaded(cs)) {
101         cpu_ppc_store_tbu(env, val);
102         return;
103     }
104 
105     THREAD_SIBLING_FOREACH(cs, ccs) {
106         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
107         cpu_ppc_store_tbu(cenv, val);
108     }
109 }
110 
helper_store_atbl(CPUPPCState * env,target_ulong val)111 void helper_store_atbl(CPUPPCState *env, target_ulong val)
112 {
113     cpu_ppc_store_atbl(env, val);
114 }
115 
helper_store_atbu(CPUPPCState * env,target_ulong val)116 void helper_store_atbu(CPUPPCState *env, target_ulong val)
117 {
118     cpu_ppc_store_atbu(env, val);
119 }
120 
helper_load_decr(CPUPPCState * env)121 target_ulong helper_load_decr(CPUPPCState *env)
122 {
123     return cpu_ppc_load_decr(env);
124 }
125 
helper_store_decr(CPUPPCState * env,target_ulong val)126 void helper_store_decr(CPUPPCState *env, target_ulong val)
127 {
128     cpu_ppc_store_decr(env, val);
129 }
130 
helper_load_hdecr(CPUPPCState * env)131 target_ulong helper_load_hdecr(CPUPPCState *env)
132 {
133     return cpu_ppc_load_hdecr(env);
134 }
135 
helper_store_hdecr(CPUPPCState * env,target_ulong val)136 void helper_store_hdecr(CPUPPCState *env, target_ulong val)
137 {
138     CPUState *cs = env_cpu(env);
139     CPUState *ccs;
140 
141     if (ppc_cpu_lpar_single_threaded(cs)) {
142         cpu_ppc_store_hdecr(env, val);
143         return;
144     }
145 
146     THREAD_SIBLING_FOREACH(cs, ccs) {
147         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
148         cpu_ppc_store_hdecr(cenv, val);
149     }
150 }
151 
helper_store_vtb(CPUPPCState * env,target_ulong val)152 void helper_store_vtb(CPUPPCState *env, target_ulong val)
153 {
154     CPUState *cs = env_cpu(env);
155     CPUState *ccs;
156 
157     if (ppc_cpu_lpar_single_threaded(cs)) {
158         cpu_ppc_store_vtb(env, val);
159         return;
160     }
161 
162     THREAD_SIBLING_FOREACH(cs, ccs) {
163         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
164         cpu_ppc_store_vtb(cenv, val);
165     }
166 }
167 
helper_store_tbu40(CPUPPCState * env,target_ulong val)168 void helper_store_tbu40(CPUPPCState *env, target_ulong val)
169 {
170     CPUState *cs = env_cpu(env);
171     CPUState *ccs;
172 
173     if (ppc_cpu_lpar_single_threaded(cs)) {
174         cpu_ppc_store_tbu40(env, val);
175         return;
176     }
177 
178     THREAD_SIBLING_FOREACH(cs, ccs) {
179         CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
180         cpu_ppc_store_tbu40(cenv, val);
181     }
182 }
183 
helper_load_40x_pit(CPUPPCState * env)184 target_ulong helper_load_40x_pit(CPUPPCState *env)
185 {
186     return load_40x_pit(env);
187 }
188 
helper_store_40x_pit(CPUPPCState * env,target_ulong val)189 void helper_store_40x_pit(CPUPPCState *env, target_ulong val)
190 {
191     store_40x_pit(env, val);
192 }
193 
helper_store_40x_tcr(CPUPPCState * env,target_ulong val)194 void helper_store_40x_tcr(CPUPPCState *env, target_ulong val)
195 {
196     store_40x_tcr(env, val);
197 }
198 
helper_store_40x_tsr(CPUPPCState * env,target_ulong val)199 void helper_store_40x_tsr(CPUPPCState *env, target_ulong val)
200 {
201     store_40x_tsr(env, val);
202 }
203 
helper_store_booke_tcr(CPUPPCState * env,target_ulong val)204 void helper_store_booke_tcr(CPUPPCState *env, target_ulong val)
205 {
206     store_booke_tcr(env, val);
207 }
208 
helper_store_booke_tsr(CPUPPCState * env,target_ulong val)209 void helper_store_booke_tsr(CPUPPCState *env, target_ulong val)
210 {
211     store_booke_tsr(env, val);
212 }
213 
214 #if defined(TARGET_PPC64) && !defined(CONFIG_USER_ONLY)
215 /*
216  * qemu-user breaks with pnv headers, so they go under ifdefs for now.
217  * A clean up may be to move powernv specific registers and helpers into
218  * target/ppc/pnv_helper.c
219  */
220 #include "hw/ppc/pnv_core.h"
221 #include "hw/ppc/pnv_chip.h"
222 /*
223  * POWER processor Timebase Facility
224  */
225 
226 /*
227  * The TBST is the timebase state machine, which is a per-core machine that
228  * is used to synchronize the core TB with the ChipTOD. States 3,4,5 are
229  * not used in POWER8/9/10.
230  *
231  * The state machine gets driven by writes to TFMR SPR from the core, and
232  * by signals from the ChipTOD. The state machine table for common
233  * transitions is as follows (according to hardware specs, not necessarily
234  * this implementation):
235  *
236  * | Cur            | Event                            | New |
237  * +----------------+----------------------------------+-----+
238  * | 0 RESET        | TFMR |= LOAD_TOD_MOD             | 1   |
239  * | 1 SEND_TOD_MOD | "immediate transition"           | 2   |
240  * | 2 NOT_SET      | mttbu/mttbu40/mttbl              | 2   |
241  * | 2 NOT_SET      | TFMR |= MOVE_CHIP_TOD_TO_TB      | 6   |
242  * | 6 SYNC_WAIT    | "sync pulse from ChipTOD"        | 7   |
243  * | 7 GET_TOD      | ChipTOD xscom MOVE_TOD_TO_TB_REG | 8   |
244  * | 8 TB_RUNNING   | mttbu/mttbu40                    | 8   |
245  * | 8 TB_RUNNING   | TFMR |= LOAD_TOD_MOD             | 1   |
246  * | 8 TB_RUNNING   | mttbl                            | 9   |
247  * | 9 TB_ERROR     | TFMR |= CLEAR_TB_ERRORS          | 0   |
248  *
249  * - LOAD_TOD_MOD will also move states 2,6 to state 1, omitted from table
250  *   because it's not a typical init flow.
251  *
252  * - The ERROR state can be entered from most/all other states on invalid
253  *   states (e.g., if some TFMR control bit is set from a state where it's
254  *   not listed to cause a transition away from), omitted to avoid clutter.
255  *
256  * Note: mttbl causes a timebase error because this inevitably causes
257  * ticks to be lost and TB to become unsynchronized, whereas TB can be
258  * adjusted using mttbu* without losing ticks. mttbl behaviour is not
259  * modelled.
260  *
261  * Note: the TB state machine does not actually cause any real TB adjustment!
262  * TB starts out synchronized across all vCPUs (hardware threads) in
263  * QMEU, so for now the purpose of the TBST and ChipTOD model is simply
264  * to step through firmware initialisation sequences.
265  */
tfmr_get_tb_state(uint64_t tfmr)266 static unsigned int tfmr_get_tb_state(uint64_t tfmr)
267 {
268     return (tfmr & TFMR_TBST_ENCODED) >> (63 - 31);
269 }
270 
tfmr_new_tb_state(uint64_t tfmr,unsigned int tbst)271 static uint64_t tfmr_new_tb_state(uint64_t tfmr, unsigned int tbst)
272 {
273     tfmr &= ~TFMR_TBST_LAST;
274     tfmr |= (tfmr & TFMR_TBST_ENCODED) >> 4; /* move state to last state */
275     tfmr &= ~TFMR_TBST_ENCODED;
276     tfmr |= (uint64_t)tbst << (63 - 31); /* move new state to state */
277 
278     if (tbst == TBST_TB_RUNNING) {
279         tfmr |= TFMR_TB_VALID;
280     } else {
281         tfmr &= ~TFMR_TB_VALID;
282     }
283 
284     return tfmr;
285 }
286 
write_tfmr(CPUPPCState * env,target_ulong val)287 static void write_tfmr(CPUPPCState *env, target_ulong val)
288 {
289     CPUState *cs = env_cpu(env);
290 
291     if (ppc_cpu_core_single_threaded(cs)) {
292         env->spr[SPR_TFMR] = val;
293     } else {
294         CPUState *ccs;
295         THREAD_SIBLING_FOREACH(cs, ccs) {
296             CPUPPCState *cenv = &POWERPC_CPU(ccs)->env;
297             cenv->spr[SPR_TFMR] = val;
298         }
299     }
300 }
301 
cpu_get_tbst(PowerPCCPU * cpu)302 static PnvCoreTODState *cpu_get_tbst(PowerPCCPU *cpu)
303 {
304     PnvCore *pc = pnv_cpu_state(cpu)->pnv_core;
305 
306     if (pc->big_core && pc->tod_state.big_core_quirk) {
307         /* Must operate on the even small core */
308         int core_id = CPU_CORE(pc)->core_id;
309         if (core_id & 1) {
310             pc = pc->chip->cores[core_id & ~1];
311         }
312     }
313 
314     return &pc->tod_state;
315 }
316 
tb_state_machine_step(CPUPPCState * env)317 static void tb_state_machine_step(CPUPPCState *env)
318 {
319     PowerPCCPU *cpu = env_archcpu(env);
320     PnvCoreTODState *tod_state = cpu_get_tbst(cpu);
321     uint64_t tfmr = env->spr[SPR_TFMR];
322     unsigned int tbst = tfmr_get_tb_state(tfmr);
323 
324     if (!(tfmr & TFMR_TB_ECLIPZ) || tbst == TBST_TB_ERROR) {
325         return;
326     }
327 
328     if (tod_state->tb_sync_pulse_timer) {
329         tod_state->tb_sync_pulse_timer--;
330     } else {
331         tfmr |= TFMR_TB_SYNC_OCCURED;
332         write_tfmr(env, tfmr);
333     }
334 
335     if (tod_state->tb_state_timer) {
336         tod_state->tb_state_timer--;
337         return;
338     }
339 
340     if (tfmr & TFMR_LOAD_TOD_MOD) {
341         tfmr &= ~TFMR_LOAD_TOD_MOD;
342         if (tbst == TBST_GET_TOD) {
343             tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
344             tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
345         } else {
346             tfmr = tfmr_new_tb_state(tfmr, TBST_SEND_TOD_MOD);
347             /* State seems to transition immediately */
348             tfmr = tfmr_new_tb_state(tfmr, TBST_NOT_SET);
349         }
350     } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) {
351         if (tbst == TBST_SYNC_WAIT) {
352             tfmr = tfmr_new_tb_state(tfmr, TBST_GET_TOD);
353             tod_state->tb_state_timer = 3;
354         } else if (tbst == TBST_GET_TOD) {
355             if (tod_state->tod_sent_to_tb) {
356                 tfmr = tfmr_new_tb_state(tfmr, TBST_TB_RUNNING);
357                 tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB;
358                 tod_state->tb_ready_for_tod = 0;
359                 tod_state->tod_sent_to_tb = 0;
360             }
361         } else {
362             qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB "
363                           "state machine in invalid state 0x%x\n", tbst);
364             tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
365             tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
366             tod_state->tb_ready_for_tod = 0;
367         }
368     }
369 
370     write_tfmr(env, tfmr);
371 }
372 
helper_load_tfmr(CPUPPCState * env)373 target_ulong helper_load_tfmr(CPUPPCState *env)
374 {
375     tb_state_machine_step(env);
376 
377     return env->spr[SPR_TFMR] | TFMR_TB_ECLIPZ;
378 }
379 
helper_store_tfmr(CPUPPCState * env,target_ulong val)380 void helper_store_tfmr(CPUPPCState *env, target_ulong val)
381 {
382     PowerPCCPU *cpu = env_archcpu(env);
383     PnvCoreTODState *tod_state = cpu_get_tbst(cpu);
384     uint64_t tfmr = env->spr[SPR_TFMR];
385     uint64_t clear_on_write;
386     unsigned int tbst = tfmr_get_tb_state(tfmr);
387 
388     if (!(val & TFMR_TB_ECLIPZ)) {
389         qemu_log_mask(LOG_UNIMP, "TFMR non-ECLIPZ mode not implemented\n");
390         tfmr &= ~TFMR_TBST_ENCODED;
391         tfmr &= ~TFMR_TBST_LAST;
392         goto out;
393     }
394 
395     /* Update control bits */
396     tfmr = (tfmr & ~TFMR_CONTROL_MASK) | (val & TFMR_CONTROL_MASK);
397 
398     /* Several bits are clear-on-write, only one is implemented so far */
399     clear_on_write = val & TFMR_FIRMWARE_CONTROL_ERROR;
400     tfmr &= ~clear_on_write;
401 
402     /*
403      * mtspr always clears this. The sync pulse timer makes it come back
404      * after the second mfspr.
405      */
406     tfmr &= ~TFMR_TB_SYNC_OCCURED;
407     tod_state->tb_sync_pulse_timer = 1;
408 
409     if (((tfmr | val) & (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) ==
410                         (TFMR_LOAD_TOD_MOD | TFMR_MOVE_CHIP_TOD_TO_TB)) {
411         qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: LOAD_TOD_MOD and "
412                                        "MOVE_CHIP_TOD_TO_TB both set\n");
413         tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
414         tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
415         tod_state->tb_ready_for_tod = 0;
416         goto out;
417     }
418 
419     if (tfmr & TFMR_CLEAR_TB_ERRORS) {
420         /*
421          * Workbook says TFMR_CLEAR_TB_ERRORS should be written twice.
422          * This is not simulated/required here.
423          */
424         tfmr = tfmr_new_tb_state(tfmr, TBST_RESET);
425         tfmr &= ~TFMR_CLEAR_TB_ERRORS;
426         tfmr &= ~TFMR_LOAD_TOD_MOD;
427         tfmr &= ~TFMR_MOVE_CHIP_TOD_TO_TB;
428         tfmr &= ~TFMR_FIRMWARE_CONTROL_ERROR; /* XXX: should this be cleared? */
429         tod_state->tb_ready_for_tod = 0;
430         tod_state->tod_sent_to_tb = 0;
431         goto out;
432     }
433 
434     if (tbst == TBST_TB_ERROR) {
435         qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: mtspr TFMR in TB_ERROR"
436                                        " state\n");
437         tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
438         return;
439     }
440 
441     if (tfmr & TFMR_LOAD_TOD_MOD) {
442         /* Wait for an arbitrary 3 mfspr until the next state transition. */
443         tod_state->tb_state_timer = 3;
444     } else if (tfmr & TFMR_MOVE_CHIP_TOD_TO_TB) {
445         if (tbst == TBST_NOT_SET) {
446             tfmr = tfmr_new_tb_state(tfmr, TBST_SYNC_WAIT);
447             tod_state->tb_ready_for_tod = 1;
448             tod_state->tb_state_timer = 3; /* arbitrary */
449         } else {
450             qemu_log_mask(LOG_GUEST_ERROR, "TFMR error: MOVE_CHIP_TOD_TO_TB "
451                                            "not in TB not set state 0x%x\n",
452                                            tbst);
453             tfmr = tfmr_new_tb_state(tfmr, TBST_TB_ERROR);
454             tfmr |= TFMR_FIRMWARE_CONTROL_ERROR;
455             tod_state->tb_ready_for_tod = 0;
456         }
457     }
458 
459 out:
460     write_tfmr(env, tfmr);
461 }
462 #endif
463 
464 /*****************************************************************************/
465 /* Embedded PowerPC specific helpers */
466 
467 /* XXX: to be improved to check access rights when in user-mode */
helper_load_dcr(CPUPPCState * env,target_ulong dcrn)468 target_ulong helper_load_dcr(CPUPPCState *env, target_ulong dcrn)
469 {
470     uint32_t val = 0;
471 
472     if (unlikely(env->dcr_env == NULL)) {
473         qemu_log_mask(LOG_GUEST_ERROR, "No DCR environment\n");
474         raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
475                                POWERPC_EXCP_INVAL |
476                                POWERPC_EXCP_INVAL_INVAL, GETPC());
477     } else {
478         int ret;
479 
480         bql_lock();
481         ret = ppc_dcr_read(env->dcr_env, (uint32_t)dcrn, &val);
482         bql_unlock();
483         if (unlikely(ret != 0)) {
484             qemu_log_mask(LOG_GUEST_ERROR, "DCR read error %d %03x\n",
485                           (uint32_t)dcrn, (uint32_t)dcrn);
486             raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
487                                    POWERPC_EXCP_INVAL |
488                                    POWERPC_EXCP_INVAL_INVAL, GETPC());
489         }
490     }
491     return val;
492 }
493 
helper_store_dcr(CPUPPCState * env,target_ulong dcrn,target_ulong val)494 void helper_store_dcr(CPUPPCState *env, target_ulong dcrn, target_ulong val)
495 {
496     if (unlikely(env->dcr_env == NULL)) {
497         qemu_log_mask(LOG_GUEST_ERROR, "No DCR environment\n");
498         raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
499                                POWERPC_EXCP_INVAL |
500                                POWERPC_EXCP_INVAL_INVAL, GETPC());
501     } else {
502         int ret;
503         bql_lock();
504         ret = ppc_dcr_write(env->dcr_env, (uint32_t)dcrn, (uint32_t)val);
505         bql_unlock();
506         if (unlikely(ret != 0)) {
507             qemu_log_mask(LOG_GUEST_ERROR, "DCR write error %d %03x\n",
508                           (uint32_t)dcrn, (uint32_t)dcrn);
509             raise_exception_err_ra(env, POWERPC_EXCP_PROGRAM,
510                                    POWERPC_EXCP_INVAL |
511                                    POWERPC_EXCP_INVAL_INVAL, GETPC());
512         }
513     }
514 }
515 #endif
516