xref: /openbmc/linux/arch/powerpc/kvm/book3s_rtas.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28e591cb7SMichael Ellerman /*
38e591cb7SMichael Ellerman  * Copyright 2012 Michael Ellerman, IBM Corporation.
48e591cb7SMichael Ellerman  */
58e591cb7SMichael Ellerman 
68e591cb7SMichael Ellerman #include <linux/kernel.h>
78e591cb7SMichael Ellerman #include <linux/kvm_host.h>
88e591cb7SMichael Ellerman #include <linux/kvm.h>
98e591cb7SMichael Ellerman #include <linux/err.h>
108e591cb7SMichael Ellerman 
117c0f6ba6SLinus Torvalds #include <linux/uaccess.h>
128e591cb7SMichael Ellerman #include <asm/kvm_book3s.h>
138e591cb7SMichael Ellerman #include <asm/kvm_ppc.h>
148e591cb7SMichael Ellerman #include <asm/hvcall.h>
158e591cb7SMichael Ellerman #include <asm/rtas.h>
165af50993SBenjamin Herrenschmidt #include <asm/xive.h>
178e591cb7SMichael Ellerman 
18bc5ad3f3SBenjamin Herrenschmidt #ifdef CONFIG_KVM_XICS
kvm_rtas_set_xive(struct kvm_vcpu * vcpu,struct rtas_args * args)19bc5ad3f3SBenjamin Herrenschmidt static void kvm_rtas_set_xive(struct kvm_vcpu *vcpu, struct rtas_args *args)
20bc5ad3f3SBenjamin Herrenschmidt {
21bc5ad3f3SBenjamin Herrenschmidt 	u32 irq, server, priority;
22bc5ad3f3SBenjamin Herrenschmidt 	int rc;
23bc5ad3f3SBenjamin Herrenschmidt 
2419a44ecfSAlexander Graf 	if (be32_to_cpu(args->nargs) != 3 || be32_to_cpu(args->nret) != 1) {
25bc5ad3f3SBenjamin Herrenschmidt 		rc = -3;
26bc5ad3f3SBenjamin Herrenschmidt 		goto out;
27bc5ad3f3SBenjamin Herrenschmidt 	}
28bc5ad3f3SBenjamin Herrenschmidt 
2919a44ecfSAlexander Graf 	irq = be32_to_cpu(args->args[0]);
3019a44ecfSAlexander Graf 	server = be32_to_cpu(args->args[1]);
3119a44ecfSAlexander Graf 	priority = be32_to_cpu(args->args[2]);
32bc5ad3f3SBenjamin Herrenschmidt 
3303f95332SPaul Mackerras 	if (xics_on_xive())
345af50993SBenjamin Herrenschmidt 		rc = kvmppc_xive_set_xive(vcpu->kvm, irq, server, priority);
355af50993SBenjamin Herrenschmidt 	else
36bc5ad3f3SBenjamin Herrenschmidt 		rc = kvmppc_xics_set_xive(vcpu->kvm, irq, server, priority);
37bc5ad3f3SBenjamin Herrenschmidt 	if (rc)
38bc5ad3f3SBenjamin Herrenschmidt 		rc = -3;
39bc5ad3f3SBenjamin Herrenschmidt out:
4019a44ecfSAlexander Graf 	args->rets[0] = cpu_to_be32(rc);
41bc5ad3f3SBenjamin Herrenschmidt }
42bc5ad3f3SBenjamin Herrenschmidt 
kvm_rtas_get_xive(struct kvm_vcpu * vcpu,struct rtas_args * args)43bc5ad3f3SBenjamin Herrenschmidt static void kvm_rtas_get_xive(struct kvm_vcpu *vcpu, struct rtas_args *args)
44bc5ad3f3SBenjamin Herrenschmidt {
45bc5ad3f3SBenjamin Herrenschmidt 	u32 irq, server, priority;
46bc5ad3f3SBenjamin Herrenschmidt 	int rc;
47bc5ad3f3SBenjamin Herrenschmidt 
4819a44ecfSAlexander Graf 	if (be32_to_cpu(args->nargs) != 1 || be32_to_cpu(args->nret) != 3) {
49bc5ad3f3SBenjamin Herrenschmidt 		rc = -3;
50bc5ad3f3SBenjamin Herrenschmidt 		goto out;
51bc5ad3f3SBenjamin Herrenschmidt 	}
52bc5ad3f3SBenjamin Herrenschmidt 
5319a44ecfSAlexander Graf 	irq = be32_to_cpu(args->args[0]);
54bc5ad3f3SBenjamin Herrenschmidt 
55bc5ad3f3SBenjamin Herrenschmidt 	server = priority = 0;
5603f95332SPaul Mackerras 	if (xics_on_xive())
575af50993SBenjamin Herrenschmidt 		rc = kvmppc_xive_get_xive(vcpu->kvm, irq, &server, &priority);
585af50993SBenjamin Herrenschmidt 	else
59bc5ad3f3SBenjamin Herrenschmidt 		rc = kvmppc_xics_get_xive(vcpu->kvm, irq, &server, &priority);
60bc5ad3f3SBenjamin Herrenschmidt 	if (rc) {
61bc5ad3f3SBenjamin Herrenschmidt 		rc = -3;
62bc5ad3f3SBenjamin Herrenschmidt 		goto out;
63bc5ad3f3SBenjamin Herrenschmidt 	}
64bc5ad3f3SBenjamin Herrenschmidt 
6519a44ecfSAlexander Graf 	args->rets[1] = cpu_to_be32(server);
6619a44ecfSAlexander Graf 	args->rets[2] = cpu_to_be32(priority);
67bc5ad3f3SBenjamin Herrenschmidt out:
6819a44ecfSAlexander Graf 	args->rets[0] = cpu_to_be32(rc);
69bc5ad3f3SBenjamin Herrenschmidt }
70d19bd862SPaul Mackerras 
kvm_rtas_int_off(struct kvm_vcpu * vcpu,struct rtas_args * args)71d19bd862SPaul Mackerras static void kvm_rtas_int_off(struct kvm_vcpu *vcpu, struct rtas_args *args)
72d19bd862SPaul Mackerras {
73d19bd862SPaul Mackerras 	u32 irq;
74d19bd862SPaul Mackerras 	int rc;
75d19bd862SPaul Mackerras 
7619a44ecfSAlexander Graf 	if (be32_to_cpu(args->nargs) != 1 || be32_to_cpu(args->nret) != 1) {
77d19bd862SPaul Mackerras 		rc = -3;
78d19bd862SPaul Mackerras 		goto out;
79d19bd862SPaul Mackerras 	}
80d19bd862SPaul Mackerras 
8119a44ecfSAlexander Graf 	irq = be32_to_cpu(args->args[0]);
82d19bd862SPaul Mackerras 
8303f95332SPaul Mackerras 	if (xics_on_xive())
845af50993SBenjamin Herrenschmidt 		rc = kvmppc_xive_int_off(vcpu->kvm, irq);
855af50993SBenjamin Herrenschmidt 	else
86d19bd862SPaul Mackerras 		rc = kvmppc_xics_int_off(vcpu->kvm, irq);
87d19bd862SPaul Mackerras 	if (rc)
88d19bd862SPaul Mackerras 		rc = -3;
89d19bd862SPaul Mackerras out:
9019a44ecfSAlexander Graf 	args->rets[0] = cpu_to_be32(rc);
91d19bd862SPaul Mackerras }
92d19bd862SPaul Mackerras 
kvm_rtas_int_on(struct kvm_vcpu * vcpu,struct rtas_args * args)93d19bd862SPaul Mackerras static void kvm_rtas_int_on(struct kvm_vcpu *vcpu, struct rtas_args *args)
94d19bd862SPaul Mackerras {
95d19bd862SPaul Mackerras 	u32 irq;
96d19bd862SPaul Mackerras 	int rc;
97d19bd862SPaul Mackerras 
9819a44ecfSAlexander Graf 	if (be32_to_cpu(args->nargs) != 1 || be32_to_cpu(args->nret) != 1) {
99d19bd862SPaul Mackerras 		rc = -3;
100d19bd862SPaul Mackerras 		goto out;
101d19bd862SPaul Mackerras 	}
102d19bd862SPaul Mackerras 
10319a44ecfSAlexander Graf 	irq = be32_to_cpu(args->args[0]);
104d19bd862SPaul Mackerras 
10503f95332SPaul Mackerras 	if (xics_on_xive())
1065af50993SBenjamin Herrenschmidt 		rc = kvmppc_xive_int_on(vcpu->kvm, irq);
1075af50993SBenjamin Herrenschmidt 	else
108d19bd862SPaul Mackerras 		rc = kvmppc_xics_int_on(vcpu->kvm, irq);
109d19bd862SPaul Mackerras 	if (rc)
110d19bd862SPaul Mackerras 		rc = -3;
111d19bd862SPaul Mackerras out:
11219a44ecfSAlexander Graf 	args->rets[0] = cpu_to_be32(rc);
113d19bd862SPaul Mackerras }
114bc5ad3f3SBenjamin Herrenschmidt #endif /* CONFIG_KVM_XICS */
1158e591cb7SMichael Ellerman 
1168e591cb7SMichael Ellerman struct rtas_handler {
1178e591cb7SMichael Ellerman 	void (*handler)(struct kvm_vcpu *vcpu, struct rtas_args *args);
1188e591cb7SMichael Ellerman 	char *name;
1198e591cb7SMichael Ellerman };
1208e591cb7SMichael Ellerman 
121bc5ad3f3SBenjamin Herrenschmidt static struct rtas_handler rtas_handlers[] = {
122bc5ad3f3SBenjamin Herrenschmidt #ifdef CONFIG_KVM_XICS
123bc5ad3f3SBenjamin Herrenschmidt 	{ .name = "ibm,set-xive", .handler = kvm_rtas_set_xive },
124bc5ad3f3SBenjamin Herrenschmidt 	{ .name = "ibm,get-xive", .handler = kvm_rtas_get_xive },
125d19bd862SPaul Mackerras 	{ .name = "ibm,int-off",  .handler = kvm_rtas_int_off },
126d19bd862SPaul Mackerras 	{ .name = "ibm,int-on",   .handler = kvm_rtas_int_on },
127bc5ad3f3SBenjamin Herrenschmidt #endif
128bc5ad3f3SBenjamin Herrenschmidt };
1298e591cb7SMichael Ellerman 
1308e591cb7SMichael Ellerman struct rtas_token_definition {
1318e591cb7SMichael Ellerman 	struct list_head list;
1328e591cb7SMichael Ellerman 	struct rtas_handler *handler;
1338e591cb7SMichael Ellerman 	u64 token;
1348e591cb7SMichael Ellerman };
1358e591cb7SMichael Ellerman 
rtas_name_matches(char * s1,char * s2)1368e591cb7SMichael Ellerman static int rtas_name_matches(char *s1, char *s2)
1378e591cb7SMichael Ellerman {
1388e591cb7SMichael Ellerman 	struct kvm_rtas_token_args args;
1398e591cb7SMichael Ellerman 	return !strncmp(s1, s2, sizeof(args.name));
1408e591cb7SMichael Ellerman }
1418e591cb7SMichael Ellerman 
rtas_token_undefine(struct kvm * kvm,char * name)1428e591cb7SMichael Ellerman static int rtas_token_undefine(struct kvm *kvm, char *name)
1438e591cb7SMichael Ellerman {
1448e591cb7SMichael Ellerman 	struct rtas_token_definition *d, *tmp;
1458e591cb7SMichael Ellerman 
1461659e27dSPaul Mackerras 	lockdep_assert_held(&kvm->arch.rtas_token_lock);
1478e591cb7SMichael Ellerman 
1488e591cb7SMichael Ellerman 	list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) {
1498e591cb7SMichael Ellerman 		if (rtas_name_matches(d->handler->name, name)) {
1508e591cb7SMichael Ellerman 			list_del(&d->list);
1518e591cb7SMichael Ellerman 			kfree(d);
1528e591cb7SMichael Ellerman 			return 0;
1538e591cb7SMichael Ellerman 		}
1548e591cb7SMichael Ellerman 	}
1558e591cb7SMichael Ellerman 
1568e591cb7SMichael Ellerman 	/* It's not an error to undefine an undefined token */
1578e591cb7SMichael Ellerman 	return 0;
1588e591cb7SMichael Ellerman }
1598e591cb7SMichael Ellerman 
rtas_token_define(struct kvm * kvm,char * name,u64 token)1608e591cb7SMichael Ellerman static int rtas_token_define(struct kvm *kvm, char *name, u64 token)
1618e591cb7SMichael Ellerman {
1628e591cb7SMichael Ellerman 	struct rtas_token_definition *d;
1638e591cb7SMichael Ellerman 	struct rtas_handler *h = NULL;
1648e591cb7SMichael Ellerman 	bool found;
1658e591cb7SMichael Ellerman 	int i;
1668e591cb7SMichael Ellerman 
1671659e27dSPaul Mackerras 	lockdep_assert_held(&kvm->arch.rtas_token_lock);
1688e591cb7SMichael Ellerman 
1698e591cb7SMichael Ellerman 	list_for_each_entry(d, &kvm->arch.rtas_tokens, list) {
1708e591cb7SMichael Ellerman 		if (d->token == token)
1718e591cb7SMichael Ellerman 			return -EEXIST;
1728e591cb7SMichael Ellerman 	}
1738e591cb7SMichael Ellerman 
1748e591cb7SMichael Ellerman 	found = false;
1758e591cb7SMichael Ellerman 	for (i = 0; i < ARRAY_SIZE(rtas_handlers); i++) {
1768e591cb7SMichael Ellerman 		h = &rtas_handlers[i];
1778e591cb7SMichael Ellerman 		if (rtas_name_matches(h->name, name)) {
1788e591cb7SMichael Ellerman 			found = true;
1798e591cb7SMichael Ellerman 			break;
1808e591cb7SMichael Ellerman 		}
1818e591cb7SMichael Ellerman 	}
1828e591cb7SMichael Ellerman 
1838e591cb7SMichael Ellerman 	if (!found)
1848e591cb7SMichael Ellerman 		return -ENOENT;
1858e591cb7SMichael Ellerman 
1868e591cb7SMichael Ellerman 	d = kzalloc(sizeof(*d), GFP_KERNEL);
1878e591cb7SMichael Ellerman 	if (!d)
1888e591cb7SMichael Ellerman 		return -ENOMEM;
1898e591cb7SMichael Ellerman 
1908e591cb7SMichael Ellerman 	d->handler = h;
1918e591cb7SMichael Ellerman 	d->token = token;
1928e591cb7SMichael Ellerman 
1938e591cb7SMichael Ellerman 	list_add_tail(&d->list, &kvm->arch.rtas_tokens);
1948e591cb7SMichael Ellerman 
1958e591cb7SMichael Ellerman 	return 0;
1968e591cb7SMichael Ellerman }
1978e591cb7SMichael Ellerman 
kvm_vm_ioctl_rtas_define_token(struct kvm * kvm,void __user * argp)1988e591cb7SMichael Ellerman int kvm_vm_ioctl_rtas_define_token(struct kvm *kvm, void __user *argp)
1998e591cb7SMichael Ellerman {
2008e591cb7SMichael Ellerman 	struct kvm_rtas_token_args args;
2018e591cb7SMichael Ellerman 	int rc;
2028e591cb7SMichael Ellerman 
2038e591cb7SMichael Ellerman 	if (copy_from_user(&args, argp, sizeof(args)))
2048e591cb7SMichael Ellerman 		return -EFAULT;
2058e591cb7SMichael Ellerman 
2061659e27dSPaul Mackerras 	mutex_lock(&kvm->arch.rtas_token_lock);
2078e591cb7SMichael Ellerman 
2088e591cb7SMichael Ellerman 	if (args.token)
2098e591cb7SMichael Ellerman 		rc = rtas_token_define(kvm, args.name, args.token);
2108e591cb7SMichael Ellerman 	else
2118e591cb7SMichael Ellerman 		rc = rtas_token_undefine(kvm, args.name);
2128e591cb7SMichael Ellerman 
2131659e27dSPaul Mackerras 	mutex_unlock(&kvm->arch.rtas_token_lock);
2148e591cb7SMichael Ellerman 
2158e591cb7SMichael Ellerman 	return rc;
2168e591cb7SMichael Ellerman }
2178e591cb7SMichael Ellerman 
kvmppc_rtas_hcall(struct kvm_vcpu * vcpu)2188e591cb7SMichael Ellerman int kvmppc_rtas_hcall(struct kvm_vcpu *vcpu)
2198e591cb7SMichael Ellerman {
2208e591cb7SMichael Ellerman 	struct rtas_token_definition *d;
2218e591cb7SMichael Ellerman 	struct rtas_args args;
2228e591cb7SMichael Ellerman 	rtas_arg_t *orig_rets;
2238e591cb7SMichael Ellerman 	gpa_t args_phys;
2248e591cb7SMichael Ellerman 	int rc;
2258e591cb7SMichael Ellerman 
226b24f36f3SPaul Mackerras 	/*
227b24f36f3SPaul Mackerras 	 * r4 contains the guest physical address of the RTAS args
228b24f36f3SPaul Mackerras 	 * Mask off the top 4 bits since this is a guest real address
229b24f36f3SPaul Mackerras 	 */
230b24f36f3SPaul Mackerras 	args_phys = kvmppc_get_gpr(vcpu, 4) & KVM_PAM;
2318e591cb7SMichael Ellerman 
232*2031f287SSean Christopherson 	kvm_vcpu_srcu_read_lock(vcpu);
2338e591cb7SMichael Ellerman 	rc = kvm_read_guest(vcpu->kvm, args_phys, &args, sizeof(args));
234*2031f287SSean Christopherson 	kvm_vcpu_srcu_read_unlock(vcpu);
2358e591cb7SMichael Ellerman 	if (rc)
2368e591cb7SMichael Ellerman 		goto fail;
2378e591cb7SMichael Ellerman 
2388e591cb7SMichael Ellerman 	/*
2398e591cb7SMichael Ellerman 	 * args->rets is a pointer into args->args. Now that we've
2408e591cb7SMichael Ellerman 	 * copied args we need to fix it up to point into our copy,
2418e591cb7SMichael Ellerman 	 * not the guest args. We also need to save the original
2428e591cb7SMichael Ellerman 	 * value so we can restore it on the way out.
2438e591cb7SMichael Ellerman 	 */
2448e591cb7SMichael Ellerman 	orig_rets = args.rets;
245f62f3c20SNicholas Piggin 	if (be32_to_cpu(args.nargs) >= ARRAY_SIZE(args.args)) {
246f62f3c20SNicholas Piggin 		/*
247f62f3c20SNicholas Piggin 		 * Don't overflow our args array: ensure there is room for
248f62f3c20SNicholas Piggin 		 * at least rets[0] (even if the call specifies 0 nret).
249f62f3c20SNicholas Piggin 		 *
250f62f3c20SNicholas Piggin 		 * Each handler must then check for the correct nargs and nret
251f62f3c20SNicholas Piggin 		 * values, but they may always return failure in rets[0].
252f62f3c20SNicholas Piggin 		 */
253f62f3c20SNicholas Piggin 		rc = -EINVAL;
254f62f3c20SNicholas Piggin 		goto fail;
255f62f3c20SNicholas Piggin 	}
25619a44ecfSAlexander Graf 	args.rets = &args.args[be32_to_cpu(args.nargs)];
2578e591cb7SMichael Ellerman 
2581659e27dSPaul Mackerras 	mutex_lock(&vcpu->kvm->arch.rtas_token_lock);
2598e591cb7SMichael Ellerman 
2608e591cb7SMichael Ellerman 	rc = -ENOENT;
2618e591cb7SMichael Ellerman 	list_for_each_entry(d, &vcpu->kvm->arch.rtas_tokens, list) {
26219a44ecfSAlexander Graf 		if (d->token == be32_to_cpu(args.token)) {
2638e591cb7SMichael Ellerman 			d->handler->handler(vcpu, &args);
2648e591cb7SMichael Ellerman 			rc = 0;
2658e591cb7SMichael Ellerman 			break;
2668e591cb7SMichael Ellerman 		}
2678e591cb7SMichael Ellerman 	}
2688e591cb7SMichael Ellerman 
2691659e27dSPaul Mackerras 	mutex_unlock(&vcpu->kvm->arch.rtas_token_lock);
2708e591cb7SMichael Ellerman 
2718e591cb7SMichael Ellerman 	if (rc == 0) {
2728e591cb7SMichael Ellerman 		args.rets = orig_rets;
2738e591cb7SMichael Ellerman 		rc = kvm_write_guest(vcpu->kvm, args_phys, &args, sizeof(args));
2748e591cb7SMichael Ellerman 		if (rc)
2758e591cb7SMichael Ellerman 			goto fail;
2768e591cb7SMichael Ellerman 	}
2778e591cb7SMichael Ellerman 
2788e591cb7SMichael Ellerman 	return rc;
2798e591cb7SMichael Ellerman 
2808e591cb7SMichael Ellerman fail:
2818e591cb7SMichael Ellerman 	/*
2828e591cb7SMichael Ellerman 	 * We only get here if the guest has called RTAS with a bogus
283f62f3c20SNicholas Piggin 	 * args pointer or nargs/nret values that would overflow the
284f62f3c20SNicholas Piggin 	 * array. That means we can't get to the args, and so we can't
285f62f3c20SNicholas Piggin 	 * fail the RTAS call. So fail right out to userspace, which
286f62f3c20SNicholas Piggin 	 * should kill the guest.
287f62f3c20SNicholas Piggin 	 *
288f62f3c20SNicholas Piggin 	 * SLOF should actually pass the hcall return value from the
289f62f3c20SNicholas Piggin 	 * rtas handler call in r3, so enter_rtas could be modified to
290f62f3c20SNicholas Piggin 	 * return a failure indication in r3 and we could return such
291f62f3c20SNicholas Piggin 	 * errors to the guest rather than failing to host userspace.
292f62f3c20SNicholas Piggin 	 * However old guests that don't test for failure could then
293f62f3c20SNicholas Piggin 	 * continue silently after errors, so for now we won't do this.
2948e591cb7SMichael Ellerman 	 */
2958e591cb7SMichael Ellerman 	return rc;
2968e591cb7SMichael Ellerman }
2972ba9f0d8SAneesh Kumar K.V EXPORT_SYMBOL_GPL(kvmppc_rtas_hcall);
2988e591cb7SMichael Ellerman 
kvmppc_rtas_tokens_free(struct kvm * kvm)2998e591cb7SMichael Ellerman void kvmppc_rtas_tokens_free(struct kvm *kvm)
3008e591cb7SMichael Ellerman {
3018e591cb7SMichael Ellerman 	struct rtas_token_definition *d, *tmp;
3028e591cb7SMichael Ellerman 
3038e591cb7SMichael Ellerman 	list_for_each_entry_safe(d, tmp, &kvm->arch.rtas_tokens, list) {
3048e591cb7SMichael Ellerman 		list_del(&d->list);
3058e591cb7SMichael Ellerman 		kfree(d);
3068e591cb7SMichael Ellerman 	}
3078e591cb7SMichael Ellerman }
308