Skip to content

Commit eaadce9

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 31fb6f4 commit eaadce9

File tree

3 files changed

+65
-5
lines changed

3 files changed

+65
-5
lines changed

arch/arm64/Kconfig

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

1699+
config ARM64_FORCE_PCIE_MMIO_DEVICE_MAPPINGS
1700+
bool "Force Device memory mappings for PCIe MMIO space"
1701+
default y
1702+
help
1703+
PCIe device drivers may map MMIO space as Normal non-cacheable,
1704+
for the purpose of enabling write combining or unaligned accesses.
1705+
1706+
On many platforms (e.g. Ampere Altra, RK35xx), the PCIe interface
1707+
cannot support unaligned outbound transactions. This may lead to
1708+
data corruption, for instance, when a regular memcpy is performed by
1709+
an application on a GPU's VRAM BAR.
1710+
1711+
This option forces all software that maps PCIe MMIO space as Normal
1712+
non-cacheable memory to use Device-nGnRE instead. If the strict alignment
1713+
is not met, the CPU will raise alignment faults that can be further
1714+
handled by the kernel by enabling CONFIG_ARM64_ALIGNMENT_FIXUPS.
1715+
16991716
config ARM64_ALIGNMENT_FIXUPS
17001717
bool "Fix up misaligned multi-word loads and stores in 64-bit kernel/user space"
17011718
default y

arch/arm64/include/asm/pgtable.h

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

343-
static inline pte_t pte_mkspecial(pte_t pte)
344-
{
345-
return set_pte_bit(pte, __pgprot(PTE_SPECIAL));
346-
}
347-
348343
static inline pte_t pte_mkcont(pte_t pte)
349344
{
350345
return set_pte_bit(pte, __pgprot(PTE_CONT));
@@ -804,6 +799,21 @@ static inline void __set_puds(struct mm_struct *mm,
804799
__pgprot_modify(prot, PTE_ATTRINDX_MASK, \
805800
PTE_ATTRINDX(MT_NORMAL_NC) | PTE_PXN | PTE_UXN)
806801

802+
extern bool range_is_pci(phys_addr_t phys_addr, size_t size);
803+
804+
static inline pte_t pte_mkspecial(pte_t pte)
805+
{
806+
#ifdef CONFIG_ARM64_FORCE_PCIE_MMIO_DEVICE_MAPPINGS
807+
phys_addr_t phys = __pte_to_phys(pte);
808+
pgprot_t prot = __pgprot(pte_val(pte) & ~__phys_to_pte_val(__pte_to_phys(__pte(~0ull))));
809+
810+
if ((pgprot_val(prot) != pgprot_val(pgprot_device(prot))) &&
811+
range_is_pci(phys, PAGE_SIZE))
812+
pte = __pte(__phys_to_pte_val(phys) | pgprot_val(pgprot_device(prot)));
813+
#endif
814+
return set_pte_bit(pte, __pgprot(PTE_SPECIAL));
815+
}
816+
807817
#define __HAVE_PHYS_MEM_ACCESS_PROT
808818
struct file;
809819
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)