* [Help] Microwatt (Zynqwatt) — Kernel halts after Radix MMU init on booting Linux on Zynq version of Microwatt
@ 2025-11-12 14:45 Mohammad Amin Nili
2025-11-13 0:29 ` Oliver O'Halloran
0 siblings, 1 reply; 7+ messages in thread
From: Mohammad Amin Nili @ 2025-11-12 14:45 UTC (permalink / raw)
To: linuxppc-dev
Dear PPC64 Kernel Developers,
Hi all,
I'm trying to redo the Microwatt project on my own ZCU104 evaluation board
(Zynq US+). The main goal is to expose PS-side peripherals (specifically
the UART0 and LPDDR4 memory) to the PL-side Microwatt via the AXI4 bus and
MMIO. For simplicity, I'll call this whole project 'Zynqwatt'.
I successfully managed to boot and execute both `Hello World` and
`MicroPython` on my Zynqwatt. Here's what I've done so far:
1. I removed almost every peripheral except the `XICS` from the `SoC`
module.
2. I routed all addresses to a `WB2AXI` bridge so that the Zynq's PS-side
memory/MMIO space is completely accessible by the PL-side `Microwatt`.
3. I booted up my PS (Arm Cortex-A53) and let the Arm's `FSBL` initialize
almost all of the PS peripherals (e.g. UART, LPDDR4, etc.).
4. I copied my program from the SD card (`Hello World` or `MicroPython`)
into a fixed location of the PS DRAM using the Arm.
5. I released the `Microwatt` reset signal.
6. I parked the Arm processor.
7. The system worked successfully.
However, booting Linux seems to be a whole different story! Here's what
I've done so far to boot Linux on my Zynqwatt platform:
1. I’ve written a simple **bootloader** that runs on the Zynq’s Arm core.
Its job is to read the kernel image — `dtbImage.microwatt.elf` — from
the SD card and relocate it to the proper PS-side memory according to
the ELF header information.
2. The bootloader **releases the Microwatt reset signal**, and Microwatt
starts executing a super simple “hello world” bootloader. This minimal
program just prints a `Welcome` message and jumps to the kernel start
address (usually at `0x01700000`). Here is the log of this stage:
```
Zynq MP First Stage Boot Loader
Release 2025.1 Nov 9 2025 - 19:22:42
PMU-FW is not running, certain applications may not be supported.
Downloading bootloader to the DRAM...
Successfully downloaded bootloader to the DRAM at 0x20000000!
Downloading Linux ELF file to the DRAM...
Starting ELF read from SD card...
Initializing SDPS driver...
SDPS driver and card initialized successfully.
Reading 7340032 bytes from sector offset 0 to address 0x30000000
ELF file read from SD card successfully.
Successfully downloaded ELF file to the DRAM at 0x30000000!
Extracting Linux ELF file to the DRAM...
Successfully extracted ELF file to the DRAM!
Configuring Microwatt for booting...
Successfully configured Microwatt!
Booting up Microwatt from bootloader at 0x20000000...
--------------------------------------------------
.oOOo.
." ".
; .mw. ; Microwatt, it works.
. ' ' .
\ || /
;..;
;..;
`ww'
Function <my_printf> is located at 0x00001354.
Executing: *(0x01700000) --> 0x480000d0.
Press any key to continue...
```
3. The **kernel wrapper** tries to read the DTB and decompress the gzipped
kernel into `0x00000000`:
```
zImage starting: loaded at 0x0000000001700000 (sp: 0x0000000001b1beb0)
Allocating 0x16c5144 bytes for kernel...
Decompressing (0x0000000000000000 <- 0x0000000001714000:0x0000000001b19fa2)...
Done! Decompressed 0x16c5144 bytes
Linux/PowerPC load: earlycon earlyprintk debug loglevel=9
Finalizing device tree... flat tree at 0x1b1cc80
```
4. The kernel successfully decompresses and hands control over to
`arch/powerpc/kernel/head_64.S`. This is exactly where I’m stuck. Here’s
the relevant log output:
```
Linux/PowerPC load: earlycon earlyprintk debug loglevel=9
Finalizing device tree... flat tree at 0x1b1cc80
[ 0.000000] printk: legacy bootconsole [udbg0] enabled
[ 0.000000] ioremap() called early from of_setup_earlycon+0xd0/0x2d4. Use early_ioremap() instead
[ 0.000000] earlycon: earlycon_map: Couldn't map 0x00000000ff000000
[ 0.000000] earlycon: cdns0 at MMIO 0x00000000ff000000 (options '')
[ 0.000000] printk: legacy bootconsole [cdns0] enabled
[ 0.000000] dt-cpu-ftrs: setup for ISA 3100
[ 0.000000] dt-cpu-ftrs: final cpu/mmu features = 0x00040083800bb181 0x20005040
[ 0.000000] radix-mmu: Page sizes from device-tree:
[ 0.000000] radix-mmu: Page size shift = 12 AP=0x0
[ 0.000000] radix-mmu: Page size shift = 16 AP=0x5
[ 0.000000] radix-mmu: Page size shift = 21 AP=0x1
[ 0.000000] radix-mmu: Page size shift = 30 AP=0x2
[ 0.000000] radix-mmu: Mapped 0x0000000000000000-0x0000000001800000 with 2.00 MiB pages (exec)
[ 0.000000] radix-mmu: Mapped 0x0000000001800000-0x0000000010000000 with 2.00 MiB pages
[ 0.000000] radix-mmu: Initializing Radix MMU
```
NOTE: As you know, the `earlycon` driver depends on calling `ioremap`
in `drivers/tty/serial/earlycon.c:L47` to get the virtual address of its
`mapbase/membase`, which is later used by the main console driver (e.g.
`cdns,uart-r1p12`). During the very early boot phase, `ioremap` returns
`0x00000000` because it is called before `early_init_mmu` and
`early_ioremap_setup` in the `early_setup` of
`arch/powerpc/kernel/setup_64.S`.
For debugging purposes, I hardcoded it to return the **physical address**
`0xFF000000` — which corresponds to the Zynq PS-side UART0.
5. From what I can tell, **the system crashes or loses console output
immediately after the MMU is activated** — specifically right after the
call to `RFI_TO_KERNEL` in `arch/powerpc/kernel/head_64.S:L1019`.
6. To test my assumption, I tried **disabling the MMU** by inserting the
following lines around `head_64.S:L1019`:
```
LOAD_REG_ADDR(r3, start_here_common)
ld r4,PACAKMSR(r13)
//*****************************************************
li r5, -49
and r4, r4, r5
//*****************************************************
mtspr SPRN_SRR0,r3
mtspr SPRN_SRR1,r4
RFI_TO_KERNEL
b . /* prevent speculative execution */
```
The result was:
```
[ 0.000000] printk: legacy bootconsole [udbg0] enabled
[ 0.000000] ioremap() called early from earlycon_map+0x20/0x6c. Use early_ioremap() instead
[ 0.000000] earlycon: earlycon_map: Couldn't map 0x00000000ff000000
[ 0.000000] earlycon: Temporarily mapped it to 0x00000000ff000000
[ 0.000000] earlycon: cdns0 at MMIO 0x00000000ff000000 (options '')
[ 0.000000] printk: legacy bootconsole [cdns0] enabled
[ 0.000000] dt-cpu-ftrs: setup for ISA 3100
[ 0.000000] dt-cpu-ftrs: final cpu/mmu features = 0x00040083800bb181 0x20005040
[ 0.000000] radix-mmu: Page sizes from device-tree:
[ 0.000000] radix-mmu: Page size shift = 12 AP=0x0
[ 0.000000] radix-mmu: Page size shift = 16 AP=0x5
[ 0.000000] radix-mmu: Page size shift = 21 AP=0x1
[ 0.000000] radix-mmu: Page size shift = 30 AP=0x2
[ 0.000000] radix-mmu: Mapped 0x0000000000000000-0x0000000001800000 with 2.00 MiB pages (exec)
[ 0.000000] radix-mmu: Mapped 0x0000000001800000-0x0000000010000000 with 2.00 MiB pages
[ 0.000000] radix-mmu: Initializing Radix MMU
[ 0.000000] Linux version 6.18.0-rc3-00007-gfd57572253bc-dirty (manili@manili) (powerpc64le-linux-gcc.br_real (Buildroot 2021.11-18033-g83947c7bb6) 14.3.0, GNU ld (GNU Binutils) 2.43.1) #124 Sun Nov 9 23:46:11 EST 2025
[ 0.000000] Hardware name: microwatt Microwatt 0x630000 microwatt
[ 0.000000] -----------------------------------------------------
[ 0.000000] phys_mem_size = 0x10000000
[ 0.000000] dcache_bsize = 0x40
[ 0.000000] icache_bsize = 0x40
[ 0.000000] cpu_features = 0x00040083800bb181
[ 0.000000] possible = 0x003ffbebcb5fb185
[ 0.000000] always = 0x0000000380008181
[ 0.000000] cpu_user_features = 0xcc002102 0x8c940000
[ 0.000000] mmu_features = 0x20005040
[ 0.000000] firmware_features = 0x0000000000000000
[ 0.000000] vmalloc start = 0xc008000000000000
[ 0.000000] IO start = 0xc00a000000000000
[ 0.000000] vmemmap start = 0xc00c000000000000
[ 0.000000] -----------------------------------------------------
[ 0.000000] barrier-nospec: using ORI speculation barrier
[ 0.000000] barrier-nospec: patched 100 locations
[ 0.000000] Top of RAM: 0x10000000, Total RAM: 0x10000000
[ 0.000000] Memory hole size: 0MB
[ 0.000000] Zone ranges:
[ 0.000000] Normal [mem 0x0000000000000000-0x000000000fffffff]
[ 0.000000] Movable zone start for each node
[ 0.000000] Early memory node ranges
[ 0.000000] node 0: [mem 0x0000000000000000-0x000000000fffffff]
[ 0.000000] Initmem setup node 0 [mem 0x0000000000000000-0x000000000fffffff]
```
So unlike the previous logs, it looks like the kernel continues the boot
process after `radix-mmu: Initializing Radix MMU`, but this time halts at
`Initmem setup...` because the MMU is off (I guess).
7. Here’s my current DTS configuration. I’ve kept it as **simple and close
to Microwatt’s default** as possible:
```
/dts-v1/;
#include <dt-bindings/gpio/gpio.h>
/ {
#size-cells = <0x02>;
#address-cells = <0x02>;
model = "microwatt";
compatible = "microwatt-soc";
aliases {
serial0 = &UART0;
};
reserved-memory {
#size-cells = <0x02>;
#address-cells = <0x02>;
ranges;
};
memory@0 {
device_type = "memory";
reg = <0x00000000 0x00000000 0x00000000 0x10000000>;
};
clocks {
sys_clk: litex_sys_clk {
#clock-cells = <0>;
compatible = "fixed-clock";
clock-frequency = <100000000>;
};
};
cpus {
#size-cells = <0x00>;
#address-cells = <0x01>;
ibm,powerpc-cpu-features {
display-name = "Microwatt";
isa = <3100>;
device_type = "cpu-features";
compatible = "ibm,powerpc-cpu-features";
mmu-radix {
isa = <3000>;
usable-privilege = <6>;
os-support = <0>;
};
little-endian {
isa = <0>;
usable-privilege = <7>;
os-support = <0>;
hwcap-bit-nr = <1>;
};
cache-inhibited-large-page {
isa = <0>;
usable-privilege = <6>;
os-support = <0>;
};
fixed-point-v3 {
isa = <3000>;
usable-privilege = <7>;
};
no-execute {
isa = <0x00>;
usable-privilege = <2>;
os-support = <0>;
};
floating-point {
hfscr-bit-nr = <0>;
hwcap-bit-nr = <27>;
isa = <0>;
usable-privilege = <7>;
hv-support = <1>;
os-support = <0>;
};
prefixed-instructions {
hfscr-bit-nr = <13>;
fscr-bit-nr = <13>;
isa = <3010>;
usable-privilege = <7>;
os-support = <1>;
hv-support = <1>;
};
tar {
hfscr-bit-nr = <8>;
fscr-bit-nr = <8>;
isa = <2070>;
usable-privilege = <7>;
os-support = <1>;
hv-support = <1>;
hwcap-bit-nr = <58>;
};
control-register {
isa = <0>;
usable-privilege = <7>;
};
system-call-vectored {
isa = <3000>;
usable-privilege = <7>;
os-support = <1>;
fscr-bit-nr = <12>;
hwcap-bit-nr = <52>;
};
};
PowerPC,Microwatt@0 {
i-cache-sets = <2>;
ibm,dec-bits = <64>;
reservation-granule-size = <64>;
clock-frequency = <100000000>;
timebase-frequency = <100000000>;
i-tlb-sets = <1>;
ibm,ppc-interrupt-server#s = <0>;
i-cache-block-size = <64>;
d-cache-block-size = <64>;
d-cache-sets = <2>;
i-tlb-size = <64>;
cpu-version = <0x990000>;
status = "okay";
i-cache-size = <0x1000>;
ibm,processor-radix-AP-encodings = <0x0c 0xa0000010 0x20000015 0x4000001e>;
tlb-size = <0>;
tlb-sets = <0>;
device_type = "cpu";
d-tlb-size = <128>;
d-tlb-sets = <2>;
reg = <0>;
general-purpose;
64-bit;
d-cache-size = <0x1000>;
ibm,chip-id = <0>;
ibm,mmu-lpid-bits = <12>;
ibm,mmu-pid-bits = <20>;
};
};
soc {
compatible = "simple-bus";
#address-cells = <0x02>;
#size-cells = <0x02>;
ranges;
interrupt-controller@c0004000 {
compatible = "openpower,xics-presentation", "ibm,ppc-xicp";
ibm,interrupt-server-ranges = <0x0 0x1>;
reg = <0x0 0xc0004000 0x0 0x10>;
};
ICS: interrupt-controller@c0005000 {
compatible = "openpower,xics-sources";
interrupt-controller;
interrupt-ranges = <0x10 0x10>;
reg = <0x0 0xc0005000 0x0 0x100>;
#address-cells = <0>;
#size-cells = <0>;
#interrupt-cells = <2>;
};
UART0: serial@ff000000 {
device_type = "serial";
compatible = "cdns,uart-r1p12";
reg = <0x0 0xff000000 0x0 0x1000>;
clock-frequency = <100000000>;
current-speed = <115200>;
};
};
chosen {
bootargs = "earlycon earlyprintk debug loglevel=9";
ibm,architecture-vec-5 = [19 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 40 00 40];
stdout-path = &UART0;
};
};
```
So, why is the MMU preventing the kernel from booting properly — or at
least stopping the UART driver from accessing its mapped address at
`0xFF000000`? Could this be an issue specific to the **Cadence/Xilinx UART
driver**? Or am I missing something fundamental about how the MMU should
be configured in this setup?
Let me know if you need any other sources or data to share. I’ve tried
everything I can think of but still cannot figure out what’s wrong with my
kernel porting process. Any help would be much appreciated.
Bests,
Manili
^ permalink raw reply [flat|nested] 7+ messages in thread* Re: [Help] Microwatt (Zynqwatt) — Kernel halts after Radix MMU init on booting Linux on Zynq version of Microwatt 2025-11-12 14:45 [Help] Microwatt (Zynqwatt) — Kernel halts after Radix MMU init on booting Linux on Zynq version of Microwatt Mohammad Amin Nili @ 2025-11-13 0:29 ` Oliver O'Halloran 2025-11-13 22:32 ` Mohammad Amin Nili 0 siblings, 1 reply; 7+ messages in thread From: Oliver O'Halloran @ 2025-11-13 0:29 UTC (permalink / raw) To: Mohammad Amin Nili; +Cc: linuxppc-dev On Thu, Nov 13, 2025 at 1:45 AM Mohammad Amin Nili <manili.devteam@gmail.com> wrote: > > [ 0.000000] printk: legacy bootconsole [udbg0] enabled > [ 0.000000] ioremap() called early from of_setup_earlycon+0xd0/0x2d4. Use early_ioremap() instead > [ 0.000000] earlycon: earlycon_map: Couldn't map 0x00000000ff000000 pretty big red flag right there > NOTE: As you know bold of you to assume i know things > the `earlycon` driver depends on calling `ioremap` > in `drivers/tty/serial/earlycon.c:L47` to get the virtual address of its > `mapbase/membase`, which is later used by the main console driver (e.g. > `cdns,uart-r1p12`). During the very early boot phase, `ioremap` returns > `0x00000000` because it is called before `early_init_mmu` and > `early_ioremap_setup` in the `early_setup` of > `arch/powerpc/kernel/setup_64.S`. > > For debugging purposes, I hardcoded it to return the **physical address** > `0xFF000000` — which corresponds to the Zynq PS-side UART0. > > 5. From what I can tell, **the system crashes or loses console output > immediately after the MMU is activated** — specifically right after the > call to `RFI_TO_KERNEL` in `arch/powerpc/kernel/head_64.S:L1019`. Right, you turn on the MMU and the next time printk() is called the console driver tries to write to 0xFF00_0000. That's not a valid virtual address so it explodes. To make an address usable in both real mode (i.e. pre-mmu) and virtual mode you need to have the page tables setup so that virtual address maps to the same physical address. Setting up that mapping is what early_ioremap() does. That's why there's a warning telling you to use it. Based on the in-tree DTS files earlycon doesn't seem to be used on any powerpc systems. My guess would be that most ppc platform use udbg (very old, powerpc specific thing) rather than earlycon for this kind of super-early debug output. Considering you're getting console output via udbg I'd say just removing earlycon from your kernel command line will probably fix your issue. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Help] Microwatt (Zynqwatt) — Kernel halts after Radix MMU init on booting Linux on Zynq version of Microwatt 2025-11-13 0:29 ` Oliver O'Halloran @ 2025-11-13 22:32 ` Mohammad Amin Nili 2025-11-14 2:42 ` Oliver O'Halloran 0 siblings, 1 reply; 7+ messages in thread From: Mohammad Amin Nili @ 2025-11-13 22:32 UTC (permalink / raw) To: Oliver O'Halloran; +Cc: linuxppc-dev [-- Attachment #1: Type: text/plain, Size: 1721 bytes --] Hi Oliver, Thanks a lot for your time and the answer. > Right, you turn on the MMU and the next time printk() is called the > console driver tries to write to 0xFF00_0000. That's not a valid > virtual address so it explodes. To make an address usable in both real > mode (i.e. pre-mmu) and virtual mode you need to have the page tables > setup so that virtual address maps to the same physical address. > Setting up that mapping is what early_ioremap() does. That's why > there's a warning telling you to use it. > > Based on the in-tree DTS files earlycon doesn't seem to be used on any > powerpc systems. My guess would be that most ppc platform use udbg > (very old, powerpc specific thing) rather than earlycon for this kind > of super-early debug output. Considering you're getting console output > via udbg I'd say just removing earlycon from your kernel command line > will probably fix your issue. Well, I disabled all the early logs (now bootargs = “” in dts) and modified .config so that no earlycon.c gets compiled at all. The followings are the only compiled files within the `/derivers/tty/serial`: serial_base_bus.o, serial_core.o, serial_ctrl.o, serial_port.o, xilinx_uartps.o Now, I get no outputs during booting procedure which sounds normal, I guess. But still no luck getting to the rootfs or anywhere which actually initializes the `xilinx_uartps` driver and print something. Also please check shenki’s blogpost’s logs, if you have enough time: https://shenki.github.io/boot-linux-on-microwatt <https://shenki.github.io/boot-linux-on-microwatt> Based on the blog post, I think there should be no problem to get the early booting logs. Bests, Manili [-- Attachment #2: Type: text/html, Size: 3514 bytes --] ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Help] Microwatt (Zynqwatt) — Kernel halts after Radix MMU init on booting Linux on Zynq version of Microwatt 2025-11-13 22:32 ` Mohammad Amin Nili @ 2025-11-14 2:42 ` Oliver O'Halloran 2025-11-21 14:59 ` Mohammad Amin Nili 0 siblings, 1 reply; 7+ messages in thread From: Oliver O'Halloran @ 2025-11-14 2:42 UTC (permalink / raw) To: Mohammad Amin Nili; +Cc: linuxppc-dev On Fri, Nov 14, 2025 at 9:32 AM Mohammad Amin Nili <manili.devteam@gmail.com> wrote: > > Well, I disabled all the early logs (now bootargs = “” in dts) and modified .config > so that no earlycon.c gets compiled at all. The followings are the only compiled > files within the `/derivers/tty/serial`: > > serial_base_bus.o, serial_core.o, serial_ctrl.o, serial_port.o, xilinx_uartps.o > > Now, I get no outputs during booting procedure which sounds normal, I guess. > But still no luck getting to the rootfs or anywhere which actually initializes the > `xilinx_uartps` driver and print something. It might be reaching the rootfs. Initialising a console doesn't necessarily mean anything is going to use it. Try adding console=ttyS0 to your command line. > Also please check shenki’s blogpost’s logs, if you have enough time: > > https://shenki.github.io/boot-linux-on-microwatt > > Based on the blog post, I think there should be no problem to get the early > booting logs. And yet you're having problems... The series that blog post is based on added a udbg driver for the potato UART they were using for microwatt development. The udbg driver itself provides early console output and gets turned into hvc0 by the hvc_udbg driver later on during boot. In your case you don't have either of those things so you don't get any output. If you wanted to do something similar you would need to implement a udbg driver for your xilinx UART. That shouldn't be too hard to do, but fixing the earlycon driver so it uses early_ioremap() might also work. ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Help] Microwatt (Zynqwatt) — Kernel halts after Radix MMU init on booting Linux on Zynq version of Microwatt 2025-11-14 2:42 ` Oliver O'Halloran @ 2025-11-21 14:59 ` Mohammad Amin Nili 2025-11-22 2:48 ` Oliver O'Halloran 0 siblings, 1 reply; 7+ messages in thread From: Mohammad Amin Nili @ 2025-11-21 14:59 UTC (permalink / raw) To: Oliver O'Halloran; +Cc: linuxppc-dev [-- Attachment #1: Type: text/plain, Size: 4255 bytes --] Hi Oliver! > If you wanted to do something similar you would need to > implement a udbg driver for your xilinx UART. Actually this was the key point which finally led me to the right direction. Thank you SOOO much for your help. I implemented a udbg driver for Xilinx’s UART, worked through a number of bugs, and managed to get bash and userspace to start. During the work I hit two things in the source that look odd to me; I’d appreciate any pointers or explanations: 1. There appear to be two ways udbg can be initiated: - https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup_64.c#L378 <https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup_64.c#L378> - https://elixir.bootlin.com/linux/v6.18-rc5/source/init/main.c#L932 <https://elixir.bootlin.com/linux/v6.18-rc5/source/init/main.c#L932> -> https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup-common.c#L944 <https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup-common.c#L944> -> https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup-common.c#L947 <https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup-common.c#L947> The problem with the *first* (early) path is that a udbg driver often calls `early_ioremap()` **before** any MMU/memory setup has completed. The MMU setup for 64-bit happens here: - https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup_64.c#L418 <https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup_64.c#L418> The 32-bit setup (`setup_32.c`) calls `udbg_early_init` *after* `early_ioremap_init`, so it doesn’t hit this ordering problem: - https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup_32.c#L87 <https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup_32.c#L87> In my case, attempting to initialize the Xilinx udbg driver via the early path can (and did) cause kernel panics because `early_ioremap()` runs before the memory/MMU is ready. Practically, the only reliable place for my driver to initialize is later in `init/main.c`. Is this the intended/expected behavior for PPC64? Or am I missing something? 2. I had to inject a busy-wait loop between lines 125–126 in `kernel/rcu/tiny.c` to prevent a crash when/after switching to userspace: - https://elixir.bootlin.com/linux/v6.18-rc5/source/kernel/rcu/tiny.c#L125 <https://elixir.bootlin.com/linux/v6.18-rc5/source/kernel/rcu/tiny.c#L125> Here is the loop I added: for (volatile uint32_t i = 0; i < 10; i++); If I omit that trivial busy-wait, the kernel crashes while/after switching to userspace with an error LIKE: [ 42.397074] kernel tried to execute exec-protected page (c00c000000000000) - exploit attempt? (uid: 0) [ 42.408148] BUG: Unable to handle kernel instruction fetch [ 42.414964] Faulting instruction address: 0xc00c000000000000 Vector: 400 (Instruction Access) at [c00000000207fae0] pc: c00c000000000000 lr: c00000000008c798: rcu_process_callbacks+0xf8/0x100 sp: c00000000207fd80 msr: 900000001000b033 current = 0xc000000002056300 paca = 0xc0000000016e8000 irqmask: 0x03 irq_happened: 0x09 pid = 10, comm = ksoftirqd/0 Linux version 6.18.0-rc5-00111-g6fa9041b7177-dirty (manili@manili) (powerpc64le-linux-gcc.br_real (Buildroot 2021.11-18033-g83947c7bb6) 14.3.0, GNU ld (GNU Binutils) 2.43.1) #3 Thu Nov 20 09:33:11 EST 2025 enter ? for help [link register ] c00000000008c798 rcu_process_callbacks+0xf8/0x100 [c00000000207fd80] c00000000008c748 rcu_process_callbacks+0xa8/0x100 (unreliable) [c00000000207fe00] c00000000003f320 handle_softirqs+0x1ec/0x23c [c00000000207ff00] c00000000003f3a8 run_ksoftirqd+0x38/0x58 [c00000000207ff20] c00000000005f9c4 smpboot_thread_fn+0x1a0/0x1a8 [c00000000207ff80] c00000000005b190 kthread+0x1c0/0x1cc [c00000000207ffe0] c00000000000b160 start_kernel_thread+0x14/0x18 mon> The exact addresses in the error vary, but the crash template is the same. My suspicion is that this is a core/thread synchronization issue. Do you have any ideas on this issue and why a simple while loop is able to solve it? Bests, Manili [-- Attachment #2: Type: text/html, Size: 6003 bytes --] ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Help] Microwatt (Zynqwatt) — Kernel halts after Radix MMU init on booting Linux on Zynq version of Microwatt 2025-11-21 14:59 ` Mohammad Amin Nili @ 2025-11-22 2:48 ` Oliver O'Halloran 2025-11-22 11:32 ` Mohammad Amin Nili 0 siblings, 1 reply; 7+ messages in thread From: Oliver O'Halloran @ 2025-11-22 2:48 UTC (permalink / raw) To: Mohammad Amin Nili; +Cc: linuxppc-dev On Sat, Nov 22, 2025 at 1:59 AM Mohammad Amin Nili <manili.devteam@gmail.com> wrote: > > The problem with the *first* (early) path is that a udbg driver > often calls `early_ioremap()` **before** any MMU/memory > setup has completed. The MMU setup for 64-bit happens here: > - https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup_64.c#L418 > > The 32-bit setup (`setup_32.c`) calls `udbg_early_init` *after* > `early_ioremap_init`, so it doesn’t hit this ordering problem: > - https://elixir.bootlin.com/linux/v6.18-rc5/source/arch/powerpc/kernel/setup_32.c#L87 > > In my case, attempting to initialize the Xilinx udbg driver via > the early path can (and did) cause kernel panics because > `early_ioremap()` runs before the memory/MMU is ready. > Practically, the only reliable place for my driver to initialize > is later in `init/main.c`. Is this the intended/expected behavior > for PPC64? Or am I missing something? Nah it's just broken for your case. Most ppc64 development happens on IBM hardware and the udbg drivers for those platforms (pseries for VMs, powernv for bare metal) don't require the MMU to be setup. Enabling debug output pre-MMU is handy since it lets you debug MMU setup issues, but obviously it's not going to work if your udbg driver needs to map stuff. You could avoid depending on the MMU by using the potato uart driver's trick of just disabling data relocation when writing to the UART's registers. That's pretty slow and a bit janky though. A better fix might be to split udbg init into an early (pre-mmu) and late variants with the udbg drivers that need the mmu are initialised later. > 2. I had to inject a busy-wait loop between lines 125–126 in > `kernel/rcu/tiny.c` to prevent a crash when/after switching to > userspace: > - https://elixir.bootlin.com/linux/v6.18-rc5/source/kernel/rcu/tiny.c#L125 > > Here is the loop I added: > for (volatile uint32_t i = 0; i < 10; i++); > > If I omit that trivial busy-wait, the kernel crashes while/after > switching to userspace with an error LIKE: > > [ 42.397074] kernel tried to execute exec-protected page (c00c000000000000) - exploit attempt? (uid: 0) > [ 42.408148] BUG: Unable to handle kernel instruction fetch > [ 42.414964] Faulting instruction address: 0xc00c000000000000 > Vector: 400 (Instruction Access) at [c00000000207fae0] > pc: c00c000000000000 > lr: c00000000008c798: rcu_process_callbacks+0xf8/0x100 > sp: c00000000207fd80 > msr: 900000001000b033 > current = 0xc000000002056300 > paca = 0xc0000000016e8000 irqmask: 0x03 irq_happened: 0x09 > pid = 10, comm = ksoftirqd/0 > Linux version 6.18.0-rc5-00111-g6fa9041b7177-dirty (manili@manili) (powerpc64le-linux-gcc.br_real (Buildroot 2021.11-18033-g83947c7bb6) 14.3.0, GNU ld (GNU Binutils) 2.43.1) #3 Thu Nov 20 09:33:11 EST 2025 > enter ? for help > [link register ] c00000000008c798 rcu_process_callbacks+0xf8/0x100 > [c00000000207fd80] c00000000008c748 rcu_process_callbacks+0xa8/0x100 (unreliable) > [c00000000207fe00] c00000000003f320 handle_softirqs+0x1ec/0x23c > [c00000000207ff00] c00000000003f3a8 run_ksoftirqd+0x38/0x58 > [c00000000207ff20] c00000000005f9c4 smpboot_thread_fn+0x1a0/0x1a8 > [c00000000207ff80] c00000000005b190 kthread+0x1c0/0x1cc > [c00000000207ffe0] c00000000000b160 start_kernel_thread+0x14/0x18 > mon> > > The exact addresses in the error vary, but the crash > template is the same. My suspicion is that this is a > core/thread synchronization issue. Do you have any > ideas on this issue and why a simple while loop is able > to solve it? That's very odd. rcu_reclaim_tiny() is probably being folded into rcu_process_callbacks() by the compiler and the crash occurs when branching to the callback function from the rcu_head (https://elixir.bootlin.com/linux/v6.18-rc5/source/kernel/rcu/tiny.c#L95). That said, the "callback" address it branched to (0xc00c000000000000) is actually the base of the vmemmap (i.e. the struct page array) so I doubt that's actually the callback address stored in the rcu_head. You can use xmon to dump the registers and examine memory to confirm this. It's hard to say why this is happening, but it's pretty likely to either be the compiler optimizing away code you'd prefer to keep or a bug in the core itself. I'd compare the disasm of rcu_process_callbacks() with and without your wait loop added and see how the emitted code changes. If adding the loop changes nothing then it might be a logic bug in microwatt itself or some other timing induced problem. > > Bests, > Manili ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [Help] Microwatt (Zynqwatt) — Kernel halts after Radix MMU init on booting Linux on Zynq version of Microwatt 2025-11-22 2:48 ` Oliver O'Halloran @ 2025-11-22 11:32 ` Mohammad Amin Nili 0 siblings, 0 replies; 7+ messages in thread From: Mohammad Amin Nili @ 2025-11-22 11:32 UTC (permalink / raw) To: Oliver O'Halloran; +Cc: linuxppc-dev [-- Attachment #1: Type: text/plain, Size: 9041 bytes --] Hello Oliver, > That's very odd. rcu_reclaim_tiny() is probably being folded into > rcu_process_callbacks() by the compiler and the crash occurs when > branching to the callback function from the rcu_head > (https://elixir.bootlin.com/linux/v6.18-rc5/source/kernel/rcu/tiny.c#L95 <https://elixir.bootlin.com/linux/v6.18-rc5/source/kernel/rcu/tiny.c#L95>). > That said, the "callback" address it branched to (0xc00c000000000000) > is actually the base of the vmemmap (i.e. the struct page array) so I > doubt that's actually the callback address stored in the rcu_head. You > can use xmon to dump the registers and examine memory to confirm this. > It's hard to say why this is happening, but it's pretty likely to > either be the compiler optimizing away code you'd prefer to keep or a > bug in the core itself. > > I'd compare the disasm of rcu_process_callbacks() with and without > your wait loop added and see how the emitted code changes. If adding > the loop changes nothing then it might be a logic bug in microwatt > itself or some other timing induced problem. I just replaced the wait-loop with the following (and it also prevents the kernel panic): > volatile uint32_t dumb = 0xFEDC0000; > dumb++; - Here is the disasm of the `rcu_process_callbacks` with the injected code: c00000000008c5e8 <rcu_process_callbacks>: c00000000008c5e8: 32 00 4c 3c addis r2,r12,50 c00000000008c5ec: 18 ca 42 38 addi r2,r2,-13800 c00000000008c5f0: a6 02 08 7c mflr r0 c00000000008c5f4: a1 c3 23 48 bl c0000000002c8994 <_savegpr0_27> c00000000008c5f8: a1 ff 21 f8 stdu r1,-96(r1) c00000000008c5fc: fa 00 6d 88 lbz r3,250(r13) c00000000008c600: 01 00 69 60 ori r9,r3,1 c00000000008c604: fa 00 2d 99 stb r9,250(r13) c00000000008c608: 2b 01 42 3d addis r10,r2,299 c00000000008c60c: 2b 01 22 3d addis r9,r2,299 c00000000008c610: d0 3c 4a e9 ld r10,15568(r10) c00000000008c614: c8 3c 29 39 addi r9,r9,15560 c00000000008c618: 00 48 2a 7c cmpd r10,r9 c00000000008c61c: 14 00 82 40 bne c00000000008c630 <rcu_process_callbacks+0x48> c00000000008c620: 6d d6 f8 4b bl c000000000019c8c <arch_local_irq_restore+0x8> c00000000008c624: 00 00 00 60 nop c00000000008c628: 60 00 21 38 addi r1,r1,96 c00000000008c62c: b8 c3 23 48 b c0000000002c89e4 <_restgpr0_27> c00000000008c630: 00 00 0a e9 ld r8,0(r10) c00000000008c634: 2b 01 e2 3c addis r7,r2,299 c00000000008c638: c8 3c e7 eb ld r31,15560(r7) c00000000008c63c: c8 3c 07 f9 std r8,15560(r7) c00000000008c640: 00 00 00 39 li r8,0 c00000000008c644: 00 00 0a f9 std r8,0(r10) c00000000008c648: 2b 01 42 3d addis r10,r2,299 c00000000008c64c: d8 3c 0a e9 ld r8,15576(r10) c00000000008c650: 2b 01 42 3d addis r10,r2,299 c00000000008c654: d0 3c 4a e9 ld r10,15568(r10) c00000000008c658: 00 50 28 7c cmpd r8,r10 c00000000008c65c: 08 00 82 40 bne c00000000008c664 <rcu_process_callbacks+0x7c> c00000000008c660: 10 00 29 f9 std r9,16(r9) c00000000008c664: 08 00 29 f9 std r9,8(r9) c00000000008c668: dc fe 80 3f lis r28,-292 <<<<====== "THIS IS INJECTED CODE" c00000000008c66c: 21 d6 f8 4b bl c000000000019c8c <arch_local_irq_restore+0x8> c00000000008c670: 00 00 00 60 nop c00000000008c674: 00 00 60 3b li r27,0 c00000000008c678: 00 00 3f 2c cmpdi r31,0 c00000000008c67c: ac ff 82 41 beq c00000000008c628 <rcu_process_callbacks+0x40> c00000000008c680: 00 00 df eb ld r30,0(r31) c00000000008c684: 00 00 3e 2c cmpdi r30,0 c00000000008c688: 08 00 82 41 beq c00000000008c690 <rcu_process_callbacks+0xa8> c00000000008c68c: 2c f2 00 7c dcbtct 0,r30 c00000000008c690: 2c 00 81 93 stw r28,44(r1) c00000000008c694: 08 00 bf eb ld r29,8(r31) c00000000008c698: 00 00 3d 2c cmpdi r29,0 c00000000008c69c: 2c 00 21 81 lwz r9,44(r1) c00000000008c6a0: 01 00 29 39 addi r9,r9,1 c00000000008c6a4: 2c 00 21 91 stw r9,44(r1) c00000000008c6a8: 10 00 82 40 bne c00000000008c6b8 <rcu_process_callbacks+0xd0> c00000000008c6ac: 78 fb e3 7f mr r3,r31 c00000000008c6b0: 15 f8 03 48 bl c0000000000cbec4 <kmem_dump_obj+0x8> c00000000008c6b4: 00 00 00 60 nop c00000000008c6b8: 08 00 7f fb std r27,8(r31) c00000000008c6bc: 78 fb e3 7f mr r3,r31 c00000000008c6c0: a6 03 a9 7f mtctr r29 c00000000008c6c4: 78 eb ac 7f mr r12,r29 c00000000008c6c8: 18 00 41 f8 std r2,24(r1) c00000000008c6cc: 78 f3 df 7f mr r31,r30 c00000000008c6d0: 21 04 80 4e bctrl c00000000008c6d4: 18 00 41 e8 ld r2,24(r1) c00000000008c6d8: a0 ff ff 4b b c00000000008c678 <rcu_process_callbacks+0x90> - And here is the disasm without the injected code: c00000000008c5e8 <rcu_process_callbacks>: c00000000008c5e8: 32 00 4c 3c addis r2,r12,50 c00000000008c5ec: 18 ca 42 38 addi r2,r2,-13800 c00000000008c5f0: a6 02 08 7c mflr r0 c00000000008c5f4: 95 c3 23 48 bl c0000000002c8988 <_savegpr0_28> c00000000008c5f8: c1 ff 21 f8 stdu r1,-64(r1) c00000000008c5fc: fa 00 6d 88 lbz r3,250(r13) c00000000008c600: 01 00 69 60 ori r9,r3,1 c00000000008c604: fa 00 2d 99 stb r9,250(r13) c00000000008c608: 2b 01 42 3d addis r10,r2,299 c00000000008c60c: 2b 01 22 3d addis r9,r2,299 c00000000008c610: d0 3c 4a e9 ld r10,15568(r10) c00000000008c614: c8 3c 29 39 addi r9,r9,15560 c00000000008c618: 00 48 2a 7c cmpd r10,r9 c00000000008c61c: 14 00 82 40 bne c00000000008c630 <rcu_process_callbacks+0x48> c00000000008c620: 6d d6 f8 4b bl c000000000019c8c <arch_local_irq_restore+0x8> c00000000008c624: 00 00 00 60 nop c00000000008c628: 40 00 21 38 addi r1,r1,64 c00000000008c62c: ac c3 23 48 b c0000000002c89d8 <_restgpr0_28> c00000000008c630: 00 00 0a e9 ld r8,0(r10) c00000000008c634: 2b 01 e2 3c addis r7,r2,299 c00000000008c638: c8 3c e7 eb ld r31,15560(r7) c00000000008c63c: c8 3c 07 f9 std r8,15560(r7) c00000000008c640: 00 00 00 39 li r8,0 c00000000008c644: 00 00 0a f9 std r8,0(r10) c00000000008c648: 2b 01 42 3d addis r10,r2,299 c00000000008c64c: d8 3c 0a e9 ld r8,15576(r10) c00000000008c650: 2b 01 42 3d addis r10,r2,299 c00000000008c654: d0 3c 4a e9 ld r10,15568(r10) c00000000008c658: 00 50 28 7c cmpd r8,r10 c00000000008c65c: 08 00 82 40 bne c00000000008c664 <rcu_process_callbacks+0x7c> c00000000008c660: 10 00 29 f9 std r9,16(r9) c00000000008c664: 08 00 29 f9 std r9,8(r9) c00000000008c668: 00 00 80 3b li r28,0 c00000000008c66c: 21 d6 f8 4b bl c000000000019c8c <arch_local_irq_restore+0x8> c00000000008c670: 00 00 00 60 nop c00000000008c674: 00 00 3f 2c cmpdi r31,0 c00000000008c678: b0 ff 82 41 beq c00000000008c628 <rcu_process_callbacks+0x40> c00000000008c67c: 00 00 df eb ld r30,0(r31) c00000000008c680: 00 00 3e 2c cmpdi r30,0 c00000000008c684: 08 00 82 41 beq c00000000008c68c <rcu_process_callbacks+0xa4> c00000000008c688: 2c f2 00 7c dcbtct 0,r30 c00000000008c68c: 08 00 bf eb ld r29,8(r31) c00000000008c690: 00 00 3d 2c cmpdi r29,0 c00000000008c694: 10 00 82 40 bne c00000000008c6a4 <rcu_process_callbacks+0xbc> c00000000008c698: 78 fb e3 7f mr r3,r31 c00000000008c69c: 19 f8 03 48 bl c0000000000cbeb4 <kmem_dump_obj+0x8> c00000000008c6a0: 00 00 00 60 nop c00000000008c6a4: 08 00 9f fb std r28,8(r31) c00000000008c6a8: 78 fb e3 7f mr r3,r31 c00000000008c6ac: a6 03 a9 7f mtctr r29 c00000000008c6b0: 78 eb ac 7f mr r12,r29 c00000000008c6b4: 18 00 41 f8 std r2,24(r1) c00000000008c6b8: 78 f3 df 7f mr r31,r30 c00000000008c6bc: 21 04 80 4e bctrl c00000000008c6c0: 18 00 41 e8 ld r2,24(r1) c00000000008c6c4: b0 ff ff 4b b c00000000008c674 <rcu_process_callbacks+0x8c> Sounds like some sorta compiler optimizations, right? Bests, Manili [-- Attachment #2: Type: text/html, Size: 27833 bytes --] ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2025-11-22 11:33 UTC | newest] Thread overview: 7+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-11-12 14:45 [Help] Microwatt (Zynqwatt) — Kernel halts after Radix MMU init on booting Linux on Zynq version of Microwatt Mohammad Amin Nili 2025-11-13 0:29 ` Oliver O'Halloran 2025-11-13 22:32 ` Mohammad Amin Nili 2025-11-14 2:42 ` Oliver O'Halloran 2025-11-21 14:59 ` Mohammad Amin Nili 2025-11-22 2:48 ` Oliver O'Halloran 2025-11-22 11:32 ` Mohammad Amin Nili
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox