diff mbox series

[v2,7/7] libpdbg/p8chip.c: Emulate sreset using ramming for active threads

Message ID 20190312014920.25368-8-npiggin@gmail.com
State Accepted
Headers show
Series sreset support for P8 systems | expand

Checks

Context Check Description
snowpatch_ozlabs/build-multiarch fail Test build-multiarch on branch master
snowpatch_ozlabs/apply_patch success Successfully applied on branch master (deb577949a3505064f471e7b7c692e37c38ec8a4)

Commit Message

Nicholas Piggin March 12, 2019, 1:49 a.m. UTC
Based on patch from Alistair, some fixes and changes:
- account HILE bit, set/clear MSR_LE
- clear MSR_PR
- don't use raw ramming (clearer this way, not perf critical)

At the moment, must manually stop all threads in the core, and manually
restart them. Can change behaviour depending on what exactly we want
(e.g., sreset all threads may be good for debugging).
---
 libpdbg/p8chip.c | 131 +++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 128 insertions(+), 3 deletions(-)

Comments

Stewart Smith March 26, 2019, 5:38 a.m. UTC | #1
Nicholas Piggin <npiggin@gmail.com> writes:
> Based on patch from Alistair, some fixes and changes:
> - account HILE bit, set/clear MSR_LE
> - clear MSR_PR
> - don't use raw ramming (clearer this way, not perf critical)
>
> At the moment, must manually stop all threads in the core, and manually
> restart them. Can change behaviour depending on what exactly we want
> (e.g., sreset all threads may be good for debugging).
> ---
>  libpdbg/p8chip.c | 131 +++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 128 insertions(+), 3 deletions(-)

I've decently hammered this with a WIP op-test patch that does a lot of
enter/exit xmon by using pdbg to inject an NMI.

Well, decently hammered enough to find some good skiboot bugs. I'm not
quite convinced as to where one bug is (the symptom is we start dumping
a whole bunch of memory out to the console), but I'm sure this is better
than what we had before.

Tested-by: Stewart Smith <stewart@linux.ibm.com>
diff mbox series

Patch

diff --git a/libpdbg/p8chip.c b/libpdbg/p8chip.c
index cd4a50b..ff21d6b 100644
--- a/libpdbg/p8chip.c
+++ b/libpdbg/p8chip.c
@@ -29,6 +29,7 @@ 
 #define RAS_STATUS_TIMEOUT	100
 
 #define DIRECT_CONTROLS_REG    		0x0
+#define  DIRECT_CONTROL_SP_SRESET	PPC_BIT(60)
 #define  DIRECT_CONTROL_SP_STEP		PPC_BIT(61)
 #define  DIRECT_CONTROL_SP_START 	PPC_BIT(62)
 #define  DIRECT_CONTROL_SP_STOP 	PPC_BIT(63)
@@ -314,10 +315,60 @@  static int p8_thread_start(struct thread *thread)
 	return 0;
 }
 
-static int p8_thread_sreset(struct thread *thread)
+static void p8_ram_unquiesce_siblings(struct thread *thread)
+{
+	struct pdbg_target *target;
+	struct core *chip = target_to_core(
+		pdbg_target_require_parent("core", &thread->target));
+
+	pdbg_for_each_compatible(&chip->target, target, "ibm,power8-thread") {
+		struct thread *tmp;
+
+		if (pdbg_target_probe(target) != PDBG_TARGET_ENABLED)
+			continue;
+
+		tmp = target_to_thread(target);
+		if (!tmp->status.quiesced)
+			continue;
+
+		if (!tmp->ram_did_quiesce)
+			continue;
+
+		p8_thread_start(tmp);
+
+		tmp->ram_did_quiesce = false;
+	}
+}
+
+static int p8_ram_quiesce_siblings(struct thread *thread)
 {
-	/* Broken on p8 */
-	return 1;
+	struct pdbg_target *target;
+	struct core *chip = target_to_core(
+		pdbg_target_require_parent("core", &thread->target));
+	int rc = 0;
+
+	pdbg_for_each_compatible(&chip->target, target, "ibm,power8-thread") {
+		struct thread *tmp;
+
+		if (pdbg_target_probe(target) != PDBG_TARGET_ENABLED)
+			continue;
+
+		tmp = target_to_thread(target);
+		if (tmp->status.quiesced)
+			continue;
+
+		rc = p8_thread_stop(tmp);
+		if (rc)
+			break;
+		tmp->ram_did_quiesce = true;
+	}
+
+	if (!rc)
+		return 0;
+
+	p8_ram_unquiesce_siblings(thread);
+
+	return rc;
 }
 
 static int p8_ram_setup(struct thread *thread)
