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 | |
| 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>
| -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 |
