diff options
author | Tony Luck <tony.luck@intel.com> | 2006-06-21 17:50:10 -0400 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2006-06-21 17:50:10 -0400 |
commit | 1323523f505606cfd24af6122369afddefc3b09d (patch) | |
tree | a3238a27220dd91ec0918478683e59e48605865f /arch/ia64/kernel | |
parent | 9ba89334552b96e2127dcafb1c46ce255ecf2667 (diff) | |
parent | 32e62c636a728cb39c0b3bd191286f2ca65d4028 (diff) |
Pull rework-memory-attribute-aliasing into release branch
Diffstat (limited to 'arch/ia64/kernel')
-rw-r--r-- | arch/ia64/kernel/efi.c | 156 |
1 files changed, 102 insertions, 54 deletions
diff --git a/arch/ia64/kernel/efi.c b/arch/ia64/kernel/efi.c index 12cfedce73b1..c33d0ba7e300 100644 --- a/arch/ia64/kernel/efi.c +++ b/arch/ia64/kernel/efi.c | |||
@@ -8,6 +8,8 @@ | |||
8 | * Copyright (C) 1999-2003 Hewlett-Packard Co. | 8 | * Copyright (C) 1999-2003 Hewlett-Packard Co. |
9 | * David Mosberger-Tang <davidm@hpl.hp.com> | 9 | * David Mosberger-Tang <davidm@hpl.hp.com> |
10 | * Stephane Eranian <eranian@hpl.hp.com> | 10 | * Stephane Eranian <eranian@hpl.hp.com> |
11 | * (c) Copyright 2006 Hewlett-Packard Development Company, L.P. | ||
12 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | ||
11 | * | 13 | * |
12 | * All EFI Runtime Services are not implemented yet as EFI only | 14 | * All EFI Runtime Services are not implemented yet as EFI only |
13 | * supports physical mode addressing on SoftSDV. This is to be fixed | 15 | * supports physical mode addressing on SoftSDV. This is to be fixed |
@@ -622,28 +624,20 @@ efi_get_iobase (void) | |||
622 | return 0; | 624 | return 0; |
623 | } | 625 | } |
624 | 626 | ||
625 | static efi_memory_desc_t * | 627 | static struct kern_memdesc * |
626 | efi_memory_descriptor (unsigned long phys_addr) | 628 | kern_memory_descriptor (unsigned long phys_addr) |
627 | { | 629 | { |
628 | void *efi_map_start, *efi_map_end, *p; | 630 | struct kern_memdesc *md; |
629 | efi_memory_desc_t *md; | ||
630 | u64 efi_desc_size; | ||
631 | |||
632 | efi_map_start = __va(ia64_boot_param->efi_memmap); | ||
633 | efi_map_end = efi_map_start + ia64_boot_param->efi_memmap_size; | ||
634 | efi_desc_size = ia64_boot_param->efi_memdesc_size; | ||
635 | 631 | ||
636 | for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { | 632 | for (md = kern_memmap; md->start != ~0UL; md++) { |
637 | md = p; | 633 | if (phys_addr - md->start < (md->num_pages << EFI_PAGE_SHIFT)) |
638 | |||
639 | if (phys_addr - md->phys_addr < (md->num_pages << EFI_PAGE_SHIFT)) | ||
640 | return md; | 634 | return md; |
641 | } | 635 | } |
642 | return 0; | 636 | return 0; |
643 | } | 637 | } |
644 | 638 | ||
645 | static int | 639 | static efi_memory_desc_t * |
646 | efi_memmap_has_mmio (void) | 640 | efi_memory_descriptor (unsigned long phys_addr) |
647 | { | 641 | { |
648 | void *efi_map_start, *efi_map_end, *p; | 642 | void *efi_map_start, *efi_map_end, *p; |
649 | efi_memory_desc_t *md; | 643 | efi_memory_desc_t *md; |
@@ -656,8 +650,8 @@ efi_memmap_has_mmio (void) | |||
656 | for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { | 650 | for (p = efi_map_start; p < efi_map_end; p += efi_desc_size) { |
657 | md = p; | 651 | md = p; |
658 | 652 | ||
659 | if (md->type == EFI_MEMORY_MAPPED_IO) | 653 | if (phys_addr - md->phys_addr < (md->num_pages << EFI_PAGE_SHIFT)) |
660 | return 1; | 654 | return md; |
661 | } | 655 | } |
662 | return 0; | 656 | return 0; |
663 | } | 657 | } |
@@ -683,71 +677,125 @@ efi_mem_attributes (unsigned long phys_addr) | |||
683 | } | 677 | } |
684 | EXPORT_SYMBOL(efi_mem_attributes); | 678 | EXPORT_SYMBOL(efi_mem_attributes); |
685 | 679 | ||
686 | /* | 680 | u64 |
687 | * Determines whether the memory at phys_addr supports the desired | 681 | efi_mem_attribute (unsigned long phys_addr, unsigned long size) |
688 | * attribute (WB, UC, etc). If this returns 1, the caller can safely | ||
689 | * access size bytes at phys_addr with the specified attribute. | ||
690 | */ | ||
691 | int | ||
692 | efi_mem_attribute_range (unsigned long phys_addr, unsigned long size, u64 attr) | ||
693 | { | 682 | { |
694 | unsigned long end = phys_addr + size; | 683 | unsigned long end = phys_addr + size; |
695 | efi_memory_desc_t *md = efi_memory_descriptor(phys_addr); | 684 | efi_memory_desc_t *md = efi_memory_descriptor(phys_addr); |
685 | u64 attr; | ||
686 | |||
687 | if (!md) | ||
688 | return 0; | ||
689 | |||
690 | /* | ||
691 | * EFI_MEMORY_RUNTIME is not a memory attribute; it just tells | ||
692 | * the kernel that firmware needs this region mapped. | ||
693 | */ | ||
694 | attr = md->attribute & ~EFI_MEMORY_RUNTIME; | ||
695 | do { | ||
696 | unsigned long md_end = efi_md_end(md); | ||
697 | |||
698 | if (end <= md_end) | ||
699 | return attr; | ||
700 | |||
701 | md = efi_memory_descriptor(md_end); | ||
702 | if (!md || (md->attribute & ~EFI_MEMORY_RUNTIME) != attr) | ||
703 | return 0; | ||
704 | } while (md); | ||
705 | return 0; | ||
706 | } | ||
707 | |||
708 | u64 | ||
709 | kern_mem_attribute (unsigned long phys_addr, unsigned long size) | ||
710 | { | ||
711 | unsigned long end = phys_addr + size; | ||
712 | struct kern_memdesc *md; | ||
713 | u64 attr; | ||
696 | 714 | ||
697 | /* | 715 | /* |
698 | * Some firmware doesn't report MMIO regions in the EFI memory | 716 | * This is a hack for ioremap calls before we set up kern_memmap. |
699 | * map. The Intel BigSur (a.k.a. HP i2000) has this problem. | 717 | * Maybe we should do efi_memmap_init() earlier instead. |
700 | * On those platforms, we have to assume UC is valid everywhere. | ||
701 | */ | 718 | */ |
702 | if (!md || (md->attribute & attr) != attr) { | 719 | if (!kern_memmap) { |
703 | if (attr == EFI_MEMORY_UC && !efi_memmap_has_mmio()) | 720 | attr = efi_mem_attribute(phys_addr, size); |
704 | return 1; | 721 | if (attr & EFI_MEMORY_WB) |
722 | return EFI_MEMORY_WB; | ||
705 | return 0; | 723 | return 0; |
706 | } | 724 | } |
707 | 725 | ||
726 | md = kern_memory_descriptor(phys_addr); | ||
727 | if (!md) | ||
728 | return 0; | ||
729 | |||
730 | attr = md->attribute; | ||
708 | do { | 731 | do { |
709 | unsigned long md_end = efi_md_end(md); | 732 | unsigned long md_end = kmd_end(md); |
710 | 733 | ||
711 | if (end <= md_end) | 734 | if (end <= md_end) |
712 | return 1; | 735 | return attr; |
713 | 736 | ||
714 | md = efi_memory_descriptor(md_end); | 737 | md = kern_memory_descriptor(md_end); |
715 | if (!md || (md->attribute & attr) != attr) | 738 | if (!md || md->attribute != attr) |
716 | return 0; | 739 | return 0; |
717 | } while (md); | 740 | } while (md); |
718 | return 0; | 741 | return 0; |
719 | } | 742 | } |
743 | EXPORT_SYMBOL(kern_mem_attribute); | ||
720 | 744 | ||
721 | /* | ||
722 | * For /dev/mem, we only allow read & write system calls to access | ||
723 | * write-back memory, because read & write don't allow the user to | ||
724 | * control access size. | ||
725 | */ | ||
726 | int | 745 | int |
727 | valid_phys_addr_range (unsigned long phys_addr, unsigned long size) | 746 | valid_phys_addr_range (unsigned long phys_addr, unsigned long size) |
728 | { | 747 | { |
729 | return efi_mem_attribute_range(phys_addr, size, EFI_MEMORY_WB); | 748 | u64 attr; |
749 | |||
750 | /* | ||
751 | * /dev/mem reads and writes use copy_to_user(), which implicitly | ||
752 | * uses a granule-sized kernel identity mapping. It's really | ||
753 | * only safe to do this for regions in kern_memmap. For more | ||
754 | * details, see Documentation/ia64/aliasing.txt. | ||
755 | */ | ||
756 | attr = kern_mem_attribute(phys_addr, size); | ||
757 | if (attr & EFI_MEMORY_WB || attr & EFI_MEMORY_UC) | ||
758 | return 1; | ||
759 | return 0; | ||
730 | } | 760 | } |
731 | 761 | ||
732 | /* | ||
733 | * We allow mmap of anything in the EFI memory map that supports | ||
734 | * either write-back or uncacheable access. For uncacheable regions, | ||
735 | * the supported access sizes are system-dependent, and the user is | ||
736 | * responsible for using the correct size. | ||
737 | * | ||
738 | * Note that this doesn't currently allow access to hot-added memory, | ||
739 | * because that doesn't appear in the boot-time EFI memory map. | ||
740 | */ | ||
741 | int | 762 | int |
742 | valid_mmap_phys_addr_range (unsigned long phys_addr, unsigned long size) | 763 | valid_mmap_phys_addr_range (unsigned long phys_addr, unsigned long size) |
743 | { | 764 | { |
744 | if (efi_mem_attribute_range(phys_addr, size, EFI_MEMORY_WB)) | 765 | /* |
745 | return 1; | 766 | * MMIO regions are often missing from the EFI memory map. |
767 | * We must allow mmap of them for programs like X, so we | ||
768 | * currently can't do any useful validation. | ||
769 | */ | ||
770 | return 1; | ||
771 | } | ||
746 | 772 | ||
747 | if (efi_mem_attribute_range(phys_addr, size, EFI_MEMORY_UC)) | 773 | pgprot_t |
748 | return 1; | 774 | phys_mem_access_prot(struct file *file, unsigned long pfn, unsigned long size, |
775 | pgprot_t vma_prot) | ||
776 | { | ||
777 | unsigned long phys_addr = pfn << PAGE_SHIFT; | ||
778 | u64 attr; | ||
749 | 779 | ||
750 | return 0; | 780 | /* |
781 | * For /dev/mem mmap, we use user mappings, but if the region is | ||
782 | * in kern_memmap (and hence may be covered by a kernel mapping), | ||
783 | * we must use the same attribute as the kernel mapping. | ||
784 | */ | ||
785 | attr = kern_mem_attribute(phys_addr, size); | ||
786 | if (attr & EFI_MEMORY_WB) | ||
787 | return pgprot_cacheable(vma_prot); | ||
788 | else if (attr & EFI_MEMORY_UC) | ||
789 | return pgprot_noncached(vma_prot); | ||
790 | |||
791 | /* | ||
792 | * Some chipsets don't support UC access to memory. If | ||
793 | * WB is supported, we prefer that. | ||
794 | */ | ||
795 | if (efi_mem_attribute(phys_addr, size) & EFI_MEMORY_WB) | ||
796 | return pgprot_cacheable(vma_prot); | ||
797 | |||
798 | return pgprot_noncached(vma_prot); | ||
751 | } | 799 | } |
752 | 800 | ||
753 | int __init | 801 | int __init |