aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorCyril Bur <cyrilbur@gmail.com>2015-01-20 21:32:00 -0500
committerMichael Ellerman <mpe@ellerman.id.au>2015-01-26 22:03:53 -0500
commit3df76a9dcc74d5f012b94ea01ed6e7aaf8362c5a (patch)
tree58da3423241a01bbabef188d0dd4462517b0982f
parent521adf5357105f6f750fbe7bca958fab3b19df2e (diff)
powerpc/pseries: Fix endian problems with LE migration
RTAS events require arguments be passed in big endian while hypercalls have their arguments passed in registers and the values should therefore be in CPU endian. The "ibm,suspend_me" 'RTAS' call makes a sequence of hypercalls to setup one true RTAS call. This means that "ibm,suspend_me" is handled specially in the ppc_rtas() syscall. The ppc_rtas() syscall has its arguments in big endian and can therefore pass these arguments directly to the RTAS call. "ibm,suspend_me" is handled specially from within ppc_rtas() (by calling rtas_ibm_suspend_me()) which has left an endian bug on little endian systems due to the requirement of hypercalls. The return value from rtas_ibm_suspend_me() gets returned in cpu endian, and is left unconverted, also a bug on little endian systems. rtas_ibm_suspend_me() does not actually make use of the rtas_args that it is passed. This patch removes the convoluted use of the rtas_args struct to pass params to rtas_ibm_suspend_me() in favour of passing what it needs as actual arguments. This patch also ensures the two callers of rtas_ibm_suspend_me() pass function parameters in cpu endian and in the case of ppc_rtas(), converts the return value. migrate_store() (the other caller of rtas_ibm_suspend_me()) is from a sysfs file which deals with everything in cpu endian so this function only underwent cleanup. This patch has been tested with KVM both LE and BE and on PowerVM both LE and BE. Under QEMU/KVM the migration happens without touching these code pathes. For PowerVM there is no obvious regression on BE and the LE code path now provides the correct parameters to the hypervisor. Signed-off-by: Cyril Bur <cyrilbur@gmail.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
-rw-r--r--arch/powerpc/include/asm/rtas.h2
-rw-r--r--arch/powerpc/kernel/rtas.c22
-rw-r--r--arch/powerpc/platforms/pseries/mobility.c22
3 files changed, 22 insertions, 24 deletions
diff --git a/arch/powerpc/include/asm/rtas.h b/arch/powerpc/include/asm/rtas.h
index b390f55b0df1..2e23e92a4372 100644
--- a/arch/powerpc/include/asm/rtas.h
+++ b/arch/powerpc/include/asm/rtas.h
@@ -327,7 +327,7 @@ extern int rtas_suspend_cpu(struct rtas_suspend_me_data *data);
327extern int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data); 327extern int rtas_suspend_last_cpu(struct rtas_suspend_me_data *data);
328extern int rtas_online_cpus_mask(cpumask_var_t cpus); 328extern int rtas_online_cpus_mask(cpumask_var_t cpus);
329extern int rtas_offline_cpus_mask(cpumask_var_t cpus); 329extern int rtas_offline_cpus_mask(cpumask_var_t cpus);
330extern int rtas_ibm_suspend_me(struct rtas_args *); 330extern int rtas_ibm_suspend_me(u64 handle, int *vasi_return);
331 331
332struct rtc_time; 332struct rtc_time;
333extern unsigned long rtas_get_boot_time(void); 333extern unsigned long rtas_get_boot_time(void);
diff --git a/arch/powerpc/kernel/rtas.c b/arch/powerpc/kernel/rtas.c
index 4af905e81ab0..21c45a2d0706 100644
--- a/arch/powerpc/kernel/rtas.c
+++ b/arch/powerpc/kernel/rtas.c
@@ -897,7 +897,7 @@ int rtas_offline_cpus_mask(cpumask_var_t cpus)
897} 897}
898EXPORT_SYMBOL(rtas_offline_cpus_mask); 898EXPORT_SYMBOL(rtas_offline_cpus_mask);
899 899
900int rtas_ibm_suspend_me(struct rtas_args *args) 900int rtas_ibm_suspend_me(u64 handle, int *vasi_return)
901{ 901{
902 long state; 902 long state;
903 long rc; 903 long rc;
@@ -911,8 +911,7 @@ int rtas_ibm_suspend_me(struct rtas_args *args)
911 return -ENOSYS; 911 return -ENOSYS;
912 912
913 /* Make sure the state is valid */ 913 /* Make sure the state is valid */
914 rc = plpar_hcall(H_VASI_STATE, retbuf, 914 rc = plpar_hcall(H_VASI_STATE, retbuf, handle);
915 ((u64)args->args[0] << 32) | args->args[1]);
916 915
917 state = retbuf[0]; 916 state = retbuf[0];
918 917
@@ -920,12 +919,12 @@ int rtas_ibm_suspend_me(struct rtas_args *args)
920 printk(KERN_ERR "rtas_ibm_suspend_me: vasi_state returned %ld\n",rc); 919 printk(KERN_ERR "rtas_ibm_suspend_me: vasi_state returned %ld\n",rc);
921 return rc; 920 return rc;
922 } else if (state == H_VASI_ENABLED) { 921 } else if (state == H_VASI_ENABLED) {
923 args->args[args->nargs] = RTAS_NOT_SUSPENDABLE; 922 *vasi_return = RTAS_NOT_SUSPENDABLE;
924 return 0; 923 return 0;
925 } else if (state != H_VASI_SUSPENDING) { 924 } else if (state != H_VASI_SUSPENDING) {
926 printk(KERN_ERR "rtas_ibm_suspend_me: vasi_state returned state %ld\n", 925 printk(KERN_ERR "rtas_ibm_suspend_me: vasi_state returned state %ld\n",
927 state); 926 state);
928 args->args[args->nargs] = -1; 927 *vasi_return = -1;
929 return 0; 928 return 0;
930 } 929 }
931 930
@@ -973,7 +972,7 @@ out:
973 return atomic_read(&data.error); 972 return atomic_read(&data.error);
974} 973}
975#else /* CONFIG_PPC_PSERIES */ 974#else /* CONFIG_PPC_PSERIES */
976int rtas_ibm_suspend_me(struct rtas_args *args) 975int rtas_ibm_suspend_me(u64 handle, int *vasi_return)
977{ 976{
978 return -ENOSYS; 977 return -ENOSYS;
979} 978}
@@ -1053,7 +1052,16 @@ asmlinkage int ppc_rtas(struct rtas_args __user *uargs)
1053 1052
1054 /* Need to handle ibm,suspend_me call specially */ 1053 /* Need to handle ibm,suspend_me call specially */
1055 if (token == ibm_suspend_me_token) { 1054 if (token == ibm_suspend_me_token) {
1056 rc = rtas_ibm_suspend_me(&args); 1055
1056 /*
1057 * rtas_ibm_suspend_me assumes args are in cpu endian, or at least the
1058 * hcall within it requires it.
1059 */
1060 int vasi_rc = 0;
1061 u64 handle = ((u64)be32_to_cpu(args.args[0]) << 32)
1062 | be32_to_cpu(args.args[1]);
1063 rc = rtas_ibm_suspend_me(handle, &vasi_rc);
1064 args.rets[0] = cpu_to_be32(vasi_rc);
1057 if (rc) 1065 if (rc)
1058 return rc; 1066 return rc;
1059 goto copy_return; 1067 goto copy_return;
diff --git a/arch/powerpc/platforms/pseries/mobility.c b/arch/powerpc/platforms/pseries/mobility.c
index e7cb6d4a871a..90cf3dcbd9f2 100644
--- a/arch/powerpc/platforms/pseries/mobility.c
+++ b/arch/powerpc/platforms/pseries/mobility.c
@@ -316,34 +316,24 @@ void post_mobility_fixup(void)
316static ssize_t migrate_store(struct class *class, struct class_attribute *attr, 316static ssize_t migrate_store(struct class *class, struct class_attribute *attr,
317 const char *buf, size_t count) 317 const char *buf, size_t count)
318{ 318{
319 struct rtas_args args;
320 u64 streamid; 319 u64 streamid;
321 int rc; 320 int rc;
321 int vasi_rc = 0;
322 322
323 rc = kstrtou64(buf, 0, &streamid); 323 rc = kstrtou64(buf, 0, &streamid);
324 if (rc) 324 if (rc)
325 return rc; 325 return rc;
326 326
327 memset(&args, 0, sizeof(args));
328 args.token = rtas_token("ibm,suspend-me");
329 args.nargs = 2;
330 args.nret = 1;
331
332 args.args[0] = streamid >> 32 ;
333 args.args[1] = streamid & 0xffffffff;
334 args.rets = &args.args[args.nargs];
335
336 do { 327 do {
337 args.rets[0] = 0; 328 rc = rtas_ibm_suspend_me(streamid, &vasi_rc);
338 rc = rtas_ibm_suspend_me(&args); 329 if (!rc && vasi_rc == RTAS_NOT_SUSPENDABLE)
339 if (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE)
340 ssleep(1); 330 ssleep(1);
341 } while (!rc && args.rets[0] == RTAS_NOT_SUSPENDABLE); 331 } while (!rc && vasi_rc == RTAS_NOT_SUSPENDABLE);
342 332
343 if (rc) 333 if (rc)
344 return rc; 334 return rc;
345 else if (args.rets[0]) 335 if (vasi_rc)
346 return args.rets[0]; 336 return vasi_rc;
347 337
348 post_mobility_fixup(); 338 post_mobility_fixup();
349 return count; 339 return count;