Revision 9fddaa0c hw/ppc.c

b/hw/ppc.c
28 28
		    const char *kernel_filename, const char *kernel_cmdline,
29 29
		    const char *initrd_filename);
30 30

  
31
/*****************************************************************************/
32
/* PPC time base and decrementer emulation */
33
//#define DEBUG_TB
34

  
35
struct ppc_tb_t {
36
    /* Time base management */
37
    int64_t  tb_offset;    /* Compensation               */
38
    uint32_t tb_freq;      /* TB frequency               */
39
    /* Decrementer management */
40
    uint64_t decr_next;    /* Tick for next decr interrupt  */
41
    struct QEMUTimer *decr_timer;
42
};
43

  
44
static inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env)
45
{
46
    /* TB time in tb periods */
47
    return muldiv64(qemu_get_clock(vm_clock) + tb_env->tb_offset,
48
		    tb_env->tb_freq, ticks_per_sec);
49
}
50

  
51
uint32_t cpu_ppc_load_tbl (CPUState *env)
52
{
53
    ppc_tb_t *tb_env = env->tb_env;
54
    uint64_t tb;
55

  
56
    tb = cpu_ppc_get_tb(tb_env);
57
#ifdef DEBUG_TB
58
    {
59
         static int last_time;
60
	 int now;
61
	 now = time(NULL);
62
	 if (last_time != now) {
63
	     last_time = now;
64
	     printf("%s: tb=0x%016lx %d %08lx\n",
65
                    __func__, tb, now, tb_env->tb_offset);
66
	 }
67
    }
68
#endif
69

  
70
    return tb & 0xFFFFFFFF;
71
}
72

  
73
uint32_t cpu_ppc_load_tbu (CPUState *env)
74
{
75
    ppc_tb_t *tb_env = env->tb_env;
76
    uint64_t tb;
77

  
78
    tb = cpu_ppc_get_tb(tb_env);
79
#ifdef DEBUG_TB
80
    printf("%s: tb=0x%016lx\n", __func__, tb);
81
#endif
82
    return tb >> 32;
83
}
84

  
85
static void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t value)
86
{
87
    tb_env->tb_offset = muldiv64(value, ticks_per_sec, tb_env->tb_freq)
88
        - qemu_get_clock(vm_clock);
89
#ifdef DEBUG_TB
90
    printf("%s: tb=0x%016lx offset=%08x\n", __func__, value);
91
#endif
92
}
93

  
94
void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
95
{
96
    ppc_tb_t *tb_env = env->tb_env;
97

  
98
    cpu_ppc_store_tb(tb_env,
99
                     ((uint64_t)value << 32) | cpu_ppc_load_tbl(env));
100
}
101

  
102
void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
103
{
104
    ppc_tb_t *tb_env = env->tb_env;
105

  
106
    cpu_ppc_store_tb(tb_env,
107
                     ((uint64_t)cpu_ppc_load_tbu(env) << 32) | value);
108
}
109

  
110
uint32_t cpu_ppc_load_decr (CPUState *env)
111
{
112
    ppc_tb_t *tb_env = env->tb_env;
113
    uint32_t decr;
114

  
115
    decr = muldiv64(tb_env->decr_next - qemu_get_clock(vm_clock),
116
                    tb_env->tb_freq, ticks_per_sec);
117
#ifdef DEBUG_TB
118
    printf("%s: 0x%08x\n", __func__, decr);
119
#endif
120

  
121
    return decr;
122
}
123

  
124
/* When decrementer expires,
125
 * all we need to do is generate or queue a CPU exception
126
 */
127
static inline void cpu_ppc_decr_excp (CPUState *env)
128
{
129
    /* Raise it */
130
#ifdef DEBUG_TB
131
    printf("raise decrementer exception\n");
132
#endif
133
    cpu_interrupt(env, CPU_INTERRUPT_TIMER);
134
}
135

  
136
static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr,
137
                                 uint32_t value, int is_excp)
138
{
139
    ppc_tb_t *tb_env = env->tb_env;
140
    uint64_t now, next;
141

  
142
#ifdef DEBUG_TB
143
    printf("%s: 0x%08x => 0x%08x\n", __func__, decr, value);
144
#endif
145
    now = qemu_get_clock(vm_clock);
146
    next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq);
147
    if (is_excp)
148
        next += tb_env->decr_next - now;
149
    if (next == now)
150
	next++;
151
    tb_env->decr_next = next;
152
    /* Adjust timer */
153
    qemu_mod_timer(tb_env->decr_timer, next);
154
    /* If we set a negative value and the decrementer was positive,
155
     * raise an exception.
156
     */
157
    if ((value & 0x80000000) && !(decr & 0x80000000))
158
	cpu_ppc_decr_excp(env);
159
}
160

  
161
void cpu_ppc_store_decr (CPUState *env, uint32_t value)
162
{
163
    _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0);
164
}
165

  
166
static void cpu_ppc_decr_cb (void *opaque)
167
{
168
    _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1);
169
}
170

  
171
/* Set up (once) timebase frequency (in Hz) */
172
ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq)
173
{
174
    ppc_tb_t *tb_env;
175

  
176
    tb_env = qemu_mallocz(sizeof(ppc_tb_t));
177
    if (tb_env == NULL)
178
        return NULL;
179
    env->tb_env = tb_env;
180
    if (tb_env->tb_freq == 0 || 1) {
181
	tb_env->tb_freq = freq;
182
	/* Create new timer */
183
	tb_env->decr_timer =
184
            qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env);
185
	/* There is a bug in  2.4 kernels:
186
	 * if a decrementer exception is pending when it enables msr_ee,
187
	 * it's not ready to handle it...
188
	 */
189
	_cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
190
    }
191

  
192
    return tb_env;
193
}
194

  
195
#if 0
196
/*****************************************************************************/
197
/* Handle system reset (for now, just stop emulation) */
198
void cpu_ppc_reset (CPUState *env)
199
{
200
    printf("Reset asked... Stop emulation\n");
201
    abort();
202
}
203
#endif
204

  
205
/*****************************************************************************/
31 206
void ppc_init (int ram_size, int vga_ram_size, int boot_device,
32 207
	       DisplayState *ds, const char **fd_filename, int snapshot,
33 208
	       const char *kernel_filename, const char *kernel_cmdline,

Also available in: Unified diff