18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci#include <linux/memblock.h>
38c2ecf20Sopenharmony_ci#include <linux/gfp.h>
48c2ecf20Sopenharmony_ci#include <linux/export.h>
58c2ecf20Sopenharmony_ci#include <linux/spinlock.h>
68c2ecf20Sopenharmony_ci#include <linux/slab.h>
78c2ecf20Sopenharmony_ci#include <linux/types.h>
88c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h>
98c2ecf20Sopenharmony_ci#include <linux/vmalloc.h>
108c2ecf20Sopenharmony_ci#include <linux/swiotlb.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <xen/xen.h>
138c2ecf20Sopenharmony_ci#include <xen/interface/memory.h>
148c2ecf20Sopenharmony_ci#include <xen/page.h>
158c2ecf20Sopenharmony_ci#include <xen/swiotlb-xen.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <asm/cacheflush.h>
188c2ecf20Sopenharmony_ci#include <asm/xen/hypercall.h>
198c2ecf20Sopenharmony_ci#include <asm/xen/interface.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistruct xen_p2m_entry {
228c2ecf20Sopenharmony_ci	unsigned long pfn;
238c2ecf20Sopenharmony_ci	unsigned long mfn;
248c2ecf20Sopenharmony_ci	unsigned long nr_pages;
258c2ecf20Sopenharmony_ci	struct rb_node rbnode_phys;
268c2ecf20Sopenharmony_ci};
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic rwlock_t p2m_lock;
298c2ecf20Sopenharmony_cistruct rb_root phys_to_mach = RB_ROOT;
308c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(phys_to_mach);
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic int xen_add_phys_to_mach_entry(struct xen_p2m_entry *new)
338c2ecf20Sopenharmony_ci{
348c2ecf20Sopenharmony_ci	struct rb_node **link = &phys_to_mach.rb_node;
358c2ecf20Sopenharmony_ci	struct rb_node *parent = NULL;
368c2ecf20Sopenharmony_ci	struct xen_p2m_entry *entry;
378c2ecf20Sopenharmony_ci	int rc = 0;
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_ci	while (*link) {
408c2ecf20Sopenharmony_ci		parent = *link;
418c2ecf20Sopenharmony_ci		entry = rb_entry(parent, struct xen_p2m_entry, rbnode_phys);
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci		if (new->pfn == entry->pfn)
448c2ecf20Sopenharmony_ci			goto err_out;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci		if (new->pfn < entry->pfn)
478c2ecf20Sopenharmony_ci			link = &(*link)->rb_left;
488c2ecf20Sopenharmony_ci		else
498c2ecf20Sopenharmony_ci			link = &(*link)->rb_right;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci	rb_link_node(&new->rbnode_phys, parent, link);
528c2ecf20Sopenharmony_ci	rb_insert_color(&new->rbnode_phys, &phys_to_mach);
538c2ecf20Sopenharmony_ci	goto out;
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cierr_out:
568c2ecf20Sopenharmony_ci	rc = -EINVAL;
578c2ecf20Sopenharmony_ci	pr_warn("%s: cannot add pfn=%pa -> mfn=%pa: pfn=%pa -> mfn=%pa already exists\n",
588c2ecf20Sopenharmony_ci			__func__, &new->pfn, &new->mfn, &entry->pfn, &entry->mfn);
598c2ecf20Sopenharmony_ciout:
608c2ecf20Sopenharmony_ci	return rc;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciunsigned long __pfn_to_mfn(unsigned long pfn)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	struct rb_node *n;
668c2ecf20Sopenharmony_ci	struct xen_p2m_entry *entry;
678c2ecf20Sopenharmony_ci	unsigned long irqflags;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	read_lock_irqsave(&p2m_lock, irqflags);
708c2ecf20Sopenharmony_ci	n = phys_to_mach.rb_node;
718c2ecf20Sopenharmony_ci	while (n) {
728c2ecf20Sopenharmony_ci		entry = rb_entry(n, struct xen_p2m_entry, rbnode_phys);
738c2ecf20Sopenharmony_ci		if (entry->pfn <= pfn &&
748c2ecf20Sopenharmony_ci				entry->pfn + entry->nr_pages > pfn) {
758c2ecf20Sopenharmony_ci			unsigned long mfn = entry->mfn + (pfn - entry->pfn);
768c2ecf20Sopenharmony_ci			read_unlock_irqrestore(&p2m_lock, irqflags);
778c2ecf20Sopenharmony_ci			return mfn;
788c2ecf20Sopenharmony_ci		}
798c2ecf20Sopenharmony_ci		if (pfn < entry->pfn)
808c2ecf20Sopenharmony_ci			n = n->rb_left;
818c2ecf20Sopenharmony_ci		else
828c2ecf20Sopenharmony_ci			n = n->rb_right;
838c2ecf20Sopenharmony_ci	}
848c2ecf20Sopenharmony_ci	read_unlock_irqrestore(&p2m_lock, irqflags);
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	return INVALID_P2M_ENTRY;
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__pfn_to_mfn);
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ciint set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
918c2ecf20Sopenharmony_ci			    struct gnttab_map_grant_ref *kmap_ops,
928c2ecf20Sopenharmony_ci			    struct page **pages, unsigned int count)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	int i;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
978c2ecf20Sopenharmony_ci		struct gnttab_unmap_grant_ref unmap;
988c2ecf20Sopenharmony_ci		int rc;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci		if (map_ops[i].status)
1018c2ecf20Sopenharmony_ci			continue;
1028c2ecf20Sopenharmony_ci		if (likely(set_phys_to_machine(map_ops[i].host_addr >> XEN_PAGE_SHIFT,
1038c2ecf20Sopenharmony_ci				    map_ops[i].dev_bus_addr >> XEN_PAGE_SHIFT)))
1048c2ecf20Sopenharmony_ci			continue;
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci		/*
1078c2ecf20Sopenharmony_ci		 * Signal an error for this slot. This in turn requires
1088c2ecf20Sopenharmony_ci		 * immediate unmapping.
1098c2ecf20Sopenharmony_ci		 */
1108c2ecf20Sopenharmony_ci		map_ops[i].status = GNTST_general_error;
1118c2ecf20Sopenharmony_ci		unmap.host_addr = map_ops[i].host_addr,
1128c2ecf20Sopenharmony_ci		unmap.handle = map_ops[i].handle;
1138c2ecf20Sopenharmony_ci		map_ops[i].handle = ~0;
1148c2ecf20Sopenharmony_ci		if (map_ops[i].flags & GNTMAP_device_map)
1158c2ecf20Sopenharmony_ci			unmap.dev_bus_addr = map_ops[i].dev_bus_addr;
1168c2ecf20Sopenharmony_ci		else
1178c2ecf20Sopenharmony_ci			unmap.dev_bus_addr = 0;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		/*
1208c2ecf20Sopenharmony_ci		 * Pre-populate the status field, to be recognizable in
1218c2ecf20Sopenharmony_ci		 * the log message below.
1228c2ecf20Sopenharmony_ci		 */
1238c2ecf20Sopenharmony_ci		unmap.status = 1;
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci		rc = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref,
1268c2ecf20Sopenharmony_ci					       &unmap, 1);
1278c2ecf20Sopenharmony_ci		if (rc || unmap.status != GNTST_okay)
1288c2ecf20Sopenharmony_ci			pr_err_once("gnttab unmap failed: rc=%d st=%d\n",
1298c2ecf20Sopenharmony_ci				    rc, unmap.status);
1308c2ecf20Sopenharmony_ci	}
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci	return 0;
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(set_foreign_p2m_mapping);
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ciint clear_foreign_p2m_mapping(struct gnttab_unmap_grant_ref *unmap_ops,
1378c2ecf20Sopenharmony_ci			      struct gnttab_unmap_grant_ref *kunmap_ops,
1388c2ecf20Sopenharmony_ci			      struct page **pages, unsigned int count)
1398c2ecf20Sopenharmony_ci{
1408c2ecf20Sopenharmony_ci	int i;
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	for (i = 0; i < count; i++) {
1438c2ecf20Sopenharmony_ci		set_phys_to_machine(unmap_ops[i].host_addr >> XEN_PAGE_SHIFT,
1448c2ecf20Sopenharmony_ci				    INVALID_P2M_ENTRY);
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	return 0;
1488c2ecf20Sopenharmony_ci}
1498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(clear_foreign_p2m_mapping);
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_cibool __set_phys_to_machine_multi(unsigned long pfn,
1528c2ecf20Sopenharmony_ci		unsigned long mfn, unsigned long nr_pages)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	int rc;
1558c2ecf20Sopenharmony_ci	unsigned long irqflags;
1568c2ecf20Sopenharmony_ci	struct xen_p2m_entry *p2m_entry;
1578c2ecf20Sopenharmony_ci	struct rb_node *n;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	if (mfn == INVALID_P2M_ENTRY) {
1608c2ecf20Sopenharmony_ci		write_lock_irqsave(&p2m_lock, irqflags);
1618c2ecf20Sopenharmony_ci		n = phys_to_mach.rb_node;
1628c2ecf20Sopenharmony_ci		while (n) {
1638c2ecf20Sopenharmony_ci			p2m_entry = rb_entry(n, struct xen_p2m_entry, rbnode_phys);
1648c2ecf20Sopenharmony_ci			if (p2m_entry->pfn <= pfn &&
1658c2ecf20Sopenharmony_ci					p2m_entry->pfn + p2m_entry->nr_pages > pfn) {
1668c2ecf20Sopenharmony_ci				rb_erase(&p2m_entry->rbnode_phys, &phys_to_mach);
1678c2ecf20Sopenharmony_ci				write_unlock_irqrestore(&p2m_lock, irqflags);
1688c2ecf20Sopenharmony_ci				kfree(p2m_entry);
1698c2ecf20Sopenharmony_ci				return true;
1708c2ecf20Sopenharmony_ci			}
1718c2ecf20Sopenharmony_ci			if (pfn < p2m_entry->pfn)
1728c2ecf20Sopenharmony_ci				n = n->rb_left;
1738c2ecf20Sopenharmony_ci			else
1748c2ecf20Sopenharmony_ci				n = n->rb_right;
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci		write_unlock_irqrestore(&p2m_lock, irqflags);
1778c2ecf20Sopenharmony_ci		return true;
1788c2ecf20Sopenharmony_ci	}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_ci	p2m_entry = kzalloc(sizeof(*p2m_entry), GFP_NOWAIT);
1818c2ecf20Sopenharmony_ci	if (!p2m_entry)
1828c2ecf20Sopenharmony_ci		return false;
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	p2m_entry->pfn = pfn;
1858c2ecf20Sopenharmony_ci	p2m_entry->nr_pages = nr_pages;
1868c2ecf20Sopenharmony_ci	p2m_entry->mfn = mfn;
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_ci	write_lock_irqsave(&p2m_lock, irqflags);
1898c2ecf20Sopenharmony_ci	rc = xen_add_phys_to_mach_entry(p2m_entry);
1908c2ecf20Sopenharmony_ci	if (rc < 0) {
1918c2ecf20Sopenharmony_ci		write_unlock_irqrestore(&p2m_lock, irqflags);
1928c2ecf20Sopenharmony_ci		kfree(p2m_entry);
1938c2ecf20Sopenharmony_ci		return false;
1948c2ecf20Sopenharmony_ci	}
1958c2ecf20Sopenharmony_ci	write_unlock_irqrestore(&p2m_lock, irqflags);
1968c2ecf20Sopenharmony_ci	return true;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__set_phys_to_machine_multi);
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cibool __set_phys_to_machine(unsigned long pfn, unsigned long mfn)
2018c2ecf20Sopenharmony_ci{
2028c2ecf20Sopenharmony_ci	return __set_phys_to_machine_multi(pfn, mfn, 1);
2038c2ecf20Sopenharmony_ci}
2048c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(__set_phys_to_machine);
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_cistatic int p2m_init(void)
2078c2ecf20Sopenharmony_ci{
2088c2ecf20Sopenharmony_ci	rwlock_init(&p2m_lock);
2098c2ecf20Sopenharmony_ci	return 0;
2108c2ecf20Sopenharmony_ci}
2118c2ecf20Sopenharmony_ciarch_initcall(p2m_init);
212