Index: sys/amd64/amd64/db_interface.c =================================================================== --- sys/amd64/amd64/db_interface.c +++ sys/amd64/amd64/db_interface.c @@ -37,6 +37,7 @@ #include #include +#include #include #include @@ -75,19 +76,19 @@ jmp_buf jb; void *prev_jb; char *dst; - u_long cr0save; + bool old_wp; int ret; - cr0save = rcr0(); + old_wp = false; prev_jb = kdb_jmpbuf(jb); ret = setjmp(jb); if (ret == 0) { - load_cr0(cr0save & ~CR0_WP); + old_wp = disable_wp(); dst = (char *)addr; while (size-- > 0) *dst++ = *data++; } - load_cr0(cr0save); + restore_wp(old_wp); (void)kdb_jmpbuf(prev_jb); return (ret); } Index: sys/amd64/amd64/fpu.c =================================================================== --- sys/amd64/amd64/fpu.c +++ sys/amd64/amd64/fpu.c @@ -205,6 +205,7 @@ { u_int cp[4]; uint64_t xsave_mask_user; + bool old_wp; if ((cpu_feature2 & CPUID2_XSAVE) != 0) { use_xsave = 1; @@ -233,8 +234,14 @@ * Patch the XSAVE instruction in the cpu_switch code * to XSAVEOPT. We assume that XSAVE encoding used * REX byte, and set the bit 4 of the r/m byte. + * + * It seems that some BIOSes give control to the OS + * with CR0.WP already set, making the kernel text + * read-only before cpu_startup(). */ + old_wp = disable_wp(); ctx_switch_xsave[3] |= 0x10; + restore_wp(old_wp); } } Index: sys/amd64/amd64/gdb_machdep.c =================================================================== --- sys/amd64/amd64/gdb_machdep.c +++ sys/amd64/amd64/gdb_machdep.c @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -127,17 +128,14 @@ void * gdb_begin_write(void) { - u_long cr0save; - cr0save = rcr0(); - load_cr0(cr0save & ~CR0_WP); - return ((void *)cr0save); + return (disable_wp() ? &gdb_begin_write : NULL); } void gdb_end_write(void *arg) { - load_cr0((u_long)arg); + restore_wp(arg != NULL); } Index: sys/amd64/amd64/machdep.c =================================================================== --- sys/amd64/amd64/machdep.c +++ sys/amd64/amd64/machdep.c @@ -1554,6 +1554,12 @@ TSRAW(&thread0, TS_ENTER, __func__, NULL); + /* + * Enable write to the kernel text and read-only data segment + * for early boot fixups. + */ + (void)disable_wp(); + /* * This may be done better later if it gets more high level * components in it. If so just link td->td_proc here. @@ -2597,6 +2603,31 @@ : "cc", "memory"); } +/* + * Enable and restore kernel text write permissions. + * Callers must ensure that disable_wp()/restore_wp() are executed + * without rescheduling on the same core. + */ +bool +disable_wp(void) +{ + u_int cr0; + + cr0 = rcr0(); + if ((cr0 & CR0_WP) == 0) + return (false); + load_cr0(cr0 & ~CR0_WP); + return (true); +} + +void +restore_wp(bool old_wp) +{ + + if (old_wp) + load_cr0(rcr0() | CR0_WP); +} + #ifdef KDB /* Index: sys/amd64/include/md_var.h =================================================================== --- sys/amd64/include/md_var.h +++ sys/amd64/include/md_var.h @@ -53,6 +53,8 @@ void amd64_db_resume_dbreg(void); void amd64_lower_shared_page(struct sysentvec *); void amd64_syscall(struct thread *td, int traced); +bool disable_wp(void); +void restore_wp(bool old_wp); void doreti_iret(void) __asm(__STRING(doreti_iret)); void doreti_iret_fault(void) __asm(__STRING(doreti_iret_fault)); void ld_ds(void) __asm(__STRING(ld_ds));