Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e844b1ee authored by Anton Blanchard's avatar Anton Blanchard Committed by Benjamin Herrenschmidt
Browse files

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: default avatarAnton Blanchard <anton@samba.org>
Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
parent b91da2d4
Loading
Loading
Loading
Loading
+2 −0
Original line number Original line Diff line number Diff line
@@ -403,6 +403,8 @@ static inline unsigned long cmo_get_page_size(void)
extern long pSeries_enable_reloc_on_exc(void);
extern long pSeries_enable_reloc_on_exc(void);
extern long pSeries_disable_reloc_on_exc(void);
extern long pSeries_disable_reloc_on_exc(void);


extern long pseries_big_endian_exceptions(void);

#else
#else


#define pSeries_enable_reloc_on_exc()  do {} while (0)
#define pSeries_enable_reloc_on_exc()  do {} while (0)
+26 −0
Original line number Original line Diff line number Diff line
@@ -287,6 +287,32 @@ static inline long disable_reloc_on_exceptions(void) {
	return plpar_set_mode(0, 3, 0, 0);
	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)
static inline long plapr_set_ciabr(unsigned long ciabr)
{
{
	return plpar_set_mode(0, 1, ciabr, 0);
	return plpar_set_mode(0, 1, ciabr, 0);
+17 −0
Original line number Original line Diff line number Diff line
@@ -245,6 +245,23 @@ static void pSeries_lpar_hptab_clear(void)
					&(ptes[j].pteh), &(ptes[j].ptel));
					&(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
}
}


/*
/*
+42 −0
Original line number Original line Diff line number Diff line
@@ -442,6 +442,32 @@ static void pSeries_machine_kexec(struct kimage *image)
}
}
#endif
#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)
static void __init pSeries_setup_arch(void)
{
{
	panic_timeout = 10;
	panic_timeout = 10;
@@ -698,6 +724,22 @@ static int __init pSeries_probe(void)
	/* Now try to figure out if we are running on LPAR */
	/* Now try to figure out if we are running on LPAR */
	of_scan_flat_dt(pseries_probe_fw_features, NULL);
	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))
	if (firmware_has_feature(FW_FEATURE_LPAR))
		hpte_init_lpar();
		hpte_init_lpar();
	else
	else