aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Belousov <kib@FreeBSD.org>2025-09-18 21:55:19 +0000
committerKonstantin Belousov <kib@FreeBSD.org>2025-09-24 13:02:27 +0000
commita319696875451229f492b6c15e58a0ac54dbcda1 (patch)
tree2151569be4d57718a347dd4f3d18b891a8ec82b9
parent50ee990e1874d6d40b8e3dc359c37e2ef2ebf477 (diff)
amd64: add wrmsr_early_safe(9)
The variant of wrmsr_safe(9) that might work before IDT and curpcb are initialized. Assumes BSP, and that all APs are parked. Before calling wrmsr_early_safe(), the wrmsr_early_safe_start() should be called, afterward wrmsr_early_safe_end() restores the bootenv IDT. Reviewed by: markj Tested by: glebius Sponsored by: The FreeBSD Foundation MFC after: 1 week Differential revision: https://reviews.freebsd.org/D52607
-rw-r--r--sys/amd64/amd64/machdep.c33
-rw-r--r--sys/amd64/amd64/support.S16
-rw-r--r--sys/amd64/include/md_var.h4
3 files changed, 53 insertions, 0 deletions
diff --git a/sys/amd64/amd64/machdep.c b/sys/amd64/amd64/machdep.c
index 9ff60439d1ec..2fce1a7e64b6 100644
--- a/sys/amd64/amd64/machdep.c
+++ b/sys/amd64/amd64/machdep.c
@@ -1822,6 +1822,39 @@ clear_pcb_flags(struct pcb *pcb, const u_int flags)
: "cc", "memory");
}
+extern const char wrmsr_early_safe_gp_handler[];
+static struct region_descriptor wrmsr_early_safe_orig_efi_idt;
+
+void
+wrmsr_early_safe_start(void)
+{
+ struct region_descriptor efi_idt;
+ struct gate_descriptor *gpf_descr;
+
+ sidt(&wrmsr_early_safe_orig_efi_idt);
+ efi_idt.rd_limit = 32 * sizeof(idt0[0]);
+ efi_idt.rd_base = (uintptr_t)idt0;
+ lidt(&efi_idt);
+
+ gpf_descr = &idt0[IDT_GP];
+ gpf_descr->gd_looffset = (uintptr_t)wrmsr_early_safe_gp_handler;
+ gpf_descr->gd_hioffset = (uintptr_t)wrmsr_early_safe_gp_handler >> 16;
+ gpf_descr->gd_selector = rcs();
+ gpf_descr->gd_type = SDT_SYSTGT;
+ gpf_descr->gd_p = 1;
+}
+
+void
+wrmsr_early_safe_end(void)
+{
+ struct gate_descriptor *gpf_descr;
+
+ lidt(&wrmsr_early_safe_orig_efi_idt);
+
+ gpf_descr = &idt0[IDT_GP];
+ memset(gpf_descr, 0, sizeof(*gpf_descr));
+}
+
#ifdef KDB
/*
diff --git a/sys/amd64/amd64/support.S b/sys/amd64/amd64/support.S
index 870cd255abb7..27694a95653c 100644
--- a/sys/amd64/amd64/support.S
+++ b/sys/amd64/amd64/support.S
@@ -1565,6 +1565,22 @@ msr_onfault:
POP_FRAME_POINTER
ret
+ENTRY(wrmsr_early_safe)
+ movl %edi,%ecx
+ movl %esi,%eax
+ sarq $32,%rsi
+ movl %esi,%edx
+ wrmsr
+ xorl %eax,%eax
+wrmsr_early_faulted:
+ ret
+
+ENTRY(wrmsr_early_safe_gp_handler)
+ addq $8,%rsp
+ movl $EFAULT,%eax
+ movq $wrmsr_early_faulted,(%rsp)
+ iretq
+
/*
* 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/include/md_var.h b/sys/amd64/include/md_var.h
index b6ddc6eaaebe..b6d8c469cdf6 100644
--- a/sys/amd64/include/md_var.h
+++ b/sys/amd64/include/md_var.h
@@ -99,6 +99,10 @@ void get_fpcontext(struct thread *td, struct __mcontext *mcp,
int set_fpcontext(struct thread *td, struct __mcontext *mcp,
char *xfpustate, size_t xfpustate_len);
+void wrmsr_early_safe_start(void);
+void wrmsr_early_safe_end(void);
+int wrmsr_early_safe(u_int msr, uint64_t data);
+
#endif /* !_MACHINE_MD_VAR_H_ */
#endif /* __i386__ */