Porting android device to PostmarketOS (Part-II)
Earlier in the 1st part, the focus was purely on toolchain setup, and get the project working directory up-to-date. This part i.e 2nd part is intended to verify and correct the default outputs generated by initial pmbootstrap init, as well as get a concrete working plan. This also includes resolving the existing shipped source tree for gta4l, and understand the vendor specific naming of device models, to get complete idea of the board slapped on top of qualcomm SM6115 Snapdragon 662. Last time we missed an important step which is to verify boot offsets from an extracted boot.img file of the android device. If any fixes are to be done we edit the device info.
Second stage is to write sm6115-samsung-gta4l.dts, though this may sound simple, but the most horrific part is the vendor actually never provides a single file dts for T505(specific model), and hence we used dtc(Open Firmware device tree compiler), to get a working dts straight out of the dtbo image provided by the bootloader to the kernel on system boot. Well the issue is, dtc is based on reverse engineering, and the whole basis of reverse engineering is assumption based on behaviour. Not to say that the extracted dts is going to be inaccurate, but it means it has chances of being incomplete.
Then we move to the 3rd stage, where we will use the written dts file(mainline compatible) and store it locally on pmaports kernel package. Step 4 will be generate pmbootstrap builds using linux-postmarketos-qcom-sm6115 (mainline kernel), the DTB compiled from the DTS we write and Alpine rootfs(As pmos is based on ALpine linux). Step 5 will be pmbootstrap packaging everything into boot.img(kernel + DTB + pmos initramfs), here we may have to tackle partition space related issues. Step 6 will be flashing via heimdall(Samsung device,odin protocol implemented at hardware level, so we don’t have much realistic chance of bricking).
If step 3,4 and 5 are a success, we replicate it by booting,check dmesg, and iterating to build a complete DTS, as the plan is to move in accordance with peripheral priority. P1: UFS + USB + serial console (boot critical) P2: Regulators (supporting everything above and below) P3: Display + touch + power keys (usability) P4: WiFi + BT + modem (connectivity)
Before moving it is important we get a heavy theoritical grasp of whats inside the chasis and what behaviour to expect. As we know SM6115 is supported in mainline kernel, so we do have SoC base, but if SoC is supported it doesn’t mean a specific device using the SoC has a mainline dts. The SoC clock drivers,regulators and core peripherals work flawlessly but the board specific DTS for gta4l doesn’t exist upstream yet, so we will be writing it.
The pmos wiki lists “Display broken”, a broken display might refer to “black screen” i.e kernel can’t initialise the DSI panel, so we will need serial UART or pstore to debug. Serial UART( Universal asynchronous receiver-transmitter ),Hardware protocol for serial communication. “Asynchronous” means no shared clock between sender and receiver — they agree on baud rate (115200 in this case,we got it from the extracted dts file) beforehand and that’s how they stay in sync. For a hardware serial port on qcomm devices its ttyMSM0. The kernel sends all boot messages to it in real time, before display initializes,before USB comes up or any other mechanism.
Our cmdline has the parameter:
console=ttyMSM0,115200n8
earlycon=msm_geni_serial,0x4a90000
Hence the kernel will output to UART from the very first boot message,the issue being to actually read it we need a physical UART adapted connected to test pads on the PCB(printed circuit board - it is the physical board inside the tablet on which all the chips are soldered). UART test pads are small copper pads on the PCB that the manufacturer uses during factory testing to read serial output. On most consumer devices Samsung doesn’t expose them as a proper connector,they’re just bare pads you’d need to solder thin wires to and connect to a USB-UART adapter to read the serial output on our PC. PStore stands for persistent storage,A kernel subsystem that writes the last kernel log to persistent storage (reserved RAM or flash) before a crash. After a reboot you can read what the kernel printed just before it panicked.
cat /sys/fs/pstore/dmesg-ramoops-0
# this is how we read and debug a kernel panic that reboots the device before we can even read the dmesg.
pstore is wired in via the ramoops node, we will include it,as it costs nothing and saves us if the kernel panics on first boot. Also, “Oops” is kernel terminology for a non-fatal kernel error/crash. ramoops = RAM-based oops logger.
We also take a close look at the android stack Android apps(APK’s) –> Android frameworks(ART,Binder,..)–> Android HALs –> Linux Kernel(with qcomm downstream patches).
In case of pmos, the stack looks like Alpine linux userspace(apk(alpine package) packages,musl,..) –> Linux kernel(downstream/mainline).
Downstream kernels are easier to work with because the vendor distributed android version was in some way compatible with that Linux major version, as Linux doesn’t have a stable ABI, the pattern changes drastically. On the PCB, SoC is the main chip, it integrates CPU,GPU,modem,ISP,DSP,memory controller, and peripheral controllers(like I2C,SPI,UART,USB) everything on it. So SoC has it dts already defined in the mainline in our case, so the difference between SoC and device board(for gta4l) is
SoC (SM6115) — same across all devices using it
Kryo 260 CPU cores
Adreno 610 GPU
Qualcomm modem (LTE)
QCA BT/WiFi controller
Peripheral buses (I2C, SPI, UART…)
Clock/regulator controllers
Board (Samsung gta4l) — unique per device Which I2C bus the touchscreen is on Which GPIO resets the display Which PMIC regulator powers what Panel type (DSI, timing parameters) Battery charger IC model Physical routing of all the above
Hence we know that SoC drivers are upstream, the clock,the regulators, the adreno 610 GPU driver. What isn’t present is the board DTS(device tree source) for gta4l specifically. This DTS file knows the touch screen is located on say I2C bus 3,panel reset on GPIO 47, display DSI timing is X, and so on. We know there is gta4l sibling gta4lwifi, even if it had mainline support we simply couldn’t use it because the board might be the same but peripherals might be connected or communicate to a different bus. Though it would be miles easier to take it as reference to create the DTS. Downstream kerenl isn’t just older kernel, its a fork of an older kernel with a massive out of tree patches from qcomm and vendor specific patches from samsung that never made it up for mainline submission/merge. The reason might be vendor gatekeeping or not satisfying the kernel standards, or android specific interfaces like ION allocator, sync fences built into drivers. Mainline is what we refer to as Linus’s tree, where the same hardware gets implemeted cleanly. The hardware specific code there follows kernel subsystem APIs, which is approved by maintainers. Thus downstream kernel is static support, as its built on top of older kernel with massive out of tree blobs, it has no continuation like the mainline counterpart that receives constant support because of ongoing development.
One of the main distinction is the android specific downstream kernel code, the actual drivers which are written in C, can’t be simply reused because
- Kernel ABI isn’t stable - Linux doesn’t have any stable ABI, which is an intentional well documented policy. So struct layouts might change,function signature changes, entire subsystem gets rewritten.
- The interfaces themselves don’t exist - Downstream drivers might call android specific kernel APIs that mainline might have removed.
As a refernce eg,
// downstream code uses: ion_alloc() // Android ION allocator - removed from mainline sync_fence_create() // Android sync framework - replaced by dma-fence mdss_fb_* // Samsung display subsystem - not in mainlineSo what we understand is the drivers in the kernel exist to pass control to the hardware, which is a binary, commonly referred to as device firmware. What isn’t cleanly portable is the driver itself.
Another important distinction is the dts and dtsi. From the mainline tree, we can look it from the cloned linux repo,
arch/arm64/boot/dts/qcom/sm6115.dtsi # file exists
This direcotry holds CPU nodes,Adreno 610 DRM/KMS driver, btqca driver,clock controller(gcc-sm6115),interconnect nodes, USB,I2C and SPI controllers. Hence our job is writing the sm6115-samsung-gta4l.dts, The .dtsi (i = include) defines all SoC-level nodes like CPU, clocks, interconnects, peripheral controllers, all of which ,with status = “disabled” by default. Our board .dts enables the ones our device uses and fills in board-specific properties.
Another important aspect is the booting criteria -: Android devices handle process and wakelocks differently because they were meant for handheld devices. For eg, say Lineage os holds “Boot” and “userdata” partition, the boot contains the android kernel(we refer it as downstream) + android ramdisk(loads the minimal kernel into memory). Whereas userdata partition holds the android userspace which is ART and framworks along with apps.
After say a theoritical pmos flash, boot partition holds the mainline kernel + pmos initramfs, the userdata partition holds alpine linux rootfs(in our case musl, blueZ, systemd ..). What we don’t dare to touch is the bootloader, the bootloader is layered and holds a hardware level watchdog, so if accidently a different image is flashed, after a certain amount of time the device enters bootloader(samsung refers it as download mode).
Hence the full chain of android booting looks like -:
XBL (Qualcomm bootloader)
→ ABL (Android bootloader)
→ reads BOOT partition
→ loads your pmOS kernel + DTB into RAM
→ kernel boots, reads DTB, initializes hardware
→ mounts Alpine rootfs from USERDATA
→ systemd starts
The main question is where does our superstar the DTS actually resides? There is dual possibility, path A - local(inside pmsports kernel package directory,pmbootstrap compiles it and includes it in the boot image). path B - we submit it to mainline kernel tree as a proper patch. It then resides in “arch/arm64/boot/dts/qcom/sm6115-samsung-gta4l.dts” . Well the tracersal path is A to B in this case, we iterate with A as we are going to build the dts in stages with priority(as mentioned earlier).
More info on the 2 stage bootloader and the watchdog - If a broken DTS is built into the boot.img and flashed, the qcomm bootloader(XBL -> ABL) does not validate the dtb contents at all. So we don’t get thrown to the download mode, the bootloader boots successfully but peripherals/components remain dysfunctional. The bootloader behaviour is also reliant on how wrong the DTS was, which generated the DTB.
Case-1:
Wrong compatible string
.kernel boots, driver doesn't bind, device just absent
.dmesg shows - no driver found
.everything else works fine
Case-2:
Wrong GPIO number for display reset
.kernel boots, display stays black
.SSH in via USB networking, read dmesg,we fix it
Case-3:
Wrong regulator reference
.kernel panics during probe, or device reboots
.flash corrected boot.img, we try again
Case-4:
Completely malformed DTS syntax
.DTB compilation fails at build time
.pmbootstrap won't even produce boot.img
.never reaches the device
The safety net here is USB networking. pmOS initramfs brings up USB networking automatically even before the rootfs mounts. So even with black screen, no touch, nothing working, we can always
# Device shows up as USB ethernet on our PC
ssh user@<ip address>
# Then read kernel log
dmesg | grep -i "error\|fail\|panic"
Thus earlier we had choosen none as UI and dropbear for a minimal ssh client.Console over ssh is our debugging interface. The only case where a reflash might be needed is , kernel panics at boot and device reboots into download mode after watchdog timeout.Then we just reflash the fixed boot.img. Minimal chances of bricking. Watchdog is in hardware, its a timer register in the SoC itself,qcomm refers it as APPS_WDOG.Completely independent of the vendor or any samsung firmware or security policy. Watchdog will work independent of the OS, it resides at the silicon level itself.
The last part being the PIT, a partition table, it describes where partitions live on eMMC(Embedded Multimedia Card),its essentially a soldered-on SD card. Flash storage (NAND) with a controller chip, packaged together and soldered directly to the PCB. The “embedded” means it’s not removable like a regular SD card. No boot conditions are stored in the PIT. These are qcomm firmware partitions, which remain same as stock and are untouched like XBL,TZ,ABL,..