ppc.c 37.5 KB
Newer Older
1
/*
2
 * QEMU generic PowerPC hardware System Emulator
3
 *
4
 * Copyright (c) 2003-2007 Jocelyn Mayer
5
 *
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
P
pbrook 已提交
24 25 26 27 28
#include "hw.h"
#include "ppc.h"
#include "qemu-timer.h"
#include "sysemu.h"
#include "nvram.h"
B
blueswir1 已提交
29
#include "qemu-log.h"
B
Blue Swirl 已提交
30
#include "loader.h"
31

32
//#define PPC_DEBUG_IRQ
J
j_mayer 已提交
33
//#define PPC_DEBUG_TB
34

35
#ifdef PPC_DEBUG_IRQ
36
#  define LOG_IRQ(...) qemu_log_mask(CPU_LOG_INT, ## __VA_ARGS__)
37 38 39 40 41 42
#else
#  define LOG_IRQ(...) do { } while (0)
#endif


#ifdef PPC_DEBUG_TB
43
#  define LOG_TB(...) qemu_log(__VA_ARGS__)
44 45 46 47
#else
#  define LOG_TB(...) do { } while (0)
#endif

J
j_mayer 已提交
48 49 50
static void cpu_ppc_tb_stop (CPUState *env);
static void cpu_ppc_tb_start (CPUState *env);

J
j_mayer 已提交
51
static void ppc_set_irq (CPUState *env, int n_IRQ, int level)
52 53 54 55 56 57 58 59 60
{
    if (level) {
        env->pending_interrupts |= 1 << n_IRQ;
        cpu_interrupt(env, CPU_INTERRUPT_HARD);
    } else {
        env->pending_interrupts &= ~(1 << n_IRQ);
        if (env->pending_interrupts == 0)
            cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
    }
61
    LOG_IRQ("%s: %p n_IRQ %d level %d => pending %08" PRIx32
62
                "req %08x\n", __func__, env, n_IRQ, level,
63
                env->pending_interrupts, env->interrupt_request);
64 65
}

66 67
/* PowerPC 6xx / 7xx internal IRQ controller */
static void ppc6xx_set_irq (void *opaque, int pin, int level)
P
pbrook 已提交
68
{
69 70
    CPUState *env = opaque;
    int cur_level;
P
pbrook 已提交
71

72
    LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
73
                env, pin, level);
74 75
    cur_level = (env->irq_input_state >> pin) & 1;
    /* Don't generate spurious events */
76
    if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
77
        switch (pin) {
J
j_mayer 已提交
78 79
        case PPC6xx_INPUT_TBEN:
            /* Level sensitive - active high */
80
            LOG_IRQ("%s: %s the time base\n",
J
j_mayer 已提交
81 82 83 84 85 86
                        __func__, level ? "start" : "stop");
            if (level) {
                cpu_ppc_tb_start(env);
            } else {
                cpu_ppc_tb_stop(env);
            }
87 88
        case PPC6xx_INPUT_INT:
            /* Level sensitive - active high */
89
            LOG_IRQ("%s: set the external IRQ state to %d\n",
90
                        __func__, level);
91 92
            ppc_set_irq(env, PPC_INTERRUPT_EXT, level);
            break;
93
        case PPC6xx_INPUT_SMI:
94
            /* Level sensitive - active high */
95
            LOG_IRQ("%s: set the SMI IRQ state to %d\n",
96
                        __func__, level);
97 98
            ppc_set_irq(env, PPC_INTERRUPT_SMI, level);
            break;
99
        case PPC6xx_INPUT_MCP:
100 101 102 103 104
            /* Negative edge sensitive */
            /* XXX: TODO: actual reaction may depends on HID0 status
             *            603/604/740/750: check HID0[EMCP]
             */
            if (cur_level == 1 && level == 0) {
105
                LOG_IRQ("%s: raise machine check state\n",
106
                            __func__);
107 108 109
                ppc_set_irq(env, PPC_INTERRUPT_MCK, 1);
            }
            break;
110
        case PPC6xx_INPUT_CKSTP_IN:
111 112
            /* Level sensitive - active low */
            /* XXX: TODO: relay the signal to CKSTP_OUT pin */
113
            /* XXX: Note that the only way to restart the CPU is to reset it */
114
            if (level) {
115
                LOG_IRQ("%s: stop the CPU\n", __func__);
116 117 118
                env->halted = 1;
            }
            break;
119
        case PPC6xx_INPUT_HRESET:
120 121
            /* Level sensitive - active low */
            if (level) {
122
                LOG_IRQ("%s: reset the CPU\n", __func__);
123 124 125
                env->interrupt_request |= CPU_INTERRUPT_EXITTB;
                /* XXX: TOFIX */
#if 0
126
                cpu_reset(env);
127 128
#else
                qemu_system_reset_request();
129 130 131
#endif
            }
            break;
132
        case PPC6xx_INPUT_SRESET:
133
            LOG_IRQ("%s: set the RESET IRQ state to %d\n",
134
                        __func__, level);
135 136 137 138
            ppc_set_irq(env, PPC_INTERRUPT_RESET, level);
            break;
        default:
            /* Unknown pin - do nothing */
139
            LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
140 141 142 143 144 145
            return;
        }
        if (level)
            env->irq_input_state |= 1 << pin;
        else
            env->irq_input_state &= ~(1 << pin);
P
pbrook 已提交
146 147 148
    }
}

149
void ppc6xx_irq_init (CPUState *env)
150
{
151 152
    env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, env,
                                                  PPC6xx_INPUT_NB);
153 154
}

