Statistics
| Branch: | Revision:

root / hw / ppc.c @ e9df014c

History | View | Annotate | Download (20.4 kB)

1
/*
2
 * QEMU generic PowerPC hardware System Emulator
3
 * 
4
 * Copyright (c) 2003-2007 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
#include "m48t59.h"
26

    
27
//#define PPC_DEBUG_IRQ
28

    
29
extern FILE *logfile;
30
extern int loglevel;
31

    
32
void ppc_set_irq (CPUState *env, int n_IRQ, int level)
33
{
34
    if (level) {
35
        env->pending_interrupts |= 1 << n_IRQ;
36
        cpu_interrupt(env, CPU_INTERRUPT_HARD);
37
    } else {
38
        env->pending_interrupts &= ~(1 << n_IRQ);
39
        if (env->pending_interrupts == 0)
40
            cpu_reset_interrupt(env, CPU_INTERRUPT_HARD);
41
    }
42
#if defined(PPC_DEBUG_IRQ)
43
    printf("%s: %p n_IRQ %d level %d => pending %08x req %08x\n", __func__,
44
           env, n_IRQ, level, env->pending_interrupts, env->interrupt_request);
45
#endif
46
}
47

    
48
/* PowerPC 6xx / 7xx internal IRQ controller */
49
static void ppc6xx_set_irq (void *opaque, int pin, int level)
50
{
51
    CPUState *env = opaque;
52
    int cur_level;
53

    
54
#if defined(PPC_DEBUG_IRQ)
55
    printf("%s: env %p pin %d level %d\n", __func__, env, pin, level);
56
#endif
57
    cur_level = (env->irq_input_state >> pin) & 1;
58
    /* Don't generate spurious events */
59
    if ((cur_level == 1 && level == 0) || (cur_level == 0 && level != 0) || 0) {
60
        switch (pin) {
61
        case PPC_INPUT_INT:
62
            /* Level sensitive - asserted high */
63
#if defined(PPC_DEBUG_IRQ)
64
            printf("%s: set the external IRQ state to %d\n", __func__, level);
65
#endif
66
            ppc_set_irq(env, PPC_INTERRUPT_EXT, level);
67
            break;
68
        case PPC_INPUT_SMI:
69
            /* Level sensitive - active high */
70
#if defined(PPC_DEBUG_IRQ)
71
            printf("%s: set the SMI IRQ state to %d\n", __func__, level);
72
#endif
73
            ppc_set_irq(env, PPC_INTERRUPT_SMI, level);
74
            break;
75
        case PPC_INPUT_MCP:
76
            /* Negative edge sensitive */
77
            /* XXX: TODO: actual reaction may depends on HID0 status
78
             *            603/604/740/750: check HID0[EMCP]
79
             */
80
            if (cur_level == 1 && level == 0) {
81
#if defined(PPC_DEBUG_IRQ)
82
                printf("%s: raise machine check state\n", __func__);
83
#endif
84
                ppc_set_irq(env, PPC_INTERRUPT_MCK, 1);
85
            }
86
            break;
87
        case PPC_INPUT_CKSTP_IN:
88
            /* Level sensitive - active low */
89
            /* XXX: TODO: relay the signal to CKSTP_OUT pin */
90
            if (level) {
91
#if defined(PPC_DEBUG_IRQ)
92
                printf("%s: stop the CPU\n", __func__);
93
#endif
94
                env->halted = 1;
95
            } else {
96
#if defined(PPC_DEBUG_IRQ)
97
                printf("%s: restart the CPU\n", __func__);
98
#endif
99
                env->halted = 0;
100
            }
101
            break;
102
        case PPC_INPUT_HRESET:
103
            /* Level sensitive - active low */
104
            if (level) {
105
#if 0 // XXX: TOFIX
106
#if defined(PPC_DEBUG_IRQ)
107
                printf("%s: reset the CPU\n", __func__);
108
#endif
109
                cpu_reset(env);
110
#endif
111
            }
112
            break;
113
        case PPC_INPUT_SRESET:
114
#if defined(PPC_DEBUG_IRQ)
115
            printf("%s: set the RESET IRQ state to %d\n", __func__, level);
116
#endif
117
            ppc_set_irq(env, PPC_INTERRUPT_RESET, level);
118
            break;
119
        default:
120
            /* Unknown pin - do nothing */
121
#if defined(PPC_DEBUG_IRQ)
122
            printf("%s: unknown IRQ pin %d\n", __func__, pin);
123
#endif
124
            return;
125
        }
126
        if (level)
127
            env->irq_input_state |= 1 << pin;
128
        else
129
            env->irq_input_state &= ~(1 << pin);
130
    }
131
}
132

    
133
void ppc6xx_irq_init (CPUState *env)
134
{
135
    env->irq_inputs = (void **)qemu_allocate_irqs(&ppc6xx_set_irq, env, 6);
136
}
137

    
138
/*****************************************************************************/
139
/* PowerPC time base and decrementer emulation */
140
//#define DEBUG_TB
141

    
142
struct ppc_tb_t {
143
    /* Time base management */
144
    int64_t  tb_offset;    /* Compensation               */
145
    uint32_t tb_freq;      /* TB frequency               */
146
    /* Decrementer management */
147
    uint64_t decr_next;    /* Tick for next decr interrupt  */
148
    struct QEMUTimer *decr_timer;
149
    void *opaque;
150
};
151

    
152
static inline uint64_t cpu_ppc_get_tb (ppc_tb_t *tb_env)
153
{
154
    /* TB time in tb periods */
155
    return muldiv64(qemu_get_clock(vm_clock) + tb_env->tb_offset,
156
                    tb_env->tb_freq, ticks_per_sec);
157
}
158

    
159
uint32_t cpu_ppc_load_tbl (CPUState *env)
160
{
161
    ppc_tb_t *tb_env = env->tb_env;
162
    uint64_t tb;
163

    
164
    tb = cpu_ppc_get_tb(tb_env);
165
#ifdef DEBUG_TB
166
    {
167
        static int last_time;
168
        int now;
169
        now = time(NULL);
170
        if (last_time != now) {
171
            last_time = now;
172
            printf("%s: tb=0x%016lx %d %08lx\n",
173
                   __func__, tb, now, tb_env->tb_offset);
174
        }
175
    }
176
#endif
177

    
178
    return tb & 0xFFFFFFFF;
179
}
180

    
181
uint32_t cpu_ppc_load_tbu (CPUState *env)
182
{
183
    ppc_tb_t *tb_env = env->tb_env;
184
    uint64_t tb;
185

    
186
    tb = cpu_ppc_get_tb(tb_env);
187
#ifdef DEBUG_TB
188
    printf("%s: tb=0x%016lx\n", __func__, tb);
189
#endif
190

    
191
    return tb >> 32;
192
}
193

    
194
static void cpu_ppc_store_tb (ppc_tb_t *tb_env, uint64_t value)
195
{
196
    tb_env->tb_offset = muldiv64(value, ticks_per_sec, tb_env->tb_freq)
197
        - qemu_get_clock(vm_clock);
198
#ifdef DEBUG_TB
199
    printf("%s: tb=0x%016lx offset=%08x\n", __func__, value);
200
#endif
201
}
202

    
203
void cpu_ppc_store_tbu (CPUState *env, uint32_t value)
204
{
205
    ppc_tb_t *tb_env = env->tb_env;
206

    
207
    cpu_ppc_store_tb(tb_env,
208
                     ((uint64_t)value << 32) | cpu_ppc_load_tbl(env));
209
}
210

    
211
void cpu_ppc_store_tbl (CPUState *env, uint32_t value)
212
{
213
    ppc_tb_t *tb_env = env->tb_env;
214

    
215
    cpu_ppc_store_tb(tb_env,
216
                     ((uint64_t)cpu_ppc_load_tbu(env) << 32) | value);
217
}
218

    
219
uint32_t cpu_ppc_load_decr (CPUState *env)
220
{
221
    ppc_tb_t *tb_env = env->tb_env;
222
    uint32_t decr;
223
    int64_t diff;
224

    
225
    diff = tb_env->decr_next - qemu_get_clock(vm_clock);
226
    if (diff >= 0)
227
        decr = muldiv64(diff, tb_env->tb_freq, ticks_per_sec);
228
    else
229
        decr = -muldiv64(-diff, tb_env->tb_freq, ticks_per_sec);
230
#if defined(DEBUG_TB)
231
    printf("%s: 0x%08x\n", __func__, decr);
232
#endif
233

    
234
    return decr;
235
}
236

    
237
/* When decrementer expires,
238
 * all we need to do is generate or queue a CPU exception
239
 */
