Commit 3de18c86 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'stable/for-linus-5.15' of...

Merge branch 'stable/for-linus-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/konrad/swiotlb

Pull swiotlb updates from Konrad Rzeszutek Wilk:
 "A new feature called restricted DMA pools. It allows SWIOTLB to
  utilize per-device (or per-platform) allocated memory pools instead of
  using the global one.

  The first big user of this is ARM Confidential Computing where the
  memory for DMA operations can be set per platform"

* 'stable/for-linus-5.15' of git://git.kernel.org/pub/scm/linux/kernel/git/konrad/swiotlb: (23 commits)
  swiotlb: use depends on for DMA_RESTRICTED_POOL
  of: restricted dma: Don't fail device probe on rmem init failure
  of: Move of_dma_set_restricted_buffer() into device.c
  powerpc/svm: Don't issue ultracalls if !mem_encrypt_active()
  s390/pv: fix the forcing of the swiotlb
  swiotlb: Free tbl memory in swiotlb_exit()
  swiotlb: Emit diagnostic in swiotlb_exit()
  swiotlb: Convert io_default_tlb_mem to static allocation
  of: Return success from of_dma_set_restricted_buffer() when !OF_ADDRESS
  swiotlb: add overflow checks to swiotlb_bounce
  swiotlb: fix implicit debugfs declarations
  of: Add plumbing for restricted DMA pool
  dt-bindings: of: Add restricted DMA pool
  swiotlb: Add restricted DMA pool initialization
  swiotlb: Add restricted DMA alloc/free support
  swiotlb: Refactor swiotlb_tbl_unmap_single
  swiotlb: Move alloc_size to swiotlb_find_slots
  swiotlb: Use is_swiotlb_force_bounce for swiotlb data bouncing
  swiotlb: Update is_swiotlb_active to add a struct device argument
  swiotlb: Update is_swiotlb_buffer to add a struct device argument
  ...
