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