Skip to content

Instantly share code, notes, and snippets.

@CodeZombie
Last active October 13, 2026 17:12
Show Gist options
  • Select an option

  • Save CodeZombie/9a89c669faec17c3d11eddb1eede2370 to your computer and use it in GitHub Desktop.

Select an option

Save CodeZombie/9a89c669faec17c3d11eddb1eede2370 to your computer and use it in GitHub Desktop.
GA36-MB V1.2-20260416 investigation

GA36-MB V1.2-20260416 [The latest(?) fake r36s]

So I got scammed.

I was trying to buy a proper r36s clone so I could play Stardew Valley in the bathtub. My mistake. Instead, I ended up with a "GA36-MB." Identical in every way from the outside, this little guy is quite the imposter. The CPU is allegedly not a Rockchip RK3326, but an Allwinner A33. From a performance perspective that's a little bit of a downgrade, but the real kicker is that the rockchip is a 64 bit chip and the allwinner is 32. More like allloser amirite. I'm not 100% convinced that this is the case though, I've seen a lot of conflicting information on this, so I intend to put the question to rest, though I'm hoping it's a rockchip.

(Spoiler alert: It's not a rockchip. It's a 32-bit allwinner with 512mb of ram.)

The MicroSD card that comes with it: Piece of shit. Create an image of it with dd then throw it away.

Hopefully you didn't pay extra for the """128gb""" model. The "64gb" card it shipped with turned out to be 16, and there are more that enough reports of these things failing early to indicate to you that you should backup the data off of it and throw it in the bin as quickly as possible.

Back up the SD!

Plug the micro sd card into your linux computer (you are using Linux, aren't you?). Identify where it is with

lsblk

Then run

sudo dd if=/dev/sdX of=ga36mb_sdcard.img bs=4M status=progress conv=fdatasync

And finally,

sync

Great now you have a backup. Now throw it on a new 16gb+ microSD card:

sudo dd if=ga36mb_sdcard.img of=/dev/sdX bs=4M status=progress conv=fdatasync

sync

Great!

Getting inside the Micro Sd Card

The official instructions want you to stick a second microSD card in the 'games' slot and use that for your roms.

Fuck that.

I'm a one-sd-card kinda guy (though you may actually get slightly better performance if you use two, idk tho).

The sd card is formatted in some super fucked up way, which made it very annoying to figure out how to mount.

Partitions are overlapping, there's logical partitions nested inside of extended partitions. It's a mess. I can't tell if this is a result of them faking the 16gb SD card into reporting as a 64, or some kind of half-assed copy protection scheme, or both. Anyway, because of this, your OS will not mount it nicely. Not without some help:

We can just manually mount the volume at a certain offset because linux rules.

The .FEX file defines a hard-coded offset:

[card_boot]
logical_start = 40960

With that, we can dedermine the actual physical sectors where all our data lives:

label logical start physical start (sectors / bytes) size (sectors) /dev/ file system notes
bootloader 0x1000000 73728 / 37748736 65536 mmcblk0p2 fat16 splash image(s), magic.bin
env 0x3000000 139264 / 71303168 32768 mmcblk0p5 none raw data
boot 0x4000000 172032 / 88080384 65536 mmcblk0p6 none android bootimg
ARKOS 0x6000000 237568 / 121634816 1048576 mmcblk0p7 fat16 rootfs & splash image(s)
storage 0x26000000 1286144 / 658505728 2097192 mmcblk0p8 ext4 /storage
UDISK 0x66005000 3383336 / 1732268032 27150295 mmcblk0p1 fat32 /storage/roms

and you mount those with this command:

sudo mount -o offset=<offset in bytes> /dev/sdX <mount point>

Where <offset> is the offset, <mount point> is an empty folder to mount to, and sdX is your microSD card identified by lsblk. Note: you can append ,uid=$(id -u),gid=$(id -g) to the end of the offset to mount some of the partitions in a way that lets you access it as not-root, which is slightly more convenient)

From there I would highly recommend going into the bootloader partition and changing the splash screen image

SQUASHFS

Inside of the ARKOS partition is a nice big file called SYSTEM which is a squashfs volume. I beleive we can modify this file to basically create a custom firmware for this stupid ass device. Let's try!

First, I'm gonna grab the squashfs file's stats:

$ unsquashfs -s SYSTEM
Found a valid SQUASHFS 4:0 superblock on SYSTEM.
Creation or last append time Thu May  7 22:00:46 2026
Filesystem size 422180918 bytes (412286.05 Kbytes / 402.62 Mbytes)
Compression lzo
        algorithm lzo1x_999
        compression level 9
Block size 524288
Filesystem is exportable via NFS
Inodes are compressed
Data is compressed
Uids/Gids (Id table) are compressed
Fragments are compressed
Always-use-fragments option is not specified
Xattrs are not stored
Duplicates are removed
Number of fragments 423
Number of inodes 12277
Number of ids 2

aaand, decompressing it gives us a linux root directory. Woohoo.

The bad news:

There's a file in the root dir called ee_arch. I can only assume this is the system architecture, and bad news everyone: it contains three characters "A33".

This doesn't absolutely mean the chip is a 32 bit Allwinner a33, but I can't see what else this file is doing here.

Inside of /etc/os-release it literally says this is a build of EmuELEC for the Allwinner A33 lmao, BUT it also says aarch64 everywhere, which isn't possible on the allwinner chip. So who fucking knows.

Also, usr/bin is full of 32 bit binaries. Fuck.

(cw: cope) It is ENTIRELY POSSIBLE, that this IS a build for the a33 running on the rockchip, as armv8 is backward compatible with armv7, and 64 bit processors can run 32 bit software. It's POSSIBLE. I'm not giving up hope yet.