240
static inline void cpu_ppc_decr_excp (CPUState *env)
241
{
242
    /* Raise it */
243
#ifdef DEBUG_TB
244
    printf("raise decrementer exception\n");
245
#endif
246
    ppc_set_irq(env, PPC_INTERRUPT_DECR, 1);
247
}
248

    
249
static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr,
250
                                 uint32_t value, int is_excp)
251
{
252
    ppc_tb_t *tb_env = env->tb_env;
253
    uint64_t now, next;
254

    
255
#ifdef DEBUG_TB
256
    printf("%s: 0x%08x => 0x%08x\n", __func__, decr, value);
257
#endif
258
    now = qemu_get_clock(vm_clock);
259
    next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq);
260
    if (is_excp)
261
        next += tb_env->decr_next - now;
262
    if (next == now)
263
        next++;
264
    tb_env->decr_next = next;
265
    /* Adjust timer */
266
    qemu_mod_timer(tb_env->decr_timer, next);
267
    /* If we set a negative value and the decrementer was positive,
268
     * raise an exception.
269
     */
270
    if ((value & 0x80000000) && !(decr & 0x80000000))
271
        cpu_ppc_decr_excp(env);
272
}
273

    
274
void cpu_ppc_store_decr (CPUState *env, uint32_t value)
275
{
276
    _cpu_ppc_store_decr(env, cpu_ppc_load_decr(env), value, 0);
277
}
278

    
279
static void cpu_ppc_decr_cb (void *opaque)
280
{
281
    _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1);
