Figuring out what happens at boot
Kobo releases the bootloader source code as well as the kernel sources it uses for their devices here.
After downloading and extracting both, I started investigating a bit more. The first thing I looked at is the bootloader config, and more specifically for any signs of secureboot config options.
But for this, I first need to know which config file I need to look into. I know from cat /proc/cpuinfo that my SOC is of the mediatek MT811x family but I have no clue yet what I should be looking into in this list:
➜ configs ls mt811*
mt8110_m1_emmc_defconfig
mt8110_p2_d1_emmc_defconfig
mt8112_p1_defconfig
mt8113_p1_emmc_defconfig
mt8110_p1_emmc_defconfig
mt8110_p2_d2_emmc_defconfig
mt8113_lp1_emmc_defconfig
mt8113_tp1_emmc_defconfig
Maybe I can find something useful in how the device is initialized, and since I have root access, it shouldn’t be that hard. The device uses busybox init and has this in its /etc/inittab file:
[root@kobo ~]# cat /etc/inittab
# This is run first except when booting in single-user mode.
::sysinit:/etc/init.d/rcS
::respawn:/sbin/kobo_getty.sh
::ctrlaltdel:/sbin/reboot
::shutdown:/etc/init.d/rcK
::restart:/sbin/init
Here’s what /etc/init.d/rcS actually does. I only kept the relevant bits:
- Platform detection: Identifies the Kobo device platform (Freescale, MTK8113T, etc.) and hardware configuration by checking some data into
mmcblk0p6
- Partition management: Determines rootfs and onboard storage partitions based on platform (
mmcblk0p10 in the case of a mt8113t-ntxplatform, and p1 in other cases. Since p10 is actually mounted, it means our platform is identified as mt8113t-ntx)
- User authentication: Handles password file creation and updates for user management when ssh is enabled through a file as mentioned in the first posts
- Filesystem mounting: Mounts proc, tmpfs, sysfs, and various temporary directories
- U-Boot environment: Manages bootloader environment variables for different boot modes (normal/recovery) if you press the screen at certain coordinates (
1070 250 150 600 for our system)
- Device initialization: Sets up udev for hardware detection and device node creation
- Platform-specific setup: Loads firmware, initializes display drivers, and configures platform services
- Filesystem checks: Runs dosfsck on onboard partition and checks for corruption
- Firmware upgrades: Processes
Kobo.tgz and KoboRoot.tgz update packages from onboard storage, which is how updates are distributed by Kobo and the rcS flashes the partitions
- Network configuration: Configures WiFi module and interface based on detected hardware
- System services: Starts D-Bus, SSH, and launches Kobo applications (Nickel, hindenburg)
- Environment setup: Sets locale, Qt, and library paths for the Kobo software stack
It does quite a lot of things actually, but it at least gives us the indication that we are running on an MT8113T SOC, so we can conclude that the u-boot config file we should be looking into is mt8113_tp1_emmc_defconfig. But let’s make absolutely sure of this.
Looking into partition contents
When we listed the partitions above, we discovered that there was a boot_a partition. Since this would likely be where the devicetree and kernel is located, there is probaby some information we can get from it. So let’s dump it using dd, transfer it to my computer and try to look at what’s inside using hexdump.
➜ Partitions hexdump -C -n 256 mmcblk0p4.img
00000000 d0 0d fe ed 00 e1 69 8f 00 00 00 38 00 e1 67 74 |......i....8..gt|
00000010 00 00 00 28 00 00 00 11 00 00 00 10 00 00 00 00 |...(............|
00000020 00 00 00 ca 00 e1 67 3c 00 00 00 00 00 00 00 00 |......g<........|
00000030 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00 00 |................|
00000040 00 00 00 03 00 00 00 04 00 00 00 83 67 77 9b 83 |............gw..|
00000050 00 00 00 03 00 00 00 49 00 00 00 00 55 2d 42 6f |.......I....U-Bo|
00000060 6f 74 20 66 69 74 49 6d 61 67 65 20 66 6f 72 20 |ot fitImage for |
00000070 50 6f 6b 79 20 28 59 6f 63 74 6f 20 50 72 6f 6a |Poky (Yocto Proj|
00000080 65 63 74 20 52 65 66 65 72 65 6e 63 65 20 44 69 |ect Reference Di|
00000090 73 74 72 6f 29 2f 34 2e 39 2f 61 75 64 38 31 31 |stro)/4.9/aud811|
000000a0 33 74 70 31 00 00 00 00 00 00 00 03 00 00 00 04 |3tp1............|
000000b0 00 00 00 0c 00 00 00 01 00 00 00 01 69 6d 61 67 |............imag|
000000c0 65 73 00 00 00 00 00 01 6b 65 72 6e 65 6c 40 31 |es......kernel@1|
000000d0 00 00 00 00 00 00 00 03 00 00 00 0d 00 00 00 00 |................|
000000e0 4c 69 6e 75 78 20 6b 65 72 6e 65 6c 00 00 00 00 |Linux kernel....|
000000f0 00 00 00 03 00 df f0 00 00 00 00 1b 56 32 04 eb |............V2..|
00000100
It shows clearly that it is a U-Boot FIT image and it likely contains the kernel and devicetree indeed 
With uboot-tools, we can actually show much more valuable information.
➜ Partitions mkimage -l mmcblk0p4.img
Image contains unit addresses @, this will break signing
FIT description: U-Boot fitImage for Poky (Yocto Project Reference Distro)/4.9/aud8113tp1
Created: Fri Jan 3 09:10:43 2025
Image 0 (kernel@1)
Description: Linux kernel
Created: Fri Jan 3 09:10:43 2025
Type: Kernel Image
Compression: uncompressed
Data Size: 14675968 Bytes = 14332.00 KiB = 14.00 MiB
Architecture: ARM
OS: Linux
Load Address: 0x40008000
Entry Point: 0x40008000
Hash algo: sha256
Hash value: 0a962151b27cbf88ae94098ddbcc561cbc3c5f4dfc6a8ddee8a10f7cf50f389f
Image 1 (fdt@1)
Description: aud8113tp1-E60T00-D0x00-8113T.dtb
Created: Fri Jan 3 09:10:43 2025
Type: Flat Device Tree
Compression: uncompressed
Data Size: 47651 Bytes = 46.53 KiB = 0.05 MiB
Architecture: ARM
Load Address: 0x44000000
Hash algo: sha256
Hash value: 4f5c5fd5fbfbc126fa30bc3b8949dba3e651fa09210bc4dcf76be2a9829ba372
Image 2 (fdt@2)
Description: aud8113tp1-E60T00-D0x00-BW.dtb
Created: Fri Jan 3 09:10:43 2025
Type: Flat Device Tree
Compression: uncompressed
Data Size: 46771 Bytes = 45.67 KiB = 0.04 MiB
Architecture: ARM
Load Address: 0x44000000
Hash algo: sha256
Hash value: 0af777efe8d4736e6d695ef147b215835483f714219d0df4afc8dc77ae50ea27
Default Configuration: 'conf@1'
Configuration 0 (conf@1)
Description: Boot Linux kernel with FDT blob
Kernel: kernel@1
FDT: fdt@1
Sign algo: sha256,rsa2048:dev
Sign value: 9411d831223692aabcf85d23f6ea6a9d9fb685f59daa10f4272aeea6ca64d0a04c3d4a210522969c9a4fda7d991a0e3823c91d6859e2a360b64b27a3b345db7510bef573427fa26d098c31cb7a740902744b92a48741d5f165085d7c921447f1eaf683dbe94822ab6905c161859f409942121c177547ad1f91a40100854dbb253c0dc116501c5dfe38108c761ddcfad581c8f8ecc4fb3b338bd6826b7e430862af1cd7a63dcbebc949c91584abeaf77f1ccb05f53c8977799f073defb18384c9c303f549da2fc315a363a11013ff19b3b01eeaa45f21824809f9a6a8be85b7446a1a3c74211e8465c1ea54b12203e6ad1bcebec358a8f7fa4ee98edf770f6e25
Timestamp: Fri Jan 3 09:10:43 2025
What this tells us:
- the main device tree file is
aud8113tp1-E60T00-D0x00-8113T.dtb
- the Kernel, the devicetree and FIT image are signed
- they are using yocto as a build system

So our device is most likely an MT8113 TP1 and since our Kobo reader has emmc, I think we can be certain that the U-Boot defconfig we should be looking into is mt8113_tp1_emmc_defconfig.
In the file, we can find that these two options have been activated:
CONFIG_FIT=y
CONFIG_FIT_SIGNATURE=y
This tells us that U-Boot will verify the FIT image signature and its contents before booting it. That means it’s unlikely we will be able to boot another FIT image while keeping the same U-Boot on the Kobo.
Since we have the source code, we could change it’s config, rebuild it and try to flash it, but if the first level bootloader also checks the signature of the U-Boot image, we will just brick it.
There is only one thing left to do to figure that part out…
Opening it up and finding a serial port
I was pleasantly surprised to find a UART port just there on the left of the motherboard, waiting to be used 
By just inserting some jumper wires in the holes, I could connect the ground and RX to my FDTI serial dongle. I just needed to figure out the baudrate now.
Once more, having root access to the device made this step easy:
[root@kobo ~]# cat /proc/cmdline
console=ttyS0,921600n1 rootwait skip_initramfs earlycon=uart8250,mmio32,0x11002000 initcall_debug androidboot.hardware=mt8512 firmware_class.path=/vendor/firmware no_console_suspend root=/dev/mmcblk0p10 quiet hwcfg_p=0x5f2ffe00 hwcfg_sz=110 ntxfw_p=0x5f2fd800 ntxfw_sz=9474
There is a bunch of stuff we can ignore, the most important part for us is console=ttyS0,921600n1. So the baudrate is 921600. I just needed to start minicom and press the Kobo’s power button.
sudo minicom -D /dev/ttyUSB0 -b 921600
The entire boot log is quite long but here are some of the interesting parts:
starting app fitboot
failed to open MISC
choose first boot partition:UBOOT , tee choose: tee_a
Verifying bootimg_ctl Sign
Verifying tz_ctl Sign
fit_verify_configed_sign key-name-hint: dev, result: 0
checking bootimg_ctl - kernel integrity
fit_verify_configed_sign key-name-hint: dev, result: 0
checking tz_ctl - kernel integrity
checking tz_ctl - tee integrity
checking bootimg_ctl - fdt integrity
LK run time: 2080062 (us)
# Some more logs
U-Boot 2019.04_295f85dcd-20240624T173725-B0624174002 (Jun 24 2024 - 17:40:01 +0800)
Model: MT8110 P1 EMMC
DRAM: 506 MiB
MMC: mmc@11230000: 0
In: serial@11002000
Out: serial@11002000
Err: serial@11002000
Net: Net Initialization Skipped
No ethernet found.
Hit any key to stop autoboot: 0
switch to partitions #0, OK
mmc0(part 0) is current device
MMC read: dev # 0, block # 38912, count 1 ... 1 blocks read: OK
MMC read: dev # 0, block # 38912, count 28856 ... 28856 blocks read: OK
## Checking Image at 41000000 ...
FIT image found
FIT description: U-Boot fitImage for Poky (Yocto Project Reference Distro)/4.9/aud8113tp1
# The actual FIT image like we saw before
## Checking hash(es) for FIT Image at 41000000 ...
Hash(es) for Image 0 (kernel@1): sha256+
Hash(es) for Image 1 (fdt@1): sha256+
Hash(es) for Image 2 (fdt@2): sha256+
# Log continues and loads the devicetrees and kernel
So, it seems like the FIT image is verified before U-Boot is even started, which seems to be consistent with what is documented in the mediatek secureboot documentation.
It’s unlikely that with my current knowledge, I can bypass secure boot, even with access to a serial port. It might be that there are exploits existing on Mediatek SOCs and shady tools I could use to try to find a way around it, but I couldn’t find something “off the shelf” to use.
At this point, the first picture that came to my head was the “Directed by Robert B. Weide” meme video… Then it got me thinking.
A way forward
There are two main blockers to figure out:
- Inappropriate kernel version, config and secure boot enabled
- The partition table is fixed and there is not enough empty space for a Nerves system
Let’s look at our options for each of them.
Inappropriate kernel config
Nerves requires additional kernel modules in order to work. It can be for filesystems (F2FS, Squashfs) or to set up ethernet over USB. As listed above, lsmod doesn’t give any output besides the wifi and BT drivers, which means that all modules were built-in the kernel.
A lot of Android kernels are locked like this. They usually don’t allow you to load external, unsigned modules. But I can check two things to see if that’s the case here:
- Look into the kernel config
- Compile a kernel module with the Kobo’s toolchain (also present in their git), and try to load it with insmod on the device
The good news here is that module support is indeed activated in the kernel config:
CONFIG_MODULES=y
# CONFIG_MODULE_FORCE_LOAD is not set
CONFIG_MODULE_UNLOAD=y
# CONFIG_MODULE_FORCE_UNLOAD is not set
CONFIG_MODVERSIONS=y
CONFIG_MODULE_SRCVERSION_ALL=y
It was expected since the wifi module is loaded in the rcS script. And we can also see that:
# CONFIG_MODULE_SIG is not set
Which means that this kernel can load any external, unsigned modules 
All I need to do is compile a module on my computer, send it to the Kobo over ssh and try to load it.
After installing the toolchain, and configuring the kernel menuconfig, I was able to have a f2fs.ko module, ready to be tested:
➜ modules_4.9.77 ls fs/f2fs
f2fs.ko
And it loaded without any issue on the Kobo 
[root@kobo ~]# insmod lib/modules/4.9.77/fs/f2fs/f2fs.ko
[root@kobo ~]# lsmod
f2fs 217559 0 - Live 0xbf31d000
wlan_drv_gen4m 1908365 0 - Live 0xbf14a000 (O)
wmt_cdev_bt 16871 0 - Live 0xbf141000 (O)
wmt_chrdev_wifi 12825 1 wlan_drv_gen4m, Live 0xbf138000 (O)
wmt_drv 1059215 4 wlan_drv_gen4m,wmt_cdev_bt,wmt_chrdev_wifi, Live 0xbf000000 (O)
This gives me a way to extend the current kernel with additional modules.
Finding space in the partition table
One option is to resize the userdata partition and make space for a Nerves system but I would like to keep using the Kobo as an ereader AND boot Nerves when I want/need it. So I need a more “creative” plan.
During my work running Nerves on Android e-waste, I’ve used “subpartitions” for the Fairphone 2. It means that one of the existing partitions was flashed with an entire disk image with its own partition table. Using tools like kpartx in an initramfs allowed me to mount the Nerves system in the subpartitions.
I could do something similar here. I could “use” the existing Kobo system as if it were an initramfs, and, by modifying its init script, mount a Nerves system in a binary file stored in the userdata partition.
In order to see if that would work, I can test this with a buildroot system, built with the Kobo toolchain and see if I can pivot_root to it from the Kobo system.
If it works (I don’t see why it wouldn’t), then my next steps would be:
- Make a basic Nerves System (without kernel and without bootloader)
- Create some kind of “dual-boot” init script that will replace the one currently on the Kobo and implement a “boot Nerves when tap is detected on screen” and boot the Kobo system otherwise
- Mount the Nerves system stored as a binary file on the userdata partition filesystem (so it can be uploaded as a regular file when needed)
I’ve got my work cut out for me…