xref: /openbmc/linux/drivers/virt/fsl_hypervisor.c (revision 10ce6b70)
16db71994STimur Tabi /*
26db71994STimur Tabi  * Freescale Hypervisor Management Driver
36db71994STimur Tabi 
46db71994STimur Tabi  * Copyright (C) 2008-2011 Freescale Semiconductor, Inc.
56db71994STimur Tabi  * Author: Timur Tabi <timur@freescale.com>
66db71994STimur Tabi  *
76db71994STimur Tabi  * This file is licensed under the terms of the GNU General Public License
86db71994STimur Tabi  * version 2.  This program is licensed "as is" without any warranty of any
96db71994STimur Tabi  * kind, whether express or implied.
106db71994STimur Tabi  *
116db71994STimur Tabi  * The Freescale hypervisor management driver provides several services to
126db71994STimur Tabi  * drivers and applications related to the Freescale hypervisor:
136db71994STimur Tabi  *
146db71994STimur Tabi  * 1. An ioctl interface for querying and managing partitions.
156db71994STimur Tabi  *
166db71994STimur Tabi  * 2. A file interface to reading incoming doorbells.
176db71994STimur Tabi  *
186db71994STimur Tabi  * 3. An interrupt handler for shutting down the partition upon receiving the
196db71994STimur Tabi  *    shutdown doorbell from a manager partition.
206db71994STimur Tabi  *
216db71994STimur Tabi  * 4. A kernel interface for receiving callbacks when a managed partition
226db71994STimur Tabi  *    shuts down.
236db71994STimur Tabi  */
246db71994STimur Tabi 
256db71994STimur Tabi #include <linux/kernel.h>
266db71994STimur Tabi #include <linux/module.h>
276db71994STimur Tabi #include <linux/init.h>
286db71994STimur Tabi #include <linux/types.h>
296db71994STimur Tabi #include <linux/err.h>
306db71994STimur Tabi #include <linux/fs.h>
316db71994STimur Tabi #include <linux/miscdevice.h>
326db71994STimur Tabi #include <linux/mm.h>
336db71994STimur Tabi #include <linux/pagemap.h>
346db71994STimur Tabi #include <linux/slab.h>
356db71994STimur Tabi #include <linux/poll.h>
366db71994STimur Tabi #include <linux/of.h>
375af50730SRob Herring #include <linux/of_irq.h>
386db71994STimur Tabi #include <linux/reboot.h>
396db71994STimur Tabi #include <linux/uaccess.h>
406db71994STimur Tabi #include <linux/notifier.h>
41f1f4ee01STimur Tabi #include <linux/interrupt.h>
426db71994STimur Tabi 
436db71994STimur Tabi #include <linux/io.h>
446db71994STimur Tabi #include <asm/fsl_hcalls.h>
456db71994STimur Tabi 
466db71994STimur Tabi #include <linux/fsl_hypervisor.h>
476db71994STimur Tabi 
486db71994STimur Tabi static BLOCKING_NOTIFIER_HEAD(failover_subscribers);
496db71994STimur Tabi 
506db71994STimur Tabi /*
516db71994STimur Tabi  * Ioctl interface for FSL_HV_IOCTL_PARTITION_RESTART
526db71994STimur Tabi  *
536db71994STimur Tabi  * Restart a running partition
546db71994STimur Tabi  */
ioctl_restart(struct fsl_hv_ioctl_restart __user * p)556db71994STimur Tabi static long ioctl_restart(struct fsl_hv_ioctl_restart __user *p)
566db71994STimur Tabi {
576db71994STimur Tabi 	struct fsl_hv_ioctl_restart param;
586db71994STimur Tabi 
596db71994STimur Tabi 	/* Get the parameters from the user */
606db71994STimur Tabi 	if (copy_from_user(&param, p, sizeof(struct fsl_hv_ioctl_restart)))
616db71994STimur Tabi 		return -EFAULT;
626db71994STimur Tabi 
636db71994STimur Tabi 	param.ret = fh_partition_restart(param.partition);
646db71994STimur Tabi 
656db71994STimur Tabi 	if (copy_to_user(&p->ret, &param.ret, sizeof(__u32)))
666db71994STimur Tabi 		return -EFAULT;
676db71994STimur Tabi 
686db71994STimur Tabi 	return 0;
696db71994STimur Tabi }
706db71994STimur Tabi 
716db71994STimur Tabi /*
726db71994STimur Tabi  * Ioctl interface for FSL_HV_IOCTL_PARTITION_STATUS
736db71994STimur Tabi  *
746db71994STimur Tabi  * Query the status of a partition
756db71994STimur Tabi  */
ioctl_status(struct fsl_hv_ioctl_status __user * p)766db71994STimur Tabi static long ioctl_status(struct fsl_hv_ioctl_status __user *p)
776db71994STimur Tabi {
786db71994STimur Tabi 	struct fsl_hv_ioctl_status param;
796db71994STimur Tabi 	u32 status;
806db71994STimur Tabi 
816db71994STimur Tabi 	/* Get the parameters from the user */
826db71994STimur Tabi 	if (copy_from_user(&param, p, sizeof(struct fsl_hv_ioctl_status)))
836db71994STimur Tabi 		return -EFAULT;
846db71994STimur Tabi 
856db71994STimur Tabi 	param.ret = fh_partition_get_status(param.partition, &status);
866db71994STimur Tabi 	if (!param.ret)
876db71994STimur Tabi 		param.status = status;
886db71994STimur Tabi 
896db71994STimur Tabi 	if (copy_to_user(p, &param, sizeof(struct fsl_hv_ioctl_status)))
906db71994STimur Tabi 		return -EFAULT;
916db71994STimur Tabi 
926db71994STimur Tabi 	return 0;
936db71994STimur Tabi }
946db71994STimur Tabi 
956db71994STimur Tabi /*
966db71994STimur Tabi  * Ioctl interface for FSL_HV_IOCTL_PARTITION_START
976db71994STimur Tabi  *
986db71994STimur Tabi  * Start a stopped partition.
996db71994STimur Tabi  */
ioctl_start(struct fsl_hv_ioctl_start __user * p)1006db71994STimur Tabi static long ioctl_start(struct fsl_hv_ioctl_start __user *p)
1016db71994STimur Tabi {
1026db71994STimur Tabi 	struct fsl_hv_ioctl_start param;
1036db71994STimur Tabi 
1046db71994STimur Tabi 	/* Get the parameters from the user */
1056db71994STimur Tabi 	if (copy_from_user(&param, p, sizeof(struct fsl_hv_ioctl_start)))
1066db71994STimur Tabi 		return -EFAULT;
1076db71994STimur Tabi 
1086db71994STimur Tabi 	param.ret = fh_partition_start(param.partition, param.entry_point,
1096db71994STimur Tabi 				       param.load);
1106db71994STimur Tabi 
1116db71994STimur Tabi 	if (copy_to_user(&p->ret, &param.ret, sizeof(__u32)))
1126db71994STimur Tabi 		return -EFAULT;
1136db71994STimur Tabi 
1146db71994STimur Tabi 	return 0;
1156db71994STimur Tabi }
1166db71994STimur Tabi 
1176db71994STimur Tabi /*
1186db71994STimur Tabi  * Ioctl interface for FSL_HV_IOCTL_PARTITION_STOP
1196db71994STimur Tabi  *
1206db71994STimur Tabi  * Stop a running partition
1216db71994STimur Tabi  */
ioctl_stop(struct fsl_hv_ioctl_stop __user * p)1226db71994STimur Tabi static long ioctl_stop(struct fsl_hv_ioctl_stop __user *p)
1236db71994STimur Tabi {
1246db71994STimur Tabi 	struct fsl_hv_ioctl_stop param;
1256db71994STimur Tabi 
1266db71994STimur Tabi 	/* Get the parameters from the user */
1276db71994STimur Tabi 	if (copy_from_user(&param, p, sizeof(struct fsl_hv_ioctl_stop)))
1286db71994STimur Tabi 		return -EFAULT;
1296db71994STimur Tabi 
1306db71994STimur Tabi 	param.ret = fh_partition_stop(param.partition);
1316db71994STimur Tabi 
1326db71994STimur Tabi 	if (copy_to_user(&p->ret, &param.ret, sizeof(__u32)))
1336db71994STimur Tabi 		return -EFAULT;
1346db71994STimur Tabi 
1356db71994STimur Tabi 	return 0;
1366db71994STimur Tabi }
1376db71994STimur Tabi 
1386db71994STimur Tabi /*
1396db71994STimur Tabi  * Ioctl interface for FSL_HV_IOCTL_MEMCPY
1406db71994STimur Tabi  *
1416db71994STimur Tabi  * The FH_MEMCPY hypercall takes an array of address/address/size structures
1426db71994STimur Tabi  * to represent the data being copied.  As a convenience to the user, this
1436db71994STimur Tabi  * ioctl takes a user-create buffer and a pointer to a guest physically
1446db71994STimur Tabi  * contiguous buffer in the remote partition, and creates the
1456db71994STimur Tabi  * address/address/size array for the hypercall.
1466db71994STimur Tabi  */
ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user * p)1476db71994STimur Tabi static long ioctl_memcpy(struct fsl_hv_ioctl_memcpy __user *p)
1486db71994STimur Tabi {
1496db71994STimur Tabi 	struct fsl_hv_ioctl_memcpy param;
1506db71994STimur Tabi 
1516db71994STimur Tabi 	struct page **pages = NULL;
1526db71994STimur Tabi 	void *sg_list_unaligned = NULL;
1536db71994STimur Tabi 	struct fh_sg_list *sg_list = NULL;
1546db71994STimur Tabi 
1556db71994STimur Tabi 	unsigned int num_pages;
1566db71994STimur Tabi 	unsigned long lb_offset; /* Offset within a page of the local buffer */
1576db71994STimur Tabi 
1586db71994STimur Tabi 	unsigned int i;
1596db71994STimur Tabi 	long ret = 0;
1607f360becSSouptick Joarder 	int num_pinned = 0; /* return value from get_user_pages_fast() */
1616db71994STimur Tabi 	phys_addr_t remote_paddr; /* The next address in the remote buffer */
1626db71994STimur Tabi 	uint32_t count; /* The number of bytes left to copy */
1636db71994STimur Tabi 
1646db71994STimur Tabi 	/* Get the parameters from the user */
1656db71994STimur Tabi 	if (copy_from_user(&param, p, sizeof(struct fsl_hv_ioctl_memcpy)))
1666db71994STimur Tabi 		return -EFAULT;
1676db71994STimur Tabi 
1686db71994STimur Tabi 	/*
1696db71994STimur Tabi 	 * One partition must be local, the other must be remote.  In other
1706db71994STimur Tabi 	 * words, if source and target are both -1, or are both not -1, then
1716db71994STimur Tabi 	 * return an error.
1726db71994STimur Tabi 	 */
1736db71994STimur Tabi 	if ((param.source == -1) == (param.target == -1))
1746db71994STimur Tabi 		return -EINVAL;
1756db71994STimur Tabi 
1766db71994STimur Tabi 	/*
1777f360becSSouptick Joarder 	 * The array of pages returned by get_user_pages_fast() covers only
1786db71994STimur Tabi 	 * page-aligned memory.  Since the user buffer is probably not
1796db71994STimur Tabi 	 * page-aligned, we need to handle the discrepancy.
1806db71994STimur Tabi 	 *
1816db71994STimur Tabi 	 * We calculate the offset within a page of the S/G list, and make
1826db71994STimur Tabi 	 * adjustments accordingly.  This will result in a page list that looks
1836db71994STimur Tabi 	 * like this:
1846db71994STimur Tabi 	 *
1856db71994STimur Tabi 	 *      ----    <-- first page starts before the buffer
1866db71994STimur Tabi 	 *     |    |
1876db71994STimur Tabi 	 *     |////|-> ----
1886db71994STimur Tabi 	 *     |////|  |    |
1896db71994STimur Tabi 	 *      ----   |    |
1906db71994STimur Tabi 	 *             |    |
1916db71994STimur Tabi 	 *      ----   |    |
1926db71994STimur Tabi 	 *     |////|  |    |
1936db71994STimur Tabi 	 *     |////|  |    |
1946db71994STimur Tabi 	 *     |////|  |    |
1956db71994STimur Tabi 	 *      ----   |    |
1966db71994STimur Tabi 	 *             |    |
1976db71994STimur Tabi 	 *      ----   |    |
1986db71994STimur Tabi 	 *     |////|  |    |
1996db71994STimur Tabi 	 *     |////|  |    |
2006db71994STimur Tabi 	 *     |////|  |    |
2016db71994STimur Tabi 	 *      ----   |    |
2026db71994STimur Tabi 	 *             |    |
2036db71994STimur Tabi 	 *      ----   |    |
2046db71994STimur Tabi 	 *     |////|  |    |
2056db71994STimur Tabi 	 *     |////|-> ----
2066db71994STimur Tabi 	 *     |    |   <-- last page ends after the buffer
2076db71994STimur Tabi 	 *      ----
2086db71994STimur Tabi 	 *
2096db71994STimur Tabi 	 * The distance between the start of the first page and the start of the
2106db71994STimur Tabi 	 * buffer is lb_offset.  The hashed (///) areas are the parts of the
2116db71994STimur Tabi 	 * page list that contain the actual buffer.
2126db71994STimur Tabi 	 *
2136db71994STimur Tabi 	 * The advantage of this approach is that the number of pages is
2146db71994STimur Tabi 	 * equal to the number of entries in the S/G list that we give to the
2156db71994STimur Tabi 	 * hypervisor.
2166db71994STimur Tabi 	 */
2176db71994STimur Tabi 	lb_offset = param.local_vaddr & (PAGE_SIZE - 1);
2186a024330SDan Carpenter 	if (param.count == 0 ||
2196a024330SDan Carpenter 	    param.count > U64_MAX - lb_offset - PAGE_SIZE + 1)
2206a024330SDan Carpenter 		return -EINVAL;
2216db71994STimur Tabi 	num_pages = (param.count + lb_offset + PAGE_SIZE - 1) >> PAGE_SHIFT;
2226db71994STimur Tabi 
2236db71994STimur Tabi 	/* Allocate the buffers we need */
2246db71994STimur Tabi 
2256db71994STimur Tabi 	/*
2266db71994STimur Tabi 	 * 'pages' is an array of struct page pointers that's initialized by
2277f360becSSouptick Joarder 	 * get_user_pages_fast().
2286db71994STimur Tabi 	 */
2296396bb22SKees Cook 	pages = kcalloc(num_pages, sizeof(struct page *), GFP_KERNEL);
2306db71994STimur Tabi 	if (!pages) {
2316db71994STimur Tabi 		pr_debug("fsl-hv: could not allocate page list\n");
2326db71994STimur Tabi 		return -ENOMEM;
2336db71994STimur Tabi 	}
2346db71994STimur Tabi 
2356db71994STimur Tabi 	/*
2366db71994STimur Tabi 	 * sg_list is the list of fh_sg_list objects that we pass to the
2376db71994STimur Tabi 	 * hypervisor.
2386db71994STimur Tabi 	 */
2396db71994STimur Tabi 	sg_list_unaligned = kmalloc(num_pages * sizeof(struct fh_sg_list) +
2406db71994STimur Tabi 		sizeof(struct fh_sg_list) - 1, GFP_KERNEL);
2416db71994STimur Tabi 	if (!sg_list_unaligned) {
2426db71994STimur Tabi 		pr_debug("fsl-hv: could not allocate S/G list\n");
2436db71994STimur Tabi 		ret = -ENOMEM;
2447f360becSSouptick Joarder 		goto free_pages;
2456db71994STimur Tabi 	}
2466db71994STimur Tabi 	sg_list = PTR_ALIGN(sg_list_unaligned, sizeof(struct fh_sg_list));
2476db71994STimur Tabi 
2486db71994STimur Tabi 	/* Get the physical addresses of the source buffer */
2491aaa09caSAl Viro 	num_pinned = get_user_pages_fast(param.local_vaddr - lb_offset,
25073b0140bSIra Weiny 		num_pages, param.source != -1 ? FOLL_WRITE : 0, pages);
2516db71994STimur Tabi 
2526db71994STimur Tabi 	if (num_pinned != num_pages) {
2536db71994STimur Tabi 		pr_debug("fsl-hv: could not lock source buffer\n");
2546db71994STimur Tabi 		ret = (num_pinned < 0) ? num_pinned : -EFAULT;
2556db71994STimur Tabi 		goto exit;
2566db71994STimur Tabi 	}
2576db71994STimur Tabi 
2586db71994STimur Tabi 	/*
2596db71994STimur Tabi 	 * Build the fh_sg_list[] array.  The first page is special
2606db71994STimur Tabi 	 * because it's misaligned.
2616db71994STimur Tabi 	 */
2626db71994STimur Tabi 	if (param.source == -1) {
2636db71994STimur Tabi 		sg_list[0].source = page_to_phys(pages[0]) + lb_offset;
2646db71994STimur Tabi 		sg_list[0].target = param.remote_paddr;
2656db71994STimur Tabi 	} else {
2666db71994STimur Tabi 		sg_list[0].source = param.remote_paddr;
2676db71994STimur Tabi 		sg_list[0].target = page_to_phys(pages[0]) + lb_offset;
2686db71994STimur Tabi 	}
2696db71994STimur Tabi 	sg_list[0].size = min_t(uint64_t, param.count, PAGE_SIZE - lb_offset);
2706db71994STimur Tabi 
2716db71994STimur Tabi 	remote_paddr = param.remote_paddr + sg_list[0].size;
2726db71994STimur Tabi 	count = param.count - sg_list[0].size;
2736db71994STimur Tabi 
2746db71994STimur Tabi 	for (i = 1; i < num_pages; i++) {
2756db71994STimur Tabi 		if (param.source == -1) {
2766db71994STimur Tabi 			/* local to remote */
2776db71994STimur Tabi 			sg_list[i].source = page_to_phys(pages[i]);
2786db71994STimur Tabi 			sg_list[i].target = remote_paddr;
2796db71994STimur Tabi 		} else {
2806db71994STimur Tabi 			/* remote to local */
2816db71994STimur Tabi 			sg_list[i].source = remote_paddr;
2826db71994STimur Tabi 			sg_list[i].target = page_to_phys(pages[i]);
2836db71994STimur Tabi 		}
2846db71994STimur Tabi 		sg_list[i].size = min_t(uint64_t, count, PAGE_SIZE);
2856db71994STimur Tabi 
2866db71994STimur Tabi 		remote_paddr += sg_list[i].size;
2876db71994STimur Tabi 		count -= sg_list[i].size;
2886db71994STimur Tabi 	}
2896db71994STimur Tabi 
2906db71994STimur Tabi 	param.ret = fh_partition_memcpy(param.source, param.target,
2916db71994STimur Tabi 		virt_to_phys(sg_list), num_pages);
2926db71994STimur Tabi 
2936db71994STimur Tabi exit:
2947f360becSSouptick Joarder 	if (pages && (num_pinned > 0)) {
2957f360becSSouptick Joarder 		for (i = 0; i < num_pinned; i++)
2966db71994STimur Tabi 			put_page(pages[i]);
2976db71994STimur Tabi 	}
2986db71994STimur Tabi 
2996db71994STimur Tabi 	kfree(sg_list_unaligned);
3007f360becSSouptick Joarder free_pages:
3016db71994STimur Tabi 	kfree(pages);
3026db71994STimur Tabi 
3036db71994STimur Tabi 	if (!ret)
3046db71994STimur Tabi 		if (copy_to_user(&p->ret, &param.ret, sizeof(__u32)))
3056db71994STimur Tabi 			return -EFAULT;
3066db71994STimur Tabi 
3076db71994STimur Tabi 	return ret;
3086db71994STimur Tabi }
3096db71994STimur Tabi 
3106db71994STimur Tabi /*
3116db71994STimur Tabi  * Ioctl interface for FSL_HV_IOCTL_DOORBELL
3126db71994STimur Tabi  *
3136db71994STimur Tabi  * Ring a doorbell
3146db71994STimur Tabi  */
ioctl_doorbell(struct fsl_hv_ioctl_doorbell __user * p)3156db71994STimur Tabi static long ioctl_doorbell(struct fsl_hv_ioctl_doorbell __user *p)
3166db71994STimur Tabi {
3176db71994STimur Tabi 	struct fsl_hv_ioctl_doorbell param;
3186db71994STimur Tabi 
3196db71994STimur Tabi 	/* Get the parameters from the user. */
3206db71994STimur Tabi 	if (copy_from_user(&param, p, sizeof(struct fsl_hv_ioctl_doorbell)))
3216db71994STimur Tabi 		return -EFAULT;
3226db71994STimur Tabi 
3236db71994STimur Tabi 	param.ret = ev_doorbell_send(param.doorbell);
3246db71994STimur Tabi 
3256db71994STimur Tabi 	if (copy_to_user(&p->ret, &param.ret, sizeof(__u32)))
3266db71994STimur Tabi 		return -EFAULT;
3276db71994STimur Tabi 
3286db71994STimur Tabi 	return 0;
3296db71994STimur Tabi }
3306db71994STimur Tabi 
ioctl_dtprop(struct fsl_hv_ioctl_prop __user * p,int set)3316db71994STimur Tabi static long ioctl_dtprop(struct fsl_hv_ioctl_prop __user *p, int set)
3326db71994STimur Tabi {
3336db71994STimur Tabi 	struct fsl_hv_ioctl_prop param;
3346db71994STimur Tabi 	char __user *upath, *upropname;
3356db71994STimur Tabi 	void __user *upropval;
336c8ea3663SDan Carpenter 	char *path, *propname;
337c8ea3663SDan Carpenter 	void *propval;
3386db71994STimur Tabi 	int ret = 0;
3396db71994STimur Tabi 
3406db71994STimur Tabi 	/* Get the parameters from the user. */
3416db71994STimur Tabi 	if (copy_from_user(&param, p, sizeof(struct fsl_hv_ioctl_prop)))
3426db71994STimur Tabi 		return -EFAULT;
3436db71994STimur Tabi 
3446db71994STimur Tabi 	upath = (char __user *)(uintptr_t)param.path;
3456db71994STimur Tabi 	upropname = (char __user *)(uintptr_t)param.propname;
3466db71994STimur Tabi 	upropval = (void __user *)(uintptr_t)param.propval;
3476db71994STimur Tabi 
3486db71994STimur Tabi 	path = strndup_user(upath, FH_DTPROP_MAX_PATHLEN);
349c8ea3663SDan Carpenter 	if (IS_ERR(path))
350c8ea3663SDan Carpenter 		return PTR_ERR(path);
3516db71994STimur Tabi 
3526db71994STimur Tabi 	propname = strndup_user(upropname, FH_DTPROP_MAX_PATHLEN);
3536db71994STimur Tabi 	if (IS_ERR(propname)) {
3546db71994STimur Tabi 		ret = PTR_ERR(propname);
355c8ea3663SDan Carpenter 		goto err_free_path;
3566db71994STimur Tabi 	}
3576db71994STimur Tabi 
3586db71994STimur Tabi 	if (param.proplen > FH_DTPROP_MAX_PROPLEN) {
3596db71994STimur Tabi 		ret = -EINVAL;
360c8ea3663SDan Carpenter 		goto err_free_propname;
3616db71994STimur Tabi 	}
3626db71994STimur Tabi 
3636db71994STimur Tabi 	propval = kmalloc(param.proplen, GFP_KERNEL);
3646db71994STimur Tabi 	if (!propval) {
3656db71994STimur Tabi 		ret = -ENOMEM;
366c8ea3663SDan Carpenter 		goto err_free_propname;
3676db71994STimur Tabi 	}
3686db71994STimur Tabi 
3696db71994STimur Tabi 	if (set) {
3706db71994STimur Tabi 		if (copy_from_user(propval, upropval, param.proplen)) {
3716db71994STimur Tabi 			ret = -EFAULT;
372c8ea3663SDan Carpenter 			goto err_free_propval;
3736db71994STimur Tabi 		}
3746db71994STimur Tabi 
3756db71994STimur Tabi 		param.ret = fh_partition_set_dtprop(param.handle,
3766db71994STimur Tabi 						    virt_to_phys(path),
3776db71994STimur Tabi 						    virt_to_phys(propname),
3786db71994STimur Tabi 						    virt_to_phys(propval),
3796db71994STimur Tabi 						    param.proplen);
3806db71994STimur Tabi 	} else {
3816db71994STimur Tabi 		param.ret = fh_partition_get_dtprop(param.handle,
3826db71994STimur Tabi 						    virt_to_phys(path),
3836db71994STimur Tabi 						    virt_to_phys(propname),
3846db71994STimur Tabi 						    virt_to_phys(propval),
3856db71994STimur Tabi 						    &param.proplen);
3866db71994STimur Tabi 
3876db71994STimur Tabi 		if (param.ret == 0) {
3886db71994STimur Tabi 			if (copy_to_user(upropval, propval, param.proplen) ||
3896db71994STimur Tabi 			    put_user(param.proplen, &p->proplen)) {
3906db71994STimur Tabi 				ret = -EFAULT;
391c8ea3663SDan Carpenter 				goto err_free_propval;
3926db71994STimur Tabi 			}
3936db71994STimur Tabi 		}
3946db71994STimur Tabi 	}
3956db71994STimur Tabi 
3966db71994STimur Tabi 	if (put_user(param.ret, &p->ret))
3976db71994STimur Tabi 		ret = -EFAULT;
3986db71994STimur Tabi 
399c8ea3663SDan Carpenter err_free_propval:
4006db71994STimur Tabi 	kfree(propval);
401c8ea3663SDan Carpenter err_free_propname:
4026db71994STimur Tabi 	kfree(propname);
403c8ea3663SDan Carpenter err_free_path:
404c8ea3663SDan Carpenter 	kfree(path);
4056db71994STimur Tabi 
4066db71994STimur Tabi 	return ret;
4076db71994STimur Tabi }
4086db71994STimur Tabi 
4096db71994STimur Tabi /*
4106db71994STimur Tabi  * Ioctl main entry point
4116db71994STimur Tabi  */
fsl_hv_ioctl(struct file * file,unsigned int cmd,unsigned long argaddr)4126db71994STimur Tabi static long fsl_hv_ioctl(struct file *file, unsigned int cmd,
4136db71994STimur Tabi 			 unsigned long argaddr)
4146db71994STimur Tabi {
4156db71994STimur Tabi 	void __user *arg = (void __user *)argaddr;
4166db71994STimur Tabi 	long ret;
4176db71994STimur Tabi 
4186db71994STimur Tabi 	switch (cmd) {
4196db71994STimur Tabi 	case FSL_HV_IOCTL_PARTITION_RESTART:
4206db71994STimur Tabi 		ret = ioctl_restart(arg);
4216db71994STimur Tabi 		break;
4226db71994STimur Tabi 	case FSL_HV_IOCTL_PARTITION_GET_STATUS:
4236db71994STimur Tabi 		ret = ioctl_status(arg);
4246db71994STimur Tabi 		break;
4256db71994STimur Tabi 	case FSL_HV_IOCTL_PARTITION_START:
4266db71994STimur Tabi 		ret = ioctl_start(arg);
4276db71994STimur Tabi 		break;
4286db71994STimur Tabi 	case FSL_HV_IOCTL_PARTITION_STOP:
4296db71994STimur Tabi 		ret = ioctl_stop(arg);
4306db71994STimur Tabi 		break;
4316db71994STimur Tabi 	case FSL_HV_IOCTL_MEMCPY:
4326db71994STimur Tabi 		ret = ioctl_memcpy(arg);
4336db71994STimur Tabi 		break;
4346db71994STimur Tabi 	case FSL_HV_IOCTL_DOORBELL:
4356db71994STimur Tabi 		ret = ioctl_doorbell(arg);
4366db71994STimur Tabi 		break;
4376db71994STimur Tabi 	case FSL_HV_IOCTL_GETPROP:
4386db71994STimur Tabi 		ret = ioctl_dtprop(arg, 0);
4396db71994STimur Tabi 		break;
4406db71994STimur Tabi 	case FSL_HV_IOCTL_SETPROP:
4416db71994STimur Tabi 		ret = ioctl_dtprop(arg, 1);
4426db71994STimur Tabi 		break;
4436db71994STimur Tabi 	default:
4446db71994STimur Tabi 		pr_debug("fsl-hv: bad ioctl dir=%u type=%u cmd=%u size=%u\n",
4456db71994STimur Tabi 			 _IOC_DIR(cmd), _IOC_TYPE(cmd), _IOC_NR(cmd),
4466db71994STimur Tabi 			 _IOC_SIZE(cmd));
4476db71994STimur Tabi 		return -ENOTTY;
4486db71994STimur Tabi 	}
4496db71994STimur Tabi 
4506db71994STimur Tabi 	return ret;
4516db71994STimur Tabi }
4526db71994STimur Tabi 
4536db71994STimur Tabi /* Linked list of processes that have us open */
4546db71994STimur Tabi static struct list_head db_list;
4556db71994STimur Tabi 
4566db71994STimur Tabi /* spinlock for db_list */
4576db71994STimur Tabi static DEFINE_SPINLOCK(db_list_lock);
4586db71994STimur Tabi 
4596db71994STimur Tabi /* The size of the doorbell event queue.  This must be a power of two. */
4606db71994STimur Tabi #define QSIZE	16
4616db71994STimur Tabi 
4626db71994STimur Tabi /* Returns the next head/tail pointer, wrapping around the queue if necessary */
4636db71994STimur Tabi #define nextp(x) (((x) + 1) & (QSIZE - 1))
4646db71994STimur Tabi 
4656db71994STimur Tabi /* Per-open data structure */
4666db71994STimur Tabi struct doorbell_queue {
4676db71994STimur Tabi 	struct list_head list;
4686db71994STimur Tabi 	spinlock_t lock;
4696db71994STimur Tabi 	wait_queue_head_t wait;
4706db71994STimur Tabi 	unsigned int head;
4716db71994STimur Tabi 	unsigned int tail;
4726db71994STimur Tabi 	uint32_t q[QSIZE];
4736db71994STimur Tabi };
4746db71994STimur Tabi 
4756db71994STimur Tabi /* Linked list of ISRs that we registered */
4766db71994STimur Tabi struct list_head isr_list;
4776db71994STimur Tabi 
4786db71994STimur Tabi /* Per-ISR data structure */
4796db71994STimur Tabi struct doorbell_isr {
4806db71994STimur Tabi 	struct list_head list;
4816db71994STimur Tabi 	unsigned int irq;
4826db71994STimur Tabi 	uint32_t doorbell;	/* The doorbell handle */
4836db71994STimur Tabi 	uint32_t partition;	/* The partition handle, if used */
4846db71994STimur Tabi };
4856db71994STimur Tabi 
4866db71994STimur Tabi /*
4876db71994STimur Tabi  * Add a doorbell to all of the doorbell queues
4886db71994STimur Tabi  */
fsl_hv_queue_doorbell(uint32_t doorbell)4896db71994STimur Tabi static void fsl_hv_queue_doorbell(uint32_t doorbell)
4906db71994STimur Tabi {
4916db71994STimur Tabi 	struct doorbell_queue *dbq;
4926db71994STimur Tabi 	unsigned long flags;
4936db71994STimur Tabi 
4946db71994STimur Tabi 	/* Prevent another core from modifying db_list */
4956db71994STimur Tabi 	spin_lock_irqsave(&db_list_lock, flags);
4966db71994STimur Tabi 
4976db71994STimur Tabi 	list_for_each_entry(dbq, &db_list, list) {
4986db71994STimur Tabi 		if (dbq->head != nextp(dbq->tail)) {
4996db71994STimur Tabi 			dbq->q[dbq->tail] = doorbell;
5006db71994STimur Tabi 			/*
5016db71994STimur Tabi 			 * This memory barrier eliminates the need to grab
5026db71994STimur Tabi 			 * the spinlock for dbq.
5036db71994STimur Tabi 			 */
5046db71994STimur Tabi 			smp_wmb();
5056db71994STimur Tabi 			dbq->tail = nextp(dbq->tail);
5066db71994STimur Tabi 			wake_up_interruptible(&dbq->wait);
5076db71994STimur Tabi 		}
5086db71994STimur Tabi 	}
5096db71994STimur Tabi 
5106db71994STimur Tabi 	spin_unlock_irqrestore(&db_list_lock, flags);
5116db71994STimur Tabi }
5126db71994STimur Tabi 
5136db71994STimur Tabi /*
5146db71994STimur Tabi  * Interrupt handler for all doorbells
5156db71994STimur Tabi  *
5166db71994STimur Tabi  * We use the same interrupt handler for all doorbells.  Whenever a doorbell
5176db71994STimur Tabi  * is rung, and we receive an interrupt, we just put the handle for that
5186db71994STimur Tabi  * doorbell (passed to us as *data) into all of the queues.
5196db71994STimur Tabi  */
fsl_hv_isr(int irq,void * data)5206db71994STimur Tabi static irqreturn_t fsl_hv_isr(int irq, void *data)
5216db71994STimur Tabi {
5226db71994STimur Tabi 	fsl_hv_queue_doorbell((uintptr_t) data);
5236db71994STimur Tabi 
5246db71994STimur Tabi 	return IRQ_HANDLED;
5256db71994STimur Tabi }
5266db71994STimur Tabi 
5276db71994STimur Tabi /*
5286db71994STimur Tabi  * State change thread function
5296db71994STimur Tabi  *
5306db71994STimur Tabi  * The state change notification arrives in an interrupt, but we can't call
5316db71994STimur Tabi  * blocking_notifier_call_chain() in an interrupt handler.  We could call
5326db71994STimur Tabi  * atomic_notifier_call_chain(), but that would require the clients' call-back
5336db71994STimur Tabi  * function to run in interrupt context.  Since we don't want to impose that
5346db71994STimur Tabi  * restriction on the clients, we use a threaded IRQ to process the
5356db71994STimur Tabi  * notification in kernel context.
5366db71994STimur Tabi  */
fsl_hv_state_change_thread(int irq,void * data)5376db71994STimur Tabi static irqreturn_t fsl_hv_state_change_thread(int irq, void *data)
5386db71994STimur Tabi {
5396db71994STimur Tabi 	struct doorbell_isr *dbisr = data;
5406db71994STimur Tabi 
5416db71994STimur Tabi 	blocking_notifier_call_chain(&failover_subscribers, dbisr->partition,
5426db71994STimur Tabi 				     NULL);
5436db71994STimur Tabi 
5446db71994STimur Tabi 	return IRQ_HANDLED;
5456db71994STimur Tabi }
5466db71994STimur Tabi 
5476db71994STimur Tabi /*
5486db71994STimur Tabi  * Interrupt handler for state-change doorbells
5496db71994STimur Tabi  */
fsl_hv_state_change_isr(int irq,void * data)5506db71994STimur Tabi static irqreturn_t fsl_hv_state_change_isr(int irq, void *data)
5516db71994STimur Tabi {
5526db71994STimur Tabi 	unsigned int status;
5536db71994STimur Tabi 	struct doorbell_isr *dbisr = data;
5546db71994STimur Tabi 	int ret;
5556db71994STimur Tabi 
5566db71994STimur Tabi 	/* It's still a doorbell, so add it to all the queues. */
5576db71994STimur Tabi 	fsl_hv_queue_doorbell(dbisr->doorbell);
5586db71994STimur Tabi 
5596db71994STimur Tabi 	/* Determine the new state, and if it's stopped, notify the clients. */
5606db71994STimur Tabi 	ret = fh_partition_get_status(dbisr->partition, &status);
5616db71994STimur Tabi 	if (!ret && (status == FH_PARTITION_STOPPED))
5626db71994STimur Tabi 		return IRQ_WAKE_THREAD;
5636db71994STimur Tabi 
5646db71994STimur Tabi 	return IRQ_HANDLED;
5656db71994STimur Tabi }
5666db71994STimur Tabi 
5676db71994STimur Tabi /*
5686db71994STimur Tabi  * Returns a bitmask indicating whether a read will block
5696db71994STimur Tabi  */
fsl_hv_poll(struct file * filp,struct poll_table_struct * p)570afc9a42bSAl Viro static __poll_t fsl_hv_poll(struct file *filp, struct poll_table_struct *p)
5716db71994STimur Tabi {
5726db71994STimur Tabi 	struct doorbell_queue *dbq = filp->private_data;
5736db71994STimur Tabi 	unsigned long flags;
574afc9a42bSAl Viro 	__poll_t mask;
5756db71994STimur Tabi 
5766db71994STimur Tabi 	spin_lock_irqsave(&dbq->lock, flags);
5776db71994STimur Tabi 
5786db71994STimur Tabi 	poll_wait(filp, &dbq->wait, p);
579a9a08845SLinus Torvalds 	mask = (dbq->head == dbq->tail) ? 0 : (EPOLLIN | EPOLLRDNORM);
5806db71994STimur Tabi 
5816db71994STimur Tabi 	spin_unlock_irqrestore(&dbq->lock, flags);
5826db71994STimur Tabi 
5836db71994STimur Tabi 	return mask;
5846db71994STimur Tabi }
5856db71994STimur Tabi 
5866db71994STimur Tabi /*
5876db71994STimur Tabi  * Return the handles for any incoming doorbells
5886db71994STimur Tabi  *
5896db71994STimur Tabi  * If there are doorbell handles in the queue for this open instance, then
5906db71994STimur Tabi  * return them to the caller as an array of 32-bit integers.  Otherwise,
5916db71994STimur Tabi  * block until there is at least one handle to return.
5926db71994STimur Tabi  */
fsl_hv_read(struct file * filp,char __user * buf,size_t len,loff_t * off)5936db71994STimur Tabi static ssize_t fsl_hv_read(struct file *filp, char __user *buf, size_t len,
5946db71994STimur Tabi 			   loff_t *off)
5956db71994STimur Tabi {
5966db71994STimur Tabi 	struct doorbell_queue *dbq = filp->private_data;
5976db71994STimur Tabi 	uint32_t __user *p = (uint32_t __user *) buf; /* for put_user() */
5986db71994STimur Tabi 	unsigned long flags;
5996db71994STimur Tabi 	ssize_t count = 0;
6006db71994STimur Tabi 
6016db71994STimur Tabi 	/* Make sure we stop when the user buffer is full. */
6026db71994STimur Tabi 	while (len >= sizeof(uint32_t)) {
6036db71994STimur Tabi 		uint32_t dbell;	/* Local copy of doorbell queue data */
6046db71994STimur Tabi 
6056db71994STimur Tabi 		spin_lock_irqsave(&dbq->lock, flags);
6066db71994STimur Tabi 
6076db71994STimur Tabi 		/*
6086db71994STimur Tabi 		 * If the queue is empty, then either we're done or we need
6096db71994STimur Tabi 		 * to block.  If the application specified O_NONBLOCK, then
6106db71994STimur Tabi 		 * we return the appropriate error code.
6116db71994STimur Tabi 		 */
6126db71994STimur Tabi 		if (dbq->head == dbq->tail) {
6136db71994STimur Tabi 			spin_unlock_irqrestore(&dbq->lock, flags);
6146db71994STimur Tabi 			if (count)
6156db71994STimur Tabi 				break;
6166db71994STimur Tabi 			if (filp->f_flags & O_NONBLOCK)
6176db71994STimur Tabi 				return -EAGAIN;
6186db71994STimur Tabi 			if (wait_event_interruptible(dbq->wait,
6196db71994STimur Tabi 						     dbq->head != dbq->tail))
6206db71994STimur Tabi 				return -ERESTARTSYS;
6216db71994STimur Tabi 			continue;
6226db71994STimur Tabi 		}
6236db71994STimur Tabi 
6246db71994STimur Tabi 		/*
6256db71994STimur Tabi 		 * Even though we have an smp_wmb() in the ISR, the core
6266db71994STimur Tabi 		 * might speculatively execute the "dbell = ..." below while
6276db71994STimur Tabi 		 * it's evaluating the if-statement above.  In that case, the
6286db71994STimur Tabi 		 * value put into dbell could be stale if the core accepts the
6296db71994STimur Tabi 		 * speculation. To prevent that, we need a read memory barrier
6306db71994STimur Tabi 		 * here as well.
6316db71994STimur Tabi 		 */
6326db71994STimur Tabi 		smp_rmb();
6336db71994STimur Tabi 
6346db71994STimur Tabi 		/* Copy the data to a temporary local buffer, because
6356db71994STimur Tabi 		 * we can't call copy_to_user() from inside a spinlock
6366db71994STimur Tabi 		 */
6376db71994STimur Tabi 		dbell = dbq->q[dbq->head];
6386db71994STimur Tabi 		dbq->head = nextp(dbq->head);
6396db71994STimur Tabi 
6406db71994STimur Tabi 		spin_unlock_irqrestore(&dbq->lock, flags);
6416db71994STimur Tabi 
6426db71994STimur Tabi 		if (put_user(dbell, p))
6436db71994STimur Tabi 			return -EFAULT;
6446db71994STimur Tabi 		p++;
6456db71994STimur Tabi 		count += sizeof(uint32_t);
6466db71994STimur Tabi 		len -= sizeof(uint32_t);
6476db71994STimur Tabi 	}
6486db71994STimur Tabi 
6496db71994STimur Tabi 	return count;
6506db71994STimur Tabi }
6516db71994STimur Tabi 
6526db71994STimur Tabi /*
6536db71994STimur Tabi  * Open the driver and prepare for reading doorbells.
6546db71994STimur Tabi  *
6556db71994STimur Tabi  * Every time an application opens the driver, we create a doorbell queue
6566db71994STimur Tabi  * for that file handle.  This queue is used for any incoming doorbells.
6576db71994STimur Tabi  */
fsl_hv_open(struct inode * inode,struct file * filp)6586db71994STimur Tabi static int fsl_hv_open(struct inode *inode, struct file *filp)
6596db71994STimur Tabi {
6606db71994STimur Tabi 	struct doorbell_queue *dbq;
6616db71994STimur Tabi 	unsigned long flags;
6626db71994STimur Tabi 
6636db71994STimur Tabi 	dbq = kzalloc(sizeof(struct doorbell_queue), GFP_KERNEL);
6646db71994STimur Tabi 	if (!dbq) {
6656db71994STimur Tabi 		pr_err("fsl-hv: out of memory\n");
6666db71994STimur Tabi 		return -ENOMEM;
6676db71994STimur Tabi 	}
6686db71994STimur Tabi 
6696db71994STimur Tabi 	spin_lock_init(&dbq->lock);
6706db71994STimur Tabi 	init_waitqueue_head(&dbq->wait);
6716db71994STimur Tabi 
6726db71994STimur Tabi 	spin_lock_irqsave(&db_list_lock, flags);
6736db71994STimur Tabi 	list_add(&dbq->list, &db_list);
6746db71994STimur Tabi 	spin_unlock_irqrestore(&db_list_lock, flags);
6756db71994STimur Tabi 
6766db71994STimur Tabi 	filp->private_data = dbq;
6776db71994STimur Tabi 
6784647769bSGuo Zhengkui 	return 0;
6796db71994STimur Tabi }
6806db71994STimur Tabi 
6816db71994STimur Tabi /*
6826db71994STimur Tabi  * Close the driver
6836db71994STimur Tabi  */
fsl_hv_close(struct inode * inode,struct file * filp)6846db71994STimur Tabi static int fsl_hv_close(struct inode *inode, struct file *filp)
6856db71994STimur Tabi {
6866db71994STimur Tabi 	struct doorbell_queue *dbq = filp->private_data;
6876db71994STimur Tabi 	unsigned long flags;
6886db71994STimur Tabi 
6896db71994STimur Tabi 	spin_lock_irqsave(&db_list_lock, flags);
6906db71994STimur Tabi 	list_del(&dbq->list);
6916db71994STimur Tabi 	spin_unlock_irqrestore(&db_list_lock, flags);
6926db71994STimur Tabi 
6936db71994STimur Tabi 	kfree(dbq);
6946db71994STimur Tabi 
695b734fed0SHaowen Bai 	return 0;
6966db71994STimur Tabi }
6976db71994STimur Tabi 
6986db71994STimur Tabi static const struct file_operations fsl_hv_fops = {
6996db71994STimur Tabi 	.owner = THIS_MODULE,
7006db71994STimur Tabi 	.open = fsl_hv_open,
7016db71994STimur Tabi 	.release = fsl_hv_close,
7026db71994STimur Tabi 	.poll = fsl_hv_poll,
7036db71994STimur Tabi 	.read = fsl_hv_read,
7046db71994STimur Tabi 	.unlocked_ioctl = fsl_hv_ioctl,
7051832f2d8SArnd Bergmann 	.compat_ioctl = compat_ptr_ioctl,
7066db71994STimur Tabi };
7076db71994STimur Tabi 
7086db71994STimur Tabi static struct miscdevice fsl_hv_misc_dev = {
7096db71994STimur Tabi 	MISC_DYNAMIC_MINOR,
7106db71994STimur Tabi 	"fsl-hv",
7116db71994STimur Tabi 	&fsl_hv_fops
7126db71994STimur Tabi };
7136db71994STimur Tabi 
fsl_hv_shutdown_isr(int irq,void * data)7146db71994STimur Tabi static irqreturn_t fsl_hv_shutdown_isr(int irq, void *data)
7156db71994STimur Tabi {
7166db71994STimur Tabi 	orderly_poweroff(false);
7176db71994STimur Tabi 
7186db71994STimur Tabi 	return IRQ_HANDLED;
7196db71994STimur Tabi }
7206db71994STimur Tabi 
7216db71994STimur Tabi /*
7226db71994STimur Tabi  * Returns the handle of the parent of the given node
7236db71994STimur Tabi  *
7246db71994STimur Tabi  * The handle is the value of the 'hv-handle' property
7256db71994STimur Tabi  */
get_parent_handle(struct device_node * np)7266db71994STimur Tabi static int get_parent_handle(struct device_node *np)
7276db71994STimur Tabi {
7286db71994STimur Tabi 	struct device_node *parent;
7296db71994STimur Tabi 	const uint32_t *prop;
7306db71994STimur Tabi 	uint32_t handle;
7316db71994STimur Tabi 	int len;
7326db71994STimur Tabi 
7336db71994STimur Tabi 	parent = of_get_parent(np);
7346db71994STimur Tabi 	if (!parent)
7356db71994STimur Tabi 		/* It's not really possible for this to fail */
7366db71994STimur Tabi 		return -ENODEV;
7376db71994STimur Tabi 
7386db71994STimur Tabi 	/*
7396db71994STimur Tabi 	 * The proper name for the handle property is "hv-handle", but some
7406db71994STimur Tabi 	 * older versions of the hypervisor used "reg".
7416db71994STimur Tabi 	 */
7426db71994STimur Tabi 	prop = of_get_property(parent, "hv-handle", &len);
7436db71994STimur Tabi 	if (!prop)
7446db71994STimur Tabi 		prop = of_get_property(parent, "reg", &len);
7456db71994STimur Tabi 
7466db71994STimur Tabi 	if (!prop || (len != sizeof(uint32_t))) {
7476db71994STimur Tabi 		/* This can happen only if the node is malformed */
7486db71994STimur Tabi 		of_node_put(parent);
7496db71994STimur Tabi 		return -ENODEV;
7506db71994STimur Tabi 	}
7516db71994STimur Tabi 
7526db71994STimur Tabi 	handle = be32_to_cpup(prop);
7536db71994STimur Tabi 	of_node_put(parent);
7546db71994STimur Tabi 
7556db71994STimur Tabi 	return handle;
7566db71994STimur Tabi }
7576db71994STimur Tabi 
7586db71994STimur Tabi /*
7596db71994STimur Tabi  * Register a callback for failover events
7606db71994STimur Tabi  *
7616db71994STimur Tabi  * This function is called by device drivers to register their callback
7626db71994STimur Tabi  * functions for fail-over events.
7636db71994STimur Tabi  */
fsl_hv_failover_register(struct notifier_block * nb)7646db71994STimur Tabi int fsl_hv_failover_register(struct notifier_block *nb)
7656db71994STimur Tabi {
7666db71994STimur Tabi 	return blocking_notifier_chain_register(&failover_subscribers, nb);
7676db71994STimur Tabi }
7686db71994STimur Tabi EXPORT_SYMBOL(fsl_hv_failover_register);
7696db71994STimur Tabi 
7706db71994STimur Tabi /*
7716db71994STimur Tabi  * Unregister a callback for failover events
7726db71994STimur Tabi  */
fsl_hv_failover_unregister(struct notifier_block * nb)7736db71994STimur Tabi int fsl_hv_failover_unregister(struct notifier_block *nb)
7746db71994STimur Tabi {
7756db71994STimur Tabi 	return blocking_notifier_chain_unregister(&failover_subscribers, nb);
7766db71994STimur Tabi }
7776db71994STimur Tabi EXPORT_SYMBOL(fsl_hv_failover_unregister);
7786db71994STimur Tabi 
7796db71994STimur Tabi /*
7806db71994STimur Tabi  * Return TRUE if we're running under FSL hypervisor
7816db71994STimur Tabi  *
7826db71994STimur Tabi  * This function checks to see if we're running under the Freescale
7836db71994STimur Tabi  * hypervisor, and returns zero if we're not, or non-zero if we are.
7846db71994STimur Tabi  *
7856db71994STimur Tabi  * First, it checks if MSR[GS]==1, which means we're running under some
7866db71994STimur Tabi  * hypervisor.  Then it checks if there is a hypervisor node in the device
7876db71994STimur Tabi  * tree.  Currently, that means there needs to be a node in the root called
7886db71994STimur Tabi  * "hypervisor" and which has a property named "fsl,hv-version".
7896db71994STimur Tabi  */
has_fsl_hypervisor(void)7906db71994STimur Tabi static int has_fsl_hypervisor(void)
7916db71994STimur Tabi {
7926db71994STimur Tabi 	struct device_node *node;
7936db71994STimur Tabi 	int ret;
7946db71994STimur Tabi 
7956db71994STimur Tabi 	node = of_find_node_by_path("/hypervisor");
7966db71994STimur Tabi 	if (!node)
7976db71994STimur Tabi 		return 0;
7986db71994STimur Tabi 
799*10ce6b70SRob Herring 	ret = of_property_present(node, "fsl,hv-version");
8006db71994STimur Tabi 
8016db71994STimur Tabi 	of_node_put(node);
8026db71994STimur Tabi 
8036db71994STimur Tabi 	return ret;
8046db71994STimur Tabi }
8056db71994STimur Tabi 
8066db71994STimur Tabi /*
8076db71994STimur Tabi  * Freescale hypervisor management driver init
8086db71994STimur Tabi  *
8096db71994STimur Tabi  * This function is called when this module is loaded.
8106db71994STimur Tabi  *
8116db71994STimur Tabi  * Register ourselves as a miscellaneous driver.  This will register the
8126db71994STimur Tabi  * fops structure and create the right sysfs entries for udev.
8136db71994STimur Tabi  */
fsl_hypervisor_init(void)8146db71994STimur Tabi static int __init fsl_hypervisor_init(void)
8156db71994STimur Tabi {
8166db71994STimur Tabi 	struct device_node *np;
8176db71994STimur Tabi 	struct doorbell_isr *dbisr, *n;
8186db71994STimur Tabi 	int ret;
8196db71994STimur Tabi 
8206db71994STimur Tabi 	pr_info("Freescale hypervisor management driver\n");
8216db71994STimur Tabi 
8226db71994STimur Tabi 	if (!has_fsl_hypervisor()) {
8236db71994STimur Tabi 		pr_info("fsl-hv: no hypervisor found\n");
8246db71994STimur Tabi 		return -ENODEV;
8256db71994STimur Tabi 	}
8266db71994STimur Tabi 
8276db71994STimur Tabi 	ret = misc_register(&fsl_hv_misc_dev);
8286db71994STimur Tabi 	if (ret) {
8296db71994STimur Tabi 		pr_err("fsl-hv: cannot register device\n");
8306db71994STimur Tabi 		return ret;
8316db71994STimur Tabi 	}
8326db71994STimur Tabi 
8336db71994STimur Tabi 	INIT_LIST_HEAD(&db_list);
8346db71994STimur Tabi 	INIT_LIST_HEAD(&isr_list);
8356db71994STimur Tabi 
8366db71994STimur Tabi 	for_each_compatible_node(np, NULL, "epapr,hv-receive-doorbell") {
8376db71994STimur Tabi 		unsigned int irq;
8386db71994STimur Tabi 		const uint32_t *handle;
8396db71994STimur Tabi 
8406db71994STimur Tabi 		handle = of_get_property(np, "interrupts", NULL);
8416db71994STimur Tabi 		irq = irq_of_parse_and_map(np, 0);
84202c39bbbSChristophe Leroy 		if (!handle || !irq) {
84355e18b18SRob Herring 			pr_err("fsl-hv: no 'interrupts' property in %pOF node\n",
84455e18b18SRob Herring 				np);
8456db71994STimur Tabi 			continue;
8466db71994STimur Tabi 		}
8476db71994STimur Tabi 
8486db71994STimur Tabi 		dbisr = kzalloc(sizeof(*dbisr), GFP_KERNEL);
8496db71994STimur Tabi 		if (!dbisr)
8506db71994STimur Tabi 			goto out_of_memory;
8516db71994STimur Tabi 
8526db71994STimur Tabi 		dbisr->irq = irq;
8536db71994STimur Tabi 		dbisr->doorbell = be32_to_cpup(handle);
8546db71994STimur Tabi 
8556db71994STimur Tabi 		if (of_device_is_compatible(np, "fsl,hv-shutdown-doorbell")) {
8566db71994STimur Tabi 			/* The shutdown doorbell gets its own ISR */
8576db71994STimur Tabi 			ret = request_irq(irq, fsl_hv_shutdown_isr, 0,
8586db71994STimur Tabi 					  np->name, NULL);
8596db71994STimur Tabi 		} else if (of_device_is_compatible(np,
8606db71994STimur Tabi 			"fsl,hv-state-change-doorbell")) {
8616db71994STimur Tabi 			/*
8626db71994STimur Tabi 			 * The state change doorbell triggers a notification if
8636db71994STimur Tabi 			 * the state of the managed partition changes to
8646db71994STimur Tabi 			 * "stopped". We need a separate interrupt handler for
8656db71994STimur Tabi 			 * that, and we also need to know the handle of the
8666db71994STimur Tabi 			 * target partition, not just the handle of the
8676db71994STimur Tabi 			 * doorbell.
8686db71994STimur Tabi 			 */
8696db71994STimur Tabi 			dbisr->partition = ret = get_parent_handle(np);
8706db71994STimur Tabi 			if (ret < 0) {
87155e18b18SRob Herring 				pr_err("fsl-hv: node %pOF has missing or "
87255e18b18SRob Herring 				       "malformed parent\n", np);
8736db71994STimur Tabi 				kfree(dbisr);
8746db71994STimur Tabi 				continue;
8756db71994STimur Tabi 			}
8766db71994STimur Tabi 			ret = request_threaded_irq(irq, fsl_hv_state_change_isr,
8776db71994STimur Tabi 						   fsl_hv_state_change_thread,
8786db71994STimur Tabi 						   0, np->name, dbisr);
8796db71994STimur Tabi 		} else
8806db71994STimur Tabi 			ret = request_irq(irq, fsl_hv_isr, 0, np->name, dbisr);
8816db71994STimur Tabi 
8826db71994STimur Tabi 		if (ret < 0) {
88355e18b18SRob Herring 			pr_err("fsl-hv: could not request irq %u for node %pOF\n",
88455e18b18SRob Herring 			       irq, np);
8856db71994STimur Tabi 			kfree(dbisr);
8866db71994STimur Tabi 			continue;
8876db71994STimur Tabi 		}
8886db71994STimur Tabi 
8896db71994STimur Tabi 		list_add(&dbisr->list, &isr_list);
8906db71994STimur Tabi 
8916db71994STimur Tabi 		pr_info("fsl-hv: registered handler for doorbell %u\n",
8926db71994STimur Tabi 			dbisr->doorbell);
8936db71994STimur Tabi 	}
8946db71994STimur Tabi 
8956db71994STimur Tabi 	return 0;
8966db71994STimur Tabi 
8976db71994STimur Tabi out_of_memory:
8986db71994STimur Tabi 	list_for_each_entry_safe(dbisr, n, &isr_list, list) {
8996db71994STimur Tabi 		free_irq(dbisr->irq, dbisr);
9006db71994STimur Tabi 		list_del(&dbisr->list);
9016db71994STimur Tabi 		kfree(dbisr);
9026db71994STimur Tabi 	}
9036db71994STimur Tabi 
9046db71994STimur Tabi 	misc_deregister(&fsl_hv_misc_dev);
9056db71994STimur Tabi 
9066db71994STimur Tabi 	return -ENOMEM;
9076db71994STimur Tabi }
9086db71994STimur Tabi 
9096db71994STimur Tabi /*
9106db71994STimur Tabi  * Freescale hypervisor management driver termination
9116db71994STimur Tabi  *
9126db71994STimur Tabi  * This function is called when this driver is unloaded.
9136db71994STimur Tabi  */
fsl_hypervisor_exit(void)9146db71994STimur Tabi static void __exit fsl_hypervisor_exit(void)
9156db71994STimur Tabi {
9166db71994STimur Tabi 	struct doorbell_isr *dbisr, *n;
9176db71994STimur Tabi 
9186db71994STimur Tabi 	list_for_each_entry_safe(dbisr, n, &isr_list, list) {
9196db71994STimur Tabi 		free_irq(dbisr->irq, dbisr);
9206db71994STimur Tabi 		list_del(&dbisr->list);
9216db71994STimur Tabi 		kfree(dbisr);
9226db71994STimur Tabi 	}
9236db71994STimur Tabi 
9246db71994STimur Tabi 	misc_deregister(&fsl_hv_misc_dev);
9256db71994STimur Tabi }
9266db71994STimur Tabi 
9276db71994STimur Tabi module_init(fsl_hypervisor_init);
9286db71994STimur Tabi module_exit(fsl_hypervisor_exit);
9296db71994STimur Tabi 
9306db71994STimur Tabi MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
9316db71994STimur Tabi MODULE_DESCRIPTION("Freescale hypervisor management driver");
9326db71994STimur Tabi MODULE_LICENSE("GPL v2");
933