mirror of
https://github.com/AetherDroid/android_kernel_samsung_on5xelte.git
synced 2025-09-09 01:28:05 -04:00
Fixed MTP to work with TWRP
This commit is contained in:
commit
f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions
1
arch/arm/xen/Makefile
Normal file
1
arch/arm/xen/Makefile
Normal file
|
@ -0,0 +1 @@
|
|||
obj-y := enlighten.o hypercall.o grant-table.o p2m.o mm.o mm32.o
|
366
arch/arm/xen/enlighten.c
Normal file
366
arch/arm/xen/enlighten.c
Normal file
|
@ -0,0 +1,366 @@
|
|||
#include <xen/xen.h>
|
||||
#include <xen/events.h>
|
||||
#include <xen/grant_table.h>
|
||||
#include <xen/hvm.h>
|
||||
#include <xen/interface/vcpu.h>
|
||||
#include <xen/interface/xen.h>
|
||||
#include <xen/interface/memory.h>
|
||||
#include <xen/interface/hvm/params.h>
|
||||
#include <xen/features.h>
|
||||
#include <xen/platform_pci.h>
|
||||
#include <xen/xenbus.h>
|
||||
#include <xen/page.h>
|
||||
#include <xen/interface/sched.h>
|
||||
#include <xen/xen-ops.h>
|
||||
#include <asm/xen/hypervisor.h>
|
||||
#include <asm/xen/hypercall.h>
|
||||
#include <asm/system_misc.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/irqreturn.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_irq.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/cpuidle.h>
|
||||
#include <linux/cpufreq.h>
|
||||
#include <linux/cpu.h>
|
||||
|
||||
#include <linux/mm.h>
|
||||
|
||||
struct start_info _xen_start_info;
|
||||
struct start_info *xen_start_info = &_xen_start_info;
|
||||
EXPORT_SYMBOL_GPL(xen_start_info);
|
||||
|
||||
enum xen_domain_type xen_domain_type = XEN_NATIVE;
|
||||
EXPORT_SYMBOL_GPL(xen_domain_type);
|
||||
|
||||
struct shared_info xen_dummy_shared_info;
|
||||
struct shared_info *HYPERVISOR_shared_info = (void *)&xen_dummy_shared_info;
|
||||
|
||||
DEFINE_PER_CPU(struct vcpu_info *, xen_vcpu);
|
||||
static struct vcpu_info __percpu *xen_vcpu_info;
|
||||
|
||||
/* These are unused until we support booting "pre-ballooned" */
|
||||
unsigned long xen_released_pages;
|
||||
struct xen_memory_region xen_extra_mem[XEN_EXTRA_MEM_MAX_REGIONS] __initdata;
|
||||
|
||||
/* TODO: to be removed */
|
||||
__read_mostly int xen_have_vector_callback;
|
||||
EXPORT_SYMBOL_GPL(xen_have_vector_callback);
|
||||
|
||||
int xen_platform_pci_unplug = XEN_UNPLUG_ALL;
|
||||
EXPORT_SYMBOL_GPL(xen_platform_pci_unplug);
|
||||
|
||||
static __read_mostly int xen_events_irq = -1;
|
||||
|
||||
/* map fgmfn of domid to lpfn in the current domain */
|
||||
static int map_foreign_page(unsigned long lpfn, unsigned long fgmfn,
|
||||
unsigned int domid)
|
||||
{
|
||||
int rc;
|
||||
struct xen_add_to_physmap_range xatp = {
|
||||
.domid = DOMID_SELF,
|
||||
.foreign_domid = domid,
|
||||
.size = 1,
|
||||
.space = XENMAPSPACE_gmfn_foreign,
|
||||
};
|
||||
xen_ulong_t idx = fgmfn;
|
||||
xen_pfn_t gpfn = lpfn;
|
||||
int err = 0;
|
||||
|
||||
set_xen_guest_handle(xatp.idxs, &idx);
|
||||
set_xen_guest_handle(xatp.gpfns, &gpfn);
|
||||
set_xen_guest_handle(xatp.errs, &err);
|
||||
|
||||
rc = HYPERVISOR_memory_op(XENMEM_add_to_physmap_range, &xatp);
|
||||
if (rc || err) {
|
||||
pr_warn("Failed to map pfn to mfn rc:%d:%d pfn:%lx mfn:%lx\n",
|
||||
rc, err, lpfn, fgmfn);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct remap_data {
|
||||
xen_pfn_t fgmfn; /* foreign domain's gmfn */
|
||||
pgprot_t prot;
|
||||
domid_t domid;
|
||||
struct vm_area_struct *vma;
|
||||
int index;
|
||||
struct page **pages;
|
||||
struct xen_remap_mfn_info *info;
|
||||
};
|
||||
|
||||
static int remap_pte_fn(pte_t *ptep, pgtable_t token, unsigned long addr,
|
||||
void *data)
|
||||
{
|
||||
struct remap_data *info = data;
|
||||
struct page *page = info->pages[info->index++];
|
||||
unsigned long pfn = page_to_pfn(page);
|
||||
pte_t pte = pte_mkspecial(pfn_pte(pfn, info->prot));
|
||||
|
||||
if (map_foreign_page(pfn, info->fgmfn, info->domid))
|
||||
return -EFAULT;
|
||||
set_pte_at(info->vma->vm_mm, addr, ptep, pte);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int xen_remap_domain_mfn_range(struct vm_area_struct *vma,
|
||||
unsigned long addr,
|
||||
xen_pfn_t mfn, int nr,
|
||||
pgprot_t prot, unsigned domid,
|
||||
struct page **pages)
|
||||
{
|
||||
int err;
|
||||
struct remap_data data;
|
||||
|
||||
/* TBD: Batching, current sole caller only does page at a time */
|
||||
if (nr > 1)
|
||||
return -EINVAL;
|
||||
|
||||
data.fgmfn = mfn;
|
||||
data.prot = prot;
|
||||
data.domid = domid;
|
||||
data.vma = vma;
|
||||
data.index = 0;
|
||||
data.pages = pages;
|
||||
err = apply_to_page_range(vma->vm_mm, addr, nr << PAGE_SHIFT,
|
||||
remap_pte_fn, &data);
|
||||
return err;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xen_remap_domain_mfn_range);
|
||||
|
||||
int xen_unmap_domain_mfn_range(struct vm_area_struct *vma,
|
||||
int nr, struct page **pages)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nr; i++) {
|
||||
struct xen_remove_from_physmap xrp;
|
||||
unsigned long rc, pfn;
|
||||
|
||||
pfn = page_to_pfn(pages[i]);
|
||||
|
||||
xrp.domid = DOMID_SELF;
|
||||
xrp.gpfn = pfn;
|
||||
rc = HYPERVISOR_memory_op(XENMEM_remove_from_physmap, &xrp);
|
||||
if (rc) {
|
||||
pr_warn("Failed to unmap pfn:%lx rc:%ld\n",
|
||||
pfn, rc);
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xen_unmap_domain_mfn_range);
|
||||
|
||||
static void xen_percpu_init(void)
|
||||
{
|
||||
struct vcpu_register_vcpu_info info;
|
||||
struct vcpu_info *vcpup;
|
||||
int err;
|
||||
int cpu = get_cpu();
|
||||
|
||||
pr_info("Xen: initializing cpu%d\n", cpu);
|
||||
vcpup = per_cpu_ptr(xen_vcpu_info, cpu);
|
||||
|
||||
info.mfn = __pa(vcpup) >> PAGE_SHIFT;
|
||||
info.offset = offset_in_page(vcpup);
|
||||
|
||||
err = HYPERVISOR_vcpu_op(VCPUOP_register_vcpu_info, cpu, &info);
|
||||
BUG_ON(err);
|
||||
per_cpu(xen_vcpu, cpu) = vcpup;
|
||||
|
||||
enable_percpu_irq(xen_events_irq, 0);
|
||||
put_cpu();
|
||||
}
|
||||
|
||||
static void xen_restart(enum reboot_mode reboot_mode, const char *cmd)
|
||||
{
|
||||
struct sched_shutdown r = { .reason = SHUTDOWN_reboot };
|
||||
int rc;
|
||||
rc = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r);
|
||||
BUG_ON(rc);
|
||||
}
|
||||
|
||||
static void xen_power_off(void)
|
||||
{
|
||||
struct sched_shutdown r = { .reason = SHUTDOWN_poweroff };
|
||||
int rc;
|
||||
rc = HYPERVISOR_sched_op(SCHEDOP_shutdown, &r);
|
||||
BUG_ON(rc);
|
||||
}
|
||||
|
||||
static int xen_cpu_notification(struct notifier_block *self,
|
||||
unsigned long action,
|
||||
void *hcpu)
|
||||
{
|
||||
switch (action) {
|
||||
case CPU_STARTING:
|
||||
xen_percpu_init();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block xen_cpu_notifier = {
|
||||
.notifier_call = xen_cpu_notification,
|
||||
};
|
||||
|
||||
static irqreturn_t xen_arm_callback(int irq, void *arg)
|
||||
{
|
||||
xen_hvm_evtchn_do_upcall();
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* see Documentation/devicetree/bindings/arm/xen.txt for the
|
||||
* documentation of the Xen Device Tree format.
|
||||
*/
|
||||
#define GRANT_TABLE_PHYSADDR 0
|
||||
static int __init xen_guest_init(void)
|
||||
{
|
||||
struct xen_add_to_physmap xatp;
|
||||
static struct shared_info *shared_info_page = 0;
|
||||
struct device_node *node;
|
||||
int len;
|
||||
const char *s = NULL;
|
||||
const char *version = NULL;
|
||||
const char *xen_prefix = "xen,xen-";
|
||||
struct resource res;
|
||||
phys_addr_t grant_frames;
|
||||
|
||||
node = of_find_compatible_node(NULL, NULL, "xen,xen");
|
||||
if (!node) {
|
||||
pr_debug("No Xen support\n");
|
||||
return 0;
|
||||
}
|
||||
s = of_get_property(node, "compatible", &len);
|
||||
if (strlen(xen_prefix) + 3 < len &&
|
||||
!strncmp(xen_prefix, s, strlen(xen_prefix)))
|
||||
version = s + strlen(xen_prefix);
|
||||
if (version == NULL) {
|
||||
pr_debug("Xen version not found\n");
|
||||
return 0;
|
||||
}
|
||||
if (of_address_to_resource(node, GRANT_TABLE_PHYSADDR, &res))
|
||||
return 0;
|
||||
grant_frames = res.start;
|
||||
xen_events_irq = irq_of_parse_and_map(node, 0);
|
||||
pr_info("Xen %s support found, events_irq=%d gnttab_frame=%pa\n",
|
||||
version, xen_events_irq, &grant_frames);
|
||||
|
||||
if (xen_events_irq < 0)
|
||||
return -ENODEV;
|
||||
|
||||
xen_domain_type = XEN_HVM_DOMAIN;
|
||||
|
||||
xen_setup_features();
|
||||
|
||||
if (!xen_feature(XENFEAT_grant_map_identity)) {
|
||||
pr_warn("Please upgrade your Xen.\n"
|
||||
"If your platform has any non-coherent DMA devices, they won't work properly.\n");
|
||||
}
|
||||
|
||||
if (xen_feature(XENFEAT_dom0))
|
||||
xen_start_info->flags |= SIF_INITDOMAIN|SIF_PRIVILEGED;
|
||||
else
|
||||
xen_start_info->flags &= ~(SIF_INITDOMAIN|SIF_PRIVILEGED);
|
||||
|
||||
if (!shared_info_page)
|
||||
shared_info_page = (struct shared_info *)
|
||||
get_zeroed_page(GFP_KERNEL);
|
||||
if (!shared_info_page) {
|
||||
pr_err("not enough memory\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
xatp.domid = DOMID_SELF;
|
||||
xatp.idx = 0;
|
||||
xatp.space = XENMAPSPACE_shared_info;
|
||||
xatp.gpfn = __pa(shared_info_page) >> PAGE_SHIFT;
|
||||
if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
|
||||
BUG();
|
||||
|
||||
HYPERVISOR_shared_info = (struct shared_info *)shared_info_page;
|
||||
|
||||
/* xen_vcpu is a pointer to the vcpu_info struct in the shared_info
|
||||
* page, we use it in the event channel upcall and in some pvclock
|
||||
* related functions.
|
||||
* The shared info contains exactly 1 CPU (the boot CPU). The guest
|
||||
* is required to use VCPUOP_register_vcpu_info to place vcpu info
|
||||
* for secondary CPUs as they are brought up.
|
||||
* For uniformity we use VCPUOP_register_vcpu_info even on cpu0.
|
||||
*/
|
||||
xen_vcpu_info = __alloc_percpu(sizeof(struct vcpu_info),
|
||||
sizeof(struct vcpu_info));
|
||||
if (xen_vcpu_info == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (gnttab_setup_auto_xlat_frames(grant_frames)) {
|
||||
free_percpu(xen_vcpu_info);
|
||||
return -ENOMEM;
|
||||
}
|
||||
gnttab_init();
|
||||
if (!xen_initial_domain())
|
||||
xenbus_probe(NULL);
|
||||
|
||||
/*
|
||||
* Making sure board specific code will not set up ops for
|
||||
* cpu idle and cpu freq.
|
||||
*/
|
||||
disable_cpuidle();
|
||||
disable_cpufreq();
|
||||
|
||||
xen_init_IRQ();
|
||||
|
||||
if (request_percpu_irq(xen_events_irq, xen_arm_callback,
|
||||
"events", &xen_vcpu)) {
|
||||
pr_err("Error request IRQ %d\n", xen_events_irq);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
xen_percpu_init();
|
||||
|
||||
register_cpu_notifier(&xen_cpu_notifier);
|
||||
|
||||
return 0;
|
||||
}
|
||||
early_initcall(xen_guest_init);
|
||||
|
||||
static int __init xen_pm_init(void)
|
||||
{
|
||||
if (!xen_domain())
|
||||
return -ENODEV;
|
||||
|
||||
pm_power_off = xen_power_off;
|
||||
arm_pm_restart = xen_restart;
|
||||
|
||||
return 0;
|
||||
}
|
||||
late_initcall(xen_pm_init);
|
||||
|
||||
|
||||
/* empty stubs */
|
||||
void xen_arch_pre_suspend(void) { }
|
||||
void xen_arch_post_suspend(int suspend_cancelled) { }
|
||||
void xen_timer_resume(void) { }
|
||||
void xen_arch_resume(void) { }
|
||||
|
||||
|
||||
/* In the hypervisor.S file. */
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_event_channel_op);
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_grant_table_op);
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_xen_version);
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_console_io);
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_sched_op);
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_hvm_op);
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_memory_op);
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_physdev_op);
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_vcpu_op);
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_tmem_op);
|
||||
EXPORT_SYMBOL_GPL(HYPERVISOR_multicall);
|
||||
EXPORT_SYMBOL_GPL(privcmd_call);
|
51
arch/arm/xen/grant-table.c
Normal file
51
arch/arm/xen/grant-table.c
Normal file
|
@ -0,0 +1,51 @@
|
|||
/******************************************************************************
|
||||
* grant_table.c
|
||||
* ARM specific part
|
||||
*
|
||||
* Granting foreign access to our memory reservation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation; or, when distributed
|
||||
* separately from the Linux kernel or incorporated into other
|
||||
* software packages, subject to the following license:
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this source file (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include <xen/interface/xen.h>
|
||||
#include <xen/page.h>
|
||||
#include <xen/grant_table.h>
|
||||
|
||||
int arch_gnttab_map_shared(xen_pfn_t *frames, unsigned long nr_gframes,
|
||||
unsigned long max_nr_gframes,
|
||||
void **__shared)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
|
||||
void arch_gnttab_unmap(void *shared, unsigned long nr_gframes)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int arch_gnttab_init(unsigned long nr_shared)
|
||||
{
|
||||
return 0;
|
||||
}
|
105
arch/arm/xen/hypercall.S
Normal file
105
arch/arm/xen/hypercall.S
Normal file
|
@ -0,0 +1,105 @@
|
|||
/******************************************************************************
|
||||
* hypercall.S
|
||||
*
|
||||
* Xen hypercall wrappers
|
||||
*
|
||||
* Stefano Stabellini <stefano.stabellini@eu.citrix.com>, Citrix, 2012
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License version 2
|
||||
* as published by the Free Software Foundation; or, when distributed
|
||||
* separately from the Linux kernel or incorporated into other
|
||||
* software packages, subject to the following license:
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this source file (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use, copy, modify,
|
||||
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
|
||||
* and to permit persons to whom the Software is furnished to do so, subject to
|
||||
* the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
* IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The Xen hypercall calling convention is very similar to the ARM
|
||||
* procedure calling convention: the first paramter is passed in r0, the
|
||||
* second in r1, the third in r2 and the fourth in r3. Considering that
|
||||
* Xen hypercalls have 5 arguments at most, the fifth paramter is passed
|
||||
* in r4, differently from the procedure calling convention of using the
|
||||
* stack for that case.
|
||||
*
|
||||
* The hypercall number is passed in r12.
|
||||
*
|
||||
* The return value is in r0.
|
||||
*
|
||||
* The hvc ISS is required to be 0xEA1, that is the Xen specific ARM
|
||||
* hypercall tag.
|
||||
*/
|
||||
|
||||
#include <linux/linkage.h>
|
||||
#include <asm/assembler.h>
|
||||
#include <asm/opcodes-virt.h>
|
||||
#include <xen/interface/xen.h>
|
||||
|
||||
|
||||
#define XEN_IMM 0xEA1
|
||||
|
||||
#define HYPERCALL_SIMPLE(hypercall) \
|
||||
ENTRY(HYPERVISOR_##hypercall) \
|
||||
mov r12, #__HYPERVISOR_##hypercall; \
|
||||
__HVC(XEN_IMM); \
|
||||
ret lr; \
|
||||
ENDPROC(HYPERVISOR_##hypercall)
|
||||
|
||||
#define HYPERCALL0 HYPERCALL_SIMPLE
|
||||
#define HYPERCALL1 HYPERCALL_SIMPLE
|
||||
#define HYPERCALL2 HYPERCALL_SIMPLE
|
||||
#define HYPERCALL3 HYPERCALL_SIMPLE
|
||||
#define HYPERCALL4 HYPERCALL_SIMPLE
|
||||
|
||||
#define HYPERCALL5(hypercall) \
|
||||
ENTRY(HYPERVISOR_##hypercall) \
|
||||
stmdb sp!, {r4} \
|
||||
ldr r4, [sp, #4] \
|
||||
mov r12, #__HYPERVISOR_##hypercall; \
|
||||
__HVC(XEN_IMM); \
|
||||
ldm sp!, {r4} \
|
||||
ret lr \
|
||||
ENDPROC(HYPERVISOR_##hypercall)
|
||||
|
||||
.text
|
||||
|
||||
HYPERCALL2(xen_version);
|
||||
HYPERCALL3(console_io);
|
||||
HYPERCALL3(grant_table_op);
|
||||
HYPERCALL2(sched_op);
|
||||
HYPERCALL2(event_channel_op);
|
||||
HYPERCALL2(hvm_op);
|
||||
HYPERCALL2(memory_op);
|
||||
HYPERCALL2(physdev_op);
|
||||
HYPERCALL3(vcpu_op);
|
||||
HYPERCALL1(tmem_op);
|
||||
HYPERCALL2(multicall);
|
||||
|
||||
ENTRY(privcmd_call)
|
||||
stmdb sp!, {r4}
|
||||
mov r12, r0
|
||||
mov r0, r1
|
||||
mov r1, r2
|
||||
mov r2, r3
|
||||
ldr r3, [sp, #8]
|
||||
ldr r4, [sp, #4]
|
||||
__HVC(XEN_IMM)
|
||||
ldm sp!, {r4}
|
||||
ret lr
|
||||
ENDPROC(privcmd_call);
|
72
arch/arm/xen/mm.c
Normal file
72
arch/arm/xen/mm.c
Normal file
|
@ -0,0 +1,72 @@
|
|||
#include <linux/bootmem.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/swiotlb.h>
|
||||
|
||||
#include <xen/xen.h>
|
||||
#include <xen/interface/memory.h>
|
||||
#include <xen/swiotlb-xen.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/xen/page.h>
|
||||
#include <asm/xen/hypercall.h>
|
||||
#include <asm/xen/interface.h>
|
||||
|
||||
bool xen_arch_need_swiotlb(struct device *dev,
|
||||
unsigned long pfn,
|
||||
unsigned long mfn)
|
||||
{
|
||||
return (pfn != mfn);
|
||||
}
|
||||
|
||||
int xen_create_contiguous_region(phys_addr_t pstart, unsigned int order,
|
||||
unsigned int address_bits,
|
||||
dma_addr_t *dma_handle)
|
||||
{
|
||||
if (!xen_initial_domain())
|
||||
return -EINVAL;
|
||||
|
||||
/* we assume that dom0 is mapped 1:1 for now */
|
||||
*dma_handle = pstart;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xen_create_contiguous_region);
|
||||
|
||||
void xen_destroy_contiguous_region(phys_addr_t pstart, unsigned int order)
|
||||
{
|
||||
return;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(xen_destroy_contiguous_region);
|
||||
|
||||
struct dma_map_ops *xen_dma_ops;
|
||||
EXPORT_SYMBOL_GPL(xen_dma_ops);
|
||||
|
||||
static struct dma_map_ops xen_swiotlb_dma_ops = {
|
||||
.mapping_error = xen_swiotlb_dma_mapping_error,
|
||||
.alloc = xen_swiotlb_alloc_coherent,
|
||||
.free = xen_swiotlb_free_coherent,
|
||||
.sync_single_for_cpu = xen_swiotlb_sync_single_for_cpu,
|
||||
.sync_single_for_device = xen_swiotlb_sync_single_for_device,
|
||||
.sync_sg_for_cpu = xen_swiotlb_sync_sg_for_cpu,
|
||||
.sync_sg_for_device = xen_swiotlb_sync_sg_for_device,
|
||||
.map_sg = xen_swiotlb_map_sg_attrs,
|
||||
.unmap_sg = xen_swiotlb_unmap_sg_attrs,
|
||||
.map_page = xen_swiotlb_map_page,
|
||||
.unmap_page = xen_swiotlb_unmap_page,
|
||||
.dma_supported = xen_swiotlb_dma_supported,
|
||||
.set_dma_mask = xen_swiotlb_set_dma_mask,
|
||||
};
|
||||
|
||||
int __init xen_mm_init(void)
|
||||
{
|
||||
if (!xen_initial_domain())
|
||||
return 0;
|
||||
xen_swiotlb_init(1, false);
|
||||
xen_dma_ops = &xen_swiotlb_dma_ops;
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(xen_mm_init);
|
202
arch/arm/xen/mm32.c
Normal file
202
arch/arm/xen/mm32.c
Normal file
|
@ -0,0 +1,202 @@
|
|||
#include <linux/cpu.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/highmem.h>
|
||||
|
||||
#include <xen/features.h>
|
||||
|
||||
static DEFINE_PER_CPU(unsigned long, xen_mm32_scratch_virt);
|
||||
static DEFINE_PER_CPU(pte_t *, xen_mm32_scratch_ptep);
|
||||
|
||||
static int alloc_xen_mm32_scratch_page(int cpu)
|
||||
{
|
||||
struct page *page;
|
||||
unsigned long virt;
|
||||
pmd_t *pmdp;
|
||||
pte_t *ptep;
|
||||
|
||||
if (per_cpu(xen_mm32_scratch_ptep, cpu) != NULL)
|
||||
return 0;
|
||||
|
||||
page = alloc_page(GFP_KERNEL);
|
||||
if (page == NULL) {
|
||||
pr_warn("Failed to allocate xen_mm32_scratch_page for cpu %d\n", cpu);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
virt = (unsigned long)__va(page_to_phys(page));
|
||||
pmdp = pmd_offset(pud_offset(pgd_offset_k(virt), virt), virt);
|
||||
ptep = pte_offset_kernel(pmdp, virt);
|
||||
|
||||
per_cpu(xen_mm32_scratch_virt, cpu) = virt;
|
||||
per_cpu(xen_mm32_scratch_ptep, cpu) = ptep;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xen_mm32_cpu_notify(struct notifier_block *self,
|
||||
unsigned long action, void *hcpu)
|
||||
{
|
||||
int cpu = (long)hcpu;
|
||||
switch (action) {
|
||||
case CPU_UP_PREPARE:
|
||||
if (alloc_xen_mm32_scratch_page(cpu))
|
||||
return NOTIFY_BAD;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
static struct notifier_block xen_mm32_cpu_notifier = {
|
||||
.notifier_call = xen_mm32_cpu_notify,
|
||||
};
|
||||
|
||||
static void* xen_mm32_remap_page(dma_addr_t handle)
|
||||
{
|
||||
unsigned long virt = get_cpu_var(xen_mm32_scratch_virt);
|
||||
pte_t *ptep = __get_cpu_var(xen_mm32_scratch_ptep);
|
||||
|
||||
*ptep = pfn_pte(handle >> PAGE_SHIFT, PAGE_KERNEL);
|
||||
local_flush_tlb_kernel_page(virt);
|
||||
|
||||
return (void*)virt;
|
||||
}
|
||||
|
||||
static void xen_mm32_unmap(void *vaddr)
|
||||
{
|
||||
put_cpu_var(xen_mm32_scratch_virt);
|
||||
}
|
||||
|
||||
|
||||
/* functions called by SWIOTLB */
|
||||
|
||||
static void dma_cache_maint(dma_addr_t handle, unsigned long offset,
|
||||
size_t size, enum dma_data_direction dir,
|
||||
void (*op)(const void *, size_t, int))
|
||||
{
|
||||
unsigned long pfn;
|
||||
size_t left = size;
|
||||
|
||||
pfn = (handle >> PAGE_SHIFT) + offset / PAGE_SIZE;
|
||||
offset %= PAGE_SIZE;
|
||||
|
||||
do {
|
||||
size_t len = left;
|
||||
void *vaddr;
|
||||
|
||||
if (!pfn_valid(pfn))
|
||||
{
|
||||
/* Cannot map the page, we don't know its physical address.
|
||||
* Return and hope for the best */
|
||||
if (!xen_feature(XENFEAT_grant_map_identity))
|
||||
return;
|
||||
vaddr = xen_mm32_remap_page(handle) + offset;
|
||||
op(vaddr, len, dir);
|
||||
xen_mm32_unmap(vaddr - offset);
|
||||
} else {
|
||||
struct page *page = pfn_to_page(pfn);
|
||||
|
||||
if (PageHighMem(page)) {
|
||||
if (len + offset > PAGE_SIZE)
|
||||
len = PAGE_SIZE - offset;
|
||||
|
||||
if (cache_is_vipt_nonaliasing()) {
|
||||
vaddr = kmap_atomic(page);
|
||||
op(vaddr + offset, len, dir);
|
||||
kunmap_atomic(vaddr);
|
||||
} else {
|
||||
vaddr = kmap_high_get(page);
|
||||
if (vaddr) {
|
||||
op(vaddr + offset, len, dir);
|
||||
kunmap_high(page);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
vaddr = page_address(page) + offset;
|
||||
op(vaddr, len, dir);
|
||||
}
|
||||
}
|
||||
|
||||
offset = 0;
|
||||
pfn++;
|
||||
left -= len;
|
||||
} while (left);
|
||||
}
|
||||
|
||||
static void __xen_dma_page_dev_to_cpu(struct device *hwdev, dma_addr_t handle,
|
||||
size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
/* Cannot use __dma_page_dev_to_cpu because we don't have a
|
||||
* struct page for handle */
|
||||
|
||||
if (dir != DMA_TO_DEVICE)
|
||||
outer_inv_range(handle, handle + size);
|
||||
|
||||
dma_cache_maint(handle & PAGE_MASK, handle & ~PAGE_MASK, size, dir, dmac_unmap_area);
|
||||
}
|
||||
|
||||
static void __xen_dma_page_cpu_to_dev(struct device *hwdev, dma_addr_t handle,
|
||||
size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
|
||||
dma_cache_maint(handle & PAGE_MASK, handle & ~PAGE_MASK, size, dir, dmac_map_area);
|
||||
|
||||
if (dir == DMA_FROM_DEVICE) {
|
||||
outer_inv_range(handle, handle + size);
|
||||
} else {
|
||||
outer_clean_range(handle, handle + size);
|
||||
}
|
||||
}
|
||||
|
||||
void xen_dma_unmap_page(struct device *hwdev, dma_addr_t handle,
|
||||
size_t size, enum dma_data_direction dir,
|
||||
struct dma_attrs *attrs)
|
||||
|
||||
{
|
||||
if (!__generic_dma_ops(hwdev)->unmap_page)
|
||||
return;
|
||||
if (dma_get_attr(DMA_ATTR_SKIP_CPU_SYNC, attrs))
|
||||
return;
|
||||
|
||||
__xen_dma_page_dev_to_cpu(hwdev, handle, size, dir);
|
||||
}
|
||||
|
||||
void xen_dma_sync_single_for_cpu(struct device *hwdev,
|
||||
dma_addr_t handle, size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
if (!__generic_dma_ops(hwdev)->sync_single_for_cpu)
|
||||
return;
|
||||
__xen_dma_page_dev_to_cpu(hwdev, handle, size, dir);
|
||||
}
|
||||
|
||||
void xen_dma_sync_single_for_device(struct device *hwdev,
|
||||
dma_addr_t handle, size_t size, enum dma_data_direction dir)
|
||||
{
|
||||
if (!__generic_dma_ops(hwdev)->sync_single_for_device)
|
||||
return;
|
||||
__xen_dma_page_cpu_to_dev(hwdev, handle, size, dir);
|
||||
}
|
||||
|
||||
int __init xen_mm32_init(void)
|
||||
{
|
||||
int cpu;
|
||||
|
||||
if (!xen_initial_domain())
|
||||
return 0;
|
||||
|
||||
register_cpu_notifier(&xen_mm32_cpu_notifier);
|
||||
get_online_cpus();
|
||||
for_each_online_cpu(cpu) {
|
||||
if (alloc_xen_mm32_scratch_page(cpu)) {
|
||||
put_online_cpus();
|
||||
unregister_cpu_notifier(&xen_mm32_cpu_notifier);
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
put_online_cpus();
|
||||
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(xen_mm32_init);
|
177
arch/arm/xen/p2m.c
Normal file
177
arch/arm/xen/p2m.c
Normal file
|
@ -0,0 +1,177 @@
|
|||
#include <linux/bootmem.h>
|
||||
#include <linux/gfp.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/rwlock.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/vmalloc.h>
|
||||
#include <linux/swiotlb.h>
|
||||
|
||||
#include <xen/xen.h>
|
||||
#include <xen/interface/memory.h>
|
||||
#include <xen/swiotlb-xen.h>
|
||||
|
||||
#include <asm/cacheflush.h>
|
||||
#include <asm/xen/page.h>
|
||||
#include <asm/xen/hypercall.h>
|
||||
#include <asm/xen/interface.h>
|
||||
|
||||
struct xen_p2m_entry {
|
||||
unsigned long pfn;
|
||||
unsigned long mfn;
|
||||
unsigned long nr_pages;
|
||||
struct rb_node rbnode_phys;
|
||||
};
|
||||
|
||||
static rwlock_t p2m_lock;
|
||||
struct rb_root phys_to_mach = RB_ROOT;
|
||||
EXPORT_SYMBOL_GPL(phys_to_mach);
|
||||
|
||||
static int xen_add_phys_to_mach_entry(struct xen_p2m_entry *new)
|
||||
{
|
||||
struct rb_node **link = &phys_to_mach.rb_node;
|
||||
struct rb_node *parent = NULL;
|
||||
struct xen_p2m_entry *entry;
|
||||
int rc = 0;
|
||||
|
||||
while (*link) {
|
||||
parent = *link;
|
||||
entry = rb_entry(parent, struct xen_p2m_entry, rbnode_phys);
|
||||
|
||||
if (new->pfn == entry->pfn)
|
||||
goto err_out;
|
||||
|
||||
if (new->pfn < entry->pfn)
|
||||
link = &(*link)->rb_left;
|
||||
else
|
||||
link = &(*link)->rb_right;
|
||||
}
|
||||
rb_link_node(&new->rbnode_phys, parent, link);
|
||||
rb_insert_color(&new->rbnode_phys, &phys_to_mach);
|
||||
goto out;
|
||||
|
||||
err_out:
|
||||
rc = -EINVAL;
|
||||
pr_warn("%s: cannot add pfn=%pa -> mfn=%pa: pfn=%pa -> mfn=%pa already exists\n",
|
||||
__func__, &new->pfn, &new->mfn, &entry->pfn, &entry->mfn);
|
||||
out:
|
||||
return rc;
|
||||
}
|
||||
|
||||
unsigned long __pfn_to_mfn(unsigned long pfn)
|
||||
{
|
||||
struct rb_node *n = phys_to_mach.rb_node;
|
||||
struct xen_p2m_entry *entry;
|
||||
unsigned long irqflags;
|
||||
|
||||
read_lock_irqsave(&p2m_lock, irqflags);
|
||||
while (n) {
|
||||
entry = rb_entry(n, struct xen_p2m_entry, rbnode_phys);
|
||||
if (entry->pfn <= pfn &&
|
||||
entry->pfn + entry->nr_pages > pfn) {
|
||||
read_unlock_irqrestore(&p2m_lock, irqflags);
|
||||
return entry->mfn + (pfn - entry->pfn);
|
||||
}
|
||||
if (pfn < entry->pfn)
|
||||
n = n->rb_left;
|
||||
else
|
||||
n = n->rb_right;
|
||||
}
|
||||
read_unlock_irqrestore(&p2m_lock, irqflags);
|
||||
|
||||
return INVALID_P2M_ENTRY;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__pfn_to_mfn);
|
||||
|
||||
int set_foreign_p2m_mapping(struct gnttab_map_grant_ref *map_ops,
|
||||
struct gnttab_map_grant_ref *kmap_ops,
|
||||
struct page **pages, unsigned int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
if (map_ops[i].status)
|
||||
continue;
|
||||
set_phys_to_machine(map_ops[i].host_addr >> PAGE_SHIFT,
|
||||
map_ops[i].dev_bus_addr >> PAGE_SHIFT);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(set_foreign_p2m_mapping);
|
||||
|
||||
int clear_foreign_p2m_mapping(struct gnttab_unmap_grant_ref *unmap_ops,
|
||||
struct gnttab_map_grant_ref *kmap_ops,
|
||||
struct page **pages, unsigned int count)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < count; i++) {
|
||||
set_phys_to_machine(unmap_ops[i].host_addr >> PAGE_SHIFT,
|
||||
INVALID_P2M_ENTRY);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(clear_foreign_p2m_mapping);
|
||||
|
||||
bool __set_phys_to_machine_multi(unsigned long pfn,
|
||||
unsigned long mfn, unsigned long nr_pages)
|
||||
{
|
||||
int rc;
|
||||
unsigned long irqflags;
|
||||
struct xen_p2m_entry *p2m_entry;
|
||||
struct rb_node *n = phys_to_mach.rb_node;
|
||||
|
||||
if (mfn == INVALID_P2M_ENTRY) {
|
||||
write_lock_irqsave(&p2m_lock, irqflags);
|
||||
while (n) {
|
||||
p2m_entry = rb_entry(n, struct xen_p2m_entry, rbnode_phys);
|
||||
if (p2m_entry->pfn <= pfn &&
|
||||
p2m_entry->pfn + p2m_entry->nr_pages > pfn) {
|
||||
rb_erase(&p2m_entry->rbnode_phys, &phys_to_mach);
|
||||
write_unlock_irqrestore(&p2m_lock, irqflags);
|
||||
kfree(p2m_entry);
|
||||
return true;
|
||||
}
|
||||
if (pfn < p2m_entry->pfn)
|
||||
n = n->rb_left;
|
||||
else
|
||||
n = n->rb_right;
|
||||
}
|
||||
write_unlock_irqrestore(&p2m_lock, irqflags);
|
||||
return true;
|
||||
}
|
||||
|
||||
p2m_entry = kzalloc(sizeof(struct xen_p2m_entry), GFP_NOWAIT);
|
||||
if (!p2m_entry) {
|
||||
pr_warn("cannot allocate xen_p2m_entry\n");
|
||||
return false;
|
||||
}
|
||||
p2m_entry->pfn = pfn;
|
||||
p2m_entry->nr_pages = nr_pages;
|
||||
p2m_entry->mfn = mfn;
|
||||
|
||||
write_lock_irqsave(&p2m_lock, irqflags);
|
||||
if ((rc = xen_add_phys_to_mach_entry(p2m_entry)) < 0) {
|
||||
write_unlock_irqrestore(&p2m_lock, irqflags);
|
||||
return false;
|
||||
}
|
||||
write_unlock_irqrestore(&p2m_lock, irqflags);
|
||||
return true;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__set_phys_to_machine_multi);
|
||||
|
||||
bool __set_phys_to_machine(unsigned long pfn, unsigned long mfn)
|
||||
{
|
||||
return __set_phys_to_machine_multi(pfn, mfn, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__set_phys_to_machine);
|
||||
|
||||
static int p2m_init(void)
|
||||
{
|
||||
rwlock_init(&p2m_lock);
|
||||
return 0;
|
||||
}
|
||||
arch_initcall(p2m_init);
|
Loading…
Add table
Add a link
Reference in a new issue