J
j_mayer 已提交
155
#if defined(TARGET_PPC64)
156 157 158 159 160 161
/* PowerPC 970 internal IRQ controller */
static void ppc970_set_irq (void *opaque, int pin, int level)
{
    CPUState *env = opaque;
    int cur_level;

162
    LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
163 164 165 166 167 168 169
                env, pin, level);
    cur_level = (env->irq_input_state >> pin) & 1;
    /* Don't generate spurious events */
    if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
        switch (pin) {
        case PPC970_INPUT_INT:
            /* Level sensitive - active high */
170
            LOG_IRQ("%s: set the external IRQ state to %d\n",
171 172 173 174 175
                        __func__, level);
            ppc_set_irq(env, PPC_INTERRUPT_EXT, level);
            break;
        case PPC970_INPUT_THINT:
            /* Level sensitive - active high */
176
            LOG_IRQ("%s: set the SMI IRQ state to %d\n", __func__,
177 178 179 180 181 182 183 184 185
                        level);
            ppc_set_irq(env, PPC_INTERRUPT_THERM, level);
            break;
        case PPC970_INPUT_MCP:
            /* Negative edge sensitive */
            /* XXX: TODO: actual reaction may depends on HID0 status
             *            603/604/740/750: check HID0[EMCP]
             */
            if (cur_level == 1 && level == 0) {
186
                LOG_IRQ("%s: raise machine check state\n",
187 188 189 190 191 192 193 194
                            __func__);
                ppc_set_irq(env, PPC_INTERRUPT_MCK, 1);
            }
            break;
        case PPC970_INPUT_CKSTP:
            /* Level sensitive - active low */
            /* XXX: TODO: relay the signal to CKSTP_OUT pin */
            if (level) {
195
                LOG_IRQ("%s: stop the CPU\n", __func__);
196 197
                env->halted = 1;
            } else {
198
                LOG_IRQ("%s: restart the CPU\n", __func__);
199 200 201 202 203 204 205
                env->halted = 0;
            }
            break;
        case PPC970_INPUT_HRESET:
            /* Level sensitive - active low */
            if (level) {
#if 0 // XXX: TOFIX
206
                LOG_IRQ("%s: reset the CPU\n", __func__);
207 208 209 210 211
                cpu_reset(env);
#endif
            }
            break;
        case PPC970_INPUT_SRESET:
212
            LOG_IRQ("%s: set the RESET IRQ state to %d\n",
213 214 215 216
                        __func__, level);
            ppc_set_irq(env, PPC_INTERRUPT_RESET, level);
            break;
        case PPC970_INPUT_TBEN:
217
            LOG_IRQ("%s: set the TBEN state to %d\n", __func__,
218 219 220 221 222
                        level);
            /* XXX: TODO */
            break;
        default:
            /* Unknown pin - do nothing */
223
            LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
224 225 226 227 228 229 230 231 232 233 234
            return;
        }
        if (level)
            env->irq_input_state |= 1 << pin;
        else
            env->irq_input_state &= ~(1 << pin);
    }
}

void ppc970_irq_init (CPUState *env)
{
235 236
    env->irq_inputs = (void **)qemu_allocate_irqs(&ppc970_set_irq, env,
                                                  PPC970_INPUT_NB);
237
}
J
j_mayer 已提交
238
#endif /* defined(TARGET_PPC64) */
239

240 241
/* PowerPC 40x internal IRQ controller */
static void ppc40x_set_irq (void *opaque, int pin, int level)
242 243 244 245
{
    CPUState *env = opaque;
    int cur_level;

246
    LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
247
                env, pin, level);
248 249 250 251
    cur_level = (env->irq_input_state >> pin) & 1;
    /* Don't generate spurious events */
    if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
        switch (pin) {
252
        case PPC40x_INPUT_RESET_SYS:
253
            if (level) {
254
                LOG_IRQ("%s: reset the PowerPC system\n",
255 256 257 258
                            __func__);
                ppc40x_system_reset(env);
            }
            break;
259
        case PPC40x_INPUT_RESET_CHIP:
260
            if (level) {
261
                LOG_IRQ("%s: reset the PowerPC chip\n", __func__);
262 263 264
                ppc40x_chip_reset(env);
            }
            break;
265
        case PPC40x_INPUT_RESET_CORE:
266 267
            /* XXX: TODO: update DBSR[MRR] */
            if (level) {
268
                LOG_IRQ("%s: reset the PowerPC core\n", __func__);
269
                ppc40x_core_reset(env);
270 271
            }
            break;
272
        case PPC40x_INPUT_CINT:
273
            /* Level sensitive - active high */
274
            LOG_IRQ("%s: set the critical IRQ state to %d\n",
275
                        __func__, level);
276
            ppc_set_irq(env, PPC_INTERRUPT_CEXT, level);
277
            break;
278
        case PPC40x_INPUT_INT:
279
            /* Level sensitive - active high */
280
            LOG_IRQ("%s: set the external IRQ state to %d\n",
281
                        __func__, level);
282 283
            ppc_set_irq(env, PPC_INTERRUPT_EXT, level);
            break;
284
        case PPC40x_INPUT_HALT:
285 286
            /* Level sensitive - active low */
            if (level) {
287
                LOG_IRQ("%s: stop the CPU\n", __func__);
288 289
                env->halted = 1;
            } else {
290
                LOG_IRQ("%s: restart the CPU\n", __func__);
291 292 293
                env->halted = 0;
            }
            break;
294
        case PPC40x_INPUT_DEBUG:
295
            /* Level sensitive - active high */
296
            LOG_IRQ("%s: set the debug pin state to %d\n",
297
                        __func__, level);
298
            ppc_set_irq(env, PPC_INTERRUPT_DEBUG, level);
299 300 301
            break;
        default:
            /* Unknown pin - do nothing */
302
            LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
303 304 305 306 307 308 309 310 311
            return;
        }
        if (level)
            env->irq_input_state |= 1 << pin;
        else
            env->irq_input_state &= ~(1 << pin);
    }
}

312
void ppc40x_irq_init (CPUState *env)
313
{
314 315
    env->irq_inputs = (void **)qemu_allocate_irqs(&ppc40x_set_irq,
                                                  env, PPC40x_INPUT_NB);
316 317
}

318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
/* PowerPC E500 internal IRQ controller */
static void ppce500_set_irq (void *opaque, int pin, int level)
{
    CPUState *env = opaque;
    int cur_level;

    LOG_IRQ("%s: env %p pin %d level %d\n", __func__,
                env, pin, level);
    cur_level = (env->irq_input_state >> pin) & 1;
    /* Don't generate spurious events */
    if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0)) {
        switch (pin) {
        case PPCE500_INPUT_MCK:
            if (level) {
                LOG_IRQ("%s: reset the PowerPC system\n",
                            __func__);
                qemu_system_reset_request();
            }
            break;
        case PPCE500_INPUT_RESET_CORE:
            if (level) {
                LOG_IRQ("%s: reset the PowerPC core\n", __func__);
                ppc_set_irq(env, PPC_INTERRUPT_MCK, level);
            }
            break;
        case PPCE500_INPUT_CINT:
            /* Level sensitive - active high */
            LOG_IRQ("%s: set the critical IRQ state to %d\n",
                        __func__, level);
            ppc_set_irq(env, PPC_INTERRUPT_CEXT, level);
            break;
        case PPCE500_INPUT_INT:
            /* Level sensitive - active high */
            LOG_IRQ("%s: set the core IRQ state to %d\n",
                        __func__, level);
            ppc_set_irq(env, PPC_INTERRUPT_EXT, level);
            break;
        case PPCE500_INPUT_DEBUG:
            /* Level sensitive - active high */
            LOG_IRQ("%s: set the debug pin state to %d\n",
                        __func__, level);
            ppc_set_irq(env, PPC_INTERRUPT_DEBUG, level);
            break;
        default:
            /* Unknown pin - do nothing */
            LOG_IRQ("%s: unknown IRQ pin %d\n", __func__, pin);
            return;
        }
        if (level)
            env->irq_input_state |= 1 << pin;
        else
            env->irq_input_state &= ~(1 << pin);
    }
}

