Statistics
| Branch: | Revision:

root / hw / ppc.c @ 4e3e9d0b

History | View | Annotate | Download (11.9 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
/*****************************************************************************/
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 (void *opaque, 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 (void *opaque, 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 (void *opaque, 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 (void *opaque, 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 (void *opaque, 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 (void *opaque, 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
}