(EDITOR'S NOTE: He has since given up hope)

More Menus

EmuElec generally has more menus, like scripts and ports, but those are absent here. We can add them back!

find es_systems.cfg in the storage partition, and add something like this:

	<system>
		<name>setup_scripts</name>
		<fullname>Setup Scripts</fullname>
		<path>/usr/bin/scripts/setup</path>
		<extension>.sh .SH</extension>
		<command>%ROM%</command>
		<platform>scripts</platform>
		<theme>scripts</theme>
	</system>

This gives us a new menu option with the build in "setup scripts" that ship with the OS. I've tried most of them and they all fail, and the ones that do work cannot be interacted with except the PortMaster Setup Script, which needs internet to work anyway.

Misc Findings

  • There's a hidden Minecraft for PSP homebrew game in the PSP folder lmao. If you edit the psp folder's gameList.xml you can make it show up in the UI and play it. It works!
  • There's a copy of youtube-dl in /usr/bin lmao. Yeah, that's gonna come in handy on a device with no internet.
  • libSDL2_ttf is broken and cannot load font files because they shipped the wrong version. Awesome. In order to build software for this thing with libsdl2_ttf, you have to ship your own version lol.

TODO:

  • DONE: Get an interactive terminal emulator working. I have the code written and compiling for ARM, but SDL is being rude and not loading fonts.
  • Once thats figure out, write a quick guide for writing+compiling golang/SDL2 homebrew for this thing.
  • Fix the SD card partition. This is probably harder than it seems. Some early boot process requires that the SD card be formatted the way it is, so we can't just repair the overlapping regions and call it a day. We'll have to find out where the system mounts volumes early on and set it. This might be on the built-in flash, which is going to be risky to touch and hard to work with.
  • DONE: [It's an allwinner w/ 512mb ram]. Fetch actual hardware IDs from the kernel. I'm still not 100% sure which SoC this is
  • Wile I don't think I can get PortMasters working on this thing properly, it's theoretically possible to run any 32 bit portmasters games provided the dependencies are all met and can be compiled to 32 bit arm. Sort this out, maybe start a portmasters32 project or something.
  • Stardew Valley has a 32 bit build apparently ??? It probably won't support mods, but the base game should still launch. The mono runtime can be compiled to 32 bit as well, so I can't really see any reason why it' won't work.
  • Override the horrible front-panel LEDs. There should be no LEDs when the screen is on. When the screen turns off, then the LEDs can be turn on to indicate device-is-on. I'll have to find the systemd service or program that manages this.
  • Rebuild the OS. This would be an incredibly tedious and long process and I probably won't do it unless I find schematics of the motherboard. (EDITOR'S NOTE: i'm doing it lmao)
@CodeZombie

CodeZombie commented Jul 25, 2026 •

Copy link
Copy Markdown
Author

The USB OTG port is pretty broken.

Plugging a device into it is a detected event in the kernel, but it doesn't seem to want to actually supply 5v.

dmesg runs a routine to start sunxi_usb_host0 which seems good...
It attempts to "turn on 5V power" by running
axp_gpio_set_data....gpio4:1 (which flips GPIO4 to HIGH)
And then 4 seconds later it automatically runs the sunxi_hcd0_soft_disconnect routine and flips it back to 0

Weird charging behaviour:

If I plug a USB-C->USB-C cable into my my computer's USB-C port and the other end into the GA36's OTG port, nothing happens. No dmesg.

BUT

if if I plug a USB-A->USB-C cable into my computer's USB-A port and the C end into the OTG port on the GA36, the GA36 starts charging.

The funny thing is:
Charging normally with the DC port does the following:

  • emits a wake_lock message in dmesg
  • changes the emuelec battery icon to a "charging" icon, and
  • turns on the red "charging LED" (which is controlled in software via gpio (/sys/class/gpio_sw/PG13/data))

But charging via A->C cable on the OTG port:

  • emits a wake_lock message in dmesg
  • changes the emuelec battery icon to a "charging" icon, BUT
  • Does not turn on the charging LED.

I have no idea what kinda shit they're doing behind the scenes to make that happen but I thought it was funny.

@CodeZombie

Copy link
Copy Markdown
Author

running:

echo 0 > /sys/class/gpio_sw/PL4/data turns the horrible blue LED off ๐Ÿ‘ฏโ€โ™‚๏ธ

However, it comes back on after a few moments.

echo 1 > /sys/class/gpio_sw/PG13/data turns the red charging led on, which then turns itself off after a moment.

There's some kinda cron job or something polling the charging system it's status and adjusting these pins in response. We should adjust that.

@CodeZombie

Copy link
Copy Markdown
Author

Plugging a second microSD card into the "TF2-GAME" slot does not cause a dmesg event, and I can't seem to find where it might be mounting even if dmesg is silent on the matter.

My initial thoughts are that the second microSD slot are not even wired up to the SoC lmao.
At least usb gave me some dmesg events when I plugged something in. This is just nothing.

@CodeZombie

CodeZombie commented Jul 25, 2026 •

Copy link
Copy Markdown
Author

BIG FIND

So there's no .dtb file because ancient linux kernel running on this SoC doesn't use that. Instead, it uses a script.bin which contains the same info. However, the file contents is encoded somewhere on the SD card and I haven't been able to extract it from my image.
According to this post, you can still get the same info with this command:

cd /sys/class/script
echo lcd0_para > dump
cat dump

Which spits out the hardware definitions for the LCD panel!

@CodeZombie

CodeZombie commented Jul 25, 2026 •

Copy link
Copy Markdown
Author

FEL

If I take out the 'os' microSD card and plug the device into my computer via the OTG port, my device detects a device: Allwinner Technology sunxi SoC OTG connector in FEL/Flashing mode

This works with the battery disconnected, which I would recommend. There's a note on the sunxi wiki that says "Before you try to enter FEL mode, make sure that your device is truly powered off. Do not leave any cables attached.", so pulling the battery seems like the easiest way to do that.

I build sunxi-tools and ran sunxi-fel --list and we get:
USB device 001:071 Allwinner A33 0461872a:8f5c0083:2a769807:00000000
woooo

SID

Allwinner chips have a series of eFuses which act as write-once-memory. This is usually used for security-related stuff.
Dumping the state of the eFuses on the GA36-MB gives me

$ sudo sunxi-fel sid-registers
00000000:00000000:00000000:00000000

Which indicates that none of the efuses are set. That's good!

BROM version

sudo sunxi-fel version
AWUSBFEX soc=00001667(A33) 00000001 ver=0001 44 08 scratchpad=00007e00 00000000 00000000

@CodeZombie

CodeZombie commented Jul 25, 2026 •

Copy link
Copy Markdown
Author

HOW TO GET SCRIPT.BIN

Lannig on XDA wrote a bash script which grabs the full, compiled script.bin file from memory on sun8i SoCs. The script included the memory offsets. Before trying to run their script, I decided to try just dumping the data at those memory offsets with dd and see if that gives me the same thing.

Run this on the GA36-MB:
dd if=/dev/mem of=/storage/roms/ports/script.bin bs=4096 skip=274432 count=16

That dumped a file.

I copied it over to my PC and ran bin2fex script.bin > hardware_config.fex and it spat out a file.
Thankfully fex files are human readable INI files, so I could easily look at the values to see if they make sense and correspond with what I already know about the device.

YEP

I already dumped the display info on device and know that it's got a jd9366_8inch driver and powered via axp22_dc1sw, and sure enough: this fex file reports the same thing.

Ladies and gentlemen, we got him.

@CodeZombie

CodeZombie commented Jul 26, 2026 •

Copy link
Copy Markdown
Author

The Bootloader (boot0 and boot1)

The first stage bootloader (boot0) can be extracted from your sdcard image with:

dd if=sdcard.img of=boot0.bin bs=1024 skip=8 count=32

And the second stage bootloader (boot1) can be extracted with:

dd if=sdcard.img of=boot1.bin bs=512 skip=38192 count=1632

You can check the validity of these images with this python script:

import struct

with open("boot1.bin", "rb") as f:
    data = bytearray(f.read())

STAMP = 0x5F0A6C39          # Allwinner checksum seed
CHECKSUM_OFFSET = 0x0C      # offset of checksum field within header

stored_checksum = struct.unpack_from("<I", data, CHECKSUM_OFFSET)[0]

# replace checksum field with stamp value before summing
struct.pack_into("<I", data, CHECKSUM_OFFSET, STAMP)

# pad to multiple of 4 bytes if needed
while len(data) % 4 != 0:
    data.append(0)

total = 0
for i in range(0, len(data), 4):
    total = (total + struct.unpack_from("<I", data, i)[0]) & 0xFFFFFFFF

print(f"Stored checksum:   {stored_checksum:#010x}")
print(f"Computed checksum: {total:#010x}")
print("MATCH :D" if total == stored_checksum else "NOT A MATCH >:(")

Android Boot Image

The boot image is contained within an Android-style boot img file.
We can extract it via

dd if=sdcard.img of=boot.img bs=1M skip=64 count=32 status=progress

And then extract that with abootimg -x boot.img

This gives us three new files:

  • bootimg.cfg - boot config
  • initrd.img - the ramdisk
  • zImage - The kernel

The ramdisk can be extracted with

mkdir ramdisk_extracted
cd ramdisk_extracted
gunzip -c ../initrd.img | cpio -i -d -m -v

(Side Note: There's a .git folder in the ramdisk lmao. It looks like it's never been used though)

Also /etc/issue has the text Welcome to aw163x multi-core linux system. Wtf?

@CodeZombie

CodeZombie commented Jul 26, 2026 •

Copy link
Copy Markdown
Author

UART

I got uart working. Here's the location of the UART pads
Note: RX is normally supposed to connect to your adapter's TX, and TX is meant to connect to your adapters RX.
However, the labels are backwards. RX goes to RX, TX goes to TX.

The pins are 3.0v logic level (or at least they were with my battery at ~60%), so make sure your uart adapter talks at 3.3v logic level (use a multimeter and connect gnd to tx and measure the DC voltage)

The buad rate is 115200

Here's what I got booting the stock OS:

sunxi#HELLO! BOOT0 is starting!
boot0 version : 3.1.0
reg_addr 0x01f00100 =0x00000000
reg_addr 0x01f00104 =0x00000000
reg_addr 0x01f00108 =0x00000000
reg_addr 0x01f0010c =0x00000000
reg_addr 0x01f00110 =0x00000000
reg_addr 0x01f00114 =0x00000000
DRAM DRIVE INFO: V1.5
DRAM CLK =552 MHZ
DRAM simple test OK.
dram size =512
card boot number = 0
card no is 0
sdcard 0 line count 0
[mmc]: mmc driver ver 2014-07-07 16:54
[mmc]: ***Try SD card 0***
[mmc]: SD/MMC Card: 4bit, capacity: 29664MB
[mmc]: vendor: Man 0x00275048 Snr 0x018a2ee8
[mmc]: product: SD32G
[mmc]: revision: 3.0
[mmc]: ***SD/MMC 0 init OK!!!***
sdcard 0 init ok
The size of uboot is 0x000cc000.
sum=0x60dea7aa
src_sum=0x60dea7aa
Succeed in loading uboot from sdmmc flash.
Ready to disable icache.
Jump to secend Boot.
[      0.302]

U-Boot 2017.09-g05bceb2-dirty #lxl (Jul 14 2025 - 11:58:12 +0800)

[      0.310]version: 1.1.0
[      0.400]pmbus:   ready
[      0.402]PMU: 0
[      0.404]PMU: 1
[      0.407]PMU:  1200
[      0.409]PMU:  1008 ,600
AXI=336 Mhz,AHB=200 Mhz, APB1=100 Mhz
set power on vol to default
dcdc1_vol = 3300
dcdc2_vol = 1100
dcdc3_vol = 1200
dcdc4_vol = 0
dcdc5_vol = 1500
aldo2_vol = 2500
aldo3_vol = 3000
dldo3_vol = 3000
find power_sply to end
battery_exist:dc_exist 32...0
only battery exist, limit to dc
fel key new mode
run key detect
no key found
no key input
dram_para_set start
dram_para_set end
[      0.461]DRAM:  512 MiB
relocation Offset is: 15b03000
smcl's set manager is NULL
<axp22, dc1sw>
workmode = 0
MMC:     0
[      0.662][mmc]: mmc driver ver 2014-07-15 17:16:00
[      0.667][mmc]: get sdc_f_max fail,use default sdc_f_max 50000000
[      0.673][mmc]: get sdc_ex_dly_used fail,use default dly
[      0.678][mmc]: SD/MMC: 0
lx...LCD_panel_init
[      0.918][mmc]: ************Try SD card 0************
[      0.952][mmc]: MID 000027 PSN 018a2ee8
[      0.955][mmc]: PNM SD32G -- 0x53-44-33-32-47-6e61705f
[      0.960][mmc]: PRV 3.0
[      0.963][mmc]: MDT m-4 y-2016
[      0.966][mmc]: ---------------mmc->clock 50000000-----------
[      0.972][mmc]: ---------------mmc->bus_width 4--------------
[      0.978][mmc]: SD/MMC Card: 4bit, capacity: 29664MB
[      0.983][mmc]: boot0 capacity: 0KB,boot1 capacity: 0KB
[      0.988][mmc]: ************SD/MMC 0 init OK!!!************
[      0.994]flash init ok
In:    serial
Out:   serial
Err:   serial
--------fastboot partitions--------
-total partitions:6-
-name-        -start-       -size-
bootloader  : 1000000       2000000
env         : 3000000       1000000
boot        : 4000000       2000000
rootfs      : 6000000       20000000
storage     : 26000000      40005000
UDISK       : 66005000      0
-----------------------------------
base bootcmd=run setargs_mmc boot_normal
bootcmd set setargs_mmc
key 0
cant find rcvy value
cant find fstbt value
no misc partition is found
misc_message->command = 0
to be run cmd=run setargs_mmc boot_normal
mount part name bootloader
cant open script.bin, maybe it is not exist
WORK_MODE_BOOT
board_status_probe
axp22_probe_this_poweron_cause...0
[      1.084]key trigger
stage2 resule 0
bmp_logo_display
[      1.133][mmc]: blkcnt should not be 0
screen_id =0, screen_width =640, screen_height =480
frame buffer address 46400000
bitcount = 24
[      1.154]Hit any key to stop autoboot:  0
read boot or recovery all
[      1.883]flash read :offset 4000000, 17254524 bytes OK
no signature
[      1.894]ready to boot
para err in disp_ioctl, cmd = 0xa,screen id = 1
[      1.900][mmc]: MMC Device 2 not found
[      1.904][mmc]:  mmc  not find,so not exit
[      1.908]
Starting kernel ...


Welcome to EmuELEC (Community - EmuELEC is FREE! NOT FOR SALE!): 4.7-Nexus_devel_20260508095232!

[  OK  ] Created slice system-modprobe.slice.
[  OK  ] Created slice system-sixaxis.slice.
[  OK  ] Created slice user.slice.
[  OK  ] Started systemd-ask-password-console.path.
[  OK  ] Started systemd-ask-password-wall.path.
[  OK  ] Reached target paths.target.
[  OK  ] Reached target slices.target.
[  OK  ] Listening on systemd-initctl.socket.
[  OK  ] Listening on systemd-journald-dev-log.socket.
[  OK  ] Listening on systemd-journald.socket.
[  OK  ] Listening on systemd-udevd-control.socket.
[  OK  ] Listening on systemd-udevd-kernel.socket.
         Mounting sys-kernel-debug.mount...
         Mounting var.mount...
         Starting modprobe@configfs.service...
         Starting modprobe@drm.service...
         Starting modprobe@fuse.service...
         Starting show-version.service...
EmuELEC (Community - EmuELEC is FREE! NOT FOR SALE!): 4.7-Nexus_devel_20260508095232

[  OK  ] Created slice system-modprobe.slice.
[  OK  ] Created slice system-sixaxis.slice.
[  OK  ] Created slice user.slice.
[  OK  ] Started systemd-ask-password-console.path.
[  OK  ] Started systemd-ask-password-wall.path.
[  OK  ] Reached target paths.target.
[  OK  ] Reached target slices.target.
[  OK  ] Listening on systemd-initctl.socket.
[  OK  ] Listening on systemd-journald-dev-log.socket.
[  OK  ] Listening on systemd-journald.socket.
[  OK  ] Listening on systemd-udevd-control.socket.
[  OK  ] Listening on systemd-udevd-kernel.socket.
         Mounting sys-kernel-debug.mount...
         Mounting var.mount...
         Starting modprobe@configfs.service...
         Starting modprobe@drm.service...
         Starting modprobe@fuse.service...
         Starting show-version.service...
EmuELEC (Community - EmuELEC is FREE! NOT FOR SALE!): 4.7-Nexus_devel_20260508095232

         Starting storage-log.service...
         Starting swap.service...
         Starting systemd-modules-load.service...
         Starting systemd-timesyncd-setup.service...
         Starting systemd-udev-trigger.service...
[  OK  ] Mounted sys-kernel-debug.mount.
[  OK  ] Mounted var.mount.
[  OK  ] Finished modprobe@configfs.service.
[  OK  ] Finished modprobe@drm.service.
[  OK  ] Finished modprobe@fuse.service.
[  OK  ] Finished systemd-modules-load.service.
[  OK  ] Finished systemd-timesyncd-setup.service.
[  OK  ] Finished systemd-udev-trigger.service.
[  OK  ] Finished storage-log.service.
         Mounting sys-fs-fuse-connections.mount...
         Starting systemd-sysctl.service...
[  OK  ] Mounted sys-fs-fuse-connections.mount.
[  OK  ] Finished systemd-sysctl.service.
setterm: vt220: unknown terminal type
shared memfd open() failed: Function not implemented
Failed to create random directory /tmp/pulse-PKdhtXMmr18n: Read-only file system
Failed to symlink /storage/.config/pulse/f42fb943923c240fcc3478f56822bdc8-runtime.tmp: Read-only file system
[  OK  ] Finished swap.service.
[  OK  ] Reached target swap.target.
         Mounting tmp.mount...=    0KB vq=    0KB sq=    0B f=0/0
[  OK  ] Mounted tmp.mount. aq=    0KB vq=    0KB sq=    0B f=0/0
[  OK  ] Reached target local-fs.target.
         Starting machine-id.service...vq=    0KB sq=    0B f=0/0
[rawvideo @ 0xb20006b0] Estimating duration from bitrate, this may be inaccurate
Input #0, rawvideo, from '/usr/config/splash/splash-1080.raw':
  Duration: 00:00:00.04, start: 0.000000, bitrate: 184320 kb/s
  Stream #0:0: Video: rawvideo (RGB[24] / 0x18424752), rgb24, 640x480, 184320 kb/s, 25 tbr, 25 tbn, 25 tbc
[  OK  ] Finished machine-id.service.B vq=    0KB sq=    0B f=0/0
         Starting systemd-journald.service... 0KB sq=    0B f=0/0
         Starting systemd-tmpfiles-setup-dev.service...  0B f=0/0
[  OK  ] Started systemd-journald.service.    0KB sq=    0B f=0/0
    nan M-V:    nan fd=   0 aq=    0KB vq=    0KB sq=    0B f=0/0
         Starting systemd-journal-flush.service...
[  OK  ] Finished systemd-tmpfiles-setup-dev.service.
setterm: vt220: unknown terminal type
[  OK  ] Finished systemd-journal-flush.service.
[  OK  ] Finished show-version.service.
         Starting systemd-tmpfiles-setup.service...
[  OK  ] Finished systemd-tmpfiles-setup.service.
         Starting add-entropy.service...
         Starting envconfig.service...
         Starting openssl-config.service...
         Starting systemd-journal-catalog-update.service...
         Starting systemd-update-utmp.service...
         Starting tz-data.service...
         Starting usercache.service...
         Starting userconfig.service...
[  OK  ] Finished add-entropy.service.
[  OK  ] Finished envconfig.service.
[  OK  ] Finished openssl-config.service.
[  OK  ] Finished systemd-journal-catalog-update.service.
[  OK  ] Finished tz-data.service.
[  OK  ] Finished usercache.service.
         Starting systemd-udevd.service...
[  OK  ] Finished systemd-update-utmp.service.
[  OK  ] Started systemd-udevd.service.
[  OK  ] Finished userconfig.service.
         Starting network-base.service...
[  OK  ] Finished network-base.service.
[  OK  ] Reached target sysinit.target.
[  OK  ] Started systemd-tmpfiles-clean.timer.
[  OK  ] Reached target time-set.target.
[  OK  ] Started fstrim.timer.
[  OK  ] Reached target timers.target.
[  OK  ] Listening on dbus.socket.
[  OK  ] Listening on lircd.socket.
[  OK  ] Reached target sockets.target.
[  OK  ] Reached target basic.target.
         Starting cron.service...
         Starting dbus.service...
[  OK  ] Started eventlircd.service.
[  OK  ] Reached target network.target.
         Starting pulseaudio.service...
[  OK  ] Started sixaxis@multi-user.service.
         Starting systemd-logind.service...
[  OK  ] Started udt-headphones.service.
[  OK  ] Started udt-hotkeys.service.
[  OK  ] Started udt_battery.service.
[  OK  ] Started udt_pwr.service.
[  OK  ] Started wsdd2.service.
[  OK  ] Started dbus.service.
[  OK  ] Started cron.service.
         Stopping wsdd2.service...
[  OK  ] Started systemd-logind.service.
[  OK  ] Started pulseaudio.service.
[  OK  ] Stopped wsdd2.service.
[  OK  ] Reached target multi-user.target.
[  OK  ] Reached target graphical.target.
         Starting emuelec-autostart.service...
         Starting systemd-update-utmp-runlevel.service...
[  OK  ] Finished systemd-update-utmp-runlevel.service.
         Starting emustation.service...
         Stopping pulseaudio.service...
[  OK  ] Stopped pulseaudio.service.
[  OK  ] Stopped tz-data.service.
         Stopping tz-data.service...
         Starting tz-data.service...
[  OK  ] Finished tz-data.service.
[  OK  ] Started emustation.service.
[  OK  ] Finished emuelec-autostart.service.
[  OK  ] Reached target emuelec.target.

@CodeZombie

CodeZombie commented Jul 26, 2026 •

Copy link
Copy Markdown
Author

welp

Well, this would have been nice to know about before I started messinag around with dd, binwalk and hexdump

This would have turned the last 2 days of work into a 20 minute job :^)

EDIT: I couldn't get any of those tools to work properly. Maybe user error, maybe they're broken. Either way, I didn't need em.

@CodeZombie

CodeZombie commented Jul 26, 2026 •

Copy link
Copy Markdown
Author

the real microSD partition layout

So no normal partition-table-reading tools (sgdisk, fsdisk, fdisk, etc) were able to read a partition table from the sd card image, so I had to pull the already-parsed partition layout from the running system using a script:

for p in /sys/block/mmcblk0/mmcblk0p*; do
  echo "$p: start=$(cat $p/start) size=$(cat $p/size)"
done

Which gave me: this
(Side Note: I cannot express to you enough how useful it is to have this interactive root terminal on the device. Big round of applause to me for writing it)

Here it is in table format:

dev start size
mmcblk0p1 3383336 27150295
mmcblk0p2 73728 65536
mmcblk0p3 1 2
mmcblk0p5 139264 32768
mmcblk06 172032 65536
mmcblk0p7 237568 1048576
mmcblk0p8 1286144 2097192

The partitions are named via the kernel cmdline:

$ cat /proc/cmdline
console=ttyS2,115200 root=/dev/mmcblk0p7 init=/init disk=/dev/mmcblk0p8 ion_cma_512m=8m ion_cma_1g=176m ion_carveout_512m=0m ion_carveout_1g=150m coherent_pool=4m loglevel=0 partitions=bootloader@mmcblk0p2:env@mmcblk0p5:boot@mmcblk0p6:rootfs@mmcblk0p7:storage@mmcblk0p8:UDISK@mmcblk0p1 boot_type=1 config_size=0

TURNS OUT The actual partition table is stored on the SD card in Sunxi's softw411 format and can be extracted with:

dd if=sdcard.img of=sunxi_mbr.bin bs=512 skip=40960 count=64

@CodeZombie

CodeZombie commented Jul 27, 2026 •

Copy link
Copy Markdown
Author

I have compiled the Linux kernel version 6.6 with a .dts file I created from cross referencing values in the .fex I dumped.

There's a non-zero chance the values I have in the .dts will apply a voltage where voltage aught not be and fry some component on the board.

I have injected it into the boot.img with abootimg -u boot.img -k zImage and am now going to push it to the SD card with dd if=android_boot.bin of=/dev/sdd bs=1M seek=64 count=16 conv=notrunc status=progress

Now I'm hooking up UART...

And plugging in the sd card...

And booting....

And nothing. It passes stage 0 and 1, gets to the 'loading kernel... message and stops.

That's not the worst output. Nothing seems broken, so that's good? I just don't have UART output, so likely my .dts is wrong wrt the UART section. Let me try again and come back.

Update: i got it >:)