void ppce500_irq_init (CPUState *env)
{
    env->irq_inputs = (void **)qemu_allocate_irqs(&ppce500_set_irq,
                                        env, PPCE500_INPUT_NB);
}
378
/*****************************************************************************/
379
/* PowerPC time base and decrementer emulation */
A
Anthony Liguori 已提交
380
struct ppc_tb_t {
381
    /* Time base management */
J
j_mayer 已提交
382 383 384
    int64_t  tb_offset;    /* Compensation                    */
    int64_t  atb_offset;   /* Compensation                    */
    uint32_t tb_freq;      /* TB frequency                    */
385
    /* Decrementer management */
J
j_mayer 已提交
386 387
    uint64_t decr_next;    /* Tick for next decr interrupt    */
    uint32_t decr_freq;    /* decrementer frequency           */
388
    struct QEMUTimer *decr_timer;
389 390 391 392 393
    /* Hypervisor decrementer management */
    uint64_t hdecr_next;    /* Tick for next hdecr interrupt  */
    struct QEMUTimer *hdecr_timer;
    uint64_t purr_load;
    uint64_t purr_start;
394
    void *opaque;
395 396
};

A
Anthony Liguori 已提交
397
static inline uint64_t cpu_ppc_get_tb(ppc_tb_t *tb_env, uint64_t vmclk,
B
Blue Swirl 已提交
398
                                      int64_t tb_offset)
399 400
{
    /* TB time in tb periods */
401
    return muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec()) + tb_offset;
402 403
}

A
Alexander Graf 已提交
404
uint64_t cpu_ppc_load_tbl (CPUState *env)
405
{
A
Anthony Liguori 已提交
406
    ppc_tb_t *tb_env = env->tb_env;
407 408
    uint64_t tb;

J
j_mayer 已提交
409
    tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset);
410
    LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
411

A
Alexander Graf 已提交
412
    return tb;
413 414
}

B
Blue Swirl 已提交
415
static inline uint32_t _cpu_ppc_load_tbu(CPUState *env)
416
{
A
Anthony Liguori 已提交
417
    ppc_tb_t *tb_env = env->tb_env;
418 419
    uint64_t tb;

J
j_mayer 已提交
420
    tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset);
421
    LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
422

423 424 425
    return tb >> 32;
}

426 427 428 429 430
uint32_t cpu_ppc_load_tbu (CPUState *env)
{
    return _cpu_ppc_load_tbu(env);
}

A
Anthony Liguori 已提交
431
static inline void cpu_ppc_store_tb(ppc_tb_t *tb_env, uint64_t vmclk,
B
Blue Swirl 已提交
432
                                    int64_t *tb_offsetp, uint64_t value)
433
{
434
    *tb_offsetp = value - muldiv64(vmclk, tb_env->tb_freq, get_ticks_per_sec());
435
    LOG_TB("%s: tb %016" PRIx64 " offset %08" PRIx64 "\n",
436
                __func__, value, *tb_offsetp);
437 438
}

439 440
void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
{
A
Anthony Liguori 已提交
441
    ppc_tb_t *tb_env = env->tb_env;
442 443
    uint64_t tb;

J
j_mayer 已提交
444
    tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset);
445
    tb &= 0xFFFFFFFF00000000ULL;
J
j_mayer 已提交
446 447
    cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock),
                     &tb_env->tb_offset, tb | (uint64_t)value);
448 449
}

B
Blue Swirl 已提交
450
static inline void _cpu_ppc_store_tbu(CPUState *env, uint32_t value)
451
{
A
Anthony Liguori 已提交
452
    ppc_tb_t *tb_env = env->tb_env;
453
    uint64_t tb;
454

J
j_mayer 已提交
455
    tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->tb_offset);
456
    tb &= 0x00000000FFFFFFFFULL;
J
j_mayer 已提交
457 458
    cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock),
                     &tb_env->tb_offset, ((uint64_t)value << 32) | tb);
459 460
}

461 462 463 464 465
void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
{
    _cpu_ppc_store_tbu(env, value);
}

A
Aurelien Jarno 已提交
466
uint64_t cpu_ppc_load_atbl (CPUState *env)
467
{
A
Anthony Liguori 已提交
468
    ppc_tb_t *tb_env = env->tb_env;
469 470
    uint64_t tb;

J
j_mayer 已提交
471
    tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset);
472
    LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
473

A
Aurelien Jarno 已提交
474
    return tb;
475 476 477 478
}

uint32_t cpu_ppc_load_atbu (CPUState *env)
{
A
Anthony Liguori 已提交
479
    ppc_tb_t *tb_env = env->tb_env;
480 481
    uint64_t tb;

J
j_mayer 已提交
482
    tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset);
483
    LOG_TB("%s: tb %016" PRIx64 "\n", __func__, tb);
484 485 486 487 488 489

    return tb >> 32;
}

void cpu_ppc_store_atbl (CPUState *env, uint32_t value)
{
A
Anthony Liguori 已提交
490
    ppc_tb_t *tb_env = env->tb_env;
491 492
    uint64_t tb;

J
j_mayer 已提交
493
    tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset);
494
    tb &= 0xFFFFFFFF00000000ULL;
J
j_mayer 已提交
495 496
    cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock),
                     &tb_env->atb_offset, tb | (uint64_t)value);
497 498 499
}

void cpu_ppc_store_atbu (CPUState *env, uint32_t value)
500
{
A
Anthony Liguori 已提交
501
    ppc_tb_t *tb_env = env->tb_env;
502
    uint64_t tb;
503

J
j_mayer 已提交
504
    tb = cpu_ppc_get_tb(tb_env, qemu_get_clock(vm_clock), tb_env->atb_offset);
505
    tb &= 0x00000000FFFFFFFFULL;
J
j_mayer 已提交
506 507 508 509 510 511
    cpu_ppc_store_tb(tb_env, qemu_get_clock(vm_clock),
                     &tb_env->atb_offset, ((uint64_t)value << 32) | tb);
}

