MSM8960 Mainlining

From postmarketOS

The Qualcomm MSM8960/APQ8064 (Snapdragon S4 series) is a high-end smartphone SoC in 2012.

Status

The following features provided by the MSM8960 SoC are supported in mainline and should work on most MSM8960-based devices after the device tree has been set up:

Certain components (e.g. touchscreen, sensors, ...) are device-specific. With a bit of luck, some will be already supported by mainline, others won't.

Overview

msm8960-mainline/linux

The close-to-mainline Linux kernel fork with patches that have not been accepted/submitted upstream yet. Patches in the master branch are generally in good shape unless marked otherwise. Submit a PR to add new patches there.

msm8916-mainline/lk2nd

The reference bootloader provided by Qualcomm for MSM8960 is open-source and based on Little Kernel (LK). Most devices will use this bootloader in a more or less modified form. On Samsung devices for example, the standard Fastboot interface was replaced by their proprietary download mode.

lk2nd is a fork of the reference bootloader with the goal to provide a unified boot interface on all devices. It provides a standard Fastboot interface (even on Samsung devices!). It also used for a number of mainline quirks (e.g. to set a WiFi/BT MAC address in the device tree; without lk2nd, WiFi/BT does not work out of the box). Eventually, more quirks may be added in the future. It is therefore recommended for all mainline MSM8960 devices. Porting it is generally much easier than porting mainline.

Note Note: lk2nd does not replace your stock bootloader. It is packed into a standard Android boot image and loaded by the stock bootloader from the boot partition. The real boot partition used by lk2nd is then placed with a small offset into the boot partition.

Getting Started

Requirements

  • MSM8960-based device
  • UART if possible (with a bit of luck you may get USB working without UART)
  • (Downstream) Linux kernel source for your device (explaining ways to mainline without kernel source is out of scope for this article)
  • Basic knowledge about Linux, Git, C, Device Trees, ...
  • Willingness to learn about "mainlining" and to figure out things on your own

Before you start

Note Warning: Mainlining (or any unintended "modding") may brick your device permanently if you make a mistake. There is a good chance that nothing happens if you are a bit careful, but do not continue with a device that you need every day. You have been warned!

Mainlining is not easy. MSM8960 is a platform where a lot components can be easily enabled by only setting up a device tree, which can be largely copied from other devices with minor changes. However, at some point you will reach the point where you would like to enable a particular component that is device-specific (e.g. the touchscreen, sensors, ...). In this case you will be largely on your own, and need to figure out how to enable it yourself. (Do you just need to add something to the device tree or even write a new kernel driver?) That requires some familiarity with the way the Linux kernel is working for MSM8960.

The best way to make yourself familiar with the process is to attempt to figure out some simple things on your own. Therefore, this guide will only describe everything until (eventually) USB network is working. For everything else, this article provides only some information that might be helpful to figure it out yourself.

If you have any questions, ask in the mainline channel on Matrix or IRC. Make sure to mention "msm8960" if your question is specific to this article.

Please also join the MSM8960 specific channel on Matrix.

Preparations

Unfortunately, as the MSM8960 platform is outdated, many of the downstream kernels use board files instead of device trees. That means you will likely have to manually traverse these board files to make a proper device tree.

Now, let's check how it looks on mainline. Clone linux and take a look at arch/arm/boot/dts/. This is the directory where you will add your device tree later.

lk2nd

Note Note: lk2nd is not strictly required to boot mainline. However, certain features (e.g. WiFi, BT, secondary CPU cores, ...) only work with some additional mainline quirks included in lk2nd. In the future, there may be more features that depend on extra code that needs to be run in the bootloader. Therefore, lk2nd is recommended for all devices, even if your stock bootloader already provides a standard Fastboot interface.

msm8916-mainline/lk2nd does not require any device-specific code. Normally, it should just run out of the box on your device.

Try building it. Make sure you are on the experimental-tmp branch.

In order to boot mainline, lk2nd now requires a reserved memory to be specified in a dts in order to know what ram Linux can use. Leaving it out can be fine, however it will likely lead to oddities later on.

