18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/cpu.h>
38c2ecf20Sopenharmony_ci#include <linux/dma-direct.h>
48c2ecf20Sopenharmony_ci#include <linux/dma-map-ops.h>
58c2ecf20Sopenharmony_ci#include <linux/gfp.h>
68c2ecf20Sopenharmony_ci#include <linux/highmem.h>
78c2ecf20Sopenharmony_ci#include <linux/export.h>
88c2ecf20Sopenharmony_ci#include <linux/memblock.h>
98c2ecf20Sopenharmony_ci#include <linux/of_address.h>
108c2ecf20Sopenharmony_ci#include <linux/slab.h>
118c2ecf20Sopenharmony_ci#include <linux/types.h>
128c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
138c2ecf20Sopenharmony_ci#include <linux/swiotlb.h>
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#include <xen/xen.h>
168c2ecf20Sopenharmony_ci#include <xen/interface/grant_table.h>
178c2ecf20Sopenharmony_ci#include <xen/interface/memory.h>
188c2ecf20Sopenharmony_ci#include <xen/page.h>
198c2ecf20Sopenharmony_ci#include <xen/xen-ops.h>
208c2ecf20Sopenharmony_ci#include <xen/swiotlb-xen.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
238c2ecf20Sopenharmony_ci#include <asm/xen/hypercall.h>
248c2ecf20Sopenharmony_ci#include <asm/xen/interface.h>
258c2ecf20Sopenharmony_ci
268c2ecf20Sopenharmony_ciunsigned long xen_get_swiotlb_free_pages(unsigned int order)
278c2ecf20Sopenharmony_ci{
288c2ecf20Sopenharmony_ci	phys_addr_t base;
298c2ecf20Sopenharmony_ci	gfp_t flags = __GFP_NOWARN|__GFP_KSWAPD_RECLAIM;
308c2ecf20Sopenharmony_ci	u64 i;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci	for_each_mem_range(i, &base, NULL) {
338c2ecf20Sopenharmony_ci		if (base < (phys_addr_t)0xffffffff) {
348c2ecf20Sopenharmony_ci			if (IS_ENABLED(CONFIG_ZONE_DMA32))
358c2ecf20Sopenharmony_ci				flags |= __GFP_DMA32;
368c2ecf20Sopenharmony_ci			else
378c2ecf20Sopenharmony_ci				flags |= __GFP_DMA;
388c2ecf20Sopenharmony_ci			break;
398c2ecf20Sopenharmony_ci		}
408c2ecf20Sopenharmony_ci	}
418c2ecf20Sopenharmony_ci	return __get_free_pages(flags, order);
428c2ecf20Sopenharmony_ci}
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_cistatic bool hypercall_cflush = false;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci/* buffers in highmem or foreign pages cannot cross page boundaries */
478c2ecf20Sopenharmony_cistatic void dma_cache_maint(struct device *dev, dma_addr_t handle,
488c2ecf20Sopenharmony_ci			    size_t size, u32 op)
498c2ecf20Sopenharmony_ci{
508c2ecf20Sopenharmony_ci	struct gnttab_cache_flush cflush;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	cflush.offset = xen_offset_in_page(handle);
538c2ecf20Sopenharmony_ci	cflush.op = op;
548c2ecf20Sopenharmony_ci	handle &= XEN_PAGE_MASK;
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	do {
578c2ecf20Sopenharmony_ci		cflush.a.dev_bus_addr = dma_to_phys(dev, handle);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci		if (size + cflush.offset > XEN_PAGE_SIZE)
608c2ecf20Sopenharmony_ci			cflush.length = XEN_PAGE_SIZE - cflush.offset;
618c2ecf20Sopenharmony_ci		else
628c2ecf20Sopenharmony_ci			cflush.length = size;
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci		HYPERVISOR_grant_table_op(GNTTABOP_cache_flush, &cflush, 1);
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci		cflush.offset = 0;
678c2ecf20Sopenharmony_ci		handle += cflush.length;
688c2ecf20Sopenharmony_ci		size -= cflush.length;
698c2ecf20Sopenharmony_ci	} while (size);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/*
738c2ecf20Sopenharmony_ci * Dom0 is mapped 1:1, and while the Linux page can span across multiple Xen
748c2ecf20Sopenharmony_ci * pages, it is not possible for it to contain a mix of local and foreign Xen
758c2ecf20Sopenharmony_ci * pages.  Calling pfn_valid on a foreign mfn will always return false, so if
768c2ecf20Sopenharmony_ci * pfn_valid returns true the pages is local and we can use the native
778c2ecf20Sopenharmony_ci * dma-direct functions, otherwise we call the Xen specific version.
788c2ecf20Sopenharmony_ci */
798c2ecf20Sopenharmony_civoid xen_dma_sync_for_cpu(struct device *dev, dma_addr_t handle,
808c2ecf20Sopenharmony_ci			  size_t size, enum dma_data_direction dir)
818c2ecf20Sopenharmony_ci{
828c2ecf20Sopenharmony_ci	if (dir != DMA_TO_DEVICE)
838c2ecf20Sopenharmony_ci		dma_cache_maint(dev, handle, size, GNTTAB_CACHE_INVAL);
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_civoid xen_dma_sync_for_device(struct device *dev, dma_addr_t handle,
878c2ecf20Sopenharmony_ci			     size_t size, enum dma_data_direction dir)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	if (dir == DMA_FROM_DEVICE)
908c2ecf20Sopenharmony_ci		dma_cache_maint(dev, handle, size, GNTTAB_CACHE_INVAL);
918c2ecf20Sopenharmony_ci	else
928c2ecf20Sopenharmony_ci		dma_cache_maint(dev, handle, size, GNTTAB_CACHE_CLEAN);
938c2ecf20Sopenharmony_ci}
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cibool xen_arch_need_swiotlb(struct device *dev,
968c2ecf20Sopenharmony_ci			   phys_addr_t phys,
978c2ecf20Sopenharmony_ci			   dma_addr_t dev_addr)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	unsigned int xen_pfn = XEN_PFN_DOWN(phys);
1008c2ecf20Sopenharmony_ci	unsigned int bfn = XEN_PFN_DOWN(dma_to_phys(dev, dev_addr));
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	/*
1038c2ecf20Sopenharmony_ci	 * The swiotlb buffer should be used if
1048c2ecf20Sopenharmony_ci	 *	- Xen doesn't have the cache flush hypercall
1058c2ecf20Sopenharmony_ci	 *	- The Linux page refers to foreign memory
1068c2ecf20Sopenharmony_ci	 *	- The device doesn't support coherent DMA request
1078c2ecf20Sopenharmony_ci	 *
1088c2ecf20Sopenharmony_ci	 * The Linux page may be spanned acrros multiple Xen page, although
1098c2ecf20Sopenharmony_ci	 * it's not possible to have a mix of local and foreign Xen page.
1108c2ecf20Sopenharmony_ci	 * Furthermore, range_straddles_page_boundary is already checking
1118c2ecf20Sopenharmony_ci	 * if buffer is physically contiguous in the host RAM.
1128c2ecf20Sopenharmony_ci	 *
1138c2ecf20Sopenharmony_ci	 * Therefore we only need to check the first Xen page to know if we
1148c2ecf20Sopenharmony_ci	 * require a bounce buffer because the device doesn't support coherent
1158c2ecf20Sopenharmony_ci	 * memory and we are not able to flush the cache.
1168c2ecf20Sopenharmony_ci	 */
1178c2ecf20Sopenharmony_ci	return (!hypercall_cflush && (xen_pfn != bfn) &&
1188c2ecf20Sopenharmony_ci		!dev_is_dma_coherent(dev));
1198c2ecf20Sopenharmony_ci}
1208c2ecf20Sopenharmony_ci
1218c2ecf20Sopenharmony_ciint xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
1228c2ecf20Sopenharmony_ci				 unsigned int address_bits,
1238c2ecf20Sopenharmony_ci				 dma_addr_t *dma_handle)
1248c2ecf20Sopenharmony_ci{
1258c2ecf20Sopenharmony_ci	if (!xen_initial_domain())
1268c2ecf20Sopenharmony_ci		return -EINVAL;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci	/* we assume that dom0 is mapped 1:1 for now */
1298c2ecf20Sopenharmony_ci	*dma_handle = pstart;
1308c2ecf20Sopenharmony_ci	return 0;
1318c2ecf20Sopenharmony_ci}
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_civoid xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order)
1348c2ecf20Sopenharmony_ci{
1358c2ecf20Sopenharmony_ci	return;
1368c2ecf20Sopenharmony_ci}
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_cistatic int __init xen_mm_init(void)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	struct gnttab_cache_flush cflush;
1418c2ecf20Sopenharmony_ci	if (!xen_initial_domain())
1428c2ecf20Sopenharmony_ci		return 0;
1438c2ecf20Sopenharmony_ci	xen_swiotlb_init(1, false);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	cflush.op = 0;
1468c2ecf20Sopenharmony_ci	cflush.a.dev_bus_addr = 0;
1478c2ecf20Sopenharmony_ci	cflush.offset = 0;
1488c2ecf20Sopenharmony_ci	cflush.length = 0;
1498c2ecf20Sopenharmony_ci	if (HYPERVISOR_grant_table_op(GNTTABOP_cache_flush, &cflush, 1) != -ENOSYS)
1508c2ecf20Sopenharmony_ci		hypercall_cflush = true;
1518c2ecf20Sopenharmony_ci	return 0;
1528c2ecf20Sopenharmony_ci}
1538c2ecf20Sopenharmony_ciarch_initcall(xen_mm_init);
154