Skip to content

Commit e149927

Browse files
mariobalanica6by9
authored andcommitted
arm64: mm: Force Device mappings for PCIe MMIO
PCIe device drivers may map MMIO space as Normal non-cacheable, for the purpose of enabling write combining or unaligned accesses. On many platforms (e.g. Ampere Altra, RK35xx), the PCIe interface cannot support unaligned outbound transactions. This may lead to data corruption, for instance, when a regular memcpy is performed by an application on a GPU's VRAM BAR. Add an option to force all software that maps PCIe MMIO space as Normal non-cacheable memory to use Device-nGnRE instead. If the strict alignment is not met, the CPU will raise alignment faults that can be further handled by the kernel by enabling CONFIG_ARM64_ALIGNMENT_FIXUPS. Signed-off-by: Mario Bălănică <mariobalanica02@gmail.com>
1 parent aa089dd commit e149927

3 files changed

Lines changed: 65 additions & 5 deletions

File tree

arch/arm64/Kconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1708,6 +1708,23 @@ config ARM64_TAGGED_ADDR_ABI
17081708
to system calls as pointer arguments. For details, see
17091709
Documentation/arch/arm64/tagged-address-abi.rst.
17101710

1711+
config ARM64_FORCE_PCIE_MMIO_DEVICE_MAPPINGS
1712+
bool "Force Device memory mappings for PCIe MMIO space"
1713+
default y
1714+
help
1715+
PCIe device drivers may map MMIO space as Normal non-cacheable,
1716+
for the purpose of enabling write combining or unaligned accesses.
1717+
1718+
On many platforms (e.g. Ampere Altra, RK35xx), the PCIe interface
1719+
cannot support unaligned outbound transactions. This may lead to
1720+
data corruption, for instance, when a regular memcpy is performed by
1721+
an application on a GPU's VRAM BAR.
1722+
1723+
This option forces all software that maps PCIe MMIO space as Normal
1724+
non-cacheable memory to use Device-nGnRE instead. If the strict alignment
1725+
is not met, the CPU will raise alignment faults that can be further
1726+
handled by the kernel by enabling CONFIG_ARM64_ALIGNMENT_FIXUPS.
1727+
17111728
config ARM64_ALIGNMENT_FIXUPS
17121729
bool "Fix up misaligned multi-word loads and stores in 64-bit kernel/user space"
17131730
default y

arch/arm64/include/asm/pgtable.h

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -338,11 +338,6 @@ static inline pte_t pte_mkyoung(pte_t pte)
338338
return set_pte_bit(pte, __pgprot(PTE_AF));
339339
}
340340

341-
static inline pte_t pte_mkspecial(pte_t pte)
342-
{
343-
return set_pte_bit(pte, __pgprot(PTE_SPECIAL));
344-
}
345-
346341
static inline pte_t pte_mkcont(pte_t pte)
347342
{
348343
return set_pte_bit(pte, __pgprot(PTE_CONT));
@@ -807,6 +802,21 @@ static inline void __set_puds(struct mm_struct *mm,
807802
__pgprot_modify(prot, PTE_ATTRINDX_MASK, \
808803
PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
809804

805+
extern bool range_is_pci(phys_addr_t phys_addr, size_t size);
806+
807+
static inline pte_t pte_mkspecial(pte_t pte)
808+
{
809+
#ifdef CONFIG_ARM64_FORCE_PCIE_MMIO_DEVICE_MAPPINGS
810+
phys_addr_t phys = __pte_to_phys(pte);
811+
pgprot_t prot = __pgprot(pte_val(pte) & ~__phys_to_pte_val(__pte_to_phys(__pte(~0ull))));
812+
813+
if ((pgprot_val(prot) != pgprot_val(pgprot_device(prot))) &&
814+
range_is_pci(phys, PAGE_SIZE))
815+
pte = __pte(__phys_to_pte_val(phys) | pgprot_val(pgprot_device(prot)));
816+
#endif
817+
return set_pte_bit(pte, __pgprot(PTE_SPECIAL));
818+
}
819+
810820
#define __HAVE_PHYS_MEM_ACCESS_PROT
811821
struct file;
812822
extern pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,

kernel/resource.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -389,6 +389,7 @@ static int find_next_iomem_res(resource_size_t start, resource_size_t end,
389389
.flags = p->flags,
390390
.desc = p->desc,
391391
.parent = p->parent,
392+
.name = p->name,
392393
};
393394
}
394395

@@ -566,6 +567,38 @@ int __weak page_is_ram(unsigned long pfn)
566567
}
567568
EXPORT_SYMBOL_GPL(page_is_ram);
568569

570+
static int pci_res_check(struct resource *res, void *arg)
571+
{
572+
if (!res->name)
573+
return 1;
574+
575+
return strncmp(res->name, "PCI", 3);
576+
}
577+
578+
bool range_is_pci(phys_addr_t phys_addr, size_t size)
579+
{
580+
u64 start, end;
581+
int ret;
582+
583+
start = phys_addr;
584+
end = phys_addr + size;
585+
586+
/* Check 32-bit MMIO */
587+
ret = walk_iomem_res_desc(IORES_DESC_NONE, IORESOURCE_MEM,
588+
start, end, NULL, pci_res_check);
589+
if (!ret)
590+
return true;
591+
592+
/* Check 64-bit MMIO */
593+
ret = walk_iomem_res_desc(IORES_DESC_NONE, IORESOURCE_MEM_64,
594+
start, end, NULL, pci_res_check);
595+
if (!ret)
596+
return true;
597+
598+
return false;
599+
}
600+
EXPORT_SYMBOL_GPL(range_is_pci);
601+
569602
static int __region_intersects(struct resource *parent, resource_size_t start,
570603
size_t size, unsigned long flags,
571604
unsigned long desc)

0 commit comments

Comments
 (0)