Commit 58a7d32872badb7b94d2010e0100a25443e0ef77
1 parent
47c4d8f0
Code provision for hypervisor timers resources,
as described in PowerPC 2.04 specification. git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3264 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
106 additions
and
7 deletions
hw/ppc.c
... | ... | @@ -412,6 +412,13 @@ struct ppc_tb_t { |
412 | 412 | /* Decrementer management */ |
413 | 413 | uint64_t decr_next; /* Tick for next decr interrupt */ |
414 | 414 | struct QEMUTimer *decr_timer; |
415 | +#if defined(TARGET_PPC64H) | |
416 | + /* Hypervisor decrementer management */ | |
417 | + uint64_t hdecr_next; /* Tick for next hdecr interrupt */ | |
418 | + struct QEMUTimer *hdecr_timer; | |
419 | + uint64_t purr_load; | |
420 | + uint64_t purr_start; | |
421 | +#endif | |
415 | 422 | void *opaque; |
416 | 423 | }; |
417 | 424 | |
... | ... | @@ -489,7 +496,7 @@ void cpu_ppc_store_tbl (CPUState *env, uint32_t value) |
489 | 496 | ((uint64_t)cpu_ppc_load_tbu(env) << 32) | value); |
490 | 497 | } |
491 | 498 | |
492 | -uint32_t cpu_ppc_load_decr (CPUState *env) | |
499 | +static inline uint32_t _cpu_ppc_load_decr (CPUState *env, uint64_t *next) | |
493 | 500 | { |
494 | 501 | ppc_tb_t *tb_env = env->tb_env; |
495 | 502 | uint32_t decr; |
... | ... | @@ -509,6 +516,32 @@ uint32_t cpu_ppc_load_decr (CPUState *env) |
509 | 516 | return decr; |
510 | 517 | } |
511 | 518 | |
519 | +uint32_t cpu_ppc_load_decr (CPUState *env) | |
520 | +{ | |
521 | + ppc_tb_t *tb_env = env->tb_env; | |
522 | + | |
523 | + return _cpu_ppc_load_decr(env, &tb_env->decr_next); | |
524 | +} | |
525 | + | |
526 | +#if defined(TARGET_PPC64H) | |
527 | +uint32_t cpu_ppc_load_hdecr (CPUState *env) | |
528 | +{ | |
529 | + ppc_tb_t *tb_env = env->tb_env; | |
530 | + | |
531 | + return _cpu_ppc_load_decr(env, &tb_env->hdecr_next); | |
532 | +} | |
533 | + | |
534 | +uint64_t cpu_ppc_load_purr (CPUState *env) | |
535 | +{ | |
536 | + ppc_tb_t *tb_env = env->tb_env; | |
537 | + uint64_t diff; | |
538 | + | |
539 | + diff = qemu_get_clock(vm_clock) - tb_env->purr_start; | |
540 | + | |
541 | + return tb_env->purr_load + muldiv64(diff, tb_env->tb_freq, ticks_per_sec); | |
542 | +} | |
543 | +#endif /* defined(TARGET_PPC64H) */ | |
544 | + | |
512 | 545 | /* When decrementer expires, |
513 | 546 | * all we need to do is generate or queue a CPU exception |
514 | 547 | */ |
... | ... | @@ -523,8 +556,22 @@ static inline void cpu_ppc_decr_excp (CPUState *env) |
523 | 556 | ppc_set_irq(env, PPC_INTERRUPT_DECR, 1); |
524 | 557 | } |
525 | 558 | |
526 | -static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr, | |
527 | - uint32_t value, int is_excp) | |
559 | +static inline void cpu_ppc_hdecr_excp (CPUState *env) | |
560 | +{ | |
561 | + /* Raise it */ | |
562 | +#ifdef PPC_DEBUG_TB | |
563 | + if (loglevel != 0) { | |
564 | + fprintf(logfile, "raise decrementer exception\n"); | |
565 | + } | |
566 | +#endif | |
567 | + ppc_set_irq(env, PPC_INTERRUPT_HDECR, 1); | |
568 | +} | |
569 | + | |
570 | +static void __cpu_ppc_store_decr (CPUState *env, uint64_t *nextp, | |
571 | + struct QEMUTimer *timer, | |
572 | + void (*raise_excp)(CPUState *), | |
573 | + uint32_t decr, uint32_t value, | |
574 | + int is_excp) | |
528 | 575 | { |
529 | 576 | ppc_tb_t *tb_env = env->tb_env; |
530 | 577 | uint64_t now, next; |
... | ... | @@ -537,17 +584,27 @@ static void _cpu_ppc_store_decr (CPUState *env, uint32_t decr, |
537 | 584 | now = qemu_get_clock(vm_clock); |
538 | 585 | next = now + muldiv64(value, ticks_per_sec, tb_env->tb_freq); |
539 | 586 | if (is_excp) |
540 | - next += tb_env->decr_next - now; | |
587 | + next += *nextp - now; | |
541 | 588 | if (next == now) |
542 | 589 | next++; |
543 | - tb_env->decr_next = next; | |
590 | + *nextp = next; | |
544 | 591 | /* Adjust timer */ |
545 | - qemu_mod_timer(tb_env->decr_timer, next); | |
592 | + qemu_mod_timer(timer, next); | |
546 | 593 | /* If we set a negative value and the decrementer was positive, |
547 | 594 | * raise an exception. |
548 | 595 | */ |
549 | 596 | if ((value & 0x80000000) && !(decr & 0x80000000)) |
550 | - cpu_ppc_decr_excp(env); | |
597 | + (*raise_excp)(env); | |
598 | +} | |
599 | + | |
600 | + | |
601 | +static inline void _cpu_ppc_store_decr (CPUState *env, uint32_t decr, | |
602 | + uint32_t value, int is_excp) | |
603 | +{ | |
604 | + ppc_tb_t *tb_env = env->tb_env; | |
605 | + | |
606 | + __cpu_ppc_store_decr(env, &tb_env->decr_next, tb_env->decr_timer, | |
607 | + &cpu_ppc_decr_excp, decr, value, is_excp); | |
551 | 608 | } |
552 | 609 | |
553 | 610 | void cpu_ppc_store_decr (CPUState *env, uint32_t value) |
... | ... | @@ -560,6 +617,35 @@ static void cpu_ppc_decr_cb (void *opaque) |
560 | 617 | _cpu_ppc_store_decr(opaque, 0x00000000, 0xFFFFFFFF, 1); |
561 | 618 | } |
562 | 619 | |
620 | +#if defined(TARGET_PPC64H) | |
621 | +static inline void _cpu_ppc_store_hdecr (CPUState *env, uint32_t hdecr, | |
622 | + uint32_t value, int is_excp) | |
623 | +{ | |
624 | + ppc_tb_t *tb_env = env->tb_env; | |
625 | + | |
626 | + __cpu_ppc_store_decr(env, &tb_env->hdecr_next, tb_env->hdecr_timer, | |
627 | + &cpu_ppc_hdecr_excp, hdecr, value, is_excp); | |
628 | +} | |
629 | + | |
630 | +void cpu_ppc_store_hdecr (CPUState *env, uint32_t value) | |
631 | +{ | |
632 | + _cpu_ppc_store_hdecr(env, cpu_ppc_load_hdecr(env), value, 0); | |
633 | +} | |
634 | + | |
635 | +static void cpu_ppc_hdecr_cb (void *opaque) | |
636 | +{ | |
637 | + _cpu_ppc_store_hdecr(opaque, 0x00000000, 0xFFFFFFFF, 1); | |
638 | +} | |
639 | + | |
640 | +void cpu_ppc_store_purr (CPUState *env, uint64_t value) | |
641 | +{ | |
642 | + ppc_tb_t *tb_env = env->tb_env; | |
643 | + | |
644 | + tb_env->purr_load = value; | |
645 | + tb_env->purr_start = qemu_get_clock(vm_clock); | |
646 | +} | |
647 | +#endif /* defined(TARGET_PPC64H) */ | |
648 | + | |
563 | 649 | static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) |
564 | 650 | { |
565 | 651 | CPUState *env = opaque; |
... | ... | @@ -571,6 +657,10 @@ static void cpu_ppc_set_tb_clk (void *opaque, uint32_t freq) |
571 | 657 | * it's not ready to handle it... |
572 | 658 | */ |
573 | 659 | _cpu_ppc_store_decr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0); |
660 | +#if defined(TARGET_PPC64H) | |
661 | + _cpu_ppc_store_hdecr(env, 0xFFFFFFFF, 0xFFFFFFFF, 0); | |
662 | + cpu_ppc_store_purr(env, 0x0000000000000000ULL); | |
663 | +#endif /* defined(TARGET_PPC64H) */ | |
574 | 664 | } |
575 | 665 | |
576 | 666 | /* Set up (once) timebase frequency (in Hz) */ |
... | ... | @@ -584,6 +674,9 @@ clk_setup_cb cpu_ppc_tb_init (CPUState *env, uint32_t freq) |
584 | 674 | env->tb_env = tb_env; |
585 | 675 | /* Create new timer */ |
586 | 676 | tb_env->decr_timer = qemu_new_timer(vm_clock, &cpu_ppc_decr_cb, env); |
677 | +#if defined(TARGET_PPC64H) | |
678 | + tb_env->hdecr_timer = qemu_new_timer(vm_clock, &cpu_ppc_hdecr_cb, env); | |
679 | +#endif /* defined(TARGET_PPC64H) */ | |
587 | 680 | cpu_ppc_set_tb_clk(env, freq); |
588 | 681 | |
589 | 682 | return &cpu_ppc_set_tb_clk; | ... | ... |
target-ppc/cpu.h
... | ... | @@ -627,6 +627,12 @@ void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value); |
627 | 627 | void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value); |
628 | 628 | uint32_t cpu_ppc_load_decr (CPUPPCState *env); |
629 | 629 | void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value); |
630 | +#if defined(TARGET_PPC64H) | |
631 | +uint32_t cpu_ppc_load_hdecr (CPUPPCState *env); | |
632 | +void cpu_ppc_store_hdecr (CPUPPCState *env, uint32_t value); | |
633 | +uint64_t cpu_ppc_load_purr (CPUPPCState *env); | |
634 | +void cpu_ppc_store_purr (CPUPPCState *env, uint64_t value); | |
635 | +#endif | |
630 | 636 | uint32_t cpu_ppc601_load_rtcl (CPUPPCState *env); |
631 | 637 | uint32_t cpu_ppc601_load_rtcu (CPUPPCState *env); |
632 | 638 | #if !defined(CONFIG_USER_ONLY) | ... | ... |