mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH] m68k: Implement kernel memory protection
@ 2025-11-16 22:05 Daniel Palmer
  2025-11-17  3:04 ` Greg Ungerer
  0 siblings, 1 reply; 7+ messages in thread
From: Daniel Palmer @ 2025-11-16 22:05 UTC (permalink / raw)
  To: geert, linux-m68k; +Cc: linux-kernel, Daniel Palmer

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@thingy.jp>
---
 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
-- 
2.51.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] m68k: Implement kernel memory protection
  2025-11-16 22:05 [PATCH] m68k: Implement kernel memory protection Daniel Palmer
@ 2025-11-17  3:04 ` Greg Ungerer
  2025-11-17  9:42   ` Daniel Palmer
  0 siblings, 1 reply; 7+ messages in thread
From: Greg Ungerer @ 2025-11-17  3:04 UTC (permalink / raw)
  To: Daniel Palmer, geert, linux-m68k; +Cc: linux-kernel

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@thingy.jp>

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@linux-m68k.org>

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


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] m68k: Implement kernel memory protection
  2025-11-17  3:04 ` Greg Ungerer
@ 2025-11-17  9:42   ` Daniel Palmer
  2025-11-17 17:41     ` Michael Schmitz
  0 siblings, 1 reply; 7+ messages in thread
From: Daniel Palmer @ 2025-11-17  9:42 UTC (permalink / raw)
  To: Greg Ungerer; +Cc: geert, linux-m68k, linux-kernel

Hi Greg,

On Mon, 17 Nov 2025 at 12:04, Greg Ungerer <gerg@kernel.org> wrote:
> Seems to work fine on real ColdFire hardware (tested on an M5475 -
> that is one that has an MMU):

Thanks for testing. I just tried on my MVME147 (030) and the marking
RO seems to work but it locks up starting init:

[    6.520000] devtmpfs: mounted
[    6.540000] Freeing unused kernel image (initmem) memory: 120K
[    6.550000] Write protecting kernel text: 0x2000 - 0x3676f0
[    6.560000] Write protecting kernel read-only data: 0x36a000 - 0x3be000
[    6.580000] rodata_test: all tests were successful
[    6.590000] Run /sbin/init as init process
<dead>

My 060 Amiga 4000 seems ok. So I guess some 030 mmu issue. I'll debug
and send again..

Thanks!

Daniel

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] m68k: Implement kernel memory protection
  2025-11-17  9:42   ` Daniel Palmer
@ 2025-11-17 17:41     ` Michael Schmitz
  2025-11-17 22:01       ` Daniel Palmer
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Schmitz @ 2025-11-17 17:41 UTC (permalink / raw)
  To: Daniel Palmer, Greg Ungerer; +Cc: geert, linux-m68k, linux-kernel

Daniel,

take a look at arch/m68k/kernel/head.S - 030 MMU setup uses early 
termination descriptors for kernel memory, _not_ regular page tables.

Cheers,

	Michael


Am 17.11.2025 um 22:42 schrieb Daniel Palmer:
> Hi Greg,
>
> On Mon, 17 Nov 2025 at 12:04, Greg Ungerer <gerg@kernel.org> wrote:
>> Seems to work fine on real ColdFire hardware (tested on an M5475 -
>> that is one that has an MMU):
>
> Thanks for testing. I just tried on my MVME147 (030) and the marking
> RO seems to work but it locks up starting init:
>
> [    6.520000] devtmpfs: mounted
> [    6.540000] Freeing unused kernel image (initmem) memory: 120K
> [    6.550000] Write protecting kernel text: 0x2000 - 0x3676f0
> [    6.560000] Write protecting kernel read-only data: 0x36a000 - 0x3be000
> [    6.580000] rodata_test: all tests were successful
> [    6.590000] Run /sbin/init as init process
> <dead>
>
> My 060 Amiga 4000 seems ok. So I guess some 030 mmu issue. I'll debug
> and send again..
>
> Thanks!
>
> Daniel
>

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] m68k: Implement kernel memory protection
  2025-11-17 17:41     ` Michael Schmitz
@ 2025-11-17 22:01       ` Daniel Palmer
  2025-11-17 23:14         ` Michael Schmitz
  0 siblings, 1 reply; 7+ messages in thread
