@@ -18,6 +18,7 @@
#define ARM64_SW_FEATURE_OVERRIDE_NOKASLR 0
#define ARM64_SW_FEATURE_OVERRIDE_HVHE 4
#define ARM64_SW_FEATURE_OVERRIDE_RODATA_OFF 8
+#define ARM64_SW_FEATURE_OVERRIDE_PAGESHIFT 12
#ifndef __ASSEMBLY__
@@ -963,6 +964,16 @@ static inline bool arm64_test_sw_feature_override(int feat)
&arm64_sw_feature_override);
}
+static inline int arm64_pageshift_cmdline(void)
+{
+ int val;
+
+ val = arm64_apply_feature_override(0,
+ ARM64_SW_FEATURE_OVERRIDE_PAGESHIFT,
+ 4, &arm64_sw_feature_override);
+ return val ? val * 2 + 10 : 0;
+}
+
static inline bool kaslr_disabled_cmdline(void)
{
return arm64_test_sw_feature_override(ARM64_SW_FEATURE_OVERRIDE_NOKASLR);
@@ -183,6 +183,38 @@ static bool __init hvhe_filter(u64 val)
ID_AA64MMFR1_EL1_VH_SHIFT));
}
+static bool __init pageshift_filter(u64 val)
+{
+ u64 mmfr0 = read_sysreg_s(SYS_ID_AA64MMFR0_EL1);
+ u32 tgran64 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, TGRAN64, mmfr0);
+ u32 tgran16 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, TGRAN16, mmfr0);
+ u32 tgran4 = SYS_FIELD_GET(ID_AA64MMFR0_EL1, TGRAN4, mmfr0);
+
+ /* pageshift is stored compressed in 4 bit field. */
+ if (val)
+ val = val * 2 + 10;
+
+ if (val < PAGE_SHIFT_MIN || val > PAGE_SHIFT_MAX)
+ return false;
+
+ if (val == ARM64_PAGE_SHIFT_64K &&
+ tgran64 >= ID_AA64MMFR0_EL1_TGRAN64_SUPPORTED_MIN &&
+ tgran64 <= ID_AA64MMFR0_EL1_TGRAN64_SUPPORTED_MAX)
+ return true;
+
+ if (val == ARM64_PAGE_SHIFT_16K &&
+ tgran16 >= ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED_MIN &&
+ tgran16 <= ID_AA64MMFR0_EL1_TGRAN16_SUPPORTED_MAX)
+ return true;
+
+ if (val == ARM64_PAGE_SHIFT_4K &&
+ tgran4 >= ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED_MIN &&
+ tgran4 <= ID_AA64MMFR0_EL1_TGRAN4_SUPPORTED_MAX)
+ return true;
+
+ return false;
+}
+
static const struct ftr_set_desc sw_features __prel64_initconst = {
.name = "arm64_sw",
.override = &arm64_sw_feature_override,
@@ -190,6 +222,7 @@ static const struct ftr_set_desc sw_features __prel64_initconst = {
FIELD("nokaslr", ARM64_SW_FEATURE_OVERRIDE_NOKASLR, NULL),
FIELD("hvhe", ARM64_SW_FEATURE_OVERRIDE_HVHE, hvhe_filter),
FIELD("rodataoff", ARM64_SW_FEATURE_OVERRIDE_RODATA_OFF, NULL),
+ FIELD("pageshift", ARM64_SW_FEATURE_OVERRIDE_PAGESHIFT, pageshift_filter),
{}
},
};
@@ -225,6 +258,9 @@ static const struct {
{ "rodata=off", "arm64_sw.rodataoff=1" },
{ "arm64.nolva", "id_aa64mmfr2.varange=0" },
{ "arm64.no32bit_el0", "id_aa64pfr0.el0=1" },
+ { "arm64.pagesize=4k", "arm64_sw.pageshift=1" },
+ { "arm64.pagesize=16k", "arm64_sw.pageshift=2" },
+ { "arm64.pagesize=64k", "arm64_sw.pageshift=3" },
};
static int __init parse_hexdigit(const char *p, u64 *v)
Allow user to pass desired page size via command line as either "arm64.pagesize=4k", "arm64.pagesize=16k", or "arm64.pagesize=64k". The specified value is stored in the SW_FEATURE register as an encoded page shift in a 4 bit field. We only allow setting the page size override if the requested size is supported by the HW and is within the compile-time [PAGE_SIZE_MIN, PAGE_SIZE_MAX] range. This second condition means that overrides get ignored when we have a compile-time page size (because PAGE_SIZE_MIN == PAGE_SIZE_MAX). Signed-off-by: Ryan Roberts <ryan.roberts@arm.com> --- ***NOTE*** Any confused maintainers may want to read the cover note here for context: https://lore.kernel.org/all/20241014105514.3206191-1-ryan.roberts@arm.com/ arch/arm64/include/asm/cpufeature.h | 11 ++++++++ arch/arm64/kernel/pi/idreg-override.c | 36 +++++++++++++++++++++++++++ 2 files changed, 47 insertions(+)