@CodeZombie

Copy link
Copy Markdown
Author

The bootloader injects the following into whatever linux kernel it tries to launch:

0.000000] Kernel command line: console=ttyS2,115200 root=/dev/mmcblk0p7 init=/init disk=/dev/mmcblk0p8 ion_cma_512m=8m ion_oot@mmcblk0p6:rootfs@mmcblk0p7:storage@mmcblk0p8:UDISK@mmcblk0p1 boot_type=1 config_size=0 earlycon=uart,mmi.

@CodeZombie

Copy link
Copy Markdown
Author

GETTING THE SD CARD TO MOUNT WITHOUT BREAKING ANYTHING

Okay! I figured this out.
There IS an MBR on the microSD card, however, it's boot signature is invalid (c3 5a instead of 55 aa), so no OS will recognize or read it as a partition table.

However, you can manually repair the boot signature by first identifying your microSD card with lslbk and then running printf '\x55\xaa' | sudo dd of=/dev/sdX bs=1 seek=510 conv=notrunc (swapping /dev/sdX for your actual microSD card device path).

After that runs you should immediately see two mountable partitions. The larger of them will be your ROMS partition. Woohoo.

@CodeZombie

Copy link
Copy Markdown
Author

We can flash any sd card with the first 100mb of data from the original sd card and it'll pass right through the bootloader and start loading up the kernel (which will fail because everything else the kernel needs past 100mb isn't there).