parents 14726903 f3c4b134
...@@ -51,6 +51,23 @@ compatible (optional) - standard definition ...@@ -51,6 +51,23 @@ compatible (optional) - standard definition
used as a shared pool of DMA buffers for a set of devices. It can used as a shared pool of DMA buffers for a set of devices. It can
be used by an operating system to instantiate the necessary pool be used by an operating system to instantiate the necessary pool
management subsystem if necessary. management subsystem if necessary.
- restricted-dma-pool: This indicates a region of memory meant to be
used as a pool of restricted DMA buffers for a set of devices. The
memory region would be the only region accessible to those devices.
When using this, the no-map and reusable properties must not be set,
so the operating system can create a virtual mapping that will be used
for synchronization. The main purpose for restricted DMA is to
mitigate the lack of DMA access control on systems without an IOMMU,
which could result in the DMA accessing the system memory at
unexpected times and/or unexpected addresses, possibly leading to data
leakage or corruption. The feature on its own provides a basic level
of protection against the DMA overwriting buffer contents at
unexpected times. However, to protect against general data leakage and
system memory corruption, the system needs to provide way to lock down
the memory access, e.g., MPU. Note that since coherent allocation
needs remapping, one must set up another device coherent pool by
shared-dma-pool and use dma_alloc_from_dev_coherent instead for atomic
coherent allocation.
- vendor specific string in the form <vendor>,[<device>-]<usage> - vendor specific string in the form <vendor>,[<device>-]<usage>
no-map (optional) - empty property no-map (optional) - empty property
- Indicates the operating system must not create a virtual mapping - Indicates the operating system must not create a virtual mapping
...@@ -85,10 +102,11 @@ memory-region-names (optional) - a list of names, one for each corresponding ...@@ -85,10 +102,11 @@ memory-region-names (optional) - a list of names, one for each corresponding
Example Example
------- -------
This example defines 3 contiguous regions are defined for Linux kernel: This example defines 4 contiguous regions for Linux kernel:
one default of all device drivers (named linux,cma@72000000 and 64MiB in size), one default of all device drivers (named linux,cma@72000000 and 64MiB in size),
one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB), and one dedicated to the framebuffer device (named framebuffer@78000000, 8MiB),
one for multimedia processing (named multimedia-memory@77000000, 64MiB). one for multimedia processing (named multimedia-memory@77000000, 64MiB), and
one for restricted dma pool (named restricted_dma_reserved@0x50000000, 64MiB).
/ { / {
#address-cells = <1>; #address-cells = <1>;
...@@ -120,6 +138,11 @@ one for multimedia processing (named multimedia-memory@77000000, 64MiB). ...@@ -120,6 +138,11 @@ one for multimedia processing (named multimedia-memory@77000000, 64MiB).
compatible = "acme,multimedia-memory"; compatible = "acme,multimedia-memory";
reg = <0x77000000 0x4000000>; reg = <0x77000000 0x4000000>;
}; };
restricted_dma_reserved: restricted_dma_reserved {
compatible = "restricted-dma-pool";
reg = <0x50000000 0x4000000>;
};
}; };
/* ... */ /* ... */
...@@ -138,4 +161,11 @@ one for multimedia processing (named multimedia-memory@77000000, 64MiB). ...@@ -138,4 +161,11 @@ one for multimedia processing (named multimedia-memory@77000000, 64MiB).
memory-region = <&multimedia_reserved>; memory-region = <&multimedia_reserved>;
/* ... */ /* ... */
}; };
pcie_device: pcie_device@0,0 {
reg = <0x83010000 0x0 0x00000000 0x0 0x00100000
0x83010000 0x0 0x00100000 0x0 0x00100000>;
memory-region = <&restricted_dma_reserved>;
/* ... */
};
}; };
...@@ -63,6 +63,9 @@ void __init svm_swiotlb_init(void) ...@@ -63,6 +63,9 @@ void __init svm_swiotlb_init(void)
int set_memory_encrypted(unsigned long addr, int numpages) int set_memory_encrypted(unsigned long addr, int numpages)
{ {
if (!mem_encrypt_active())
return 0;
if (!PAGE_ALIGNED(addr)) if (!PAGE_ALIGNED(addr))
return -EINVAL; return -EINVAL;
...@@ -73,6 +76,9 @@ int set_memory_encrypted(unsigned long addr, int numpages) ...@@ -73,6 +76,9 @@ int set_memory_encrypted(unsigned long addr, int numpages)
int set_memory_decrypted(unsigned long addr, int numpages) int set_memory_decrypted(unsigned long addr, int numpages)
{ {
if (!mem_encrypt_active())
return 0;
if (!PAGE_ALIGNED(addr)) if (!PAGE_ALIGNED(addr))
return -EINVAL; return -EINVAL;
......
...@@ -187,9 +187,9 @@ static void pv_init(void) ...@@ -187,9 +187,9 @@ static void pv_init(void)
return; return;
/* make sure bounce buffers are shared */ /* make sure bounce buffers are shared */
swiotlb_force = SWIOTLB_FORCE;
swiotlb_init(1); swiotlb_init(1);
swiotlb_update_mem_attributes(); swiotlb_update_mem_attributes();
swiotlb_force = SWIOTLB_FORCE;
} }
void __init mem_init(void) void __init mem_init(void)
......
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include <linux/netdevice.h> #include <linux/netdevice.h>
#include <linux/sched/signal.h> #include <linux/sched/signal.h>
#include <linux/sched/mm.h> #include <linux/sched/mm.h>
#include <linux/swiotlb.h>
#include <linux/sysfs.h> #include <linux/sysfs.h>
#include <linux/dma-map-ops.h> /* for dma_default_coherent */ #include <linux/dma-map-ops.h> /* for dma_default_coherent */
...@@ -2851,6 +2852,9 @@ void device_initialize(struct device *dev) ...@@ -2851,6 +2852,9 @@ void device_initialize(struct device *dev)
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
dev->dma_coherent = dma_default_coherent; dev->dma_coherent = dma_default_coherent;
#endif #endif
#ifdef CONFIG_SWIOTLB
dev->dma_io_tlb_mem = &io_tlb_default_mem;
#endif
} }
EXPORT_SYMBOL_GPL(device_initialize); EXPORT_SYMBOL_GPL(device_initialize);
......
...@@ -42,7 +42,7 @@ static int i915_gem_object_get_pages_internal(struct drm_i915_gem_object *obj) ...@@ -42,7 +42,7 @@ static int i915_gem_object_get_pages_internal(struct drm_i915_gem_object *obj)
max_order = MAX_ORDER; max_order = MAX_ORDER;
#ifdef CONFIG_SWIOTLB #ifdef CONFIG_SWIOTLB
if (is_swiotlb_active()) { if (is_swiotlb_active(obj->base.dev->dev)) {
unsigned int max_segment; unsigned int max_segment;
max_segment = swiotlb_max_segment(); max_segment = swiotlb_max_segment();
......
...@@ -276,7 +276,7 @@ nouveau_ttm_init(struct nouveau_drm *drm) ...@@ -276,7 +276,7 @@ nouveau_ttm_init(struct nouveau_drm *drm)
} }
#if IS_ENABLED(CONFIG_SWIOTLB) && IS_ENABLED(CONFIG_X86) #if IS_ENABLED(CONFIG_SWIOTLB) && IS_ENABLED(CONFIG_X86)
need_swiotlb = is_swiotlb_active(); need_swiotlb = is_swiotlb_active(dev->dev);
#endif #endif
ret = ttm_device_init(&drm->ttm.bdev, &nouveau_bo_driver, drm->dev->dev, ret = ttm_device_init(&drm->ttm.bdev, &nouveau_bo_driver, drm->dev->dev,
......
...@@ -506,7 +506,7 @@ static void __iommu_dma_unmap_swiotlb(struct device *dev, dma_addr_t dma_addr, ...@@ -506,7 +506,7 @@ static void __iommu_dma_unmap_swiotlb(struct device *dev, dma_addr_t dma_addr,
__iommu_dma_unmap(dev, dma_addr, size); __iommu_dma_unmap(dev, dma_addr, size);
if (unlikely(is_swiotlb_buffer(phys))) if (unlikely(is_swiotlb_buffer(dev, phys)))
swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs); swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
} }
...@@ -577,7 +577,7 @@ static dma_addr_t __iommu_dma_map_swiotlb(struct device *dev, phys_addr_t phys, ...@@ -577,7 +577,7 @@ static dma_addr_t __iommu_dma_map_swiotlb(struct device *dev, phys_addr_t phys,
} }
iova = __iommu_dma_map(dev, phys, aligned_size, prot, dma_mask); iova = __iommu_dma_map(dev, phys, aligned_size, prot, dma_mask);
if (iova == DMA_MAPPING_ERROR && is_swiotlb_buffer(phys)) if (iova == DMA_MAPPING_ERROR && is_swiotlb_buffer(dev, phys))
swiotlb_tbl_unmap_single(dev, phys, org_size, dir, attrs); swiotlb_tbl_unmap_single(dev, phys, org_size, dir, attrs);
return iova; return iova;
} }
...@@ -784,7 +784,7 @@ static void iommu_dma_sync_single_for_cpu(struct device *dev, ...@@ -784,7 +784,7 @@ static void iommu_dma_sync_single_for_cpu(struct device *dev,
if (!dev_is_dma_coherent(dev)) if (!dev_is_dma_coherent(dev))
arch_sync_dma_for_cpu(phys, size, dir); arch_sync_dma_for_cpu(phys, size, dir);
if (is_swiotlb_buffer(phys)) if (is_swiotlb_buffer(dev, phys))
swiotlb_sync_single_for_cpu(dev, phys, size, dir); swiotlb_sync_single_for_cpu(dev, phys, size, dir);
} }
...@@ -797,7 +797,7 @@ static void iommu_dma_sync_single_for_device(struct device *dev, ...@@ -797,7 +797,7 @@ static void iommu_dma_sync_single_for_device(struct device *dev,
return; return;
phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle); phys = iommu_iova_to_phys(iommu_get_dma_domain(dev), dma_handle);
if (is_swiotlb_buffer(phys)) if (is_swiotlb_buffer(dev, phys))
swiotlb_sync_single_for_device(dev, phys, size, dir); swiotlb_sync_single_for_device(dev, phys, size, dir);
if (!dev_is_dma_coherent(dev)) if (!dev_is_dma_coherent(dev))
...@@ -818,7 +818,7 @@ static void iommu_dma_sync_sg_for_cpu(struct device *dev, ...@@ -818,7 +818,7 @@ static void iommu_dma_sync_sg_for_cpu(struct device *dev,
if (!dev_is_dma_coherent(dev)) if (!dev_is_dma_coherent(dev))
arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir); arch_sync_dma_for_cpu(sg_phys(sg), sg->length, dir);
if (is_swiotlb_buffer(sg_phys(sg))) if (is_swiotlb_buffer(dev, sg_phys(sg)))
swiotlb_sync_single_for_cpu(dev, sg_phys(sg), swiotlb_sync_single_for_cpu(dev, sg_phys(sg),
sg->length, dir); sg->length, dir);
} }
...@@ -835,7 +835,7 @@ static void iommu_dma_sync_sg_for_device(struct device *dev, ...@@ -835,7 +835,7 @@ static void iommu_dma_sync_sg_for_device(struct device *dev,
return; return;
for_each_sg(sgl, sg, nelems, i) { for_each_sg(sgl, sg, nelems, i) {
if (is_swiotlb_buffer(sg_phys(sg))) if (is_swiotlb_buffer(dev, sg_phys(sg)))
swiotlb_sync_single_for_device(dev, sg_phys(sg), swiotlb_sync_single_for_device(dev, sg_phys(sg),
sg->length, dir); sg->length, dir);
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/of_address.h> #include <linux/of_address.h>
#include <linux/of_iommu.h> #include <linux/of_iommu.h>
#include <linux/of_reserved_mem.h>
#include <linux/dma-direct.h> /* for bus_dma_region */ #include <linux/dma-direct.h> /* for bus_dma_region */
#include <linux/dma-map-ops.h> #include <linux/dma-map-ops.h>
#include <linux/init.h> #include <linux/init.h>
...@@ -52,6 +53,42 @@ int of_device_add(struct platform_device *ofdev) ...@@ -52,6 +53,42 @@ int of_device_add(struct platform_device *ofdev)
return device_add(&ofdev->dev); return device_add(&ofdev->dev);
} }
static void
of_dma_set_restricted_buffer(struct device *dev, struct device_node *np)
{
struct device_node *node, *of_node = dev->of_node;
int count, i;
if (!IS_ENABLED(CONFIG_DMA_RESTRICTED_POOL))
return;
count = of_property_count_elems_of_size(of_node, "memory-region",
sizeof(u32));
/*
* If dev->of_node doesn't exist or doesn't contain memory-region, try
* the OF node having DMA configuration.
*/
if (count <= 0) {
of_node = np;
count = of_property_count_elems_of_size(
of_node, "memory-region", sizeof(u32));
}
for (i = 0; i < count; i++) {
node = of_parse_phandle(of_node, "memory-region", i);
/*
* There might be multiple memory regions, but only one
* restricted-dma-pool region is allowed.
*/
if (of_device_is_compatible(node, "restricted-dma-pool") &&
of_device_is_available(node))
break;
}
if (i != count && of_reserved_mem_device_init_by_idx(dev, of_node, i))
dev_warn(dev, "failed to initialise \"restricted-dma-pool\" memory node\n");
}
/** /**
* of_dma_configure_id - Setup DMA configuration * of_dma_configure_id - Setup DMA configuration
* @dev: Device to apply DMA configuration * @dev: Device to apply DMA configuration
...@@ -165,6 +202,9 @@ int of_dma_configure_id(struct device *dev, struct device_node *np, ...@@ -165,6 +202,9 @@ int of_dma_configure_id(struct device *dev, struct device_node *np,
arch_setup_dma_ops(dev, dma_start, size, iommu, coherent); arch_setup_dma_ops(dev, dma_start, size, iommu, coherent);
if (!iommu)
of_dma_set_restricted_buffer(dev, np);
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(of_dma_configure_id); EXPORT_SYMBOL_GPL(of_dma_configure_id);
......
...@@ -699,7 +699,7 @@ static int pcifront_connect_and_init_dma(struct pcifront_device *pdev) ...@@ -699,7 +699,7 @@ static int pcifront_connect_and_init_dma(struct pcifront_device *pdev)
spin_unlock(&pcifront_dev_lock); spin_unlock(&pcifront_dev_lock);
if (!err && !is_swiotlb_active()) { if (!err && !is_swiotlb_active(&pdev->xdev->dev)) {
err = pci_xen_swiotlb_init_late(); err = pci_xen_swiotlb_init_late();
if (err) if (err)
dev_err(&pdev->xdev->dev, "Could not setup SWIOTLB!\n"); dev_err(&pdev->xdev->dev, "Could not setup SWIOTLB!\n");
......
...@@ -100,7 +100,7 @@ static int is_xen_swiotlb_buffer(struct device *dev, dma_addr_t dma_addr) ...@@ -100,7 +100,7 @@ static int is_xen_swiotlb_buffer(struct device *dev, dma_addr_t dma_addr)
* in our domain. Therefore _only_ check address within our domain. * in our domain. Therefore _only_ check address within our domain.
*/ */
if (pfn_valid(PFN_DOWN(paddr))) if (pfn_valid(PFN_DOWN(paddr)))
return is_swiotlb_buffer(paddr); return is_swiotlb_buffer(dev, paddr);
return 0; return 0;
} }
...@@ -164,7 +164,7 @@ int __ref xen_swiotlb_init(void) ...@@ -164,7 +164,7 @@ int __ref xen_swiotlb_init(void)
int rc = -ENOMEM; int rc = -ENOMEM;
char *start; char *start;
if (io_tlb_default_mem != NULL) { if (io_tlb_default_mem.nslabs) {
pr_warn("swiotlb buffer already initialized\n"); pr_warn("swiotlb buffer already initialized\n");
return -EEXIST; return -EEXIST;
} }
...@@ -374,7 +374,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page, ...@@ -374,7 +374,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
if (dma_capable(dev, dev_addr, size, true) && if (dma_capable(dev, dev_addr, size, true) &&
!range_straddles_page_boundary(phys, size) && !range_straddles_page_boundary(phys, size) &&
!xen_arch_need_swiotlb(dev, phys, dev_addr) && !xen_arch_need_swiotlb(dev, phys, dev_addr) &&
swiotlb_force != SWIOTLB_FORCE) !is_swiotlb_force_bounce(dev))
goto done; goto done;
/* /*
...@@ -547,7 +547,7 @@ xen_swiotlb_sync_sg_for_device(struct device *dev, struct scatterlist *sgl, ...@@ -547,7 +547,7 @@ xen_swiotlb_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
static int static int
xen_swiotlb_dma_supported(struct device *hwdev, u64 mask) xen_swiotlb_dma_supported(struct device *hwdev, u64 mask)
{ {
return xen_phys_to_dma(hwdev, io_tlb_default_mem->end - 1) <= mask; return xen_phys_to_dma(hwdev, io_tlb_default_mem.end - 1) <= mask;
} }
const struct dma_map_ops xen_swiotlb_dma_ops = { const struct dma_map_ops xen_swiotlb_dma_ops = {
......
...@@ -424,6 +424,7 @@ struct dev_links_info { ...@@ -424,6 +424,7 @@ struct dev_links_info {
* @dma_pools: Dma pools (if dma'ble device). * @dma_pools: Dma pools (if dma'ble device).
* @dma_mem: Internal for coherent mem override. * @dma_mem: Internal for coherent mem override.
* @cma_area: Contiguous memory area for dma allocations * @cma_area: Contiguous memory area for dma allocations
* @dma_io_tlb_mem: Pointer to the swiotlb pool used. Not for driver use.
* @archdata: For arch-specific additions. * @archdata: For arch-specific additions.
* @of_node: Associated device tree node. * @of_node: Associated device tree node.
* @fwnode: Associated device node supplied by platform firmware. * @fwnode: Associated device node supplied by platform firmware.
...@@ -533,6 +534,9 @@ struct device { ...@@ -533,6 +534,9 @@ struct device {
#ifdef CONFIG_DMA_CMA #ifdef CONFIG_DMA_CMA
struct cma *cma_area; /* contiguous memory area for dma struct cma *cma_area; /* contiguous memory area for dma
allocations */ allocations */
#endif
#ifdef CONFIG_SWIOTLB
struct io_tlb_mem *dma_io_tlb_mem;
#endif #endif
/* arch specific additions */ /* arch specific additions */
struct dev_archdata archdata; struct dev_archdata archdata;
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#ifndef __LINUX_SWIOTLB_H #ifndef __LINUX_SWIOTLB_H
#define __LINUX_SWIOTLB_H #define __LINUX_SWIOTLB_H
#include <linux/device.h>
#include <linux/dma-direction.h> #include <linux/dma-direction.h>
#include <linux/init.h> #include <linux/init.h>
#include <linux/types.h> #include <linux/types.h>
...@@ -72,7 +73,8 @@ extern enum swiotlb_force swiotlb_force; ...@@ -72,7 +73,8 @@ extern enum swiotlb_force swiotlb_force;
* range check to see if the memory was in fact allocated by this * range check to see if the memory was in fact allocated by this
* API. * API.
* @nslabs: The number of IO TLB blocks (in groups of 64) between @start and * @nslabs: The number of IO TLB blocks (in groups of 64) between @start and
* @end. This is command line adjustable via setup_io_tlb_npages. * @end. For default swiotlb, this is command line adjustable via
* setup_io_tlb_npages.
* @used: The number of used IO TLB block. * @used: The number of used IO TLB block.
* @list: The free list describing the number of free entries available * @list: The free list describing the number of free entries available
* from each index. * from each index.
...@@ -83,6 +85,8 @@ extern enum swiotlb_force swiotlb_force; ...@@ -83,6 +85,8 @@ extern enum swiotlb_force swiotlb_force;
* unmap calls. * unmap calls.
* @debugfs: The dentry to debugfs. * @debugfs: The dentry to debugfs.
* @late_alloc: %true if allocated using the page allocator * @late_alloc: %true if allocated using the page allocator
* @force_bounce: %true if swiotlb bouncing is forced
* @for_alloc: %true if the pool is used for memory allocation
*/ */
struct io_tlb_mem { struct io_tlb_mem {
phys_addr_t start; phys_addr_t start;
...@@ -93,29 +97,42 @@ struct io_tlb_mem { ...@@ -93,29 +97,42 @@ struct io_tlb_mem {
spinlock_t lock; spinlock_t lock;
struct dentry *debugfs; struct dentry *debugfs;
bool late_alloc; bool late_alloc;
bool force_bounce;
bool for_alloc;
struct io_tlb_slot { struct io_tlb_slot {
phys_addr_t orig_addr; phys_addr_t orig_addr;
size_t alloc_size; size_t alloc_size;
unsigned int list; unsigned int list;
} slots[]; } *slots;
}; };
extern struct io_tlb_mem *io_tlb_default_mem; extern struct io_tlb_mem io_tlb_default_mem;
static inline bool is_swiotlb_buffer(phys_addr_t paddr) static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr)
{ {
struct io_tlb_mem *mem = io_tlb_default_mem; struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
return mem && paddr >= mem->start && paddr < mem->end; return mem && paddr >= mem->start && paddr < mem->end;
} }
static inline bool is_swiotlb_force_bounce(struct device *dev)
{
struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
return mem && mem->force_bounce;
}
void __init swiotlb_exit(void); void __init swiotlb_exit(void);
unsigned int swiotlb_max_segment(void); unsigned int swiotlb_max_segment(void);
size_t swiotlb_max_mapping_size(struct device *dev); size_t swiotlb_max_mapping_size(struct device *dev);
bool is_swiotlb_active(void); bool is_swiotlb_active(struct device *dev);
void __init swiotlb_adjust_size(unsigned long size); void __init swiotlb_adjust_size(unsigned long size);
#else #else
#define swiotlb_force SWIOTLB_NO_FORCE #define swiotlb_force SWIOTLB_NO_FORCE
static inline bool is_swiotlb_buffer(phys_addr_t paddr) static inline bool is_swiotlb_buffer(struct device *dev, phys_addr_t paddr)
{
return false;
}
static inline bool is_swiotlb_force_bounce(struct device *dev)
{ {
return false; return false;
} }
...@@ -131,7 +148,7 @@ static inline size_t swiotlb_max_mapping_size(struct device *dev) ...@@ -131,7 +148,7 @@ static inline size_t swiotlb_max_mapping_size(struct device *dev)
return SIZE_MAX; return SIZE_MAX;
} }
static inline bool is_swiotlb_active(void) static inline bool is_swiotlb_active(struct device *dev)
{ {
return false; return false;
} }
...@@ -144,4 +161,28 @@ static inline void swiotlb_adjust_size(unsigned long size) ...@@ -144,4 +161,28 @@ static inline void swiotlb_adjust_size(unsigned long size)
extern void swiotlb_print_info(void); extern void swiotlb_print_info(void);
extern void swiotlb_set_max_segment(unsigned int); extern void swiotlb_set_max_segment(unsigned int);
#ifdef CONFIG_DMA_RESTRICTED_POOL
struct page *swiotlb_alloc(struct device *dev, size_t size);
bool swiotlb_free(struct device *dev, struct page *page, size_t size);
static inline bool is_swiotlb_for_alloc(struct device *dev)
{
return dev->dma_io_tlb_mem->for_alloc;
}
#else
static inline struct page *swiotlb_alloc(struct device *dev, size_t size)
{
return NULL;
}
static inline bool swiotlb_free(struct device *dev, struct page *page,
size_t size)
{
return false;
}
static inline bool is_swiotlb_for_alloc(struct device *dev)
{
return false;
}
#endif /* CONFIG_DMA_RESTRICTED_POOL */
#endif /* __LINUX_SWIOTLB_H */ #endif /* __LINUX_SWIOTLB_H */
...@@ -80,6 +80,19 @@ config SWIOTLB ...@@ -80,6 +80,19 @@ config SWIOTLB
bool bool
select NEED_DMA_MAP_STATE select NEED_DMA_MAP_STATE
config DMA_RESTRICTED_POOL
bool "DMA Restricted Pool"
depends on OF && OF_RESERVED_MEM && SWIOTLB
help
This enables support for restricted DMA pools which provide a level of
DMA memory protection on systems with limited hardware protection
capabilities, such as those lacking an IOMMU.
For more information see
<Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt>
and <kernel/dma/swiotlb.c>.
If unsure, say "n".
# #
# Should be selected if we can mmap non-coherent mappings to userspace. # Should be selected if we can mmap non-coherent mappings to userspace.
# The only thing that is really required is a way to set an uncached bit # The only thing that is really required is a way to set an uncached bit
......
...@@ -75,6 +75,15 @@ static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size) ...@@ -75,6 +75,15 @@ static bool dma_coherent_ok(struct device *dev, phys_addr_t phys, size_t size)
min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit); min_not_zero(dev->coherent_dma_mask, dev->bus_dma_limit);
} }
static void __dma_direct_free_pages(struct device *dev, struct page *page,
size_t size)
{
if (IS_ENABLED(CONFIG_DMA_RESTRICTED_POOL) &&
swiotlb_free(dev, page, size))
return;
dma_free_contiguous(dev, page, size);
}
static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size, static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
gfp_t gfp) gfp_t gfp)
{ {
...@@ -86,6 +95,16 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size, ...@@ -86,6 +95,16 @@ static struct page *__dma_direct_alloc_pages(struct device *dev, size_t size,
gfp |= dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask, gfp |= dma_direct_optimal_gfp_mask(dev, dev->coherent_dma_mask,
&phys_limit); &phys_limit);
if (IS_ENABLED(CONFIG_DMA_RESTRICTED_POOL) &&
is_swiotlb_for_alloc(dev)) {
page = swiotlb_alloc(dev, size);
if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
__dma_direct_free_pages(dev, page, size);
return NULL;
}
return page;
}
page = dma_alloc_contiguous(dev, size, gfp); page = dma_alloc_contiguous(dev, size, gfp);
if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) { if (page && !dma_coherent_ok(dev, page_to_phys(page), size)) {
dma_free_contiguous(dev, page, size); dma_free_contiguous(dev, page, size);
...@@ -142,7 +161,7 @@ void *dma_direct_alloc(struct device *dev, size_t size, ...@@ -142,7 +161,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
gfp |= __GFP_NOWARN; gfp |= __GFP_NOWARN;
if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) && if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
!force_dma_unencrypted(dev)) { !force_dma_unencrypted(dev) && !is_swiotlb_for_alloc(dev)) {
page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO); page = __dma_direct_alloc_pages(dev, size, gfp & ~__GFP_ZERO);
if (!page) if (!page)
return NULL; return NULL;
...@@ -157,7 +176,8 @@ void *dma_direct_alloc(struct device *dev, size_t size, ...@@ -157,7 +176,8 @@ void *dma_direct_alloc(struct device *dev, size_t size,
if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) && if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) && !IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
!IS_ENABLED(CONFIG_DMA_GLOBAL_POOL) && !IS_ENABLED(CONFIG_DMA_GLOBAL_POOL) &&
!dev_is_dma_coherent(dev)) !dev_is_dma_coherent(dev) &&
!is_swiotlb_for_alloc(dev))
return arch_dma_alloc(dev, size, dma_handle, gfp, attrs); return arch_dma_alloc(dev, size, dma_handle, gfp, attrs);
if (IS_ENABLED(CONFIG_DMA_GLOBAL_POOL) && if (IS_ENABLED(CONFIG_DMA_GLOBAL_POOL) &&
...@@ -167,11 +187,16 @@ void *dma_direct_alloc(struct device *dev, size_t size, ...@@ -167,11 +187,16 @@ void *dma_direct_alloc(struct device *dev, size_t size,
/* /*
* Remapping or decrypting memory may block. If either is required and * Remapping or decrypting memory may block. If either is required and
* we can't block, allocate the memory from the atomic pools. * we can't block, allocate the memory from the atomic pools.
* If restricted DMA (i.e., is_swiotlb_for_alloc) is required, one must
* set up another device coherent pool by shared-dma-pool and use
* dma_alloc_from_dev_coherent instead.
*/ */
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) && if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
!gfpflags_allow_blocking(gfp) && !gfpflags_allow_blocking(gfp) &&
(force_dma_unencrypted(dev) || (force_dma_unencrypted(dev) ||
(IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) && !dev_is_dma_coherent(dev)))) (IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
!dev_is_dma_coherent(dev))) &&
!is_swiotlb_for_alloc(dev))
return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp); return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
/* we always manually zero the memory once we are done */ /* we always manually zero the memory once we are done */
...@@ -242,7 +267,7 @@ void *dma_direct_alloc(struct device *dev, size_t size, ...@@ -242,7 +267,7 @@ void *dma_direct_alloc(struct device *dev, size_t size,
return NULL; return NULL;
} }
out_free_pages: out_free_pages:
dma_free_contiguous(dev, page, size); __dma_direct_free_pages(dev, page, size);
return NULL; return NULL;
} }
...@@ -252,7 +277,7 @@ void dma_direct_free(struct device *dev, size_t size, ...@@ -252,7 +277,7 @@ void dma_direct_free(struct device *dev, size_t size,
unsigned int page_order = get_order(size); unsigned int page_order = get_order(size);
if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) && if ((attrs & DMA_ATTR_NO_KERNEL_MAPPING) &&
!force_dma_unencrypted(dev)) { !force_dma_unencrypted(dev) && !is_swiotlb_for_alloc(dev)) {
/* cpu_addr is a struct page cookie, not a kernel address */ /* cpu_addr is a struct page cookie, not a kernel address */
dma_free_contiguous(dev, cpu_addr, size); dma_free_contiguous(dev, cpu_addr, size);
return; return;
...@@ -261,7 +286,8 @@ void dma_direct_free(struct device *dev, size_t size, ...@@ -261,7 +286,8 @@ void dma_direct_free(struct device *dev, size_t size,
if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) && if (!IS_ENABLED(CONFIG_ARCH_HAS_DMA_SET_UNCACHED) &&
!IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) && !IS_ENABLED(CONFIG_DMA_DIRECT_REMAP) &&
!IS_ENABLED(CONFIG_DMA_GLOBAL_POOL) && !IS_ENABLED(CONFIG_DMA_GLOBAL_POOL) &&
!dev_is_dma_coherent(dev)) { !dev_is_dma_coherent(dev) &&
!is_swiotlb_for_alloc(dev)) {
arch_dma_free(dev, size, cpu_addr, dma_addr, attrs); arch_dma_free(dev, size, cpu_addr, dma_addr, attrs);
return; return;
} }
...@@ -286,7 +312,7 @@ void dma_direct_free(struct device *dev, size_t size, ...@@ -286,7 +312,7 @@ void dma_direct_free(struct device *dev, size_t size,
else if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_CLEAR_UNCACHED)) else if (IS_ENABLED(CONFIG_ARCH_HAS_DMA_CLEAR_UNCACHED))
arch_dma_clear_uncached(cpu_addr, size); arch_dma_clear_uncached(cpu_addr, size);
dma_free_contiguous(dev, dma_direct_to_page(dev, dma_addr), size); __dma_direct_free_pages(dev, dma_direct_to_page(dev, dma_addr), size);
} }
struct page *dma_direct_alloc_pages(struct device *dev, size_t size, struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
...@@ -296,7 +322,8 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size, ...@@ -296,7 +322,8 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
void *ret; void *ret;
if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) && if (IS_ENABLED(CONFIG_DMA_COHERENT_POOL) &&
force_dma_unencrypted(dev) && !gfpflags_allow_blocking(gfp)) force_dma_unencrypted(dev) && !gfpflags_allow_blocking(gfp) &&
!is_swiotlb_for_alloc(dev))
return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp); return dma_direct_alloc_from_pool(dev, size, dma_handle, gfp);
page = __dma_direct_alloc_pages(dev, size, gfp); page = __dma_direct_alloc_pages(dev, size, gfp);
...@@ -323,7 +350,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size, ...@@ -323,7 +350,7 @@ struct page *dma_direct_alloc_pages(struct device *dev, size_t size,
*dma_handle = phys_to_dma_direct(dev, page_to_phys(page)); *dma_handle = phys_to_dma_direct(dev, page_to_phys(page));
return page; return page;
out_free_pages: out_free_pages:
dma_free_contiguous(dev, page, size); __dma_direct_free_pages(dev, page, size);
return NULL; return NULL;
} }
...@@ -342,7 +369,7 @@ void dma_direct_free_pages(struct device *dev, size_t size, ...@@ -342,7 +369,7 @@ void dma_direct_free_pages(struct device *dev, size_t size,
if (force_dma_unencrypted(dev)) if (force_dma_unencrypted(dev))
set_memory_encrypted((unsigned long)vaddr, 1 << page_order); set_memory_encrypted((unsigned long)vaddr, 1 << page_order);
dma_free_contiguous(dev, page, size); __dma_direct_free_pages(dev, page, size);
} }
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
...@@ -356,7 +383,7 @@ void dma_direct_sync_sg_for_device(struct device *dev, ...@@ -356,7 +383,7 @@ void dma_direct_sync_sg_for_device(struct device *dev,
for_each_sg(sgl, sg, nents, i) { for_each_sg(sgl, sg, nents, i) {
phys_addr_t paddr = dma_to_phys(dev, sg_dma_address(sg)); phys_addr_t paddr = dma_to_phys(dev, sg_dma_address(sg));
if (unlikely(is_swiotlb_buffer(paddr))) if (unlikely(is_swiotlb_buffer(dev, paddr)))
swiotlb_sync_single_for_device(dev, paddr, sg->length, swiotlb_sync_single_for_device(dev, paddr, sg->length,
dir); dir);
...@@ -382,7 +409,7 @@ void dma_direct_sync_sg_for_cpu(struct device *dev, ...@@ -382,7 +409,7 @@ void dma_direct_sync_sg_for_cpu(struct device *dev,
if (!dev_is_dma_coherent(dev)) if (!dev_is_dma_coherent(dev))
arch_sync_dma_for_cpu(paddr, sg->length, dir); arch_sync_dma_for_cpu(paddr, sg->length, dir);
if (unlikely(is_swiotlb_buffer(paddr))) if (unlikely(is_swiotlb_buffer(dev, paddr)))
swiotlb_sync_single_for_cpu(dev, paddr, sg->length, swiotlb_sync_single_for_cpu(dev, paddr, sg->length,
dir); dir);
...@@ -510,8 +537,8 @@ int dma_direct_supported(struct device *dev, u64 mask) ...@@ -510,8 +537,8 @@ int dma_direct_supported(struct device *dev, u64 mask)
size_t dma_direct_max_mapping_size(struct device *dev) size_t dma_direct_max_mapping_size(struct device *dev)
{ {
/* If SWIOTLB is active, use its maximum mapping size */ /* If SWIOTLB is active, use its maximum mapping size */
if (is_swiotlb_active() && if (is_swiotlb_active(dev) &&
(dma_addressing_limited(dev) || swiotlb_force == SWIOTLB_FORCE)) (dma_addressing_limited(dev) || is_swiotlb_force_bounce(dev)))
return swiotlb_max_mapping_size(dev); return swiotlb_max_mapping_size(dev);
return SIZE_MAX; return SIZE_MAX;
} }
...@@ -519,7 +546,7 @@ size_t dma_direct_max_mapping_size(struct device *dev) ...@@ -519,7 +546,7 @@ size_t dma_direct_max_mapping_size(struct device *dev)
bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr) bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr)
{ {
return !dev_is_dma_coherent(dev) || return !dev_is_dma_coherent(dev) ||
is_swiotlb_buffer(dma_to_phys(dev, dma_addr)); is_swiotlb_buffer(dev, dma_to_phys(dev, dma_addr));
} }
/** /**
......
...@@ -56,7 +56,7 @@ static inline void dma_direct_sync_single_for_device(struct device *dev, ...@@ -56,7 +56,7 @@ static inline void dma_direct_sync_single_for_device(struct device *dev,
{ {
phys_addr_t paddr = dma_to_phys(dev, addr); phys_addr_t paddr = dma_to_phys(dev, addr);
if (unlikely(is_swiotlb_buffer(paddr))) if (unlikely(is_swiotlb_buffer(dev, paddr)))
swiotlb_sync_single_for_device(dev, paddr, size, dir); swiotlb_sync_single_for_device(dev, paddr, size, dir);
if (!dev_is_dma_coherent(dev)) if (!dev_is_dma_coherent(dev))
...@@ -73,7 +73,7 @@ static inline void dma_direct_sync_single_for_cpu(struct device *dev, ...@@ -73,7 +73,7 @@ static inline void dma_direct_sync_single_for_cpu(struct device *dev,
arch_sync_dma_for_cpu_all(); arch_sync_dma_for_cpu_all();
} }
if (unlikely(is_swiotlb_buffer(paddr))) if (unlikely(is_swiotlb_buffer(dev, paddr)))
swiotlb_sync_single_for_cpu(dev, paddr, size, dir); swiotlb_sync_single_for_cpu(dev, paddr, size, dir);
if (dir == DMA_FROM_DEVICE) if (dir == DMA_FROM_DEVICE)
...@@ -87,7 +87,7 @@ static inline dma_addr_t dma_direct_map_page(struct device *dev, ...@@ -87,7 +87,7 @@ static inline dma_addr_t dma_direct_map_page(struct device *dev,
phys_addr_t phys = page_to_phys(page) + offset; phys_addr_t phys = page_to_phys(page) + offset;
dma_addr_t dma_addr = phys_to_dma(dev, phys); dma_addr_t dma_addr = phys_to_dma(dev, phys);
if (unlikely(swiotlb_force == SWIOTLB_FORCE)) if (is_swiotlb_force_bounce(dev))
return swiotlb_map(dev, phys, size, dir, attrs); return swiotlb_map(dev, phys, size, dir, attrs);
if (unlikely(!dma_capable(dev, dma_addr, size, true))) { if (unlikely(!dma_capable(dev, dma_addr, size, true))) {
...@@ -113,7 +113,7 @@ static inline void dma_direct_unmap_page(struct device *dev, dma_addr_t addr, ...@@ -113,7 +113,7 @@ static inline void dma_direct_unmap_page(struct device *dev, dma_addr_t addr,
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC)) if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC))
dma_direct_sync_single_for_cpu(dev, addr, size, dir); dma_direct_sync_single_for_cpu(dev, addr, size, dir);
if (unlikely(is_swiotlb_buffer(phys))) if (unlikely(is_swiotlb_buffer(dev, phys)))
swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs); swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
} }
#endif /* _KERNEL_DMA_DIRECT_H */ #endif /* _KERNEL_DMA_DIRECT_H */
...@@ -39,6 +39,13 @@ ...@@ -39,6 +39,13 @@
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
#include <linux/debugfs.h> #include <linux/debugfs.h>
#endif #endif
#ifdef CONFIG_DMA_RESTRICTED_POOL
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_fdt.h>
#include <linux/of_reserved_mem.h>
#include <linux/slab.h>
#endif
#include <asm/io.h> #include <asm/io.h>
#include <asm/dma.h> #include <asm/dma.h>
...@@ -63,7 +70,7 @@ ...@@ -63,7 +70,7 @@
enum swiotlb_force swiotlb_force; enum swiotlb_force swiotlb_force;
struct io_tlb_mem *io_tlb_default_mem; struct io_tlb_mem io_tlb_default_mem;
/* /*
* Max segment that we can provide which (if pages are contingous) will * Max segment that we can provide which (if pages are contingous) will
...@@ -94,7 +101,7 @@ early_param("swiotlb", setup_io_tlb_npages); ...@@ -94,7 +101,7 @@ early_param("swiotlb", setup_io_tlb_npages);
unsigned int swiotlb_max_segment(void) unsigned int swiotlb_max_segment(void)
{ {
return io_tlb_default_mem ? max_segment : 0; return io_tlb_default_mem.nslabs ? max_segment : 0;
} }
EXPORT_SYMBOL_GPL(swiotlb_max_segment); EXPORT_SYMBOL_GPL(swiotlb_max_segment);
...@@ -127,9 +134,9 @@ void __init swiotlb_adjust_size(unsigned long size) ...@@ -127,9 +134,9 @@ void __init swiotlb_adjust_size(unsigned long size)
void swiotlb_print_info(void) void swiotlb_print_info(void)
{ {
struct io_tlb_mem *mem = io_tlb_default_mem; struct io_tlb_mem *mem = &io_tlb_default_mem;
if (!mem) { if (!mem->nslabs) {
pr_warn("No low mem\n"); pr_warn("No low mem\n");
return; return;
} }
...@@ -156,11 +163,11 @@ static inline unsigned long nr_slots(u64 val) ...@@ -156,11 +163,11 @@ static inline unsigned long nr_slots(u64 val)
*/ */
void __init swiotlb_update_mem_attributes(void) void __init swiotlb_update_mem_attributes(void)
{ {
struct io_tlb_mem *mem = io_tlb_default_mem; struct io_tlb_mem *mem = &io_tlb_default_mem;
void *vaddr; void *vaddr;
unsigned long bytes; unsigned long bytes;
if (!mem || mem->late_alloc) if (!mem->nslabs || mem->late_alloc)
return; return;
vaddr = phys_to_virt(mem->start); vaddr = phys_to_virt(mem->start);
bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT); bytes = PAGE_ALIGN(mem->nslabs << IO_TLB_SHIFT);
...@@ -168,36 +175,50 @@ void __init swiotlb_update_mem_attributes(void) ...@@ -168,36 +175,50 @@ void __init swiotlb_update_mem_attributes(void)
memset(vaddr, 0, bytes); memset(vaddr, 0, bytes);
} }
int __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose) static void swiotlb_init_io_tlb_mem(struct io_tlb_mem *mem, phys_addr_t start,
unsigned long nslabs, bool late_alloc)
{ {
void *vaddr = phys_to_virt(start);
unsigned long bytes = nslabs << IO_TLB_SHIFT, i; unsigned long bytes = nslabs << IO_TLB_SHIFT, i;
struct io_tlb_mem *mem;
size_t alloc_size;
if (swiotlb_force == SWIOTLB_NO_FORCE)
return 0;
/* protect against double initialization */
if (WARN_ON_ONCE(io_tlb_default_mem))
return -ENOMEM;
alloc_size = PAGE_ALIGN(struct_size(mem, slots, nslabs));
mem = memblock_alloc(alloc_size, PAGE_SIZE);
if (!mem)
panic("%s: Failed to allocate %zu bytes align=0x%lx\n",
__func__, alloc_size, PAGE_SIZE);
mem->nslabs = nslabs; mem->nslabs = nslabs;
mem->start = __pa(tlb); mem->start = start;
mem->end = mem->start + bytes; mem->end = mem->start + bytes;
mem->index = 0; mem->index = 0;
mem->late_alloc = late_alloc;
if (swiotlb_force == SWIOTLB_FORCE)
mem->force_bounce = true;
spin_lock_init(&mem->lock); spin_lock_init(&mem->lock);
for (i = 0; i < mem->nslabs; i++) { for (i = 0; i < mem->nslabs; i++) {
mem->slots[i].list = IO_TLB_SEGSIZE - io_tlb_offset(i); mem->slots[i].list = IO_TLB_SEGSIZE - io_tlb_offset(i);
mem->slots[i].orig_addr = INVALID_PHYS_ADDR; mem->slots[i].orig_addr = INVALID_PHYS_ADDR;
mem->slots[i].alloc_size = 0; mem->slots[i].alloc_size = 0;
} }
memset(vaddr, 0, bytes);
}
int __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose)
{
struct io_tlb_mem *mem = &io_tlb_default_mem;
size_t alloc_size;
if (swiotlb_force == SWIOTLB_NO_FORCE)
return 0;
/* protect against double initialization */
if (WARN_ON_ONCE(mem->nslabs))
return -ENOMEM;
alloc_size = PAGE_ALIGN(array_size(sizeof(*mem->slots), nslabs));
mem->slots = memblock_alloc(alloc_size, PAGE_SIZE);
if (!mem->slots)
panic("%s: Failed to allocate %zu bytes align=0x%lx\n",
__func__, alloc_size, PAGE_SIZE);
swiotlb_init_io_tlb_mem(mem, __pa(tlb), nslabs, false);
io_tlb_default_mem = mem;
if (verbose) if (verbose)
swiotlb_print_info(); swiotlb_print_info();
swiotlb_set_max_segment(mem->nslabs << IO_TLB_SHIFT); swiotlb_set_max_segment(mem->nslabs << IO_TLB_SHIFT);
...@@ -282,37 +303,24 @@ swiotlb_late_init_with_default_size(size_t default_size) ...@@ -282,37 +303,24 @@ swiotlb_late_init_with_default_size(size_t default_size)
int int
swiotlb_late_init_with_tbl(char *tlb, unsigned long nslabs) swiotlb_late_init_with_tbl(char *tlb, unsigned long nslabs)
{ {
unsigned long bytes = nslabs << IO_TLB_SHIFT, i; struct io_tlb_mem *mem = &io_tlb_default_mem;
struct io_tlb_mem *mem; unsigned long bytes = nslabs << IO_TLB_SHIFT;
if (swiotlb_force == SWIOTLB_NO_FORCE) if (swiotlb_force == SWIOTLB_NO_FORCE)
return 0; return 0;
/* protect against double initialization */ /* protect against double initialization */
if (WARN_ON_ONCE(io_tlb_default_mem)) if (WARN_ON_ONCE(mem->nslabs))
return -ENOMEM; return -ENOMEM;
mem = (void *)__get_free_pages(GFP_KERNEL, mem->slots = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO,
get_order(struct_size(mem, slots, nslabs))); get_order(array_size(sizeof(*mem->slots), nslabs)));
if (!mem) if (!mem->slots)
return -ENOMEM; return -ENOMEM;
mem->nslabs = nslabs;
mem->start = virt_to_phys(tlb);
mem->end = mem->start + bytes;
mem->index = 0;
mem->late_alloc = 1;
spin_lock_init(&mem->lock);
for (i = 0; i < mem->nslabs; i++) {
mem->slots[i].list = IO_TLB_SEGSIZE - io_tlb_offset(i);
mem->slots[i].orig_addr = INVALID_PHYS_ADDR;
mem->slots[i].alloc_size = 0;
}
set_memory_decrypted((unsigned long)tlb, bytes >> PAGE_SHIFT); set_memory_decrypted((unsigned long)tlb, bytes >> PAGE_SHIFT);
memset(tlb, 0, bytes); swiotlb_init_io_tlb_mem(mem, virt_to_phys(tlb), nslabs, true);
io_tlb_default_mem = mem;
swiotlb_print_info(); swiotlb_print_info();
swiotlb_set_max_segment(mem->nslabs << IO_TLB_SHIFT); swiotlb_set_max_segment(mem->nslabs << IO_TLB_SHIFT);
return 0; return 0;
...@@ -320,18 +328,28 @@ swiotlb_late_init_with_tbl(char *tlb, unsigned long nslabs) ...@@ -320,18 +328,28 @@ swiotlb_late_init_with_tbl(char *tlb, unsigned long nslabs)
void __init swiotlb_exit(void) void __init swiotlb_exit(void)
{ {
struct io_tlb_mem *mem = io_tlb_default_mem; struct io_tlb_mem *mem = &io_tlb_default_mem;
size_t size; unsigned long tbl_vaddr;
size_t tbl_size, slots_size;
if (!mem) if (!mem->nslabs)
return; return;
size = struct_size(mem, slots, mem->nslabs); pr_info("tearing down default memory pool\n");
if (mem->late_alloc) tbl_vaddr = (unsigned long)phys_to_virt(mem->start);
free_pages((unsigned long)mem, get_order(size)); tbl_size = PAGE_ALIGN(mem->end - mem->start);
else slots_size = PAGE_ALIGN(array_size(sizeof(*mem->slots), mem->nslabs));
memblock_free_late(__pa(mem), PAGE_ALIGN(size));
io_tlb_default_mem = NULL; set_memory_encrypted(tbl_vaddr, tbl_size >> PAGE_SHIFT);
if (mem->late_alloc) {
free_pages(tbl_vaddr, get_order(tbl_size));
free_pages((unsigned long)mem->slots, get_order(slots_size));
} else {
memblock_free_late(mem->start, tbl_size);
memblock_free_late(__pa(mem->slots), slots_size);
}
memset(mem, 0, sizeof(*mem));
} }
/* /*
...@@ -348,19 +366,33 @@ static unsigned int swiotlb_align_offset(struct device *dev, u64 addr) ...@@ -348,19 +366,33 @@ static unsigned int swiotlb_align_offset(struct device *dev, u64 addr)
static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size, static void swiotlb_bounce(struct device *dev, phys_addr_t tlb_addr, size_t size,
enum dma_data_direction dir) enum dma_data_direction dir)
{ {
struct io_tlb_mem *mem = io_tlb_default_mem; struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
int index = (tlb_addr - mem->start) >> IO_TLB_SHIFT; int index = (tlb_addr - mem->start) >> IO_TLB_SHIFT;
phys_addr_t orig_addr = mem->slots[index].orig_addr; phys_addr_t orig_addr = mem->slots[index].orig_addr;
size_t alloc_size = mem->slots[index].alloc_size; size_t alloc_size = mem->slots[index].alloc_size;
unsigned long pfn = PFN_DOWN(orig_addr); unsigned long pfn = PFN_DOWN(orig_addr);
unsigned char *vaddr = phys_to_virt(tlb_addr); unsigned char *vaddr = phys_to_virt(tlb_addr);
unsigned int tlb_offset; unsigned int tlb_offset, orig_addr_offset;
if (orig_addr == INVALID_PHYS_ADDR) if (orig_addr == INVALID_PHYS_ADDR)
return; return;
tlb_offset = (tlb_addr & (IO_TLB_SIZE - 1)) - tlb_offset = tlb_addr & (IO_TLB_SIZE - 1);
swiotlb_align_offset(dev, orig_addr); orig_addr_offset = swiotlb_align_offset(dev, orig_addr);
if (tlb_offset < orig_addr_offset) {
dev_WARN_ONCE(dev, 1,
"Access before mapping start detected. orig offset %u, requested offset %u.\n",
orig_addr_offset, tlb_offset);
return;
}
tlb_offset -= orig_addr_offset;
if (tlb_offset > alloc_size) {
dev_WARN_ONCE(dev, 1,
"Buffer overflow detected. Allocation size: %zu. Mapping size: %zu+%u.\n",
alloc_size, size, tlb_offset);
return;
}
orig_addr += tlb_offset; orig_addr += tlb_offset;
alloc_size -= tlb_offset; alloc_size -= tlb_offset;
...@@ -426,10 +458,10 @@ static unsigned int wrap_index(struct io_tlb_mem *mem, unsigned int index) ...@@ -426,10 +458,10 @@ static unsigned int wrap_index(struct io_tlb_mem *mem, unsigned int index)
* Find a suitable number of IO TLB entries size that will fit this request and * Find a suitable number of IO TLB entries size that will fit this request and
* allocate a buffer from that IO TLB pool. * allocate a buffer from that IO TLB pool.
*/ */
static int find_slots(struct device *dev, phys_addr_t orig_addr, static int swiotlb_find_slots(struct device *dev, phys_addr_t orig_addr,
size_t alloc_size) size_t alloc_size)
{ {
struct io_tlb_mem *mem = io_tlb_default_mem; struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
unsigned long boundary_mask = dma_get_seg_boundary(dev); unsigned long boundary_mask = dma_get_seg_boundary(dev);
dma_addr_t tbl_dma_addr = dma_addr_t tbl_dma_addr =
phys_to_dma_unencrypted(dev, mem->start) & boundary_mask; phys_to_dma_unencrypted(dev, mem->start) & boundary_mask;
...@@ -438,6 +470,7 @@ static int find_slots(struct device *dev, phys_addr_t orig_addr, ...@@ -438,6 +470,7 @@ static int find_slots(struct device *dev, phys_addr_t orig_addr,
dma_get_min_align_mask(dev) & ~(IO_TLB_SIZE - 1); dma_get_min_align_mask(dev) & ~(IO_TLB_SIZE - 1);
unsigned int nslots = nr_slots(alloc_size), stride; unsigned int nslots = nr_slots(alloc_size), stride;
unsigned int index, wrap, count = 0, i; unsigned int index, wrap, count = 0, i;
unsigned int offset = swiotlb_align_offset(dev, orig_addr);
unsigned long flags; unsigned long flags;
BUG_ON(!nslots); BUG_ON(!nslots);
...@@ -457,8 +490,9 @@ static int find_slots(struct device *dev, phys_addr_t orig_addr, ...@@ -457,8 +490,9 @@ static int find_slots(struct device *dev, phys_addr_t orig_addr,
index = wrap = wrap_index(mem, ALIGN(mem->index, stride)); index = wrap = wrap_index(mem, ALIGN(mem->index, stride));
do { do {
if ((slot_addr(tbl_dma_addr, index) & iotlb_align_mask) != if (orig_addr &&
(orig_addr & iotlb_align_mask)) { (slot_addr(tbl_dma_addr, index) & iotlb_align_mask) !=
(orig_addr & iotlb_align_mask)) {
index = wrap_index(mem, index + 1); index = wrap_index(mem, index + 1);
continue; continue;
} }
...@@ -482,8 +516,11 @@ static int find_slots(struct device *dev, phys_addr_t orig_addr, ...@@ -482,8 +516,11 @@ static int find_slots(struct device *dev, phys_addr_t orig_addr,
return -1; return -1;
found: found:
for (i = index; i < index + nslots; i++) for (i = index; i < index + nslots; i++) {
mem->slots[i].list = 0; mem->slots[i].list = 0;
mem->slots[i].alloc_size =
alloc_size - (offset + ((i - index) << IO_TLB_SHIFT));
}
for (i = index - 1; for (i = index - 1;
io_tlb_offset(i) != IO_TLB_SEGSIZE - 1 && io_tlb_offset(i) != IO_TLB_SEGSIZE - 1 &&
mem->slots[i].list; i--) mem->slots[i].list; i--)
...@@ -506,7 +543,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr, ...@@ -506,7 +543,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
size_t mapping_size, size_t alloc_size, size_t mapping_size, size_t alloc_size,
enum dma_data_direction dir, unsigned long attrs) enum dma_data_direction dir, unsigned long attrs)
{ {
struct io_tlb_mem *mem = io_tlb_default_mem; struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
unsigned int offset = swiotlb_align_offset(dev, orig_addr); unsigned int offset = swiotlb_align_offset(dev, orig_addr);
unsigned int i; unsigned int i;
int index; int index;
...@@ -524,7 +561,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr, ...@@ -524,7 +561,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
return (phys_addr_t)DMA_MAPPING_ERROR; return (phys_addr_t)DMA_MAPPING_ERROR;
} }
index = find_slots(dev, orig_addr, alloc_size + offset); index = swiotlb_find_slots(dev, orig_addr, alloc_size + offset);
if (index == -1) { if (index == -1) {
if (!(attrs & DMA_ATTR_NO_WARN)) if (!(attrs & DMA_ATTR_NO_WARN))
dev_warn_ratelimited(dev, dev_warn_ratelimited(dev,
...@@ -538,11 +575,8 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr, ...@@ -538,11 +575,8 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
* This is needed when we sync the memory. Then we sync the buffer if * This is needed when we sync the memory. Then we sync the buffer if
* needed. * needed.
*/ */
for (i = 0; i < nr_slots(alloc_size + offset); i++) { for (i = 0; i < nr_slots(alloc_size + offset); i++)
mem->slots[index + i].orig_addr = slot_addr(orig_addr, i); mem->slots[index + i].orig_addr = slot_addr(orig_addr, i);
mem->slots[index + i].alloc_size =
alloc_size - (i << IO_TLB_SHIFT);
}
tlb_addr = slot_addr(mem->start, index) + offset; tlb_addr = slot_addr(mem->start, index) + offset;
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) && if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL)) (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL))
...@@ -550,27 +584,15 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr, ...@@ -550,27 +584,15 @@ phys_addr_t swiotlb_tbl_map_single(struct device *dev, phys_addr_t orig_addr,
return tlb_addr; return tlb_addr;
} }
/* static void swiotlb_release_slots(struct device *dev, phys_addr_t tlb_addr)
* tlb_addr is the physical address of the bounce buffer to unmap.
*/
void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
size_t mapping_size, enum dma_data_direction dir,
unsigned long attrs)
{ {
struct io_tlb_mem *mem = io_tlb_default_mem; struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
unsigned long flags; unsigned long flags;
unsigned int offset = swiotlb_align_offset(hwdev, tlb_addr); unsigned int offset = swiotlb_align_offset(dev, tlb_addr);
int index = (tlb_addr - offset - mem->start) >> IO_TLB_SHIFT; int index = (tlb_addr - offset - mem->start) >> IO_TLB_SHIFT;
int nslots = nr_slots(mem->slots[index].alloc_size + offset); int nslots = nr_slots(mem->slots[index].alloc_size + offset);
int count, i; int count, i;
/*
* First, sync the memory before unmapping the entry
*/
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
(dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL))
swiotlb_bounce(hwdev, tlb_addr, mapping_size, DMA_FROM_DEVICE);
/* /*
* Return the buffer to the free list by setting the corresponding * Return the buffer to the free list by setting the corresponding
* entries to indicate the number of contiguous entries available. * entries to indicate the number of contiguous entries available.
...@@ -605,6 +627,23 @@ void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr, ...@@ -605,6 +627,23 @@ void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
spin_unlock_irqrestore(&mem->lock, flags); spin_unlock_irqrestore(&mem->lock, flags);
} }
/*
* tlb_addr is the physical address of the bounce buffer to unmap.
*/
void swiotlb_tbl_unmap_single(struct device *dev, phys_addr_t tlb_addr,
size_t mapping_size, enum dma_data_direction dir,
unsigned long attrs)
{
/*
* First, sync the memory before unmapping the entry
*/
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
(dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL))
swiotlb_bounce(dev, tlb_addr, mapping_size, DMA_FROM_DEVICE);
swiotlb_release_slots(dev, tlb_addr);
}
void swiotlb_sync_single_for_device(struct device *dev, phys_addr_t tlb_addr, void swiotlb_sync_single_for_device(struct device *dev, phys_addr_t tlb_addr,
size_t size, enum dma_data_direction dir) size_t size, enum dma_data_direction dir)
{ {
...@@ -662,26 +701,155 @@ size_t swiotlb_max_mapping_size(struct device *dev) ...@@ -662,26 +701,155 @@ size_t swiotlb_max_mapping_size(struct device *dev)
return ((size_t)IO_TLB_SIZE) * IO_TLB_SEGSIZE; return ((size_t)IO_TLB_SIZE) * IO_TLB_SEGSIZE;
} }
bool is_swiotlb_active(void) bool is_swiotlb_active(struct device *dev)
{ {
return io_tlb_default_mem != NULL; struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
return mem && mem->nslabs;
} }
EXPORT_SYMBOL_GPL(is_swiotlb_active); EXPORT_SYMBOL_GPL(is_swiotlb_active);
#ifdef CONFIG_DEBUG_FS #ifdef CONFIG_DEBUG_FS
static struct dentry *debugfs_dir;
static int __init swiotlb_create_debugfs(void) static void swiotlb_create_debugfs_files(struct io_tlb_mem *mem)
{ {
struct io_tlb_mem *mem = io_tlb_default_mem;
if (!mem)
return 0;
mem->debugfs = debugfs_create_dir("swiotlb", NULL);
debugfs_create_ulong("io_tlb_nslabs", 0400, mem->debugfs, &mem->nslabs); debugfs_create_ulong("io_tlb_nslabs", 0400, mem->debugfs, &mem->nslabs);
debugfs_create_ulong("io_tlb_used", 0400, mem->debugfs, &mem->used); debugfs_create_ulong("io_tlb_used", 0400, mem->debugfs, &mem->used);
}
static int __init swiotlb_create_default_debugfs(void)
{
struct io_tlb_mem *mem = &io_tlb_default_mem;
debugfs_dir = debugfs_create_dir("swiotlb", NULL);
if (mem->nslabs) {
mem->debugfs = debugfs_dir;
swiotlb_create_debugfs_files(mem);
}
return 0; return 0;
} }
late_initcall(swiotlb_create_debugfs); late_initcall(swiotlb_create_default_debugfs);
#endif
#ifdef CONFIG_DMA_RESTRICTED_POOL
#ifdef CONFIG_DEBUG_FS
static void rmem_swiotlb_debugfs_init(struct reserved_mem *rmem)
{
struct io_tlb_mem *mem = rmem->priv;
mem->debugfs = debugfs_create_dir(rmem->name, debugfs_dir);
swiotlb_create_debugfs_files(mem);
}
#else
static void rmem_swiotlb_debugfs_init(struct reserved_mem *rmem)
{
}
#endif #endif
struct page *swiotlb_alloc(struct device *dev, size_t size)
{
struct io_tlb_mem *mem = dev->dma_io_tlb_mem;
phys_addr_t tlb_addr;
int index;
if (!mem)
return NULL;
index = swiotlb_find_slots(dev, 0, size);
if (index == -1)
return NULL;
tlb_addr = slot_addr(mem->start, index);
return pfn_to_page(PFN_DOWN(tlb_addr));
}
bool swiotlb_free(struct device *dev, struct page *page, size_t size)
{
phys_addr_t tlb_addr = page_to_phys(page);
if (!is_swiotlb_buffer(dev, tlb_addr))
return false;
swiotlb_release_slots(dev, tlb_addr);
return true;
}
static int rmem_swiotlb_device_init(struct reserved_mem *rmem,
struct device *dev)
{
struct io_tlb_mem *mem = rmem->priv;
unsigned long nslabs = rmem->size >> IO_TLB_SHIFT;
/*
* Since multiple devices can share the same pool, the private data,
* io_tlb_mem struct, will be initialized by the first device attached
* to it.
*/
if (!mem) {
mem = kzalloc(sizeof(*mem), GFP_KERNEL);
if (!mem)
return -ENOMEM;
mem->slots = kzalloc(array_size(sizeof(*mem->slots), nslabs),
GFP_KERNEL);
if (!mem->slots) {
kfree(mem);
return -ENOMEM;
}
set_memory_decrypted((unsigned long)phys_to_virt(rmem->base),
rmem->size >> PAGE_SHIFT);
swiotlb_init_io_tlb_mem(mem, rmem->base, nslabs, false);
mem->force_bounce = true;
mem->for_alloc = true;
rmem->priv = mem;
rmem_swiotlb_debugfs_init(rmem);
}
dev->dma_io_tlb_mem = mem;
return 0;
}
static void rmem_swiotlb_device_release(struct reserved_mem *rmem,
struct device *dev)
{
dev->dma_io_tlb_mem = &io_tlb_default_mem;
}
static const struct reserved_mem_ops rmem_swiotlb_ops = {
.device_init = rmem_swiotlb_device_init,
.device_release = rmem_swiotlb_device_release,
};
static int __init rmem_swiotlb_setup(struct reserved_mem *rmem)
{
unsigned long node = rmem->fdt_node;
if (of_get_flat_dt_prop(node, "reusable", NULL) ||
of_get_flat_dt_prop(node, "linux,cma-default", NULL) ||
of_get_flat_dt_prop(node, "linux,dma-default", NULL) ||
of_get_flat_dt_prop(node, "no-map", NULL))
return -EINVAL;
if (PageHighMem(pfn_to_page(PHYS_PFN(rmem->base)))) {
pr_err("Restricted DMA pool must be accessible within the linear mapping.");
return -EINVAL;
}
rmem->ops = &rmem_swiotlb_ops;
pr_info("Reserved memory: created restricted DMA pool at %pa, size %ld MiB\n",
&rmem->base, (unsigned long)rmem->size / SZ_1M);
return 0;
}
RESERVEDMEM_OF_DECLARE(dma, "restricted-dma-pool", rmem_swiotlb_setup);
#endif /* CONFIG_DMA_RESTRICTED_POOL */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment