git: f66a407de25e - main - sys: Add cpu_update_pcb hook

From: John Baldwin <jhb_at_FreeBSD.org>
Date: Fri, 31 Jan 2025 20:40:37 UTC
The branch main has been updated by jhb:

URL: https://cgit.FreeBSD.org/src/commit/?id=f66a407de25eaa4c58b4f6f02086d55141593b63

commit f66a407de25eaa4c58b4f6f02086d55141593b63
Author:     John Baldwin <jhb@FreeBSD.org>
AuthorDate: 2025-01-31 20:39:10 +0000
Commit:     John Baldwin <jhb@FreeBSD.org>
CommitDate: 2025-01-31 20:40:29 +0000

    sys: Add cpu_update_pcb hook
    
    This MD function is invoked before dumping register set notes when
    writing out a core dump to ensure that the PCB for a given thread is
    up to date.  This provides a centralized place to update the PCB with
    values of the current thread for each arch rather than doing this work
    in each register set's get method.
    
    Discussed with: jrtc27
    Reviewed by:    kib, markj
    Sponsored by:   AFRL, DARPA
    Differential Revision:  https://reviews.freebsd.org/D44910
---
 share/man/man9/Makefile             |  3 ++-
 share/man/man9/cpu_machdep.9        | 23 +++++++++++++++++++++--
 sys/amd64/amd64/ptrace_machdep.c    |  4 ----
 sys/amd64/amd64/vm_machdep.c        |  7 +++++++
 sys/arm/arm/vm_machdep.c            |  7 +++++++
 sys/arm64/arm64/vm_machdep.c        |  8 ++++++++
 sys/i386/i386/vm_machdep.c          |  7 +++++++
 sys/kern/imgact_elf.c               |  3 +++
 sys/powerpc/include/reg.h           |  5 -----
 sys/powerpc/powerpc/elf32_machdep.c |  2 --
 sys/powerpc/powerpc/elf64_machdep.c |  2 --
 sys/powerpc/powerpc/exec_machdep.c  |  6 +++---
 sys/powerpc/powerpc/vm_machdep.c    |  2 +-
 sys/riscv/riscv/vm_machdep.c        |  5 +++++
 sys/sys/proc.h                      |  1 +
 15 files changed, 65 insertions(+), 20 deletions(-)

diff --git a/share/man/man9/Makefile b/share/man/man9/Makefile
index a284eaca5ecb..6af9880b8d57 100644
--- a/share/man/man9/Makefile
+++ b/share/man/man9/Makefile
@@ -931,7 +931,8 @@ MLINKS+=cpu_machdep.9 cpu_copy_thread.9 \
 	cpu_machdep.9 cpu_thread_clean.9 \
 	cpu_machdep.9 cpu_thread_exit.9 \
 	cpu_machdep.9 cpu_thread_free.9 \
-	cpu_machdep.9 cpu_throw.9
+	cpu_machdep.9 cpu_throw.9 \
+	cpu_machdep.9 cpu_update_pcb.9
 MLINKS+=cpuset.9 CPUSET_T_INITIALIZER.9 \
 	cpuset.9 CPUSET_FSET.9 \
 	cpuset.9 CPU_CLR.9 \
diff --git a/share/man/man9/cpu_machdep.9 b/share/man/man9/cpu_machdep.9
index 9ab42807eac1..30ac5ea36642 100644
--- a/share/man/man9/cpu_machdep.9
+++ b/share/man/man9/cpu_machdep.9
@@ -8,7 +8,7 @@
 .\" Technology), and Capabilities Limited under Defense Advanced Research
 .\" Projects Agency (DARPA) Contract No. FA8750-24-C-B047 ("DEC").
 .\"
-.Dd January 3, 2025
+.Dd January 31, 2025
 .Dt cpu_machdep 9
 .Os
 .Sh NAME
@@ -31,7 +31,8 @@
 .Nm cpu_thread_clean ,
 .Nm cpu_thread_exit ,
 .Nm cpu_thread_free ,
-.Nm cpu_throw
+.Nm cpu_throw ,
+.Nm cpu_update_pcb
 .Nd machine-dependent interfaces to handle CPU and thread state
 .Sh SYNOPSIS
 .In sys/proc.h