static void cpu_ppc_tb_stop (CPUState *env)
{
A
Anthony Liguori 已提交
512
    ppc_tb_t *tb_env = env->tb_env;
J
j_mayer 已提交
513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533
    uint64_t tb, atb, vmclk;

    /* If the time base is already frozen, do nothing */
    if (tb_env->tb_freq != 0) {
        vmclk = qemu_get_clock(vm_clock);
        /* Get the time base */
        tb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->tb_offset);
        /* Get the alternate time base */
        atb = cpu_ppc_get_tb(tb_env, vmclk, tb_env->atb_offset);
        /* Store the time base value (ie compute the current offset) */
        cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb);
        /* Store the alternate time base value (compute the current offset) */
        cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb);
        /* Set the time base frequency to zero */
        tb_env->tb_freq = 0;
        /* Now, the time bases are frozen to tb_offset / atb_offset value */
    }
}

static void cpu_ppc_tb_start (CPUState *env)
{
A
Anthony Liguori 已提交
534
    ppc_tb_t *tb_env = env->tb_env;
J
j_mayer 已提交
535
    uint64_t tb, atb, vmclk;
536

J
j_mayer 已提交
537 538 539 540 541 542 543 544 545 546 547 548 549 550
    /* If the time base is not frozen, do nothing */
    if (tb_env->tb_freq == 0) {
        vmclk = qemu_get_clock(vm_clock);
        /* Get the time base from tb_offset */
        tb = tb_env->tb_offset;
        /* Get the alternate time base from atb_offset */
        atb = tb_env->atb_offset;
        /* Restore the tb frequency from the decrementer frequency */
        tb_env->tb_freq = tb_env->decr_freq;
        /* Store the time base value */
        cpu_ppc_store_tb(tb_env, vmclk, &tb_env->tb_offset, tb);
        /* Store the alternate time base value */
        cpu_ppc_store_tb(tb_env, vmclk, &tb_env->atb_offset, atb);
    }
551 552
}

B
Blue Swirl 已提交
553
static inline uint32_t _cpu_ppc_load_decr(CPUState *env, uint64_t next)
554
{
A
Anthony Liguori 已提交
555
    ppc_tb_t *tb_env = env->tb_env;
556
    uint32_t decr;
B
bellard 已提交
557
    int64_t diff;
558

559
    diff = next - qemu_get_clock(vm_clock);
B
bellard 已提交
560
    if (diff >= 0)
561
        decr = muldiv64(diff, tb_env->decr_freq, get_ticks_per_sec());
B
bellard 已提交
562
    else
563
        decr = -muldiv64(-diff, tb_env->decr_freq, get_ticks_per_sec());
564
    LOG_TB("%s: %08" PRIx32 "\n", __func__, decr);
565

566 567 568
    return decr;
}

569 570
uint32_t cpu_ppc_load_decr (CPUState *env)
{
A
Anthony Liguori 已提交
571
    ppc_tb_t *tb_env = env->tb_env;
572

573
    return _cpu_ppc_load_decr(env, tb_env->decr_next);
574 575 576 577
}

uint32_t cpu_ppc_load_hdecr (CPUState *env)
{
A
Anthony Liguori 已提交
578
    ppc_tb_t *tb_env = env->tb_env;
579

580
    return _cpu_ppc_load_decr(env, tb_env->hdecr_next);
581 582 583 584
}

uint64_t cpu_ppc_load_purr (CPUState *env)
{
A
Anthony Liguori 已提交
585
    ppc_tb_t *tb_env = env->tb_env;
586 587 588
    uint64_t diff;

    diff = qemu_get_clock(vm_clock) - tb_env->purr_start;
J
j_mayer 已提交
589

590
    return tb_env->purr_load + muldiv64(diff, tb_env->tb_freq, get_ticks_per_sec());
591 592
}

593 594 595
/* When decrementer expires,
 * all we need to do is generate or queue a CPU exception
 */
B
Blue Swirl 已提交
596
static inline void cpu_ppc_decr_excp(CPUState *env)
597 598
{
    /* Raise it */
599
    LOG_TB("raise decrementer exception\n");
600
    ppc_set_irq(env, PPC_INTERRUPT_DECR, 1);
601 602
}

B
Blue Swirl 已提交
603
static inline void cpu_ppc_hdecr_excp(CPUState *env)
604 605
{
    /* Raise it */
606
    LOG_TB("raise decrementer exception\n");
607 608 609 610
    ppc_set_irq(env, PPC_INTERRUPT_HDECR, 1);
}

static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp,
J
j_mayer 已提交
611 612 613 614
                                  struct QEMUTimer *timer,
                                  void (*raise_excp)(CPUState *),
                                  uint32_t decr, uint32_t value,
                                  int is_excp)
615
{
A
Anthony Liguori 已提交
616
    ppc_tb_t *tb_env = env->tb_env;
617 618
    uint64_t now, next;

619
    LOG_TB("%s: %08" PRIx32 " => %08" PRIx32 "\n", __func__,
620
                decr, value);
621
    now = qemu_get_clock(vm_clock);
622
    next = now + muldiv64(value, get_ticks_per_sec(), tb_env->decr_freq);
623
    if (is_excp)
624
        next += *nextp - now;
625
    if (next == now)
626
        next++;
627
    *nextp = next;
628
    /* Adjust timer */
629
    qemu_mod_timer(timer, next);
630 631 632 633
    /* If we set a negative value and the decrementer was positive,
     * raise an exception.
     */
    if ((value & 0x80000000) && !(decr & 0x80000000))
634 635 636
        (*raise_excp)(env);
}

B
Blue Swirl 已提交
637 638
static inline void _cpu_ppc_store_decr(CPUState *env, uint32_t decr,
                                       uint32_t value, int is_excp)
639
{
A
Anthony Liguori 已提交
640
    ppc_tb_t *tb_env = env->tb_env;
641 642 643

    __cpu_ppc_store_decr(env, &tb_env->decr_next, tb_env->decr_timer,
                         &cpu_ppc_decr_excp, decr, value, is_excp);
644 645 646 647 648 649 650 651 652 653 654 655
}

void cpu_ppc_store_decr (CPUState *env, uint32_t value)
{
    _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0);
}

static void cpu_ppc_decr_cb (void *opaque)
{
    _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1);
}

B
Blue Swirl 已提交
656 657
static inline void _cpu_ppc_store_hdecr(CPUState *env, uint32_t hdecr,
                                        uint32_t value, int is_excp)
658
{
A
Anthony Liguori 已提交
659
    ppc_tb_t *tb_env = env->tb_env;
660

661 662 663 664
    if (tb_env->hdecr_timer != NULL) {
        __cpu_ppc_store_decr(env, &tb_env->hdecr_next, tb_env->hdecr_timer,
                             &cpu_ppc_hdecr_excp, hdecr, value, is_excp);
    }
665 666 667 668 669 670 671 672 673 674 675 676 677 678
}

