linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
From: Alexander Graf <agraf@suse.de>
To: linuxppc-dev@lists.ozlabs.org
Cc: Dinar Valeev <dvaleev@suse.com>, Anton Blanchard <anton@samba.org>
Subject: [PATCH 3/4] powerpc: Add hack to make ppc64le work on hosts without ILE
Date: Mon, 23 Dec 2013 02:24:44 +0100	[thread overview]
Message-ID: <1387761885-39599-4-git-send-email-agraf@suse.de> (raw)
In-Reply-To: <1387761885-39599-1-git-send-email-agraf@suse.de>

Some hypervisors don't implement the H_SET_MODE hypercall that we
need to set the ILE bit in LPCR which allows us to execute interrupts
in little endian mode.

However otherwise we would be able to run on those hypervisors just
fine.

So let's be creative. This patch creates a few small helpers that
work without register clobbering that execute in big endian mode.

Every interrupt gets patched with a branch instruction as the first
instruction which jumps into their respective helper. That helper
enables MSR.LE and returns back to the original interrupt.

That way we can keep our interrupt handlers in little endian code
while the hypervisor is unaware that we need them to be in little
endian.

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 arch/powerpc/kernel/Makefile           |   3 +
 arch/powerpc/kernel/fake_ile.S         | 101 +++++++++++++++++++++++++++++++++
 arch/powerpc/kernel/vmlinux.lds.S      |  14 +++++
 arch/powerpc/platforms/pseries/setup.c |  50 ++++++++++++++++
 4 files changed, 168 insertions(+)
 create mode 100644 arch/powerpc/kernel/fake_ile.S

diff --git a/arch/powerpc/kernel/Makefile b/arch/powerpc/kernel/Makefile
index 445cb6e..31842e6 100644
--- a/arch/powerpc/kernel/Makefile
+++ b/arch/powerpc/kernel/Makefile
@@ -131,6 +131,9 @@ endif
 
 obj-$(CONFIG_EPAPR_PARAVIRT)	+= epapr_paravirt.o epapr_hcalls.o
 obj-$(CONFIG_KVM_GUEST)		+= kvm.o kvm_emul.o
+ifeq ($(CONFIG_CPU_LITTLE_ENDIAN),y)
+obj-$(CONFIG_PPC_BOOK3S_64)	+= fake_ile.o
+endif
 
 # Disable GCOV in odd or sensitive code
 GCOV_PROFILE_prom_init.o := n
