162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR MIT
262306a36Sopenharmony_ci/******************************************************************************
362306a36Sopenharmony_ci * grant_table.c
462306a36Sopenharmony_ci * x86 specific part
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Granting foreign access to our memory reservation.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Copyright (c) 2005-2006, Christopher Clark
962306a36Sopenharmony_ci * Copyright (c) 2004-2005, K A Fraser
1062306a36Sopenharmony_ci * Copyright (c) 2008 Isaku Yamahata <yamahata at valinux co jp>
1162306a36Sopenharmony_ci *                    VA Linux Systems Japan. Split out x86 specific part.
1262306a36Sopenharmony_ci */
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/sched.h>
1562306a36Sopenharmony_ci#include <linux/mm.h>
1662306a36Sopenharmony_ci#include <linux/slab.h>
1762306a36Sopenharmony_ci#include <linux/vmalloc.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#include <xen/interface/xen.h>
2062306a36Sopenharmony_ci#include <xen/page.h>
2162306a36Sopenharmony_ci#include <xen/grant_table.h>
2262306a36Sopenharmony_ci#include <xen/xen.h>
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_cistatic struct gnttab_vm_area {
2662306a36Sopenharmony_ci	struct vm_struct *area;
2762306a36Sopenharmony_ci	pte_t **ptes;
2862306a36Sopenharmony_ci	int idx;
2962306a36Sopenharmony_ci} gnttab_shared_vm_area, gnttab_status_vm_area;
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ciint arch_gnttab_map_shared(unsigned long *frames, unsigned long nr_gframes,
3262306a36Sopenharmony_ci			   unsigned long max_nr_gframes,
3362306a36Sopenharmony_ci			   void **__shared)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	void *shared = *__shared;
3662306a36Sopenharmony_ci	unsigned long addr;
3762306a36Sopenharmony_ci	unsigned long i;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (shared == NULL)
4062306a36Sopenharmony_ci		*__shared = shared = gnttab_shared_vm_area.area->addr;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	addr = (unsigned long)shared;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	for (i = 0; i < nr_gframes; i++) {
4562306a36Sopenharmony_ci		set_pte_at(&init_mm, addr, gnttab_shared_vm_area.ptes[i],
4662306a36Sopenharmony_ci			   mfn_pte(frames[i], PAGE_KERNEL));
4762306a36Sopenharmony_ci		addr += PAGE_SIZE;
4862306a36Sopenharmony_ci	}
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return 0;
5162306a36Sopenharmony_ci}
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ciint arch_gnttab_map_status(uint64_t *frames, unsigned long nr_gframes,
5462306a36Sopenharmony_ci			   unsigned long max_nr_gframes,
5562306a36Sopenharmony_ci			   grant_status_t **__shared)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	grant_status_t *shared = *__shared;
5862306a36Sopenharmony_ci	unsigned long addr;
5962306a36Sopenharmony_ci	unsigned long i;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	if (shared == NULL)
6262306a36Sopenharmony_ci		*__shared = shared = gnttab_status_vm_area.area->addr;
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	addr = (unsigned long)shared;
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	for (i = 0; i < nr_gframes; i++) {
6762306a36Sopenharmony_ci		set_pte_at(&init_mm, addr, gnttab_status_vm_area.ptes[i],
6862306a36Sopenharmony_ci			   mfn_pte(frames[i], PAGE_KERNEL));
6962306a36Sopenharmony_ci		addr += PAGE_SIZE;
7062306a36Sopenharmony_ci	}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	return 0;
7362306a36Sopenharmony_ci}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_civoid arch_gnttab_unmap(void *shared, unsigned long nr_gframes)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	pte_t **ptes;
7862306a36Sopenharmony_ci	unsigned long addr;
7962306a36Sopenharmony_ci	unsigned long i;
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_ci	if (shared == gnttab_status_vm_area.area->addr)
8262306a36Sopenharmony_ci		ptes = gnttab_status_vm_area.ptes;
8362306a36Sopenharmony_ci	else
8462306a36Sopenharmony_ci		ptes = gnttab_shared_vm_area.ptes;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	addr = (unsigned long)shared;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	for (i = 0; i < nr_gframes; i++) {
8962306a36Sopenharmony_ci		set_pte_at(&init_mm, addr, ptes[i], __pte(0));
9062306a36Sopenharmony_ci		addr += PAGE_SIZE;
9162306a36Sopenharmony_ci	}
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic int gnttab_apply(pte_t *pte, unsigned long addr, void *data)
9562306a36Sopenharmony_ci{
9662306a36Sopenharmony_ci	struct gnttab_vm_area *area = data;
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_ci	area->ptes[area->idx++] = pte;
9962306a36Sopenharmony_ci	return 0;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int arch_gnttab_valloc(struct gnttab_vm_area *area, unsigned nr_frames)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	area->ptes = kmalloc_array(nr_frames, sizeof(*area->ptes), GFP_KERNEL);
10562306a36Sopenharmony_ci	if (area->ptes == NULL)
10662306a36Sopenharmony_ci		return -ENOMEM;
10762306a36Sopenharmony_ci	area->area = get_vm_area(PAGE_SIZE * nr_frames, VM_IOREMAP);
10862306a36Sopenharmony_ci	if (!area->area)
10962306a36Sopenharmony_ci		goto out_free_ptes;
11062306a36Sopenharmony_ci	if (apply_to_page_range(&init_mm, (unsigned long)area->area->addr,
11162306a36Sopenharmony_ci			PAGE_SIZE * nr_frames, gnttab_apply, area))
11262306a36Sopenharmony_ci		goto out_free_vm_area;
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ciout_free_vm_area:
11562306a36Sopenharmony_ci	free_vm_area(area->area);
11662306a36Sopenharmony_ciout_free_ptes:
11762306a36Sopenharmony_ci	kfree(area->ptes);
11862306a36Sopenharmony_ci	return -ENOMEM;
11962306a36Sopenharmony_ci}
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_cistatic void arch_gnttab_vfree(struct gnttab_vm_area *area)
12262306a36Sopenharmony_ci{
12362306a36Sopenharmony_ci	free_vm_area(area->area);
12462306a36Sopenharmony_ci	kfree(area->ptes);
12562306a36Sopenharmony_ci}
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ciint arch_gnttab_init(unsigned long nr_shared, unsigned long nr_status)
12862306a36Sopenharmony_ci{
12962306a36Sopenharmony_ci	int ret;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	if (!xen_pv_domain())
13262306a36Sopenharmony_ci		return 0;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci	ret = arch_gnttab_valloc(&gnttab_shared_vm_area, nr_shared);
13562306a36Sopenharmony_ci	if (ret < 0)
13662306a36Sopenharmony_ci		return ret;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/*
13962306a36Sopenharmony_ci	 * Always allocate the space for the status frames in case
14062306a36Sopenharmony_ci	 * we're migrated to a host with V2 support.
14162306a36Sopenharmony_ci	 */
14262306a36Sopenharmony_ci	ret = arch_gnttab_valloc(&gnttab_status_vm_area, nr_status);
14362306a36Sopenharmony_ci	if (ret < 0)
14462306a36Sopenharmony_ci		goto err;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_cierr:
14862306a36Sopenharmony_ci	arch_gnttab_vfree(&gnttab_shared_vm_area);
14962306a36Sopenharmony_ci	return -ENOMEM;
15062306a36Sopenharmony_ci}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci#ifdef CONFIG_XEN_PVH
15362306a36Sopenharmony_ci#include <xen/events.h>
15462306a36Sopenharmony_ci#include <xen/xen-ops.h>
15562306a36Sopenharmony_cistatic int __init xen_pvh_gnttab_setup(void)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	if (!xen_pvh_domain())
15862306a36Sopenharmony_ci		return -ENODEV;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	xen_auto_xlat_grant_frames.count = gnttab_max_grant_frames();
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	return xen_xlate_map_ballooned_pages(&xen_auto_xlat_grant_frames.pfn,
16362306a36Sopenharmony_ci					     &xen_auto_xlat_grant_frames.vaddr,
16462306a36Sopenharmony_ci					     xen_auto_xlat_grant_frames.count);
16562306a36Sopenharmony_ci}
16662306a36Sopenharmony_ci/* Call it _before_ __gnttab_init as we need to initialize the
16762306a36Sopenharmony_ci * xen_auto_xlat_grant_frames first. */
16862306a36Sopenharmony_cicore_initcall(xen_pvh_gnttab_setup);
16962306a36Sopenharmony_ci#endif
170