Commit 4b4f782c78f49c78c912e1e44c6a63fb7bf9aab4
1 parent
84b7b8e7
NX support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1677 c046a42c-6fe2-441c-8c8c-71466251a162
Showing
1 changed file
with
120 additions
and
40 deletions
target-i386/helper2.c
| @@ -127,7 +127,7 @@ CPUX86State *cpu_x86_init(void) | @@ -127,7 +127,7 @@ CPUX86State *cpu_x86_init(void) | ||
| 127 | /* currently not enabled for std i386 because not fully tested */ | 127 | /* currently not enabled for std i386 because not fully tested */ |
| 128 | env->cpuid_features |= CPUID_APIC; | 128 | env->cpuid_features |= CPUID_APIC; |
| 129 | env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF); | 129 | env->cpuid_ext2_features = (env->cpuid_features & 0x0183F3FF); |
| 130 | - env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL; | 130 | + env->cpuid_ext2_features |= CPUID_EXT2_LM | CPUID_EXT2_SYSCALL | CPUID_EXT2_NX; |
| 131 | env->cpuid_xlevel = 0x80000008; | 131 | env->cpuid_xlevel = 0x80000008; |
| 132 | 132 | ||
| 133 | /* these features are needed for Win64 and aren't fully implemented */ | 133 | /* these features are needed for Win64 and aren't fully implemented */ |
| @@ -576,6 +576,8 @@ target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) | @@ -576,6 +576,8 @@ target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) | ||
| 576 | 576 | ||
| 577 | #else | 577 | #else |
| 578 | 578 | ||
| 579 | +#define PHYS_ADDR_MASK 0xfffff000 | ||
| 580 | + | ||
| 579 | /* return value: | 581 | /* return value: |
| 580 | -1 = cannot handle fault | 582 | -1 = cannot handle fault |
| 581 | 0 = nothing more to do | 583 | 0 = nothing more to do |
| @@ -583,37 +585,38 @@ target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) | @@ -583,37 +585,38 @@ target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr) | ||
| 583 | 2 = soft MMU activation required for this block | 585 | 2 = soft MMU activation required for this block |
| 584 | */ | 586 | */ |
| 585 | int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | 587 | int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, |
| 586 | - int is_write, int is_user, int is_softmmu) | 588 | + int is_write1, int is_user, int is_softmmu) |
| 587 | { | 589 | { |
| 590 | + uint64_t ptep, pte; | ||
| 588 | uint32_t pdpe_addr, pde_addr, pte_addr; | 591 | uint32_t pdpe_addr, pde_addr, pte_addr; |
| 589 | - uint32_t pde, pte, ptep, pdpe; | ||
| 590 | - int error_code, is_dirty, prot, page_size, ret; | 592 | + int error_code, is_dirty, prot, page_size, ret, is_write; |
| 591 | unsigned long paddr, page_offset; | 593 | unsigned long paddr, page_offset; |
| 592 | target_ulong vaddr, virt_addr; | 594 | target_ulong vaddr, virt_addr; |
| 593 | 595 | ||
| 594 | #if defined(DEBUG_MMU) | 596 | #if defined(DEBUG_MMU) |
| 595 | printf("MMU fault: addr=" TARGET_FMT_lx " w=%d u=%d eip=" TARGET_FMT_lx "\n", | 597 | printf("MMU fault: addr=" TARGET_FMT_lx " w=%d u=%d eip=" TARGET_FMT_lx "\n", |
| 596 | - addr, is_write, is_user, env->eip); | 598 | + addr, is_write1, is_user, env->eip); |
| 597 | #endif | 599 | #endif |
| 598 | - is_write &= 1; | 600 | + is_write = is_write1 & 1; |
| 599 | 601 | ||
| 600 | if (!(env->cr[0] & CR0_PG_MASK)) { | 602 | if (!(env->cr[0] & CR0_PG_MASK)) { |
| 601 | pte = addr; | 603 | pte = addr; |
| 602 | virt_addr = addr & TARGET_PAGE_MASK; | 604 | virt_addr = addr & TARGET_PAGE_MASK; |
| 603 | - prot = PAGE_READ | PAGE_WRITE; | 605 | + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; |
| 604 | page_size = 4096; | 606 | page_size = 4096; |
| 605 | goto do_mapping; | 607 | goto do_mapping; |
| 606 | } | 608 | } |
| 607 | 609 | ||
| 608 | if (env->cr[4] & CR4_PAE_MASK) { | 610 | if (env->cr[4] & CR4_PAE_MASK) { |
| 611 | + uint64_t pde, pdpe; | ||
| 612 | + | ||
| 609 | /* XXX: we only use 32 bit physical addresses */ | 613 | /* XXX: we only use 32 bit physical addresses */ |
| 610 | #ifdef TARGET_X86_64 | 614 | #ifdef TARGET_X86_64 |
| 611 | if (env->hflags & HF_LMA_MASK) { | 615 | if (env->hflags & HF_LMA_MASK) { |
| 612 | - uint32_t pml4e_addr, pml4e; | 616 | + uint32_t pml4e_addr; |
| 617 | + uint64_t pml4e; | ||
| 613 | int32_t sext; | 618 | int32_t sext; |
| 614 | 619 | ||
| 615 | - /* XXX: handle user + rw rights */ | ||
| 616 | - /* XXX: handle NX flag */ | ||
| 617 | /* test virtual address sign extension */ | 620 | /* test virtual address sign extension */ |
| 618 | sext = (int64_t)addr >> 47; | 621 | sext = (int64_t)addr >> 47; |
| 619 | if (sext != 0 && sext != -1) { | 622 | if (sext != 0 && sext != -1) { |
| @@ -623,61 +626,134 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | @@ -623,61 +626,134 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | ||
| 623 | 626 | ||
| 624 | pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) & | 627 | pml4e_addr = ((env->cr[3] & ~0xfff) + (((addr >> 39) & 0x1ff) << 3)) & |
| 625 | env->a20_mask; | 628 | env->a20_mask; |
| 626 | - pml4e = ldl_phys(pml4e_addr); | 629 | + pml4e = ldq_phys(pml4e_addr); |
| 627 | if (!(pml4e & PG_PRESENT_MASK)) { | 630 | if (!(pml4e & PG_PRESENT_MASK)) { |
| 628 | error_code = 0; | 631 | error_code = 0; |
| 629 | goto do_fault; | 632 | goto do_fault; |
| 630 | } | 633 | } |
| 634 | + if (!(env->efer & MSR_EFER_NXE) && (pml4e & PG_NX_MASK)) { | ||
| 635 | + error_code = PG_ERROR_RSVD_MASK; | ||
| 636 | + goto do_fault; | ||
| 637 | + } | ||
| 631 | if (!(pml4e & PG_ACCESSED_MASK)) { | 638 | if (!(pml4e & PG_ACCESSED_MASK)) { |
| 632 | pml4e |= PG_ACCESSED_MASK; | 639 | pml4e |= PG_ACCESSED_MASK; |
| 633 | stl_phys_notdirty(pml4e_addr, pml4e); | 640 | stl_phys_notdirty(pml4e_addr, pml4e); |
| 634 | } | 641 | } |
| 635 | - | ||
| 636 | - pdpe_addr = ((pml4e & ~0xfff) + (((addr >> 30) & 0x1ff) << 3)) & | 642 | + ptep = pml4e ^ PG_NX_MASK; |
| 643 | + pdpe_addr = ((pml4e & PHYS_ADDR_MASK) + (((addr >> 30) & 0x1ff) << 3)) & | ||
| 637 | env->a20_mask; | 644 | env->a20_mask; |
| 638 | - pdpe = ldl_phys(pdpe_addr); | 645 | + pdpe = ldq_phys(pdpe_addr); |
| 639 | if (!(pdpe & PG_PRESENT_MASK)) { | 646 | if (!(pdpe & PG_PRESENT_MASK)) { |
| 640 | error_code = 0; | 647 | error_code = 0; |
| 641 | goto do_fault; | 648 | goto do_fault; |
| 642 | } | 649 | } |
| 650 | + if (!(env->efer & MSR_EFER_NXE) && (pdpe & PG_NX_MASK)) { | ||
| 651 | + error_code = PG_ERROR_RSVD_MASK; | ||
| 652 | + goto do_fault; | ||
| 653 | + } | ||
| 654 | + ptep &= pdpe ^ PG_NX_MASK; | ||
| 643 | if (!(pdpe & PG_ACCESSED_MASK)) { | 655 | if (!(pdpe & PG_ACCESSED_MASK)) { |
| 644 | pdpe |= PG_ACCESSED_MASK; | 656 | pdpe |= PG_ACCESSED_MASK; |
| 645 | stl_phys_notdirty(pdpe_addr, pdpe); | 657 | stl_phys_notdirty(pdpe_addr, pdpe); |
| 646 | } | 658 | } |
| 647 | - } else | 659 | + } else |
| 648 | #endif | 660 | #endif |
| 649 | { | 661 | { |
| 662 | + /* XXX: load them when cr3 is loaded ? */ | ||
| 650 | pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 30) << 3)) & | 663 | pdpe_addr = ((env->cr[3] & ~0x1f) + ((addr >> 30) << 3)) & |
| 651 | env->a20_mask; | 664 | env->a20_mask; |
| 652 | - pdpe = ldl_phys(pdpe_addr); | 665 | + pdpe = ldq_phys(pdpe_addr); |
| 653 | if (!(pdpe & PG_PRESENT_MASK)) { | 666 | if (!(pdpe & PG_PRESENT_MASK)) { |
| 654 | error_code = 0; | 667 | error_code = 0; |
| 655 | goto do_fault; | 668 | goto do_fault; |
| 656 | } | 669 | } |
| 670 | + ptep = PG_NX_MASK | PG_USER_MASK | PG_RW_MASK; | ||
| 657 | } | 671 | } |
| 658 | 672 | ||
| 659 | - pde_addr = ((pdpe & ~0xfff) + (((addr >> 21) & 0x1ff) << 3)) & | 673 | + pde_addr = ((pdpe & PHYS_ADDR_MASK) + (((addr >> 21) & 0x1ff) << 3)) & |
| 660 | env->a20_mask; | 674 | env->a20_mask; |
| 661 | - pde = ldl_phys(pde_addr); | 675 | + pde = ldq_phys(pde_addr); |
| 662 | if (!(pde & PG_PRESENT_MASK)) { | 676 | if (!(pde & PG_PRESENT_MASK)) { |
| 663 | error_code = 0; | 677 | error_code = 0; |
| 664 | goto do_fault; | 678 | goto do_fault; |
| 665 | } | 679 | } |
| 680 | + if (!(env->efer & MSR_EFER_NXE) && (pde & PG_NX_MASK)) { | ||
| 681 | + error_code = PG_ERROR_RSVD_MASK; | ||
| 682 | + goto do_fault; | ||
| 683 | + } | ||
| 684 | + ptep &= pde ^ PG_NX_MASK; | ||
| 666 | if (pde & PG_PSE_MASK) { | 685 | if (pde & PG_PSE_MASK) { |
| 667 | /* 2 MB page */ | 686 | /* 2 MB page */ |
| 668 | page_size = 2048 * 1024; | 687 | page_size = 2048 * 1024; |
| 669 | - goto handle_big_page; | 688 | + ptep ^= PG_NX_MASK; |
| 689 | + if ((ptep & PG_NX_MASK) && is_write1 == 2) | ||
| 690 | + goto do_fault_protect; | ||
| 691 | + if (is_user) { | ||
| 692 | + if (!(ptep & PG_USER_MASK)) | ||
| 693 | + goto do_fault_protect; | ||
| 694 | + if (is_write && !(ptep & PG_RW_MASK)) | ||
| 695 | + goto do_fault_protect; | ||
| 696 | + } else { | ||
| 697 | + if ((env->cr[0] & CR0_WP_MASK) && | ||
| 698 | + is_write && !(ptep & PG_RW_MASK)) | ||
| 699 | + goto do_fault_protect; | ||
| 700 | + } | ||
| 701 | + is_dirty = is_write && !(pde & PG_DIRTY_MASK); | ||
| 702 | + if (!(pde & PG_ACCESSED_MASK) || is_dirty) { | ||
| 703 | + pde |= PG_ACCESSED_MASK; | ||
| 704 | + if (is_dirty) | ||
| 705 | + pde |= PG_DIRTY_MASK; | ||
| 706 | + stl_phys_notdirty(pde_addr, pde); | ||
| 707 | + } | ||
| 708 | + /* align to page_size */ | ||
| 709 | + pte = pde & ((PHYS_ADDR_MASK & ~(page_size - 1)) | 0xfff); | ||
| 710 | + virt_addr = addr & ~(page_size - 1); | ||
| 670 | } else { | 711 | } else { |
| 671 | /* 4 KB page */ | 712 | /* 4 KB page */ |
| 672 | if (!(pde & PG_ACCESSED_MASK)) { | 713 | if (!(pde & PG_ACCESSED_MASK)) { |
| 673 | pde |= PG_ACCESSED_MASK; | 714 | pde |= PG_ACCESSED_MASK; |
| 674 | stl_phys_notdirty(pde_addr, pde); | 715 | stl_phys_notdirty(pde_addr, pde); |
| 675 | } | 716 | } |
| 676 | - pte_addr = ((pde & ~0xfff) + (((addr >> 12) & 0x1ff) << 3)) & | 717 | + pte_addr = ((pde & PHYS_ADDR_MASK) + (((addr >> 12) & 0x1ff) << 3)) & |
| 677 | env->a20_mask; | 718 | env->a20_mask; |
| 678 | - goto handle_4k_page; | 719 | + pte = ldq_phys(pte_addr); |
| 720 | + if (!(pte & PG_PRESENT_MASK)) { | ||
| 721 | + error_code = 0; | ||
| 722 | + goto do_fault; | ||
| 723 | + } | ||
| 724 | + if (!(env->efer & MSR_EFER_NXE) && (pte & PG_NX_MASK)) { | ||
| 725 | + error_code = PG_ERROR_RSVD_MASK; | ||
| 726 | + goto do_fault; | ||
| 727 | + } | ||
| 728 | + /* combine pde and pte nx, user and rw protections */ | ||
| 729 | + ptep &= pte ^ PG_NX_MASK; | ||
| 730 | + ptep ^= PG_NX_MASK; | ||
| 731 | + if ((ptep & PG_NX_MASK) && is_write1 == 2) | ||
| 732 | + goto do_fault_protect; | ||
| 733 | + if (is_user) { | ||
| 734 | + if (!(ptep & PG_USER_MASK)) | ||
| 735 | + goto do_fault_protect; | ||
| 736 | + if (is_write && !(ptep & PG_RW_MASK)) | ||
| 737 | + goto do_fault_protect; | ||
| 738 | + } else { | ||
| 739 | + if ((env->cr[0] & CR0_WP_MASK) && | ||
| 740 | + is_write && !(ptep & PG_RW_MASK)) | ||
| 741 | + goto do_fault_protect; | ||
| 742 | + } | ||
| 743 | + is_dirty = is_write && !(pte & PG_DIRTY_MASK); | ||
| 744 | + if (!(pte & PG_ACCESSED_MASK) || is_dirty) { | ||
| 745 | + pte |= PG_ACCESSED_MASK; | ||
| 746 | + if (is_dirty) | ||
| 747 | + pte |= PG_DIRTY_MASK; | ||
| 748 | + stl_phys_notdirty(pte_addr, pte); | ||
| 749 | + } | ||
| 750 | + page_size = 4096; | ||
| 751 | + virt_addr = addr & ~0xfff; | ||
| 752 | + pte = pte & (PHYS_ADDR_MASK | 0xfff); | ||
| 679 | } | 753 | } |
| 680 | } else { | 754 | } else { |
| 755 | + uint32_t pde; | ||
| 756 | + | ||
| 681 | /* page directory entry */ | 757 | /* page directory entry */ |
| 682 | pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & ~3)) & | 758 | pde_addr = ((env->cr[3] & ~0xfff) + ((addr >> 20) & ~3)) & |
| 683 | env->a20_mask; | 759 | env->a20_mask; |
| @@ -689,7 +765,6 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | @@ -689,7 +765,6 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | ||
| 689 | /* if PSE bit is set, then we use a 4MB page */ | 765 | /* if PSE bit is set, then we use a 4MB page */ |
| 690 | if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { | 766 | if ((pde & PG_PSE_MASK) && (env->cr[4] & CR4_PSE_MASK)) { |
| 691 | page_size = 4096 * 1024; | 767 | page_size = 4096 * 1024; |
| 692 | - handle_big_page: | ||
| 693 | if (is_user) { | 768 | if (is_user) { |
| 694 | if (!(pde & PG_USER_MASK)) | 769 | if (!(pde & PG_USER_MASK)) |
| 695 | goto do_fault_protect; | 770 | goto do_fault_protect; |
| @@ -720,7 +795,6 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | @@ -720,7 +795,6 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | ||
| 720 | /* page directory entry */ | 795 | /* page directory entry */ |
| 721 | pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & | 796 | pte_addr = ((pde & ~0xfff) + ((addr >> 10) & 0xffc)) & |
| 722 | env->a20_mask; | 797 | env->a20_mask; |
| 723 | - handle_4k_page: | ||
| 724 | pte = ldl_phys(pte_addr); | 798 | pte = ldl_phys(pte_addr); |
| 725 | if (!(pte & PG_PRESENT_MASK)) { | 799 | if (!(pte & PG_PRESENT_MASK)) { |
| 726 | error_code = 0; | 800 | error_code = 0; |
| @@ -748,20 +822,21 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | @@ -748,20 +822,21 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | ||
| 748 | page_size = 4096; | 822 | page_size = 4096; |
| 749 | virt_addr = addr & ~0xfff; | 823 | virt_addr = addr & ~0xfff; |
| 750 | } | 824 | } |
| 751 | - | ||
| 752 | - /* the page can be put in the TLB */ | ||
| 753 | - prot = PAGE_READ; | ||
| 754 | - if (pte & PG_DIRTY_MASK) { | ||
| 755 | - /* only set write access if already dirty... otherwise wait | ||
| 756 | - for dirty access */ | ||
| 757 | - if (is_user) { | ||
| 758 | - if (ptep & PG_RW_MASK) | ||
| 759 | - prot |= PAGE_WRITE; | ||
| 760 | - } else { | ||
| 761 | - if (!(env->cr[0] & CR0_WP_MASK) || | ||
| 762 | - (ptep & PG_RW_MASK)) | ||
| 763 | - prot |= PAGE_WRITE; | ||
| 764 | - } | 825 | + } |
| 826 | + /* the page can be put in the TLB */ | ||
| 827 | + prot = PAGE_READ; | ||
| 828 | + if (!(ptep & PG_NX_MASK)) | ||
| 829 | + prot |= PAGE_EXEC; | ||
| 830 | + if (pte & PG_DIRTY_MASK) { | ||
| 831 | + /* only set write access if already dirty... otherwise wait | ||
| 832 | + for dirty access */ | ||
| 833 | + if (is_user) { | ||
| 834 | + if (ptep & PG_RW_MASK) | ||
| 835 | + prot |= PAGE_WRITE; | ||
| 836 | + } else { | ||
| 837 | + if (!(env->cr[0] & CR0_WP_MASK) || | ||
| 838 | + (ptep & PG_RW_MASK)) | ||
| 839 | + prot |= PAGE_WRITE; | ||
| 765 | } | 840 | } |
| 766 | } | 841 | } |
| 767 | do_mapping: | 842 | do_mapping: |
| @@ -773,15 +848,20 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | @@ -773,15 +848,20 @@ int cpu_x86_handle_mmu_fault(CPUX86State *env, target_ulong addr, | ||
| 773 | paddr = (pte & TARGET_PAGE_MASK) + page_offset; | 848 | paddr = (pte & TARGET_PAGE_MASK) + page_offset; |
| 774 | vaddr = virt_addr + page_offset; | 849 | vaddr = virt_addr + page_offset; |
| 775 | 850 | ||
| 776 | - ret = tlb_set_page(env, vaddr, paddr, prot, is_user, is_softmmu); | 851 | + ret = tlb_set_page_exec(env, vaddr, paddr, prot, is_user, is_softmmu); |
| 777 | return ret; | 852 | return ret; |
| 778 | do_fault_protect: | 853 | do_fault_protect: |
| 779 | error_code = PG_ERROR_P_MASK; | 854 | error_code = PG_ERROR_P_MASK; |
| 780 | do_fault: | 855 | do_fault: |
| 781 | env->cr[2] = addr; | 856 | env->cr[2] = addr; |
| 782 | - env->error_code = (is_write << PG_ERROR_W_BIT) | error_code; | 857 | + error_code |= (is_write << PG_ERROR_W_BIT); |
| 783 | if (is_user) | 858 | if (is_user) |
| 784 | - env->error_code |= PG_ERROR_U_MASK; | 859 | + error_code |= PG_ERROR_U_MASK; |
| 860 | + if (is_write1 == 2 && | ||
| 861 | + (env->efer & MSR_EFER_NXE) && | ||
| 862 | + (env->cr[4] & CR4_PAE_MASK)) | ||
| 863 | + error_code |= PG_ERROR_I_D_MASK; | ||
| 864 | + env->error_code = error_code; | ||
| 785 | return 1; | 865 | return 1; |
| 786 | } | 866 | } |
| 787 | 867 |