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>
This commit is contained in:
parent
b91da2d4ce
commit
e844b1eeae
|
@ -403,6 +403,8 @@ static inline unsigned long cmo_get_page_size(void)
|
|||
extern long pSeries_enable_reloc_on_exc(void);
|
||||
extern long pSeries_disable_reloc_on_exc(void);
|
||||
|
||||
extern long pseries_big_endian_exceptions(void);
|
||||
|
||||
#else
|
||||
|
||||
#define pSeries_enable_reloc_on_exc() do {} while (0)
|
||||
|
|
|
@ -287,6 +287,32 @@ static inline long disable_reloc_on_exceptions(void) {
|
|||
return plpar_set_mode(0, 3, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Take exceptions in big endian mode on this partition
|
||||
*
|
||||
* Note: this call has a partition wide scope and can take a while to complete.
|
||||
* If it returns H_LONG_BUSY_* it should be retried periodically until it
|
||||
* returns H_SUCCESS.
|
||||
*/
|
||||
static inline long enable_big_endian_exceptions(void)
|
||||
{
|
||||
/* mflags = 0: big endian exceptions */
|
||||
return plpar_set_mode(0, 4, 0, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Take exceptions in little endian mode on this partition
|
||||
*
|
||||
* Note: this call has a partition wide scope and can take a while to complete.
|
||||
* If it returns H_LONG_BUSY_* it should be retried periodically until it
|
||||
* returns H_SUCCESS.
|
||||
*/
|
||||
static inline long enable_little_endian_exceptions(void)
|
||||
{
|
||||
/* mflags = 1: little endian exceptions */
|
||||
return plpar_set_mode(1, 4, 0, 0);
|
||||
}
|
||||
|
||||
static inline long plapr_set_ciabr(unsigned long ciabr)
|
||||
{
|
||||
return plpar_set_mode(0, 1, ciabr, 0);
|
||||
|
|
|
@ -245,6 +245,23 @@ static void pSeries_lpar_hptab_clear(void)
|
|||
&(ptes[j].pteh), &(ptes[j].ptel));
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
/* Reset exceptions to big endian */
|
||||
if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
|
||||
long rc;
|
||||
|
||||
rc = pseries_big_endian_exceptions();
|
||||
/*
|
||||
* At this point it is unlikely panic() will get anything
|
||||
* out to the user, but at least this will stop us from
|
||||
* continuing on further and creating an even more
|
||||
* difficult to debug situation.
|
||||
*/
|
||||
if (rc)
|
||||
panic("Could not enable big endian exceptions");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -442,6 +442,32 @@ static void pSeries_machine_kexec(struct kimage *image)
|
|||
}
|
||||
#endif
|
||||
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
long pseries_big_endian_exceptions(void)
|
||||
{
|
||||
long rc;
|
||||
|
||||
while (1) {
|
||||
rc = enable_big_endian_exceptions();
|
||||
if (!H_IS_LONG_BUSY(rc))
|
||||
return rc;
|
||||
mdelay(get_longbusy_msecs(rc));
|
||||
}
|
||||
}
|
||||
|
||||
static long pseries_little_endian_exceptions(void)
|
||||
{
|
||||
long rc;
|
||||
|
||||
while (1) {
|
||||
rc = enable_little_endian_exceptions();
|
||||
if (!H_IS_LONG_BUSY(rc))
|
||||
return rc;
|
||||
mdelay(get_longbusy_msecs(rc));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void __init pSeries_setup_arch(void)
|
||||
{
|
||||
panic_timeout = 10;
|
||||
|
@ -698,6 +724,22 @@ static int __init pSeries_probe(void)
|
|||
/* Now try to figure out if we are running on LPAR */
|
||||
of_scan_flat_dt(pseries_probe_fw_features, NULL);
|
||||
|
||||
#ifdef __LITTLE_ENDIAN__
|
||||
if (firmware_has_feature(FW_FEATURE_SET_MODE)) {
|
||||
long rc;
|
||||
/*
|
||||
* Tell the hypervisor that we want our exceptions to
|
||||
* be taken in little endian mode. If this fails we don't
|
||||
* want to use BUG() because it will trigger an exception.
|
||||
*/
|
||||
rc = pseries_little_endian_exceptions();
|
||||
if (rc) {
|
||||
ppc_md.progress("H_SET_MODE LE exception fail", 0);
|
||||
panic("Could not enable little endian exceptions");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (firmware_has_feature(FW_FEATURE_LPAR))
|
||||
hpte_init_lpar();
|
||||
else
|
||||
|
|
Loading…
Reference in a new issue