aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorAnton Blanchard <anton@samba.org>2013-11-20 06:14:59 -0500
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2013-11-20 17:19:16 -0500
commite844b1eeae42dc93bf13e67812a95ee7b58be8c7 (patch)
tree7c36d37d883ed5ec6b386277c38e20d9485fdaca /arch
parentb91da2d4ce896c324bdbf1a078dbbe2db9a35ef1 (diff)
pseries: Add H_SET_MODE to change exception endianness
On little endian builds call H_SET_MODE so exceptions have the correct endianness. We need to reset the endian during kexec so do that in the MMU hashtable clear callback. Signed-off-by: Anton Blanchard <anton@samba.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'arch')
-rw-r--r--arch/powerpc/include/asm/hvcall.h2
-rw-r--r--arch/powerpc/include/asm/plpar_wrappers.h26
-rw-r--r--arch/powerpc/platforms/pseries/lpar.c17
-rw-r--r--arch/powerpc/platforms/pseries/setup.c42
4 files changed, 87 insertions, 0 deletions
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index 0c7f2bfcf134..d8b600b3f058 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -403,6 +403,8 @@ static inline unsigned long cmo_get_page_size(void)
403extern long pSeries_enable_reloc_on_exc(void); 403extern long pSeries_enable_reloc_on_exc(void);
404extern long pSeries_disable_reloc_on_exc(void); 404extern long pSeries_disable_reloc_on_exc(void);
405 405
406extern long pseries_big_endian_exceptions(void);
407
406#else 408#else
407 409
408#define pSeries_enable_reloc_on_exc() do {} while (0) 410#define pSeries_enable_reloc_on_exc() do {} while (0)
diff --git a/arch/powerpc/include/asm/plpar_wrappers.h b/arch/powerpc/include/asm/plpar_wrappers.h
index a63b045e707c..12c32c5f533d 100644
--- a/arch/powerpc/include/asm/plpar_wrappers.h
+++ b/arch/powerpc/include/asm/plpar_wrappers.h
@@ -287,6 +287,32 @@ static inline long disable_reloc_on_exceptions(void) {
287 return plpar_set_mode(0, 3, 0, 0); 287 return plpar_set_mode(0, 3, 0, 0);
288} 288}
289 289
290/*
291 * Take exceptions in big endian mode on this partition
292 *
293 * Note: this call has a partition wide scope and can take a while to complete.
294 * If it returns H_LONG_BUSY_* it should be retried periodically until it
295 * returns H_SUCCESS.
296 */
297static inline long enable_big_endian_exceptions(void)
298{
299 /* mflags = 0: big endian exceptions */
300 return plpar_set_mode(0, 4, 0, 0);
301}
302
303/*
304 * Take exceptions in little endian mode on this partition
305 *
306 * Note: this call has a partition wide scope and can take a while to complete.
307 * If it returns H_LONG_BUSY_* it should be retried periodically until it
308 * returns H_SUCCESS.
309 */
310static inline long enable_little_endian_exceptions(void)
311{
312 /* mflags = 1: little endian exceptions */
313 return plpar_set_mode(1, 4, 0, 0);
314}
315
290static inline long plapr_set_ciabr(unsigned long ciabr) 316static inline long plapr_set_ciabr(unsigned long ciabr)
291{ 317{
292 return plpar_set_mode(0, 1, ciabr, 0); 318 return plpar_set_mode(0, 1, ciabr, 0);
diff --git a/arch/powerpc/platforms/pseries/lpar.c b/arch/powerpc/platforms/pseries/lpar.c
index 356bc75ca74f..4fca3def9db9 100644
--- a/arch/powerpc/platforms/pseries/lpar.c
+++ b/arch/powerpc/platforms/pseries/lpar.c
@@ -245,6 +245,23 @@ static void pSeries_lpar_hptab_clear(void)
245 &(ptes[j].pteh), &(ptes[j].ptel)); 245 &(ptes[j].pteh), &(ptes[j].ptel));
246 } 246 }
247 } 247 }
248
249#ifdef __LITTLE_ENDIAN__
250 /* Reset exceptions to big endian */
251 if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
252 long rc;
253
254 rc = pseries_big_endian_exceptions();
255 /*
256 * At this point it is unlikely panic() will get anything
257 * out to the user, but at least this will stop us from
258 * continuing on further and creating an even more
259 * difficult to debug situation.
260 */
261 if (rc)
262 panic("Could not enable big endian exceptions");
263 }
264#endif
248} 265}
249 266
250/* 267/*
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index 1f97e2b87a62..c1f190858701 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -442,6 +442,32 @@ static void pSeries_machine_kexec(struct kimage *image)
442} 442}
443#endif 443#endif
444 444
445#ifdef __LITTLE_ENDIAN__
446long pseries_big_endian_exceptions(void)
447{
448 long rc;
449
450 while (1) {
451 rc = enable_big_endian_exceptions();
452 if (!H_IS_LONG_BUSY(rc))
453 return rc;
454 mdelay(get_longbusy_msecs(rc));
455 }
456}
457
458static long pseries_little_endian_exceptions(void)
459{
460 long rc;
461
462 while (1) {
463 rc = enable_little_endian_exceptions();
464 if (!H_IS_LONG_BUSY(rc))
465 return rc;
466 mdelay(get_longbusy_msecs(rc));
467 }
468}
469#endif
470
445static void __init pSeries_setup_arch(void) 471static void __init pSeries_setup_arch(void)
446{ 472{
447 panic_timeout = 10; 473 panic_timeout = 10;
@@ -698,6 +724,22 @@ static int __init pSeries_probe(void)
698 /* Now try to figure out if we are running on LPAR */ 724 /* Now try to figure out if we are running on LPAR */
699 of_scan_flat_dt(pseries_probe_fw_features, NULL); 725 of_scan_flat_dt(pseries_probe_fw_features, NULL);
700 726
727#ifdef __LITTLE_ENDIAN__
728 if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
729 long rc;
730 /*
731 * Tell the hypervisor that we want our exceptions to
732 * be taken in little endian mode. If this fails we don't
733 * want to use BUG() because it will trigger an exception.
734 */
735 rc = pseries_little_endian_exceptions();
736 if (rc) {
737 ppc_md.progress("H_SET_MODE LE exception fail", 0);
738 panic("Could not enable little endian exceptions");
739 }
740 }
741#endif
742
701 if (firmware_has_feature(FW_FEATURE_LPAR)) 743 if (firmware_has_feature(FW_FEATURE_LPAR))
702 hpte_init_lpar(); 744 hpte_init_lpar();
703 else 745 else