Configurations: the common kernel, a SoC, a phone
A Linux kernel is configured with a .config made from a defconfig, and an Android kernel is put together from layers: the
common kernel's gki_defconfig, the SoC vendor's fragment, the device's. This kernel does the same, with one difference that
is most of the design: most of its options are not compiled in. XNU takes them from the boot-arguments, so most of a
configuration here is CONFIG_* symbols that tools/hakios-config.py turns into the kernel's command line. The exception is
the drivers themselves: CONFIG_HAKIOS_<NAME> decides whether a driver is compiled into the kernel at all, through XNU's own
options and conf/files (drivers-config.md), so that a device's defconfig builds the drivers that phone has.
configs/defconfig what every configuration starts from: the OPEN one (nothing of a phone's own, one core)
configs/common_defconfig THE COMMON KERNEL: every setting there is, each with what it does (about 1000 lines)
configs/sc7280.config the Qualcomm SC7280 family (SM7325, QCM6490): eight cores, the sensors, the chip's identity
configs/redwood.config the POCO X5 Pro 5G: its vibration motor, where its modem's buffer is
configs/redwood_defconfig a phone: #include common_defconfig, sc7280.config, redwood.config (three lines)
configs/qemu_defconfig QEMU's virt machine: the phone's drivers left out of the kernel (CONFIG_HAKIOS_*)
configs/open.config fragment: the open profile (CONFIG_FIRMWARE="none")
configs/virt.config fragment: QEMU's virt machine (virtio network)
configs/trace.config fragment: log a line for each of the first frames on the network interfaces
configs/persist.config fragment: keep /private/var across reboots (THIS WRITES TO THE PHONE, partition `darwin`)
configs/symbols.tsv the symbols that are made from the source (see below); do not edit
The common kernel does not say which phone it is for. A phone is the common kernel plus the facts of its SoC plus its own, which is how Android builds a device's kernel; a new device adds a fragment and a three-line defconfig, not a new copy of everything.
Use
make redwood_defconfig # .config = defconfig + common_defconfig + sc7280.config + redwood.config
make redwood_defconfig FRAGMENTS=trace # ... and a fragment on top
make common_defconfig # the common kernel alone, for a phone that has no fragments yet
make virt.config # lay a fragment over the .config there is
make config # show it: symbols, the kernel's arguments, the firmware it wants
make savedefconfig # the .config as a minimal defconfig (what differs from configs/defconfig)
make phone # with a .config, ITS arguments are the kernel's
The same without make: tools/hakios-config.py defconfig|merge|args|check|show|needs|savedefconfig|list|listing|complete|help.
tools/hakios-config.py help GPU says what a symbol means, what it needs and what it requires. make doctor checks the host.
What a symbol is
CONFIG_HAKIOS_GOODIX=y is not an argument: it builds the touch driver into the kernel (drivers-config.md).
Every other symbol is one: CONFIG_GPU=y is the argument gpu=1; CONFIG_CPUS=8 is cpus=8; CONFIG_MODEM_BUFFER=0xa8000000 is
modem_buffer=0xa8000000. A symbol that is # CONFIG_X is not set is left out, and the kernel's own default applies.
Three symbols are facts about the build, not arguments: CONFIG_DEVICE, CONFIG_SOC (it goes into the kernel's version
string, see versioning.md) and CONFIG_FIRMWARE.
There are two kinds of symbol, and the difference is honest:
- The stable switches are written by hand in the
SYMBOLStable at the top oftools/hakios-config.py: they have a meaning, a type, a range, a firmware they need, a symbol they require (CONFIG_SOUNDrequiresCONFIG_ADSP). About thirty. - The rest are made from the source. Every argument that the kernel, the loader or user space reads
(boot-args.md) gets a symbol, except the few that the loader and the runner set themselves (
epoch, the time of the run ...). They are typed as "a switch (yis 1), a number as written, or a quoted text", and the one-line meaning is taken from the source where the file that reads the argument has a documentation line for it; where it has none, the line says which file reads it and nothing more. They areconfigs/symbols.tsv, made bytools/boot-args.py --symbols, and CI fails when a driver adds an argument and the file is not made again. Most of these are the knobs of bring-up (a driver's steps and pauses, traces, test modes); they are here so that a run is written down in one file with typos refused, not because each one is a feature.
common_defconfig: every setting, documented
Linux's defconfigs list only what differs from the defaults. This one lists every symbol, set or # CONFIG_X is not set,
each under a comment that says what it does, in groups (GPU and display, Wi-Fi, Cellular, Storage ...). That is why it is a
thousand lines long, and why a reader of one file can see every setting the kernel has. It is made, not written:
tools/hakios-config.py listing common -o configs/common_defconfig # after changing a value or adding a driver
tools/hakios-config.py complete common # it must name every symbol (CI runs this, in the tests)
Change a value by editing the line and running listing common -o configs/common_defconfig again: the file reads back the
same. Not listing common > configs/common_defconfig: the shell empties the file before the tool reads it, and what is written is
then configs/defconfig alone. That is how common_defconfig once lost the values of the phone line (the GPU, Wi-Fi, audio and
modem switches, CONFIG_FIRMWARE="device"), and why -o exists.
#include: putting a device together
A line #include NAME in a defconfig or fragment (a comment to any other reader) stands for configs/NAME, NAME_defconfig or
NAME.config, laid first; the including file wins over what it includes; a loop is an error. This is the whole of
redwood_defconfig. The order for make redwood_defconfig is defconfig (open, one core), then the common kernel, then the
SoC, then the phone, then the fragments you named.
What is checked, and why
- Types and ranges. A bool is
yor not set, a hex number starts with0x,CONFIG_CPUSis 1 to 8,CONFIG_THERMAL_STOPis 0 to 160. A typo in a name gets a suggestion (no symbol CONFIG_GPUU (did you mean CONFIG_GPU?)). - Dependencies.
CONFIG_VAR_STORErequiresCONFIG_UFS_WRITE, which requiresCONFIG_UFS=2;CONFIG_SOUNDrequiresCONFIG_ADSP;CONFIG_THERMAL_STOPrequiresCONFIG_THERMAL; the modem requires the storage service. - Firmware.
CONFIG_FIRMWARE="none"is the open profile: nothing of the phone's own is loaded. The check refuses a configuration that saysnoneand still asks for the GPU, Wi-Fi, audio, haptics, cameras or the modem, because each of those uses firmware pulled from the owner's own phone (fw-*/, never in this repository). - The command line fits. The kernel's boot line holds 607 characters, and a longer one loses its tail without an error. The check counts the arguments, adds what a run adds itself (about 260) and refuses what cannot fit.
CONFIG_BOOTARGS: Linux's CONFIG_CMDLINE
For an argument that has no symbol of its own, or a value the symbols cannot say: CONFIG_BOOTARGS="fb_beat=2 loader_return_after=5". It is not an
escape from the checks. Every name must be one the source reads (boot-args.md), so fb_beet=2 is an error with a
suggestion rather than a silent no-op; an argument that a symbol already sets (cpus=8) is refused with the name of the symbol; a
word is name=value with no space, quote or backslash in it; and the words count towards the 607 characters.
What is the same as Linux, and what is not
| Linux / Android | here | |
|---|---|---|
| the common kernel | gki_defconfig |
common_defconfig |
| a SoC, a device | vendor and device fragments, build.config |
sc7280.config, redwood.config, #include |
| a fragment | kernel/configs/*.config, merge_config.sh |
configs/*.config, make NAME.config |
| the result | .config, compiled in |
.config: the drivers compiled in (CONFIG_HAKIOS_*), the rest becomes the boot-arguments |
| a driver's options | Kconfig + obj-$(CONFIG_X) += x.o |
drivers/<subsystem>/conf, XNU's options and optional in conf/files |
| a minimal config | make savedefconfig |
make savedefconfig |
| a command line baked in | CONFIG_CMDLINE |
CONFIG_BOOTARGS (names checked) |
| an option that needs a blob | CONFIG_EXTRA_FIRMWARE |
CONFIG_FIRMWARE="device" / "none", and needs |
| the version | uname -r |
kern.hakios.version, kernel.ver= |
| how many options | thousands, in Kconfig | about 310, one for each argument the source reads |
The last row is the honest one. This is not Kconfig and has no thousand options: apart from which drivers it has, the kernel is not built from its options. The number of symbols is the number of arguments the code reads, plus one for each driver.
A new device
tools/new-device.py makes devices/<name>/ from a phone's own device tree. Its configuration is a fragment and a defconfig of a few
lines: configs/<name>.config with what is true of that phone only, and configs/<name>_defconfig that includes the common kernel, the
SoC's fragment and that one, and nothing more until the phone has been shown to do it. The fragment is also where the phone leaves out
the drivers of hardware it does not have or that has not been ported to it (# CONFIG_HAKIOS_GOODIX is not set: see
drivers-config.md).
What a configuration does not do
It does not make a driver exist, and it does not make a driver work on a phone it was not written for. A symbol builds in, or turns on, a part of the kernel that is already there; whether it works on a given phone is what the device's page and the tests say, and the project does not claim a result it has not seen.