Commit c2a5a46be4a1c682e18e29e67785126b7610b14d
Committed by
David S. Miller
1 parent
486ad10a7e
Exists in
master
and in
7 other branches
[SPARC64]: Fix for Niagara memory corruption.
On some sun4v systems, after netboot the ethernet controller and it's DMA mappings can be left active. The net result is that the kernel can end up using memory the ethernet controller will continue to DMA into, resulting in corruption. To deal with this, we are more careful about importing IOMMU translations which OBP has left in the IO-TLB. If the mapping maps into an area the firmware claimed was free and available memory for the kernel to use, we demap instead of import that IOMMU entry. This is going to cause the network chip to take a PCI master abort on the next DMA it attempts, if it has been left going like this. All tests show that this is handled properly by the PCI layer and the e1000 drivers. Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 3 changed files with 39 additions and 6 deletions Side-by-side Diff
arch/sparc64/kernel/pci_sun4v.c
... | ... | @@ -988,8 +988,13 @@ |
988 | 988 | HV_PCI_TSBID(0, i), |
989 | 989 | &io_attrs, &ra); |
990 | 990 | if (ret == HV_EOK) { |
991 | - cnt++; | |
992 | - __set_bit(i, arena->map); | |
991 | + if (page_in_phys_avail(ra)) { | |
992 | + pci_sun4v_iommu_demap(devhandle, | |
993 | + HV_PCI_TSBID(0, i), 1); | |
994 | + } else { | |
995 | + cnt++; | |
996 | + __set_bit(i, arena->map); | |
997 | + } | |
993 | 998 | } |
994 | 999 | } |
995 | 1000 | |
... | ... | @@ -1062,9 +1067,9 @@ |
1062 | 1067 | iommu->arena.limit = num_tsb_entries; |
1063 | 1068 | |
1064 | 1069 | sz = probe_existing_entries(pbm, iommu); |
1065 | - | |
1066 | - printk("%s: TSB entries [%lu], existing mapings [%lu]\n", | |
1067 | - pbm->name, num_tsb_entries, sz); | |
1070 | + if (sz) | |
1071 | + printk("%s: Imported %lu TSB entries from OBP\n", | |
1072 | + pbm->name, sz); | |
1068 | 1073 | } |
1069 | 1074 | |
1070 | 1075 | static void pci_sun4v_get_bus_range(struct pci_pbm_info *pbm) |
arch/sparc64/mm/init.c
... | ... | @@ -1396,7 +1396,7 @@ |
1396 | 1396 | while (old_start < old_end) { |
1397 | 1397 | int n; |
1398 | 1398 | |
1399 | - for (n = 0; pavail_rescan_ents; n++) { | |
1399 | + for (n = 0; n < pavail_rescan_ents; n++) { | |
1400 | 1400 | unsigned long new_start, new_end; |
1401 | 1401 | |
1402 | 1402 | new_start = pavail_rescan[n].phys_addr; |
... | ... | @@ -1416,6 +1416,32 @@ |
1416 | 1416 | old_start += PAGE_SIZE; |
1417 | 1417 | } |
1418 | 1418 | } |
1419 | +} | |
1420 | + | |
1421 | +int __init page_in_phys_avail(unsigned long paddr) | |
1422 | +{ | |
1423 | + int i; | |
1424 | + | |
1425 | + paddr &= PAGE_MASK; | |
1426 | + | |
1427 | + for (i = 0; i < pavail_rescan_ents; i++) { | |
1428 | + unsigned long start, end; | |
1429 | + | |
1430 | + start = pavail_rescan[i].phys_addr; | |
1431 | + end = start + pavail_rescan[i].reg_size; | |
1432 | + | |
1433 | + if (paddr >= start && paddr < end) | |
1434 | + return 1; | |
1435 | + } | |
1436 | + if (paddr >= kern_base && paddr < (kern_base + kern_size)) | |
1437 | + return 1; | |
1438 | +#ifdef CONFIG_BLK_DEV_INITRD | |
1439 | + if (paddr >= __pa(initrd_start) && | |
1440 | + paddr < __pa(PAGE_ALIGN(initrd_end))) | |
1441 | + return 1; | |
1442 | +#endif | |
1443 | + | |
1444 | + return 0; | |
1419 | 1445 | } |
1420 | 1446 | |
1421 | 1447 | void __init mem_init(void) |
include/asm-sparc64/pgtable.h
... | ... | @@ -756,6 +756,8 @@ |
756 | 756 | #define kern_addr_valid(addr) \ |
757 | 757 | (test_bit(__pa((unsigned long)(addr))>>22, sparc64_valid_addr_bitmap)) |
758 | 758 | |
759 | +extern int page_in_phys_avail(unsigned long paddr); | |
760 | + | |
759 | 761 | extern int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long from, |
760 | 762 | unsigned long pfn, |
761 | 763 | unsigned long size, pgprot_t prot); |