/**
@file write_protection.h
@brief Disable/Enable write protection
@details Copyright (c) 2024 Acronis International GmbH
@author Denis Kopyrin (Denis.Kopyrin@acronis.com)
@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);
}
|