root / hw / ppc.c @ 9fddaa0c
History | View | Annotate | Download (6.2 kB)
1 |
/*
|
---|---|
2 |
* QEMU generic PPC hardware System Emulator
|
3 |
*
|
4 |
* Copyright (c) 2003-2004 Jocelyn Mayer
|
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 |
#include "vl.h" |
25 |
|
26 |
void ppc_prep_init (int ram_size, int vga_ram_size, int boot_device, |
27 |
DisplayState *ds, const char **fd_filename, int snapshot, |
28 |
const char *kernel_filename, const char *kernel_cmdline, |
29 |
const char *initrd_filename); |
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 |
/*****************************************************************************/
|
206 |
void ppc_init (int ram_size, int vga_ram_size, int boot_device, |
207 |
DisplayState *ds, const char **fd_filename, int snapshot, |
208 |
const char *kernel_filename, const char *kernel_cmdline, |
209 |
const char *initrd_filename) |
210 |
{ |
211 |
/* For now, only PREP is supported */
|
212 |
return ppc_prep_init(ram_size, vga_ram_size, boot_device, ds, fd_filename,
|
213 |
snapshot, kernel_filename, kernel_cmdline, |
214 |
initrd_filename); |
215 |
} |