Mmm (Memory Map Manipulator) perfect tool to (un?)break your SOC!

So i have this tool, where you can manipulate the registers of your SOC from your userpace. This is meant to be used by power users and most likely full of bugs but in past i leveraged this a lot when porting noname chinese tables to linux.

The idea is to mmap the SOC registers from user space and read and write to them. This tool additionally introduces a catalog approach where the target SOC’s memory registers are defined in a very simple format, and the tool calls for the registers for writing and reading.

i have ported rk3588 GPIO Core to and you can clearly see al the GPIOs as below. But it can be extended, with whatever CORE is available in the SOC say, vop2, dramctl, vpu, gpu, and my actual target pvtm.

So yeah here it is in case someone picks an interest.

Here are the external signals on GPIO4 pins ie (A0=0, and A1=1)

[alarm@alarm mmm]$ sudo python mmm.py get -c rk3588 -d GPIO4 -r EXT_PORT
-c rk3588 -d GPIO4 -r EXT_PORT -p A0 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p A1 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p A2 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p A3 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p A4 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p A5 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p A6 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p A7 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p B0 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p B1 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p B2 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p B3 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p B4 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p B5 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p B6 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p B7 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p C0 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p C1 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p C2 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p C3 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p C4 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p C5 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p C6 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p C7 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p D0 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p D1 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p D2 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p D3 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p D4 = 1, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p D5 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p D6 = 0, (default=0)
-c rk3588 -d GPIO4 -r EXT_PORT -p D7 = 0, (default=0)

to start you can basically issue:

sudo python mmm.py get -c rk3588

WARNING: this tool can be very powerful, and as spiderman’s uncle said, it comes with big responsbilitiy. Ie: if you set a gpio to output true where it is phsically grounded without pull resistor, there is a certain risk that you might actually burn your socs GPIO output.

NOTE: to make it work you either need to pass iomem=relaxed to your kernel command line or you need to build your kernel with CONFIG_STRICT_DEVMEM=n CONFIG_IO_STRICT_DEVMEM=n options.

Edit: forgot to link the actual stuff :slight_smile:

1 Like

Thank you! After adding this to cmdline it looks like this:

root@rock-5b:/usr/local/src/mmm# python3 mmm.py get -c rk3588 | grep -i pvtm
-c rk3588 -d CORE_B0_PVTM -r VERSION -p reserved = 0, (default=0)
-c rk3588 -d CORE_B0_PVTM -r VERSION -p VERSION = 0, (default=515)
-c rk3588 -d CORE_B0_PVTM -r CON0 -p START = 0, (default=0)
-c rk3588 -d CORE_B0_PVTM -r CON0 -p OSC_EN = 0, (default=0)
-c rk3588 -d CORE_B0_PVTM -r CON0 -p OSC_SEL = 0, (default=0)
-c rk3588 -d CORE_B0_PVTM -r CON0 -p SEED_EN = 0, (default=0)
-c rk3588 -d CORE_B0_PVTM -r CON0 -p reserved = 0, (default=0)
-c rk3588 -d CORE_B0_PVTM -r CON0 -p WRITE_ENABLE = 0, (default=0)

Is this expected output or still something wrong? Also I saw the other PVTM candidates than big core 0 are commented. Why exactly?

Yeah i know PVTM not working so i commented them out, for some other PVTMS (ie: CORE_L) it even crashes the kernel on read so i commented them out.

My observation is for some memory areas, i presume kernel is still preventing to mmap or providing bogus 00 values. I also paced this issue with Allwinner SOC and DMC (memory contolelr) core. However they are readable when accessing from uboot command line (mainline). This can also be reproduced with devmem tool from busybox as well.

I implemented the GPIO core just to verify my approach is at least correct. Any ideas why bogus null values are there i would like to hear.

EDIT: https://github.com/radxa/kernel/blob/0a544b8c7f0c7eaad7c98498b9e18b5accfcff39/drivers/char/mem.c#L161
I am really suspecting this line, but i am notsure how code would even branch here, allowedvariable can only be 0 or 1 in arm64 according to here:
https://github.com/radxa/kernel/blob/0a544b8c7f0c7eaad7c98498b9e18b5accfcff39/arch/arm64/mm/mmap.c#L61.
May be i should attach a debugger somewhere to trace, but i think it is quite challenging to do it inside the kernel.

EDIT2: I think the only way to workaround this is just writing a dummy kernel module where it copies to physical memory to user space insecurely. I have a proof of concept and seems to be working. I will update later again.

@JonGroff Currently i have only added GPIO devices into rk3588 catalog, theoretically NPU would also work when implemented but please note that this tool can be notoriously slow if you are planning to interface it to some library for daily use. It is meant to be a hacking tool rather than user space driver toolkit.

@tkaiser For the PVTM iomem region providing all zero, i have written a kernel module called insecure_mem where all the security checks are bypassed but the result is still the same 00. This means that kernel itself would also not see PVTM regions. So i assume iomem region for PVTM is in a state that no values are valid for them. That part still needs investigation.