From: Daniel Palmer @ 2025-11-17 22:01 UTC (permalink / raw)
  To: Michael Schmitz; +Cc: Greg Ungerer, geert, linux-m68k, linux-kernel

Hi Michael,

On Tue, 18 Nov 2025 at 02:41, Michael Schmitz <schmitzmic@gmail.com> wrote:
>
> Daniel,
>
> take a look at arch/m68k/kernel/head.S - 030 MMU setup uses early
> termination descriptors for kernel memory, _not_ regular page tables.

Yeah, so when setting write protect on the last bit of either the text
or the rodata the end is getting rounded up to the next 256KB and
write protecting too much.
I worked that out and adding some alignment in the linker script so
that everything after the places getting write protected is pushed off
to the next 256KB does fix it but wastes some memory.
Looking at the manual for the 68030 it seems like early termination
descriptors can have a limit so I could set the alignment, fix up the
limit and then give the wasted memory back. But we are using the short
format that doesn't have the limit field.

Cheers,

Daniel

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] m68k: Implement kernel memory protection
  2025-11-17 22:01       ` Daniel Palmer
@ 2025-11-17 23:14         ` Michael Schmitz
  2025-11-18 10:55           ` Daniel Palmer
  0 siblings, 1 reply; 7+ messages in thread
From: Michael Schmitz @ 2025-11-17 23:14 UTC (permalink / raw)
  To: Daniel Palmer; +Cc: Greg Ungerer, geert, linux-m68k, linux-kernel

Daniel,

it might be possible to fix up the last (partially used) descriptor from 
C code, if there's a limit field. I find the head.S assembly too much of 
a headache to change, but that would certainly be safer.

Wasting up to 256 kB is a pain on 030. Maybe rather skip protecting the 
last section?

(* ducks ... *)

Cheers,

     Michael

On 18/11/25 11:01, Daniel Palmer wrote:
> Hi Michael,
>
> On Tue, 18 Nov 2025 at 02:41, Michael Schmitz <schmitzmic@gmail.com> wrote:
>> Daniel,
>>
>> take a look at arch/m68k/kernel/head.S - 030 MMU setup uses early
>> termination descriptors for kernel memory, _not_ regular page tables.
> Yeah, so when setting write protect on the last bit of either the text
> or the rodata the end is getting rounded up to the next 256KB and
> write protecting too much.
> I worked that out and adding some alignment in the linker script so
> that everything after the places getting write protected is pushed off
> to the next 256KB does fix it but wastes some memory.
> Looking at the manual for the 68030 it seems like early termination
> descriptors can have a limit so I could set the alignment, fix up the
> limit and then give the wasted memory back. But we are using the short
> format that doesn't have the limit field.
>
> Cheers,
>
> Daniel

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH] m68k: Implement kernel memory protection
  2025-11-17 23:14         ` Michael Schmitz
@ 2025-11-18 10:55           ` Daniel Palmer
  0 siblings, 0 replies; 7+ messages in thread
From: Daniel Palmer @ 2025-11-18 10:55 UTC (permalink / raw)
  To: Michael Schmitz; +Cc: Greg Ungerer, geert, linux-m68k, linux-kernel

Hi Michael,

On Tue, 18 Nov 2025 at 08:14, Michael Schmitz <schmitzmic@gmail.com> wrote:
> I find the head.S assembly too much of
> a headache to change, but that would certainly be safer.

Yeah, I'm going to avoid touching that if possible. :)
I think I can be done from C and I think I have something working but
it's not pretty.

> Wasting up to 256 kB is a pain on 030. Maybe rather skip protecting the
> last section?

Right now it's more than that as the end of .text needs to be aligned
to 256KB and then the start and end of the RO data need to be aligned.
It's about 512KB waste on my MVME147. If EXCEPTION_TABLE(16) was moved
so .text and RO data become a contiguous region that'd make it always
<256KB padding.

Cheers,

Daniel

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2025-11-18 10:55 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-11-16 22:05 [PATCH] m68k: Implement kernel memory protection Daniel Palmer
2025-11-17  3:04 ` Greg Ungerer
2025-11-17  9:42   ` Daniel Palmer
2025-11-17 17:41     ` Michael Schmitz
2025-11-17 22:01       ` Daniel Palmer
2025-11-17 23:14         ` Michael Schmitz
2025-11-18 10:55           ` Daniel Palmer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®