Greetings!
In a conversation with Andrew about his page table check code for
PowerPC he found out there seems to be a general problem of the code
patching code on ppc:
> I note that STATIC_CALL_SELFTEST seems to fail, which could be but isn't
> necessarily related.
> Specifically, it looks like code patching fails if the instruction
being patched
> is in an __init function, and in this case, that occurs due to
> page_table_check_pte_clear() being inlined into ptep_clear(), which
is in turn
> inlined into pte_clear_tests() in debug_vm_pgtable.c which is marked
__init.
> Very curious!
Indeed, when I enable STATIC_CALL_SELFTEST=y on my G4 the kernel fails
to boot at an early stage, the OpenFirmware console showing only:
done
found display : /pci@f0000000/ATY,AlteracParent@10/ATY,Alterac_B@1,
opening...
Without STATIC_CALL_SELFTEST the kernel boots just fine.
Kernel .config attached.
Regards,
Erhard
Hi Erhard,
Le 04/09/2025 à 00:44, Erhard Furtner a écrit :
Greetings!
In a conversation with Andrew about his page table check code for
PowerPC he found out there seems to be a general problem of the code
patching code on ppc:
> I note that STATIC_CALL_SELFTEST seems to fail, which could be but isn't
> necessarily related.
> Specifically, it looks like code patching fails if the instruction
being patched
> is in an __init function, and in this case, that occurs due to
> page_table_check_pte_clear() being inlined into ptep_clear(), which
is in turn
> inlined into pte_clear_tests() in debug_vm_pgtable.c which is marked
__init.
> Very curious!
Indeed, when I enable STATIC_CALL_SELFTEST=y on my G4 the kernel fails
to boot at an early stage, the OpenFirmware console showing only:
done
found display : /pci@f0000000/ATY,AlteracParent@10/ATY,Alterac_B@1,
opening...
Without STATIC_CALL_SELFTEST the kernel boots just fine.
Kernel .config attached.
With your .config, on QEMU I get the following. I will investigate.
[ 0.000000] Kernel panic - not syncing: arch_static_call_transform:
patching failed func_a+0x0/0x10 at 0x0
[ 0.000000] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted
6.17.0-rc4-PMacG4+ #1628 PREEMPTLAZY
[ 0.000000] Hardware name: PowerMac3,1 7400 0xc0209 PowerMac
[ 0.000000] Call Trace:
[ 0.000000] [c0dd7e60] [c0810e54] dump_stack_lvl+0x70/0x8c (unreliable)
[ 0.000000] [c0dd7e80] [c004b220] vpanic+0x114/0x2e4
[ 0.000000] [c0dd7eb0] [c004b45c] arch_set_bit+0x0/0x38
[ 0.000000] [c0dd7f00] [c00155fc] arch_static_call_transform+0xec/0x1e8
[ 0.000000] [c0dd7f30] [c0176944] __static_call_init+0x110/0x288
[ 0.000000] [c0dd7f80] [c0c19ab0] static_call_init+0x4c/0x9c
[ 0.000000] [c0dd7fa0] [c0c02e00] start_kernel+0x7c/0x664
[ 0.000000] [c0dd7ff0] [000035d0] 0x35d0
[ 0.000000] Rebooting in 40 seconds..
Christophe
Hi Erhard,
Le 04/09/2025 à 00:44, Erhard Furtner a écrit :
quoted
Greetings!
In a conversation with Andrew about his page table check code for PowerPC he found out there seems to be a general problem of the code patching code on ppc:
> I note that STATIC_CALL_SELFTEST seems to fail, which could be but isn't
> necessarily related.
> Specifically, it looks like code patching fails if the instruction being patched
> is in an __init function, and in this case, that occurs due to
> page_table_check_pte_clear() being inlined into ptep_clear(), which is in turn
> inlined into pte_clear_tests() in debug_vm_pgtable.c which is marked __init.
> Very curious!
Indeed, when I enable STATIC_CALL_SELFTEST=y on my G4 the kernel fails to boot at an early stage, the OpenFirmware console showing only:
done
found display : /pci@f0000000/ATY,AlteracParent@10/ATY,Alterac_B@1, opening...
Without STATIC_CALL_SELFTEST the kernel boots just fine.
Kernel .config attached.
With your .config, on QEMU I get the following. I will investigate.
[ 0.000000] Kernel panic - not syncing: arch_static_call_transform: patching failed func_a+0x0/0x10 at 0x0
[ 0.000000] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.17.0-rc4-PMacG4+ #1628 PREEMPTLAZY
[ 0.000000] Hardware name: PowerMac3,1 7400 0xc0209 PowerMac
[ 0.000000] Call Trace:
I am using qemu with -M mac99 and it boots
Hardware name: PowerMac3,1 7400 0xc0209 PowerMac
printk: legacy bootconsole [udbg0] enabled
-----------------------------------------------------
phys_mem_size = 0x8000000
dcache_bsize = 0x20
I am missing something here. Digging some more
Maddy
Le 04/09/2025 à 10:40, Madhavan Srinivasan a écrit :
quoted
On 9/4/25 1:01 PM, Christophe Leroy wrote:
quoted
Hi Erhard,
Le 04/09/2025 à 00:44, Erhard Furtner a écrit :
quoted
Greetings!
In a conversation with Andrew about his page table check code for PowerPC he found out there seems to be a general problem of the code patching code on ppc:
> I note that STATIC_CALL_SELFTEST seems to fail, which could be but isn't
> necessarily related.
> Specifically, it looks like code patching fails if the instruction being patched
> is in an __init function, and in this case, that occurs due to
> page_table_check_pte_clear() being inlined into ptep_clear(), which is in turn
> inlined into pte_clear_tests() in debug_vm_pgtable.c which is marked __init.
> Very curious!
Indeed, when I enable STATIC_CALL_SELFTEST=y on my G4 the kernel fails to boot at an early stage, the OpenFirmware console showing only:
done
found display : /pci@f0000000/ATY,AlteracParent@10/ATY,Alterac_B@1, opening...
Without STATIC_CALL_SELFTEST the kernel boots just fine.
Kernel .config attached.
With your .config, on QEMU I get the following. I will investigate.
[ 0.000000] Kernel panic - not syncing: arch_static_call_transform: patching failed func_a+0x0/0x10 at 0x0
[ 0.000000] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.17.0-rc4-PMacG4+ #1628 PREEMPTLAZY
[ 0.000000] Hardware name: PowerMac3,1 7400 0xc0209 PowerMac
[ 0.000000] Call Trace:
I am using qemu with -M mac99 and it boots
Hardware name: PowerMac3,1 7400 0xc0209 PowerMac
printk: legacy bootconsole [udbg0] enabled
-----------------------------------------------------
phys_mem_size = 0x8000000
dcache_bsize = 0x20
I am missing something here. Digging some more
Hi Again,
Le 04/09/2025 à 09:31, Christophe Leroy a écrit :
Hi Erhard,
Le 04/09/2025 à 00:44, Erhard Furtner a écrit :
quoted
Greetings!
In a conversation with Andrew about his page table check code for
PowerPC he found out there seems to be a general problem of the code
patching code on ppc:
> I note that STATIC_CALL_SELFTEST seems to fail, which could be but
isn't
> necessarily related.
> Specifically, it looks like code patching fails if the instruction
being patched
> is in an __init function, and in this case, that occurs due to
> page_table_check_pte_clear() being inlined into ptep_clear(), which
is in turn
> inlined into pte_clear_tests() in debug_vm_pgtable.c which is
marked __init.
> Very curious!
Indeed, when I enable STATIC_CALL_SELFTEST=y on my G4 the kernel fails
to boot at an early stage, the OpenFirmware console showing only:
done
found display : /pci@f0000000/ATY,AlteracParent@10/ATY,Alterac_B@1,
opening...
Without STATIC_CALL_SELFTEST the kernel boots just fine.
Kernel .config attached.
With your .config, on QEMU I get the following. I will investigate.
[ 0.000000] Kernel panic - not syncing: arch_static_call_transform:
patching failed func_a+0x0/0x10 at 0x0
[ 0.000000] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.17.0-
rc4-PMacG4+ #1628 PREEMPTLAZY
[ 0.000000] Hardware name: PowerMac3,1 7400 0xc0209 PowerMac
[ 0.000000] Call Trace:
[ 0.000000] [c0dd7e60] [c0810e54] dump_stack_lvl+0x70/0x8c (unreliable)
[ 0.000000] [c0dd7e80] [c004b220] vpanic+0x114/0x2e4
[ 0.000000] [c0dd7eb0] [c004b45c] arch_set_bit+0x0/0x38
[ 0.000000] [c0dd7f00] [c00155fc] arch_static_call_transform+0xec/0x1e8
[ 0.000000] [c0dd7f30] [c0176944] __static_call_init+0x110/0x288
[ 0.000000] [c0dd7f80] [c0c19ab0] static_call_init+0x4c/0x9c
[ 0.000000] [c0dd7fa0] [c0c02e00] start_kernel+0x7c/0x664
[ 0.000000] [c0dd7ff0] [000035d0] 0x35d0
[ 0.000000] Rebooting in 40 seconds..
The problem comes from the following in arch/powerpc/include/asm/pgtable.h :
/*
* Protection used for kernel text. We want the debuggers to be able to
* set breakpoints anywhere, so don't write protect the kernel text
* on platforms where such control is possible.
*/
#if defined(CONFIG_KGDB) || defined(CONFIG_XMON) ||
defined(CONFIG_BDI_SWITCH) || \
defined(CONFIG_KPROBES) || defined(CONFIG_DYNAMIC_FTRACE)
#define PAGE_KERNEL_TEXT PAGE_KERNEL_X
#else
#define PAGE_KERNEL_TEXT PAGE_KERNEL_ROX
#endif
I don't know what the best fix is at the moment. This probably comes
from the old days when we didn't have CONFIG_STRICT_KERNEL_RWX. Would it
nowadays be acceptable to inconditionnally set:
#define PAGE_KERNEL_TEXT PAGE_KERNEL_X
It propably would, taking into account it is used exclusively on powerpc/32:
arch/powerpc/mm/book3s32/mmu.c: setibat(i++, PAGE_OFFSET + base,
base, size, PAGE_KERNEL_TEXT);
arch/powerpc/mm/book3s32/mmu.c: setibat(i++, PAGE_OFFSET + base,
base, size, PAGE_KERNEL_TEXT);
arch/powerpc/mm/pgtable_32.c: map_kernel_page(v, p, ktext ?
PAGE_KERNEL_TEXT : PAGE_KERNEL);
Christophe
From: Andrew Donnellan <hidden> Date: 2025-09-04 09:58:11
On Thu, 2025-09-04 at 14:33 +0530, Madhavan Srinivasan wrote:
quoted
quoted
I am using qemu with -M mac99 and it boots
Hardware name: PowerMac3,1 7400 0xc0209 PowerMac
printk: legacy bootconsole [udbg0] enabled
-----------------------------------------------------
phys_mem_size = 0x8000000
dcache_bsize = 0x20
I am missing something here. Digging some more
Did you use the .config provided by Erhard ?
Oops. sorry for the noise
I noticed that I don't hit this using defconfigs (with JUMP_LABEL and
STATIC_CALL_SELFTEST manually enabled), so I did some config bisection with
Erhard's config.
It looks like you need CONFIG_KFENCE=y, and CONFIG_XMON=n.
Attached is a minimal config for current mainline, allnoconfig + CONFIG_PPC_PMAC
(so I can get console output in qemu) + CONFIG_KFENCE + CONFIG_JUMP_LABEL +
CONFIG_STATIC_CALL_SELFTEST.
Running with qemu-system-ppc -M mac99, we get the below.
Christophe: I'm not exactly sure what changes when you enable/disable KFENCE on
book3s32, but it looks to me like it affects whether .init.text is mapped using
BATs or not?
Andrew
---------------------------------
quoted
=============================================================
OpenBIOS 1.1 [Feb 12 2025 13:02]
Configuration device id QEMU version 1 machine id 1
CPUs: 1
Memory: 128M
UUID: 00000000-0000-0000-0000-000000000000
CPU type PowerPC,G4
milliseconds isn't unique.
Welcome to OpenBIOS v1.1 built on Feb 12 2025 13:02
Le 04/09/2025 à 10:40, Madhavan Srinivasan a écrit :
On 9/4/25 1:01 PM, Christophe Leroy wrote:
quoted
Hi Erhard,
Le 04/09/2025 à 00:44, Erhard Furtner a écrit :
quoted
Greetings!
In a conversation with Andrew about his page table check code for PowerPC he found out there seems to be a general problem of the code patching code on ppc:
> I note that STATIC_CALL_SELFTEST seems to fail, which could be but isn't
> necessarily related.
> Specifically, it looks like code patching fails if the instruction being patched
> is in an __init function, and in this case, that occurs due to
> page_table_check_pte_clear() being inlined into ptep_clear(), which is in turn
> inlined into pte_clear_tests() in debug_vm_pgtable.c which is marked __init.
> Very curious!
Indeed, when I enable STATIC_CALL_SELFTEST=y on my G4 the kernel fails to boot at an early stage, the OpenFirmware console showing only:
done
found display : /pci@f0000000/ATY,AlteracParent@10/ATY,Alterac_B@1, opening...
Without STATIC_CALL_SELFTEST the kernel boots just fine.
Kernel .config attached.
With your .config, on QEMU I get the following. I will investigate.
[ 0.000000] Kernel panic - not syncing: arch_static_call_transform: patching failed func_a+0x0/0x10 at 0x0
[ 0.000000] CPU: 0 UID: 0 PID: 0 Comm: swapper Not tainted 6.17.0-rc4-PMacG4+ #1628 PREEMPTLAZY
[ 0.000000] Hardware name: PowerMac3,1 7400 0xc0209 PowerMac
[ 0.000000] Call Trace:
I am using qemu with -M mac99 and it boots
Hardware name: PowerMac3,1 7400 0xc0209 PowerMac
printk: legacy bootconsole [udbg0] enabled
-----------------------------------------------------
phys_mem_size = 0x8000000
dcache_bsize = 0x20
I am missing something here. Digging some more
Did you use the .config provided by Erhard ?
Christophe
On Thu, 2025-09-04 at 14:33 +0530, Madhavan Srinivasan wrote:
quoted
quoted
quoted
I am using qemu with -M mac99 and it boots
Hardware name: PowerMac3,1 7400 0xc0209 PowerMac
printk: legacy bootconsole [udbg0] enabled
-----------------------------------------------------
phys_mem_size = 0x8000000
dcache_bsize = 0x20
I am missing something here. Digging some more
Did you use the .config provided by Erhard ?
Oops. sorry for the noise
I noticed that I don't hit this using defconfigs (with JUMP_LABEL and
STATIC_CALL_SELFTEST manually enabled), so I did some config bisection with
Erhard's config.
It looks like you need CONFIG_KFENCE=y, and CONFIG_XMON=n.
Attached is a minimal config for current mainline, allnoconfig + CONFIG_PPC_PMAC
(so I can get console output in qemu) + CONFIG_KFENCE + CONFIG_JUMP_LABEL +
CONFIG_STATIC_CALL_SELFTEST.
Running with qemu-system-ppc -M mac99, we get the below.
Christophe: I'm not exactly sure what changes when you enable/disable KFENCE on
book3s32, but it looks to me like it affects whether .init.text is mapped using
BATs or not?
As I said in previous response, the problem seems to come from here:
arch/powerpc/include/asm/pgtable.h :
/*
* Protection used for kernel text. We want the debuggers to be able to
* set breakpoints anywhere, so don't write protect the kernel text
* on platforms where such control is possible.
*/
#if defined(CONFIG_KGDB) || defined(CONFIG_XMON) ||
defined(CONFIG_BDI_SWITCH) || \
defined(CONFIG_KPROBES) || defined(CONFIG_DYNAMIC_FTRACE)
#define PAGE_KERNEL_TEXT PAGE_KERNEL_X
#else
#define PAGE_KERNEL_TEXT PAGE_KERNEL_ROX
#endif
If you have neither CONFIG_KGDB nor CONFIG_XMON nor CONFIG_BDI_SWITCH
nor CONFIG_KPROBES or CONFIG_DYNAMIC_FTRACE, kernel text is mapped
Read-Only.
Using BATs or not shouldn't make any difference because:
With BATs we do:
arch/powerpc/mm/book3s32/mmu.c: setibat(i++, PAGE_OFFSET + base,
base, size, PAGE_KERNEL_TEXT);
arch/powerpc/mm/book3s32/mmu.c: setibat(i++, PAGE_OFFSET + base,
base, size, PAGE_KERNEL_TEXT);
Without BATs we do:
arch/powerpc/mm/pgtable_32.c: map_kernel_page(v, p, ktext ?
PAGE_KERNEL_TEXT : PAGE_KERNEL);
Andrew
---------------------------------
quoted
quoted
=============================================================
OpenBIOS 1.1 [Feb 12 2025 13:02]
Configuration device id QEMU version 1 machine id 1
CPUs: 1
Memory: 128M
UUID: 00000000-0000-0000-0000-000000000000
CPU type PowerPC,G4
milliseconds isn't unique.
Welcome to OpenBIOS v1.1 built on Feb 12 2025 13:02
PAGE_KERNEL_TEXT is an old macro that is used to tell kernel whether
kernel text has to be mapped read-only or read-write based on build
time options.
But nowadays, with functionnalities like jump_labels, static links,
etc ... more only less all kernels need to be read-write at some
point, and some combinations of configs failed to work due to
innacurate setting of PAGE_KERNEL_TEXT. On the other hand, today
we have CONFIG_STRICT_KERNEL_RWX which implements a more controlled
access to kernel modifications.
Instead of trying to keep PAGE_KERNEL_TEXT accurate with all
possible options that may imply kernel text modification, always
set kernel text read-write at startup and rely on
CONFIG_STRICT_KERNEL_RWX to provide accurate protection.
Reported-by: Erhard Furtner <redacted>
Closes: https://lore.kernel.org/all/342b4120-911c-4723-82ec-d8c9b03a8aef@mailbox.org/
Signed-off-by: Christophe Leroy <redacted>
---
arch/powerpc/include/asm/pgtable.h | 12 ------------
arch/powerpc/mm/book3s32/mmu.c | 4 ++--
arch/powerpc/mm/pgtable_32.c | 2 +-
3 files changed, 3 insertions(+), 15 deletions(-)
@@ -204,7 +204,7 @@ int mmu_mark_initmem_nx(void)for(i=0;i<nb-1&&base<top;){size=bat_block_size(base,top);-setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_TEXT);+setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_X);base+=size;}if(base<top){
@@ -215,7 +215,7 @@ int mmu_mark_initmem_nx(void)pr_warn("Some RW data is getting mapped X. ""Adjust CONFIG_DATA_SHIFT to avoid that.\n");}-setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_TEXT);+setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_X);base+=size;}for(;i<nb;i++)
PAGE_KERNEL_TEXT is an old macro that is used to tell kernel whether
kernel text has to be mapped read-only or read-write based on build
time options.
But nowadays, with functionnalities like jump_labels, static links,
etc ... more only less all kernels need to be read-write at some
point, and some combinations of configs failed to work due to
innacurate setting of PAGE_KERNEL_TEXT. On the other hand, today
we have CONFIG_STRICT_KERNEL_RWX which implements a more controlled
access to kernel modifications.
Instead of trying to keep PAGE_KERNEL_TEXT accurate with all
possible options that may imply kernel text modification, always
set kernel text read-write at startup and rely on
CONFIG_STRICT_KERNEL_RWX to provide accurate protection.
Reported-by: Erhard Furtner <redacted>
Closes: https://lore.kernel.org/all/342b4120-911c-4723-82ec-d8c9b03a8aef@mailbox.org/
Signed-off-by: Christophe Leroy <redacted>
---
arch/powerpc/include/asm/pgtable.h | 12 ------------
arch/powerpc/mm/book3s32/mmu.c | 4 ++--
arch/powerpc/mm/pgtable_32.c | 2 +-
3 files changed, 3 insertions(+), 15 deletions(-)
AFAIU - mmu_mark_initmem_nx gets called during kernel_init() which is
way after static call initialization correct? i.e.
start_kernel
...
jump_label_init()
static_call_init()
...
...
rest_init() /* Do the rest non-__init'ed, we're now alive */
kernel_init()
free_initmem() -> mark_initmem_nx() -> __mark_initmem_nx -> mmu_mark_initmem_nx()
mark_readonly()
if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX) && rodata_enabled) {
jump_label_init_ro()
mark_rodata_ro() -> ....
...
...
Then I guess we mainly only need __mapin_ram_chunk() to be PAGE_KERNEL_X (RWX)
instead of PAGE_KERNEL_TEXT (ROX), isn't it?
Let me quickly validate it...
...Ok, so I was able to get just this diff to be working.
Thoughts?
@@ -204,7 +204,7 @@ int mmu_mark_initmem_nx(void)for(i=0;i<nb-1&&base<top;){size=bat_block_size(base,top);-setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_TEXT);+setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_X);base+=size;}if(base<top){
@@ -215,7 +215,7 @@ int mmu_mark_initmem_nx(void)pr_warn("Some RW data is getting mapped X. ""Adjust CONFIG_DATA_SHIFT to avoid that.\n");}-setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_TEXT);+setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_X);base+=size;}for(;i<nb;i++)
PAGE_KERNEL_TEXT is an old macro that is used to tell kernel whether
kernel text has to be mapped read-only or read-write based on build
time options.
But nowadays, with functionnalities like jump_labels, static links,
etc ... more only less all kernels need to be read-write at some
point, and some combinations of configs failed to work due to
innacurate setting of PAGE_KERNEL_TEXT. On the other hand, today
we have CONFIG_STRICT_KERNEL_RWX which implements a more controlled
access to kernel modifications.
Instead of trying to keep PAGE_KERNEL_TEXT accurate with all
possible options that may imply kernel text modification, always
set kernel text read-write at startup and rely on
CONFIG_STRICT_KERNEL_RWX to provide accurate protection.
Reported-by: Erhard Furtner <redacted>
Closes: https://lore.kernel.org/all/342b4120-911c-4723-82ec-d8c9b03a8aef@mailbox.org/
Signed-off-by: Christophe Leroy <redacted>
---
arch/powerpc/include/asm/pgtable.h | 12 ------------
arch/powerpc/mm/book3s32/mmu.c | 4 ++--
arch/powerpc/mm/pgtable_32.c | 2 +-
3 files changed, 3 insertions(+), 15 deletions(-)
AFAIU - mmu_mark_initmem_nx gets called during kernel_init() which is
way after static call initialization correct? i.e.
start_kernel
...
jump_label_init()
static_call_init()
...
...
rest_init() /* Do the rest non-__init'ed, we're now alive */
kernel_init()
free_initmem() -> mark_initmem_nx() -> __mark_initmem_nx -> mmu_mark_initmem_nx()
mark_readonly()
if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX) && rodata_enabled) {
jump_label_init_ro()
mark_rodata_ro() -> ....
...
...
Then I guess we mainly only need __mapin_ram_chunk() to be PAGE_KERNEL_X (RWX)
instead of PAGE_KERNEL_TEXT (ROX), isn't it?
Let me quickly validate it...
...Ok, so I was able to get just this diff to be working.
Thoughts?
setibat() doesn't take into account whether it is RO or RW. Only X or NX
is taken into account, so it doesn't matter whether it is X or ROX.
Then allthough you are right in principle, once the PAGE_KERNEL_TEXT is
removed from __mapin_ram_chunk() it becomes completely useless, so
better get rid of PAGE_KERNEL_TEXT completely.
@@ -204,7 +204,7 @@ int mmu_mark_initmem_nx(void)for(i=0;i<nb-1&&base<top;){size=bat_block_size(base,top);-setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_TEXT);+setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_X);base+=size;}if(base<top){
@@ -215,7 +215,7 @@ int mmu_mark_initmem_nx(void)pr_warn("Some RW data is getting mapped X. ""Adjust CONFIG_DATA_SHIFT to avoid that.\n");}-setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_TEXT);+setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_X);base+=size;}for(;i<nb;i++)
PAGE_KERNEL_TEXT is an old macro that is used to tell kernel whether
kernel text has to be mapped read-only or read-write based on build
time options.
But nowadays, with functionnalities like jump_labels, static links,
etc ... more only less all kernels need to be read-write at some
point, and some combinations of configs failed to work due to
innacurate setting of PAGE_KERNEL_TEXT. On the other hand, today
we have CONFIG_STRICT_KERNEL_RWX which implements a more controlled
access to kernel modifications.
Instead of trying to keep PAGE_KERNEL_TEXT accurate with all
possible options that may imply kernel text modification, always
set kernel text read-write at startup and rely on
CONFIG_STRICT_KERNEL_RWX to provide accurate protection.
Reported-by: Erhard Furtner <redacted>
Closes: https://lore.kernel.org/all/342b4120-911c-4723-82ec-d8c9b03a8aef@mailbox.org/
Signed-off-by: Christophe Leroy <redacted>
---
arch/powerpc/include/asm/pgtable.h | 12 ------------
arch/powerpc/mm/book3s32/mmu.c | 4 ++--
arch/powerpc/mm/pgtable_32.c | 2 +-
3 files changed, 3 insertions(+), 15 deletions(-)
AFAIU - mmu_mark_initmem_nx gets called during kernel_init() which is
way after static call initialization correct? i.e.
start_kernel
...
jump_label_init()
static_call_init()
...
...
rest_init() /* Do the rest non-__init'ed, we're now alive */
kernel_init()
free_initmem() -> mark_initmem_nx() -> __mark_initmem_nx -> mmu_mark_initmem_nx()
mark_readonly()
if (IS_ENABLED(CONFIG_STRICT_KERNEL_RWX) && rodata_enabled) {
jump_label_init_ro()
mark_rodata_ro() -> ....
...
...
Then I guess we mainly only need __mapin_ram_chunk() to be PAGE_KERNEL_X (RWX)
instead of PAGE_KERNEL_TEXT (ROX), isn't it?
Let me quickly validate it...
...Ok, so I was able to get just this diff to be working.
Thoughts?
setibat() doesn't take into account whether it is RO or RW. Only X or NX
is taken into account, so it doesn't matter whether it is X or ROX.
Then allthough you are right in principle, once the PAGE_KERNEL_TEXT is
removed from __mapin_ram_chunk() it becomes completely useless, so
better get rid of PAGE_KERNEL_TEXT completely.
Aah yes, I checked the function setibat() and as you mentioned, it
doesn't honour RW permission anyways. Can we please update the same in
the commit message too? That makes it more clear then.
-ritesh
From: Andrew Donnellan <hidden> Date: 2025-09-05 06:57:42
On Thu, 2025-09-04 at 18:33 +0200, Christophe Leroy wrote:
PAGE_KERNEL_TEXT is an old macro that is used to tell kernel whether
kernel text has to be mapped read-only or read-write based on build
time options.
But nowadays, with functionnalities like jump_labels, static links,
etc ... more only less all kernels need to be read-write at some
point, and some combinations of configs failed to work due to
innacurate setting of PAGE_KERNEL_TEXT. On the other hand, today
we have CONFIG_STRICT_KERNEL_RWX which implements a more controlled
access to kernel modifications.
Instead of trying to keep PAGE_KERNEL_TEXT accurate with all
possible options that may imply kernel text modification, always
set kernel text read-write at startup and rely on
CONFIG_STRICT_KERNEL_RWX to provide accurate protection.
Reported-by: Erhard Furtner <redacted>
Closes:
https://lore.kernel.org/all/342b4120-911c-4723-82ec-d8c9b03a8aef@mailbox.org/
Signed-off-by: Christophe Leroy <redacted>
The original issue that Erhard and I were investigating was why the latest
version of the PowerPC page table check series[0] was failing on his G4, when
built as part of a config with many other debugging options enabled.
With further instrumentation, it turns out that this was due to a failed
instruction patch while setting up a jump label for the
page_table_check_disabled static key, which was being checked in
page_table_check_pte_clear(), which was in turn inlined ultimately into
debug_vm_pgtable().
This patch seems to fix the problem, so:
Tested-by: Andrew Donnellan <redacted>
But I'm still curious about why I only see the issue when:
(a) CONFIG_KFENCE=y (even when disabled using kfence.sample_interval=0) -
noting that changing CONFIG_KFENCE doesn't change the definition of
PAGE_KERNEL_TEXT; and
(b) when the jump label ends up in a __init function (removing __init from
debug_vm_pgtable() and its associated functions, or changing the code in such a
way that the static key check doesn't get inlined, resolves the issue, and
similarly for test_static_call_init() when CONFIG_STATIC_CALL_SELFTEST=y).
I don't understand the mm code well enough to make sense of this.
[0] https://lore.kernel.org/all/20250813062614.51759-1-ajd@linux.ibm.com/
--
Andrew Donnellan OzLabs, ADL Canberra
ajd@linux.ibm.com IBM Australia Limited
The original issue that Erhard and I were investigating was why the latest
version of the PowerPC page table check series[0] was failing on his G4, when
built as part of a config with many other debugging options enabled.
With further instrumentation, it turns out that this was due to a failed
instruction patch while setting up a jump label for the
page_table_check_disabled static key, which was being checked in
page_table_check_pte_clear(), which was in turn inlined ultimately into
debug_vm_pgtable().
This patch seems to fix the problem, so:
Tested-by: Andrew Donnellan <redacted>
But I'm still curious about why I only see the issue when:
(a) CONFIG_KFENCE=y (even when disabled using kfence.sample_interval=0) -
noting that changing CONFIG_KFENCE doesn't change the definition of
PAGE_KERNEL_TEXT; and
(b) when the jump label ends up in a __init function (removing __init from
debug_vm_pgtable() and its associated functions, or changing the code in such a
way that the static key check doesn't get inlined, resolves the issue, and
similarly for test_static_call_init() when CONFIG_STATIC_CALL_SELFTEST=y).
I don't understand the mm code well enough to make sense of this.
That makes sense. When CONFIG_KFENCE is selected, only text and rodata
are mapped with BATs. Everything else including inittext is mapped with
pages. When CONFIG_KFENCE and CONFIG_DEBUG_PAGEALLOC are not selected,
we map as much as possible with BATs.
And as you can see below, BATs are mapped with PAGE_KERNEL_X not with
PAGE_KERNEL_TEXT.
Everything happen here below:
static unsigned long __init __mmu_mapin_ram(unsigned long base, unsigned
long top)
{
int idx;
while ((idx = find_free_bat()) != -1 && base != top) {
unsigned int size = bat_block_size(base, top);
if (size < 128 << 10)
break;
setbat(idx, PAGE_OFFSET + base, base, size, PAGE_KERNEL_X);
base += size;
}
return base;
}
unsigned long __init mmu_mapin_ram(unsigned long base, unsigned long top)
{
unsigned long done;
unsigned long border = (unsigned long)__srwx_boundary - PAGE_OFFSET;
unsigned long size;
size = roundup_pow_of_two((unsigned long)_einittext - PAGE_OFFSET);
setibat(0, PAGE_OFFSET, 0, size, PAGE_KERNEL_X);
if (debug_pagealloc_enabled_or_kfence()) {
pr_debug_once("Read-Write memory mapped without BATs\n");
if (base >= border)
return base;
if (top >= border)
top = border;
}
if (!strict_kernel_rwx_enabled() || base >= border || top <= border)
return __mmu_mapin_ram(base, top);
done = __mmu_mapin_ram(base, border);
if (done != border)
return done;
return __mmu_mapin_ram(border, top);
}
PAGE_KERNEL_TEXT is an old macro that is used to tell kernel whether
kernel text has to be mapped read-only or read-write based on build
time options.
But nowadays, with functionnalities like jump_labels, static links,
etc ... more only less all kernels need to be read-write at some
point, and some combinations of configs failed to work due to
innacurate setting of PAGE_KERNEL_TEXT. On the other hand, today
we have CONFIG_STRICT_KERNEL_RWX which implements a more controlled
access to kernel modifications.
Instead of trying to keep PAGE_KERNEL_TEXT accurate with all
possible options that may imply kernel text modification, always
set kernel text read-write at startup and rely on
CONFIG_STRICT_KERNEL_RWX to provide accurate protection.
I can confirm your patch fixes the startup failure for my G4 .config.
Thanks!
Regards,
Erhard
@@ -204,7 +204,7 @@ int mmu_mark_initmem_nx(void)for(i=0;i<nb-1&&base<top;){size=bat_block_size(base,top);-setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_TEXT);+setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_X);base+=size;}if(base<top){
@@ -215,7 +215,7 @@ int mmu_mark_initmem_nx(void)pr_warn("Some RW data is getting mapped X. ""Adjust CONFIG_DATA_SHIFT to avoid that.\n");}-setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_TEXT);+setibat(i++,PAGE_OFFSET+base,base,size,PAGE_KERNEL_X);base+=size;}for(;i<nb;i++)