@@ -84,6 +85,8 @@
 .Fn cpu_thread_free "struct thread *td"
 .Ft void
 .Fn cpu_throw "struct thread *old" "struct thread *new"
+.Ft void
+.Fn cpu_update_pcb "struct thread *td"
 .Sh DESCRIPTION
 These functions provide architecture-specific implementations of
 machine-independent abstractions.
@@ -183,6 +186,22 @@ sets a new thread's initial user thread pointer register to
 reference the user TLS base pointer
 .Fa tls_base .
 .Pp
+.Fn cpu_update_pcb
+updates the pcb of the current thread with current user register values.
+This is invoked before writing out register notes in a core dump.
+This function typically only has to update user registers for the current
+thread that are saved in the pcb during context switches rather than
+in the trapframe on kernel entry.
+.Pp
+Note that when
+.Fn cpu_update_pcb
+is used,
+threads in a process other than the current thread are stopped,
+typically by
+.Fn thread_single .
+The pcbs of those stopped threads should already be updated by
+.Fn cpu_switch .
+.Pp
 .Fn cpu_fetch_syscall_args
 fetches the current system call arguments for the native FreeBSD ABI from the
 current thread's user register state and/or user stack.
diff --git a/sys/amd64/amd64/ptrace_machdep.c b/sys/amd64/amd64/ptrace_machdep.c
index 3b05fded585f..715f0749bda9 100644
--- a/sys/amd64/amd64/ptrace_machdep.c
+++ b/sys/amd64/amd64/ptrace_machdep.c
@@ -63,8 +63,6 @@ get_segbases(struct regset *rs, struct thread *td, void *buf,
 		reg = buf;
 
 		pcb = td->td_pcb;
-		if (td == curthread)
-			update_pcb_bases(pcb);
 		reg->r_fsbase = pcb->pcb_fsbase;
 		reg->r_gsbase = pcb->pcb_gsbase;
 	}
@@ -113,8 +111,6 @@ get_segbases32(struct regset *rs, struct thread *td, void *buf,
 		reg = buf;
 
 		pcb = td->td_pcb;
-		if (td == curthread)
-			update_pcb_bases(pcb);
 		reg->r_fsbase = (uint32_t)pcb->pcb_fsbase;
 		reg->r_gsbase = (uint32_t)pcb->pcb_gsbase;
 	}
diff --git a/sys/amd64/amd64/vm_machdep.c b/sys/amd64/amd64/vm_machdep.c
index 1c6b1549422b..2b280e960b2e 100644
--- a/sys/amd64/amd64/vm_machdep.c
+++ b/sys/amd64/amd64/vm_machdep.c
@@ -673,3 +673,10 @@ cpu_set_user_tls(struct thread *td, void *tls_base)
 	pcb->pcb_fsbase = (register_t)tls_base;
 	return (0);
 }
+
+void
+cpu_update_pcb(struct thread *td)
+{
+	MPASS(td == curthread);
+	update_pcb_bases(td->td_pcb);
+}
diff --git a/sys/arm/arm/vm_machdep.c b/sys/arm/arm/vm_machdep.c
index 4e0a6bb9cbc5..d31d36ba807d 100644
--- a/sys/arm/arm/vm_machdep.c
+++ b/sys/arm/arm/vm_machdep.c
@@ -278,6 +278,13 @@ cpu_fork_kthread_handler(struct thread *td, void (*func)(void *), void *arg)
 	td->td_pcb->pcb_regs.sf_r5 = (register_t)arg;	/* first arg */
 }
 
+void
+cpu_update_pcb(struct thread *td)
+{
+	MPASS(td == curthread);
+	td->td_pcb->pcb_regs.sf_tpidrurw = (register_t)get_tls();
+}
+
 void
 cpu_exit(struct thread *td)
 {
diff --git a/sys/arm64/arm64/vm_machdep.c b/sys/arm64/arm64/vm_machdep.c
index 924628001103..9869ce6cae0b 100644
--- a/sys/arm64/arm64/vm_machdep.c
+++ b/sys/arm64/arm64/vm_machdep.c
@@ -290,6 +290,14 @@ cpu_fork_kthread_handler(struct thread *td, void (*func)(void *), void *arg)
 	td->td_pcb->pcb_x[PCB_X20] = (uintptr_t)arg;
 }
 