So this seems good, we can start creating our own partitions just after the kernel partition (the one with the bootimage.bmp in it as well) and use that unmodified from the original SD card, and then carve out a fat16, ext4, and fat32 partitions for the kernel image, rootfs, and roms, respectively.

@CodeZombie

CodeZombie commented Jul 29, 2026 •

Copy link
Copy Markdown
Author

Small update: I have the ga36mb-v1.2 booting debian 13 with the kernel version 6.6.

I'm in the process of mapping out the regulator voltages so it will stays on for more than 45 seconds, and then I'll be working on drivers.

I wanted to note a little trick I figured out, which seems obvious in retrospect, and will only really be useful in a handful of situations for people, but whatever.

So the bootloader, which we have not customized or rebuilt at all knows how to set up power controls for this device properly. The voltages inside the bootloader binary were set by the manufacturer. We want those, but we can't just decompile the bootloader and see what they set reg_dc5ldo to.
Ideally that info would be in the .fex we dumped, but it's not.

What we can do instead is just ask the regulator driver itself. We know it's an axp22x because the fex says so, so that's easy enough to get working inside of linux. And once we're inside of linux, we can ask the driver itself what every regulator is currently set to by calling cat /sys/kernel/debug/regulator/regulator_summary

root@debuerreotype:~# cat /sys/kernel/debug/regulator/regulator_summary
 regulator                      use open bypass  opmode voltage current     min     max
