Commit a8dea12f453851d68c968d23faff2f1b9205d811
1 parent
1d0a48fb
Merge PowerPC 405 MMU model.
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@2554 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
2 changed files
with
243 additions
and
50 deletions
target-ppc/helper.c
| ... | ... | @@ -549,8 +549,6 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, |
| 549 | 549 | if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_6xx)) { |
| 550 | 550 | /* Software TLB search */ |
| 551 | 551 | ret = ppc6xx_tlb_check(env, ctx, eaddr, rw, type); |
| 552 | - } else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) { | |
| 553 | - /* XXX: TODO */ | |
| 554 | 552 | } else { |
| 555 | 553 | #if defined (DEBUG_MMU) |
| 556 | 554 | if (loglevel > 0) { |
| ... | ... | @@ -632,6 +630,115 @@ static int get_segment (CPUState *env, mmu_ctx_t *ctx, |
| 632 | 630 | return ret; |
| 633 | 631 | } |
| 634 | 632 | |
| 633 | +int mmu4xx_get_physical_address (CPUState *env, mmu_ctx_t *ctx, | |
| 634 | + uint32_t address, int rw, int access_type) | |
| 635 | +{ | |
| 636 | + ppcemb_tlb_t *tlb; | |
| 637 | + target_phys_addr_t raddr; | |
| 638 | + target_ulong mask; | |
| 639 | + int i, ret, zsel, zpr; | |
| 640 | + | |
| 641 | + ret = -6; | |
| 642 | + for (i = 0; i < env->nb_tlb; i++) { | |
| 643 | + tlb = &env->tlb[i].tlbe; | |
| 644 | + /* Check valid flag */ | |
| 645 | + if (!(tlb->prot & PAGE_VALID)) { | |
| 646 | + if (loglevel) | |
| 647 | + fprintf(logfile, "%s: TLB %d not valid\n", __func__, i); | |
| 648 | + continue; | |
| 649 | + } | |
| 650 | + mask = ~(tlb->size - 1); | |
| 651 | + if (loglevel) { | |
| 652 | + fprintf(logfile, "%s: TLB %d address %08x PID %04x <=> " | |
| 653 | + "%08x %08x %04x\n", | |
| 654 | + __func__, i, address, env->spr[SPR_40x_PID], | |
| 655 | + tlb->EPN, mask, tlb->PID); | |
| 656 | + } | |
| 657 | + /* Check PID */ | |
| 658 | + if (tlb->PID != 0 && tlb->PID != env->spr[SPR_40x_PID]) | |
| 659 | + continue; | |
| 660 | + /* Check effective address */ | |
| 661 | + if ((address & mask) != tlb->EPN) | |
| 662 | + continue; | |
| 663 | + raddr = (tlb->RPN & mask) | (address & ~mask); | |
| 664 | + zsel = (tlb->attr >> 4) & 0xF; | |
| 665 | + zpr = (env->spr[SPR_40x_ZPR] >> (28 - (2 * zsel))) & 0x3; | |
| 666 | + if (loglevel) { | |
| 667 | + fprintf(logfile, "%s: TLB %d zsel %d zpr %d rw %d attr %08x\n", | |
| 668 | + __func__, i, zsel, zpr, rw, tlb->attr); | |
| 669 | + } | |
| 670 | + if (access_type == ACCESS_CODE) { | |
| 671 | + /* Check execute enable bit */ | |
| 672 | + switch (zpr) { | |
| 673 | + case 0x0: | |
| 674 | + if (msr_pr) { | |
| 675 | + ret = -3; | |
| 676 | + ctx->prot = 0; | |
| 677 | + break; | |
| 678 | + } | |
| 679 | + /* No break here */ | |
| 680 | + case 0x1: | |
| 681 | + case 0x2: | |
| 682 | + /* Check from TLB entry */ | |
| 683 | + if (!(tlb->prot & PAGE_EXEC)) { | |
| 684 | + ret = -3; | |
| 685 | + } else { | |
| 686 | + if (tlb->prot & PAGE_WRITE) | |
| 687 | + ctx->prot = PAGE_READ | PAGE_WRITE; | |
| 688 | + else | |
| 689 | + ctx->prot = PAGE_READ; | |
| 690 | + ret = 0; | |
| 691 | + } | |
| 692 | + break; | |
| 693 | + case 0x3: | |
| 694 | + /* All accesses granted */ | |
| 695 | + ret = 0; | |
| 696 | + ctx->prot = PAGE_READ | PAGE_WRITE; | |
| 697 | + break; | |
| 698 | + } | |
| 699 | + } else { | |
| 700 | + switch (zpr) { | |
| 701 | + case 0x0: | |
| 702 | + if (msr_pr) { | |
| 703 | + ret = -2; | |
| 704 | + ctx->prot = 0; | |
| 705 | + break; | |
| 706 | + } | |
| 707 | + /* No break here */ | |
| 708 | + case 0x1: | |
| 709 | + case 0x2: | |
| 710 | + /* Check from TLB entry */ | |
| 711 | + /* Check write protection bit */ | |
| 712 | + if (rw && !(tlb->prot & PAGE_WRITE)) { | |
| 713 | + ret = -2; | |
| 714 | + } else { | |
| 715 | + ret = 2; | |
| 716 | + if (tlb->prot & PAGE_WRITE) | |
| 717 | + ctx->prot = PAGE_READ | PAGE_WRITE; | |
| 718 | + else | |
| 719 | + ctx->prot = PAGE_READ; | |
| 720 | + } | |
| 721 | + break; | |
| 722 | + case 0x3: | |
| 723 | + /* All accesses granted */ | |
| 724 | + ret = 2; | |
| 725 | + ctx->prot = PAGE_READ | PAGE_WRITE; | |
| 726 | + break; | |
| 727 | + } | |
| 728 | + } | |
| 729 | + if (ret >= 0) { | |
| 730 | + ctx->raddr = raddr; | |
| 731 | + if (loglevel) { | |
| 732 | + fprintf(logfile, "%s: access granted " ADDRX " => " REGX | |
| 733 | + " %d\n", __func__, address, ctx->raddr, ctx->prot); | |
| 734 | + } | |
| 735 | + return i; | |
| 736 | + } | |
| 737 | + } | |
| 738 | + | |
| 739 | + return ret; | |
| 740 | +} | |
| 741 | + | |
| 635 | 742 | static int check_physical (CPUState *env, mmu_ctx_t *ctx, |
| 636 | 743 | target_ulong eaddr, int rw) |
| 637 | 744 | { |
| ... | ... | @@ -682,13 +789,26 @@ int get_physical_address (CPUState *env, mmu_ctx_t *ctx, target_ulong eaddr, |
| 682 | 789 | /* No address translation */ |
| 683 | 790 | ret = check_physical(env, ctx, eaddr, rw); |
| 684 | 791 | } else { |
| 685 | - /* Try to find a BAT */ | |
| 686 | - ret = -1; | |
| 687 | - if (check_BATs) | |
| 688 | - ret = get_bat(env, ctx, eaddr, rw, access_type); | |
| 689 | - if (ret < 0) { | |
| 690 | - /* We didn't match any BAT entry */ | |
| 691 | - ret = get_segment(env, ctx, eaddr, rw, access_type); | |
| 792 | + switch (PPC_MMU(env)) { | |
| 793 | + case PPC_FLAGS_MMU_32B: | |
| 794 | + case PPC_FLAGS_MMU_SOFT_6xx: | |
| 795 | + /* Try to find a BAT */ | |
| 796 | + ret = -1; | |
| 797 | + if (check_BATs) | |
| 798 | + ret = get_bat(env, ctx, eaddr, rw, access_type); | |
| 799 | + if (ret < 0) { | |
| 800 | + /* We didn't match any BAT entry */ | |
| 801 | + ret = get_segment(env, ctx, eaddr, rw, access_type); | |
| 802 | + } | |
| 803 | + break; | |
| 804 | + case PPC_FLAGS_MMU_SOFT_4xx: | |
| 805 | + ret = mmu4xx_get_physical_address(env, ctx, eaddr, | |
| 806 | + rw, access_type); | |
| 807 | + break; | |
| 808 | + default: | |
| 809 | + /* XXX: TODO */ | |
| 810 | + cpu_abort(env, "MMU model not implemented\n"); | |
| 811 | + return -1; | |
| 692 | 812 | } |
| 693 | 813 | } |
| 694 | 814 | #if 0 |
| ... | ... | @@ -753,7 +873,10 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, |
| 753 | 873 | error_code = 1 << 18; |
| 754 | 874 | goto tlb_miss; |
| 755 | 875 | } else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) { |
| 756 | - /* XXX: TODO */ | |
| 876 | + exception = EXCP_40x_ITLBMISS; | |
| 877 | + error_code = 0; | |
| 878 | + env->spr[SPR_40x_DEAR] = address; | |
| 879 | + env->spr[SPR_40x_ESR] = 0x00000000; | |
| 757 | 880 | } else { |
| 758 | 881 | error_code = 0x40000000; |
| 759 | 882 | } |
| ... | ... | @@ -799,7 +922,13 @@ int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw, |
| 799 | 922 | /* Do not alter DAR nor DSISR */ |
| 800 | 923 | goto out; |
| 801 | 924 | } else if (unlikely(PPC_MMU(env) == PPC_FLAGS_MMU_SOFT_4xx)) { |
| 802 | - /* XXX: TODO */ | |
| 925 | + exception = EXCP_40x_DTLBMISS; | |
| 926 | + error_code = 0; | |
| 927 | + env->spr[SPR_40x_DEAR] = address; | |
| 928 | + if (rw) | |
| 929 | + env->spr[SPR_40x_ESR] = 0x00800000; | |
| 930 | + else | |
| 931 | + env->spr[SPR_40x_ESR] = 0x00000000; | |
| 803 | 932 | } else { |
| 804 | 933 | error_code = 0x40000000; |
| 805 | 934 | } |
| ... | ... | @@ -1518,9 +1647,7 @@ void do_interrupt (CPUState *env) |
| 1518 | 1647 | switch (PPC_EXCP(env)) { |
| 1519 | 1648 | case PPC_FLAGS_EXCP_40x: |
| 1520 | 1649 | /* DTLBMISS on 4xx */ |
| 1521 | - /* XXX: TODO */ | |
| 1522 | - cpu_abort(env, | |
| 1523 | - "40x DTLBMISS exception is not implemented yet !\n"); | |
| 1650 | + msr &= ~0xFFFF0000; | |
| 1524 | 1651 | goto store_next; |
| 1525 | 1652 | case PPC_FLAGS_EXCP_602: |
| 1526 | 1653 | case PPC_FLAGS_EXCP_603: |
| ... | ... | @@ -1538,9 +1665,7 @@ void do_interrupt (CPUState *env) |
| 1538 | 1665 | switch (PPC_EXCP(env)) { |
| 1539 | 1666 | case PPC_FLAGS_EXCP_40x: |
| 1540 | 1667 | /* ITLBMISS on 4xx */ |
| 1541 | - /* XXX: TODO */ | |
| 1542 | - cpu_abort(env, | |
| 1543 | - "40x ITLBMISS exception is not implemented yet !\n"); | |
| 1668 | + msr &= ~0xFFFF0000; | |
| 1544 | 1669 | goto store_next; |
| 1545 | 1670 | case PPC_FLAGS_EXCP_602: |
| 1546 | 1671 | case PPC_FLAGS_EXCP_603: | ... | ... |
target-ppc/op_helper.c
| ... | ... | @@ -2365,65 +2365,139 @@ void do_load_6xx_tlb (int is_code) |
| 2365 | 2365 | way, is_code, CMP, RPN); |
| 2366 | 2366 | } |
| 2367 | 2367 | |
| 2368 | +static target_ulong booke_tlb_to_page_size (int size) | |
| 2369 | +{ | |
| 2370 | + return 1024 << (2 * size); | |
| 2371 | +} | |
| 2372 | + | |
| 2373 | +static int booke_page_size_to_tlb (target_ulong page_size) | |
| 2374 | +{ | |
| 2375 | + int size; | |
| 2376 | + | |
| 2377 | + switch (page_size) { | |
| 2378 | + case 0x00000400UL: | |
| 2379 | + size = 0x0; | |
| 2380 | + break; | |
| 2381 | + case 0x00001000UL: | |
| 2382 | + size = 0x1; | |
| 2383 | + break; | |
| 2384 | + case 0x00004000UL: | |
| 2385 | + size = 0x2; | |
| 2386 | + break; | |
| 2387 | + case 0x00010000UL: | |
| 2388 | + size = 0x3; | |
| 2389 | + break; | |
| 2390 | + case 0x00040000UL: | |
| 2391 | + size = 0x4; | |
| 2392 | + break; | |
| 2393 | + case 0x00100000UL: | |
| 2394 | + size = 0x5; | |
| 2395 | + break; | |
| 2396 | + case 0x00400000UL: | |
| 2397 | + size = 0x6; | |
| 2398 | + break; | |
| 2399 | + case 0x01000000UL: | |
| 2400 | + size = 0x7; | |
| 2401 | + break; | |
| 2402 | + case 0x04000000UL: | |
| 2403 | + size = 0x8; | |
| 2404 | + break; | |
| 2405 | + case 0x10000000UL: | |
| 2406 | + size = 0x9; | |
| 2407 | + break; | |
| 2408 | + case 0x40000000UL: | |
| 2409 | + size = 0xA; | |
| 2410 | + break; | |
| 2411 | +#if defined (TARGET_PPC64) | |
| 2412 | + case 0x000100000000ULL: | |
| 2413 | + size = 0xB; | |
| 2414 | + break; | |
| 2415 | + case 0x000400000000ULL: | |
| 2416 | + size = 0xC; | |
| 2417 | + break; | |
| 2418 | + case 0x001000000000ULL: | |
| 2419 | + size = 0xD; | |
| 2420 | + break; | |
| 2421 | + case 0x004000000000ULL: | |
| 2422 | + size = 0xE; | |
| 2423 | + break; | |
| 2424 | + case 0x010000000000ULL: | |
| 2425 | + size = 0xF; | |
| 2426 | + break; | |
| 2427 | +#endif | |
| 2428 | + default: | |
| 2429 | + size = -1; | |
| 2430 | + break; | |
| 2431 | + } | |
| 2432 | + | |
| 2433 | + return size; | |
| 2434 | +} | |
| 2435 | + | |
| 2368 | 2436 | /* Helpers for 4xx TLB management */ |
| 2369 | 2437 | void do_4xx_tlbia (void) |
| 2370 | 2438 | { |
| 2371 | -#if 0 | |
| 2372 | - ppc_tlb_t *tlb; | |
| 2373 | - target_ulong page, end; | |
| 2439 | + ppcemb_tlb_t *tlb; | |
| 2374 | 2440 | int i; |
| 2375 | 2441 | |
| 2376 | 2442 | for (i = 0; i < 64; i++) { |
| 2377 | - tlb = &env->tlb[i]; | |
| 2443 | + tlb = &env->tlb[i].tlbe; | |
| 2378 | 2444 | if (tlb->prot & PAGE_VALID) { |
| 2445 | +#if 0 | |
| 2379 | 2446 | end = tlb->EPN + tlb->size; |
| 2380 | 2447 | for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) |
| 2381 | 2448 | tlb_flush_page(env, page); |
| 2449 | +#endif | |
| 2382 | 2450 | tlb->prot &= ~PAGE_VALID; |
| 2383 | 2451 | } |
| 2384 | 2452 | } |
| 2385 | -#endif | |
| 2453 | + tlb_flush(env, 1); | |
| 2386 | 2454 | } |
| 2387 | 2455 | |
| 2388 | 2456 | void do_4xx_tlbre_lo (void) |
| 2389 | 2457 | { |
| 2390 | -#if 0 | |
| 2391 | - ppc_tlb_t *tlb; | |
| 2458 | + ppcemb_tlb_t *tlb; | |
| 2459 | + int size; | |
| 2392 | 2460 | |
| 2393 | 2461 | T0 &= 0x3F; |
| 2394 | - tlb = &env->tlb[T0]; | |
| 2395 | - T0 = tlb->stor[0]; | |
| 2396 | - env->spr[SPR_40x_PID] = tlb->pid; | |
| 2397 | -#endif | |
| 2462 | + tlb = &env->tlb[T0].tlbe; | |
| 2463 | + T0 = tlb->EPN; | |
| 2464 | + if (tlb->prot & PAGE_VALID) | |
| 2465 | + T0 |= 0x400; | |
| 2466 | + size = booke_page_size_to_tlb(tlb->size); | |
| 2467 | + if (size < 0 || size > 0x7) | |
| 2468 | + size = 1; | |
| 2469 | + T0 |= size << 7; | |
| 2470 | + env->spr[SPR_40x_PID] = tlb->PID; | |
| 2398 | 2471 | } |
| 2399 | 2472 | |
| 2400 | 2473 | void do_4xx_tlbre_hi (void) |
| 2401 | 2474 | { |
| 2402 | -#if 0 | |
| 2403 | - ppc_tlb_t *tlb; | |
| 2475 | + ppcemb_tlb_t *tlb; | |
| 2404 | 2476 | |
| 2405 | 2477 | T0 &= 0x3F; |
| 2406 | - tlb = &env->tlb[T0]; | |
| 2407 | - T0 = tlb->stor[1]; | |
| 2408 | -#endif | |
| 2478 | + tlb = &env->tlb[T0].tlbe; | |
| 2479 | + T0 = tlb->RPN; | |
| 2480 | + if (tlb->prot & PAGE_EXEC) | |
| 2481 | + T0 |= 0x200; | |
| 2482 | + if (tlb->prot & PAGE_WRITE) | |
| 2483 | + T0 |= 0x100; | |
| 2409 | 2484 | } |
| 2410 | 2485 | |
| 2411 | 2486 | static int tlb_4xx_search (target_ulong virtual) |
| 2412 | 2487 | { |
| 2413 | -#if 0 | |
| 2414 | - ppc_tlb_t *tlb; | |
| 2488 | + ppcemb_tlb_t *tlb; | |
| 2415 | 2489 | target_ulong base, mask; |
| 2416 | 2490 | int i, ret; |
| 2417 | 2491 | |
| 2418 | 2492 | /* Default return value is no match */ |
| 2419 | 2493 | ret = -1; |
| 2420 | 2494 | for (i = 0; i < 64; i++) { |
| 2421 | - tlb = &env->tlb[i]; | |
| 2495 | + tlb = &env->tlb[i].tlbe; | |
| 2422 | 2496 | /* Check TLB validity */ |
| 2423 | 2497 | if (!(tlb->prot & PAGE_VALID)) |
| 2424 | 2498 | continue; |
| 2425 | 2499 | /* Check TLB PID vs current PID */ |
| 2426 | - if (tlb->pid != 0 && tlb->pid != env->spr[SPR_40x_PID]) | |
| 2500 | + if (tlb->PID != 0 && tlb->PID != env->spr[SPR_40x_PID]) | |
| 2427 | 2501 | continue; |
| 2428 | 2502 | /* Check TLB address vs virtual address */ |
| 2429 | 2503 | base = tlb->EPN; |
| ... | ... | @@ -2435,9 +2509,6 @@ static int tlb_4xx_search (target_ulong virtual) |
| 2435 | 2509 | } |
| 2436 | 2510 | |
| 2437 | 2511 | return ret; |
| 2438 | -#else | |
| 2439 | - return -1; | |
| 2440 | -#endif | |
| 2441 | 2512 | } |
| 2442 | 2513 | |
| 2443 | 2514 | void do_4xx_tlbsx (void) |
| ... | ... | @@ -2457,47 +2528,44 @@ void do_4xx_tlbsx_ (void) |
| 2457 | 2528 | |
| 2458 | 2529 | void do_4xx_tlbwe_lo (void) |
| 2459 | 2530 | { |
| 2460 | -#if 0 | |
| 2461 | - ppc_tlb_t *tlb; | |
| 2531 | + ppcemb_tlb_t *tlb; | |
| 2462 | 2532 | target_ulong page, end; |
| 2463 | 2533 | |
| 2464 | 2534 | T0 &= 0x3F; |
| 2465 | - tlb = &env->tlb[T0]; | |
| 2535 | + tlb = &env->tlb[T0].tlbe; | |
| 2466 | 2536 | /* Invalidate previous TLB (if it's valid) */ |
| 2467 | 2537 | if (tlb->prot & PAGE_VALID) { |
| 2468 | 2538 | end = tlb->EPN + tlb->size; |
| 2469 | 2539 | for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) |
| 2470 | 2540 | tlb_flush_page(env, page); |
| 2471 | 2541 | } |
| 2472 | - tlb->size = 1024 << (2 * ((T1 >> 7) & 0x7)); | |
| 2542 | + tlb->size = booke_tlb_to_page_size((T1 >> 7) & 0x7); | |
| 2473 | 2543 | tlb->EPN = (T1 & 0xFFFFFC00) & ~(tlb->size - 1); |
| 2474 | 2544 | if (T1 & 0x400) |
| 2475 | 2545 | tlb->prot |= PAGE_VALID; |
| 2476 | 2546 | else |
| 2477 | 2547 | tlb->prot &= ~PAGE_VALID; |
| 2478 | - tlb->pid = env->spr[SPR_BOOKE_PID]; /* PID */ | |
| 2548 | + tlb->PID = env->spr[SPR_BOOKE_PID]; /* PID */ | |
| 2549 | + tlb->attr = T1 & 0xFF; | |
| 2479 | 2550 | /* Invalidate new TLB (if valid) */ |
| 2480 | 2551 | if (tlb->prot & PAGE_VALID) { |
| 2481 | 2552 | end = tlb->EPN + tlb->size; |
| 2482 | 2553 | for (page = tlb->EPN; page < end; page += TARGET_PAGE_SIZE) |
| 2483 | 2554 | tlb_flush_page(env, page); |
| 2484 | 2555 | } |
| 2485 | -#endif | |
| 2486 | 2556 | } |
| 2487 | 2557 | |
| 2488 | 2558 | void do_4xx_tlbwe_hi (void) |
| 2489 | 2559 | { |
| 2490 | -#if 0 | |
| 2491 | - ppc_tlb_t *tlb; | |
| 2560 | + ppcemb_tlb_t *tlb; | |
| 2492 | 2561 | |
| 2493 | 2562 | T0 &= 0x3F; |
| 2494 | - tlb = &env->tlb[T0]; | |
| 2563 | + tlb = &env->tlb[T0].tlbe; | |
| 2495 | 2564 | tlb->RPN = T1 & 0xFFFFFC00; |
| 2496 | 2565 | tlb->prot = PAGE_READ; |
| 2497 | 2566 | if (T1 & 0x200) |
| 2498 | 2567 | tlb->prot |= PAGE_EXEC; |
| 2499 | 2568 | if (T1 & 0x100) |
| 2500 | 2569 | tlb->prot |= PAGE_WRITE; |
| 2501 | -#endif | |
| 2502 | 2570 | } |
| 2503 | 2571 | #endif /* !CONFIG_USER_ONLY */ | ... | ... |