To figure out what your reserved memories are, first install lk2nd onto your device and then run fastboot oem parsed-tags && fastboot get_staged atags.bin. You should now have a file called atags.bin

Next, make a file called atags.c in path/to/linux/arch/arm/include/uapi/asm/ and put the following code inside

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "setup.h"

void parse_tag(const struct tag *t) {
        switch (t->hdr.tag) {
        case ATAG_MEM:
                printf("Mem: 0x%x - 0x%x (size %d)\n", t->u.mem.start, t->u.mem.start + t->u.mem.size, t->u.mem.size);
                break;
        case ATAG_INITRD2:
                printf("Initrd: 0x%x - 0x%x (size: %d)\n", t->u.initrd.start, t->u.initrd.start + t->u.initrd.size, t->u.initrd.size);
                break;
        case ATAG_SERIAL:
                printf("Serial: %08x%08x\n", t->u.serialnr.high, t->u.serialnr.low);
                break;
        case ATAG_REVISION:
                printf("Revision: 0x%x\n", t->u.revision.rev);
                break;
        case ATAG_CMDLINE:
                printf("Cmdline: %s\n", t->u.cmdline.cmdline);
                break;
        case ATAG_CORE:
        case ATAG_NONE:
                break; // Ignore
        default:
                printf("Unknown: 0x%x\n", t->hdr.tag);
        }
}

int main(int argc, char* argv[]) {
    FILE *f = fopen(argv[1], "rb");
    fseek(f, 0, SEEK_END);
    long fsize = ftell(f);
    fseek(f, 0, SEEK_SET);

    struct tag *t = malloc(fsize);
    fread(t, fsize, 1, f);
    fclose(f);

    if (t->hdr.tag != ATAG_CORE) {
            printf("Fail: 0x%x\n", t->hdr.tag);
            return 1;
    }

    for (; t->hdr.size; t = tag_next(t)) {
            parse_tag(t);
    }
    return 0;
}

Compile that c file with `cc atags.c -o atags` and run `./atags atags.bin`. It should give you something like the following:

Revision: 0x8
Serial: 0000b1c10000f58e
Mem: 0x80200000 - 0x88d00000 (size 145752064)
Mem: 0x90000000 - 0xc0000000 (size 805306368)
Cmdline: lk2nd sec_log=0x80000@0x88d00008 sec_dbg=0x40000@0x88d90004 sec_debug.reset_reason=0x1a2b3c00 etb_buf=0x4000@0x8fffb9c0 androidboot.debug_level=0x4f4c sec_debug.enable=0 sec_debug.enable_user=0 androidboot.cp_debug_level=0x55FF sec_debug.enable_cp_debug=0 cordon=9aca3e010986c9435237198590d9cb10 sysscope=0xee000000 loglevel=4 samsung.hardware=SGH-I437 androidboot.emmc_checksum=3 androidboot.bootloader=I437UCDMG4 androidboot.nvdata_backup=0 androidboot.boot_recovery=0 androidboot.batt_check_recovery=0 level=0x574f4c44 sec_pvs=0 androidboot.emmc=true androidboot.serialno=b1c1f58e androidboot.baseband=msm

Device Package

https://gitlab.com/postmarketOS/pmaports/-/commit/805cf7bb625a41c28411424175cd4c7a5afe3b44

Build Kernel

Go back to your msm8960-mainline/linux clone. Try to build it with envkernel.sh:

$ cd path/to/msm8960-mainline/linux
$ source path/to/pmbootstrap/helpers/envkernel.sh

# Initialize kernel configuration
$ make qcom_apq8064_defconfig

# Compile kernel; replace <cores> with the number of cores you'd like to use for compilation
$ make -j<cores>

# Create postmarketOS package with your built kernel
$ pmbootstrap build --envkernel linux-postmarketos-qcom-msm890

This is how you can build and test local changes for the kernel from now on. Now we will continue setting up an initial device tree for your device.

Later on, if you want to change the config, for example to build a new display driver as a module, you can run make menuconfig.