void cpu_ppc_store_hdecr (CPUState *env, uint32_t value)
{
    _cpu_ppc_store_hdecr(env, cpu_ppc_load_hdecr(env), value, 0);
}

static void cpu_ppc_hdecr_cb (void *opaque)
{
    _cpu_ppc_store_hdecr(opaque, 0x00000000, 0xFFFFFFFF, 1);
}

void cpu_ppc_store_purr (CPUState *env, uint64_t value)
{
A
Anthony Liguori 已提交
679
    ppc_tb_t *tb_env = env->tb_env;
680 681 682 683 684

    tb_env->purr_load = value;
    tb_env->purr_start = qemu_get_clock(vm_clock);
}

685 686 687
static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq)
{
    CPUState *env = opaque;
A
Anthony Liguori 已提交
688
    ppc_tb_t *tb_env = env->tb_env;
689 690

    tb_env->tb_freq = freq;
J
j_mayer 已提交
691
    tb_env->decr_freq = freq;
692 693 694 695 696
    /* There is a bug in Linux 2.4 kernels:
     * if a decrementer exception is pending when it enables msr_ee at startup,
     * it's not ready to handle it...
     */
    _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
697 698
    _cpu_ppc_store_hdecr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
    cpu_ppc_store_purr(env, 0x0000000000000000ULL);
699 700
}

701
/* Set up (once) timebase frequency (in Hz) */
702
clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq)
703
{
A
Anthony Liguori 已提交
704
    ppc_tb_t *tb_env;
705

A
Anthony Liguori 已提交
706
    tb_env = qemu_mallocz(sizeof(ppc_tb_t));
707
    env->tb_env = tb_env;
708 709
    /* Create new timer */
    tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env);
710 711 712 713 714 715 716
    if (0) {
        /* XXX: find a suitable condition to enable the hypervisor decrementer
         */
        tb_env->hdecr_timer = qemu_new_timer(vm_clock, &cpu_ppc_hdecr_cb, env);
    } else {
        tb_env->hdecr_timer = NULL;
    }
717
    cpu_ppc_set_tb_clk(env, freq);
718

719
    return &cpu_ppc_set_tb_clk;
720 721
}

722
/* Specific helpers for POWER & PowerPC 601 RTC */
723 724
#if 0
static clk_setup_cb cpu_ppc601_rtc_init (CPUState *env)
725 726 727
{
    return cpu_ppc_tb_init(env, 7812500);
}
728
#endif
729 730

void cpu_ppc601_store_rtcu (CPUState *env, uint32_t value)
731 732 733
{
    _cpu_ppc_store_tbu(env, value);
}
734 735

uint32_t cpu_ppc601_load_rtcu (CPUState *env)
736 737 738
{
    return _cpu_ppc_load_tbu(env);
}
739 740 741 742 743 744 745 746 747 748 749

void cpu_ppc601_store_rtcl (CPUState *env, uint32_t value)
{
    cpu_ppc_store_tbl(env, value & 0x3FFFFF80);
}

uint32_t cpu_ppc601_load_rtcl (CPUState *env)
{
    return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
}

J
j_mayer 已提交
750
/*****************************************************************************/
751
/* Embedded PowerPC timers */
J
j_mayer 已提交
752 753

/* PIT, FIT & WDT */
A
Anthony Liguori 已提交
754 755
typedef struct ppcemb_timer_t ppcemb_timer_t;
struct ppcemb_timer_t {
J
j_mayer 已提交
756 757 758 759 760 761
    uint64_t pit_reload;  /* PIT auto-reload value        */
    uint64_t fit_next;    /* Tick for next FIT interrupt  */
    struct QEMUTimer *fit_timer;
    uint64_t wdt_next;    /* Tick for next WDT interrupt  */
    struct QEMUTimer *wdt_timer;
};
762

J
j_mayer 已提交
763 764 765 766
/* Fixed interval timer */
static void cpu_4xx_fit_cb (void *opaque)
{
    CPUState *env;
A
Anthony Liguori 已提交
767 768
    ppc_tb_t *tb_env;
    ppcemb_timer_t *ppcemb_timer;
J
j_mayer 已提交
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
    uint64_t now, next;

    env = opaque;
    tb_env = env->tb_env;
    ppcemb_timer = tb_env->opaque;
    now = qemu_get_clock(vm_clock);
    switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
    case 0:
        next = 1 << 9;
        break;
    case 1:
        next = 1 << 13;
        break;
    case 2:
        next = 1 << 17;
        break;
    case 3:
        next = 1 << 21;
        break;
    default:
        /* Cannot occur, but makes gcc happy */
        return;
    }
792
    next = now + muldiv64(next, get_ticks_per_sec(), tb_env->tb_freq);
J
j_mayer 已提交
793 794 795 796 797 798
    if (next == now)
        next++;
    qemu_mod_timer(ppcemb_timer->fit_timer, next);
    env->spr[SPR_40x_TSR] |= 1 << 26;
    if ((env->spr[SPR_40x_TCR] >> 23) & 0x1)
        ppc_set_irq(env, PPC_INTERRUPT_FIT, 1);
799 800 801
    LOG_TB("%s: ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__,
           (int)((env->spr[SPR_40x_TCR] >> 23) & 0x1),
           env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
J
j_mayer 已提交
802 803 804
}

/* Programmable interval timer */
A
Anthony Liguori 已提交
805
static void start_stop_pit (CPUState *env, ppc_tb_t *tb_env, int is_excp)
806
{
A
Anthony Liguori 已提交
807
    ppcemb_timer_t *ppcemb_timer;
J
j_mayer 已提交
808 809 810
    uint64_t now, next;

    ppcemb_timer = tb_env->opaque;
J
j_mayer 已提交
811 812 813 814
    if (ppcemb_timer->pit_reload <= 1 ||
        !((env->spr[SPR_40x_TCR] >> 26) & 0x1) ||
        (is_excp && !((env->spr[SPR_40x_TCR] >> 22) & 0x1))) {
        /* Stop PIT */
815
        LOG_TB("%s: stop PIT\n", __func__);
J
j_mayer 已提交
816 817
        qemu_del_timer(tb_env->decr_timer);
    } else {
818
        LOG_TB("%s: start PIT %016" PRIx64 "\n",
J
j_mayer 已提交
819 820
                    __func__, ppcemb_timer->pit_reload);
        now = qemu_get_clock(vm_clock);
J
j_mayer 已提交
821
        next = now + muldiv64(ppcemb_timer->pit_reload,
822
                              get_ticks_per_sec(), tb_env->decr_freq);
J
j_mayer 已提交
823 824
        if (is_excp)
            next += tb_env->decr_next - now;
J
j_mayer 已提交
825 826 827 828 829
        if (next == now)
            next++;
        qemu_mod_timer(tb_env->decr_timer, next);
        tb_env->decr_next = next;
    }
J
j_mayer 已提交
830 831 832 833 834
}