diff --git a/arch/powerpc/kernel/fake_ile.S b/arch/powerpc/kernel/fake_ile.S
new file mode 100644
index 0000000..21e9bd7
--- /dev/null
+++ b/arch/powerpc/kernel/fake_ile.S
@@ -0,0 +1,101 @@
+/*
+ * PowerPC helpers for hypervisors without ILE implementation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ * Copyright SUSE Linux Products GmbH 2013
+ *
+ * Authors: Alexander Graf <agraf@suse.de>
+ */
+
+#include <asm/reg.h>
+#include <asm/ppc_asm.h>
+#include <asm/asm-offsets.h>
+#include <asm/exception-64s.h>
+
+/* Little Endian fixups for hosts that don't support Little Endian */
+
+#define FAKE_ILE_HANDLER(handler, area) 			 \
+								 \
+/* This runs in BE mode */					 \
+fake_ile_##handler:						 \
+	.section __be_patch,"a"					;\
+	.llong fake_ile_##handler				;\
+	.previous						;\
+	SET_SCRATCH0(r13)					;\
+	GET_PACA(r13)						;\
+	std     r9, area + EX_R9(r13)				;\
+	std     r10, area + EX_R10(r13)				;\
+	mfsrr0	r9						;\
+	mfsrr1	r10						;\
+	std	r9, area + EX_SRR0(r13)				;\
+	std	r10, area + EX_R11(r13)				;\
+	mflr	r9						;\
+	bl	1f						;\
+	1:							;\
+	mflr	r10						;\
+	mtlr	r9						;\
+	addi	r9, r10, back_to_interrupt_##handler - 1b	;\
+	mfmsr	r10						;\
+	ori	r10, r10, MSR_LE				;\
+	mtsrr0	r9						;\
+	mtsrr1	r10						;\
+	ld	r9, area + EX_SRR0(r13)				;\
+	ld	r10, area + EX_R11(r13)				;\
+	RFI							;\
+	end_fake_ile_##handler:					;\
+	.section __be_patch,"a"					;\
+	.llong end_fake_ile_##handler				;\
+	.previous						;\
+								;\
+/* This runs in LE mode */					 \
+back_to_interrupt_##handler:					;\
+	mtsrr0	r9						;\
+	mtsrr1	r10						;\
+	li	r9, area + EX_R9				;\
+	li	r10, area + EX_R10				;\
+	ldbrx	r9, r13, r9					;\
+	ldbrx	r10, r13, r10					;\
+	GET_SCRATCH0(r13)					;\
+	/* This becomes the instruction we patched away */	 \
+	patched_insn_##handler:					;\
+	.long 0							;\
+	b 	handler + 4					;\
+								 \
+	.section __fake_ile,"a"					;\
+	.llong handler						;\
+	.llong patched_insn_##handler				;\
+	.llong fake_ile_##handler				;\
+	.previous						;\
+
+FAKE_ILE_HANDLER(system_reset_pSeries, PACA_EXMC)
+FAKE_ILE_HANDLER(machine_check_pSeries_1, PACA_EXMC)
+FAKE_ILE_HANDLER(data_access_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(data_access_slb_pSeries, PACA_EXSLB)
+FAKE_ILE_HANDLER(instruction_access_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(instruction_access_slb_pSeries, PACA_EXSLB)
+FAKE_ILE_HANDLER(hardware_interrupt_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(alignment_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(program_check_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(fp_unavailable_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(decrementer_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(doorbell_super_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(trap_0b_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(system_call_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(performance_monitor_pseries_trampoline, PACA_EXGEN)
+FAKE_ILE_HANDLER(altivec_unavailable_pseries_trampoline, PACA_EXGEN)
+FAKE_ILE_HANDLER(vsx_unavailable_pseries_trampoline, PACA_EXGEN)
+FAKE_ILE_HANDLER(facility_unavailable_trampoline, PACA_EXGEN)
+FAKE_ILE_HANDLER(instruction_breakpoint_pSeries, PACA_EXGEN)
+FAKE_ILE_HANDLER(altivec_assist_pSeries, PACA_EXGEN)
diff --git a/arch/powerpc/kernel/vmlinux.lds.S b/arch/powerpc/kernel/vmlinux.lds.S
index f096e72..7fdb7c9 100644
--- a/arch/powerpc/kernel/vmlinux.lds.S
+++ b/arch/powerpc/kernel/vmlinux.lds.S
@@ -147,6 +147,20 @@ SECTIONS
 		*(__fw_ftr_fixup)
 		__stop___fw_ftr_fixup = .;
 	}
+
+	. = ALIGN(8);
+	__fake_ile : AT(ADDR(__fake_ile) - LOAD_OFFSET) {
+		__start___fake_ile = .;
+		*(__fake_ile)
+		__stop___fake_ile = .;
+	}
+
+	. = ALIGN(8);
+	__be_patch : AT(ADDR(__be_patch) - LOAD_OFFSET) {
+		__start___be_patch = .;
+		*(__be_patch)
+		__stop___be_patch = .;
+	}
 #endif
 	.init.ramfs : AT(ADDR(.init.ramfs) - LOAD_OFFSET) {
 		INIT_RAM_FS
diff --git a/arch/powerpc/platforms/pseries/setup.c b/arch/powerpc/platforms/pseries/setup.c
index c1f1908..fb5d98c 100644
--- a/arch/powerpc/platforms/pseries/setup.c
+++ b/arch/powerpc/platforms/pseries/setup.c
@@ -67,6 +67,9 @@
 #include <asm/eeh.h>
 #include <asm/reg.h>
 #include <asm/plpar_wrappers.h>
+#include <asm/cacheflush.h>
+#include <asm/cputable.h>
+#include <asm/code-patching.h>
 
 #include "pseries.h"
 
@@ -455,6 +458,40 @@ long pseries_big_endian_exceptions(void)
 	}
 }
 
+static void swizzle_endian(u32 *start, u32 *end)
+{
+	for (; (long)start < (long)end; start++)
+		patch_instruction(start, swab32(*start));
+}
+
+static void fixup_missing_little_endian_exceptions(void)
+{
+	extern u32 *__start___fake_ile, *__stop___fake_ile;
+	extern u32 *__start___be_patch, *__stop___be_patch;
+	u32 **be_table = &__start___be_patch;
+	u32 **fake_table = &__start___fake_ile;
+
+	/* Make our big endian code look like big endian code */
+	for (; (long)be_table < (long)&__stop___be_patch; be_table += 2)
+		swizzle_endian(be_table[0], be_table[1]);
+
+	/* Now patch the interrupt handlers to branch to our BE code */
+	for (; (long)fake_table < (long)&__stop___fake_ile; fake_table += 3) {
+		u32 *le_handler = fake_table[0];
+		u32 *patched_insn = fake_table[1];
+		u32 *be_handler = fake_table[2];
+		u32 le_be_diff = (long)be_handler - (long)le_handler;
+		patch_instruction(patched_insn, *le_handler);
+		/* This patches the interrupt handler's first instruction into
+		   a branch that jumps to our BE handler that enables MSR_LE */
+		patch_instruction(le_handler, swab32(0x48000000 | le_be_diff));
+		/* Make sure that feature fixups use the new address for its
+		   code patching */
+		relocate_fixup_entry(&__start___ftr_fixup, &__stop___ftr_fixup,
+				     le_handler, patched_insn);
+	}
+}
+
 static long pseries_little_endian_exceptions(void)
 {
 	long rc;
@@ -737,6 +774,19 @@ static int __init pSeries_probe(void)
 			ppc_md.progress("H_SET_MODE LE exception fail", 0);
 			panic("Could not enable little endian exceptions");
 		}
+	} else {
+		/*
+		 * The hypervisor we're running on does not know how to
+		 * configure us to run interrupts in little endian mode,
+		 * so we have to cheat a bit.
+		 *
+		 * This call reprograms all interrupt handlers' first
+		 * instruction into a branch to a big endian fixup section
+		 * which only transitions us into little endian mode, then
+		 * returns back to the normal little endian interrupt
+		 * handler.
+		 */
+		fixup_missing_little_endian_exceptions();
 	}
 #endif
 
-- 
1.8.1.4

  parent reply	other threads:[~2013-12-23  1:24 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-12-23  1:24 [PATCH 0/4] powerpc: Enable ILE on pSeries without H_MODE_SET Alexander Graf
2013-12-23  1:24 ` [PATCH 1/4] powerpc: Add global exports for all interrupt vectors Alexander Graf
2013-12-23  1:24 ` [PATCH 2/4] powerpc: Add relocation code for fixups Alexander Graf
2013-12-23  1:24 ` Alexander Graf [this message]
2013-12-23  1:24 ` [PATCH 4/4] powerpc: Don't return to BE mode when we are already there Alexander Graf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1387761885-39599-4-git-send-email-agraf@suse.de \
    --to=agraf@suse.de \
    --cc=anton@samba.org \
    --cc=dvaleev@suse.com \
    --cc=linuxppc-dev@lists.ozlabs.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).