diff mbox series

[v2,21/38] gdbserver: check for attn using the SPATTN register

Message ID 20220329154931.493851-22-npiggin@gmail.com
State New
Headers show
Series gdbserver multi-threaded debugging and POWER9/10 support | expand

Commit Message

Nicholas Piggin March 29, 2022, 3:49 p.m. UTC
When POWER9/10 execute an attn instruction, they set a bit in the SPATTN
instruction and quiesce the thread.

This bit can be checked to confirm the thread hit an attn instruction,
rather than assuming based on the thread being quiesced.

This also clears the SPATTN bit which clears the exception condition
that seems to be involved with the IPOLL interrupt storm and lock up
(although this does not fix POWER9).

Signed-off-by: Nicholas Piggin <npiggin@gmail.com>
---
 src/pdbgproxy.c | 79 ++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 68 insertions(+), 11 deletions(-)
diff mbox series

Patch

diff --git a/src/pdbgproxy.c b/src/pdbgproxy.c
index eddf813..1843517 100644
--- a/src/pdbgproxy.c
+++ b/src/pdbgproxy.c
@@ -457,6 +457,59 @@  static void v_contc(uint64_t *stack, void *priv)
 	poll_interval = 1;
 }
 
+#define P9_SPATTN_AND	0x20010A98
+#define P9_SPATTN	0x20010A99
+
+#define P10_SPATTN_AND	0x20028498
+#define P10_SPATTN	0x20028499
+
+static bool thread_check_attn(struct pdbg_target *target)
+{
+	struct thread *thread = target_to_thread(target);
+	struct pdbg_target *core;
+	uint64_t spattn;
+
+	if (pdbg_target_compatible(target, "ibm,power8-thread")) {
+		return true; /* XXX */
+	} else if (pdbg_target_compatible(target, "ibm,power9-thread")) {
+		core = pdbg_target_require_parent("core", target);
+		if (pib_read(core, P9_SPATTN, &spattn)) {
+			PR_ERROR("SPATTN read failed\n");
+			return false;
+		}
+
+		if (spattn & PPC_BIT(1 + 4*thread->id)) {
+			uint64_t mask = ~PPC_BIT(1 + 4*thread->id);
+
+			if (pib_write(core, P9_SPATTN_AND, mask)) {
+				PR_ERROR("SPATTN clear failed\n");
+				return false;
+			}
+
+			return true;
+		}
+	} else if (pdbg_target_compatible(target, "ibm,power10-thread")) {
+		core = pdbg_target_require_parent("core", target);
+		if (pib_read(core, P10_SPATTN, &spattn)) {
+			PR_ERROR("SPATTN read failed\n");
+			return false;
+		}
+
+		if (spattn & PPC_BIT(1 + 4*thread->id)) {
+			uint64_t mask = ~PPC_BIT(1 + 4*thread->id);
+
+			if (pib_write(core, P10_SPATTN_AND, mask)) {
+				PR_ERROR("SPATTN clear failed\n");
+				return false;
+			}
+
+			return true;
+		}
+	}
+
+	return false;
+}
+
 static void interrupt(uint64_t *stack, void *priv)
 {
 	struct thread_state status;
@@ -477,7 +530,6 @@  static void interrupt(uint64_t *stack, void *priv)
 
 static void poll(void)
 {
-	uint64_t nia;
 	struct thread_state status;
 
 	thread_target->probe(thread_target);
@@ -495,17 +547,22 @@  static void poll(void)
 
 		state = IDLE;
 		poll_interval = VCONT_POLL_DELAY;
-		if (!(status.active)) {
-			PR_ERROR("Thread inactive after trap\n");
-			send_response(fd, ERROR(EPERM));
-			return;
-		}
 
-		/* Restore NIA */
-		if (thread_getnia(thread_target, &nia))
-			PR_ERROR("Error during getnia\n");
-		if (thread_putnia(thread_target, nia - 4))
-			PR_ERROR("Error during putnia\n");
+		if (thread_check_attn(thread_target)) {
+			uint64_t nia;
+
+			if (!(status.active)) {
+				PR_ERROR("Thread inactive after trap\n");
+				send_response(fd, ERROR(EPERM));
+				return;
+			}
+
+			/* Restore NIA */
+			if (thread_getnia(thread_target, &nia))
+				PR_ERROR("Error during getnia\n");
+			if (thread_putnia(thread_target, nia - 4))
+				PR_ERROR("Error during putnia\n");
+		}
 		send_response(fd, TRAP);
 		break;
 	}