---------------------------------------------------------------------------------------
 regulator-dummy                  9   21      0 unknown     0mV     0mA     0mV     0mV 
    1c20800.pinctrl-vcc-pb        1                                 0mA     0mV     0mV
    1c20800.pinctrl-vcc-pg        1                                 0mA     0mV     0mV
    1f02c00.pinctrl-vcc-pl        1                                 0mA     0mV     0mV
    vcc-io                        4    3      0 unknown  3300mV     0mA  3300mV  3300mV 
       1c0f000.mmc-vmmc           1                                 0mA  3300mV  3400mV
       1c20800.pinctrl-vcc-pf     1                                 0mA     0mV     0mV
       vcc-lcd                    1    0      0 unknown  3300mV     0mA     0mV     0mV 
    vdd-cpu                       1    0      0 unknown  1100mV     0mA  1040mV  1320mV 
    vdd-sys                       1    0      0 unknown  1200mV     0mA  1200mV  1200mV 
    dcdc4                         0    0      0 unknown  1100mV     0mA     0mV     0mV 
    vcc-dram                      1    1      0 unknown  1500mV     0mA  1500mV  1500mV 
       dc5ldo                     0    0      0 unknown  1100mV     0mA     0mV     0mV 
    aldo1                         0    0      0 unknown  3000mV     0mA     0mV     0mV 
    vcc-aldo2                     0    0      0 unknown  2500mV     0mA  2500mV  2500mV 
    vcc-aldo3                     0    0      0 unknown  3000mV     0mA  3000mV  3000mV 
    eldo1                         0    0      0 unknown   700mV     0mA     0mV     0mV 
    eldo2                         0    0      0 unknown   700mV     0mA     0mV     0mV 
    eldo3                         0    0      0 unknown   700mV     0mA     0mV     0mV 
    dldo1                         0    0      0 unknown   700mV     0mA     0mV     0mV 
    dldo2                         0    0      0 unknown   700mV     0mA     0mV     0mV 
    vcc-dldo3                     0    0      0 unknown  3000mV     0mA  3000mV  3000mV 
    dldo4                         0    0      0 unknown   700mV     0mA     0mV     0mV 
    rtc_ldo                       1    0      0 unknown  3000mV     0mA  3000mV  3000mV 
    ldo_io0                       0    0      0 unknown  3800mV     0mA     0mV     0mV 
    ldo_io1                       0    0      0 unknown  3800mV     0mA     0mV     0mV

