321 lines
18 KiB
Markdown
321 lines
18 KiB
Markdown
|
+++
|
||
|
title = "Mainline Hero Part 0 - Modern Linux For My Galaxy S7"
|
||
|
date = "2019-07-01"
|
||
|
template = "post.html"
|
||
|
aliases = [ "/Mainline-Hero.html" ]
|
||
|
|
||
|
[extra]
|
||
|
mathjax = true
|
||
|
+++
|
||
|
Ever heard of [PostmarketOS](https://postmarketos.org/)? If not, then here's a short summary:
|
||
|
PostmarketOS aims to bring *"[a] real Linux distribution for phones and other mobile devices [...]"* to,
|
||
|
well, phones and other mobile devices.
|
||
|
|
||
|
<!-- more -->
|
||
|
|
||
|
Ever since reading about it, I've been intrigued by the idea of running a real Linux distro
|
||
|
with my UI of choice, be it *Plasma* or *Unity*, on my phone. Perhaps even running the device
|
||
|
without any proprietary firmware blobs. So, I tried my best at contributing to PostmarketOS, which
|
||
|
resulted in 3 MRs that have been accepted into master (Sorry for forgetting to bump the pkgver...).
|
||
|
|
||
|
With this series - if I manage to not break my phone - I want to document what I, someone
|
||
|
who has absolutely no idea what he is doing, learned about all this stuff, how I went about it
|
||
|
and what the results are.
|
||
|
|
||
|
## Mainline Hero #0 - Preparations
|
||
|
Before I can even think about trying to make mainline Linux run on my *Galaxy S7*, we should think
|
||
|
about how we can diagnose any issues that the kernel or the bootloader might have. And how do
|
||
|
professionals debug? Exactly! With **a lot** of `printf()` statements. But how can we retrieve those
|
||
|
from the device?
|
||
|
|
||
|
### Getting Output
|
||
|
While preparing myself for this task, I learned that there are a couple of ways.
|
||
|
|
||
|
One is called [*RAM console*](https://wiki.postmarketos.org/wiki/Mainlining_FAQ#Writing_dmesg_to_RAM_and_reading_it_out_after_reboot). What it does is just dump everything that the kernel prints into a
|
||
|
reserved region of memory, which can later be retrieved by reading from `/proc/last_kmsg` with a
|
||
|
downstream kernel.
|
||
|
|
||
|
The other one is via a [serial cable](https://wiki.postmarketos.org/wiki/Serial_debugging). This sounded
|
||
|
pretty difficult at first, the reason being that I have no idea about hardware, besides the occasional
|
||
|
**PC** hardware talk. I imagined a cable coming out of a box, packed to the brim with electronics
|
||
|
doing some black magic.
|
||
|
|
||
|
The reality is - thankfully - much simpler. It is, basically, just a normal USB cable. I mean: *USB* literally
|
||
|
stands for [*Universal Serial Bus*](https://en.wikipedia.org/wiki/USB). But how come my PC does not
|
||
|
read those kernel logs when I plug in my phone?
|
||
|
|
||
|
As it turns out, there is a component built into my phone which decides exactly what data flows from my
|
||
|
phone to the PC. Reading the [XDA post](https://forum.xda-developers.com/galaxy-s7/how-to/guide-samsung-galaxy-s7-uart-t3743895) which the PostmarketOS Wiki linked helped understand that my
|
||
|
device contains a *MUIC*, a chip which multiplexes the data lines of the USB cable towards different
|
||
|
"subsystems". As I later learned, the USB standard for connectors of type Micro Type B requires 5 pins:
|
||
|
power, ground, RX, TX and ID. Power and ground should be self-explanatory if you know anything
|
||
|
about electronics (I don't). RX and TX are the two data lines that USB uses. As USB is just a serial
|
||
|
connection, only **one** line is used for sending and one for receiving data. The ID line is the interesting
|
||
|
one: it tells the MUIC what subsystem it should multiplex the data lines to.
|
||
|
|
||
|
[Pinout diagram](https://web.archive.org/web/20190120234321/https://pinouts.ru/PortableDevices/micro_usb_pinout.shtml) of the Micro Type B connector:
|
||
|
```
|
||
|
_______________
|
||
|
/ \
|
||
|
| 1 2 3 4 5 |
|
||
|
+--|--|--|--|--|--+
|
||
|
| | | | +-o Ground
|
||
|
| | | +----o ID
|
||
|
| | +-------o D+ (Data)
|
||
|
| +----------o D- (Data)
|
||
|
+-------------o VCC (Power)
|
||
|
```
|
||
|
|
||
|
According to the XDA post, the MUIC switches to serial - used for dumping output of the bootloader and the
|
||
|
kernel - if it measures a resistance of 619kOhm attached to the ID pin. So, according to the diagram in the
|
||
|
post, I built a serial cable.
|
||
|
|
||
|
But how did the author of the XDA post know of the exact resistance that would tell the MUIC to switch to
|
||
|
serial? If you `grep` the
|
||
|
[*S7*'s defconfig](https://raw.githubusercontent.com/ivanmeler/android_kernel_samsung_herolte/lineage-15.1/arch/arm64/configs/exynos8890-herolte_defconfig),
|
||
|
for `MUIC`, then one of the results is the KConfig flag `CONFIG_MUIC_UNIVERSAL_MAX77854`.
|
||
|
If we then search the kernel tree for the keyword `max77854`, we find multiple files; one being
|
||
|
`drivers/mfd/max77854.c`. This file's copyright header tells us that we deal with a *Maxim 77854* chip. Judging
|
||
|
from the different files we find, it seems as if this chip is not only responsible for switching between serial
|
||
|
and regular USB, but also for e.g. charging (`drivers/battery_v2/include/charger/max77854_charger.h`).
|
||
|
|
||
|
However, the really interesting file is `drivers/muic/max77854.c`, since there we can find an array of structs
|
||
|
that contain strings. Sounds pretty normal until you look at the strings more closely: One of the strings is
|
||
|
the value `"Jig UART On"`:
|
||
|
```
|
||
|
[...]
|
||
|
#if defined(CONFIG_SEC_FACTORY)
|
||
|
{
|
||
|
.adc1k = 0x00,
|
||
|
.adcerr = 0x00,
|
||
|
.adc = ADC_JIG_UART_ON,
|
||
|
.vbvolt = VB_LOW,
|
||
|
.chgdetrun = CHGDETRUN_FALSE,
|
||
|
.chgtyp = CHGTYP_NO_VOLTAGE,
|
||
|
.control1 = CTRL1_UART,
|
||
|
.vps_name = "Jig UART On",
|
||
|
.attached_dev = ATTACHED_DEV_JIG_UART_ON_MUIC,
|
||
|
},
|
||
|
#endif /* CONFIG_SEC_FACTORY */
|
||
|
[...]
|
||
|
```
|
||
|
|
||
|
The keyword `ADC_JIG_UART_ON` seems especially interesting. Why? Well, the driver has to know what to do
|
||
|
with each measured resistance. It would make sense that we call the constant which contains the resistance
|
||
|
something like that. Additionally, it is the only constant name that does not immediately hint at its
|
||
|
value or function.
|
||
|
|
||
|
So we search the kernel source for this keyword. Most occurences are just
|
||
|
drivers using this constant. But one hit shows its definition: `include/linux/muic/muic.h`. There we
|
||
|
find on [line 106](https://github.com/ivanmeler/android_kernel_samsung_herolte/blob/b51cf88008606ebac535785ff549b9f55e5660b4/include/linux/muic/muic.h#L106)
|
||
|
a comment which states that this constant represents a resistance of 619kOhm.
|
||
|
|
||
|
To actually build the serial cable, we need to have a USB Type B male connector that we can solder our cables to.
|
||
|
My first thought was to buy a simple and cheap USB Type B cable, cut it, remove the isolation and solder my
|
||
|
connectors to it. I, however, failed to notice that the Type A part of the cable - the one you plug into e.g.
|
||
|
your PC - only has 4 pins, while the Type B part has 5. After stumbling upon some random diagram, I learned that
|
||
|
for regular USB connectivity, such as connecting your phone to your PC, the ID pin is not needed, so it is left
|
||
|
disconnected. As this plan failed, I proceeded to buy a USB Type B male connector. Since I bought it on the
|
||
|
Internet and the seller did not provide a diagram of what pad on the connector connects to what pin, I also
|
||
|
ordered a USB Type B female breakout board.
|
||
|
|
||
|
After all parts arrived, I used a digital multimeter to measure the resistance between each pad on the connector
|
||
|
and on the breakout board. Since I have no idea about electronics, let me explain: Resistance is defined as
|
||
|
$R = \frac{U}{I}$, where $R$ is the resistance, $U$ the voltage and $I$ the current. This means that we should
|
||
|
measure - practically speaking - infinite resistance when no current is flowing and some resistance $R \gt 0$
|
||
|
when we have a flowing current, meaning that we can test for continuity by attempting to measure resistance.
|
||
|
|
||
|
After some poking around, I got the following diagram:
|
||
|
```
|
||
|
+---------o VCC
|
||
|
| +-----o D+
|
||
|
| | +-o GND
|
||
|
___|___|___|___
|
||
|
/ ? ? ? \
|
||
|
| ? ? |
|
||
|
+------|---|------+
|
||
|
| +---o ID
|
||
|
+-------o D-
|
||
|
```
|
||
|
|
||
|
![The "Serial Cable"](/img/serial-cable.jpg)
|
||
|
|
||
|
Since the data that the serial port inside the phone is coming in using a certain protocol, which also includes
|
||
|
timing, bit order and error correcting codes, we need something to convert this data into something that is
|
||
|
usable on the host. Since the USB specification for data may differ from what we actually receive, we can't just
|
||
|
connect the phone's D- and D+ lines to the host USB's D- and D+. Hence the need for a device which does this
|
||
|
conversion for us and also deals with the timing of the data: The tiny board to which all cables lead to
|
||
|
basically just contains an *FT232RL* chip from *FTDI*. It is what does all the conversion and timing magic.
|
||
|
|
||
|
Since I don't want to accidentally brick by phone by frying it with 3.3V or 5V - though I think that damaging
|
||
|
the hardware with 5V is pretty difficult - I did not connect the USB's 5V to the *FT232*'s VCC port.
|
||
|
|
||
|
Booting up the device, we start to see data being sent via serial!
|
||
|
```
|
||
|
[...]
|
||
|
CP Mailbox Debug
|
||
|
0x10540180 : 0xdca7b414 0x 804f99f
|
||
|
0x10540184 : 0xdeb36080 0x8112566f
|
||
|
0x10540188 : 0xf4bf0800 0x2534862d
|
||
|
0x1054018C : 0x61ff350e 0x1208fd27
|
||
|
0x10540190 : 0x17e60624 0x18121baf
|
||
|
0x105C0038 : 0x3bd58404 0x5674fb39
|
||
|
CP BL flow
|
||
|
0x10920014 : 0x79dab841 0x9b01b3fd
|
||
|
0x10800028 : 0xffbd34b1 0x9fd118cc
|
||
|
Resume el3 flow
|
||
|
EL3_VAL : 0xdcfee785 0xfbb6b0a2 0xccf99641
|
||
|
muic_register_max77854_apis
|
||
|
muic_is_max77854 chip_id:0x54 muic_id:0xb5 -> matched.
|
||
|
[MUIC] print_init_regs
|
||
|
INT:01 00 00 ST:1d 00 00 IM:00 00 00 CDET:2d 0c CTRL:1b 3b 09 b2 HVCT:00 00 LDO0:47
|
||
|
|
||
|
MUIC rev = MAX77854(181)
|
||
|
init_multi_microusb_ic Active MUIC 0xb5
|
||
|
[...]
|
||
|
```
|
||
|
|
||
|
Nice! We can see what *SBOOT*, the bootloader that *Samsung* uses, tells us. But for some reason, I wasn't
|
||
|
able to get into the *SBOOT* prompt to tell the kernel to dump everything via serial. While the XDA post
|
||
|
used the programm `minicom`, which I could use to get *SBOOT* output, it never seemed to send the carriage
|
||
|
returns while I was pressing the return key like crazy. So what I did was try to use a different tool to
|
||
|
interact with the serial converter: `picocom`. And it worked!
|
||
|
|
||
|
Although I set the kernel parameters to output to the TTY device `ttySAC4`, just like the XDA post said,
|
||
|
I did not receive any data.
|
||
|
|
||
|
### Device Tree
|
||
|
So we can just try and boot mainline on the phone then, yes? With a very high probability: no. The reason being
|
||
|
that the kernel has no idea about the actual hardware inside the phone.
|
||
|
|
||
|
This may seem weird as you don't have to tell your kernel about your shiny new GPU or about your RAM. The reason
|
||
|
is that your PC is designed to be modular: You can swap the CPU, the RAM and even the attached devices, like
|
||
|
your GPU. This means that on X86, the CPU is able to discover its hardware since there is only one bus for
|
||
|
attaching devices (ignoring RAM and the CPU): the PCI bus. How does the CPU know about its RAM?
|
||
|
The RAM-modules are swappable, which means that the CPU cannot anticipate just how much RAM you
|
||
|
have in your system. These information get relayed, perhaps via the MMU, to the CPU.
|
||
|
|
||
|
Can't we just probe the available memory in an ARM SoC? Technically yes, but it would take a lot
|
||
|
of time if we have a modern 64 bit CPU. Moreover, how do you know that a probed memory location
|
||
|
is not a memory mapped device? Wouldn't it make sense to bake this data into the SoC then? Here
|
||
|
again: not really. The reason is that the SoCs are vendor specific. This means that the vendor
|
||
|
basically just buys the rights to put the CPU into their SoC. The rest is up to the vendor. They
|
||
|
can add as much RAM as they want, without the CPU designer having much input. This means that the
|
||
|
data must not be **hardcoded** into the CPU.
|
||
|
|
||
|
On ARM and probably most other microprocessors devices can be memory mapped, which means that they respond to
|
||
|
a certain region of memory being written to or read from. This makes auto-discovering devices quite difficult
|
||
|
as you would have to probe **a lot** of memory regions.
|
||
|
|
||
|
As an example: Imagine we can access 4 different locations in memory, each holding 1 byte of data. These regions
|
||
|
are at the memory addresses `0x1` to `0x4`. This means that we would have to probe 4 memory locations. Easy,
|
||
|
right?
|
||
|
Not exactly. We would have to probe 4 times to discover 4 possible memory mapped areas with a width of 1 byte.
|
||
|
If we allow a width of 2 bytes, then we would have to probe 3 different regions: `0x1`-`0x2`, `0x2`-`0x3` and
|
||
|
`0x3`-`0x4`.
|
||
|
This assumes that memory maps need to be directly next to each other. Otherwise we would need to use the
|
||
|
binomial coefficient.
|
||
|
|
||
|
This results in 10 (4x 1 byte, 3x 2 bytes, 2x 3 bytes and 1x 4 bytes) different probing attempts to discover
|
||
|
possible memory mapped devices. This does not seem much when we only have a 2 bit CPU, but in the case of the
|
||
|
*S7*, we have a 64 bit CPU; so we would have to probe about $\sum_{n=1}^{2^{64}} n$ times. This finite sum
|
||
|
is equal ([German Wikipedia](https://de.wikipedia.org/wiki/Gau%C3%9Fsche_Summenformel)) to
|
||
|
$\frac{1}{2} 2^{64} {(2^{64} + 1)} = 1.7014 \cdot 10^{38}$. Quite a lot! Keep in mind that this
|
||
|
calculation does not factor in any other busses that the SoC might use; they can, probably, use their own
|
||
|
address space.
|
||
|
|
||
|
So, long story short: We need to tell the kernel about all the hardware beforehand. This is where the so-called
|
||
|
Device Tree comes into play. It is a structured way of describing the attached hardware. You can find examples
|
||
|
in the kernel tree under `arch/arm{,64}/boot/dts/`. The problem that arises for my phone is that it
|
||
|
uses the Exynos SoC from Samsung. While Exynos 7 or older would just require an addition to the already existing
|
||
|
Device Tree files, the *S7* uses the Exynos 8890 SoC. This one is not in mainline, which mean that it is
|
||
|
required to port it from the [downstream kernel](https://github.com/ivanmeler/android_kernel_samsung_universal8890/) into mainline.
|
||
|
|
||
|
### Device Support
|
||
|
The challenge that follows, provided I don't brick my phone, is the kernel support for the SoC's hardware.
|
||
|
|
||
|
#### GPU
|
||
|
The GPU of the Exynos 8890 SoC is a Mali-T880 from ARM. While there is no "official" FOSS-driver for it, one
|
||
|
is in development: [Panfrost](https://gitlab.freedesktop.org/panfrost/linux). One of the developers once
|
||
|
mentioned in PostmarketOS' Matrix channel that the driver is not ready for day-to-day use. But hopefully it
|
||
|
will be in the forseeable future.
|
||
|
|
||
|
#### Wifi
|
||
|
While I found no data on the Exynos 8890's Wifi-chip, I managed to allow the downstream kernel to use it, albeit
|
||
|
with its proprietary firmware ([MR](https://gitlab.com/postmarketOS/pmaports/merge_requests/309)).
|
||
|
|
||
|
This patch requires a patch which changes the path of the firmware in the file `drivers/net/wireless/bcmdhd4359/dhd.h`.
|
||
|
The license header of [said file](https://github.com/ivanmeler/android_kernel_samsung_universal8890/blob/lineage-15.0/drivers/net/wireless/bcmdhd4359/dhd.h)
|
||
|
hints at a chip from Broadcom. The model of the chip appears to be 4359. What the *dhd* stand for? I don't know.
|
||
|
|
||
|
Looking at the compatibility of the [kernel modules](https://wireless.wiki.kernel.org/en/users/drivers/brcm80211) for Broadcom wireless chips, we can find
|
||
|
that the *BCM4359* chip is compatible. But is that the same as the module folder's name specifies? Again, I don't know.
|
||
|
Hopefully it is...
|
||
|
|
||
|
#### Other Components
|
||
|
At the time of writing this post, it has been a "long time" since I last flashed PostmarketOS on
|
||
|
my phone to look at what the kernel is saying. All of this device data I gathered by looking at
|
||
|
spec sheets by Samsung or the kernel. So I don't really know what other hardware is inside my
|
||
|
*S7*.
|
||
|
|
||
|
## Next Steps
|
||
|
The next steps are actually testing things out and playing around with values and settings and all kinds of things.
|
||
|
|
||
|
## Other Devices I Have Lying Around
|
||
|
This may be off-topic for the "*Mainline Hero*" series but I recently tried to find out whether another device
|
||
|
I have lying around - a *Samsung Galaxy Note 8.0* - also uses such a MUIC to multiplex its USB port. While
|
||
|
at first I somehow found out, which I now know is wrong, that the *Note 8.0* uses the same *Maxim 77854* as my
|
||
|
*S7*, I discovered that the *Note 8.0* does use a MUIC, just not the *77854*. Since I found no other links
|
||
|
talking about this, I am not sure until I test it, but what I will do is tell you about how I reached this
|
||
|
conclusion!
|
||
|
|
||
|
If you `grep` the [defconfig for the herolte](https://github.com/ivanmeler/android_kernel_samsung_herolte/blob/lineage-15.1/arch/arm64/configs/exynos8890-herolte_defconfig) for
|
||
|
"*77854*", then one of the results is the flag `CONFIG_MUIC_UNIVERSAL_MAX77854`. The prefix `CONFIG_MUIC` makes
|
||
|
sense since this enables kernel support for the *Maxim 77854* **MUIC**. As such, we should be able to find
|
||
|
an enabled MUIC in the *Note 8.0*'s [defconfig](https://github.com/LineageOS/android_kernel_samsung_smdk4412/blob/lineage-16.0/arch/arm/configs/lineageos_n5110_defconfig).
|
||
|
|
||
|
If we grep for `CONFIG_MUIC`, then we indeed get results. While the results do not look like the one for
|
||
|
the *77854*, we get ones like `CONFIG_MUIC_MAX77693_SUPPORT_OTG_AUDIO_DOCK`. This indicates that the *Note 8.0*
|
||
|
has a *Maxim 77693* MUIC built in. But it's not a very strong indicator. Since the [kernel source](https://github.com/LineageOS/android_kernel_samsung_smdk4412/) is available
|
||
|
on Github, we can just search the repo for the keyword "*MAX77693*". One of the results hints at the file
|
||
|
`drivers/misc/max77693-muic.c`. Looking at the Makefile of the `drivers/misc` directory, we find that this
|
||
|
source file is only compiled with the KConfig flag `CONFIG_MFD_MAX77693`. Grepping the *Note 8.0*'s defconfig
|
||
|
for this flag yields the result that this kernel module is enabled, hence hinting at the existence of a MUIC
|
||
|
in the *Note 8.0*.
|
||
|
|
||
|
If we take a closer look at the source file at `drivers/misc/max77693-muic.c`, we can find an interesting part
|
||
|
at [line 102](https://github.com/LineageOS/android_kernel_samsung_smdk4412/blob/b7ffe7f2aea2391737cdeac2a33217ee0ea4f2ba/drivers/misc/max77693-muic.c#L102):
|
||
|
|
||
|
```
|
||
|
[...]
|
||
|
ADC_JIG_UART_ON = 0x1d, /* 0x11101 619K ohm */
|
||
|
[...]
|
||
|
```
|
||
|
|
||
|
This means that, as the *Maxim 77854* requires a 619kOhm resistor to enable UART, we can debug
|
||
|
the *Note 8.0* with the same serial cable as the *S7*.
|
||
|
|
||
|
Plugging it into the DIY serial cable and booting it up, we also get some output:
|
||
|
```
|
||
|
[...]
|
||
|
BUCK1OUT(vdd_mif) = 0x05
|
||
|
BUCK3DVS1(vdd_int) = 0x20
|
||
|
cardtype: 0x00000007
|
||
|
SB_MMC_HS_52MHZ_1_8V_3V_IO
|
||
|
mmc->card_caps: 0x00000311
|
||
|
mmc->host_caps: 0x00000311
|
||
|
[mmc] capacity = 30777344
|
||
|
```
|
||
|
|
||
|
Theory proven! We **can** also serial debug the *Note 8.0* using the same cable.
|
||
|
|
||
|
## Some Closing Words
|
||
|
I want to emphasize that just very few of the things I mentioned were discovered or implemented by me. I just collected
|
||
|
all these information to tell you about what I learned. The only thing that I can truly say I discovered is the MR for
|
||
|
the Wifi firmware...
|
||
|
|
||
|
Additionally, I want to make it clear that I have no idea about microelectronics, electronics or ARM in general. All the
|
||
|
things I wrote that are about ARM or electronic - especially everything in the *Device Tree* section - is pure speculation
|
||
|
on my side. I never really looked into these things, but all the statements I made make sense to me. You can't just probe
|
||
|
$2^{64}$ different memory addresses just to figure out how much RAM you have, can you?
|