static void cpu_4xx_pit_cb (void *opaque)
{
    CPUState *env;
A
Anthony Liguori 已提交
835 836
    ppc_tb_t *tb_env;
    ppcemb_timer_t *ppcemb_timer;
J
j_mayer 已提交
837 838 839 840

    env = opaque;
    tb_env = env->tb_env;
    ppcemb_timer = tb_env->opaque;
J
j_mayer 已提交
841 842 843
    env->spr[SPR_40x_TSR] |= 1 << 27;
    if ((env->spr[SPR_40x_TCR] >> 26) & 0x1)
        ppc_set_irq(env, PPC_INTERRUPT_PIT, 1);
J
j_mayer 已提交
844
    start_stop_pit(env, tb_env, 1);
845 846 847 848 849 850
    LOG_TB("%s: ar %d ir %d TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx " "
           "%016" PRIx64 "\n", __func__,
           (int)((env->spr[SPR_40x_TCR] >> 22) & 0x1),
           (int)((env->spr[SPR_40x_TCR] >> 26) & 0x1),
           env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
           ppcemb_timer->pit_reload);
J
j_mayer 已提交
851 852 853 854 855 856
}

/* Watchdog timer */
static void cpu_4xx_wdt_cb (void *opaque)
{
    CPUState *env;
A
Anthony Liguori 已提交
857 858
    ppc_tb_t *tb_env;
    ppcemb_timer_t *ppcemb_timer;
J
j_mayer 已提交
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881
    uint64_t now, next;

    env = opaque;
    tb_env = env->tb_env;
    ppcemb_timer = tb_env->opaque;
    now = qemu_get_clock(vm_clock);
    switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
    case 0:
        next = 1 << 17;
        break;
    case 1:
        next = 1 << 21;
        break;
    case 2:
        next = 1 << 25;
        break;
    case 3:
        next = 1 << 29;
        break;
    default:
        /* Cannot occur, but makes gcc happy */
        return;
    }
882
    next = now + muldiv64(next, get_ticks_per_sec(), tb_env->decr_freq);
J
j_mayer 已提交
883 884
    if (next == now)
        next++;
885 886
    LOG_TB("%s: TCR " TARGET_FMT_lx " TSR " TARGET_FMT_lx "\n", __func__,
           env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
J
j_mayer 已提交
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908
    switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) {
    case 0x0:
    case 0x1:
        qemu_mod_timer(ppcemb_timer->wdt_timer, next);
        ppcemb_timer->wdt_next = next;
        env->spr[SPR_40x_TSR] |= 1 << 31;
        break;
    case 0x2:
        qemu_mod_timer(ppcemb_timer->wdt_timer, next);
        ppcemb_timer->wdt_next = next;
        env->spr[SPR_40x_TSR] |= 1 << 30;
        if ((env->spr[SPR_40x_TCR] >> 27) & 0x1)
            ppc_set_irq(env, PPC_INTERRUPT_WDT, 1);
        break;
    case 0x3:
        env->spr[SPR_40x_TSR] &= ~0x30000000;
        env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000;
        switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) {
        case 0x0:
            /* No reset */
            break;
        case 0x1: /* Core reset */
909 910
            ppc40x_core_reset(env);
            break;
J
j_mayer 已提交
911
        case 0x2: /* Chip reset */
912 913
            ppc40x_chip_reset(env);
            break;
J
j_mayer 已提交
914
        case 0x3: /* System reset */
915 916
            ppc40x_system_reset(env);
            break;
J
j_mayer 已提交
917 918
        }
    }
919 920 921 922
}

void store_40x_pit (CPUState *env, target_ulong val)
{
A
Anthony Liguori 已提交
923 924
    ppc_tb_t *tb_env;
    ppcemb_timer_t *ppcemb_timer;
J
j_mayer 已提交
925 926 927

    tb_env = env->tb_env;
    ppcemb_timer = tb_env->opaque;
928
    LOG_TB("%s val" TARGET_FMT_lx "\n", __func__, val);
J
j_mayer 已提交
929
    ppcemb_timer->pit_reload = val;
J
j_mayer 已提交
930
    start_stop_pit(env, tb_env, 0);
931 932
}

J
j_mayer 已提交
933
target_ulong load_40x_pit (CPUState *env)
934
{
J
j_mayer 已提交
935
    return cpu_ppc_load_decr(env);
936 937 938 939
}

void store_booke_tsr (CPUState *env, target_ulong val)
{
940
    LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
J
j_mayer 已提交
941 942 943
    env->spr[SPR_40x_TSR] &= ~(val & 0xFC000000);
    if (val & 0x80000000)
        ppc_set_irq(env, PPC_INTERRUPT_PIT, 0);
J
j_mayer 已提交
944 945 946 947
}

void store_booke_tcr (CPUState *env, target_ulong val)
{
A
Anthony Liguori 已提交
948
    ppc_tb_t *tb_env;
J
j_mayer 已提交
949 950

    tb_env = env->tb_env;
951
    LOG_TB("%s: val " TARGET_FMT_lx "\n", __func__, val);
J
j_mayer 已提交
952 953
    env->spr[SPR_40x_TCR] = val & 0xFFC00000;
    start_stop_pit(env, tb_env, 1);
954
    cpu_4xx_wdt_cb(env);
J
j_mayer 已提交
955 956
}

J
j_mayer 已提交
957 958 959
static void ppc_emb_set_tb_clk (void *opaque, uint32_t freq)
{
    CPUState *env = opaque;
A
Anthony Liguori 已提交
960
    ppc_tb_t *tb_env = env->tb_env;
J
j_mayer 已提交
961

962
    LOG_TB("%s set new frequency to %" PRIu32 "\n", __func__,
963
                freq);
J
j_mayer 已提交
964
    tb_env->tb_freq = freq;
J
j_mayer 已提交
965
    tb_env->decr_freq = freq;
J
j_mayer 已提交
966 967 968
    /* XXX: we should also update all timers */
}

