diff options
author | Alex Chiang <achiang@hp.com> | 2007-07-11 13:02:15 -0400 |
---|---|---|
committer | Tony Luck <tony.luck@intel.com> | 2007-07-11 14:34:49 -0400 |
commit | 012b7105cc816fb797eb1c161cdfc0052b5c3f53 (patch) | |
tree | 084ede189a3284c17f09ea68d0d4573d95e7499f | |
parent | 9e121327b37b751ef66e6f57e2d02dd568955148 (diff) |
[IA64] prevent MCA when performing MMIO mmap to PCI config space
Example memory map (HP rx7640 with 'default' acpiconfig setting, VGA disabled):
0x00000000 - 0x3FFFBFFF supports only WB (cacheable) access
If a user attempts to perform an MMIO mmap (using the PCIIOC_MMAP_IS_MEM ioctl)
to PCI config space (like mmap'ing and accessing memory at 0xA0000),
we will MCA because the kernel will attempt to use a mapping with the UC
attribute.
So check the memory attribute in kern_mmap and the EFI memmap. If WC is
requested, and WC or UC access is supported for the region, allow it.
Otherwise, use the same attribute the kernel uses.
Updates documentation and test cases as well.
Signed-off-by: Alex Chiang <achiang@hp.com>
Signed-off-by: Bjorn Helgaas <bjorn.helgaas@hp.com>
Signed-off-by: Tony Luck <tony.luck@intel.com>
-rw-r--r-- | Documentation/ia64/aliasing-test.c | 26 | ||||
-rw-r--r-- | Documentation/ia64/aliasing.txt | 12 | ||||
-rw-r--r-- | arch/ia64/pci/pci.c | 22 |
3 files changed, 48 insertions, 12 deletions
diff --git a/Documentation/ia64/aliasing-test.c b/Documentation/ia64/aliasing-test.c index d485256ee1ce..773a814d4093 100644 --- a/Documentation/ia64/aliasing-test.c +++ b/Documentation/ia64/aliasing-test.c | |||
@@ -19,6 +19,7 @@ | |||
19 | #include <sys/mman.h> | 19 | #include <sys/mman.h> |
20 | #include <sys/stat.h> | 20 | #include <sys/stat.h> |
21 | #include <unistd.h> | 21 | #include <unistd.h> |
22 | #include <linux/pci.h> | ||
22 | 23 | ||
23 | int sum; | 24 | int sum; |
24 | 25 | ||
@@ -34,13 +35,19 @@ int map_mem(char *path, off_t offset, size_t length, int touch) | |||
34 | return -1; | 35 | return -1; |
35 | } | 36 | } |
36 | 37 | ||
38 | if (fnmatch("/proc/bus/pci/*", path, 0) == 0) { | ||
39 | rc = ioctl(fd, PCIIOC_MMAP_IS_MEM); | ||
40 | if (rc == -1) | ||
41 | perror("PCIIOC_MMAP_IS_MEM ioctl"); | ||
42 | } | ||
43 | |||
37 | addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); | 44 | addr = mmap(NULL, length, PROT_READ|PROT_WRITE, MAP_SHARED, fd, offset); |
38 | if (addr == MAP_FAILED) | 45 | if (addr == MAP_FAILED) |
39 | return 1; | 46 | return 1; |
40 | 47 | ||
41 | if (touch) { | 48 | if (touch) { |
42 | c = (int *) addr; | 49 | c = (int *) addr; |
43 | while (c < (int *) (offset + length)) | 50 | while (c < (int *) (addr + length)) |
44 | sum += *c++; | 51 | sum += *c++; |
45 | } | 52 | } |
46 | 53 | ||
@@ -54,7 +61,7 @@ int map_mem(char *path, off_t offset, size_t length, int touch) | |||
54 | return 0; | 61 | return 0; |
55 | } | 62 | } |
56 | 63 | ||
57 | int scan_sysfs(char *path, char *file, off_t offset, size_t length, int touch) | 64 | int scan_tree(char *path, char *file, off_t offset, size_t length, int touch) |
58 | { | 65 | { |
59 | struct dirent **namelist; | 66 | struct dirent **namelist; |
60 | char *name, *path2; | 67 | char *name, *path2; |
@@ -93,7 +100,7 @@ int scan_sysfs(char *path, char *file, off_t offset, size_t length, int touch) | |||
93 | } else { | 100 | } else { |
94 | r = lstat(path2, &buf); | 101 | r = lstat(path2, &buf); |
95 | if (r == 0 && S_ISDIR(buf.st_mode)) { | 102 | if (r == 0 && S_ISDIR(buf.st_mode)) { |
96 | rc = scan_sysfs(path2, file, offset, length, touch); | 103 | rc = scan_tree(path2, file, offset, length, touch); |
97 | if (rc < 0) | 104 | if (rc < 0) |
98 | return rc; | 105 | return rc; |
99 | } | 106 | } |
@@ -238,10 +245,15 @@ int main() | |||
238 | else | 245 | else |
239 | fprintf(stderr, "FAIL: /dev/mem 0x0-0x100000 not accessible\n"); | 246 | fprintf(stderr, "FAIL: /dev/mem 0x0-0x100000 not accessible\n"); |
240 | 247 | ||
241 | scan_sysfs("/sys/class/pci_bus", "legacy_mem", 0, 0xA0000, 1); | 248 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 0xA0000, 1); |
242 | scan_sysfs("/sys/class/pci_bus", "legacy_mem", 0xA0000, 0x20000, 0); | 249 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0xA0000, 0x20000, 0); |
243 | scan_sysfs("/sys/class/pci_bus", "legacy_mem", 0xC0000, 0x40000, 1); | 250 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0xC0000, 0x40000, 1); |
244 | scan_sysfs("/sys/class/pci_bus", "legacy_mem", 0, 1024*1024, 0); | 251 | scan_tree("/sys/class/pci_bus", "legacy_mem", 0, 1024*1024, 0); |
245 | 252 | ||
246 | scan_rom("/sys/devices", "rom"); | 253 | scan_rom("/sys/devices", "rom"); |
254 | |||
255 | scan_tree("/proc/bus/pci", "??.?", 0, 0xA0000, 1); | ||
256 | scan_tree("/proc/bus/pci", "??.?", 0xA0000, 0x20000, 0); | ||
257 | scan_tree("/proc/bus/pci", "??.?", 0xC0000, 0x40000, 1); | ||
258 | scan_tree("/proc/bus/pci", "??.?", 0, 1024*1024, 0); | ||
247 | } | 259 | } |
diff --git a/Documentation/ia64/aliasing.txt b/Documentation/ia64/aliasing.txt index 9a431a7d0f5d..aa3e953f0f7b 100644 --- a/Documentation/ia64/aliasing.txt +++ b/Documentation/ia64/aliasing.txt | |||
@@ -112,6 +112,18 @@ POTENTIAL ATTRIBUTE ALIASING CASES | |||
112 | 112 | ||
113 | The /dev/mem mmap constraints apply. | 113 | The /dev/mem mmap constraints apply. |
114 | 114 | ||
115 | mmap of /proc/bus/pci/.../??.? | ||
116 | |||
117 | This is an MMIO mmap of PCI functions, which additionally may or | ||
118 | may not be requested as using the WC attribute. | ||
119 | |||
120 | If WC is requested, and the region in kern_memmap is either WC | ||
121 | or UC, and the EFI memory map designates the region as WC, then | ||
122 | the WC mapping is allowed. | ||
123 | |||
124 | Otherwise, the user mapping must use the same attribute as the | ||
125 | kernel mapping. | ||
126 | |||
115 | read/write of /dev/mem | 127 | read/write of /dev/mem |
116 | 128 | ||
117 | This uses copy_from_user(), which implicitly uses a kernel | 129 | This uses copy_from_user(), which implicitly uses a kernel |
diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 73696b4a2eed..07d0e92742c8 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c | |||
@@ -591,6 +591,9 @@ int | |||
591 | pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, | 591 | pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, |
592 | enum pci_mmap_state mmap_state, int write_combine) | 592 | enum pci_mmap_state mmap_state, int write_combine) |
593 | { | 593 | { |
594 | unsigned long size = vma->vm_end - vma->vm_start; | ||
595 | pgprot_t prot; | ||
596 | |||
594 | /* | 597 | /* |
595 | * I/O space cannot be accessed via normal processor loads and | 598 | * I/O space cannot be accessed via normal processor loads and |
596 | * stores on this platform. | 599 | * stores on this platform. |
@@ -604,15 +607,24 @@ pci_mmap_page_range (struct pci_dev *dev, struct vm_area_struct *vma, | |||
604 | */ | 607 | */ |
605 | return -EINVAL; | 608 | return -EINVAL; |
606 | 609 | ||
610 | if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size)) | ||
611 | return -EINVAL; | ||
612 | |||
613 | prot = phys_mem_access_prot(NULL, vma->vm_pgoff, size, | ||
614 | vma->vm_page_prot); | ||
615 | |||
607 | /* | 616 | /* |
608 | * Leave vm_pgoff as-is, the PCI space address is the physical | 617 | * If the user requested WC, the kernel uses UC or WC for this region, |
609 | * address on this platform. | 618 | * and the chipset supports WC, we can use WC. Otherwise, we have to |
619 | * use the same attribute the kernel uses. | ||
610 | */ | 620 | */ |
611 | if (write_combine && efi_range_is_wc(vma->vm_start, | 621 | if (write_combine && |
612 | vma->vm_end - vma->vm_start)) | 622 | ((pgprot_val(prot) & _PAGE_MA_MASK) == _PAGE_MA_UC || |
623 | (pgprot_val(prot) & _PAGE_MA_MASK) == _PAGE_MA_WC) && | ||
624 | efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start)) | ||
613 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); | 625 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); |
614 | else | 626 | else |
615 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); | 627 | vma->vm_page_prot = prot; |
616 | 628 | ||
617 | if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, | 629 | if (remap_pfn_range(vma, vma->vm_start, vma->vm_pgoff, |
618 | vma->vm_end - vma->vm_start, vma->vm_page_prot)) | 630 | vma->vm_end - vma->vm_start, vma->vm_page_prot)) |