282
}
283

    
284
/* Set up (once) timebase frequency (in Hz) */
285
ppc_tb_t *cpu_ppc_tb_init (CPUState *env, uint32_t freq)
286
{
287
    ppc_tb_t *tb_env;
288

    
289
    tb_env = qemu_mallocz(sizeof(ppc_tb_t));
290
    if (tb_env == NULL)
291
        return NULL;
292
    env->tb_env = tb_env;
293
    if (tb_env->tb_freq == 0 || 1) {
294
        tb_env->tb_freq = freq;
295
        /* Create new timer */
296
        tb_env->decr_timer =
297
            qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env);
298
        /* There is a bug in Linux 2.4 kernels:
299
         * if a decrementer exception is pending when it enables msr_ee,
300
         * it's not ready to handle it...
301
         */
302
        _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0);
303
    }
304

    
305
    return tb_env;
306
}
307

    
308
/* Specific helpers for POWER & PowerPC 601 RTC */
309
ppc_tb_t *cpu_ppc601_rtc_init (CPUState *env)
310
{
311
    return cpu_ppc_tb_init(env, 7812500);
312
}
313

    
314
void cpu_ppc601_store_rtcu (CPUState *env, uint32_t value)
315
__attribute__ (( alias ("cpu_ppc_store_tbu") ));
316

    
317
uint32_t cpu_ppc601_load_rtcu (CPUState *env)
318
__attribute__ (( alias ("cpu_ppc_load_tbu") ));
319

    
320
void cpu_ppc601_store_rtcl (CPUState *env, uint32_t value)
321
{
322
    cpu_ppc_store_tbl(env, value & 0x3FFFFF80);
323
}
324

    
325
uint32_t cpu_ppc601_load_rtcl (CPUState *env)
326
{
327
    return cpu_ppc_load_tbl(env) & 0x3FFFFF80;
328
}
329

    
330
/*****************************************************************************/
331
/* Embedded PowerPC timers */
332

    
333
/* PIT, FIT & WDT */
334
typedef struct ppcemb_timer_t ppcemb_timer_t;
335
struct ppcemb_timer_t {
336
    uint64_t pit_reload;  /* PIT auto-reload value        */
337
    uint64_t fit_next;    /* Tick for next FIT interrupt  */
338
    struct QEMUTimer *fit_timer;
339
    uint64_t wdt_next;    /* Tick for next WDT interrupt  */
340
    struct QEMUTimer *wdt_timer;
341
};
342
   
343
/* Fixed interval timer */
344
static void cpu_4xx_fit_cb (void *opaque)
345
{
346
    CPUState *env;
347
    ppc_tb_t *tb_env;
348
    ppcemb_timer_t *ppcemb_timer;
349
    uint64_t now, next;
350

    
351
    env = opaque;
352
    tb_env = env->tb_env;
353
    ppcemb_timer = tb_env->opaque;
354
    now = qemu_get_clock(vm_clock);
355
    switch ((env->spr[SPR_40x_TCR] >> 24) & 0x3) {
356
    case 0:
357
        next = 1 << 9;
358
        break;
359
    case 1:
360
        next = 1 << 13;
361
        break;
362
    case 2:
363
        next = 1 << 17;
364
        break;
365
    case 3:
366
        next = 1 << 21;
367
        break;
368
    default:
369
        /* Cannot occur, but makes gcc happy */
370
        return;
371
    }
372
    next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq);
373
    if (next == now)
374
        next++;
375
    qemu_mod_timer(ppcemb_timer->fit_timer, next);
376
    tb_env->decr_next = next;
377
    env->spr[SPR_40x_TSR] |= 1 << 26;
378
    if ((env->spr[SPR_40x_TCR] >> 23) & 0x1)
379
        ppc_set_irq(env, PPC_INTERRUPT_FIT, 1);
380
    if (loglevel) {
381
        fprintf(logfile, "%s: ir %d TCR %08x TSR %08x\n", __func__,
382
                (env->spr[SPR_40x_TCR] >> 23) & 0x1,
383
                env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
384
    }
385
}
386

    
387
/* Programmable interval timer */
388
static void cpu_4xx_pit_cb (void *opaque)
389
{
390
    CPUState *env;
391
    ppc_tb_t *tb_env;
392
    ppcemb_timer_t *ppcemb_timer;
393
    uint64_t now, next;
394

    
395
    env = opaque;
396
    tb_env = env->tb_env;
397
    ppcemb_timer = tb_env->opaque;
398
    now = qemu_get_clock(vm_clock);
399
    if ((env->spr[SPR_40x_TCR] >> 22) & 0x1) {
400
        /* Auto reload */
401
        next = now + muldiv64(ppcemb_timer->pit_reload,
402
                              ticks_per_sec, tb_env->tb_freq);
403
        if (next == now)
404
            next++;
405
        qemu_mod_timer(tb_env->decr_timer, next);
406
        tb_env->decr_next = next;
407
    }
408
    env->spr[SPR_40x_TSR] |= 1 << 27;
409
    if ((env->spr[SPR_40x_TCR] >> 26) & 0x1)
410
        ppc_set_irq(env, PPC_INTERRUPT_PIT, 1);
411
    if (loglevel) {
412
        fprintf(logfile, "%s: ar %d ir %d TCR %08x TSR %08x %08lx\n", __func__,
413
                (env->spr[SPR_40x_TCR] >> 22) & 0x1,
414
                (env->spr[SPR_40x_TCR] >> 26) & 0x1,
415
                env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR],
416
                ppcemb_timer->pit_reload);
417
    }