@@ -458,6 +509,80 @@  static int p8_ram_putxer(struct pdbg_target *thread, uint64_t value)
 	return 0;
 }
 
+#define SPR_SRR0 0x01a
+#define SPR_SRR1 0x01b
+
+#define HID0_HILE	PPC_BIT(19)
+
+#define MSR_HV          PPC_BIT(3)      /* Hypervisor mode */
+#define MSR_EE          PPC_BIT(48)     /* External Int. Enable */
+#define MSR_PR          PPC_BIT(49)     /* Problem State */
+#define MSR_FE0         PPC_BIT(52)     /* FP Exception 0 */
+#define MSR_FE1         PPC_BIT(55)     /* FP Exception 1 */
+#define MSR_IR          PPC_BIT(58)     /* Instructions reloc */
+#define MSR_DR          PPC_BIT(59)     /* Data reloc */
+#define MSR_RI          PPC_BIT(62)     /* Recoverable Interrupt */
+#define MSR_LE		PPC_BIT(63)	/* Little Endian */
+
+static int p8_get_hid0(struct pdbg_target *chip, uint64_t *value);
+static int emulate_sreset(struct thread *thread)
+{
+	struct pdbg_target *chip = pdbg_target_parent("core", &thread->target);
+	uint64_t hid0;
+	uint64_t old_nia, old_msr;
+	uint64_t new_nia, new_msr;
+
+	printf("emulate sreset begin\n");
+	CHECK_ERR(p8_get_hid0(chip, &hid0));
+	printf("emulate sreset HILE=%d\n", !!(hid0 & HID0_HILE));
+	CHECK_ERR(ram_getnia(&thread->target, &old_nia));
+	CHECK_ERR(ram_getmsr(&thread->target, &old_msr));
+	new_nia = 0x100;
+	new_msr = (old_msr & ~(MSR_PR | MSR_IR | MSR_DR | MSR_FE0 | MSR_FE1 | MSR_EE | MSR_RI)) | MSR_HV;
+	if (hid0 & HID0_HILE)
+		new_msr |= MSR_LE;
+	else
+		new_msr &= ~MSR_LE;
+	printf("emulate sreset old NIA:%llx MSR:%llx\n", old_nia, old_msr);
+	printf("emulate sreset new NIA:%llx MSR:%llx\n", new_nia, new_msr);
+	CHECK_ERR(ram_putspr(&thread->target, SPR_SRR0, old_nia));
+	CHECK_ERR(ram_putspr(&thread->target, SPR_SRR1, old_msr));
+	CHECK_ERR(ram_putnia(&thread->target, new_nia));
+	CHECK_ERR(ram_putmsr(&thread->target, new_msr));
+	printf("emulate sreset done\n");
+
+	return 0;
+}
+
+static int p8_thread_sreset(struct thread *thread)
+{
+	int rc;
+
+	if (!(thread->status.active)) {
+		CHECK_ERR(pib_write(&thread->target, DIRECT_CONTROLS_REG, DIRECT_CONTROL_SP_SRESET));
+		thread->status = get_thread_status(thread);
+
+		return 0;
+	}
+
+	rc = p8_ram_quiesce_siblings(thread);
+	if (rc)
+		return rc;
+
+	/* Thread was active, emulate the sreset */
+	rc = p8_ram_setup(thread);
+	if (rc) {
+		p8_ram_unquiesce_siblings(thread);
+		return rc;
+	}
+	rc = emulate_sreset(thread);
+	p8_ram_destroy(thread);
+	p8_ram_unquiesce_siblings(thread);
+	if (rc)
+		return rc;
+	return p8_thread_start(thread);
+}
+
 /*
  * Initialise all viable threads for ramming on the given core.
  */