blob: 28f2b6a7e7daaf494a5ef43038fa88b071840a66 [file] [edit]
// SPDX-License-Identifier: GPL-2.0
//! x86_64 page size compat
//!
//! C header: [`include/linux/page_size_compat.h`](srctree/include/linux/page_size_compat.h).
//!
//! Rust helpers for x86_64 page size emulation.
use kernel::{bindings, page};
/// __page_shift is the emulated page shift.
#[inline(always)]
pub fn __page_shift() -> usize {
// SAFETY: Accessing C static key
let emulated = unsafe {
bindings::static_key_count(&raw mut bindings::page_shift_compat_enabled.key) != 0
};
if emulated {
// SAFETY: Reading static C variable page_shift_compat.
// page_size_compat is marked __ro_after_init in the C code.
let shift = unsafe { bindings::page_shift_compat };
// page_shift_compat is sanitized and range checked by boot
// parameter parsing; so this conversion should always be safe.
shift.try_into().unwrap()
} else {
page::PAGE_SHIFT
}
}
/// __page_size is the emulated page size.
#[inline(always)]
pub fn __page_size() -> usize {
let shift = __page_shift();
1usize << shift
}
/// __page_mask can be used to align addresses to a __page boundary.
#[inline(always)]
pub fn __page_mask() -> usize {
let page_size = __page_size();
!(page_size - 1)
}
/// Aligns the given address UP to the nearest __page boundary.
#[inline(always)]
pub fn __page_align(addr: usize) -> usize {
let page_size = __page_size();
let mask = !(page_size - 1);
// Parentheses around `page_size - 1` to avoid triggering
// overflow sanitizers in the wrong cases.
(addr + (page_size - 1)) & mask
}
/// Aligns the given address DOWN to the nearest __page boundary.
#[inline(always)]
pub fn __page_align_down(addr: usize) -> usize {
let mask = __page_mask();
addr & mask
}