diff --git a/sys/amd64/amd64/support.S b/sys/amd64/amd64/support.S --- a/sys/amd64/amd64/support.S +++ b/sys/amd64/amd64/support.S @@ -1577,6 +1577,26 @@ POP_FRAME_POINTER ret +ENTRY(safe_read) +/* int safe_read(vm_offset_t address %rdi, char *valp %rsi) */ + PUSH_FRAME_POINTER + movq PCPU(CURPCB),%r8 + movq PCB_ONFAULT(%r8),%r9 + movq $safe_read_onfault,PCB_ONFAULT(%r8) + .globl safe_read_read +safe_read_read: + movb (%rdi),%al + movq %r9,PCB_ONFAULT(%r8) + movb %al,(%rsi) + xorl %eax,%eax + POP_FRAME_POINTER + ret +safe_read_onfault: + movq %r9,PCB_ONFAULT(%r8) + movl $EFAULT,%eax + POP_FRAME_POINTER + ret + /* * void pmap_pti_pcid_invalidate(uint64_t ucr3, uint64_t kcr3); * Invalidates address space addressed by ucr3, then returns to kcr3. diff --git a/sys/amd64/amd64/trap.c b/sys/amd64/amd64/trap.c --- a/sys/amd64/amd64/trap.c +++ b/sys/amd64/amd64/trap.c @@ -210,6 +210,8 @@ }, }; +extern const char safe_read_read[]; + /* * Exception, fault, and trap interface to the FreeBSD kernel. * This common code is called from assembly language IDT gate entry @@ -716,6 +718,13 @@ p = td->td_proc; eva = frame->tf_addr; + if (__predict_false(frame->tf_rip == (uintptr_t)safe_read_read && + curpcb->pcb_onfault != NULL && !usermode && + eva >= VM_MIN_KERNEL_ADDRESS)) { + frame->tf_rip = (long)curpcb->pcb_onfault; + return (0); + } + if (__predict_false((td->td_pflags & TDP_NOFAULTING) != 0)) { /* * Due to both processor errata and lazy TLB invalidation when diff --git a/sys/sys/systm.h b/sys/sys/systm.h --- a/sys/sys/systm.h +++ b/sys/sys/systm.h @@ -563,6 +563,14 @@ void counted_warning(unsigned *counter, const char *msg); +/* + * Safely read the kernel memory at address addr, places the value + * into *valp. Returns 0 on success, EFAULT if read was impossible, + * e.g. due to the address not mapped or not having neccessary + * permissions. + */ +int safe_read(vm_offset_t addr, char *valp); + /* * APIs to manage deprecation and obsolescence. */