969
clk_setup_cb ppc_emb_timers_init (CPUState *env, uint32_t freq)
J
j_mayer 已提交
970
{
A
Anthony Liguori 已提交
971 972
    ppc_tb_t *tb_env;
    ppcemb_timer_t *ppcemb_timer;
J
j_mayer 已提交
973

A
Anthony Liguori 已提交
974
    tb_env = qemu_mallocz(sizeof(ppc_tb_t));
975
    env->tb_env = tb_env;
A
Anthony Liguori 已提交
976
    ppcemb_timer = qemu_mallocz(sizeof(ppcemb_timer_t));
977
    tb_env->tb_freq = freq;
J
j_mayer 已提交
978
    tb_env->decr_freq = freq;
J
j_mayer 已提交
979
    tb_env->opaque = ppcemb_timer;
980
    LOG_TB("%s freq %" PRIu32 "\n", __func__, freq);
J
j_mayer 已提交
981 982 983 984 985 986 987 988
    if (ppcemb_timer != NULL) {
        /* We use decr timer for PIT */
        tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_4xx_pit_cb, env);
        ppcemb_timer->fit_timer =
            qemu_new_timer(vm_clock, &cpu_4xx_fit_cb, env);
        ppcemb_timer->wdt_timer =
            qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env);
    }
989

J
j_mayer 已提交
990
    return &ppc_emb_set_tb_clk;
991 992
}

993 994
/*****************************************************************************/
/* Embedded PowerPC Device Control Registers */
A
Anthony Liguori 已提交
995 996
typedef struct ppc_dcrn_t ppc_dcrn_t;
struct ppc_dcrn_t {
997 998 999 1000 1001
    dcr_read_cb dcr_read;
    dcr_write_cb dcr_write;
    void *opaque;
};

1002 1003 1004
/* XXX: on 460, DCR addresses are 32 bits wide,
 *      using DCRIPR to get the 22 upper bits of the DCR address
 */
1005
#define DCRN_NB 1024
A
Anthony Liguori 已提交
1006 1007
struct ppc_dcr_t {
    ppc_dcrn_t dcrn[DCRN_NB];
1008 1009 1010 1011
    int (*read_error)(int dcrn);
    int (*write_error)(int dcrn);
};

A
Alexander Graf 已提交
1012
int ppc_dcr_read (ppc_dcr_t *dcr_env, int dcrn, uint32_t *valp)
1013
{
A
Anthony Liguori 已提交
1014
    ppc_dcrn_t *dcr;
1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031

    if (dcrn < 0 || dcrn >= DCRN_NB)
        goto error;
    dcr = &dcr_env->dcrn[dcrn];
    if (dcr->dcr_read == NULL)
        goto error;
    *valp = (*dcr->dcr_read)(dcr->opaque, dcrn);

    return 0;

 error:
    if (dcr_env->read_error != NULL)
        return (*dcr_env->read_error)(dcrn);

    return -1;
}

A
Alexander Graf 已提交
1032
int ppc_dcr_write (ppc_dcr_t *dcr_env, int dcrn, uint32_t val)
1033
{
A
Anthony Liguori 已提交
1034
    ppc_dcrn_t *dcr;
1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054

    if (dcrn < 0 || dcrn >= DCRN_NB)
        goto error;
    dcr = &dcr_env->dcrn[dcrn];
    if (dcr->dcr_write == NULL)
        goto error;
    (*dcr->dcr_write)(dcr->opaque, dcrn, val);

    return 0;

 error:
    if (dcr_env->write_error != NULL)
        return (*dcr_env->write_error)(dcrn);

    return -1;
}

int ppc_dcr_register (CPUState *env, int dcrn, void *opaque,
                      dcr_read_cb dcr_read, dcr_write_cb dcr_write)
{
A
Anthony Liguori 已提交
1055 1056
    ppc_dcr_t *dcr_env;
    ppc_dcrn_t *dcr;
1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077

    dcr_env = env->dcr_env;
    if (dcr_env == NULL)
        return -1;
    if (dcrn < 0 || dcrn >= DCRN_NB)
        return -1;
    dcr = &dcr_env->dcrn[dcrn];
    if (dcr->opaque != NULL ||
        dcr->dcr_read != NULL ||
        dcr->dcr_write != NULL)
        return -1;
    dcr->opaque = opaque;
    dcr->dcr_read = dcr_read;
    dcr->dcr_write = dcr_write;

    return 0;
}

int ppc_dcr_init (CPUState *env, int (*read_error)(int dcrn),
                  int (*write_error)(int dcrn))
{
A
Anthony Liguori 已提交
1078
    ppc_dcr_t *dcr_env;
1079

A
Anthony Liguori 已提交
1080
    dcr_env = qemu_mallocz(sizeof(ppc_dcr_t));
1081 1082 1083 1084 1085 1086 1087
    dcr_env->read_error = read_error;
    dcr_env->write_error = write_error;
    env->dcr_env = dcr_env;

    return 0;
}

B
bellard 已提交
1088 1089
/*****************************************************************************/
/* Debug port */
B
bellard 已提交
1090
void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val)
B
bellard 已提交
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101
{
    addr &= 0xF;
    switch (addr) {
    case 0:
        printf("%c", val);
        break;
    case 1:
        printf("\n");
        fflush(stdout);
        break;
    case 2:
1102
        printf("Set loglevel to %04" PRIx32 "\n", val);
B
bellard 已提交
1103
        cpu_set_log(val | 0x100);
B
bellard 已提交
1104 1105 1106 1107 1108 1109
        break;
    }
}

/*****************************************************************************/
/* NVRAM helpers */
A
Anthony Liguori 已提交
1110
static inline uint32_t nvram_read (nvram_t *nvram, uint32_t addr)
B
bellard 已提交
1111
{
J
j_mayer 已提交
1112
    return (*nvram->read_fn)(nvram->opaque, addr);;
B
bellard 已提交
1113 1114
}

A
Anthony Liguori 已提交
1115
static inline void nvram_write (nvram_t *nvram, uint32_t addr, uint32_t val)
B
bellard 已提交
1116
{
J
j_mayer 已提交
1117
    (*nvram->write_fn)(nvram->opaque, addr, val);
B
bellard 已提交
1118 1119
}

A
Anthony Liguori 已提交
1120
void NVRAM_set_byte (nvram_t *nvram, uint32_t addr, uint8_t value)
B
bellard 已提交
1121
{
J
j_mayer 已提交
1122
    nvram_write(nvram, addr, value);
B
bellard 已提交
1123 1124
}

A
Anthony Liguori 已提交
1125
uint8_t NVRAM_get_byte (nvram_t *nvram, uint32_t addr)
J
j_mayer 已提交
1126 1127 1128 1129
{
    return nvram_read(nvram, addr);
}

A
Anthony Liguori 已提交
1130
void NVRAM_set_word (nvram_t *nvram, uint32_t addr, uint16_t value)
J
j_mayer 已提交
1131 1132 1133 1134 1135
{
    nvram_write(nvram, addr, value >> 8);
    nvram_write(nvram, addr + 1, value & 0xFF);
}

