summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-28 09:02:04 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-28 09:02:04 -0700
commit9d607ae0f7d629dc068aed5f498e24d3e9875ef1 (patch)
treea66740d5e5fa38735a640f41b2e6d30ce9a43a60
parent72ecc30d607f6c8ef8ac0d7b88b0c176a7409367 (diff)
parent2bd533739234d79b74afabfece1abfe9c6d52c83 (diff)
Merge tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux
Pull arm64 fixes from Will Deacon: "A mixture of ptdump, compat and MTE fixes that came in during the merge window: - Fix address handling of final memory region in ptdump - Fix emulation of decrementing load/store multiple from 32-bit task - Fix SCTLR context-switching for store-only MTE mode - Fix numerous issues in MTE selftests" * tag 'arm64-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux: selftests/arm64: Add MTE test config fragment selftests/arm64: Fix MTE prctl TAP plan selftests/arm64: Treat KSM merge_across_nodes as optional selftests/arm64: Print missing MTE TAP headers arm64: compat: Fix decrementing LDM/STM alignment emulation arm64: process: Fix context switching MTE store-only tag check KVM: arm64: ptdump: Flush the last region arm64: ptdump: Make note_page_flush() range aware
-rw-r--r--arch/arm64/include/asm/processor.h2
-rw-r--r--arch/arm64/include/asm/ptdump.h2
-rw-r--r--arch/arm64/kernel/compat_alignment.c4
-rw-r--r--arch/arm64/kvm/ptdump.c11
-rw-r--r--arch/arm64/mm/ptdump.c14
-rw-r--r--tools/testing/selftests/arm64/config17
-rw-r--r--tools/testing/selftests/arm64/mte/check_buffer_fill.c2
-rw-r--r--tools/testing/selftests/arm64/mte/check_child_memory.c2
-rw-r--r--tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c1
-rw-r--r--tools/testing/selftests/arm64/mte/check_hugetlb_options.c2
-rw-r--r--tools/testing/selftests/arm64/mte/check_ksm_options.c31
-rw-r--r--tools/testing/selftests/arm64/mte/check_mmap_options.c2
-rw-r--r--tools/testing/selftests/arm64/mte/check_prctl.c2
-rw-r--r--tools/testing/selftests/arm64/mte/check_tags_inclusion.c2
-rw-r--r--tools/testing/selftests/arm64/mte/check_user_mem.c2
15 files changed, 84 insertions, 12 deletions
diff --git a/arch/arm64/include/asm/processor.h b/arch/arm64/include/asm/processor.h
index c2a627f39314..6dfbcacd9ba0 100644
--- a/arch/arm64/include/asm/processor.h
+++ b/arch/arm64/include/asm/processor.h
@@ -264,7 +264,7 @@ static inline void task_set_sve_vl_onexec(struct task_struct *task,
#define SCTLR_USER_MASK \
(SCTLR_ELx_ENIA | SCTLR_ELx_ENIB | SCTLR_ELx_ENDA | SCTLR_ELx_ENDB | \
- SCTLR_EL1_TCF0_MASK)
+ SCTLR_EL1_TCF0_MASK | SCTLR_EL1_TCSO0_MASK)
static inline void arch_thread_struct_whitelist(unsigned long *offset,
unsigned long *size)
diff --git a/arch/arm64/include/asm/ptdump.h b/arch/arm64/include/asm/ptdump.h
index 50a195eda8ed..3aa8fa2fcec7 100644
--- a/arch/arm64/include/asm/ptdump.h
+++ b/arch/arm64/include/asm/ptdump.h
@@ -50,6 +50,8 @@ struct ptdump_pg_state {
const struct addr_marker *marker;
const struct mm_struct *mm;
unsigned long start_address;
+ /* exclusive end, ULONG_MAX represents an end at 1 << 64 */
+ unsigned long end_address;
int level;
ptval_t current_prot;
bool check_wx;
diff --git a/arch/arm64/kernel/compat_alignment.c b/arch/arm64/kernel/compat_alignment.c
index b68e1d328d4c..9b58d0aa38d2 100644
--- a/arch/arm64/kernel/compat_alignment.c
+++ b/arch/arm64/kernel/compat_alignment.c
@@ -114,8 +114,8 @@ do_alignment_ldrdstrd(unsigned long addr, u32 instr, struct pt_regs *regs)
static int
do_alignment_ldmstm(unsigned long addr, u32 instr, struct pt_regs *regs)
{
- unsigned int rd, rn, nr_regs, regbits;
- unsigned long eaddr, newaddr;
+ unsigned int rd, rn, regbits;
+ unsigned long eaddr, newaddr, nr_regs;
unsigned int val;
/* count the number of registers in the mask to be transferred */
diff --git a/arch/arm64/kvm/ptdump.c b/arch/arm64/kvm/ptdump.c
index c9140e22abcf..69899797dbad 100644
--- a/arch/arm64/kvm/ptdump.c
+++ b/arch/arm64/kvm/ptdump.c
@@ -130,7 +130,7 @@ static struct kvm_ptdump_guest_state *kvm_ptdump_parser_create(struct kvm_s2_mmu
}
st->ipa_marker[0].name = "Guest IPA";
- st->ipa_marker[1].start_address = BIT(pgtable->ia_bits);
+ st->ipa_marker[1].start_address = ULONG_MAX;
st->mmu = mmu;
return st;
@@ -148,18 +148,21 @@ static int kvm_ptdump_guest_show(struct seq_file *m, void *unused)
.flags = KVM_PGTABLE_WALK_LEAF,
};
+ guard(write_lock)(&kvm->mmu_lock);
st->parser_state = (struct ptdump_pg_state) {
.marker = &st->ipa_marker[0],
+ .end_address = BIT(mmu->pgt->ia_bits),
.level = -1,
.pg_level = &st->level[0],
.seq = m,
};
- write_lock(&kvm->mmu_lock);
ret = kvm_pgtable_walk(mmu->pgt, 0, BIT(mmu->pgt->ia_bits), &walker);
- write_unlock(&kvm->mmu_lock);
+ if (ret)
+ return ret;
+ note_page_flush(&st->parser_state.ptdump);
- return ret;
+ return 0;
}
static int kvm_ptdump_guest_open(struct inode *m, struct file *file)
diff --git a/arch/arm64/mm/ptdump.c b/arch/arm64/mm/ptdump.c
index 5a76c59b5ada..9d9bcb6738f4 100644
--- a/arch/arm64/mm/ptdump.c
+++ b/arch/arm64/mm/ptdump.c
@@ -278,9 +278,19 @@ void note_page_pgd(struct ptdump_state *pt_st, unsigned long addr, pgd_t pgd)
void note_page_flush(struct ptdump_state *pt_st)
{
+ struct ptdump_pg_state *st = container_of(pt_st, struct ptdump_pg_state, ptdump);
+ unsigned long end = st->end_address;
pte_t pte_zero = {0};
- note_page(pt_st, 0, -1, pte_val(pte_zero));
+ /*
+ * Address spaces that end at 1 << 64 have end_address == ULONG_MAX,
+ * but note_page() expects the exclusive end. In this case adjust end
+ * to the wraparound value 0.
+ */
+ if (end == ULONG_MAX)
+ end = 0;
+
+ note_page(pt_st, end, -1, pte_val(pte_zero));
}
void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
@@ -296,6 +306,7 @@ void ptdump_walk(struct seq_file *s, struct ptdump_info *info)
.marker = info->markers,
.mm = info->mm,
.pg_level = &kernel_pg_levels[0],
+ .end_address = end,
.level = -1,
.ptdump = {
.note_page_pte = note_page_pte,
@@ -337,6 +348,7 @@ bool ptdump_check_wx(void)
{ -1, NULL},
},
.pg_level = &kernel_pg_levels[0],
+ .end_address = ~0UL,
.level = -1,
.check_wx = true,
.ptdump = {
diff --git a/tools/testing/selftests/arm64/config b/tools/testing/selftests/arm64/config
new file mode 100644
index 000000000000..0fa975585392
--- /dev/null
+++ b/tools/testing/selftests/arm64/config
@@ -0,0 +1,17 @@
+CONFIG_ARM64_BTI=y
+CONFIG_ARM64_GCS=y
+CONFIG_ARM64_MTE=y
+CONFIG_ARM64_POE=y
+CONFIG_ARM64_PTR_AUTH=y
+CONFIG_ARM64_SME=y
+CONFIG_ARM64_SVE=y
+CONFIG_ARM64_TAGGED_ADDR_ABI=y
+CONFIG_HUGETLBFS=y
+CONFIG_KSM=y
+CONFIG_PROC_FS=y
+CONFIG_SECCOMP=y
+CONFIG_SECCOMP_FILTER=y
+CONFIG_SHMEM=y
+CONFIG_SYSCTL=y
+CONFIG_SYSFS=y
+CONFIG_TMPFS=y
diff --git a/tools/testing/selftests/arm64/mte/check_buffer_fill.c b/tools/testing/selftests/arm64/mte/check_buffer_fill.c
index ff4e07503349..039b1d7d8566 100644
--- a/tools/testing/selftests/arm64/mte/check_buffer_fill.c
+++ b/tools/testing/selftests/arm64/mte/check_buffer_fill.c
@@ -406,6 +406,8 @@ int main(int argc, char *argv[])
size_t page_size = getpagesize();
int item = ARRAY_SIZE(sizes);
+ ksft_print_header();
+
sizes[item - 3] = page_size - 1;
sizes[item - 2] = page_size;
sizes[item - 1] = page_size + 1;
diff --git a/tools/testing/selftests/arm64/mte/check_child_memory.c b/tools/testing/selftests/arm64/mte/check_child_memory.c
index 5e97ee792e4d..e6a8acca2a94 100644
--- a/tools/testing/selftests/arm64/mte/check_child_memory.c
+++ b/tools/testing/selftests/arm64/mte/check_child_memory.c
@@ -146,6 +146,8 @@ int main(int argc, char *argv[])
int err;
int item = ARRAY_SIZE(sizes);
+ ksft_print_header();
+
page_size = getpagesize();
if (!page_size) {
ksft_print_msg("ERR: Unable to get page size\n");
diff --git a/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c b/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
index 325bca0de0f6..d23f154d3288 100644
--- a/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
+++ b/tools/testing/selftests/arm64/mte/check_gcr_el1_cswitch.c
@@ -131,6 +131,7 @@ int main(int argc, char *argv[])
if (err)
return err;
+ ksft_print_header();
ksft_set_plan(1);
evaluate_test(mte_gcr_fork_test(),
diff --git a/tools/testing/selftests/arm64/mte/check_hugetlb_options.c b/tools/testing/selftests/arm64/mte/check_hugetlb_options.c
index aad1234c7e0f..23e4a7a9950c 100644
--- a/tools/testing/selftests/arm64/mte/check_hugetlb_options.c
+++ b/tools/testing/selftests/arm64/mte/check_hugetlb_options.c
@@ -230,6 +230,8 @@ int main(int argc, char *argv[])
void *map_ptr;
unsigned long map_size;
+ ksft_print_header();
+
err = mte_default_setup();
if (err)
return err;
diff --git a/tools/testing/selftests/arm64/mte/check_ksm_options.c b/tools/testing/selftests/arm64/mte/check_ksm_options.c
index 0cf5faef1724..4855b737d550 100644
--- a/tools/testing/selftests/arm64/mte/check_ksm_options.c
+++ b/tools/testing/selftests/arm64/mte/check_ksm_options.c
@@ -6,6 +6,7 @@
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
+#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -22,6 +23,20 @@
static size_t page_sz;
static unsigned long ksm_sysfs[5];
+static bool has_merge_across_nodes;
+
+static bool merge_across_nodes_available(void)
+{
+ const char *path = PATH_KSM "merge_across_nodes";
+
+ if (!access(path, R_OK | W_OK))
+ return true;
+ if (errno == ENOENT)
+ return false;
+
+ ksft_exit_skip("Unable to read and write %s: %s\n", path,
+ strerror(errno));
+}
static unsigned long read_sysfs(char *str)
{
@@ -56,8 +71,10 @@ static void write_sysfs(char *str, unsigned long val)
static void mte_ksm_setup(void)
{
- ksm_sysfs[0] = read_sysfs(PATH_KSM "merge_across_nodes");
- write_sysfs(PATH_KSM "merge_across_nodes", 1);
+ if (has_merge_across_nodes) {
+ ksm_sysfs[0] = read_sysfs(PATH_KSM "merge_across_nodes");
+ write_sysfs(PATH_KSM "merge_across_nodes", 1);
+ }
ksm_sysfs[1] = read_sysfs(PATH_KSM "sleep_millisecs");
write_sysfs(PATH_KSM "sleep_millisecs", 0);
ksm_sysfs[2] = read_sysfs(PATH_KSM "run");
@@ -70,7 +87,8 @@ static void mte_ksm_setup(void)
static void mte_ksm_restore(void)
{
- write_sysfs(PATH_KSM "merge_across_nodes", ksm_sysfs[0]);
+ if (has_merge_across_nodes)
+ write_sysfs(PATH_KSM "merge_across_nodes", ksm_sysfs[0]);
write_sysfs(PATH_KSM "sleep_millisecs", ksm_sysfs[1]);
write_sysfs(PATH_KSM "run", ksm_sysfs[2]);
write_sysfs(PATH_KSM "max_page_sharing", ksm_sysfs[3]);
@@ -132,9 +150,16 @@ int main(int argc, char *argv[])
{
int err;
+ ksft_print_header();
+
err = mte_default_setup();
if (err)
return err;
+
+ if (geteuid() != 0)
+ ksft_exit_skip("Please run the test as root\n");
+
+ has_merge_across_nodes = merge_across_nodes_available();
page_sz = getpagesize();
if (!page_sz) {
ksft_print_msg("ERR: Unable to get page size\n");
diff --git a/tools/testing/selftests/arm64/mte/check_mmap_options.c b/tools/testing/selftests/arm64/mte/check_mmap_options.c
index c100af3012cb..492f2cd41f43 100644
--- a/tools/testing/selftests/arm64/mte/check_mmap_options.c
+++ b/tools/testing/selftests/arm64/mte/check_mmap_options.c
@@ -945,6 +945,8 @@ int main(int argc, char *argv[])
},
};
+ ksft_print_header();
+
err = mte_default_setup();
if (err)
return err;
diff --git a/tools/testing/selftests/arm64/mte/check_prctl.c b/tools/testing/selftests/arm64/mte/check_prctl.c
index f7f320defa7b..d16a91117eef 100644
--- a/tools/testing/selftests/arm64/mte/check_prctl.c
+++ b/tools/testing/selftests/arm64/mte/check_prctl.c
@@ -119,7 +119,7 @@ int main(void)
int i;
ksft_print_header();
- ksft_set_plan(ARRAY_SIZE(mte_modes));
+ ksft_set_plan(ARRAY_SIZE(mte_modes) + 1);
check_basic_read();
for (i = 0; i < ARRAY_SIZE(mte_modes); i++)
diff --git a/tools/testing/selftests/arm64/mte/check_tags_inclusion.c b/tools/testing/selftests/arm64/mte/check_tags_inclusion.c
index 4b764f2a8185..6b4fa6705d7c 100644
--- a/tools/testing/selftests/arm64/mte/check_tags_inclusion.c
+++ b/tools/testing/selftests/arm64/mte/check_tags_inclusion.c
@@ -175,6 +175,8 @@ int main(int argc, char *argv[])
{
int err;
+ ksft_print_header();
+
err = mte_default_setup();
if (err)
return err;
diff --git a/tools/testing/selftests/arm64/mte/check_user_mem.c b/tools/testing/selftests/arm64/mte/check_user_mem.c
index fb7936c4e097..af343aa61732 100644
--- a/tools/testing/selftests/arm64/mte/check_user_mem.c
+++ b/tools/testing/selftests/arm64/mte/check_user_mem.c
@@ -201,6 +201,8 @@ int main(int argc, char *argv[])
int tag_offsets[] = {page_sz, MT_GRANULE_SIZE};
char test_name[TEST_NAME_MAX];
+ ksft_print_header();
+
page_sz = getpagesize();
if (!page_sz) {
ksft_print_msg("ERR: Unable to get page size\n");