418
}
419

    
420
/* Watchdog timer */
421
static void cpu_4xx_wdt_cb (void *opaque)
422
{
423
    CPUState *env;
424
    ppc_tb_t *tb_env;
425
    ppcemb_timer_t *ppcemb_timer;
426
    uint64_t now, next;
427

    
428
    env = opaque;
429
    tb_env = env->tb_env;
430
    ppcemb_timer = tb_env->opaque;
431
    now = qemu_get_clock(vm_clock);
432
    switch ((env->spr[SPR_40x_TCR] >> 30) & 0x3) {
433
    case 0:
434
        next = 1 << 17;
435
        break;
436
    case 1:
437
        next = 1 << 21;
438
        break;
439
    case 2:
440
        next = 1 << 25;
441
        break;
442
    case 3:
443
        next = 1 << 29;
444
        break;
445
    default:
446
        /* Cannot occur, but makes gcc happy */
447
        return;
448
    }
449
    next = now + muldiv64(next, ticks_per_sec, tb_env->tb_freq);
450
    if (next == now)
451
        next++;
452
    if (loglevel) {
453
        fprintf(logfile, "%s: TCR %08x TSR %08x\n", __func__,
454
                env->spr[SPR_40x_TCR], env->spr[SPR_40x_TSR]);
455
    }
456
    switch ((env->spr[SPR_40x_TSR] >> 30) & 0x3) {
457
    case 0x0:
458
    case 0x1:
459
        qemu_mod_timer(ppcemb_timer->wdt_timer, next);
460
        ppcemb_timer->wdt_next = next;
461
        env->spr[SPR_40x_TSR] |= 1 << 31;
462
        break;
463
    case 0x2:
464
        qemu_mod_timer(ppcemb_timer->wdt_timer, next);
465
        ppcemb_timer->wdt_next = next;
466
        env->spr[SPR_40x_TSR] |= 1 << 30;
467
        if ((env->spr[SPR_40x_TCR] >> 27) & 0x1)
468
            ppc_set_irq(env, PPC_INTERRUPT_WDT, 1);
469
        break;
470
    case 0x3:
471
        env->spr[SPR_40x_TSR] &= ~0x30000000;
472
        env->spr[SPR_40x_TSR] |= env->spr[SPR_40x_TCR] & 0x30000000;
473
        switch ((env->spr[SPR_40x_TCR] >> 28) & 0x3) {
474
        case 0x0:
475
            /* No reset */
476
            break;
477
        case 0x1: /* Core reset */
478
        case 0x2: /* Chip reset */
479
        case 0x3: /* System reset */
480
            qemu_system_reset_request();
481
            return;
482
        }
483
    }
484
}
485

    
486
void store_40x_pit (CPUState *env, target_ulong val)
487
{
488
    ppc_tb_t *tb_env;
489
    ppcemb_timer_t *ppcemb_timer;
490
    uint64_t now, next;
491

    
492
    tb_env = env->tb_env;
493
    ppcemb_timer = tb_env->opaque;
494
    if (loglevel)
495
        fprintf(logfile, "%s %p %p\n", __func__, tb_env, ppcemb_timer);
496
    ppcemb_timer->pit_reload = val;
497
    if (val == 0) {
498
        /* Stop PIT */
499
        if (loglevel)
500
            fprintf(logfile, "%s: stop PIT\n", __func__);
501
        qemu_del_timer(tb_env->decr_timer);
502
    } else {
503
        if (loglevel)
504
            fprintf(logfile, "%s: start PIT 0x%08x\n", __func__, val);
505
        now = qemu_get_clock(vm_clock);
506
        next = now + muldiv64(val, ticks_per_sec, tb_env->tb_freq);
507
         if (next == now)
508
            next++;
509
        qemu_mod_timer(tb_env->decr_timer, next);
510
        tb_env->decr_next = next;
511
    }
512
}
513

    
514
target_ulong load_40x_pit (CPUState *env)
515
{
516
    return cpu_ppc_load_decr(env);
517
}
518

    
519
void store_booke_tsr (CPUState *env, target_ulong val)
520
{
521
    env->spr[SPR_40x_TSR] = val & 0xFC000000;
522
}
523

    
524
void store_booke_tcr (CPUState *env, target_ulong val)
525
{
526
    /* We don't update timers now. Maybe we should... */
527
    env->spr[SPR_40x_TCR] = val & 0xFF800000;
528
}
529

    
530
void ppc_emb_timers_init (CPUState *env)
531
{
532
    ppc_tb_t *tb_env;
533
    ppcemb_timer_t *ppcemb_timer;
534

    
535
    tb_env = env->tb_env;
536
    ppcemb_timer = qemu_mallocz(sizeof(ppcemb_timer_t));
537
    tb_env->opaque = ppcemb_timer;
538
    if (loglevel)
539
        fprintf(logfile, "%s %p %p\n", __func__, tb_env, ppcemb_timer);
540
    if (ppcemb_timer != NULL) {
541
        /* We use decr timer for PIT */
542
        tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_4xx_pit_cb, env);
543
        ppcemb_timer->fit_timer =
544
            qemu_new_timer(vm_clock, &cpu_4xx_fit_cb, env);
545
        ppcemb_timer->wdt_timer =
546
            qemu_new_timer(vm_clock, &cpu_4xx_wdt_cb, env);
547
    }