no more guessing! Now we can just declare those regulators (that aren't already being inherited by other dtsi files) as nodes in the dts and pin them at their voltages alongside regulator-always-on; (because we don't have devices mapped to many regulators yet, so linux is still liable to turn them off if it thinks nothing is using it, as is the case with the display driver, uart, probably usb, and other things)

EDIT: ideally this would be run from within the original firmware, as that will definitely have the regulators set to it's correct voltages, AND should give us the correct voltage ranges as well. I'll have to do that at some point.

@CodeZombie

Copy link
Copy Markdown
Author

Is it really only half a gig of ram?

The markings on the DRAM chips, SEC 429 K4B4G16, seem to indicate that each module is a 512mb.
It's very possible that this is actually true, but for whatever reason, the bootloader was not configured to initialize the full 1gb.

Actually determining if this is true is going to require fully recompiling the u-boot bootloader, which itself is going to require a proper .dts file with all the PMU and DRAM settings written correctly. That is going to be a physically risky task, so I may hold off on that until I can acquire some spare units (if you wanna send me your GA36-MB, I'll buy it for real cheap!)

@mortelil

Copy link
Copy Markdown

Hey! I've been working on restoring some functionality to my GA36-MB V1.1. I have fixed the partitions on my SD card and have a basic version of portmaster up and running that can install and run most armhf binaries. I got a file manager, but can't get USB OTG or the second SD card slot working. I'm impressed by your work, and I am also curious how similar the different versions of the devices are. I know my v1.1 unit can boot images ripped from v1.0. I had a tester try my image on a v1.2 device and he was unable to boot it.

@CodeZombie

CodeZombie commented Jul 31, 2026 •

Copy link
Copy Markdown
Author

I have fixed the partitions on my SD card and have a basic version of portmaster up and running that can install and run most armhf binaries.

That's super impressive. I compiled an armhf mono runtime last week in the hopes of getting Stardew Valley running, but never managed to get it to work. I would love to see your notes, if you have any.

I've been unable to get the v1.2's OTG or 2nd SD slot working on the stock firmware as well, but I'm hopeful that it's a software problem and not an issue with the hardware.

For the 2nd SD slot, the hardware definition file (.fex) contains incomplete information about slot 2 (mmc1), primarily no "sdc_power_supply" entry. I think this prevents the os from registering the slot as a usable peripheral, so as far as emuelec is concerned: it doesn't exist. I haven't been able to confirm this though, It's possible the hardware routing on the PCB is just wrong. The slot is surrounded by resistors though, and I'd be surprised that they spent the additional 0.1 cent per board populating those pads for a peripheral they knew didn't even function, but who knows.

USB OTG is more interesting though, I really think it's possible to get it working.

If you turn the device off, pull the battery, and take out the OS sd card, then plug the device into your PC with a USBC-to-A via the device's OTG port, your computer will recognize the device as an ALLWINNER FEL, and you can get into a simple debugger mode on the A33. This proves that there's a functioning data line between the OTG port and the SoC, which I'm not sure exists on the "DC" usb port.

It doesn't, however, prove that the axp22* regulator is wired up to the OTG port in a way that's possible to drive 5v to it, but the .fex file indicates that it might be. I'm going to have to probe around with my multimeter to figure out what's hooked up to what and mess around with my DTS file to get debian to recognize it (oh yeah I got debian 13 booting on the v1.2. Had to decompile the display driver from the original firmware to extract the correct init sequence and then port this driver to linux 6.12 lol)

I'm also quite curious about how similar the three devices are. If v1.0 and v1.1 images are both interchangeable on the same hardware, I'd wager that they're functionally identical. The real test would be to compare the .fex files from all three devices.

If you can run commands/scripts on your v1.1, could you try running dd if=/dev/mem of=/storage/roms/script.bin bs=4096 skip=274432 count=16 and uploading the resulting script.bin file somewhere? It can be decoded with bin2fex script.bin > hardware_config.fex but if you don't wanna install whatever package provides that bin I can do it.

@naz93-py

naz93-py commented Aug 1, 2026

Copy link
Copy Markdown

hi
ALLWINNER a33 ??
ALLWINNER a33 is 4 core, i have in system info 2 core ! so maybe i have ALLWINNER a23, "2 defrint module"
ram 512m + swap ~480 that is whay sd brack with time
is the second sd slote work ? and FN key too?
i have : GA36-MB V1.2-20260416

@naz93-py

naz93-py commented Aug 1, 2026

Copy link
Copy Markdown

user@******:~$ sunxi-fel version
AWUSBFEX soc=00001650(A23) 00000001 ver=0001 44 08 scratchpad=00007e00 00000000 00000000
i have ALLWINNER a23

@mortelil

mortelil commented Aug 1, 2026

Copy link
Copy Markdown

I've exacted my .fex file

Would you mind sharing your terminal app? I so miss having a terminal. Currently I am making small apps just to run one command. The UART stuff gives you full remote terminal access? Like SSHing into the device? I really should have written a terminal app to begin with.

My goal has been to improve the device and make it more usable by modifying the original image. Hearing you getting Debian 13 running is super cool. This paves the way for truly custom firmware for the device. The device truly is shitty, but browsing r/R36S on reddit proves it is a very common device at the moment, and even though it's shitty my kids love it.

Anyway, I would love a copy of your Debian build.

Cool that you actually had some success with the USB-OTG port, I though it was totally dead. Having the device recognize USB devices could get it online which would make working on the device so much easier. On my V1.1 I never got host mode behaving properly.

I've been using a lot of AI for my work, I'm not much of a programmer really. I'll do a more in depth write up of what I have done and the issues I faced later.

@mortelil

mortelil commented Aug 1, 2026

Copy link
Copy Markdown

I soldered up UART on my device too. I used solid core 22 AWG wire, which was not the best idea since the tension from the stiff wire ended up ripping of both my RX and TX pad. Really high quality pcb for sure! I ended up unmasking the traces and soldering onto them.

The RX TX was messed up on my board too, and RX to RX and TX to TX worked fine.

I interrupted the device during boot and ran these commands which granted me a root shell after boot:
run setargs_mmc
setenv bootargs ${bootargs} systemd.debug_shell
run boot_normal

Time to poke around a bit.

@mortelil

mortelil commented Aug 1, 2026

Copy link
Copy Markdown

It seems the USB OTG port actually works! It just provides no power. With a breakout + multimeter I get 0โ€ฏV on VBUS even when dmesg shows Set USB Power ON and otg_drv_vbus is high. However, using a powered usb hub/ethernet adapter, I can see usb drives with lsblk and I could even get online after getting a usb ethernet driver loaded.
Theres still a lot to do, mount supports hardly any filesystems, there are a lack of common drivers in the kernel, and no sshd. With all the quirks, broken packages, and the old kernel of the stock image, I feel like working on getting modern debian up and running with a EmulationStation setup is much more worthwhile than fighting EmuELEC.

@CodeZombie

Copy link
Copy Markdown
Author

Absolutely incredible work.

I'm so glad the OTG port actually works, even if you do have to supply power via a hub. That opens up a lot of possibilities, and getting it online is huuuuge, fantastic work, seriously.

Really glad you didn't tear up the UART pads too bad. Having a uart root shell on the og firmware is so helpful. I was able to iterate much faster and probe for voltages, and then managed to pull all the regulator ranges. This is so much faster than typing in my little console app. I'll put that online today or tomorrow in case it's useful for anyone else, but it's basically useless to me now that we have uart root shell.
(Note for myself: I use GNU Screen for tty, and I had to mash the s key as soon as the device starts up to interrupt sunxi-uboot in order to put mortelil's commands in)

I'll put my Debian build files/instructions online today. The .fex file you extracted is identical to mine, so I would hazard a guess that the hardware is mostly identical, so it might boot on your v1.1 unmodified, but I'm fairly confident it won't fry anything.

For reference, here are the regulator values I pulled from the original firmware:

for r in /sys/class/regulator/regulator.*; do
    printf "%-15s current: %s | min: %s | max: %s \n" "$(cat $r/name 2>/dev/null)" "$(cat $r/microvolts 2>/dev/null)" "$(cat $r/min_microvolts 2>/dev/null)" "$(cat $r/max_microvolts 2>/dev/null)"
done
dummy           current:  | min:  | max:  
axp22_dcdc1     current: 3300000 | min: 1600000 | max: 3400000 
axp22_dldo1     current: 700000 | min: 700000 | max: 3300000 
axp22_dldo2     current: 700000 | min: 700000 | max: 3300000 
axp22_dldo3     current: 3000000 | min: 700000 | max: 3300000 
axp22_dldo4     current: 700000 | min: 700000 | max: 3300000 
axp22_eldo1     current: 700000 | min: 700000 | max: 3300000 
axp22_eldo2     current: 700000 | min: 700000 | max: 3300000 
axp22_eldo3     current: 700000 | min: 700000 | max: 3300000 
axp22_dc5ldo    current: 1100000 | min: 700000 | max: 1400000 
axp22_ldoio0    current: 3800000 | min: 700000 | max: 3300000 
axp22_ldoio1    current: 3800000 | min: 700000 | max: 3300000 
axp22_dcdc2     current: 1100000 | min: 600000 | max: 1540000 
axp22_dc1sw     current: 700000 | min: 0 | max: 0 
axp22_dcdc3     current: 1040000 | min: 600000 | max: 1860000 
axp22_dcdc4     current: 1100000 | min: 600000 | max: 1540000 
axp22_dcdc5     current: 1500000 | min: 1000000 | max: 2550000 
axp22_rtc       current: 3000000 | min: 3000000 | max: 3000000 
axp22_aldo1     current: 3000000 | min: 700000 | max: 3300000 
axp22_aldo2     current: 2500000 | min: 700000 | max: 3300000 

@CodeZombie

CodeZombie commented Aug 2, 2026 •

Copy link
Copy Markdown
Author

@naz93-py
That's really disappointing to hear. The manufacturers of this device are clearly going as cheap as possible, and I guess they got a batch of a23 chips and started mixing those into production.

As far as I've been able to tell, the a23 is identical to the a33 except that it has 2 cores instead of 4, so performance is going to be significantly worse. Everything should work exactly the same, it'll just be slower.

Great point about the danger of keeping swap on the microSD card. I noticed that they put a 300mb swapfile in one of the partitions, but didn't put it together that it would murder your microSD. That would explain why everyone's microSDs die quickly using the stock firmware. I know the microSDs cards themselves are factory rejects and are prone to spontaneous block-death , but I would be really curious to see if the stock firmware on "legit" r36s' have swap on the sd as well.

For future os developers (and me): Consider zswap/zram, and do NOT put swap on the sd card.

I'm unsure if the "FN" key does anything. I couldn't get it to do anything at all in the stock firmware, but that could just be a software problem. We'll have to probe the GPIOs to figure out if it has any connection to the SoC.

@mortelil

mortelil commented Aug 2, 2026

Copy link
Copy Markdown

Great! I have built my own debian image, no screen as of yet though, looking forward to getting your display driver up and running. I am working on that second RAM chip myself at the moment. Maybe we should set up a repo so we can share our work more easily?

@mortelil

mortelil commented Aug 2, 2026

Copy link
Copy Markdown

After lots of trying I give up. Thought for a while that there were an extra 512MB ram to unlock on this device, but I am fairly sure each of the chips are 256MB. I both modified boot0 and got mainline u-boot halfway running. Mainline reads the memory as rank=2 - two memory chips that respond, trying to change rows from 14 to 15 just gives me a kernel panic when trying to do pretty much anything after boot.
Also, I noticed i spilled some IPA on one of the chips when cleaning up after soldering. The drop of IPA washed away the markings on the chip. Which leads me to belive they are definitely fake markings.

@CodeZombie

Copy link
Copy Markdown
Author

Ah, that's a shame, but not all that surprising. I guess 2x256mb chips were cheaper than 1x512.

I created a repo here with my build environment: https://github.com/CodeZombie/GA36-MB-Linux

That includes the display driver and the .dts, and the Dockerfile has all the commands necessary to get it all hooked up together.

@mortelil

mortelil commented Aug 2, 2026

Copy link
Copy Markdown

Yeah but why bother sanding them down and painting on fake markings? And same for the SOC which say rockchip. I don't get it, who are they trying to fool? ๐Ÿ˜‚ Anyway, great job setting up the repo, I hope to contribute something tomorrow!

@Makcololosh

Makcololosh commented Oct 13, 2026 •

Copy link
Copy Markdown

So if it runs Allwinner A33 and has 512mb of ram, its basically a miyoo a30, and it has some good custom fw (minui, spruceos)
Is there any chance to port those cfws to our system?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment