PowerPC prep/chrp/pmac support
[qemu] / hw / ppc.c
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 /*****************************************************************************/
27 /* PPC time base and decrementer emulation */
28 //#define DEBUG_TB
29
30 struct ppc_tb_t {
31     /* Time base management */
32     int64_t  tb_offset;    /* Compensation               */
33     uint32_t tb_freq;      /* TB frequency               */
34     /* Decrementer management */
35     uint64_t decr_next;    /* Tick for next decr interrupt  */
36     struct QEMUTimer *decr_timer;
37 };
38
39 static inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env)
40 {
41     /* TB time in tb periods */
42     return muldiv64(qemu_get_clock(vm_clock) + tb_env->tb_offset,
43                     tb_env->tb_freq, ticks_per_sec);
44 }
45
46 uint32_t cpu_ppc_load_tbl (CPUState *env)
47 {
48     ppc_tb_t *tb_env = env->tb_env;
49     uint64_t tb;
50
51     tb = cpu_ppc_get_tb(tb_env);
52 #ifdef DEBUG_TB
53     {
54          static int last_time;
55          int now;
56          now = time(NULL);
57          if (last_time != now) {
58              last_time = now;
59              printf("%s: tb=0x%016lx %d %08lx\n",
60                     __func__, tb, now, tb_env->tb_offset);
61          }
62     }
63 #endif
64
65     return tb & 0xFFFFFFFF;
66 }
67
68 uint32_t cpu_ppc_load_tbu (CPUState *env)
69 {
70     ppc_tb_t *tb_env = env->tb_env;
71     uint64_t tb;
72
73     tb = cpu_ppc_get_tb(tb_env);
74 #ifdef DEBUG_TB
75     printf("%s: tb=0x%016lx\n", __func__, tb);
76 #endif
77     return tb >> 32;
78 }
79
80 static void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t value)
81 {
82     tb_env->tb_offset = muldiv64(value, ticks_per_sec, tb_env->tb_freq)
83         - qemu_get_clock(vm_clock);
84 #ifdef DEBUG_TB
85     printf("%s: tb=0x%016lx offset=%08x\n", __func__, value);
86 #endif
87 }
88
89 void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
90 {
91     ppc_tb_t *tb_env = env->tb_env;
92
93     cpu_ppc_store_tb(tb_env,
94                      ((uint64_t)value << 32) | cpu_ppc_load_tbl(env));
95 }
96
97 void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
98 {
99     ppc_tb_t *tb_env = env->tb_env;
100
101     cpu_ppc_store_tb(tb_env,
102                      ((uint64_t)cpu_ppc_load_tbu(env) << 32) | value);
103 }
104
105 uint32_t cpu_ppc_load_decr (CPUState *env)
106 {
107     ppc_tb_t *tb_env = env->tb_env;
108     uint32_t decr;
109
110     decr = muldiv64(tb_env->decr_next - qemu_get_clock(vm_clock),
111                     tb_env->tb_freq, ticks_per_sec);
112 #ifdef DEBUG_TB
113     printf("%s: 0x%08x\n", __func__, decr);
114 #endif
115
116     return decr;
117 }
118
119 /* When decrementer expires,
120  * all we need to do is generate or queue a CPU exception
121  */
122 static inline void cpu_ppc_decr_excp (CPUState *env)
123 {
124     /* Raise it */
125 #ifdef DEBUG_TB
126     printf("raise decrementer exception\n");
127 #endif
128     cpu_interrupt(env, CPU_INTERRUPT_TIMER);
129 }
130
131 static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr,
132                                  uint32_t value, int is_excp)
133 {
134     ppc_tb_t *tb_env = env->tb_env;
135     uint64_t now, next;
136
137 #ifdef DEBUG_TB
138     printf("%s: 0x%08x => 0x%08x\n", __func__, decr, value);
139 #endif
140     now = qemu_get_clock(vm_clock);
141     next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq);
142     if (is_excp)
143         next += tb_env->decr_next - now;
144     if (next == now)
145         next++;
146     tb_env->decr_next = next;
147     /* Adjust timer */
148     qemu_mod_timer(tb_env->decr_timer, next);
149     /* If we set a negative value and the decrementer was positive,
150      * raise an exception.
151      */
152     if ((value & 0x80000000) && !(decr & 0x80000000))
153         cpu_ppc_decr_excp(env);
154 }
155
156 void cpu_ppc_store_decr (CPUState *env, uint32_t value)
157 {
158     _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0);
159 }
160
161 static void cpu_ppc_decr_cb (void *opaque)
162 {
163     _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1);
164 }
165
166 /* Set up (once) timebase frequency (in Hz) */
167 ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq)
168 {
169     ppc_tb_t *tb_env;
170
171     tb_env = qemu_mallocz(sizeof(ppc_tb_t));
172     if (tb_env == NULL)
173         return NULL;
174     env->tb_env = tb_env;
175     if (tb_env->tb_freq == 0 || 1) {
176         tb_env->tb_freq = freq;
177         /* Create new timer */
178         tb_env->decr_timer =
179             qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env);
180         /* There is a bug in  2.4 kernels:
181          * if a decrementer exception is pending when it enables msr_ee,
182          * it's not ready to handle it...
183          */
184         _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
185     }
186
187     return tb_env;
188 }
189
190 #if 0
191 /*****************************************************************************/
192 /* Handle system reset (for now, just stop emulation) */
193 void cpu_ppc_reset (CPUState *env)
194 {
195     printf("Reset asked... Stop emulation\n");
196     abort();
197 }
198 #endif
199
200 static void PPC_io_writeb (target_phys_addr_t addr, uint32_t value)
201 {
202     cpu_outb(NULL, addr & 0xffff, value);
203 }
204
205 static uint32_t PPC_io_readb (target_phys_addr_t addr)
206 {
207     uint32_t ret = cpu_inb(NULL, addr & 0xffff);
208     return ret;
209 }
210
211 static void PPC_io_writew (target_phys_addr_t addr, uint32_t value)
212 {
213 #ifdef TARGET_WORDS_BIGENDIAN
214     value = bswap16(value);
215 #endif
216     cpu_outw(NULL, addr & 0xffff, value);
217 }
218
219 static uint32_t PPC_io_readw (target_phys_addr_t addr)
220 {
221     uint32_t ret = cpu_inw(NULL, addr & 0xffff);
222 #ifdef TARGET_WORDS_BIGENDIAN
223     ret = bswap16(ret);
224 #endif
225     return ret;
226 }
227
228 static void PPC_io_writel (target_phys_addr_t addr, uint32_t value)
229 {
230 #ifdef TARGET_WORDS_BIGENDIAN
231     value = bswap32(value);
232 #endif
233     cpu_outl(NULL, addr & 0xffff, value);
234 }
235
236 static uint32_t PPC_io_readl (target_phys_addr_t addr)
237 {
238     uint32_t ret = cpu_inl(NULL, addr & 0xffff);
239
240 #ifdef TARGET_WORDS_BIGENDIAN
241     ret = bswap32(ret);
242 #endif
243     return ret;
244 }
245
246 CPUWriteMemoryFunc *PPC_io_write[] = {
247     &PPC_io_writeb,
248     &PPC_io_writew,
249     &PPC_io_writel,
250 };
251
252 CPUReadMemoryFunc *PPC_io_read[] = {
253     &PPC_io_readb,
254     &PPC_io_readw,
255     &PPC_io_readl,
256 };
257
258 /*****************************************************************************/
259 /* Debug port */
260 void PREP_debug_write (void *opaque, uint32_t addr, uint32_t val)
261 {
262     addr &= 0xF;
263     switch (addr) {
264     case 0:
265         printf("%c", val);
266         break;
267     case 1:
268         printf("\n");
269         fflush(stdout);
270         break;
271     case 2:
272         printf("Set loglevel to %04x\n", val);
273         cpu_set_log(val);
274         break;
275     }
276 }
277
278 /*****************************************************************************/
279 /* NVRAM helpers */
280 void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value)
281 {
282     m48t59_set_addr(nvram, addr);
283     m48t59_write(nvram, value);
284 }
285
286 uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr)
287 {
288     m48t59_set_addr(nvram, addr);
289     return m48t59_read(nvram);
290 }
291
292 void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value)
293 {
294     m48t59_set_addr(nvram, addr);
295     m48t59_write(nvram, value >> 8);
296     m48t59_set_addr(nvram, addr + 1);
297     m48t59_write(nvram, value & 0xFF);
298 }
299
300 uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr)
301 {
302     uint16_t tmp;
303
304     m48t59_set_addr(nvram, addr);
305     tmp = m48t59_read(nvram) << 8;
306     m48t59_set_addr(nvram, addr + 1);
307     tmp |= m48t59_read(nvram);
308
309     return tmp;
310 }
311
312 void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value)
313 {
314     m48t59_set_addr(nvram, addr);
315     m48t59_write(nvram, value >> 24);
316     m48t59_set_addr(nvram, addr + 1);
317     m48t59_write(nvram, (value >> 16) & 0xFF);
318     m48t59_set_addr(nvram, addr + 2);
319     m48t59_write(nvram, (value >> 8) & 0xFF);
320     m48t59_set_addr(nvram, addr + 3);
321     m48t59_write(nvram, value & 0xFF);
322 }
323
324 uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr)
325 {
326     uint32_t tmp;
327
328     m48t59_set_addr(nvram, addr);
329     tmp = m48t59_read(nvram) << 24;
330     m48t59_set_addr(nvram, addr + 1);
331     tmp |= m48t59_read(nvram) << 16;
332     m48t59_set_addr(nvram, addr + 2);
333     tmp |= m48t59_read(nvram) << 8;
334     m48t59_set_addr(nvram, addr + 3);
335     tmp |= m48t59_read(nvram);
336
337     return tmp;
338 }
339
340 void NVRAM_set_string (m48t59_t *nvram, uint32_t addr,
341                        const unsigned char *str, uint32_t max)
342 {
343     int i;
344
345     for (i = 0; i < max && str[i] != '\0'; i++) {
346         m48t59_set_addr(nvram, addr + i);
347         m48t59_write(nvram, str[i]);
348     }
349     m48t59_set_addr(nvram, addr + max - 1);
350     m48t59_write(nvram, '\0');
351 }
352
353 int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max)
354 {
355     int i;
356
357     memset(dst, 0, max);
358     for (i = 0; i < max; i++) {
359         dst[i] = NVRAM_get_byte(nvram, addr + i);
360         if (dst[i] == '\0')
361             break;
362     }
363
364     return i;
365 }
366
367 static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value)
368 {
369     uint16_t tmp;
370     uint16_t pd, pd1, pd2;
371
372     tmp = prev >> 8;
373     pd = prev ^ value;
374     pd1 = pd & 0x000F;
375     pd2 = ((pd >> 4) & 0x000F) ^ pd1;
376     tmp ^= (pd1 << 3) | (pd1 << 8);
377     tmp ^= pd2 | (pd2 << 7) | (pd2 << 12);
378
379     return tmp;
380 }
381
382 uint16_t NVRAM_compute_crc (m48t59_t *nvram, uint32_t start, uint32_t count)
383 {
384     uint32_t i;
385     uint16_t crc = 0xFFFF;
386     int odd;
387
388     odd = count & 1;
389     count &= ~1;
390     for (i = 0; i != count; i++) {
391         crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i));
392     }
393     if (odd) {
394         crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8);
395     }
396
397     return crc;
398 }
399
400 int PPC_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size,
401                           const unsigned char *arch,
402                           uint32_t RAM_size, int boot_device,
403                           uint32_t kernel_image, uint32_t kernel_size,
404                           uint32_t cmdline, uint32_t cmdline_size,
405                           uint32_t initrd_image, uint32_t initrd_size,
406                           uint32_t NVRAM_image)
407 {
408     uint16_t crc;
409
410     /* Set parameters for Open Hack'Ware BIOS */
411     NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16);
412     NVRAM_set_lword(nvram,  0x10, 0x00000002); /* structure v2 */
413     NVRAM_set_word(nvram,   0x14, NVRAM_size);
414     NVRAM_set_string(nvram, 0x20, arch, 16);
415     NVRAM_set_lword(nvram,  0x30, RAM_size);
416     NVRAM_set_byte(nvram,   0x34, boot_device);
417     NVRAM_set_lword(nvram,  0x38, kernel_image);
418     NVRAM_set_lword(nvram,  0x3C, kernel_size);
419     NVRAM_set_lword(nvram,  0x40, cmdline);
420     NVRAM_set_lword(nvram,  0x44, cmdline_size);
421     NVRAM_set_lword(nvram,  0x48, initrd_image);
422     NVRAM_set_lword(nvram,  0x4C, initrd_size);
423     NVRAM_set_lword(nvram,  0x50, NVRAM_image);
424     crc = NVRAM_compute_crc(nvram, 0x00, 0x5C);
425     NVRAM_set_word(nvram,  0x5C, crc);
426
427     return 0;
428  }
429
430 /*****************************************************************************/
431 void ppc_init (int ram_size, int vga_ram_size, int boot_device,
432                DisplayState *ds, const char **fd_filename, int snapshot,
433                const char *kernel_filename, const char *kernel_cmdline,
434                const char *initrd_filename)
435 {
436     if (prep_enabled) {
437         ppc_prep_init(ram_size, vga_ram_size, boot_device, ds, fd_filename,
438                       snapshot, kernel_filename, kernel_cmdline,
439                       initrd_filename);
440     } else {
441         ppc_chrp_init(ram_size, vga_ram_size, boot_device, ds, fd_filename,
442                       snapshot, kernel_filename, kernel_cmdline,
443                       initrd_filename);
444     }
445 }