548
}
549

    
550
#if 0
551
/*****************************************************************************/
552
/* Handle system reset (for now, just stop emulation) */
553
void cpu_ppc_reset (CPUState *env)
554
{
555
    printf("Reset asked... Stop emulation\n");
556
    abort();
557
}
558
#endif
559

    
560
/*****************************************************************************/
561
/* Debug port */
562
void PPC_debug_write (void *opaque, uint32_t addr, uint32_t val)
563
{
564
    addr &= 0xF;
565
    switch (addr) {
566
    case 0:
567
        printf("%c", val);
568
        break;
569
    case 1:
570
        printf("\n");
571
        fflush(stdout);
572
        break;
573
    case 2:
574
        printf("Set loglevel to %04x\n", val);
575
        cpu_set_log(val | 0x100);
576
        break;
577
    }
578
}
579

    
580
/*****************************************************************************/
581
/* NVRAM helpers */
582
void NVRAM_set_byte (m48t59_t *nvram, uint32_t addr, uint8_t value)
583
{
584
    m48t59_write(nvram, addr, value);
585
}
586

    
587
uint8_t NVRAM_get_byte (m48t59_t *nvram, uint32_t addr)
588
{
589
    return m48t59_read(nvram, addr);
590
}
591

    
592
void NVRAM_set_word (m48t59_t *nvram, uint32_t addr, uint16_t value)
593
{
594
    m48t59_write(nvram, addr, value >> 8);
595
    m48t59_write(nvram, addr + 1, value & 0xFF);
596
}
597

    
598
uint16_t NVRAM_get_word (m48t59_t *nvram, uint32_t addr)
599
{
600
    uint16_t tmp;
601

    
602
    tmp = m48t59_read(nvram, addr) << 8;
603
    tmp |= m48t59_read(nvram, addr + 1);
604
    return tmp;
605
}
606

    
607
void NVRAM_set_lword (m48t59_t *nvram, uint32_t addr, uint32_t value)
608
{
609
    m48t59_write(nvram, addr, value >> 24);
610
    m48t59_write(nvram, addr + 1, (value >> 16) & 0xFF);
611
    m48t59_write(nvram, addr + 2, (value >> 8) & 0xFF);
612
    m48t59_write(nvram, addr + 3, value & 0xFF);
613
}
614

    
615
uint32_t NVRAM_get_lword (m48t59_t *nvram, uint32_t addr)
616
{
617
    uint32_t tmp;
618

    
619
    tmp = m48t59_read(nvram, addr) << 24;
620
    tmp |= m48t59_read(nvram, addr + 1) << 16;
621
    tmp |= m48t59_read(nvram, addr + 2) << 8;
622
    tmp |= m48t59_read(nvram, addr + 3);
623

    
624
    return tmp;
625
}
626

    
627
void NVRAM_set_string (m48t59_t *nvram, uint32_t addr,
628
                       const unsigned char *str, uint32_t max)
629
{
630
    int i;
631

    
632
    for (i = 0; i < max && str[i] != '\0'; i++) {
633
        m48t59_write(nvram, addr + i, str[i]);
634
    }
635
    m48t59_write(nvram, addr + max - 1, '\0');
636
}
637

    
638
int NVRAM_get_string (m48t59_t *nvram, uint8_t *dst, uint16_t addr, int max)
639
{
640
    int i;
641

    
642
    memset(dst, 0, max);
643
    for (i = 0; i < max; i++) {
644
        dst[i] = NVRAM_get_byte(nvram, addr + i);
645
        if (dst[i] == '\0')
646
            break;
647
    }
648

    
649
    return i;
650
}
651

    
652
static uint16_t NVRAM_crc_update (uint16_t prev, uint16_t value)
653
{
654
    uint16_t tmp;
655
    uint16_t pd, pd1, pd2;
656

    
657
    tmp = prev >> 8;
658
    pd = prev ^ value;
659
    pd1 = pd & 0x000F;
660
    pd2 = ((pd >> 4) & 0x000F) ^ pd1;
661
    tmp ^= (pd1 << 3) | (pd1 << 8);
662
    tmp ^= pd2 | (pd2 << 7) | (pd2 << 12);
663

    
664
    return tmp;
665
}
666

    
667
uint16_t NVRAM_compute_crc (m48t59_t *nvram, uint32_t start, uint32_t count)
668
{
669
    uint32_t i;
670
    uint16_t crc = 0xFFFF;
671
    int odd;
672

    
673
    odd = count & 1;
674
    count &= ~1;
675
    for (i = 0; i != count; i++) {
676
        crc = NVRAM_crc_update(crc, NVRAM_get_word(nvram, start + i));
677
    }
678
    if (odd) {
679
        crc = NVRAM_crc_update(crc, NVRAM_get_byte(nvram, start + i) << 8);
680
    }
681

    
682
    return crc;
683
}
684

    
685
#define CMDLINE_ADDR 0x017ff000
686

    
687
int PPC_NVRAM_set_params (m48t59_t *nvram, uint16_t NVRAM_size,
688
                          const unsigned char *arch,
689
                          uint32_t RAM_size, int boot_device,
690
                          uint32_t kernel_image, uint32_t kernel_size,
691
                          const char *cmdline,
692
                          uint32_t initrd_image, uint32_t initrd_size,
693
                          uint32_t NVRAM_image,
694
                          int width, int height, int depth)
695
{
696
    uint16_t crc;
697

    
698
    /* Set parameters for Open Hack'Ware BIOS */
699
    NVRAM_set_string(nvram, 0x00, "QEMU_BIOS", 16);
700
    NVRAM_set_lword(nvram,  0x10, 0x00000002); /* structure v2 */
701
    NVRAM_set_word(nvram,   0x14, NVRAM_size);
702
    NVRAM_set_string(nvram, 0x20, arch, 16);
703
    NVRAM_set_lword(nvram,  0x30, RAM_size);
704
    NVRAM_set_byte(nvram,   0x34, boot_device);
705
    NVRAM_set_lword(nvram,  0x38, kernel_image);
706
    NVRAM_set_lword(nvram,  0x3C, kernel_size);
707
    if (cmdline) {
708
        /* XXX: put the cmdline in NVRAM too ? */
709
        strcpy(phys_ram_base + CMDLINE_ADDR, cmdline);
710
        NVRAM_set_lword(nvram,  0x40, CMDLINE_ADDR);
711
        NVRAM_set_lword(nvram,  0x44, strlen(cmdline));
712
    } else {
713
        NVRAM_set_lword(nvram,  0x40, 0);
714
        NVRAM_set_lword(nvram,  0x44, 0);
715
    }
716
    NVRAM_set_lword(nvram,  0x48, initrd_image);
717
    NVRAM_set_lword(nvram,  0x4C, initrd_size);
718
    NVRAM_set_lword(nvram,  0x50, NVRAM_image);
719

    
720
    NVRAM_set_word(nvram,   0x54, width);
721
    NVRAM_set_word(nvram,   0x56, height);
722
    NVRAM_set_word(nvram,   0x58, depth);
723
    crc = NVRAM_compute_crc(nvram, 0x00, 0xF8);
724
    NVRAM_set_word(nvram,  0xFC, crc);
725

    
726
    return 0;
727
}