Skip to content

Commit

Permalink
pseries: Add H_SET_MODE to change exception endianness
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Anton Blanchard authored and Benjamin Herrenschmidt committed Nov 20, 2013
1 parent b91da2d commit e844b1e
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 0 deletions.
2 changes: 2 additions & 0 deletions arch/powerpc/include/asm/hvcall.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
26 changes: 26 additions & 0 deletions arch/powerpc/include/asm/plpar_wrappers.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
17 changes: 17 additions & 0 deletions arch/powerpc/platforms/pseries/lpar.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

/*
Expand Down
42 changes: 42 additions & 0 deletions arch/powerpc/platforms/pseries/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit e844b1e

Please sign in to comment.