/** @file write_protection.h @brief Disable/Enable write protection @details Copyright (c) 2024 Acronis International GmbH @author Denis Kopyrin ([email protected]) @since $Id: $ */ #pragma once #include "compat.h" #ifndef X86_CR4_CET # define X86_CR4_CET (1UL << 23) #endif #ifndef __FORCE_ORDER # define __FORCE_ORDER "m"(*(unsigned int *)0x1000UL) #endif static inline unsigned long fp_read_cr4(void) { #ifndef KERNEL_MOCK // cr4 is protected register in userspace unsigned long val; asm volatile("mov %%cr4, %0\n" : "=r" (val) : __FORCE_ORDER); return val; #else return gCR4; #endif } static inline void fp_write_cr4(unsigned long val) { #ifndef KERNEL_MOCK // cr0 is protected register in userspace asm volatile("mov %0, %%cr4\n": "+r" (val) : : "memory"); #else gCR4 = val; #endif } // On 5.3.0 using the legal 'write_cp0' causes panic because WP is not disabled. // So let's just use out own 'write_cp0' that avoid silly checks. static inline void wp_cr0(unsigned long cr0) { #ifndef KERNEL_MOCK // cr0 is protected register in userspace __asm__ __volatile__ ("mov %0, %%cr0": "+r"(cr0)); #else gCR0 = cr0; #endif } typedef struct { unsigned long saved_cr0; unsigned long written_cr0; unsigned long saved_cr4; unsigned long written_cr4; bool was_written_cr0; bool was_written_cr4; } cr0_write_protect_t; static inline cr0_write_protect_t disable_write_protect(void) { cr0_write_protect_t wp; wp.saved_cr0 = read_cr0(); wp.saved_cr4 = fp_read_cr4(); wp.written_cr0 = wp.saved_cr0; wp.written_cr4 = wp.saved_cr4; wp.was_written_cr0 = false; wp.was_written_cr4 = false; if (wp.saved_cr4 & X86_CR4_CET) { wp.was_written_cr4 = true; wp.written_cr4 &= ~X86_CR4_CET; fp_write_cr4(wp.written_cr4); } if (wp.saved_cr0 & X86_CR0_WP) { wp.was_written_cr0 = true; wp.written_cr0 &= ~X86_CR0_WP; wp_cr0(wp.written_cr0); } return wp; } static inline void restore_write_protect(cr0_write_protect_t wp) { if (wp.was_written_cr0) wp_cr0(wp.saved_cr0); if (wp.was_written_cr4) fp_write_cr4(wp.saved_cr4); } static inline void redisable_write_protect(cr0_write_protect_t wp) { if (wp.was_written_cr4) fp_write_cr4(wp.written_cr4); if (wp.was_written_cr0) wp_cr0(wp.written_cr0); }