diff options
author | Anton Blanchard <anton@samba.org> | 2013-11-20 06:14:59 -0500 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2013-11-20 17:19:16 -0500 |
commit | e844b1eeae42dc93bf13e67812a95ee7b58be8c7 (patch) | |
tree | 7c36d37d883ed5ec6b386277c38e20d9485fdaca /arch | |
parent | b91da2d4ce896c324bdbf1a078dbbe2db9a35ef1 (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.h | 2 | ||||
-rw-r--r-- | arch/powerpc/include/asm/plpar_wrappers.h | 26 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/lpar.c | 17 | ||||
-rw-r--r-- | arch/powerpc/platforms/pseries/setup.c | 42 |
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) | |||
403 | extern long pSeries_enable_reloc_on_exc(void); | 403 | extern long pSeries_enable_reloc_on_exc(void); |
404 | extern long pSeries_disable_reloc_on_exc(void); | 404 | extern long pSeries_disable_reloc_on_exc(void); |
405 | 405 | ||
406 | extern 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 | */ | ||
297 | static 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 | */ | ||
310 | static 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 | |||
290 | static inline long plapr_set_ciabr(unsigned long ciabr) | 316 | static 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__ | ||
446 | long 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 | |||
458 | static 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 | |||
445 | static void __init pSeries_setup_arch(void) | 471 | static 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 |