Commit c2a5a46be4a1c682e18e29e67785126b7610b14d

Authored by David S. Miller
Committed by David S. Miller
1 parent 486ad10a7e

[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);