+void
+cpu_update_pcb(struct thread *td)
+{
+	MPASS(td == curthread);
+	td->td_pcb->pcb_tpidr_el0 = READ_SPECIALREG(tpidr_el0);
+	td->td_pcb->pcb_tpidrro_el0 = READ_SPECIALREG(tpidrro_el0);
+}
+
 void
 cpu_exit(struct thread *td)
 {
diff --git a/sys/i386/i386/vm_machdep.c b/sys/i386/i386/vm_machdep.c
index b0dd7534633b..c64f19a30cbd 100644
--- a/sys/i386/i386/vm_machdep.c
+++ b/sys/i386/i386/vm_machdep.c
@@ -534,6 +534,13 @@ cpu_set_user_tls(struct thread *td, void *tls_base)
 	return (0);
 }
 
+void
+cpu_update_pcb(struct thread *td)
+{
+	MPASS(td == curthread);
+	td->td_pcb->pcb_gs = rgs();
+}
+
 /*
  * Convert kernel VA to physical address
  */
diff --git a/sys/kern/imgact_elf.c b/sys/kern/imgact_elf.c
index d94f9e1e5143..fc074ad74e6b 100644
--- a/sys/kern/imgact_elf.c
+++ b/sys/kern/imgact_elf.c
@@ -2421,6 +2421,9 @@ __elfN(prepare_register_notes)(struct thread *td, struct note_info_list *list,
 
 	size = 0;
 
+	if (target_td == td)
+		cpu_update_pcb(target_td);
+
 	/* NT_PRSTATUS must be the first register set note. */
 	size += __elfN(register_regset_note)(td, list, &__elfN(regset_prstatus),
 	    target_td);
diff --git a/sys/powerpc/include/reg.h b/sys/powerpc/include/reg.h
index 1fe67ac3967d..781ee3b02289 100644
--- a/sys/powerpc/include/reg.h
+++ b/sys/powerpc/include/reg.h
@@ -69,11 +69,6 @@ int	set_fpregs(struct thread *, struct fpreg *);
 int	fill_dbregs(struct thread *, struct dbreg *);
 int	set_dbregs(struct thread *, struct dbreg *);
 
-/*
- * MD interfaces.
- */
-void	cpu_save_thread_regs(struct thread *);
-
 #ifdef COMPAT_FREEBSD32
 struct image_params;
 
diff --git a/sys/powerpc/powerpc/elf32_machdep.c b/sys/powerpc/powerpc/elf32_machdep.c
index fcdc1aa1e8a5..af01043878db 100644
--- a/sys/powerpc/powerpc/elf32_machdep.c
+++ b/sys/powerpc/powerpc/elf32_machdep.c
@@ -190,7 +190,6 @@ elf32_dump_thread(struct thread *td, void *dst, size_t *off)
 	pcb = td->td_pcb;
 
 	if (pcb->pcb_flags & PCB_VEC) {
-		save_vec_nodrop(td);
 		if (dst != NULL) {
 			len += elf32_populate_note(NT_PPC_VMX,
 			    &pcb->pcb_vec, (char *)dst + len,
@@ -201,7 +200,6 @@ elf32_dump_thread(struct thread *td, void *dst, size_t *off)
 	}
 
 	if (pcb->pcb_flags & PCB_VSX) {
-		save_fpu_nodrop(td);
 		if (dst != NULL) {
 			/*
 			 * Doubleword 0 of VSR0-VSR31 overlap with FPR0-FPR31 and
diff --git a/sys/powerpc/powerpc/elf64_machdep.c b/sys/powerpc/powerpc/elf64_machdep.c
index b780a2ed82fc..1035e35d286e 100644
--- a/sys/powerpc/powerpc/elf64_machdep.c
+++ b/sys/powerpc/powerpc/elf64_machdep.c
@@ -279,7 +279,6 @@ elf64_dump_thread(struct thread *td, void *dst, size_t *off)
 	pcb = td->td_pcb;
 
 	if (pcb->pcb_flags & PCB_VEC) {
-		save_vec_nodrop(td);
 		if (dst != NULL) {
 			len += elf64_populate_note(NT_PPC_VMX,
 			    &pcb->pcb_vec, (char *)dst + len,
@@ -290,7 +289,6 @@ elf64_dump_thread(struct thread *td, void *dst, size_t *off)
 	}
 
 	if (pcb->pcb_flags & PCB_VSX) {
-		save_fpu_nodrop(td);
 		if (dst != NULL) {
 			/*
 			 * Doubleword 0 of VSR0-VSR31 overlap with FPR0-FPR31 and
diff --git a/sys/powerpc/powerpc/exec_machdep.c b/sys/powerpc/powerpc/exec_machdep.c
index 9ab323a0e300..3586c01d6652 100644
--- a/sys/powerpc/powerpc/exec_machdep.c
+++ b/sys/powerpc/powerpc/exec_machdep.c
@@ -593,13 +593,13 @@ cleanup_power_extras(struct thread *td)
  * Keep this in sync with the assembly code in cpu_switch()!
  */
 void
-cpu_save_thread_regs(struct thread *td)
+cpu_update_pcb(struct thread *td)
 {
 	uint32_t pcb_flags;
 	struct pcb *pcb;
 
 	KASSERT(td == curthread,
-	    ("cpu_save_thread_regs: td is not curthread"));
+	    ("cpu_update_pcb: td is not curthread"));
 
 	pcb = td->td_pcb;
 
@@ -1110,7 +1110,7 @@ cpu_copy_thread(struct thread *td, struct thread *td0)
 
 	/* Ensure td0 pcb is up to date. */
 	if (td0 == curthread)
-		cpu_save_thread_regs(td0);
+		cpu_update_pcb(td0);
 
 	pcb2 = td->td_pcb;
 
diff --git a/sys/powerpc/powerpc/vm_machdep.c b/sys/powerpc/powerpc/vm_machdep.c
index 12c64f9e38bf..d47beedb595e 100644
--- a/sys/powerpc/powerpc/vm_machdep.c
+++ b/sys/powerpc/powerpc/vm_machdep.c
@@ -121,7 +121,7 @@ cpu_fork(struct thread *td1, struct proc *p2, struct thread *td2, int flags)
 
 	/* Ensure td1 is up to date before copy. */
 	if (td1 == curthread)
-		cpu_save_thread_regs(td1);
+		cpu_update_pcb(td1);
 
 	pcb = (struct pcb *)((td2->td_kstack +
 	    td2->td_kstack_pages * PAGE_SIZE - sizeof(struct pcb)) & ~0x2fUL);
diff --git a/sys/riscv/riscv/vm_machdep.c b/sys/riscv/riscv/vm_machdep.c
index 726f95213c91..bd510080e02c 100644
--- a/sys/riscv/riscv/vm_machdep.c
+++ b/sys/riscv/riscv/vm_machdep.c
@@ -239,6 +239,11 @@ cpu_fork_kthread_handler(struct thread *td, void (*func)(void *), void *arg)
 	td->td_pcb->pcb_sp = (uintptr_t)td->td_frame;
 }
 
+void
+cpu_update_pcb(struct thread *td)
+{
+}
+
 void
 cpu_exit(struct thread *td)
 {
diff --git a/sys/sys/proc.h b/sys/sys/proc.h
index 786cc447dc2c..46482f26e0ef 100644
--- a/sys/sys/proc.h
+++ b/sys/sys/proc.h
@@ -1220,6 +1220,7 @@ extern	void (*cpu_idle_hook)(sbintime_t);	/* Hook to machdep CPU idler. */
 void	cpu_switch(struct thread *, struct thread *, struct mtx *);
 void	cpu_sync_core(void);
 void	cpu_throw(struct thread *, struct thread *) __dead2;
+void	cpu_update_pcb(struct thread *);
 bool	curproc_sigkilled(void);
 void	userret(struct thread *, struct trapframe *);