A
Anthony Liguori 已提交
1136
uint16_t NVRAM_get_word (nvram_t *nvram, uint32_t addr)
B
bellard 已提交
1137 1138 1139
{
    uint16_t tmp;

J
j_mayer 已提交
1140 1141 1142
    tmp = nvram_read(nvram, addr) << 8;
    tmp |= nvram_read(nvram, addr + 1);

B
bellard 已提交
1143 1144 1145
    return tmp;
}

A
Anthony Liguori 已提交
1146
void NVRAM_set_lword (nvram_t *nvram, uint32_t addr, uint32_t value)
B
bellard 已提交
1147
{
J
j_mayer 已提交
1148 1149 1150 1151
    nvram_write(nvram, addr, value >> 24);
    nvram_write(nvram, addr + 1, (value >> 16) & 0xFF);
    nvram_write(nvram, addr + 2, (value >> 8) & 0xFF);
    nvram_write(nvram, addr + 3, value & 0xFF);
B
bellard 已提交
1152 1153
}

A
Anthony Liguori 已提交
1154
uint32_t NVRAM_get_lword (nvram_t *nvram, uint32_t addr)
B
bellard 已提交
1155 1156 1157
{
    uint32_t tmp;

J
j_mayer 已提交
1158 1159 1160 1161
    tmp = nvram_read(nvram, addr) << 24;
    tmp |= nvram_read(nvram, addr + 1) << 16;
    tmp |= nvram_read(nvram, addr + 2) << 8;
    tmp |= nvram_read(nvram, addr + 3);
1162

B
bellard 已提交
1163 1164 1165
    return tmp;
}

A
Anthony Liguori 已提交
1166
void NVRAM_set_string (nvram_t *nvram, uint32_t addr,
1167
                       const char *str, uint32_t max)
B
bellard 已提交
1168 1169 1170 1171
{
    int i;

    for (i = 0; i < max && str[i] != '\0'; i++) {
J
j_mayer 已提交
1172
        nvram_write(nvram, addr + i, str[i]);
B
bellard 已提交
1173
    }
J
j_mayer 已提交
1174 1175
    nvram_write(nvram, addr + i, str[i]);
    nvram_write(nvram, addr + max - 1, '\0');
B
bellard 已提交
1176 1177
}

A
Anthony Liguori 已提交
1178
int NVRAM_get_string (nvram_t *nvram, uint8_t *dst, uint16_t addr, int max)
B
bellard 已提交
1179 1180 1181 1182 1183 1184 1185 1186 1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206
{
    int i;

    memset(dst, 0, max);
    for (i = 0; i < max; i++) {
        dst[i] = NVRAM_get_byte(nvram, addr + i);
        if (dst[i] == '\0')
            break;
    }

    return i;
}

static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value)
{
    uint16_t tmp;
    uint16_t pd, pd1, pd2;

    tmp = prev >> 8;
    pd = prev ^ value;
    pd1 = pd & 0x000F;
    pd2 = ((pd >> 4) & 0x000F) ^ pd1;
    tmp ^= (pd1 << 3) | (pd1 << 8);
    tmp ^= pd2 | (pd2 << 7) | (pd2 << 12);

    return tmp;
}

A
Anthony Liguori 已提交
1207
static uint16_t NVRAM_compute_crc (nvram_t *nvram, uint32_t start, uint32_t count)
B
bellard 已提交
1208 1209 1210 1211 1212 1213 1214 1215
{
    uint32_t i;
    uint16_t crc = 0xFFFF;
    int odd;

    odd = count & 1;
    count &= ~1;
    for (i = 0; i != count; i++) {
1216
        crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i));
B
bellard 已提交
1217 1218
    }
    if (odd) {
1219
        crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8);
B
bellard 已提交
1220 1221 1222 1223 1224
    }

    return crc;
}

B
bellard 已提交
1225 1226
#define CMDLINE_ADDR 0x017ff000

A
Anthony Liguori 已提交
1227
int PPC_NVRAM_set_params (nvram_t *nvram, uint16_t NVRAM_size,
1228
                          const char *arch,
B
bellard 已提交
1229 1230
                          uint32_t RAM_size, int boot_device,
                          uint32_t kernel_image, uint32_t kernel_size,
B
bellard 已提交
1231
                          const char *cmdline,
B
bellard 已提交
1232
                          uint32_t initrd_image, uint32_t initrd_size,
B
bellard 已提交
1233 1234
                          uint32_t NVRAM_image,
                          int width, int height, int depth)
B
bellard 已提交
1235 1236 1237 1238 1239 1240 1241 1242 1243 1244 1245 1246
{
    uint16_t crc;

    /* Set parameters for Open Hack'Ware BIOS */
    NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16);
    NVRAM_set_lword(nvram,  0x10, 0x00000002); /* structure v2 */
    NVRAM_set_word(nvram,   0x14, NVRAM_size);
    NVRAM_set_string(nvram, 0x20, arch, 16);
    NVRAM_set_lword(nvram,  0x30, RAM_size);
    NVRAM_set_byte(nvram,   0x34, boot_device);
    NVRAM_set_lword(nvram,  0x38, kernel_image);
    NVRAM_set_lword(nvram,  0x3C, kernel_size);
B
bellard 已提交
1247 1248
    if (cmdline) {
        /* XXX: put the cmdline in NVRAM too ? */
1249
        pstrcpy_targphys("cmdline", CMDLINE_ADDR, RAM_size - CMDLINE_ADDR, cmdline);
B
bellard 已提交
1250 1251 1252 1253 1254 1255
        NVRAM_set_lword(nvram,  0x40, CMDLINE_ADDR);
        NVRAM_set_lword(nvram,  0x44, strlen(cmdline));
    } else {
        NVRAM_set_lword(nvram,  0x40, 0);
        NVRAM_set_lword(nvram,  0x44, 0);
    }
B
bellard 已提交
1256 1257 1258
    NVRAM_set_lword(nvram,  0x48, initrd_image);
    NVRAM_set_lword(nvram,  0x4C, initrd_size);
    NVRAM_set_lword(nvram,  0x50, NVRAM_image);
B
bellard 已提交
1259 1260 1261 1262 1263

    NVRAM_set_word(nvram,   0x54, width);
    NVRAM_set_word(nvram,   0x56, height);
    NVRAM_set_word(nvram,   0x58, depth);
    crc = NVRAM_compute_crc(nvram, 0x00, 0xF8);
J
j_mayer 已提交
1264
    NVRAM_set_word(nvram,   0xFC, crc);
B
bellard 已提交
1265 1266

    return 0;
1267
}