Re: [PATCH] m68k: Implement kernel memory protection
From: Greg Ungerer
Date: Sun Nov 16 2025 - 22:10:27 EST
Hi Daniel,
On 17/11/25 08:05, Daniel Palmer wrote:
Every time I boot linux on my various m68k machines I see
"This architecture does not have kernel memory protection."
I wondered why this was as some of my machines even have one of
those fancy MMU doodads. I worked out it was because we don't have
CONFIG_ARCH_HAS_STRICT_KERNEL_RWX, found kernel_set_cachemode()
seemed like it had the code for setting some extra flags for
kernel pages and turned that into something that sets write
protect for kernel pages.
So now we can make CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y and
provide mark_rodata_ro() to mark the kernel text and rodata
as write protected.
The test enabled by CONFIG_DEBUG_RODATA_TEST=y says this is
working, but I've only tested on the virt machine.
Signed-off-by: Daniel Palmer <daniel@xxxxxxxxx>
Seems to work fine on real ColdFire hardware (tested on an M5475 -
that is one that has an MMU):
...
VFS: Mounted root (romfs filesystem) readonly on device 31:0.
Freeing unused kernel image (initmem) memory: 88K
Write protecting kernel text: 0x22000 - 0x393f70
Write protecting kernel read-only data: 0x394000 - 0x450000
Run /sbin/init as init process
Run /etc/init as init process
Run /bin/init as init process
process '/bin/init' started with executable stack
...
Tested-by: Greg Ungerer <gerg@xxxxxxxxxxxxxx>
Regards
Greg
---
arch/m68k/Kconfig | 1 +
arch/m68k/mm/init.c | 69 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 70 insertions(+)
diff --git a/arch/m68k/Kconfig b/arch/m68k/Kconfig
index 11835eb59d94..2137fd19ffbd 100644
--- a/arch/m68k/Kconfig
+++ b/arch/m68k/Kconfig
@@ -8,6 +8,7 @@ config M68K
select ARCH_HAS_CPU_FINALIZE_INIT if MMU
select ARCH_HAS_CURRENT_STACK_POINTER
select ARCH_HAS_DMA_PREP_COHERENT if M68K_NONCOHERENT_DMA && !COLDFIRE
+ select ARCH_HAS_STRICT_KERNEL_RWX if MMU
select ARCH_HAS_SYNC_DMA_FOR_DEVICE if M68K_NONCOHERENT_DMA
select ARCH_HAVE_NMI_SAFE_CMPXCHG if RMW_INSNS
select ARCH_MIGHT_HAVE_PC_PARPORT if ISA
diff --git a/arch/m68k/mm/init.c b/arch/m68k/mm/init.c
index 488411af1b3f..bc1147f25624 100644
--- a/arch/m68k/mm/init.c
+++ b/arch/m68k/mm/init.c
@@ -123,3 +123,72 @@ void __init mem_init(void)
{
init_pointer_tables();
}
+
+#ifdef CONFIG_MMU
+/*
+ * Based on (basically copy/pasted) kernel_set_cachemode() because
+ * presumably that is correct and covers the required differences.
+ */
+static void __mark_ro_data(unsigned long virtaddr, ssize_t size)
+{
+ pgd_t *pgd_dir;
+ p4d_t *p4d_dir;
+ pud_t *pud_dir;
+ pmd_t *pmd_dir;
+ pte_t *pte_dir;
+
+ while (size > 0) {
+ pgd_dir = pgd_offset_k(virtaddr);
+ p4d_dir = p4d_offset(pgd_dir, virtaddr);
+ pud_dir = pud_offset(p4d_dir, virtaddr);
+ if (pud_bad(*pud_dir)) {
+ pud_clear(pud_dir);
+ return;
+ }
+ pmd_dir = pmd_offset(pud_dir, virtaddr);
+
+#if CONFIG_PGTABLE_LEVELS == 3
+ if (CPU_IS_020_OR_030) {
+ unsigned long pmd = pmd_val(*pmd_dir);
+
+ if ((pmd & _DESCTYPE_MASK) == _PAGE_PRESENT) {
+ *pmd_dir = __pmd(pmd | _PAGE_RONLY);
+ virtaddr += PMD_SIZE;
+ size -= PMD_SIZE;
+ continue;
+ }
+ }
+#endif
+
+ if (pmd_bad(*pmd_dir)) {
+ pmd_clear(pmd_dir);
+ return;
+ }
+ pte_dir = pte_offset_kernel(pmd_dir, virtaddr);
+
+ set_pte(pte_dir, pte_wrprotect(*pte_dir));
+ virtaddr += PAGE_SIZE;
+ size -= PAGE_SIZE;
+ }
+}
+
+void mark_rodata_ro(void)
+{
+ unsigned long start;
+ unsigned long end;
+
+ /* kernel text - kernel_pg_dir lives in the first page, so skip that */
+ start = (unsigned long) _stext + PAGE_SIZE;
+ end = (unsigned long) _etext;
+ pr_info("Write protecting kernel text: 0x%lx - 0x%lx\n", start, end);
+ __mark_ro_data(start, end - start);
+
+ /* ro data */
+ start = (unsigned long) __start_rodata;
+ end = (unsigned long) __end_rodata;
+ pr_info("Write protecting kernel read-only data: 0x%lx - 0x%lx\n", start, end);
+ __mark_ro_data(start, end - start);
+
+ flush_tlb_all();
+}
+#endif