├── .dockerignore
├── .gitattributes
├── .github
└── workflows
│ └── main.yml
├── .gitignore
├── .gitlab-ci.yml
├── .gitmodules
├── .pre-commit-config.yaml
├── .python-version
├── Dockerfile
├── LICENSE
├── README.md
├── builder
├── __main__.py
├── clients
│ ├── __init__.py
│ ├── github.py
│ ├── los.py
│ ├── pa.py
│ └── rom_api.py
├── commands
│ ├── __init__.py
│ ├── assets.py
│ ├── bundle.py
│ └── kernel.py
├── configs
│ ├── __init__.py
│ ├── argument.py
│ └── directory.py
├── core
│ ├── __init__.py
│ ├── assets_collector.py
│ └── kernel_builder.py
├── engines
│ ├── __init__.py
│ └── generic_container.py
├── interfaces
│ ├── __init__.py
│ ├── clients.py
│ ├── commands.py
│ ├── engines.py
│ ├── managers.py
│ └── modules.py
├── managers
│ ├── __init__.py
│ └── resource.py
├── manifests
│ ├── devices.json
│ └── tools.json
├── modifications
│ ├── dumplinger
│ │ ├── 4.14
│ │ │ ├── add-wifi-injection-4.14.patch
│ │ │ ├── fix-ath9k-naming-conflict.patch
│ │ │ └── kernelsu-compat.patch
│ │ ├── 4.4
│ │ │ ├── add-wifi-injection-4.4.patch
│ │ │ ├── fix-ath9k-naming-conflict.patch
│ │ │ ├── fix-hci-uart.patch
│ │ │ ├── fix-rt2800-injection-4.04.patch
│ │ │ ├── kernelsu-compat.patch
│ │ │ └── v2-2-6-mac80211-refactor-monitor-representation-in-sdata.patch
│ │ ├── anykernel3
│ │ │ ├── anykernel.sh
│ │ │ └── ramdisk
│ │ │ │ ├── init.nethunter.rc
│ │ │ │ ├── keyboard-descriptor.bin
│ │ │ │ └── mouse-descriptor.bin
│ │ └── archive
│ │ │ └── 4.4
│ │ │ └── qcacld_pa.patch
│ ├── gki
│ │ └── .gitkeep
│ ├── lemonade
│ │ └── 5.4
│ │ │ └── .gitkeep
│ ├── lemonadep
│ │ └── 5.4
│ │ │ └── .gitkeep
│ └── nhpatch.sh
├── tools
│ ├── __init__.py
│ ├── banner.py
│ ├── cleaning.py
│ ├── commands.py
│ ├── fileoperations.py
│ └── logger.py
└── utils
│ ├── __init__.py
│ └── bridge.py
├── conanfile.py
├── docs
├── FAQ.md
├── FLASHING.md
├── TODO.md
└── architecture
│ ├── clients
│ ├── classes.puml
│ └── packages.puml
│ ├── commands
│ ├── classes.puml
│ └── packages.puml
│ ├── configs
│ ├── classes.puml
│ └── packages.puml
│ ├── core
│ ├── classes.puml
│ └── packages.puml
│ ├── engines
│ ├── classes.puml
│ └── packages.puml
│ ├── interfaces
│ ├── classes.puml
│ └── packages.puml
│ ├── managers
│ ├── classes.puml
│ └── packages.puml
│ ├── tools
│ ├── classes.puml
│ └── packages.puml
│ └── utils
│ ├── classes.puml
│ └── packages.puml
├── pyproject.toml
├── requirement-uv.txt
├── scripts
├── install_hooks.py
├── install_rtw_drivers.sh
└── multi_build.py
├── tests
└── unit
│ └── builder
│ ├── core
│ └── test_kernel_builder.py
│ └── tools
│ ├── test_commands.py
│ └── test_messages.py
└── uv.lock
/.dockerignore:
--------------------------------------------------------------------------------
1 | # basic
2 | __pycache__
3 | .vscode
4 | .coverage
5 | .pytest_cache
6 | .venv
7 |
8 | # git subrepos
9 | # (uncomment below if you explicitely want Docker/Podman to not use locally downloaded dirs)
10 | #android_*
11 | #*_kernel_*
12 | #clang*
13 | #rtl8812au
14 | #AnyKernel3
15 | #KernelSU
16 |
17 | # misc local artifacts
18 | kernel
19 | assets
20 | source
21 | bundle
22 | localversion
23 |
24 | # and the Dockerfile itself
25 | Dockerfile
26 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto eol=LF
2 |
--------------------------------------------------------------------------------
/.github/workflows/main.yml:
--------------------------------------------------------------------------------
1 | name: build and release
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | workflow_dispatch:
8 | inputs:
9 | isPrerelease:
10 | description: "Select whether this is pre-release"
11 | required: true
12 | default: "false"
13 | type: choice
14 | options:
15 | - "true"
16 | - "false"
17 |
18 | permissions:
19 | contents: write
20 |
21 | env:
22 | IS_PRERELEASE: ${{ github.event.inputs.isPrerelease || false }}
23 |
24 | jobs:
25 | build:
26 | runs-on: ubuntu-latest
27 | steps:
28 | - name: Checkout
29 | uses: actions/checkout@v4
30 | with:
31 | submodules: recursive
32 |
33 | - name: Install UV
34 | run: |
35 | uv_version=$(cat ./requirement-uv.txt | awk -F'==' '{print $2}' | tr -d ' \n')
36 | curl -LsSf https://astral.sh/uv/${uv_version}/install.sh | sh
37 |
38 | - name: Record original PATH
39 | id: record_original_path
40 | run: echo "PATH_ORIGINAL=$PATH" >> $GITHUB_OUTPUT
41 |
42 | - name: Activate .venv
43 | run: |
44 | uv sync --frozen
45 | . .venv/bin/activate
46 | echo PATH=${GITHUB_WORKSPACE}/.venv/bin:$PATH >> $GITHUB_ENV
47 |
48 | - name: Run Tests
49 | run: |
50 | python3 -m pre_commit run --files builder/**/*.py
51 |
52 | - name: Build
53 | run: |
54 | export PYTHONPATH=$(pwd)
55 | python3 scripts/multi_build.py
56 |
57 | - name: Publish Artifacts
58 | uses: actions/upload-artifact@v4
59 | with:
60 | name: build-artifacts
61 | path: "multi-build"
62 | retention-days: 1
63 | if-no-files-found: "error"
64 |
65 | - name: Deactivate .venv
66 | run: echo PATH=${{ steps.record_original_path.outputs.PATH_ORIGINAL }}
67 |
68 | release:
69 | runs-on: ubuntu-latest
70 | needs: build
71 | steps:
72 | - name: Checkout
73 | uses: actions/checkout@v4
74 |
75 | - name: Retrieve Artifacts
76 | uses: actions/download-artifact@v4
77 | with:
78 | name: build-artifacts
79 | path: "multi-build"
80 |
81 | - name: Get Current Version
82 | id: version
83 | run: echo "version=$(python3 scripts/common/py/get_version.py)" >> $GITHUB_OUTPUT
84 |
85 | - name: Form Tag
86 | id: tagname
87 | run: echo "tagname=v${{ steps.version.outputs.version }}" >> $GITHUB_OUTPUT
88 |
89 | - name: Release
90 | uses: ncipollo/release-action@v1
91 | with:
92 | draft: true
93 | skipIfReleaseExists: true
94 | tag: ${{ steps.tagname.outputs.tagname }}
95 | prerelease: ${{ env.IS_PRERELEASE }}
96 | token: ${{ secrets.GITHUB_TOKEN }}
97 | artifacts: "multi-build/*.zip"
98 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # basic
2 | __pycache__
3 | /dist
4 | .venv
5 | .vscode
6 | .coverage
7 | .pytest_cache
8 | .ruff_cache
9 | .ropeproject
10 |
11 | # git subrepos
12 | /android_*
13 | /*_kernel_*
14 | /clang*
15 | /rtl8812au
16 | /AnyKernel3
17 | /KernelSU
18 |
19 | # misc local artifacts
20 | /*.log
21 | /kernel
22 | /assets
23 | /source
24 | /bundle
25 | /localversion
26 | /multi-build
27 |
--------------------------------------------------------------------------------
/.gitlab-ci.yml:
--------------------------------------------------------------------------------
1 | stages:
2 | - build_and_tag
3 |
4 | variables:
5 | GIT_STRATEGY: "clone"
6 | GIT_SUBMODULE_STRATEGY: recursive
7 |
8 | job-build:
9 | stage: build_and_tag
10 | image: docker:25.0.3-cli-alpine3.19
11 | services:
12 | - docker:dind
13 | script:
14 | - apk update && apk add python3 py3-pip
15 | - python3 -m pip install -r requirement-uv.txt --break-system-packages
16 | - uv sync --frozen --no-install-project
17 | - source .venv/bin/activate
18 | - export PYTHONPATH=$(pwd)
19 | - python3 scripts/common/py/run_tests.py
20 | - python3 scripts/multi_build.py
21 | - deactivate
22 | artifacts:
23 | paths:
24 | - "multi-build/zero-*.zip"
25 | when: on_success
26 | expire_in: never
27 | rules:
28 | - when: manual
29 |
30 | job-tag:
31 | stage: build_and_tag
32 | script:
33 | - USERNAME="$CUSTOM_CI_USERNAME"
34 | - PASSWORD="$CUSTOM_CI_PASSWORD"
35 | - EMAIL="$CUSTOM_CI_EMAIL"
36 | - TAGNAME="v$(sh scripts/common/py/get_version.sh)"
37 | - git config --global user.name "${USERNAME}"
38 | - git config --global user.email "${EMAIL}"
39 | - git remote remove origin
40 | - git remote add origin https://${USERNAME}:${PASSWORD}@gitlab.com/${CI_PROJECT_PATH}
41 | - git fetch --force --tags
42 | - if [ $(git tag | grep "$TAGNAME") ]; then echo "[ * ] Tag already exists, skipping.."; else git tag $TAGNAME && git push origin $TAGNAME; fi
43 | rules:
44 | - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
45 | - when: manual
46 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "scripts/common"]
2 | path = scripts/common
3 | url = git@github.com:seppzer0/scripts
4 |
--------------------------------------------------------------------------------
/.pre-commit-config.yaml:
--------------------------------------------------------------------------------
1 | default_stages: [pre-commit]
2 |
3 | repos:
4 | - repo: local
5 | hooks:
6 | - id: ruff
7 | name: Ruff
8 | description: "Static analysts (SAST) via Ruff."
9 | entry: ruff check
10 | language: python
11 | types_or: [python]
12 | args: []
13 | require_serial: true
14 | additional_dependencies: []
15 | minimum_pre_commit_version: "2.9.2"
16 |
17 | - id: pyright
18 | name: Pyright
19 | description: "Static typing check via Pyright."
20 | entry: pyright
21 | language: python
22 | types_or: [python]
23 | require_serial: true
24 | additional_dependencies: []
25 | minimum_pre_commit_version: "2.9.2"
26 |
27 | - id: pytest
28 | name: Pytest
29 | pass_filenames: false
30 | description: "Unit testing via Pytest."
31 | entry: pytest
32 | language: python
33 | types_or: [python]
34 | require_serial: true
35 | additional_dependencies: []
36 | minimum_pre_commit_version: "2.9.2"
37 |
--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------
1 | 3.13.3
2 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | FROM debian:bookworm-slim AS base
2 |
3 | # variable store
4 | ARG WDIR=/zero_build
5 | ENV CONAN_UPLOAD_CUSTOM 0
6 |
7 | # transfer sources from host to container
8 | COPY . ${WDIR}
9 | WORKDIR ${WDIR}
10 | ENV PYTHONPATH ${WDIR}
11 |
12 | # install system packages
13 | RUN \
14 | apt-get update \
15 | && \
16 | apt-get install -y \
17 | curl \
18 | wget \
19 | git \
20 | gcc \
21 | g++ \
22 | libssl-dev \
23 | python3 \
24 | python3-pip \
25 | make \
26 | zip \
27 | bc \
28 | libgpgme-dev \
29 | bison \
30 | flex
31 |
32 | # install UV, .venv and shared tools;
33 | #
34 | # The main idea here is that we pre-pack all the tools into the Docker/Podman image that can be used for any device:
35 | # (toolchains, binutils -- everything except device-specific kernel source);
36 | #
37 | # This significantly reduces the total build time, as each time we make a build call for a device,
38 | # only device-specific kernel source is being downloaded into the container.
39 | #
40 | RUN curl -LsSf https://astral.sh/uv/$(cat ./requirement-uv.txt | awk -F'==' '{print $2}' | tr -d ' \n')/install.sh | sh && \
41 | . $HOME/.local/bin/env && \
42 | uv sync --frozen --no-install-project && \
43 | uv run ${WDIR}/builder/utils/bridge.py --shared
44 |
45 | # activate .venv
46 | CMD [ "source", ".venv/bin/activate" ]
47 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # zero_kernel
2 |
3 | An advanced Android kernel builder with assets collection and Kali NetHunter support.
4 |
5 | ## Contents
6 |
7 | - [zero\_kernel](#zero_kernel)
8 | - [Contents](#contents)
9 | - [**Important**](#important)
10 | - [Description](#description)
11 | - [Kernel Features](#kernel-features)
12 | - [Supported Devices \& ROMs](#supported-devices--roms)
13 | - [Usage](#usage)
14 | - [Prerequisites](#prerequisites)
15 | - [Kernel](#kernel)
16 | - [Assets](#assets)
17 | - [Bundle](#bundle)
18 | - [Examples](#examples)
19 | - [See also](#see-also)
20 | - [Credits](#credits)
21 |
22 | ## **Important**
23 |
24 | > [!IMPORTANT]
25 | > **\- DISCLAIMER \-**
26 | >
27 | > **This kernel is made for educational purposes only.**
28 | >
29 | > **I am not responsible for anything that may or may not happen to your device by installing any custom ROMs, kernels and/or any other forms of software.**
30 | >
31 | > **Anything you do with this kernel and your device you do at your own risk. By using it, you take the responsibility upon yourself and in case of any issue you are not to blame me or other related contributors.**
32 |
33 | > [!NOTE]
34 | > \- ROM artifacts in releases \-
35 | >
36 | > The contents of each release include ROM builds compatible with corresponding kernel builds. These ROM files are **unmodified and mirrored from official sources**.
37 | >
38 | >This can be verified via the checksums, which should be identical to the ones presented on the ROM project's official web page.
39 | >
40 | >You can always download the same ROM file from official sources if you'd like. The mirroring in this repository is only done due to the fact that some ROM projects remove their older builds once they become too outdated.
41 |
42 | ## Description
43 |
44 | The codebase of this project is an extensive build wrapper automating the entire Android kernel build process, starting from kernel source collection and ending with artifact packaging.
45 |
46 | The key goal is to modify the kernel in such a way that enables unique features of [Kali NetHunter](https://www.kali.org/docs/nethunter) — a ROM layer designed to add extended functionality for penetration testing in a mobile form factor.
47 |
48 | The architecture of this wrapper is ~~trying to be~~ as modular as possible, making it a little easier to add support for new devices.
49 |
50 | ## Kernel Features
51 |
52 | The kernel has the following features:
53 |
54 | - Kali NetHunter support;
55 | - RTL8812/21AU + RTL8814AU + RTL8187 Wi-Fi drivers;
56 | - packet injection support for internal Wi-Fi chipset;
57 | - optional KernelSU support (v0.9.5, max compatible version for non-GKI kernels).
58 |
59 | ## Supported Devices & ROMs
60 |
61 |
62 | OnePlus 5/T
63 |
64 | - 4.4 Linux kernel version:
65 | - LineageOS;
66 | - ParanoidAndroid;
67 | - x_kernel supported (universal)`*`.
68 |
69 | - 4.14 Linux kernel version:
70 | - ParanoidAndroid (unofficial & testing);
71 | - x-ft_kernel supported (universal)`**`.
72 |
73 | `*` -- this is mostly relevant to ROMs based on LineageOS; however, technically speaking, this includes ParanoidAndroid as well, which makes x_kernel-based builds universal.
74 |
75 | `**` -- this, **in theory**, is relevant to all 4.14-based ROMs for this device in existence.
76 |
77 |
78 |
79 | ## Usage
80 |
81 | The custom build wrapper (aka "builder") consists of 2 core components and 3 primary commands:
82 |
83 | Components:
84 |
85 | - `kernel_builder`;
86 | - `assets_collector`.
87 |
88 | Commands:
89 |
90 | - `kernel`;
91 | - `assets`;
92 | - `bundle`.
93 |
94 | ```help
95 | $ python3 builder --help
96 | usage: builder [-h] [--clean] {kernel,assets,bundle} ...
97 |
98 | A custom builder for the zero_kernel.
99 |
100 | positional arguments:
101 | {kernel,assets,bundle}
102 | kernel build the kernel
103 | assets collect assets
104 | bundle build the kernel + collect assets
105 |
106 | optional arguments:
107 | -h, --help show this help message and exit
108 | --clean clean the root directory
109 | ```
110 |
111 | ### Prerequisites
112 |
113 | **It is highly recommended to use `docker` option to run this tool.** For that you need Docker Engine or Docker Desktop, depending on your OS.
114 |
115 | > [!WARNING]
116 | > Because of how *specific* Linux kernel source is, building it on Windows even with Docker (using WSL2 back-end) might be [challenging](https://stackoverflow.com/questions/76754956/how-to-clone-the-linux-kernel-repository-to-my-machine-i-keep-geting-errors).
117 |
118 | To run this tool in a `local` environment, you will need:
119 |
120 | - a Debian-based Linux distribution (other distribution families are untested);
121 | - a few [packages](Dockerfile#L15) installed in your system;
122 | - a configured Python environment with Python 3.12+.
123 |
124 | ```sh
125 | # install uv version from project file
126 | python3 -m pip install -r requirement-uv.txt
127 | # make builder/ internal imports visible to itself
128 | export PYTHONPATH=$(pwd)
129 | # prepare and activate dev environment
130 | uv sync --frozen --no-install-project
131 | source .venv/bin/activate
132 | ```
133 |
134 | Once you are finished working with the project, don't forget to disable the virtual environment (venv) via simple `deactivate`.
135 |
136 | ### Kernel
137 |
138 | Kernel build process can be launched using the `kernel` subcommand.
139 |
140 | ```help
141 | $ python3 builder kernel --help
142 | usage: builder kernel [-h] --build-env {local,docker,podman} --base {los,pa,x,aosp}
143 | --codename CODENAME --lkv LKV [-c] [--clean-image] [--ksu]
144 |
145 | options:
146 | -h, --help show this help message and exit
147 | --build-env {local,docker,podman}
148 | select build environment
149 | --base {los,pa,x,aosp}
150 | select a kernel base for the build
151 | --codename CODENAME select device codename
152 | --lkv LKV select Linux Kernel Version
153 | -c, --clean don't build anything, only clean kernel directories
154 | --clean-image remove Docker/Podman image from the host machine after
155 | build
156 | --ksu add KernelSU support
157 | ```
158 |
159 | ### Assets
160 |
161 | As mentioned, there is also an asset downloader, which can collect latest versions of ROM, TWRP, Magisk and it's modules, Kali Chroot etc.
162 |
163 | ```help
164 | $ python3 builder assets --help
165 | usage: builder assets [-h] --build-env {local,docker,podman} --base {los,pa,x,aosp}
166 | --codename CODENAME --chroot {full,minimal} [--rom-only]
167 | [--clean-image] [--clean] [--ksu]
168 |
169 | options:
170 | -h, --help show this help message and exit
171 | --build-env {local,docker,podman}
172 | select build environment
173 | --base {los,pa,x,aosp}
174 | select a kernel base for the build
175 | --codename CODENAME select device codename
176 | --chroot {full,minimal}
177 | select Kali chroot type
178 | --rom-only download only the ROM as an asset
179 | --clean-image remove Docker/Podman image from the host machine after
180 | build
181 | --clean autoclean 'assets' folder if it exists
182 | --ksu add KernelSU support
183 | --defconfig DEFCONFIG
184 | specify path to custom defconfig
185 | ```
186 |
187 | ### Bundle
188 |
189 | The `bundle` command is a combined usage of kernel builder and assets collector core modules.
190 |
191 | This is especially useful for linking the kernel build with the appropriate ROM build.
192 |
193 | There are cases when an old kernel build is used with the newer ROM build. Such cases can ultimately lead to your system working improperly or breaking down completely, which is why it is important to use a *specific* kernel build with a corresponding ROM build.
194 |
195 | Currently, there are three types of packaging available:
196 |
197 | - `conan`;
198 | - `slim`;
199 | - `full`.
200 |
201 | Options `full` and `conan` collect all of the assets required to successfuly flash the kernel onto your device. The difference between the two is that `full` option places everything into a local directory, while `conan` organizes everything as a Conan package.
202 |
203 | Option named `slim` is a much lighter version of `full` packaging, as only the ROM is collected from the asset list. This is done to reduce package sizes while ensuring the kernel+ROM compatibility.
204 |
205 | ```help
206 | $ python3 builder bundle --help
207 | usage: builder bundle [-h] --build-env {local,docker,podman} --base {los,pa,x,aosp}
208 | --codename CODENAME --lkv LKV --package-type
209 | {conan,slim,full} [--conan-upload] [--clean-image] [--ksu]
210 |
211 | options:
212 | -h, --help show this help message and exit
213 | --build-env {local,docker,podman}
214 | select build environment
215 | --base {los,pa,x,aosp}
216 | select a kernel base for the build
217 | --codename CODENAME select device codename
218 | --lkv LKV select Linux Kernel Version
219 | --package-type {conan,slim,full}
220 | select package type of the bundle
221 | --conan-upload upload Conan packages to remote
222 | --clean-image remove Docker/Podman image from the host machine after
223 | build
224 | --ksu add KernelSU support
225 | ```
226 |
227 | ## Examples
228 |
229 | Here are some examples of commands:
230 |
231 | **(Recommended)** Build kernel and collect ROM via Docker:
232 |
233 | ```sh
234 | python3 builder bundle --build-env=docker --base=los --codename=dumpling --lkv=4.4 --package-type=slim
235 | ```
236 |
237 | Build kernel locally:
238 |
239 | ```sh
240 | python3 builder kernel --build-env=local --base=los --codename=dumpling --lkv=4.4
241 | ```
242 |
243 | Collect all of the assets locally:
244 |
245 | ```sh
246 | python3 builder assets --build-env=local --base=los --codename=dumpling --package-type=full
247 | ```
248 |
249 | ## See also
250 |
251 | - [FAQ](docs/FAQ.md);
252 | - [TODO List](docs/TODO.md);
253 | - [Kernel Flashing Instructions](docs/FLASHING.md).
254 |
255 | ## Credits
256 |
257 | - [x_kernel_oneplus_msm8998](https://github.com/ederekun/x_kernel_oneplus_msm8998): OnePlus 5/T kernel with many optimizations and improvements;
258 | - [4.14-kernel-oneplus-msm8998](https://github.com/roberto-sartori-gl/4.14-kernel-oneplus-msm8998): a base of 4.14 kernels for OnePlus 5/T, with KernelSU patches;
259 | - [kali-nethunter-kernel](https://gitlab.com/kalilinux/nethunter/build-scripts/kali-nethunter-kernel): official kernel patches from Kali NetHunter project.
260 |
--------------------------------------------------------------------------------
/builder/__main__.py:
--------------------------------------------------------------------------------
1 | import os
2 | import io
3 | import sys
4 | import json
5 | import argparse
6 | from pathlib import Path
7 | from importlib.metadata import version
8 |
9 | from builder.tools import cleaning as cm, commands as ccmd, Logger as logger
10 | from builder.configs import ArgumentConfig, DirectoryConfig as dcfg
11 | from builder.engines import GenericContainerEngine
12 | from builder.commands import KernelCommand, AssetsCommand, BundleCommand
13 |
14 |
15 | def __get_version() -> str:
16 | """Get app version.
17 |
18 | Version is retrieved depending on the way the app
19 | is launched (as PIP package or from source).
20 |
21 | :return: App version.
22 | :rtype: str
23 | """
24 | msg = "zero_kernel {}"
25 |
26 | try:
27 | return msg.format(version("zero-kernel"))
28 | except Exception:
29 | with open(Path(__file__).absolute().parents[1] / "pyproject.toml", "r") as f:
30 | v = f.read().split('version = "')[1].split('"')[0]
31 | return msg.format(v)
32 |
33 |
34 | def parse_args() -> argparse.Namespace:
35 | """Parse the script arguments.
36 |
37 | :return: Namespace of arguments.
38 | :rtype: argparse.Namespace
39 | """
40 | # show the 'help' message if no arguments supplied
41 | args = None if sys.argv[1:] else ["-h"]
42 |
43 | # parser and subparsers
44 | parser_parent = argparse.ArgumentParser(description="Advanced Android kernel builder with Kali NetHunter support.")
45 | subparsers = parser_parent.add_subparsers(dest="command")
46 | parser_kernel = subparsers.add_parser("kernel", help="build the kernel")
47 | parser_assets = subparsers.add_parser("assets", help="collect assets")
48 | parser_bundle = subparsers.add_parser("bundle", help="build the kernel + collect assets")
49 |
50 | # main parser arguments
51 | parser_parent.add_argument(
52 | "--clean",
53 | dest="clean_root",
54 | action="store_true",
55 | help="clean the root directory"
56 | )
57 | parser_parent.add_argument("-v", "--version", action="version", version=__get_version())
58 |
59 | # common argument attributes for subparsers
60 | help_base = "select a kernel base for the build"
61 | help_codename = "select device codename"
62 | help_benv = "select build environment"
63 | help_clean = "remove Docker/Podman image from the host machine after build"
64 | choices_benv = {"local", "docker", "podman"}
65 | choices_base = {"los", "pa", "x", "aosp"}
66 | help_defconfig = "specify path to custom defconfig"
67 | help_ksu = "add KernelSU support"
68 | help_lkv = "select Linux Kernel Version"
69 |
70 | # kernel
71 | parser_kernel.add_argument(
72 | "--build-env",
73 | type=str,
74 | dest="benv",
75 | required=True,
76 | choices=choices_benv,
77 | help=help_benv,
78 | )
79 | parser_kernel.add_argument(
80 | "--base",
81 | type=str,
82 | required=True,
83 | help=help_base,
84 | choices=choices_base
85 | )
86 | parser_kernel.add_argument(
87 | "--codename",
88 | type=str,
89 | required=True,
90 | help=help_codename
91 | )
92 | parser_kernel.add_argument(
93 | "--lkv",
94 | type=str,
95 | required=True,
96 | help=help_lkv
97 | )
98 | parser_kernel.add_argument(
99 | "-c", "--clean",
100 | dest="clean_kernel",
101 | action="store_true",
102 | help="don't build anything, only clean kernel directories"
103 | )
104 | parser_kernel.add_argument(
105 | "--clean-image",
106 | action="store_true",
107 | dest="clean_image",
108 | help=help_clean
109 | )
110 | parser_kernel.add_argument(
111 | "--ksu",
112 | action="store_true",
113 | dest="ksu",
114 | help=help_ksu
115 | )
116 | parser_kernel.add_argument(
117 | "--defconfig",
118 | type=Path,
119 | dest="defconfig",
120 | help=help_defconfig
121 | )
122 |
123 | # assets
124 | parser_assets.add_argument(
125 | "--build-env",
126 | type=str,
127 | dest="benv",
128 | required=True,
129 | choices=choices_benv,
130 | help=help_benv
131 | )
132 | parser_assets.add_argument(
133 | "--base",
134 | type=str,
135 | required=True,
136 | help=help_base,
137 | choices=choices_base
138 | )
139 | parser_assets.add_argument(
140 | "--codename",
141 | type=str,
142 | required=True,
143 | help=help_codename
144 | )
145 | parser_assets.add_argument(
146 | "--chroot",
147 | type=str,
148 | required=True,
149 | choices=("full", "minimal"),
150 | help="select Kali chroot type"
151 | )
152 | parser_assets.add_argument(
153 | "--rom-only",
154 | dest="rom_only",
155 | action="store_true",
156 | help="download only the ROM as an asset"
157 | )
158 | parser_assets.add_argument(
159 | "--clean-image",
160 | action="store_true",
161 | dest="clean_image",
162 | help=help_clean
163 | )
164 | parser_assets.add_argument(
165 | "--clean",
166 | dest="clean_assets",
167 | action="store_true",
168 | help="autoclean 'assets' folder if it exists"
169 | )
170 | parser_assets.add_argument(
171 | "--ksu",
172 | action="store_true",
173 | dest="ksu",
174 | help=help_ksu
175 | )
176 |
177 | # bundle
178 | parser_bundle.add_argument(
179 | "--build-env",
180 | type=str,
181 | dest="benv",
182 | required=True,
183 | choices=choices_benv,
184 | help=help_benv
185 | )
186 | parser_bundle.add_argument(
187 | "--base",
188 | type=str,
189 | required=True,
190 | help=help_base,
191 | choices=choices_base
192 | )
193 | parser_bundle.add_argument(
194 | "--codename",
195 | type=str,
196 | required=True,
197 | help=help_codename
198 | )
199 | parser_bundle.add_argument(
200 | "--lkv",
201 | type=str,
202 | required=True,
203 | help=help_lkv
204 | )
205 | parser_bundle.add_argument(
206 | "--package-type",
207 | type=str,
208 | required=True,
209 | dest="package_type",
210 | choices={"conan", "slim", "full"},
211 | help="select package type of the bundle"
212 | )
213 | parser_bundle.add_argument(
214 | "--conan-upload",
215 | action="store_true",
216 | dest="conan_upload",
217 | help="upload Conan packages to remote"
218 | )
219 | parser_bundle.add_argument(
220 | "--clean-image",
221 | action="store_true",
222 | dest="clean_image",
223 | help=help_clean
224 | )
225 | parser_bundle.add_argument(
226 | "--ksu",
227 | action="store_true",
228 | dest="ksu",
229 | help=help_ksu
230 | )
231 | parser_bundle.add_argument(
232 | "--defconfig",
233 | type=Path,
234 | dest="defconfig",
235 | help=help_defconfig
236 | )
237 | return parser_parent.parse_args(args)
238 |
239 |
240 | def main(args: argparse.Namespace) -> None:
241 | # initialize the logger in memory
242 | _ = logger().get_logger() # type: ignore
243 | # start preparing the environment
244 | os.chdir(dcfg.root)
245 | if args.clean_root:
246 | cm.root()
247 | sys.exit(0)
248 |
249 | # define env variable with kernel version
250 | with open(dcfg.root / "pyproject.toml", encoding="utf-8") as f:
251 | os.environ["KVERSION"] = f.read().split('version = "')[1].split('"')[0]
252 |
253 | # create a config for checking and storing arguments
254 | if args.command != "assets" and args.defconfig:
255 | args.defconfig = args.defconfig if args.defconfig.is_absolute() else Path.cwd() / args.defconfig
256 | arguments = vars(args)
257 | acfg = ArgumentConfig(**arguments)
258 | acfg.check_settings()
259 |
260 | # determine the build variation
261 | match args.benv:
262 | case "docker" | "podman":
263 | with GenericContainerEngine(**json.loads(acfg.model_dump_json())) as engined_cmd:
264 | ccmd.launch(engined_cmd)
265 |
266 | case "local":
267 | match args.command:
268 | case "kernel":
269 | kc = KernelCommand(
270 | codename = args.codename,
271 | base = args.base,
272 | lkv = args.lkv,
273 | clean_kernel = args.clean_kernel,
274 | ksu = args.ksu,
275 | defconfig = args.defconfig,
276 | )
277 | kc.execute()
278 | case "assets":
279 | ac = AssetsCommand(
280 | codename = args.codename,
281 | base = args.base,
282 | chroot = args.chroot,
283 | clean_assets = args.clean_assets,
284 | rom_only = args.rom_only,
285 | ksu = args.ksu,
286 | )
287 | ac.execute()
288 | case "bundle":
289 | bc = BundleCommand(
290 | codename = args.codename,
291 | base = args.base,
292 | lkv = args.lkv,
293 | package_type = args.package_type,
294 | ksu = args.ksu,
295 | defconfig = args.defconfig,
296 | )
297 | bc.execute()
298 |
299 |
300 | if __name__ == "__main__":
301 | # for logs to show in the right order in various build / CI/CD systems
302 | sys.stdout = io.TextIOWrapper(open(sys.stdout.fileno(), "wb", 0), write_through=True)
303 | main(parse_args())
304 |
--------------------------------------------------------------------------------
/builder/clients/__init__.py:
--------------------------------------------------------------------------------
1 | from .github import GithubApiClient
2 | from .los import LineageOsApiClient
3 | from .pa import ParanoidAndroidApiClient
4 |
--------------------------------------------------------------------------------
/builder/clients/github.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import shutil
4 | import logging
5 | import requests
6 | from pathlib import Path
7 | from typing import Optional
8 | from pydantic import BaseModel
9 |
10 | from builder.tools import Logger, cleaning as cm, commands as ccmd
11 | from builder.configs import DirectoryConfig as dcfg
12 |
13 |
14 | log = logging.getLogger("ZeroKernelLogger")
15 |
16 |
17 | class GithubApiClient(BaseModel):
18 | """Client for limited interaction with GitHub API.
19 |
20 | :param str project: GitHub project name (owner/repo).
21 | :param Optional[str]=None file_filter: A filter to select specific files from project's artifacts.
22 | """
23 |
24 | project: str
25 | file_filter: Optional[str] = None
26 |
27 | @property
28 | def endpoint(self) -> str:
29 | """Formatted endpoint.
30 |
31 | :return: GitHub API endpoint for specified project's latest release.
32 | :rtype: str
33 | """
34 | return f"https://api.github.com/repos/{self.project}/releases/latest"
35 |
36 | @property
37 | def direct_url(self) -> str:
38 | """Direct URL to GitHub project.
39 |
40 | :return: URL to GitHub project.
41 | :rtype: str
42 | """
43 | return f"https://github.com/{self.project}"
44 |
45 | def run(self) -> str | None:
46 | """Get the latest version of an artifact from GitHub project.
47 |
48 | :return: URL to download release artifact from if applicable.
49 | :rtype: str | None
50 | """
51 | response = requests.get(self.endpoint).json()
52 |
53 | # check whether the GitHub API usage is exceeded
54 | try:
55 | data = response["message"]
56 | if "API rate limit" in data:
57 | log.error("GitHub API call rate was exceeded, try a bit later.")
58 | except Exception:
59 | pass
60 |
61 | try:
62 | # get direct download URL and optionally filter it with the given parameter
63 | data = response["assets"]
64 | browser_download_urls = []
65 |
66 | for elem in data:
67 | url_dto = elem["browser_download_url"]
68 | if url_dto and self.file_filter in url_dto:
69 | browser_download_urls.append(url_dto)
70 |
71 | # if there is more than one fitting response -- throw an error
72 | if len(browser_download_urls) > 1:
73 | log.error(
74 | "Found more than one suitable assets for the given parameters.\n"\
75 | " Please adjust the file filter."
76 | )
77 | sys.exit(1)
78 | else:
79 | data = "".join(browser_download_urls)
80 |
81 | except Exception:
82 | # if not available via API -- use regular "git clone"
83 | log.warning(f"Non-API GitHub resolution for {self.project}")
84 |
85 | rdir = Path(dcfg.assets, self.direct_url.rsplit("/", 1)[1])
86 |
87 | cm.remove(rdir)
88 | ccmd.launch(
89 | "git clone --depth 1 --remote-submodules --recurse-submodules --shallow-submodules {} {}"
90 | .format(self.direct_url, rdir)
91 | )
92 | os.chdir(rdir)
93 | cm.remove(".git*")
94 | os.chdir(dcfg.assets)
95 | shutil.make_archive(str(rdir), "zip", rdir)
96 | cm.remove(rdir)
97 |
98 | return None
99 |
100 | return data
101 |
--------------------------------------------------------------------------------
/builder/clients/los.py:
--------------------------------------------------------------------------------
1 | from builder.clients.rom_api import RomApiClient
2 |
3 |
4 | class LineageOsApiClient(RomApiClient):
5 | """Client for limited interaction with Lineage API."""
6 |
7 | endpoint: str = "https://download.lineageos.org/api/v1/{}/nightly/ro.build.version.incremental"
8 | json_key: str = "response"
9 | rom_name: str = "LOS"
10 |
--------------------------------------------------------------------------------
/builder/clients/pa.py:
--------------------------------------------------------------------------------
1 | from typing import override
2 |
3 | from builder.clients.rom_api import RomApiClient
4 |
5 |
6 | class ParanoidAndroidApiClient(RomApiClient):
7 | """Client for limited interaction with ParanoidAndroid API."""
8 |
9 | endpoint: str = "https://api.paranoidandroid.co/updates/{}"
10 | json_key: str = "updates"
11 | rom_name: str = "PA"
12 |
13 | @override
14 | def map_codename(self) -> str:
15 | # specific rules for PA's API
16 | specials = {
17 | "dumpling": "oneplus5t",
18 | "cheeseburger": "oneplus5",
19 | }
20 | return specials[self.codename] if self.codename in specials else self.codename
21 |
--------------------------------------------------------------------------------
/builder/clients/rom_api.py:
--------------------------------------------------------------------------------
1 | import requests
2 | import logging
3 | from pydantic import BaseModel
4 |
5 | from builder.tools import Logger
6 | from builder.interfaces import IRomApiClient
7 |
8 |
9 | log = logging.getLogger("ZeroKernelLogger")
10 |
11 |
12 | class RomApiClient(BaseModel, IRomApiClient):
13 | """Generic class for interacting with ROMs' APIs.
14 |
15 | :param str endpoint: API endpoint to interact with.
16 | :param str json_key: A JSON key to look for in the response data.
17 | :param str rom_name: ROM project's name.
18 | :param bool rom_only: Flag indicating ROM-only asset collection.
19 | """
20 |
21 | endpoint: str
22 | json_key: str
23 | rom_name: str
24 | codename: str
25 | rom_only: bool
26 |
27 | def __init__(self, **kwargs) -> None:
28 | super().__init__(**kwargs)
29 | self.endpoint = self.endpoint.format(self.map_codename())
30 |
31 | def map_codename(self) -> str:
32 | # by default, codename is devicename
33 | return self.codename
34 |
35 | def run(self) -> str:
36 | data = requests.get(self.endpoint)
37 |
38 | try:
39 | data = data.json()[self.json_key][0]["url"]
40 | except Exception:
41 | exit_flag = False if self.rom_only else True
42 | log.error(f"Could not connect to {self.rom_name} API, HTTP status code: {data.status_code}")
43 |
44 | return str(data)
45 |
--------------------------------------------------------------------------------
/builder/commands/__init__.py:
--------------------------------------------------------------------------------
1 | from .kernel import KernelCommand
2 | from .bundle import BundleCommand
3 | from .assets import AssetsCommand
4 |
--------------------------------------------------------------------------------
/builder/commands/assets.py:
--------------------------------------------------------------------------------
1 | from typing import Literal
2 | from pydantic import BaseModel
3 |
4 | from builder.core import AssetsCollector
5 | from builder.interfaces import ICommand
6 |
7 |
8 | class AssetsCommand(BaseModel, ICommand):
9 | """Command responsible for launching the 'assets_collector' core module directly.
10 |
11 | :param str codename: Device codename.
12 | :param str base: Kernel source base.
13 | :param Literal["full","minimal"] chroot: Chroot type.
14 | :param bool rom_only: Flag indicating ROM-only asset collection.
15 | :param bool ksu: Flag indicating KernelSU support.
16 | """
17 |
18 | codename: str
19 | base: str
20 | chroot: Literal["full", "minimal"]
21 | clean_assets: bool
22 | rom_only: bool
23 | ksu: bool
24 |
25 | def execute(self) -> None:
26 | ac = AssetsCollector(**self.__dict__)
27 | ac.run()
28 |
--------------------------------------------------------------------------------
/builder/commands/bundle.py:
--------------------------------------------------------------------------------
1 | import os
2 | import json
3 | import shutil
4 | import logging
5 | import itertools
6 | from pathlib import Path
7 | from pydantic import BaseModel
8 | from typing import Literal, Optional
9 |
10 | from builder.core import KernelBuilder, AssetsCollector
11 | from builder.tools import Logger, cleaning as cm, commands as ccmd, fileoperations as fo
12 | from builder.configs import DirectoryConfig as dcfg
13 | from builder.managers import ResourceManager
14 | from builder.interfaces import ICommand, IBundleCommand
15 |
16 |
17 | log = logging.getLogger("ZeroKernelLogger")
18 |
19 |
20 | class BundleCommand(BaseModel, ICommand, IBundleCommand):
21 | """Command that packages the artifacts produced both by 'kernel_builder' and 'assets_collector' core modules.
22 |
23 | :param str base: Kernel source base.
24 | :param str lkv: Linux kernel version.
25 | :param str package_type: Package type.
26 | :param bool ksu: Flag indicating KernelSU support.
27 | :param Optional[Path]=None defconfig: Path to custom defconfig.
28 | """
29 |
30 | codename: str
31 | base: str
32 | lkv: str
33 | package_type: str
34 | ksu: bool
35 | defconfig: Optional[Path] = None
36 |
37 | def build_kernel(self, rom_name: str, clean_only: bool = False) -> None:
38 | if not dcfg.kernel.is_dir() or clean_only is True:
39 | kb = KernelBuilder(
40 | codename = self.codename,
41 | base = rom_name,
42 | lkv = self.lkv,
43 | clean_kernel = clean_only,
44 | ksu = self.ksu,
45 | rmanager=ResourceManager(codename=self.codename, lkv=self.lkv, base=self.base),
46 | defconfig = self.defconfig,
47 | )
48 | kb.run()
49 |
50 | @property
51 | def _rom_only_flag(self) -> bool:
52 | return True if "full" not in self.package_type else False
53 |
54 | def collect_assets(self, rom_name: str, chroot: Literal["full", "minimal"]) -> None:
55 | ac = AssetsCollector(
56 | codename = self.codename,
57 | base = rom_name,
58 | chroot = chroot,
59 | clean_assets = True,
60 | rom_only = self._rom_only_flag,
61 | ksu = self.ksu,
62 | )
63 | ac.run()
64 |
65 | def conan_sources(self) -> None:
66 | print("\n", end="")
67 | log.warning("Copying sources for Conan packaging..")
68 |
69 | sourcedir = dcfg.root / "source"
70 | cm.remove(str(sourcedir))
71 | # NOTE: .venv is intentionally kept here, for Python environment repoducibility
72 | fo.ucopy(
73 | dcfg.root,
74 | sourcedir,
75 | (
76 | dcfg.kernel,
77 | dcfg.assets,
78 | "__pycache__",
79 | ".vscode",
80 | ".coverage",
81 | ".pytest_cache",
82 | ".ruff_cache",
83 | ".ropeproject",
84 | "source",
85 | "localversion",
86 | "conanfile.py"
87 | )
88 | )
89 |
90 | log.info("Done!")
91 |
92 | @staticmethod
93 | def conan_options(json_file: str) -> dict:
94 | with open(json_file, encoding="utf-8") as f:
95 | json_data = json.load(f)
96 |
97 | return json_data
98 |
99 | def conan_package(self, options: tuple[str, ...], reference: str) -> None:
100 | cmd = f"conan export-pkg . {reference}"
101 |
102 | for option_value in options:
103 | # not the best solution, but will work temporarily for 'rom' and 'chroot' options
104 | option_name = "rom" if not any(c.isalpha() for c in option_value) else "chroot"
105 | cmd += f" -o {option_name}={option_value}"
106 |
107 | # add codename as an option separately
108 | cmd += f" -o codename={self.codename}"
109 | ccmd.launch(cmd)
110 |
111 | @staticmethod
112 | def conan_upload(reference: str) -> None:
113 | # configure Conan client and upload packages
114 | url = "https://gitlab.com/api/v4/projects/40803264/packages/conan"
115 | alias = "zero-kernel-conan"
116 | cmd = f"conan remote add {alias} {url} && "\
117 | f"conan user -p {os.getenv('CONAN_PASSWORD')} -r {alias} {os.getenv('CONAN_LOGIN_USERNAME')} && "\
118 | f"conan upload -f {reference} -r {alias}"
119 |
120 | ccmd.launch(cmd)
121 |
122 | def execute(self) -> None:
123 | os.chdir(dcfg.root)
124 |
125 | # determine the bundle type and process it
126 | match self.package_type:
127 | case "slim" | "full":
128 | self.build_kernel(self.base)
129 |
130 | # "full" chroot is hardcoded here
131 | self.collect_assets(self.base, "full")
132 |
133 | # clean up
134 | if dcfg.bundle.is_dir():
135 | contents = dcfg.bundle.glob("*")
136 | for f in contents:
137 | os.remove(f)
138 | else:
139 | os.makedirs(dcfg.bundle)
140 |
141 | # copy kernel
142 | kfn = "".join(os.listdir(dcfg.kernel))
143 | shutil.copy(dcfg.kernel / kfn, dcfg.bundle / kfn)
144 |
145 | # move assets (and not copy because they are way too big)
146 | for afn in os.listdir(dcfg.assets):
147 | # here, because of their size assets are moved and not copied
148 | shutil.move(dcfg.assets / afn, dcfg.bundle / afn)
149 |
150 | case "conan":
151 | # form Conan reference
152 | name = "zero_kernel"
153 | version = os.getenv("KVERSION")
154 | user = self.codename
155 | channel = ""
156 |
157 | if ccmd.launch("git branch --show-current", get_output=True) == "main":
158 | channel = "stable"
159 | else:
160 | channel = "testing"
161 |
162 | reference = f"{name}/{version}@{user}/{channel}"
163 |
164 | # form option sets
165 | chroot = ("minimal", "full")
166 | option_sets = list(itertools.product([self.base], chroot))
167 |
168 | # build and upload Conan packages
169 | for opset in option_sets:
170 | self.build_kernel(opset[0])
171 | self.build_kernel(opset[0], True)
172 | self.conan_sources()
173 | self.collect_assets(opset[0], opset[1])
174 | self.conan_package(opset, reference)
175 |
176 | # upload packages
177 | if os.getenv("CONAN_UPLOAD_CUSTOM") == "1":
178 | self.conan_upload(reference)
179 |
180 | # navigate back to root directory
181 | os.chdir(dcfg.root)
182 |
--------------------------------------------------------------------------------
/builder/commands/kernel.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from typing import Optional
3 | from pydantic import BaseModel
4 |
5 | from builder.core import KernelBuilder
6 | from builder.managers import ResourceManager
7 | from builder.interfaces import ICommand
8 |
9 |
10 | class KernelCommand(BaseModel, ICommand):
11 | """Command responsible for launching the 'kernel_builder' core module directly.
12 |
13 | :param str codename: Device codename.
14 | :param str base: Kernel source base.
15 | :param str lkv: Linux kernel version.
16 | :param bool clean_kernel: Flag to clean folder with kernel sources.
17 | :param bool ksu: Flag indicating KernelSU support.
18 | :param Optional[Path]=None defconfig: Path to custom defconfig.
19 | """
20 |
21 | codename: str
22 | base: str
23 | lkv: str
24 | clean_kernel: bool
25 | ksu: bool
26 | defconfig: Optional[Path] = None
27 |
28 | def execute(self) -> None:
29 | # create resource manager and pass it to the builder
30 | kb = KernelBuilder(
31 | **self.__dict__,
32 | rmanager=ResourceManager(codename=self.codename, lkv=self.lkv, base=self.base)
33 | )
34 | kb.run()
35 |
--------------------------------------------------------------------------------
/builder/configs/__init__.py:
--------------------------------------------------------------------------------
1 | from .argument import ArgumentConfig
2 | from .directory import DirectoryConfig
3 |
--------------------------------------------------------------------------------
/builder/configs/argument.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import json
3 | import logging
4 | import platform
5 | from pathlib import Path
6 | from pydantic import BaseModel
7 | from typing import Optional, Literal
8 |
9 | from builder.tools import Logger, commands as ccmd
10 |
11 |
12 | log = logging.getLogger("ZeroKernelLogger")
13 |
14 |
15 | class ArgumentConfig(BaseModel):
16 | """Variable storage for usage across the app.
17 |
18 | :param Literal["docker","podman","local"] benv: Build environment.
19 | :param Literal["kernel","assets","bundle"] command: Builder command to be launched.
20 | :param str codename: Device codename.
21 | :param str base: Kernel source base.
22 | :param str lkv: Linux kernel version.
23 | :param Optional[str]=None chroot: Chroot type.
24 | :param Optional[str]=None package_type: Package type.
25 | :param Optional[bool]=False clean_kernel: Flag to clean folder with kernel sources.
26 | :param Optional[bool]=False clean_assets: Flag to clean folder for assets storage.
27 | :param Optional[bool]=False clean_image: Flag to clean a Docker/Podman image from local cache.
28 | :param Optional[bool]=False rom_only: Flag indicating ROM-only asset collection.
29 | :param Optional[bool]=False conan_upload: Flag to enable Conan upload.
30 | :param Optional[bool]=False ksu: Flag indicating KernelSU support.
31 | :param Optional[Path]=None defconfig: Path to custom defconfig.
32 | """
33 |
34 | benv: Literal["docker", "podman", "local"]
35 | command: Literal["kernel", "assets", "bundle"]
36 | codename: str
37 | base: str
38 | lkv: Optional[str] = None
39 | chroot: Optional[str] = None
40 | package_type: Optional[str] = None
41 | clean_kernel: Optional[bool] = False
42 | clean_assets: Optional[bool] = False
43 | clean_image: Optional[bool] = False
44 | rom_only: Optional[bool] = False
45 | conan_upload: Optional[bool] = False
46 | ksu: Optional[bool] = False
47 | defconfig: Optional[Path] = None
48 |
49 | def check_settings(self) -> None:
50 | """Run settings validations.
51 |
52 | :return: None
53 | """
54 | # allow only asset colletion on a non-Linux machine
55 | if self.benv == "local" and self.command in {"kernel", "bundle"}:
56 | if not platform.system() == "Linux":
57 | log.error("Can't build kernel on a non-Linux machine.")
58 | sys.exit(1)
59 | else:
60 | # check that it is Debian-based
61 | try:
62 | ccmd.launch("apt --version", loglvl="quiet")
63 | except Exception:
64 | log.error("Detected Linux distribution is not Debian-based.")
65 | sys.exit(1)
66 |
67 | # check if specified device is supported
68 | with open(
69 | Path(__file__).absolute().parents[2] / "builder" / "manifests" / "devices.json", encoding="utf-8"
70 | ) as f:
71 | devices = json.load(f)
72 |
73 | if self.codename not in devices.keys():
74 | log.error("Unsupported device codename specified.")
75 | sys.exit(1)
76 | if self.command == "bundle":
77 | # check Conan-related argument usage
78 | if self.package_type != "conan" and self.conan_upload:
79 | log.error("Cannot use Conan-related arguments with non-Conan packaging\n")
80 | sys.exit(1)
81 |
82 | # check that the provided defconfig file is valid
83 | if self.defconfig and not self.defconfig.is_file():
84 | log.error("Provided path to defconfig is invalid.")
85 | sys.exit(1)
86 |
--------------------------------------------------------------------------------
/builder/configs/directory.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from pydantic.dataclasses import dataclass
3 |
4 |
5 | @dataclass
6 | class DirectoryConfig:
7 | """Config for key directory paths."""
8 | root: Path = Path(__file__).absolute().parents[2]
9 | kernel: Path = root / "kernel"
10 | assets: Path = root / "assets"
11 | bundle: Path = root / "bundle"
12 |
--------------------------------------------------------------------------------
/builder/core/__init__.py:
--------------------------------------------------------------------------------
1 | from .kernel_builder import KernelBuilder
2 | from .assets_collector import AssetsCollector
3 |
--------------------------------------------------------------------------------
/builder/core/assets_collector.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import logging
4 | from typing import Literal
5 | from pydantic import BaseModel
6 |
7 | from builder.tools import Logger, cleaning as cm, fileoperations as fo, banner
8 | from builder.clients import GithubApiClient, LineageOsApiClient, ParanoidAndroidApiClient
9 | from builder.configs import DirectoryConfig as dcfg
10 | from builder.interfaces import IAssetsCollector
11 |
12 |
13 | log = logging.getLogger("ZeroKernelLogger")
14 |
15 |
16 | class AssetsCollector(BaseModel, IAssetsCollector):
17 | """Assets collector.
18 |
19 | :param str codename: Device codename.
20 | :param str base: Kernel source base.
21 | :param Literal["full","minimal"] chroot: Chroot type.
22 | :param bool rom_only: Flag indicating ROM-only asset collection.
23 | :param bool ksu: Flag indicating KernelSU support.
24 | """
25 |
26 | codename: str
27 | base: str
28 | chroot: Literal["full", "minimal"]
29 | clean_assets: bool
30 | rom_only: bool
31 | ksu: bool
32 |
33 | @property
34 | def rom_collector_dto(self) -> LineageOsApiClient | ParanoidAndroidApiClient | None:
35 | match self.base:
36 | case "los":
37 | return LineageOsApiClient(codename=self.codename, rom_only=self.rom_only)
38 | case "pa":
39 | return ParanoidAndroidApiClient(codename=self.codename, rom_only=self.rom_only)
40 | case "x" | "aosp":
41 | # selected kernel base is ROM-universal, no specific ROM image will be collected
42 | return None
43 |
44 | @property
45 | def assets(self) -> list:
46 | # define Disable_Dm-Verity_ForceEncrypt and SU manager
47 | dfd = GithubApiClient(project="seppzer0/Disable_Dm-Verity_ForceEncrypt")
48 | su_manager = "tiann/KernelSU" if self.ksu else "topjohnwu/Magisk"
49 |
50 | # process the "ROM-only" download for non-universal kernel bases
51 | if self.rom_only:
52 | if not self.rom_collector_dto:
53 | return [dfd,]
54 | else:
55 | # add DFD alongside the ROM
56 | print("\n", end="")
57 | log.info("ROM-only asset collection specified")
58 | return [self.rom_collector_dto.run(), dfd]
59 |
60 | # process the full download
61 | else:
62 | assets = [
63 | # Disable_Dm-Verity_ForceEncrypt
64 | dfd,
65 | # files from GitHub projects
66 | GithubApiClient(
67 | project=su_manager,
68 | file_filter=".apk"
69 | ),
70 | GithubApiClient(
71 | project="klausw/hackerskeyboard",
72 | file_filter=".apk"
73 | ),
74 | GithubApiClient(
75 | project="aleksey-saenko/TTLChanger",
76 | file_filter=".apk"
77 | ),
78 | GithubApiClient(
79 | project="ukanth/afwall",
80 | file_filter=".apk"
81 | ),
82 | GithubApiClient(
83 | project="emanuele-f/PCAPdroid",
84 | file_filter=".apk"
85 | ),
86 | GithubApiClient(
87 | project="nfcgate/nfcgate",
88 | file_filter=".apk"
89 | ),
90 | # files from direct URLs
91 | "https://store.nethunter.com/NetHunter.apk",
92 | "https://store.nethunter.com/NetHunterKeX.apk",
93 | "https://store.nethunter.com/NetHunterStore.apk",
94 | "https://store.nethunter.com/NetHunterTerminal.apk",
95 | "https://sourceforge.net/projects/op5-5t/files/Android-12/TWRP/twrp-3.7.0_12-5-dyn-cheeseburger_dumpling.img/download",
96 | "https://kali.download/nethunter-images/current/rootfs/kali-nethunter-rootfs-{}-arm64.tar.xz".format(self.chroot),
97 | "https://github.com/mozilla-mobile/firefox-android/releases/download/fenix-v117.1.0/fenix-117.1.0-arm64-v8a.apk",
98 | "https://f-droid.org/F-Droid.apk",
99 | ]
100 |
101 | # add ROM if kernel base is not universal
102 | if self.rom_collector_dto:
103 | assets.append(self.rom_collector_dto.run()) # type: ignore
104 |
105 | return assets
106 |
107 | return None
108 |
109 | def _check(self) -> None:
110 | os.chdir(dcfg.root)
111 |
112 | # directory check
113 | if not dcfg.assets.is_dir():
114 | os.makedirs(dcfg.assets)
115 | else:
116 | if len(os.listdir(dcfg.assets)) != 0:
117 | cmsg = f'[ ? ] Found an existing "{dcfg.assets.name}" folder, clean it? [Y/n]: '
118 | ans = input(cmsg).lower() if not self.clean_assets else "y"
119 |
120 | match ans:
121 | case "y":
122 | log.warning("Cleaning 'assets' directory..")
123 | os.chdir(dcfg.assets)
124 | cm.remove("./*")
125 | os.chdir(dcfg.root)
126 | log.info("Done!")
127 | case "n":
128 | log.warning("Cancelling asset download.")
129 | case _:
130 | log.error("Invalid option selected.")
131 | sys.exit(1)
132 |
133 | print("\n", end="")
134 |
135 | def run(self) -> None:
136 | banner.print_banner("zero asset collector")
137 |
138 | os.chdir(dcfg.root)
139 | self._check()
140 | os.chdir(dcfg.assets)
141 | # NOTE: call "self.assets" only once!
142 | assets = self.assets
143 |
144 | if isinstance(assets, list) or isinstance(assets, tuple):
145 | for e in assets:
146 | if isinstance(e, GithubApiClient):
147 | e.run()
148 | else:
149 | fo.download(e)
150 |
151 | print("\n", end="")
152 | log.info("Assets collected!")
153 | os.chdir(dcfg.root)
154 |
--------------------------------------------------------------------------------
/builder/core/kernel_builder.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import time
4 | import logging
5 | from pathlib import Path
6 | from typing import Optional
7 | from pydantic import BaseModel
8 |
9 | from builder.tools import Logger, cleaning as cm, commands as ccmd, fileoperations as fo, banner
10 | from builder.configs import DirectoryConfig as dcfg
11 | from builder.managers import ResourceManager
12 | from builder.interfaces import IKernelBuilder
13 |
14 |
15 | log = logging.getLogger("ZeroKernelLogger")
16 |
17 |
18 | class KernelBuilder(BaseModel, IKernelBuilder):
19 | """Kernel builder.
20 |
21 | :param str codename: Device codename.
22 | :param str base: Kernel source base.
23 | :param str lkv: Linux kernel version.
24 | :param bool clean_kernel: Flag to clean folder with kernel sources.
25 | :param bool ksu: Flag indicating KernelSU support.
26 | :param Optional[Path]=None defconfig: Path to custom defconfig.
27 | """
28 |
29 | codename: str
30 | base: str
31 | lkv: str
32 | clean_kernel: bool
33 | ksu: bool
34 | rmanager: ResourceManager
35 | defconfig: Optional[Path] = None
36 |
37 | @staticmethod
38 | def write_localversion() -> None:
39 | with open("localversion", "w", encoding="utf-8") as f:
40 | f.write("~zero_kernel")
41 |
42 | @property
43 | def _ucodename(self) -> str:
44 | """Define unified codename for devices series with same kernels.
45 |
46 | :return: Unified codename.
47 | :rtype: str
48 | """
49 | if self.codename in ("dumpling", "cheeseburger"):
50 | return "dumplinger"
51 | elif "guacamole" in self.codename:
52 | return "guacamoles"
53 | else:
54 | return self.codename
55 |
56 | @property
57 | def _defconfig(self) -> Path:
58 | """Define defconfig.
59 |
60 | :return: Path to defconfig.
61 | :rtype: Path
62 | """
63 | # return custom defconfig if it is specified
64 | if self.defconfig:
65 | return Path(self.defconfig.name)
66 |
67 | # list of the available defconfigs
68 | op7_defconfigs = {
69 | "los": "lineage_sm8150_defconfig",
70 | }
71 | op5_defconfigs = {
72 | "los": "lineage_oneplus5_defconfig",
73 | "pa": "vendor/paranoid_defconfig" if self.lkv == "4.14" else "paranoid_defconfig",
74 | "x": "msm8998_oneplus_android_defconfig" if self.lkv == "4.14" else "oneplus5_defconfig"
75 | }
76 |
77 | # convert output to path object
78 | if "guacamole" in self.codename:
79 | return Path(op7_defconfigs[self.base])
80 | elif self.codename in ("dumpling", "cheeseburger"):
81 | return Path(op5_defconfigs[self.base])
82 | else:
83 | return Path()
84 |
85 | def clean_build(self) -> None:
86 | print("\n", end="")
87 | log.warning("Cleaning the build environment..")
88 |
89 | cm.git(self.rmanager.paths[self.codename])
90 | cm.git(self.rmanager.paths["AnyKernel3"])
91 | cm.git(self.rmanager.paths["KernelSU"])
92 |
93 | for fn in os.listdir():
94 | if fn == "localversion" or fn.endswith(".zip"):
95 | cm.remove(fn)
96 |
97 | log.info("Done!")
98 |
99 | def patch_strict_prototypes(self) -> None:
100 | log.warning("Patching sources for Clang 15+ compatibility..")
101 |
102 | data = {
103 | self.rmanager.paths[self.codename] /\
104 | "drivers" /\
105 | "char" /\
106 | "diag" /\
107 | "diagchar_core.c":
108 | ("void diag_ws_init()", "void diag_ws_on_notify()", "void diag_ws_release()",),
109 |
110 | self.rmanager.paths[self.codename] /\
111 | "drivers" /\
112 | "char" /\
113 | "diag" /\
114 | "diag_mux.c":
115 | ("int diag_mux_init()", "void diag_mux_exit()",),
116 |
117 | self.rmanager.paths[self.codename] /\
118 | "drivers" /\
119 | "char" /\
120 | "diag" /\
121 | "diag_memorydevice.c":
122 | ("void diag_md_open_all()", "void diag_md_close_all()",),
123 |
124 | self.rmanager.paths[self.codename] /\
125 | "drivers" /\
126 | "char" /\
127 | "diag" /\
128 | "diag_dci.c":
129 | ("void diag_dci_wakeup_clients()",),
130 |
131 | self.rmanager.paths[self.codename] /\
132 | "drivers" /\
133 | "char" /\
134 | "diag" /\
135 | "diagfwd_bridge.c":
136 | ("void diagfwd_bridge_exit()", "uint16_t diag_get_remote_device_mask()",),
137 |
138 | self.rmanager.paths[self.codename] /\
139 | "drivers" /\
140 | "char" /\
141 | "diag" /\
142 | "diagfwd_mhi.c":
143 | ("int diag_mhi_init()", "void diag_mhi_exit()",),
144 |
145 | self.rmanager.paths[self.codename] /\
146 | "drivers" /\
147 | "media" /\
148 | "platform" /\
149 | "msm" /\
150 | "camera_v2" /\
151 | "common" /\
152 | "msm_camera_tz_util.c":
153 | ("struct qseecom_handle *msm_camera_tz_get_ta_handle()",),
154 |
155 | self.rmanager.paths[self.codename] /\
156 | "drivers" /\
157 | "media" /\
158 | "platform" /\
159 | "msm" /\
160 | "vidc" /\
161 | "msm_vidc_common.c":
162 | ("void msm_comm_handle_thermal_event()",),
163 |
164 | self.rmanager.paths[self.codename] /\
165 | "drivers" /\
166 | "soc" /\
167 | "qcom" /\
168 | "msm_bus" /\
169 | "msm_bus_rpm_smd.c":
170 | ("static int voice_svc_dummy_reg()",),
171 |
172 | self.rmanager.paths[self.codename] /\
173 | "drivers" /\
174 | "soc" /\
175 | "qcom" /\
176 | "msm_bus" /\
177 | "msm_bus_rpm_smd.c":
178 | ("void msm_bus_rpm_set_mt_mask()",),
179 |
180 | self.rmanager.paths[self.codename] /\
181 | "drivers" /\
182 | "staging" /\
183 | "qca-wifi-host-cmn" /\
184 | "hif" /\
185 | "src" /\
186 | "ce" /\
187 | "ce_service.c":
188 | ("struct ce_ops *ce_services_legacy()",),
189 |
190 | self.rmanager.paths[self.codename] /\
191 | "drivers" /\
192 | "staging" /\
193 | "qcacld-3.0" /\
194 | "core" /\
195 | "hdd" /\
196 | "src" /\
197 | "wlan_hdd_main.c":
198 | ("hdd_adapter_t *hdd_get_first_valid_adapter()",),
199 |
200 | self.rmanager.paths[self.codename] /\
201 | "drivers" /\
202 | "video" /\
203 | "fbdev" /\
204 | "msm" /\
205 | "mdss_mdp.c":
206 | ("struct irq_info *mdss_intr_line()",),
207 |
208 | self.rmanager.paths[self.codename] /\
209 | "drivers" /\
210 | "video" /\
211 | "fbdev" /\
212 | "msm" /\
213 | "mdss_util.c":
214 | ("struct mdss_util_intf *mdss_get_util_intf()",)
215 | }
216 |
217 | # the following files are not present in 4.14
218 | if self._lkv_src != "4.14":
219 | extra_non_414 = {
220 | self.rmanager.paths[self.codename] /\
221 | "drivers" /\
222 | "soc" /\
223 | "qcom" /\
224 | "qdsp6v2" /\
225 | "voice_svc.c":
226 | ("void msm_bus_rpm_set_mt_mask()", "static int voice_svc_dummy_reg()"),
227 |
228 | self.rmanager.paths[self.codename] /\
229 | "drivers" /\
230 | "thermal" /\
231 | "msm_thermal-dev.c":
232 | ("int msm_thermal_ioctl_init()", "void msm_thermal_ioctl_cleanup()",),
233 | }
234 | data.update(extra_non_414)
235 |
236 | # PA needs this, LineageOS does not
237 | if self.base == "pa":
238 | extra_pa = {
239 | self.rmanager.paths[self.codename] /\
240 | "drivers" /\
241 | "staging" /\
242 | "qca-wifi-host-cmn" /\
243 | "target_if" /\
244 | "core" /\
245 | "src" /\
246 | "target_if_main.c":
247 | ("struct target_if_ctx *target_if_get_ctx()",),
248 |
249 | self.rmanager.paths[self.codename] /\
250 | "drivers" /\
251 | "staging" /\
252 | "qca-wifi-host-cmn" /\
253 | "wlan_cfg" /\
254 | "wlan_cfg.c":
255 | ("struct wlan_cfg_dp_soc_ctxt *wlan_cfg_soc_attach()",),
256 | }
257 | data.update(extra_pa)
258 |
259 | # start the patching process
260 | contents = ""
261 | for fname, funcnames in data.items():
262 | with open(fname, "r", encoding="utf-8") as f:
263 | contents = f.read()
264 | for func in funcnames:
265 | contents = contents.replace(func, func.replace("()", "(void)"))
266 | with open(fname, "w") as f:
267 | f.write(contents)
268 |
269 | log.info("Done!")
270 |
271 | def patch_anykernel3(self) -> None:
272 | cm.remove(self.rmanager.paths["AnyKernel3"] / "ramdisk")
273 | cm.remove(self.rmanager.paths["AnyKernel3"] / "models")
274 |
275 | fo.ucopy(
276 | dcfg.root / "builder" / "modifications" / self._ucodename / "anykernel3" / "ramdisk",
277 | self.rmanager.paths["AnyKernel3"] / "ramdisk"
278 | )
279 | fo.ucopy(
280 | dcfg.root / "builder" / "modifications" / self._ucodename / "anykernel3" / "anykernel.sh",
281 | self.rmanager.paths["AnyKernel3"] / "anykernel.sh"
282 | )
283 |
284 | def patch_rtl8812au_source_mod_v5642(self) -> None:
285 | # Makefile
286 | fo.replace_lines(
287 | Path("Makefile").absolute(),
288 | (
289 | "#EXTRA_CFLAGS += -Wno-parentheses-equality",
290 | "#EXTRA_CFLAGS += -Wno-pointer-bool-conversion",
291 | "$(MAKE) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) -C $(KSRC) M=$(shell pwd) modules",
292 | "CONFIG_PLATFORM_I386_PC = y",
293 | "CONFIG_PLATFORM_ANDROID_ARM64 = n",
294 | ),
295 | (
296 | "EXTRA_CFLAGS += -Wno-parentheses-equality",
297 | "EXTRA_CFLAGS += -Wno-pointer-bool-conversion\nEXTRA_CFLAGS += -Wno-pointer-bool-conversion\nEXTRA_CFLAGS += -Wno-pragma-pack\nEXTRA_CFLAGS += -Wno-unused-variable", # noqa: E501
298 | '$(MAKE) ARCH=$(ARCH) SUBARCH=$(ARCH) REAL_CC=${CC_DIR}/clang CLANG_TRIPLE=aarch64-linux-gnu- CROSS_COMPILE=$(CROSS_COMPILE) -C $(KSRC) M=$(shell pwd) O="$(KBUILD_OUTPUT)" modules', # noqa: E501
299 | "CONFIG_PLATFORM_I386_PC = n",
300 | "CONFIG_PLATFORM_ANDROID_ARM64 = y\nCONFIG_CONCURRENT_MODE = n",
301 | )
302 | )
303 |
304 | # ioctl_cfg80211.h
305 | fo.replace_lines(
306 | Path("os_dep", "linux", "ioctl_cfg80211.h").absolute(),
307 | ("#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 7, 0))",),
308 | ("#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 26)) && (LINUX_VERSION_CODE < KERNEL_VERSION(4, 4, 0))",)
309 | )
310 |
311 | # ioctl_cfg80211.c
312 | fo.replace_lines(
313 | Path("os_dep", "linux", "ioctl_cfg80211.c").absolute(),
314 | (
315 | "sinfo->bss_param.flags |= STATION_INFO_BSS_PARAM_SHORT_PREAMBLE;",
316 | "sinfo->bss_param.flags |= STATION_INFO_BSS_PARAM_SHORT_SLOT_TIME;",
317 | "sinfo->bss_param.flags |= STATION_INFO_BSS_PARAM_CTS_PROT;",
318 | "sinfo->bss_param.flags |= STATION_INFO_BSS_PARAM_DTIM_PERIOD;",
319 | ),
320 | (
321 | "sinfo->bss_param.flags |= NL80211_STA_BSS_PARAM_SHORT_PREAMBLE;",
322 | "sinfo->bss_param.flags |= NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME;",
323 | "sinfo->bss_param.flags |= NL80211_STA_BSS_PARAM_CTS_PROT;",
324 | "sinfo->bss_param.flags |= NL80211_STA_BSS_PARAM_DTIM_PERIOD;",
325 | )
326 | )
327 |
328 | def update_defconfig(self) -> None:
329 | defconfig = self.rmanager.paths[self.codename] /\
330 | "arch" /\
331 | "arm64" /\
332 | "configs" /\
333 | self._defconfig
334 |
335 | # base changes (Wi-Fi + RTL8812AU)
336 | extra_configs = [
337 | #"CONFIG_88XXAU=y",
338 | "CONFIG_MODULE_FORCE_LOAD=y",
339 | "CONFIG_MODULE_FORCE_UNLOAD=y",
340 | "CONFIG_CFG80211_WEXT=y",
341 | "CONFIG_CFG80211_WEXT_EXPORT=y",
342 | "CONFIG_CONCURRENT_MODE=n",
343 | "CONFIG_MAC80211=y",
344 | "CONFIG_RTL8187=y",
345 | "CONFIG_RTLWIFI=y",
346 | ]
347 |
348 | # KernelSU changes
349 | if self.ksu:
350 | extra_configs.extend([
351 | "CONFIG_KSU=y",
352 | "CONFIG_MODULES=y",
353 | "CONFIG_MODULE_UNLOAD=y",
354 | "CONFIG_MODVERSIONS=y",
355 | "CONFIG_DIAG_CHAR=y",
356 | "CONFIG_KPROBES=y",
357 | "CONFIG_HAVE_KPROBES=y",
358 | "CONFIG_KPROBE_EVENTS=y",
359 | ])
360 |
361 | # apply changes
362 | with open(defconfig, "a", encoding="utf-8") as f:
363 | f.write("\n".join(extra_configs))
364 | f.write("\n")
365 |
366 | def patch_rtl8812au(self) -> None:
367 | # copy RTL8812AU sources into kernel sources
368 | log.warning("Adding RTL8812AU drivers into the kernel..")
369 | fo.ucopy(
370 | self.rmanager.paths["rtl8812au"],
371 | self.rmanager.paths[self.codename] /\
372 | "drivers" /\
373 | "net" /\
374 | "wireless" /\
375 | "realtek" /\
376 | "rtl8812au"
377 | )
378 |
379 | # modify sources depending on driver version
380 | os.chdir(
381 | self.rmanager.paths[self.codename] /\
382 | "drivers" /\
383 | "net" /\
384 | "wireless" /\
385 | "realtek" /\
386 | "rtl8812au"
387 | )
388 | self.patch_rtl8812au_source_mod_v5642()
389 | cm.remove(".git*")
390 | os.chdir(dcfg.root)
391 |
392 | # include the driver into build process
393 | makefile = self.rmanager.paths[self.codename] /\
394 | "drivers" /\
395 | "net" /\
396 | "wireless" /\
397 | "realtek" /\
398 | "Makefile"
399 | kconfig = self.rmanager.paths[self.codename] /\
400 | "drivers" /\
401 | "net" /\
402 | "wireless" /\
403 | "Kconfig"
404 | with open(makefile, "a", encoding="utf-8") as f:
405 | f.write("obj-$(CONFIG_88XXAU) += rtl8812au/")
406 | fo.insert_before_line(
407 | kconfig,
408 | "endif",
409 | 'source "drivers/net/wireless/realtek/rtl8812au/Kconfig"'
410 | )
411 |
412 | def patch_ksu(self) -> None:
413 | log.warning("Adding KernelSU into the kernel..")
414 |
415 | patch_name = "kernelsu-compat.patch"
416 |
417 | # extract KSU version manually and include it via symlink
418 | goback = Path.cwd()
419 | os.chdir(self.rmanager.paths["KernelSU"])
420 | os.environ["KSU_GIT_VERSION"] = str(
421 | # official formula documented in KernelSU's Makefile
422 | 10000 + int(ccmd.launch("git rev-list --count HEAD", get_output=True)) + 200 # type: ignore
423 | )
424 | os.chdir(goback)
425 |
426 | makefile = self.rmanager.paths[self.codename] /\
427 | "drivers" /\
428 | "Makefile"
429 | kconfig = self.rmanager.paths[self.codename] /\
430 | "drivers" /\
431 | "Kconfig"
432 |
433 | os.symlink(
434 | self.rmanager.paths["KernelSU"] / "kernel",
435 | self.rmanager.paths[self.codename] /\
436 | "drivers" /\
437 | "kernelsu"
438 | )
439 |
440 | with open(makefile, "a", encoding="utf-8") as f:
441 | f.write("obj-$(CONFIG_KSU) += kernelsu/\n")
442 | fo.insert_before_line(
443 | kconfig,
444 | "endmenu",
445 | 'source "drivers/kernelsu/Kconfig"'
446 | )
447 |
448 | # either patch kernel or KernelSU sources, depending on Linux kernel version
449 | target_d = dcfg.root / "KernelSU" if self._lkv_src == "4.14" else self.rmanager.paths[self.codename]
450 | fo.ucopy(
451 | dcfg.root / "builder" / "modifications" / self._ucodename / self._lkv_src / patch_name,
452 | target_d
453 | )
454 | os.chdir(target_d)
455 | fo.apply_patch(patch_name)
456 | os.chdir(goback)
457 |
458 | def patch_qcacld(self) -> None:
459 | patch_name = "qcacld_pa.patch"
460 |
461 | goback = Path.cwd()
462 |
463 | fo.ucopy(
464 | dcfg.root / "builder" / "modifications" / self._ucodename / self._lkv_src / patch_name,
465 | self.rmanager.paths[self.codename]
466 | )
467 | os.chdir(self.rmanager.paths[self.codename])
468 |
469 | fo.apply_patch(patch_name)
470 | os.chdir(goback)
471 |
472 | def patch_ioctl(self) -> None:
473 | ioctl = self.rmanager.paths[self.codename] /\
474 | "drivers" /\
475 | "platform" /\
476 | "msm" /\
477 | "ipa" /\
478 | "ipa_v3" /\
479 | "ipa.c"
480 |
481 | fo.replace_lines(
482 | ioctl.absolute(),
483 | (" u8 header[128] = { 0 };",),
484 | (" u8 header[512] = { 0 };",),
485 | )
486 |
487 | def patch_kernel(self) -> None:
488 | # -Wstrict-prototypes patch to build with Clang 15+
489 | clang_cmd = f'{self.rmanager.paths["clang"] / "bin" / "clang"} --version'
490 | clang_ver = str(ccmd.launch(clang_cmd, get_output=True)).split("clang version ")[1].split(".")[0]
491 |
492 | if int(clang_ver) >= 15:
493 | self.patch_strict_prototypes()
494 |
495 | # apply .patch files
496 | fo.ucopy(
497 | dcfg.root / "builder" / "modifications" / self._ucodename / self._lkv_src,
498 | self.rmanager.paths[self.codename],
499 | ("kernelsu-compat.patch", "qcacld_pa.patch")
500 | )
501 | os.chdir(self.rmanager.paths[self.codename])
502 |
503 | for pf in Path.cwd().glob("*.patch"):
504 | fo.apply_patch(pf)
505 |
506 | # add support for CONFIG_MAC80211 kernel option
507 | data = ""
508 | files = ("tx.c", "mlme.c")
509 |
510 | os.chdir(self.rmanager.paths[self.codename])
511 |
512 | for fn in files:
513 | f_path = self.rmanager.paths[self.codename] / "net" / "mac80211" / fn
514 | if f_path.is_file():
515 | with open(f_path, "r", encoding="utf-8") as f:
516 | data = f.read().replace("case IEEE80211_BAND_60GHZ:", "case NL80211_BAND_60GHZ:")
517 | with open(f_path, "w", encoding="utf-8") as f:
518 | f.write(data)
519 | else:
520 | log.warning(f"Modification of {str(f_path)} is skipped")
521 |
522 | # some patches only for ParanoidAndroid
523 | if self.base == "pa":
524 | if self._lkv_src == "4.4":
525 | self.patch_qcacld()
526 | self.patch_ioctl()
527 |
528 | os.chdir(dcfg.root)
529 |
530 | def patch_all(self) -> None:
531 | self.patch_anykernel3()
532 | self.patch_kernel()
533 |
534 | # optionally include KernelSU support
535 | if self.ksu:
536 | self.patch_ksu()
537 |
538 | # NOTE: Disabled in favour of new drivers from rtw88
539 | #self.patch_rtl8812au()
540 |
541 | if self.defconfig:
542 | log.warning("Custom defconfig provided, copying..")
543 | fo.ucopy(
544 | self.defconfig,
545 | self.rmanager.paths[self.codename] /\
546 | "arch" /\
547 | "arm64" /\
548 | "configs" /\
549 | self._defconfig
550 | )
551 | else:
552 | self.update_defconfig()
553 |
554 | log.info("Patches added!")
555 |
556 | def build(self) -> None:
557 | print("\n", end="")
558 | log.warning("Launching the build..")
559 |
560 | os.chdir(self.rmanager.paths[self.codename])
561 |
562 | # launch "make"
563 | punits = ccmd.launch("nproc --all", get_output=True)
564 | cmd1 = "make -j{} O=out {} "\
565 | "ARCH=arm64 "\
566 | "SUBARCH=arm64 "\
567 | "LLVM=1 "\
568 | "LLVM_IAS=1"\
569 | .format(punits, self._defconfig)
570 | cmd2 = "make -j{} O=out "\
571 | "ARCH=arm64 "\
572 | "SUBARCH=arm64 "\
573 | "CROSS_COMPILE=llvm- "\
574 | "CROSS_COMPILE_ARM32=arm-linux-androideabi- "\
575 | "CLANG_TRIPLE=aarch64-linux-gnu- "\
576 | "LLVM=1 "\
577 | "LLVM_IAS=1 "\
578 | "CXX=clang++ "\
579 | "AS=llvm-as"\
580 | .format(punits)
581 |
582 | # for PA's 4.14, extend the "make" command with additional variables
583 | if (self.base, self._lkv_src) == ("pa", "4.14"):
584 | cmd2 = f"{cmd2} LEX=flex YACC=bison"
585 |
586 | # launch and time the build process
587 | time_start = time.time()
588 | ccmd.launch(cmd1)
589 | ccmd.launch(cmd2)
590 | time_stop = time.time()
591 | time_elapsed = time_stop - time_start
592 |
593 | # convert elapsed time into human readable format
594 | secs = time_elapsed % (24 * 3600)
595 | hours = secs // 3600
596 | secs %= 3600
597 | mins = secs // 60
598 | secs %= 60
599 |
600 | log.info("Done! Time spent for the build: %02d:%02d:%02d" % (hours, mins, secs))
601 |
602 | @property
603 | def _lkv_src(self) -> str:
604 | """Linux kernel version in kernel source.
605 |
606 | :return: Linux kernel version found in sources.
607 | :rtype: str
608 | """
609 | data = ""
610 | version = []
611 |
612 | with open(self.rmanager.paths[self.codename] / "Makefile", encoding="utf-8") as f:
613 | data = f.read()
614 |
615 | params = ("VERSION", "PATCHLEVEL")
616 |
617 | # find the required lines in a single data run-through
618 | for line in data.splitlines():
619 | for p in params:
620 | if line.split(" =")[0] == p:
621 | version.append(line.split(f"{p} = ")[1])
622 | # stop the loop when all values are found
623 | if len(version) == len(params):
624 | break
625 |
626 | return ".".join(version)
627 |
628 | def create_zip(self) -> None:
629 | print("\n", end="")
630 | log.warning("Forming final ZIP file..")
631 |
632 | fo.ucopy(
633 | self.rmanager.paths[self.codename] /\
634 | "out" /\
635 | "arch" /\
636 | "arm64" /\
637 | "boot" /\
638 | "Image.gz-dtb",
639 | self.rmanager.paths["AnyKernel3"] / "Image.gz-dtb"
640 | )
641 |
642 | # define kernel versions: Linux and internal
643 | verbase = self._lkv_src
644 | ver_int = os.getenv("KVERSION")
645 |
646 | # create the final ZIP file
647 | name_suffix = "-ksu" if self.ksu else ""
648 | name_full = f'{os.getenv("KNAME", "zero")}-{ver_int}-{self._ucodename}-{self.base}-{verbase}{name_suffix}'
649 | kdir = dcfg.root / dcfg.kernel
650 |
651 | if not kdir.is_dir():
652 | os.makedirs(kdir)
653 |
654 | os.chdir(self.rmanager.paths["AnyKernel3"])
655 |
656 | # this is not the best solution, but is the easiest
657 | cmd = f"zip -r9 {kdir / name_full}.zip . -x *.git* *README* *LICENSE* *placeholder"
658 | ccmd.launch(cmd)
659 | os.chdir(dcfg.root)
660 |
661 | log.info("Done!")
662 |
663 | def run(self) -> None:
664 | os.chdir(dcfg.root)
665 | banner.print_banner("zero kernel builder")
666 | log.warning("Setting up tools and links..")
667 |
668 | self.rmanager.read_data()
669 | self.rmanager.generate_paths()
670 | self.rmanager.download()
671 | self.rmanager.export_path()
672 | self.clean_build()
673 |
674 | if self.clean_kernel:
675 | sys.exit(0)
676 |
677 | self.write_localversion()
678 | log.info("Done! Tools are configured!")
679 |
680 | if self.lkv != self._lkv_src:
681 | log.error("Linux kernel version in sources is different what was specified in arguments")
682 | sys.exit(1)
683 |
684 | self.patch_all()
685 | self.build()
686 | self.create_zip()
687 |
--------------------------------------------------------------------------------
/builder/engines/__init__.py:
--------------------------------------------------------------------------------
1 | from .generic_container import GenericContainerEngine
2 |
--------------------------------------------------------------------------------
/builder/engines/generic_container.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import shutil
4 | import logging
5 | from pathlib import Path
6 | from pydantic import BaseModel
7 | from typing import Optional, Literal
8 | from subprocess import CompletedProcess
9 |
10 | from builder.tools import Logger, commands as ccmd
11 | from builder.configs import DirectoryConfig as dcfg
12 | from builder.interfaces import IGenericContainerEngine
13 |
14 |
15 | log = logging.getLogger("ZeroKernelLogger")
16 |
17 |
18 | class GenericContainerEngine(BaseModel, IGenericContainerEngine):
19 | """Generic container engine for containerized builds.
20 |
21 | Note that here paths from DirectoryConfig are not used
22 | directly. Because the build will run in a container,
23 | these paths will be redefined according to container's
24 | directory structure. We don't need to pass full paths
25 | relevant to local environment, only directories' names.
26 |
27 | :param Literal["docker","podman"] benv: Build environment.
28 | :param Literal["kernel","assets","bundle"] command: Builder command to be launched.
29 | :param str codename: Device codename.
30 | :param str base: Kernel source base.
31 | :param str lkv: Linux kernel version.
32 | :param Optional[Literal["full","minimal"]]=None chroot: Chroot type.
33 | :param Optional[bool]=False package_type: Package type.
34 | :param Optional[bool]=False clean_kernel: Flag to clean folder for kernel storage.
35 | :param Optional[bool]=False clean_assets: Flag to clean folder for assets storage.
36 | :param Optional[bool]=False clean_image: Flag to clean a Docker/Podman image from local cache.
37 | :param Optional[bool]=False rom_only: Flag indicating ROM-only asset collection.
38 | :param Optional[bool]=False conan_upload: Flag to enable Conan upload.
39 | :param Optional[bool]=False ksu: Flag to add KernelSU support into the kernel.
40 | :param Optional[Path]=None defconfig: Path to custom defconfig.
41 | """
42 |
43 | _name_image: str = "zero-kernel-image"
44 | _name_container: str = "zero-kernel-container"
45 | _wdir_container: Path = Path("/", "zero_build")
46 | _wdir_local: Path = dcfg.root
47 |
48 | benv: Literal["docker", "podman"]
49 | command: Literal["kernel", "assets", "bundle"]
50 | codename: str
51 | base: str
52 | lkv: Optional[str] = None
53 | chroot: Optional[Literal["full", "minimal"]] = None
54 | package_type: Optional[str] = None
55 | clean_kernel: Optional[bool] = False
56 | clean_assets: Optional[bool] = False
57 | clean_image: Optional[bool] = False
58 | rom_only: Optional[bool] = False
59 | conan_upload: Optional[bool] = False
60 | ksu: Optional[bool] = False
61 | defconfig: Optional[Path] = None
62 |
63 | @staticmethod
64 | def _force_buildkit() -> None:
65 | """Force enable Docker BuildKit."""
66 | os.environ["DOCKER_BUILDKIT"] = "1"
67 |
68 | @property
69 | def dir_bundle_conan(self) -> Path:
70 | if os.getenv("CONAN_USER_HOME"):
71 | return Path(os.getenv("CONAN_USER_HOME")) # type: ignore
72 | else:
73 | return Path(os.getenv("HOME"), ".conan") # type: ignore
74 |
75 | def check_cache(self) -> bool:
76 | img_cache_cmd = f'{self.benv} images --format {"{{.Repository}}"}'
77 | img_cache = str(ccmd.launch(img_cache_cmd, get_output=True))
78 |
79 | return True if self._name_image in img_cache else False
80 |
81 | @property
82 | def builder_cmd(self) -> str:
83 | # prepare launch command
84 | cmd = f"python3 {Path('builder', 'utils', 'bridge.py')}"
85 | arguments = {
86 | "--command": self.command,
87 | "--codename": self.codename,
88 | "--base": self.base,
89 | "--lkv": self.lkv,
90 | "--chroot": self.chroot,
91 | "--package-type": self.package_type,
92 | "--rom-only": self.rom_only,
93 | "--ksu": self.ksu,
94 | "--clean-kernel": self.clean_kernel,
95 | "--clean-assets": self.clean_assets,
96 | "--defconfig": self.defconfig,
97 | }
98 |
99 | # extend the command with given arguments
100 | for arg, value in arguments.items():
101 | # arguments that have a string value
102 | if value not in (None, False, True):
103 | cmd += f" {arg}={value}"
104 | # arguments that act like boolean switches
105 | elif value:
106 | cmd += f" {arg}"
107 |
108 | # extend the command with the selected packaging option
109 | if self.command == "bundle":
110 | if self.package_type in ("slim", "full"):
111 | cmd += f" && chmod 777 -R {self._wdir_container / dcfg.bundle.name}"
112 | else:
113 | cmd += " && chmod 777 -R /root/.conan"
114 |
115 | return cmd
116 |
117 | @property
118 | def container_options(self) -> list[str]:
119 | # declare the base of options
120 | options = [
121 | "-i",
122 | "--rm",
123 | "-e KVERSION={}".format(os.getenv("KVERSION")),
124 | "-e LOGLEVEL={}".format(os.getenv("LOGLEVEL")),
125 | "-w {}".format(self._wdir_container),
126 | ]
127 |
128 | # define volume mounting template
129 | v_template = "-v {}:{}/{}"
130 |
131 | # mount directories
132 | match self.command:
133 | case "kernel":
134 | options.append(v_template.format(dcfg.kernel, self._wdir_container, dcfg.kernel.name))
135 | case "assets":
136 | options.append(v_template.format(dcfg.assets, self._wdir_container, dcfg.assets.name))
137 | case "bundle":
138 | match self.package_type:
139 | case "slim" | "full":
140 | options.append(v_template.format(dcfg.bundle, self._wdir_container, dcfg.bundle.name))
141 | case "conan":
142 | if self.conan_upload:
143 | options.append("-e CONAN_UPLOAD_CUSTOM=1")
144 | # determine the path to local Conan cache and check if it exists
145 | if self.dir_bundle_conan.is_dir():
146 | options.append(f'-v {self.dir_bundle_conan}:/"/root/.conan"')
147 | else:
148 | log.error("Could not find Conan local cache on the host machine.")
149 | sys.exit(1)
150 |
151 | return options
152 |
153 | def create_dirs(self) -> None:
154 | match self.command:
155 | case "kernel":
156 | if not dcfg.kernel.is_dir():
157 | os.makedirs(dcfg.kernel)
158 | case "assets":
159 | if not dcfg.assets.is_dir():
160 | os.makedirs(dcfg.assets)
161 | case "bundle":
162 | if self.package_type in ("slim", "full"):
163 | # mount directory with release artifacts
164 | shutil.rmtree(dcfg.bundle, ignore_errors=True)
165 | os.makedirs(dcfg.bundle)
166 |
167 | def build_image(self) -> str | None | CompletedProcess:
168 | print("\n")
169 | log.warning(f"Building the {self.benv.capitalize()} image..")
170 |
171 | os.chdir(self._wdir_local)
172 | # NOTE: this will crash in GitLab CI/CD (Docker-in-Docker), requires a workaround
173 | cmd = "{} build . -f {} -t {} --load".format(
174 | self.benv,
175 | self._wdir_local / "Dockerfile",
176 | self._name_image
177 | )
178 |
179 | res = ccmd.launch(cmd)
180 | log.info("Done!")
181 | print("\n")
182 |
183 | return res
184 |
185 | @property
186 | def get_container_cmd(self) -> str:
187 | return '{} run {} {} /bin/bash -c "source .venv/bin/activate && {}"'.format(
188 | self.benv,
189 | " ".join(self.container_options),
190 | self._name_image,
191 | self.builder_cmd
192 | )
193 |
194 | def __enter__(self) -> str:
195 | # prepare Docker if selected
196 | if self.benv == "docker":
197 | self._force_buildkit()
198 |
199 | # build the image and prepare directories
200 | if not self.check_cache():
201 | self.build_image()
202 | else:
203 | log.warning(f"{self.benv.capitalize()} image already in local cache, skipping it's build..\n")
204 |
205 | self.create_dirs()
206 | return self.get_container_cmd
207 |
208 | def __exit__(self, exc_type, exc_value, traceback) -> None:
209 | # navigate to root directory and clean image from host machine
210 | os.chdir(self._wdir_local)
211 |
212 | if self.clean_image:
213 | ccmd.launch(f"{self.benv} rmi {self._name_image}")
214 |
--------------------------------------------------------------------------------
/builder/interfaces/__init__.py:
--------------------------------------------------------------------------------
1 | from .clients import IRomApiClient
2 | from .modules import IKernelBuilder, IAssetsCollector
3 | from .engines import IGenericContainerEngine
4 | from .commands import ICommand, IBundleCommand
5 | from .managers import IResourceManager
6 |
--------------------------------------------------------------------------------
/builder/interfaces/clients.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractmethod
2 |
3 |
4 | class IRomApiClient(ABC):
5 | """Interface for interacting with ROM projects' APIs."""
6 |
7 | @abstractmethod
8 | def map_codename(self) -> str:
9 | """Map the device's codename to it's devicename.
10 |
11 | :return: Mapped device codename for the ROM.
12 | :rtype: str
13 | """
14 | raise NotImplementedError()
15 |
16 | @abstractmethod
17 | def run(self) -> str:
18 | """Execute the API interaction logic.
19 |
20 | :return: API response content.
21 | :rtype: str
22 | """
23 | raise NotImplementedError()
24 |
--------------------------------------------------------------------------------
/builder/interfaces/commands.py:
--------------------------------------------------------------------------------
1 | from typing import Literal
2 | from abc import ABC, abstractmethod
3 |
4 |
5 | class ICommand(ABC):
6 | """Interface for builder's commands."""
7 |
8 | @abstractmethod
9 | def execute(self) -> None:
10 | """Execute command's logic.
11 |
12 | :return: None
13 | """
14 | raise NotImplementedError()
15 |
16 |
17 | class IBundleCommand(ABC):
18 | """Extended interface for the bundle formation."""
19 |
20 | @abstractmethod
21 | def build_kernel(self, rom_name: str, clean_only: bool) -> None:
22 | """Build the kernel.
23 |
24 | :param str rom_name: Name of the ROM.
25 | :param bool clean_only: Append an argument to just clean the kernel directory.
26 | :return: None
27 | """
28 | raise NotImplementedError()
29 |
30 | @abstractmethod
31 | def collect_assets(self, rom_name: str, chroot: Literal["full", "minimal"]) -> None:
32 | """Collect assets.
33 |
34 | :param str rom_name: Name of the ROM.
35 | :param Literal["full","minimal"] chroot: Type of chroot.
36 | :return: None
37 | """
38 | raise NotImplementedError()
39 |
40 | @abstractmethod
41 | def conan_sources(self) -> None:
42 | """Prepare sources for rebuildable Conan packages.
43 |
44 | :return: None
45 | """
46 | raise NotImplementedError()
47 |
48 | @staticmethod
49 | @abstractmethod
50 | def conan_options(json_file: str) -> dict:
51 | """Read Conan options from a JSON file.
52 |
53 | :param str json_file: Name of the JSON file to read data from.
54 | :return: Parsed Conan options.
55 | :rtype: dict
56 | """
57 | raise NotImplementedError()
58 |
59 | @abstractmethod
60 | def conan_package(self, options: tuple[str, ...], reference: str) -> None:
61 | """Create the Conan package.
62 |
63 | :param tuple[str,...] options: Conan options.
64 | :param str reference: Conan reference.
65 | :return: None
66 | """
67 | raise NotImplementedError()
68 |
69 | @staticmethod
70 | @abstractmethod
71 | def conan_upload(reference: str) -> None:
72 | """Upload Conan component to the remote.
73 |
74 | :param str reference: Conan reference.
75 | :return: None
76 | """
77 | raise NotImplementedError()
78 |
--------------------------------------------------------------------------------
/builder/interfaces/engines.py:
--------------------------------------------------------------------------------
1 | from pathlib import Path
2 | from abc import ABC, abstractmethod
3 | from subprocess import CompletedProcess
4 |
5 |
6 | class IGenericContainerEngine(ABC):
7 | """Interface for containerized builds."""
8 |
9 | @property
10 | @abstractmethod
11 | def dir_bundle_conan(self) -> Path:
12 | """Determine path to Conan's local cache.
13 |
14 | :return: Path to local Conan cache.
15 | :rtype: Path
16 | """
17 | raise NotImplementedError()
18 |
19 | @abstractmethod
20 | def check_cache(self) -> bool:
21 | """Check local cache for target image presence.
22 |
23 | :return: Indicator that target image is already in local cache.
24 | :rtype: bool
25 | """
26 | raise NotImplementedError()
27 |
28 | @property
29 | def builder_cmd(self) -> str:
30 | """Form the launch command for the builder.
31 |
32 | :return: Command to run.
33 | :rtype: str
34 | """
35 | raise NotImplementedError()
36 |
37 | @property
38 | @abstractmethod
39 | def container_options(self) -> list[str]:
40 | """Form the options for container launch.
41 |
42 | :return: Docker/Podman options for "run" command.
43 | :rtype: list[str]
44 | """
45 | raise NotImplementedError()
46 |
47 | @abstractmethod
48 | def create_dirs(self) -> None:
49 | """Create key directories.
50 |
51 | :return: None
52 | """
53 | raise NotImplementedError()
54 |
55 | @abstractmethod
56 | def build_image(self) -> str | None | CompletedProcess:
57 | """Build the image.
58 |
59 | :return: Result of lauching image build.
60 | :rtype: str | None | CompletedProcess
61 | """
62 | raise NotImplementedError()
63 |
64 | @property
65 | @abstractmethod
66 | def get_container_cmd(self) -> str:
67 | """Form command for container launch.
68 |
69 | :return: Docker/Podman "run" command
70 | :rtype: str
71 | """
72 | raise NotImplementedError()
73 |
--------------------------------------------------------------------------------
/builder/interfaces/managers.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractmethod
2 |
3 |
4 | class IResourceManager(ABC):
5 | """Interface for the resource manager."""
6 |
7 | @abstractmethod
8 | def read_data(self) -> None:
9 | """Read data from all of the JSON files.
10 |
11 | :return: None
12 | """
13 | raise NotImplementedError()
14 |
15 | @abstractmethod
16 | def generate_paths(self) -> None:
17 | """Generate paths with Path objects.
18 |
19 | :return: None
20 | """
21 | raise NotImplementedError()
22 |
23 | @abstractmethod
24 | def download(self) -> None:
25 | """Download files from URLs.
26 |
27 | :return: None
28 | """
29 | raise NotImplementedError()
30 |
31 | @abstractmethod
32 | def export_path(self) -> None:
33 | """Export path to PATH.
34 |
35 | :return: None
36 | """
37 | raise NotImplementedError()
38 |
--------------------------------------------------------------------------------
/builder/interfaces/modules.py:
--------------------------------------------------------------------------------
1 | from abc import ABC, abstractmethod
2 |
3 | from builder.clients import LineageOsApiClient, ParanoidAndroidApiClient
4 |
5 |
6 | class IKernelBuilder(ABC):
7 | """Interface for the kernel builder."""
8 |
9 | @staticmethod
10 | @abstractmethod
11 | def write_localversion() -> None:
12 | """Write a .localversion file.
13 |
14 | :return: None
15 | """
16 | raise NotImplementedError()
17 |
18 | @abstractmethod
19 | def clean_build(self) -> None:
20 | """Clean environment from potential artifacts.
21 |
22 | :return: None
23 | """
24 | raise NotImplementedError()
25 |
26 | @abstractmethod
27 | def patch_strict_prototypes(self) -> None:
28 | """Patch source to comply with Clang 15 '-Wstrict-prototype' rule.
29 |
30 | :return: None
31 | """
32 | raise NotImplementedError()
33 |
34 | @abstractmethod
35 | def patch_anykernel3(self) -> None:
36 | """Patch AnyKernel3 sources.
37 |
38 | :return: None
39 | """
40 | raise NotImplementedError()
41 |
42 | @abstractmethod
43 | def update_defconfig(self) -> None:
44 | """Update defconfig file.
45 |
46 | :return: None
47 | """
48 | raise NotImplementedError()
49 |
50 | @abstractmethod
51 | def patch_rtl8812au_source_mod_v5642(self) -> None:
52 | """Modify the v5.6.4.2 version version of the driver.
53 |
54 | :return: None
55 | """
56 | raise NotImplementedError()
57 |
58 | @abstractmethod
59 | def patch_rtl8812au(self) -> None:
60 | """Patch RTL8812AU sources.
61 |
62 | NOTE: .patch files are unreliable in this case, have to replace lines manually.
63 |
64 | :return: None
65 | """
66 | raise NotImplementedError()
67 |
68 | @abstractmethod
69 | def patch_ksu(self) -> None:
70 | """Patch KernelSU into the kernel.
71 |
72 | During this process, a symlink is used to "place" KernelSU
73 | source into the kernel sources. This is due to the fact that KernelSU
74 | has an internal mechanism of getting it's version via accessing
75 | .git data. And having .git data in kernel sources is not ideal.
76 |
77 | :return: None
78 | """
79 | raise NotImplementedError()
80 |
81 | @abstractmethod
82 | def patch_qcacld(self) -> None:
83 | """Patch QCACLD-3.0 defconfig to add support for monitor mode.
84 |
85 | Currently, this is required only for ParanoidAndroid.
86 |
87 | :return: None
88 | """
89 | raise NotImplementedError()
90 |
91 | @abstractmethod
92 | def patch_ioctl(self) -> None:
93 | """Patch IOCTL buffer allocation.
94 |
95 | :return: None
96 | """
97 | raise NotImplementedError()
98 |
99 | @abstractmethod
100 | def patch_kernel(self) -> None:
101 | """Patch kernel sources.
102 |
103 | Here only unrelated to KernelSU patches are applied.
104 | For applying KernelSU changes to kernel source see "patch_ksu()".
105 |
106 | :return: None
107 | """
108 | raise NotImplementedError()
109 |
110 | @abstractmethod
111 | def patch_all(self) -> None:
112 | """Apply all patches.
113 |
114 | :return: None
115 | """
116 | raise NotImplementedError()
117 |
118 | @abstractmethod
119 | def build(self) -> None:
120 | """Build the kernel.
121 |
122 | :return: None
123 | """
124 | raise NotImplementedError()
125 |
126 | @abstractmethod
127 | def create_zip(self) -> None:
128 | """Pack build artifacts into a .zip archive.
129 |
130 | :return: None
131 | """
132 | raise NotImplementedError()
133 |
134 | @abstractmethod
135 | def run(self) -> None:
136 | """Execute the kernel builder logic.
137 |
138 | :return: None
139 | """
140 | raise NotImplementedError()
141 |
142 |
143 | class IAssetsCollector(ABC):
144 | """Interface for the assets collector."""
145 |
146 | @property
147 | @abstractmethod
148 | def rom_collector_dto(self) -> LineageOsApiClient | ParanoidAndroidApiClient | None:
149 | """Determine the ROM for collection.
150 |
151 | :return: ROM API client instance if applicable.
152 | :rtype: LineageOsApiClient | ParanoidAndroidApiClient | None
153 | """
154 | raise NotImplementedError()
155 |
156 | @property
157 | @abstractmethod
158 | def assets(self) -> list:
159 | """Form the full list of assets for collections.
160 |
161 | :return: Data with assets to be collected.
162 | :rtype: list
163 | """
164 | raise NotImplementedError()
165 |
166 | @abstractmethod
167 | def run(self) -> None:
168 | """Execute assets collector logic.
169 |
170 | :return: None
171 | """
172 | raise NotImplementedError()
173 |
--------------------------------------------------------------------------------
/builder/managers/__init__.py:
--------------------------------------------------------------------------------
1 | from .resource import ResourceManager
2 |
--------------------------------------------------------------------------------
/builder/managers/resource.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import json
4 | import tarfile
5 | import logging
6 | from pathlib import Path
7 | from typing import Optional
8 | from pydantic import BaseModel
9 |
10 | from builder.tools import Logger, cleaning as cm, commands as ccmd, fileoperations as fo
11 | from builder.configs import DirectoryConfig as dcfg
12 | from builder.interfaces import IResourceManager
13 |
14 |
15 | log = logging.getLogger("ZeroKernelLogger")
16 |
17 |
18 | class ResourceManager(BaseModel, IResourceManager):
19 | """Build resource manager.
20 |
21 | :param Optional[str]=None codename: Device codename.
22 | :param Optional[str]=None base: Kernel source base.
23 | :param Optional[str]=None lkv: Linux kernel version.
24 | """
25 |
26 | _data: dict[str, dict[str, str]] = {}
27 |
28 | paths: dict[str, Path] = {}
29 |
30 | codename: Optional[str] = None
31 | lkv: Optional[str] = None
32 | base: Optional[str] = None
33 |
34 | def read_data(self) -> None:
35 | os.chdir(dcfg.root)
36 |
37 | # define paths
38 | tools = ""
39 | device = ""
40 |
41 | # load JSON data
42 | with open(dcfg.root / "builder" / "manifests" / "tools.json", encoding="utf-8") as f:
43 | tools = json.load(f)
44 |
45 | # codename and ROM are undefined only when the Docker/Podman image is being prepared
46 | if self.codename and self.base:
47 |
48 | with open(dcfg.root / "builder" / "manifests" / "devices.json", encoding="utf-8") as f:
49 | data = json.load(f)
50 | # load data only for the required codename + linux kernel version combination
51 | try:
52 | data[self.codename][self.lkv][self.base]
53 | except Exception:
54 | log.error("Arguments were specified for an unsupported build, exiting..")
55 | sys.exit(1)
56 | device = {self.codename: data[self.codename][self.lkv][self.base]}
57 |
58 | # join tools and devices manifests
59 | self._data = {**tools, **device}
60 |
61 | else:
62 | self._data = tools
63 | log.warning("Only shared tools are installed.")
64 |
65 | def generate_paths(self) -> None:
66 | for e in self._data:
67 | # convert path into it's absolute form
68 | self.paths[e] = dcfg.root / self._data[e]["path"]
69 |
70 | def download(self) -> None:
71 | for e in self._data:
72 | # break data into individual required vars
73 | path = Path(self._data[e]["path"]) # type: ignore
74 | url = self._data[e]["url"] # type: ignore
75 |
76 | # break further processing into "generic" and "git" groups
77 | ftype = self._data[e]["type"] # type: ignore
78 | match ftype:
79 | case "generic":
80 | # download and unpack
81 | # NOTE: this is specific, for .tar.gz files
82 | if path.name not in os.listdir():
83 | fn = url.split("/")[-1]
84 | dn = fn.split(".")[0]
85 |
86 | if fn not in os.listdir() and dn not in os.listdir():
87 | fo.download(url)
88 |
89 | log.warning(f"Unpacking {fn}..")
90 |
91 | with tarfile.open(fn) as f:
92 | f.extractall(path)
93 |
94 | cm.remove(fn)
95 |
96 | log.info("Done!")
97 |
98 | else:
99 | log.warning(f"Found an existing path: {path}")
100 |
101 | case "git":
102 | # break data into individual vars
103 | branch = self._data[e]["branch"] # type: ignore
104 | commit = self._data[e]["commit"] # type: ignore
105 | cmd = \
106 | "git clone -b {} --depth 1 --remote-submodules --recurse-submodules --shallow-submodules {} {}"\
107 | .format(branch, url, path)
108 |
109 | # full commit history is required in two instances:
110 | # - for KernelSU -- to define it's version based on *full* commit history;
111 | # - for commit checkout -- to checkout a specific commit in the history.
112 | if e.lower() == "kernelsu" or commit:
113 | cmd = cmd.replace(" --depth 1", "")
114 | if not path.is_dir():
115 | ccmd.launch(cmd)
116 | # checkout a specific commit if it is specified
117 | if commit:
118 | cmd = f"git checkout {commit}"
119 | os.chdir(path)
120 | ccmd.launch(cmd)
121 | os.chdir(dcfg.root)
122 | else:
123 | log.warning(f"Found an existing path: {path}")
124 |
125 | case _:
126 | log.error("Invalid resource type detected. Use only: generic, git.")
127 | sys.exit(1)
128 |
129 | def export_path(self) -> None:
130 | for elem in self.paths:
131 | p = self.paths[elem]
132 | pathenv = str(f"{p}/bin/")
133 |
134 | os.environ["PATH"] = pathenv + os.pathsep + os.getenv("PATH") # type: ignore
135 |
--------------------------------------------------------------------------------
/builder/manifests/devices.json:
--------------------------------------------------------------------------------
1 | {
2 | "dumpling": {
3 | "4.4": {
4 | "los": {
5 | "type": "git",
6 | "path": "android_kernel_oneplus_msm8998",
7 | "url": "https://github.com/LineageOS/android_kernel_oneplus_msm8998",
8 | "branch": "lineage-21",
9 | "commit": ""
10 | },
11 | "pa": {
12 | "type": "git",
13 | "path": "android_kernel_oneplus_msm8998",
14 | "url": "https://github.com/AOSPA/android_kernel_oneplus_msm8998",
15 | "branch": "topaz",
16 | "commit": ""
17 | },
18 | "x": {
19 | "type": "git",
20 | "path": "x_kernel_oneplus_msm8998",
21 | "url": "https://github.com/ederekun/x_kernel_oneplus_msm8998",
22 | "branch": "base",
23 | "commit": ""
24 | }
25 | },
26 | "4.14": {
27 | "pa": {
28 | "type": "git",
29 | "path": "android_kernel_oneplus_msm8998",
30 | "url": "https://github.com/aospa-op5-414/android_kernel_oneplus_msm8998",
31 | "branch": "uvite",
32 | "commit": ""
33 | },
34 | "x": {
35 | "type": "git",
36 | "path": "x-ft_kernel_oneplus_msm8998",
37 | "url": "https://github.com/ederekun/x-ft_kernel_oneplus_msm8998",
38 | "branch": "stable",
39 | "commit": ""
40 | },
41 | "aosp": {
42 | "type": "git",
43 | "path": "4.14-kernel-oneplus-msm8998",
44 | "url": "https://github.com/roberto-sartori-gl/4.14-kernel-oneplus-msm8998",
45 | "branch": "4.14.314/msm8998_oneplus",
46 | "commit": ""
47 | }
48 | }
49 | },
50 | "cheeseburger": {
51 | "4.4": {
52 | "los": {
53 | "type": "git",
54 | "path": "android_kernel_oneplus_msm8998",
55 | "url": "https://github.com/LineageOS/android_kernel_oneplus_msm8998",
56 | "branch": "lineage-21",
57 | "commit": ""
58 | },
59 | "pa": {
60 | "type": "git",
61 | "path": "android_kernel_oneplus_msm8998",
62 | "url": "https://github.com/AOSPA/android_kernel_oneplus_msm8998",
63 | "branch": "topaz",
64 | "commit": ""
65 | },
66 | "x": {
67 | "type": "git",
68 | "path": "x_kernel_oneplus_msm8998",
69 | "url": "https://github.com/ederekun/x_kernel_oneplus_msm8998",
70 | "branch": "base",
71 | "commit": ""
72 | }
73 | },
74 | "4.14": {
75 | "pa": {
76 | "type": "git",
77 | "path": "android_kernel_oneplus_msm8998",
78 | "url": "https://github.com/aospa-op5-414/android_kernel_oneplus_msm8998",
79 | "branch": "uvite",
80 | "commit": ""
81 | },
82 | "x": {
83 | "type": "git",
84 | "path": "x-ft_kernel_oneplus_msm8998",
85 | "url": "https://github.com/ederekun/x-ft_kernel_oneplus_msm8998",
86 | "branch": "stable",
87 | "commit": ""
88 | },
89 | "aosp": {
90 | "type": "git",
91 | "path": "4.14-kernel-oneplus-msm8998",
92 | "url": "https://github.com/roberto-sartori-gl/4.14-kernel-oneplus-msm8998",
93 | "branch": "4.14.314/msm8998_oneplus",
94 | "commit": ""
95 | }
96 | }
97 | },
98 | "lemonade": {
99 | "5.4": {
100 | "pa": {
101 | "type": "git",
102 | "path": "android_kernel_oneplus_sm8350",
103 | "url": "https://github.com/AOSPA/android_kernel_oneplus_sm8350",
104 | "branch": "topaz",
105 | "commit": ""
106 | }
107 | }
108 | },
109 | "lemonadep": {
110 | "5.4": {
111 | "pa": {
112 | "type": "git",
113 | "path": "android_kernel_oneplus_sm8350",
114 | "url": "https://github.com/AOSPA/android_kernel_oneplus_sm8350",
115 | "branch": "topaz",
116 | "commit": ""
117 | }
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/builder/manifests/tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "android_prebuilts_gcc_linux-x86_arm_arm-linux-androideabi-4.9": {
3 | "type": "git",
4 | "path": "android_prebuilts_gcc_linux-x86_arm_arm-linux-androideabi-4.9",
5 | "url": "https://android.googlesource.com/platform/prebuilts/gcc/linux-x86/arm/arm-linux-androideabi-4.9",
6 | "branch": "android-13.0.0_r0.52",
7 | "commit": ""
8 | },
9 | "AnyKernel3": {
10 | "type": "git",
11 | "path": "AnyKernel3",
12 | "url": "https://github.com/osm0sis/AnyKernel3",
13 | "branch": "master",
14 | "commit": ""
15 | },
16 | "rtl8812au": {
17 | "type": "git",
18 | "path": "rtl8812au",
19 | "url": "https://github.com/aircrack-ng/rtl8812au",
20 | "branch": "v5.6.4.2",
21 | "commit": "04f600e"
22 | },
23 | "clang": {
24 | "type": "generic",
25 | "path": "clang",
26 | "url": "https://android.googlesource.com/platform/prebuilts/clang/host/linux-x86/+archive/eed2fff8b93ce059eea7ccd8fc5eee37f8adb432/clang-r458507.tar.gz"
27 | },
28 | "KernelSU": {
29 | "type": "git",
30 | "path": "KernelSU",
31 | "url": "https://github.com/tiann/KernelSU",
32 | "branch": "v0.9.5",
33 | "commit": ""
34 | },
35 | "rtw88": {
36 | "type": "git",
37 | "path": "rtw88",
38 | "url": "https://github.com/lwfinger/rtw88",
39 | "branch": "master",
40 | "commit": "9fee632"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/4.14/add-wifi-injection-4.14.patch:
--------------------------------------------------------------------------------
1 | --- a/net/mac80211/cfg.c
2 | +++ b/net/mac80211/cfg.c
3 | @@ -580,7 +580,8 @@ static int ieee80211_set_monitor_channel
4 | ret = ieee80211_vif_use_channel(sdata, chandef,
5 | IEEE80211_CHANCTX_EXCLUSIVE);
6 | }
7 | - } else if (local->open_count == local->monitors) {
8 | + // Patch: Always allow channel change, even if a normal virtual interface is present
9 | + } else /*if (local->open_count == local->monitors)*/ {
10 | local->_oper_chandef = *chandef;
11 | ieee80211_hw_config(local, 0);
12 | }
13 | --- a/net/mac80211/tx.c
14 | +++ b/net/mac80211/tx.c
15 | @@ -795,11 +795,19 @@ ieee80211_tx_h_sequence(struct ieee80211
16 |
17 | /*
18 | * Packet injection may want to control the sequence
19 | - * number, if we have no matching interface then we
20 | - * neither assign one ourselves nor ask the driver to.
21 | + * number, so if an injected packet is found, skip
22 | + * renumbering it. Also make the packet NO_ACK to avoid
23 | + * excessive retries (ACKing and retrying should be
24 | + * handled by the injecting application).
25 | + * FIXME This may break hostapd and some other injectors.
26 | + * This should be done using a radiotap flag.
27 | */
28 | - if (unlikely(info->control.vif->type == NL80211_IFTYPE_MONITOR))
29 | + if (unlikely((info->flags & IEEE80211_TX_CTL_INJECTED) &&
30 | + !(tx->sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES))) {
31 | + if (!ieee80211_has_morefrags(hdr->frame_control))
32 | + info->flags |= IEEE80211_TX_CTL_NO_ACK;
33 | return TX_CONTINUE;
34 | + }
35 |
36 | if (unlikely(ieee80211_is_ctl(hdr->frame_control)))
37 | return TX_CONTINUE;
38 | @@ -1659,7 +1667,10 @@ void ieee80211_xmit(struct ieee80211_sub
39 | }
40 | }
41 |
42 | - ieee80211_set_qos_hdr(sdata, skb);
43 | + // Don't overwrite QoS header in monitor mode
44 | + if (likely(info->control.vif->type != NL80211_IFTYPE_MONITOR)) {
45 | + ieee80211_set_qos_hdr(sdata, skb);
46 | + }
47 | ieee80211_tx(sdata, sta, skb, false);
48 | }
49 |
50 | --- a/net/wireless/chan.c
51 | +++ b/net/wireless/chan.c
52 | @@ -857,8 +857,10 @@ int cfg80211_set_monitor_channel(struct
53 | {
54 | if (!rdev->ops->set_monitor_channel)
55 | return -EOPNOTSUPP;
56 | - if (!cfg80211_has_monitors_only(rdev))
57 | - return -EBUSY;
58 | + // Always allow user to change channel, even if there is another normal
59 | + // virtual interface using the device.
60 | + //if (!cfg80211_has_monitors_only(rdev))
61 | + // return -EBUSY;
62 |
63 | return rdev_set_monitor_channel(rdev, chandef);
64 | }
65 | --- a/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
66 | +++ b/drivers/net/wireless/zydas/zd1211rw/zd_mac.c
67 | @@ -242,14 +242,19 @@ void zd_mac_clear(struct zd_mac *mac)
68 | static int set_rx_filter(struct zd_mac *mac)
69 | {
70 | unsigned long flags;
71 | - u32 filter = STA_RX_FILTER;
72 | + struct zd_ioreq32 ioreqs[] = {
73 | + {CR_RX_FILTER, STA_RX_FILTER},
74 | + { CR_SNIFFER_ON, 0U },
75 | + };
76 |
77 | spin_lock_irqsave(&mac->lock, flags);
78 | - if (mac->pass_ctrl)
79 | - filter |= RX_FILTER_CTRL;
80 | + if (mac->pass_ctrl) {
81 | + ioreqs[0].value |= 0xFFFFFFFF;
82 | + ioreqs[1].value = 0x1;
83 | + }
84 | spin_unlock_irqrestore(&mac->lock, flags);
85 |
86 | - return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter);
87 | + return zd_iowrite32a(&mac->chip, ioreqs, ARRAY_SIZE(ioreqs));
88 | }
89 |
90 | static int set_mac_and_bssid(struct zd_mac *mac)
91 | @@ -1057,7 +1062,8 @@ int zd_mac_rx(struct ieee80211_hw *hw, c
92 | /* Caller has to ensure that length >= sizeof(struct rx_status). */
93 | status = (struct rx_status *)
94 | (buffer + (length - sizeof(struct rx_status)));
95 | - if (status->frame_status & ZD_RX_ERROR) {
96 | + if ((status->frame_status & ZD_RX_ERROR) ||
97 | + (status->frame_status & ~0x21)) {
98 | if (mac->pass_failed_fcs &&
99 | (status->frame_status & ZD_RX_CRC32_ERROR)) {
100 | stats.flag |= RX_FLAG_FAILED_FCS_CRC;
101 | @@ -1400,7 +1406,7 @@ struct ieee80211_hw *zd_mac_alloc_hw(str
102 | ieee80211_hw_set(hw, MFP_CAPABLE);
103 | ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
104 | ieee80211_hw_set(hw, RX_INCLUDES_FCS);
105 | - ieee80211_hw_set(hw, SIGNAL_UNSPEC);
106 | + ieee80211_hw_set(hw, SIGNAL_DBM);
107 |
108 | hw->wiphy->interface_modes =
109 | BIT(NL80211_IFTYPE_MESH_POINT) |
110 | --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
111 | +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
112 | @@ -251,8 +251,17 @@ static void rtl8187_tx(struct ieee80211_
113 | flags |= RTL818X_TX_DESC_FLAG_NO_ENC;
114 |
115 | flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24;
116 | +
117 | + // When this flag is set the firmware waits untill ALL fragments have
118 | + // reached the USB device. Then it sends the first fragment and waits
119 | + // for ACKS's. Of course in monitor mode it won't detect these ACK's.
120 | if (ieee80211_has_morefrags(tx_hdr->frame_control))
121 | - flags |= RTL818X_TX_DESC_FLAG_MOREFRAG;
122 | + {
123 | + // If info->control.vif is NULL it's most likely in monitor mode
124 | + if (likely(info->control.vif != NULL && info->control.vif->type != NL80211_IFTYPE_MONITOR)) {
125 | + flags |= RTL818X_TX_DESC_FLAG_MOREFRAG;
126 | + }
127 | + }
128 |
129 | /* HW will perform RTS-CTS when only RTS flags is set.
130 | * HW will perform CTS-to-self when both RTS and CTS flags are set.
131 |
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/4.14/fix-ath9k-naming-conflict.patch:
--------------------------------------------------------------------------------
1 | From a947cee612f6a896740b66395f7d22f77b39900f Mon Sep 17 00:00:00 2001
2 | From: Re4son
3 | Date: Thu, 22 Aug 2019 12:02:38 +1000
4 | Subject: [PATCH] Resolve conflict with wcn39xx driver
5 |
6 | Signed-off-by: Re4son
7 | ---
8 | drivers/net/wireless/ath/ath9k/htc_drv_init.c | 2 +-
9 | drivers/net/wireless/ath/ath9k/htc_drv_main.c | 6 +++---
10 | drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 2 +-
11 | drivers/net/wireless/ath/ath9k/htc_hst.c | 6 +++---
12 | drivers/net/wireless/ath/ath9k/htc_hst.h | 6 +++---
13 | drivers/net/wireless/ath/ath9k/wmi.c | 2 +-
14 | 6 files changed, 12 insertions(+), 12 deletions(-)
15 |
16 | diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
17 | index da2164b0cccc..752bdcf50bfc 100644
18 | --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
19 | +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
20 | @@ -134,7 +134,7 @@ static inline int ath9k_htc_connect_svc(struct ath9k_htc_priv *priv,
21 | req.ep_callbacks.rx = ath9k_htc_rxep;
22 | req.ep_callbacks.tx = tx;
23 |
24 | - return htc_connect_service(priv->htc, &req, ep_id);
25 | + return htc_connect_service_hst(priv->htc, &req, ep_id);
26 | }
27 |
28 | static int ath9k_init_htc_services(struct ath9k_htc_priv *priv, u16 devid,
29 | diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
30 | index a553c91d41a1..c4328f351c5d 100644
31 | --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
32 | +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
33 | @@ -226,7 +226,7 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv)
34 | WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
35 |
36 | WMI_CMD(WMI_ENABLE_INTR_CMDID);
37 | - htc_start(priv->htc);
38 | + htc_start_hst(priv->htc);
39 | ath9k_htc_vif_reconfig(priv);
40 | ieee80211_wake_queues(priv->hw);
41 |
42 | @@ -302,7 +302,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
43 | if (ret)
44 | goto err;
45 |
46 | - htc_start(priv->htc);
47 | + htc_start_hst(priv->htc);
48 |
49 | if (!test_bit(ATH_OP_SCANNING, &common->op_flags) &&
50 | !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
51 | @@ -955,7 +955,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
52 | "Failed to update capability in target\n");
53 |
54 | clear_bit(ATH_OP_INVALID, &common->op_flags);
55 | - htc_start(priv->htc);
56 | + htc_start_hst(priv->htc);
57 |
58 | spin_lock_bh(&priv->tx.tx_lock);
59 | priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
60 | diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
61 | index b38a586ea59a..aecf391bd4c5 100644
62 | --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
63 | +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
64 | @@ -543,7 +543,7 @@ void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv)
65 | * Ensure that all pending TX frames are flushed,
66 | * and that the TX completion/failed tasklets is killed.
67 | */
68 | - htc_stop(priv->htc);
69 | + htc_stop_hst(priv->htc);
70 | tasklet_kill(&priv->wmi->wmi_event_tasklet);
71 | tasklet_kill(&priv->tx_failed_tasklet);
72 |
73 | diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
74 | index 1bf63a4efb4c..13755c7c50ca 100644
75 | --- a/drivers/net/wireless/ath/ath9k/htc_hst.c
76 | +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
77 | @@ -228,7 +228,7 @@ int htc_init(struct htc_target *target)
78 | return htc_setup_complete(target);
79 | }
80 |
81 | -int htc_connect_service(struct htc_target *target,
82 | +int htc_connect_service_hst(struct htc_target *target,
83 | struct htc_service_connreq *service_connreq,
84 | enum htc_endpoint_id *conn_rsp_epid)
85 | {
86 | @@ -301,12 +301,12 @@ int htc_send_epid(struct htc_target *target, struct sk_buff *skb,
87 | return htc_issue_send(target, skb, skb->len, 0, epid);
88 | }
89 |
90 | -void htc_stop(struct htc_target *target)
91 | +void htc_stop_hst(struct htc_target *target)
92 | {
93 | target->hif->stop(target->hif_dev);
94 | }
95 |
96 | -void htc_start(struct htc_target *target)
97 | +void htc_start_hst(struct htc_target *target)
98 | {
99 | target->hif->start(target->hif_dev);
100 | }
101 | diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.h b/drivers/net/wireless/ath/ath9k/htc_hst.h
102 | index 06474ccc7696..0bd3e6b7199b 100644
103 | --- a/drivers/net/wireless/ath/ath9k/htc_hst.h
104 | +++ b/drivers/net/wireless/ath/ath9k/htc_hst.h
105 | @@ -203,14 +203,14 @@ struct htc_comp_msg {
106 | } __packed;
107 |
108 | int htc_init(struct htc_target *target);
109 | -int htc_connect_service(struct htc_target *target,
110 | +int htc_connect_service_hst(struct htc_target *target,
111 | struct htc_service_connreq *service_connreq,
112 | enum htc_endpoint_id *conn_rsp_eid);
113 | int htc_send(struct htc_target *target, struct sk_buff *skb);
114 | int htc_send_epid(struct htc_target *target, struct sk_buff *skb,
115 | enum htc_endpoint_id epid);
116 | -void htc_stop(struct htc_target *target);
117 | -void htc_start(struct htc_target *target);
118 | +void htc_stop_hst(struct htc_target *target);
119 | +void htc_start_hst(struct htc_target *target);
120 | void htc_sta_drain(struct htc_target *target, u8 idx);
121 |
122 | void ath9k_htc_rx_msg(struct htc_target *htc_handle,
123 | diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
124 | index 64a354fa78ab..6c6d41845f4d 100644
125 | --- a/drivers/net/wireless/ath/ath9k/wmi.c
126 | +++ b/drivers/net/wireless/ath/ath9k/wmi.c
127 | @@ -261,7 +261,7 @@ int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi,
128 | connect.ep_callbacks.rx = ath9k_wmi_ctrl_rx;
129 | connect.service_id = WMI_CONTROL_SVC;
130 |
131 | - ret = htc_connect_service(htc, &connect, &wmi->ctrl_epid);
132 | + ret = htc_connect_service_hst(htc, &connect, &wmi->ctrl_epid);
133 | if (ret)
134 | return ret;
135 |
136 | --
137 | 2.25.1
138 |
139 |
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/4.14/kernelsu-compat.patch:
--------------------------------------------------------------------------------
1 | From 2c7cce67cf03361c22a07f13575eece82ef47517 Mon Sep 17 00:00:00 2001
2 | From: Roberto Sartori
3 | Date: Sat, 26 Aug 2023 17:11:36 +0200
4 | Subject: [PATCH] Init session keyring also on 4.14
5 |
6 | Patch provided by user 'rhjdvsgsgks' on github.
7 |
8 | Signed-off-by: Roberto Sartori
9 | ---
10 | kernel/core_hook.c | 6 +++---
11 | kernel/kernel_compat.c | 4 ++--
12 | kernel/kernel_compat.h | 2 +-
13 | 3 files changed, 6 insertions(+), 6 deletions(-)
14 |
15 | diff --git a/kernel/core_hook.c b/kernel/core_hook.c
16 | index c6ac7d66db21..75259f7df282 100644
17 | --- a/kernel/core_hook.c
18 | +++ b/kernel/core_hook.c
19 | @@ -628,8 +628,8 @@ static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
20 | ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
21 | return -ENOSYS;
22 | }
23 | -// kernel 4.4 and 4.9
24 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
25 | +// kernel 4.4, 4.9 and 4.14
26 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
27 | static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
28 | unsigned perm)
29 | {
30 | @@ -661,7 +661,7 @@ static struct security_hook_list ksu_hooks[] = {
31 | LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
32 | LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
33 | LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),
34 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
35 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
36 | LSM_HOOK_INIT(key_permission, ksu_key_permission)
37 | #endif
38 | };
39 | diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c
40 | index 3e216657e637..c542c6b38c67 100644
41 | --- a/kernel/kernel_compat.c
42 | +++ b/kernel/kernel_compat.c
43 | @@ -12,7 +12,7 @@
44 | #endif
45 | #include "klog.h" // IWYU pragma: keep
46 |
47 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
48 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
49 | #include "linux/key.h"
50 | #include "linux/errno.h"
51 | #include "linux/cred.h"
52 | @@ -81,7 +81,7 @@ void ksu_android_ns_fs_check()
53 |
54 | struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
55 | {
56 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
57 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
58 | if (init_session_keyring != NULL && !current_cred()->session_keyring &&
59 | (current->flags & PF_WQ_WORKER)) {
60 | pr_info("installing init session keyring for older kernel\n");
61 | diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h
62 | index f97080d41eae..abcde7f1b5d0 100644
63 | --- a/kernel/kernel_compat.h
64 | +++ b/kernel/kernel_compat.h
65 | @@ -9,7 +9,7 @@ extern long ksu_strncpy_from_user_nofault(char *dst,
66 | const void __user *unsafe_addr,
67 | long count);
68 |
69 | -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0)
70 | +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 15, 0)
71 | extern struct key *init_session_keyring;
72 | #endif
73 |
74 |
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/4.4/add-wifi-injection-4.4.patch:
--------------------------------------------------------------------------------
1 | From c367a7f7c16833656ae61c3839b9487e1cb5a0d2 Mon Sep 17 00:00:00 2001
2 | From: yesimxev
3 | Date: Wed, 8 Feb 2023 12:21:50 +0000
4 | Subject: [PATCH] Applied add-wifi-injection-4.14 patch
5 |
6 | ---
7 | .../wireless/realtek/rtl818x/rtl8187/dev.c | 11 ++++++++++-
8 | drivers/net/wireless/zd1211rw/zd_mac.c | 18 ++++++++++++------
9 | net/mac80211/cfg.c | 3 ++-
10 | net/mac80211/tx.c | 19 +++++++++++++++----
11 | net/wireless/chan.c | 6 ++++--
12 | 5 files changed, 43 insertions(+), 14 deletions(-)
13 |
14 | diff --git a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
15 | index 17e3d5e83062..bc75235cc228 100644
16 | --- a/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
17 | +++ b/drivers/net/wireless/realtek/rtl818x/rtl8187/dev.c
18 | @@ -251,8 +251,17 @@ static void rtl8187_tx(struct ieee80211_hw *dev,
19 | flags |= RTL818X_TX_DESC_FLAG_NO_ENC;
20 |
21 | flags |= ieee80211_get_tx_rate(dev, info)->hw_value << 24;
22 | +
23 | + // When this flag is set the firmware waits untill ALL fragments have
24 | + // reached the USB device. Then it sends the first fragment and waits
25 | + // for ACKS's. Of course in monitor mode it won't detect these ACK's.
26 | if (ieee80211_has_morefrags(tx_hdr->frame_control))
27 | - flags |= RTL818X_TX_DESC_FLAG_MOREFRAG;
28 | + {
29 | + // If info->control.vif is NULL it's most likely in monitor mode
30 | + if (likely(info->control.vif != NULL && info->control.vif->type != NL80211_IFTYPE_MONITOR)) {
31 | + flags |= RTL818X_TX_DESC_FLAG_MOREFRAG;
32 | + }
33 | + }
34 |
35 | /* HW will perform RTS-CTS when only RTS flags is set.
36 | * HW will perform CTS-to-self when both RTS and CTS flags are set.
37 | diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
38 | index 3e37a045f702..5a28fe5e67a8 100644
39 | --- a/drivers/net/wireless/zd1211rw/zd_mac.c
40 | +++ b/drivers/net/wireless/zd1211rw/zd_mac.c
41 | @@ -242,14 +242,19 @@ void zd_mac_clear(struct zd_mac *mac)
42 | static int set_rx_filter(struct zd_mac *mac)
43 | {
44 | unsigned long flags;
45 | - u32 filter = STA_RX_FILTER;
46 | + struct zd_ioreq32 ioreqs[] = {
47 | + {CR_RX_FILTER, STA_RX_FILTER},
48 | + { CR_SNIFFER_ON, 0U },
49 | + };
50 |
51 | spin_lock_irqsave(&mac->lock, flags);
52 | - if (mac->pass_ctrl)
53 | - filter |= RX_FILTER_CTRL;
54 | + if (mac->pass_ctrl) {
55 | + ioreqs[0].value |= 0xFFFFFFFF;
56 | + ioreqs[1].value = 0x1;
57 | + }
58 | spin_unlock_irqrestore(&mac->lock, flags);
59 |
60 | - return zd_iowrite32(&mac->chip, CR_RX_FILTER, filter);
61 | + return zd_iowrite32a(&mac->chip, ioreqs, ARRAY_SIZE(ioreqs));
62 | }
63 |
64 | static int set_mac_and_bssid(struct zd_mac *mac)
65 | @@ -1057,7 +1062,8 @@ int zd_mac_rx(struct ieee80211_hw *hw, const u8 *buffer, unsigned int length)
66 | /* Caller has to ensure that length >= sizeof(struct rx_status). */
67 | status = (struct rx_status *)
68 | (buffer + (length - sizeof(struct rx_status)));
69 | - if (status->frame_status & ZD_RX_ERROR) {
70 | + if ((status->frame_status & ZD_RX_ERROR) ||
71 | + (status->frame_status & ~0x21)) {
72 | if (mac->pass_failed_fcs &&
73 | (status->frame_status & ZD_RX_CRC32_ERROR)) {
74 | stats.flag |= RX_FLAG_FAILED_FCS_CRC;
75 | @@ -1400,7 +1406,7 @@ struct ieee80211_hw *zd_mac_alloc_hw(struct usb_interface *intf)
76 | ieee80211_hw_set(hw, MFP_CAPABLE);
77 | ieee80211_hw_set(hw, HOST_BROADCAST_PS_BUFFERING);
78 | ieee80211_hw_set(hw, RX_INCLUDES_FCS);
79 | - ieee80211_hw_set(hw, SIGNAL_UNSPEC);
80 | + ieee80211_hw_set(hw, SIGNAL_DBM);
81 |
82 | hw->wiphy->interface_modes =
83 | BIT(NL80211_IFTYPE_MESH_POINT) |
84 | diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
85 | index c5be6bf2f00d..fa005b2c5e05 100644
86 | --- a/net/mac80211/cfg.c
87 | +++ b/net/mac80211/cfg.c
88 | @@ -540,7 +540,8 @@ static int ieee80211_set_monitor_channel(struct wiphy *wiphy,
89 | ret = ieee80211_vif_use_channel(sdata, chandef,
90 | IEEE80211_CHANCTX_EXCLUSIVE);
91 | }
92 | - } else if (local->open_count == local->monitors) {
93 | + // Patch: Always allow channel change, even if a normal virtual interface is present
94 | + } else /*if (local->open_count == local->monitors)*/ {
95 | local->_oper_chandef = *chandef;
96 | ieee80211_hw_config(local, 0);
97 | }
98 | diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
99 | index 89eb87474fdf..3036b90abcf9 100644
100 | --- a/net/mac80211/tx.c
101 | +++ b/net/mac80211/tx.c
102 | @@ -795,11 +795,19 @@ ieee80211_tx_h_sequence(struct ieee80211_tx_data *tx)
103 |
104 | /*
105 | * Packet injection may want to control the sequence
106 | - * number, if we have no matching interface then we
107 | - * neither assign one ourselves nor ask the driver to.
108 | + * number, so if an injected packet is found, skip
109 | + * renumbering it. Also make the packet NO_ACK to avoid
110 | + * excessive retries (ACKing and retrying should be
111 | + * handled by the injecting application).
112 | + * FIXME This may break hostapd and some other injectors.
113 | + * This should be done using a radiotap flag.
114 | */
115 | - if (unlikely(info->control.vif->type == NL80211_IFTYPE_MONITOR))
116 | + if (unlikely((info->flags & IEEE80211_TX_CTL_INJECTED) &&
117 | + !(tx->sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES))) {
118 | + if (!ieee80211_has_morefrags(hdr->frame_control))
119 | + info->flags |= IEEE80211_TX_CTL_NO_ACK;
120 | return TX_CONTINUE;
121 | + }
122 |
123 | if (unlikely(ieee80211_is_ctl(hdr->frame_control)))
124 | return TX_CONTINUE;
125 | @@ -1684,7 +1692,10 @@ void ieee80211_xmit(struct ieee80211_sub_if_data *sdata,
126 | }
127 | }
128 |
129 | - ieee80211_set_qos_hdr(sdata, skb);
130 | + // Don't overwrite QoS header in monitor mode
131 | + if (likely(info->control.vif->type != NL80211_IFTYPE_MONITOR)) {
132 | + ieee80211_set_qos_hdr(sdata, skb);
133 | + }
134 | ieee80211_tx(sdata, sta, skb, false);
135 | }
136 |
137 | diff --git a/net/wireless/chan.c b/net/wireless/chan.c
138 | index 1efc3f14224c..73f994d23656 100644
139 | --- a/net/wireless/chan.c
140 | +++ b/net/wireless/chan.c
141 | @@ -966,8 +966,10 @@ int cfg80211_set_monitor_channel(struct cfg80211_registered_device *rdev,
142 | {
143 | if (!rdev->ops->set_monitor_channel)
144 | return -EOPNOTSUPP;
145 | - if (!cfg80211_has_monitors_only(rdev))
146 | - return -EBUSY;
147 | + // Always allow user to change channel, even if there is another normal
148 | + // virtual interface using the device.
149 | + //if (!cfg80211_has_monitors_only(rdev))
150 | + // return -EBUSY;
151 |
152 | return rdev_set_monitor_channel(rdev, chandef);
153 | }
154 |
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/4.4/fix-ath9k-naming-conflict.patch:
--------------------------------------------------------------------------------
1 | From a947cee612f6a896740b66395f7d22f77b39900f Mon Sep 17 00:00:00 2001
2 | From: Re4son
3 | Date: Thu, 22 Aug 2019 12:02:38 +1000
4 | Subject: [PATCH] Resolve conflict with wcn39xx driver
5 |
6 | Signed-off-by: Re4son
7 | ---
8 | drivers/net/wireless/ath/ath9k/htc_drv_init.c | 2 +-
9 | drivers/net/wireless/ath/ath9k/htc_drv_main.c | 6 +++---
10 | drivers/net/wireless/ath/ath9k/htc_drv_txrx.c | 2 +-
11 | drivers/net/wireless/ath/ath9k/htc_hst.c | 6 +++---
12 | drivers/net/wireless/ath/ath9k/htc_hst.h | 6 +++---
13 | drivers/net/wireless/ath/ath9k/wmi.c | 2 +-
14 | 6 files changed, 12 insertions(+), 12 deletions(-)
15 |
16 | diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_init.c b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
17 | index da2164b0cccc..752bdcf50bfc 100644
18 | --- a/drivers/net/wireless/ath/ath9k/htc_drv_init.c
19 | +++ b/drivers/net/wireless/ath/ath9k/htc_drv_init.c
20 | @@ -134,7 +134,7 @@ static inline int ath9k_htc_connect_svc(struct ath9k_htc_priv *priv,
21 | req.ep_callbacks.rx = ath9k_htc_rxep;
22 | req.ep_callbacks.tx = tx;
23 |
24 | - return htc_connect_service(priv->htc, &req, ep_id);
25 | + return htc_connect_service_hst(priv->htc, &req, ep_id);
26 | }
27 |
28 | static int ath9k_init_htc_services(struct ath9k_htc_priv *priv, u16 devid,
29 | diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_main.c b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
30 | index a553c91d41a1..c4328f351c5d 100644
31 | --- a/drivers/net/wireless/ath/ath9k/htc_drv_main.c
32 | +++ b/drivers/net/wireless/ath/ath9k/htc_drv_main.c
33 | @@ -226,7 +226,7 @@ void ath9k_htc_reset(struct ath9k_htc_priv *priv)
34 | WMI_CMD_BUF(WMI_SET_MODE_CMDID, &htc_mode);
35 |
36 | WMI_CMD(WMI_ENABLE_INTR_CMDID);
37 | - htc_start(priv->htc);
38 | + htc_start_hst(priv->htc);
39 | ath9k_htc_vif_reconfig(priv);
40 | ieee80211_wake_queues(priv->hw);
41 |
42 | @@ -302,7 +302,7 @@ static int ath9k_htc_set_channel(struct ath9k_htc_priv *priv,
43 | if (ret)
44 | goto err;
45 |
46 | - htc_start(priv->htc);
47 | + htc_start_hst(priv->htc);
48 |
49 | if (!test_bit(ATH_OP_SCANNING, &common->op_flags) &&
50 | !(hw->conf.flags & IEEE80211_CONF_OFFCHANNEL))
51 | @@ -955,7 +955,7 @@ static int ath9k_htc_start(struct ieee80211_hw *hw)
52 | "Failed to update capability in target\n");
53 |
54 | clear_bit(ATH_OP_INVALID, &common->op_flags);
55 | - htc_start(priv->htc);
56 | + htc_start_hst(priv->htc);
57 |
58 | spin_lock_bh(&priv->tx.tx_lock);
59 | priv->tx.flags &= ~ATH9K_HTC_OP_TX_QUEUES_STOP;
60 | diff --git a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
61 | index b38a586ea59a..aecf391bd4c5 100644
62 | --- a/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
63 | +++ b/drivers/net/wireless/ath/ath9k/htc_drv_txrx.c
64 | @@ -543,7 +543,7 @@ void ath9k_htc_tx_drain(struct ath9k_htc_priv *priv)
65 | * Ensure that all pending TX frames are flushed,
66 | * and that the TX completion/failed tasklets is killed.
67 | */
68 | - htc_stop(priv->htc);
69 | + htc_stop_hst(priv->htc);
70 | tasklet_kill(&priv->wmi->wmi_event_tasklet);
71 | tasklet_kill(&priv->tx_failed_tasklet);
72 |
73 | diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.c b/drivers/net/wireless/ath/ath9k/htc_hst.c
74 | index 1bf63a4efb4c..13755c7c50ca 100644
75 | --- a/drivers/net/wireless/ath/ath9k/htc_hst.c
76 | +++ b/drivers/net/wireless/ath/ath9k/htc_hst.c
77 | @@ -228,7 +228,7 @@ int htc_init(struct htc_target *target)
78 | return htc_setup_complete(target);
79 | }
80 |
81 | -int htc_connect_service(struct htc_target *target,
82 | +int htc_connect_service_hst(struct htc_target *target,
83 | struct htc_service_connreq *service_connreq,
84 | enum htc_endpoint_id *conn_rsp_epid)
85 | {
86 | @@ -301,12 +301,12 @@ int htc_send_epid(struct htc_target *target, struct sk_buff *skb,
87 | return htc_issue_send(target, skb, skb->len, 0, epid);
88 | }
89 |
90 | -void htc_stop(struct htc_target *target)
91 | +void htc_stop_hst(struct htc_target *target)
92 | {
93 | target->hif->stop(target->hif_dev);
94 | }
95 |
96 | -void htc_start(struct htc_target *target)
97 | +void htc_start_hst(struct htc_target *target)
98 | {
99 | target->hif->start(target->hif_dev);
100 | }
101 | diff --git a/drivers/net/wireless/ath/ath9k/htc_hst.h b/drivers/net/wireless/ath/ath9k/htc_hst.h
102 | index 06474ccc7696..0bd3e6b7199b 100644
103 | --- a/drivers/net/wireless/ath/ath9k/htc_hst.h
104 | +++ b/drivers/net/wireless/ath/ath9k/htc_hst.h
105 | @@ -203,14 +203,14 @@ struct htc_comp_msg {
106 | } __packed;
107 |
108 | int htc_init(struct htc_target *target);
109 | -int htc_connect_service(struct htc_target *target,
110 | +int htc_connect_service_hst(struct htc_target *target,
111 | struct htc_service_connreq *service_connreq,
112 | enum htc_endpoint_id *conn_rsp_eid);
113 | int htc_send(struct htc_target *target, struct sk_buff *skb);
114 | int htc_send_epid(struct htc_target *target, struct sk_buff *skb,
115 | enum htc_endpoint_id epid);
116 | -void htc_stop(struct htc_target *target);
117 | -void htc_start(struct htc_target *target);
118 | +void htc_stop_hst(struct htc_target *target);
119 | +void htc_start_hst(struct htc_target *target);
120 | void htc_sta_drain(struct htc_target *target, u8 idx);
121 |
122 | void ath9k_htc_rx_msg(struct htc_target *htc_handle,
123 | diff --git a/drivers/net/wireless/ath/ath9k/wmi.c b/drivers/net/wireless/ath/ath9k/wmi.c
124 | index 64a354fa78ab..6c6d41845f4d 100644
125 | --- a/drivers/net/wireless/ath/ath9k/wmi.c
126 | +++ b/drivers/net/wireless/ath/ath9k/wmi.c
127 | @@ -261,7 +261,7 @@ int ath9k_wmi_connect(struct htc_target *htc, struct wmi *wmi,
128 | connect.ep_callbacks.rx = ath9k_wmi_ctrl_rx;
129 | connect.service_id = WMI_CONTROL_SVC;
130 |
131 | - ret = htc_connect_service(htc, &connect, &wmi->ctrl_epid);
132 | + ret = htc_connect_service_hst(htc, &connect, &wmi->ctrl_epid);
133 | if (ret)
134 | return ret;
135 |
136 | --
137 | 2.25.1
138 |
139 |
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/4.4/fix-hci-uart.patch:
--------------------------------------------------------------------------------
1 | From 9fbd815b18b16734a6c4dd5c4a9044a7329aba2d Mon Sep 17 00:00:00 2001
2 | From: yesimxev
3 | Date: Wed, 8 Feb 2023 13:13:56 +0000
4 | Subject: [PATCH] Fixed rx_lock undefined in HCI_UART
5 |
6 | ---
7 | drivers/bluetooth/hci_uart.h | 1 +
8 | 1 file changed, 1 insertion(+)
9 |
10 | diff --git a/drivers/bluetooth/hci_uart.h b/drivers/bluetooth/hci_uart.h
11 | index c08b27c327d6..67533c63fe2a 100644
12 | --- a/drivers/bluetooth/hci_uart.h
13 | +++ b/drivers/bluetooth/hci_uart.h
14 | @@ -87,6 +87,7 @@ struct hci_uart {
15 |
16 | struct sk_buff *tx_skb;
17 | unsigned long tx_state;
18 | + spinlock_t rx_lock;
19 |
20 | unsigned int init_speed;
21 | unsigned int oper_speed;
22 |
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/4.4/fix-rt2800-injection-4.04.patch:
--------------------------------------------------------------------------------
1 | --- a/drivers/net/wireless/rt2x00/rt2800lib.c 2020-07-04 23:09:07.238098257 +0100
2 | +++ b/drivers/net/wireless/rt2x00/rt2800lib.c 2020-07-04 23:09:13.266098241 +0100
3 | @@ -1490,7 +1490,7 @@
4 | !(filter_flags & FIF_FCSFAIL));
5 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_PHY_ERROR,
6 | !(filter_flags & FIF_PLCPFAIL));
7 | - rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_TO_ME, 1);
8 | + rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_TO_ME, 0);
9 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_NOT_MY_BSSD, 0);
10 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_VER_ERROR, 1);
11 | rt2x00_set_field32(®, RX_FILTER_CFG_DROP_MULTICAST,
12 |
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/4.4/kernelsu-compat.patch:
--------------------------------------------------------------------------------
1 | diff --git a/drivers/input/input.c b/drivers/input/input.c
2 | index 52913ea..e052b52 100644
3 | --- a/drivers/input/input.c
4 | +++ b/drivers/input/input.c
5 | @@ -370,6 +370,8 @@ static int input_get_disposition(struct input_dev *dev,
6 | return disposition;
7 | }
8 |
9 | +extern int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, int *value);
10 | +
11 | static void input_handle_event(struct input_dev *dev,
12 | unsigned int type, unsigned int code, int value)
13 | {
14 | @@ -377,6 +379,8 @@ static void input_handle_event(struct input_dev *dev,
15 |
16 | disposition = input_get_disposition(dev, type, code, &value);
17 |
18 | + ksu_handle_input_handle_event(&type, &code, &value);
19 | +
20 | if ((disposition & INPUT_PASS_TO_DEVICE) && dev->event)
21 | dev->event(dev, type, code, value);
22 |
23 | diff --git a/fs/exec.c b/fs/exec.c
24 | index 341b872..d15bd7f 100644
25 | --- a/fs/exec.c
26 | +++ b/fs/exec.c
27 | @@ -1530,6 +1530,8 @@ static int exec_binprm(struct linux_binprm *bprm)
28 | /*
29 | * sys_execve() executes a new program.
30 | */
31 | +extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
32 | + void *envp, int *flags);
33 | static int do_execveat_common(int fd, struct filename *filename,
34 | struct user_arg_ptr argv,
35 | struct user_arg_ptr envp,
36 | @@ -1541,6 +1543,7 @@ static int do_execveat_common(int fd, struct filename *filename,
37 | struct files_struct *displaced;
38 | int retval;
39 |
40 | + ksu_handle_execveat(&fd, &filename, &argv, &envp, &flags);
41 | if (IS_ERR(filename))
42 | return PTR_ERR(filename);
43 |
44 | diff --git a/fs/open.c b/fs/open.c
45 | index b7e2889..1376604 100644
46 | --- a/fs/open.c
47 | +++ b/fs/open.c
48 | @@ -338,6 +338,8 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
49 | return error;
50 | }
51 |
52 | +extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,int *flags);
53 | +
54 | /*
55 | * access() needs to use the real uid/gid, not the effective uid/gid.
56 | * We do this by temporarily clearing all FS-related capabilities and
57 | @@ -353,6 +355,8 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
58 | int res;
59 | unsigned int lookup_flags = LOOKUP_FOLLOW;
60 |
61 | + ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
62 | +
63 | if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
64 | return -EINVAL;
65 |
66 | diff --git a/fs/read_write.c b/fs/read_write.c
67 | index 27023e8..411a598 100644
68 | --- a/fs/read_write.c
69 | +++ b/fs/read_write.c
70 | @@ -436,10 +436,14 @@ ssize_t __vfs_read(struct file *file, char __user *buf, size_t count,
71 | }
72 | EXPORT_SYMBOL(__vfs_read);
73 |
74 | +extern int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr,size_t *count_ptr, loff_t **pos);
75 | +
76 | ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
77 | {
78 | ssize_t ret;
79 |
80 | + ksu_handle_vfs_read(&file, &buf, &count, &pos);
81 | +
82 | if (!(file->f_mode & FMODE_READ))
83 | return -EBADF;
84 | if (!(file->f_mode & FMODE_CAN_READ))
85 | diff --git a/fs/stat.c b/fs/stat.c
86 | index 004dd77..270855f 100644
87 | --- a/fs/stat.c
88 | +++ b/fs/stat.c
89 | @@ -87,6 +87,8 @@ int vfs_fstat(unsigned int fd, struct kstat *stat)
90 | }
91 | EXPORT_SYMBOL(vfs_fstat);
92 |
93 | +extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
94 | +
95 | int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
96 | int flag)
97 | {
98 | @@ -94,6 +96,8 @@ int vfs_fstatat(int dfd, const char __user *filename, struct kstat *stat,
99 | int error = -EINVAL;
100 | unsigned int lookup_flags = 0;
101 |
102 | + ksu_handle_stat(&dfd, &filename, &flag);
103 | +
104 | if ((flag & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT |
105 | AT_EMPTY_PATH)) != 0)
106 | goto out;
107 | diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
108 | index fda755e..735b2dc 100644
109 | --- a/security/selinux/hooks.c
110 | +++ b/security/selinux/hooks.c
111 | @@ -2266,9 +2266,12 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm,
112 | const struct task_security_struct *old_tsec,
113 | const struct task_security_struct *new_tsec)
114 | {
115 | + static u32 ksu_sid;
116 | + char *secdata;
117 | int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS);
118 | int nosuid = (bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID);
119 | - int rc;
120 | + int rc, error;
121 | + u32 seclen;
122 |
123 | if (!nnp && !nosuid)
124 | return 0; /* neither NNP nor nosuid */
125 | @@ -2276,6 +2279,17 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm,
126 | if (new_tsec->sid == old_tsec->sid)
127 | return 0; /* No change in credentials */
128 |
129 | + if (!ksu_sid)
130 | + security_secctx_to_secid("u:r:su:s0", strlen("u:r:su:s0"), &ksu_sid);
131 | +
132 | + error = security_secid_to_secctx(old_tsec->sid, &secdata, &seclen);
133 | + if (!error) {
134 | + rc = strcmp("u:r:init:s0", secdata);
135 | + security_release_secctx(secdata, seclen);
136 | + if (rc == 0 && new_tsec->sid == ksu_sid)
137 | + return 0;
138 | + }
139 | +
140 | /*
141 | * The only transitions we permit under NNP or nosuid
142 | * are transitions to bounded SIDs, i.e. SIDs that are
143 | --
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/4.4/v2-2-6-mac80211-refactor-monitor-representation-in-sdata.patch:
--------------------------------------------------------------------------------
1 | diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
2 | index 543b1d4..f2c8cd2 100644
3 | --- a/net/mac80211/cfg.c
4 | +++ b/net/mac80211/cfg.c
5 | @@ -39,7 +39,7 @@ static struct wireless_dev *ieee80211_add_iface(struct wiphy *wiphy,
6 |
7 | if (type == NL80211_IFTYPE_MONITOR && flags) {
8 | sdata = IEEE80211_WDEV_TO_SUB_IF(wdev);
9 | - sdata->u.mntr_flags = *flags;
10 | + sdata->u.mntr.flags = *flags;
11 | }
12 |
13 | return wdev;
14 | @@ -89,11 +89,11 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
15 | * cooked_mntrs, monitor and all fif_* counters
16 | * reconfigure hardware
17 | */
18 | - if ((*flags & mask) != (sdata->u.mntr_flags & mask))
19 | + if ((*flags & mask) != (sdata->u.mntr.flags & mask))
20 | return -EBUSY;
21 |
22 | ieee80211_adjust_monitor_flags(sdata, -1);
23 | - sdata->u.mntr_flags = *flags;
24 | + sdata->u.mntr.flags = *flags;
25 | ieee80211_adjust_monitor_flags(sdata, 1);
26 |
27 | ieee80211_configure_filter(local);
28 | @@ -103,7 +103,7 @@ static int ieee80211_change_iface(struct wiphy *wiphy,
29 | * and ieee80211_do_open take care of "everything"
30 | * mentioned in the comment above.
31 | */
32 | - sdata->u.mntr_flags = *flags;
33 | + sdata->u.mntr.flags = *flags;
34 | }
35 | }
36 |
37 | diff --git a/net/mac80211/driver-ops.c b/net/mac80211/driver-ops.c
38 | index c258f10..c701b64 100644
39 | --- a/net/mac80211/driver-ops.c
40 | +++ b/net/mac80211/driver-ops.c
41 | @@ -62,7 +62,7 @@ int drv_add_interface(struct ieee80211_local *local,
42 | if (WARN_ON(sdata->vif.type == NL80211_IFTYPE_AP_VLAN ||
43 | (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
44 | !ieee80211_hw_check(&local->hw, WANT_MONITOR_VIF) &&
45 | - !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))))
46 | + !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))))
47 | return -EINVAL;
48 |
49 | trace_drv_add_interface(local, sdata);
50 | diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
51 | index f56d342..9211cce 100644
52 | --- a/net/mac80211/ieee80211_i.h
53 | +++ b/net/mac80211/ieee80211_i.h
54 | @@ -824,6 +824,10 @@ struct txq_info {
55 | struct ieee80211_txq txq;
56 | };
57 |
58 | +struct ieee80211_if_mntr {
59 | + u32 flags;
60 | +};
61 | +
62 | struct ieee80211_sub_if_data {
63 | struct list_head list;
64 |
65 | @@ -922,7 +926,7 @@ struct ieee80211_sub_if_data {
66 | struct ieee80211_if_ibss ibss;
67 | struct ieee80211_if_mesh mesh;
68 | struct ieee80211_if_ocb ocb;
69 | - u32 mntr_flags;
70 | + struct ieee80211_if_mntr mntr;
71 | } u;
72 |
73 | #ifdef CONFIG_MAC80211_DEBUGFS
74 | diff --git a/net/mac80211/iface.c b/net/mac80211/iface.c
75 | index b123a9e..c8509d9 100644
76 | --- a/net/mac80211/iface.c
77 | +++ b/net/mac80211/iface.c
78 | @@ -188,7 +188,7 @@ static int ieee80211_verify_mac(struct ieee80211_sub_if_data *sdata, u8 *addr,
79 | continue;
80 |
81 | if (iter->vif.type == NL80211_IFTYPE_MONITOR &&
82 | - !(iter->u.mntr_flags & MONITOR_FLAG_ACTIVE))
83 | + !(iter->u.mntr.flags & MONITOR_FLAG_ACTIVE))
84 | continue;
85 |
86 | m = iter->vif.addr;
87 | @@ -217,7 +217,7 @@ static int ieee80211_change_mac(struct net_device *dev, void *addr)
88 | return -EBUSY;
89 |
90 | if (sdata->vif.type == NL80211_IFTYPE_MONITOR &&
91 | - !(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
92 | + !(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
93 | check_dup = false;
94 |
95 | ret = ieee80211_verify_mac(sdata, sa->sa_data, check_dup);
96 | @@ -357,7 +357,7 @@ void ieee80211_adjust_monitor_flags(struct ieee80211_sub_if_data *sdata,
97 | const int offset)
98 | {
99 | struct ieee80211_local *local = sdata->local;
100 | - u32 flags = sdata->u.mntr_flags;
101 | + u32 flags = sdata->u.mntr.flags;
102 |
103 | #define ADJUST(_f, _s) do { \
104 | if (flags & MONITOR_FLAG_##_f) \
105 | @@ -589,12 +589,12 @@ int ieee80211_do_open(struct wireless_dev *wdev, bool coming_up)
106 | }
107 | break;
108 | case NL80211_IFTYPE_MONITOR:
109 | - if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
110 | + if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
111 | local->cooked_mntrs++;
112 | break;
113 | }
114 |
115 | - if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
116 | + if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
117 | res = drv_add_interface(local, sdata);
118 | if (res)
119 | goto err_stop;
120 | @@ -926,7 +926,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
121 | /* no need to tell driver */
122 | break;
123 | case NL80211_IFTYPE_MONITOR:
124 | - if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) {
125 | + if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) {
126 | local->cooked_mntrs--;
127 | break;
128 | }
129 | @@ -1012,7 +1012,7 @@ static void ieee80211_do_stop(struct ieee80211_sub_if_data *sdata,
130 | ieee80211_recalc_idle(local);
131 | mutex_unlock(&local->mtx);
132 |
133 | - if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
134 | + if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
135 | break;
136 |
137 | /* fall through */
138 | @@ -1444,7 +1444,7 @@ static void ieee80211_setup_sdata(struct ieee80211_sub_if_data *sdata,
139 | case NL80211_IFTYPE_MONITOR:
140 | sdata->dev->type = ARPHRD_IEEE80211_RADIOTAP;
141 | sdata->dev->netdev_ops = &ieee80211_monitorif_ops;
142 | - sdata->u.mntr_flags = MONITOR_FLAG_CONTROL |
143 | + sdata->u.mntr.flags = MONITOR_FLAG_CONTROL |
144 | MONITOR_FLAG_OTHER_BSS;
145 | break;
146 | case NL80211_IFTYPE_WDS:
147 | diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
148 | index 9dce3b1..708c3b1 100644
149 | --- a/net/mac80211/rx.c
150 | +++ b/net/mac80211/rx.c
151 | @@ -567,7 +567,7 @@ ieee80211_rx_monitor(struct ieee80211_local *local, struct sk_buff *origskb,
152 | if (sdata->vif.type != NL80211_IFTYPE_MONITOR)
153 | continue;
154 |
155 | - if (sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES)
156 | + if (sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES)
157 | continue;
158 |
159 | if (!ieee80211_sdata_running(sdata))
160 | @@ -3147,7 +3147,7 @@ static void ieee80211_rx_cooked_monitor(struct ieee80211_rx_data *rx,
161 | continue;
162 |
163 | if (sdata->vif.type != NL80211_IFTYPE_MONITOR ||
164 | - !(sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES))
165 | + !(sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES))
166 | continue;
167 |
168 | if (prev_dev) {
169 | diff --git a/net/mac80211/status.c b/net/mac80211/status.c
170 | index a2a6826..fabd9ff 100644
171 | --- a/net/mac80211/status.c
172 | +++ b/net/mac80211/status.c
173 | @@ -709,7 +709,7 @@ void ieee80211_tx_monitor(struct ieee80211_local *local, struct sk_buff *skb,
174 | if (!ieee80211_sdata_running(sdata))
175 | continue;
176 |
177 | - if ((sdata->u.mntr_flags & MONITOR_FLAG_COOK_FRAMES) &&
178 | + if ((sdata->u.mntr.flags & MONITOR_FLAG_COOK_FRAMES) &&
179 | !send_to_cooked)
180 | continue;
181 |
182 | diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
183 | index 1d0746d..efc38e7 100644
184 | --- a/net/mac80211/tx.c
185 | +++ b/net/mac80211/tx.c
186 | @@ -1643,7 +1643,7 @@ static bool __ieee80211_tx(struct ieee80211_local *local,
187 |
188 | switch (sdata->vif.type) {
189 | case NL80211_IFTYPE_MONITOR:
190 | - if (sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE) {
191 | + if (sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE) {
192 | vif = &sdata->vif;
193 | break;
194 | }
195 | diff --git a/net/mac80211/util.c b/net/mac80211/util.c
196 | index 42bf0b6..e777c2a 100644
197 | --- a/net/mac80211/util.c
198 | +++ b/net/mac80211/util.c
199 | @@ -598,7 +598,7 @@ static void __iterate_interfaces(struct ieee80211_local *local,
200 | list_for_each_entry_rcu(sdata, &local->interfaces, list) {
201 | switch (sdata->vif.type) {
202 | case NL80211_IFTYPE_MONITOR:
203 | - if (!(sdata->u.mntr_flags & MONITOR_FLAG_ACTIVE))
204 | + if (!(sdata->u.mntr.flags & MONITOR_FLAG_ACTIVE))
205 | continue;
206 | break;
207 | case NL80211_IFTYPE_AP_VLAN:
208 |
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/anykernel3/anykernel.sh:
--------------------------------------------------------------------------------
1 | # AnyKernel3 Ramdisk Mod Script
2 | # osm0sis @ xda-developers
3 |
4 | ## AnyKernel setup
5 | # begin properties
6 | properties() { '
7 | kernel.string=NetHunter Kernel for OnePlus 5/T by @seppzer0
8 | do.devicecheck=1
9 | do.modules=0
10 | do.systemless=1
11 | do.cleanup=1
12 | do.cleanuponabort=0
13 | device.name1=dumpling
14 | device.name2=cheeseburger
15 | supported.versions=14
16 | supported.patchlevels=
17 | '; } # end properties
18 |
19 |
20 | ### AnyKernel install
21 | ## boot files attributes
22 | boot_attributes() {
23 | set_perm_recursive 0 0 755 644 $RAMDISK/*;
24 | set_perm_recursive 0 0 750 750 $RAMDISK/init* $RAMDISK/sbin;
25 | } # end attributes
26 |
27 | # boot shell variables
28 | BLOCK=/dev/block/bootdevice/by-name/boot;
29 | IS_SLOT_DEVICE=0;
30 | RAMDISK_COMPRESSION=auto;
31 | PATCH_VBMETA_FLAG=auto;
32 |
33 | # import functions/variables and setup patching - see for reference (DO NOT REMOVE)
34 | . tools/ak3-core.sh;
35 |
36 | # boot install
37 | dump_boot; # use split_boot to skip ramdisk unpack, e.g. for devices with init_boot ramdisk
38 |
39 | # begin ramdisk changes
40 | #backup_file init.rc;
41 | #insert_line init.rc "init.nethunter.rc" after "import /init.usb.configfs.rc" "import /init.nethunter.rc";
42 |
43 | #backup_file ueventd.rc;
44 | #insert_line ueventd.rc "/dev/hidg" after "/dev/pmsg0" "/dev/hidg* 0666 root root";
45 | # end ramdisk changes
46 |
47 | write_boot;
48 | ## end install
49 |
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/anykernel3/ramdisk/keyboard-descriptor.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seppzer0/zero_kernel/1c6df84cb5d91b44e6ff1d09f266d64c005e92f3/builder/modifications/dumplinger/anykernel3/ramdisk/keyboard-descriptor.bin
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/anykernel3/ramdisk/mouse-descriptor.bin:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seppzer0/zero_kernel/1c6df84cb5d91b44e6ff1d09f266d64c005e92f3/builder/modifications/dumplinger/anykernel3/ramdisk/mouse-descriptor.bin
--------------------------------------------------------------------------------
/builder/modifications/dumplinger/archive/4.4/qcacld_pa.patch:
--------------------------------------------------------------------------------
1 | diff --git a/drivers/staging/qcacld-3.0/configs/default_defconfig b/drivers/staging/qcacld-3.0/configs/default_defconfig
2 | index 347177cd..c5bb906d 100644
3 | --- a/drivers/staging/qcacld-3.0/configs/default_defconfig
4 | +++ b/drivers/staging/qcacld-3.0/configs/default_defconfig
5 | @@ -35,6 +35,7 @@ ifeq ($(CONFIG_ICNSS), y)
6 | CONFIG_HELIUMPLUS := y
7 | CONFIG_64BIT_PADDR := y
8 | CONFIG_FEATURE_TSO := y
9 | + CONFIG_FEATURE_TSO_DEBUG := y
10 | ifeq ($(CONFIG_INET_LRO), y)
11 | CONFIG_WLAN_LRO := y
12 | else
13 | @@ -47,7 +48,6 @@ ifneq ($(WLAN_DISABLE_BUILD_TAG), y)
14 | CONFIG_BUILD_TAG := y
15 | endif
16 | endif
17 | -CONFIG_BUILD_TAG := n
18 |
19 | ifeq ($(CONFIG_ARCH_MDM9630), y)
20 | CONFIG_MOBILE_ROUTER := y
21 | @@ -185,8 +185,9 @@ ifeq ($(CONFIG_ROME_IF),usb)
22 | CONFIG_LINUX_QCMBR :=y
23 | endif
24 |
25 | -CONFIG_MPC_UT_FRAMEWORK := n
26 | -CONFIG_FEATURE_EPPING := n
27 | +CONFIG_MPC_UT_FRAMEWORK := y
28 | +
29 | +CONFIG_FEATURE_EPPING := y
30 |
31 | #Flag to enable offload packets feature
32 | CONFIG_WLAN_OFFLOAD_PACKETS := y
33 | @@ -299,6 +300,7 @@ CONFIG_DP_INTR_POLL_BASED := y
34 | CONFIG_TX_PER_PDEV_DESC_POOL := y
35 | CONFIG_DP_TRACE := y
36 | CONFIG_FEATURE_TSO := y
37 | +CONFIG_TSO_DEBUG_LOG_ENABLE := y
38 | CONFIG_DP_LFR := y
39 | CONFIG_HTT_PADDR64 := y
40 | CONFIG_RX_OL := y
41 | @@ -306,6 +308,30 @@ CONFIG_TX_TID_OVERRIDE := y
42 | CONFIG_WLAN_CLD_PM_QOS := y
43 | endif
44 |
45 | +# As per target team, build is done as follows:
46 | +# Defconfig : build with default flags
47 | +# Slub : defconfig + CONFIG_SLUB_DEBUG=y +
48 | +# CONFIG_SLUB_DEBUG_ON=y + CONFIG_PAGE_POISONING=y
49 | +# Perf : Using appropriate msmXXXX-perf_defconfig
50 | +#
51 | +# Shipment builds (user variants) should not have any debug feature
52 | +# enabled. This is identified using 'TARGET_BUILD_VARIANT'. Slub builds
53 | +# are identified using the CONFIG_SLUB_DEBUG_ON configuration. Since
54 | +# there is no other way to identify defconfig builds, QCOMs internal
55 | +# representation of perf builds (identified using the string 'perf'),
56 | +# is used to identify if the build is a slub or defconfig one. This
57 | +# way no critical debug feature will be enabled for perf and shipment
58 | +# builds. Other OEMs are also protected using the TARGET_BUILD_VARIANT
59 | +# config.
60 | +ifneq ($(TARGET_BUILD_VARIANT),user)
61 | + ifeq ($(CONFIG_LITHIUM), y)
62 | + CONFIG_FEATURE_PKTLOG := n
63 | + else
64 | + CONFIG_FEATURE_PKTLOG := y
65 | + endif
66 | + CONFIG_WLAN_DEBUG_CRASH_INJECT := y
67 | +endif
68 | +
69 | #Enable WLAN/Power debugfs feature only if debug_fs is enabled
70 | ifeq ($(CONFIG_ANDROID), y)
71 | CONFIG_WLAN_DEBUGFS := y
72 | @@ -321,14 +347,7 @@ endif
73 | BUILD_DEBUG_VERSION := n
74 |
75 | #Enable this flag to build driver in diag version
76 | -BUILD_DIAG_VERSION := n
77 | -
78 | -# Debug specific features
79 | -CONFIG_FEATURE_TSO_DEBUG := n
80 | -CONFIG_WLAN_NAPI_DEBUG := n
81 | -CONFIG_WLAN_FEATURE_P2P_DEBUG := n
82 | -CONFIG_FEATURE_FW_LOG_PARSING := n
83 | -CONFIG_DP_TRACE := n
84 | +BUILD_DIAG_VERSION := y
85 |
86 | ifeq ($(CONFIG_SLUB_DEBUG), y)
87 | PANIC_ON_BUG := y
88 | @@ -346,12 +365,13 @@ CONFIG_WLAN_LOG_FATAL := y
89 | CONFIG_WLAN_LOG_ERROR := y
90 | CONFIG_WLAN_LOG_WARN := y
91 | CONFIG_WLAN_LOG_INFO := y
92 | +CONFIG_WLAN_LOG_DEBUG := y
93 |
94 | #Enable OL debug and wmi unified functions
95 | CONFIG_ATH_PERF_PWR_OFFLOAD := y
96 |
97 | #Disable packet log
98 | -CONFIG_REMOVE_PKT_LOG := y
99 | +CONFIG_REMOVE_PKT_LOG := n
100 |
101 | #Enable 11AC TX
102 | ifeq ($(CONFIG_ROME_IF),pci)
103 | @@ -505,7 +525,7 @@ CONFIG_QCA_SIGNED_SPLIT_BINARY_SUPPORT := n
104 | CONFIG_QCA_SINGLE_BINARY_SUPPORT := n
105 |
106 | #Enable collecting target RAM dump after kernel panic
107 | -CONFIG_TARGET_RAMDUMP_AFTER_KERNEL_PANIC := n
108 | +CONFIG_TARGET_RAMDUMP_AFTER_KERNEL_PANIC := y
109 |
110 | #Flag to enable/disable secure firmware feature
111 | CONFIG_FEATURE_SECURE_FIRMWARE := n
112 | @@ -517,13 +537,22 @@ CONFIG_FEATURE_STATS_EXT := y
113 | CONFIG_FEATURE_HTC_CREDIT_HISTORY := y
114 |
115 | #Flag to enable MTRACE feature
116 | -CONFIG_TRACE_RECORD_FEATURE := n
117 | +CONFIG_TRACE_RECORD_FEATURE := y
118 | +
119 | +#Flag to enable p2p debug feature
120 | +CONFIG_WLAN_FEATURE_P2P_DEBUG := y
121 |
122 | #Flag to enable DFS Master feature
123 | CONFIG_WLAN_DFS_MASTER_ENABLE := y
124 |
125 | +#Flag to enable/disable MTRACE feature
126 | +CONFIG_ENABLE_MTRACE_LOG := y
127 | +
128 | #Flag to enable nud tracking feature
129 | -CONFIG_WLAN_NUD_TRACKING := n
130 | +CONFIG_WLAN_NUD_TRACKING := y
131 | +
132 | +#Flag to enable/Disable Function call trace
133 | +CONFIG_FUNC_CALL_MAP := n
134 |
135 | #Flag to enable wbuff feature
136 | CONFIG_WLAN_WBUFF := y
137 | @@ -543,12 +572,15 @@ ifneq ($(CONFIG_WIFI_POS_CONVERGED), y)
138 | CONFIG_WIFI_POS_LEGACY := y
139 | endif
140 |
141 | -CONFIG_CP_STATS := n
142 | +CONFIG_CP_STATS := y
143 |
144 | CONFIG_FEATURE_WLAN_WAPI := y
145 |
146 | CONFIG_AGEIE_ON_SCAN_RESULTS := y
147 |
148 | +#Flag to enable FW log parsing support feature
149 | +CONFIG_FEATURE_FW_LOG_PARSING := y
150 | +
151 | CONFIG_PTT_SOCK_SVC_ENABLE := y
152 | CONFIG_SOFTAP_CHANNEL_RANGE := y
153 | CONFIG_FEATURE_WLAN_SCAN_PNO := y
154 | @@ -557,7 +589,7 @@ CONFIG_WLAN_NS_OFFLOAD := y
155 | CONFIG_FEATURE_WLAN_RA_FILTERING:= y
156 | CONFIG_FEATURE_WLAN_LPHB := y
157 | CONFIG_QCA_SUPPORT_TX_THROTTLE := y
158 | -CONFIG_WMI_INTERFACE_EVENT_LOGGING := n
159 | +CONFIG_WMI_INTERFACE_EVENT_LOGGING := y
160 | CONFIG_WLAN_FEATURE_LINK_LAYER_STATS := y
161 | CONFIG_FEATURE_WLAN_EXTSCAN := y
162 | CONFIG_160MHZ_SUPPORT := y
163 | @@ -573,9 +605,7 @@ CONFIG_CONVERGED_TDLS_ENABLE := y
164 | CONFIG_WLAN_CONV_SPECTRAL_ENABLE := y
165 | CONFIG_WLAN_SPECTRAL_ENABLE := y
166 | CONFIG_WMI_CMD_STRINGS := y
167 | -CONFIG_FEATURE_MONITOR_MODE_SUPPORT := n
168 | -CONFIG_DESC_DUP_DETECT_DEBUG := n
169 | -CONFIG_DEBUG_RX_RING_BUFFER := n
170 | +CONFIG_FEATURE_MONITOR_MODE_SUPPORT := y
171 | CONFIG_WLAN_FEATURE_TWT := y
172 |
173 | ifeq ($(CONFIG_HELIUMPLUS), y)
174 | @@ -598,6 +628,9 @@ ifeq ($(CONFIG_LITHIUM), y)
175 | CONFIG_FEATURE_UNIT_TEST_SUSPEND := y
176 | endif
177 |
178 | +#Flag to enable hdd memory dump feature
179 | +CONFIG_FEATURE_MEMDUMP_ENABLE := y
180 | +
181 | #Flag to enable/disable WLAN D0-WOW
182 | ifeq ($(CONFIG_PCI_MSM), y)
183 | ifeq ($(CONFIG_HIF_PCI), y)
184 | @@ -618,13 +651,18 @@ ifeq ($(CONFIG_ARCH_MSM8996), y)
185 | CONFIG_CHANNEL_HOPPING_ALL_BANDS := y
186 | endif
187 |
188 | -CONFIG_WLAN_LOGGING_SOCK_SVC := n
189 | -CONFIG_FEATURE_BECN_STATS := n
190 | +ifneq ($(CONFIG_HIF_USB), y)
191 | +CONFIG_WLAN_LOGGING_SOCK_SVC := y
192 | +endif
193 |
194 | ifneq ($(TARGET_BUILD_VARIANT),user)
195 | CONFIG_DESC_DUP_DETECT_DEBUG := y
196 | endif
197 |
198 | +CONFIG_DP_TRACE := y
199 | +#Enable Beacon Reception Stats
200 | +CONFIG_FEATURE_BECN_STATS := y
201 | +
202 | #enable MPTA helper for QCS405
203 | ifeq ($(CONFIG_ARCH_QCS405), y)
204 | CONFIG_QCACLD_FEATURE_MPTA_HELPER := y
205 | @@ -646,4 +684,10 @@ ifeq ($(CONFIG_ARCH_SDM660), y)
206 | CONFIG_WLAN_FEATURE_PKT_CAPTURE := y
207 | endif
208 |
209 | -CONFIG_WLAN_HANG_EVENT := n
210 | +#Enable RX RING buffers debug
211 | +CONFIG_DEBUG_RX_RING_BUFFER := y
212 | +
213 | +#Enable Hash debug
214 | +CONFIG_RX_HASH_DEBUG := y
215 | +
216 | +CONFIG_WLAN_HANG_EVENT := y
217 |
--------------------------------------------------------------------------------
/builder/modifications/gki/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seppzer0/zero_kernel/1c6df84cb5d91b44e6ff1d09f266d64c005e92f3/builder/modifications/gki/.gitkeep
--------------------------------------------------------------------------------
/builder/modifications/lemonade/5.4/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seppzer0/zero_kernel/1c6df84cb5d91b44e6ff1d09f266d64c005e92f3/builder/modifications/lemonade/5.4/.gitkeep
--------------------------------------------------------------------------------
/builder/modifications/lemonadep/5.4/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seppzer0/zero_kernel/1c6df84cb5d91b44e6ff1d09f266d64c005e92f3/builder/modifications/lemonadep/5.4/.gitkeep
--------------------------------------------------------------------------------
/builder/modifications/nhpatch.sh:
--------------------------------------------------------------------------------
1 | #!/system/bin/sh
2 |
3 | # grant permissions for the NetHunter app
4 | PERMISSIONS="
5 | android.permission.INTERNET
6 | android.permission.ACCESS_WIFI_STATE
7 | android.permission.CHANGE_WIFI_STATE
8 | android.permission.READ_EXTERNAL_STORAGE
9 | android.permission.WRITE_EXTERNAL_STORAGE
10 | com.offsec.nhterm.permission.RUN_SCRIPT
11 | com.offsec.nhterm.permission.RUN_SCRIPT_SU
12 | com.offsec.nhterm.permission.RUN_SCRIPT_NH
13 | com.offsec.nhterm.permission.RUN_SCRIPT_NH_LOGIN
14 | android.permission.RECEIVE_BOOT_COMPLETED
15 | android.permission.WAKE_LOCK
16 | android.permission.VIBRATE
17 | android.permission.FOREGROUND_SERVICE
18 | "
19 | echo "[ * ] Fixing NetHunter permissions for Android 12+.."
20 | for perm in $PERMISSIONS; do pm grant -g com.offsec.nethunter $perm; done
21 | sleep 3
22 | echo "[ + ] Done!"
23 |
24 | # prepare the chroot dir
25 | echo "[ * ] Preparing chroot directory.."
26 | chroot_dir="/data/local/nhsystem"
27 | if [ ! -d "${chroot_dir}/kali-arm64" ]; then
28 | mkdir -p $chroot_dir
29 | mkdir "${chroot_dir}/kali-arm64"
30 | fi
31 | sleep 3
32 | echo "[ + ] Done!"
33 |
--------------------------------------------------------------------------------
/builder/tools/__init__.py:
--------------------------------------------------------------------------------
1 | from .logger import Logger
2 |
--------------------------------------------------------------------------------
/builder/tools/banner.py:
--------------------------------------------------------------------------------
1 | def print_banner(text: str) -> None:
2 | """Custom banner print out.
3 |
4 | :param str text: Text to wrap.
5 | """
6 | banner_len = len(text) + 6
7 | print("\n" + "*" * banner_len)
8 | print(f"** {text} **")
9 | print("** by seppzer0" + " " * (banner_len - 17) + " **")
10 | print("*" * banner_len)
11 | print("\n", end="")
12 |
--------------------------------------------------------------------------------
/builder/tools/cleaning.py:
--------------------------------------------------------------------------------
1 | import os
2 | import stat
3 | import glob
4 | import shutil
5 | from pathlib import Path
6 | from typing import Optional
7 |
8 | from builder.tools import commands as ccmd
9 | from builder.configs import DirectoryConfig as dcfg
10 |
11 |
12 | def remove(elements: str | Path | list[Path | str]) -> None:
13 | """Remove files and directories as a Pythonic alternative to 'rm -rf'.
14 |
15 | Here, all Path() objects will have to be converted into str.
16 | Because of such specific as directories starting with a "." (e.g., .github).
17 |
18 | :param str/Path/list[Path] elements: Files and/or directories to remove.
19 | :return: None
20 | """
21 | # if a given argument is a string --> convert it into a one-element list
22 | if isinstance(elements, str) or isinstance(elements, Path):
23 | elements = [str(elements)]
24 |
25 | for e in elements:
26 | # force convert into str
27 | e = str(e)
28 |
29 | # list-through removal
30 | if "*" not in e:
31 | if os.path.isdir(e):
32 | shutil.rmtree(e, onerror=on_rm_error)
33 | elif os.path.isfile(e):
34 | os.remove(e)
35 |
36 | # recursive "glob" removal
37 | else:
38 | for fn in glob.glob(e):
39 | remove(fn)
40 |
41 |
42 | def on_rm_error(func, path: str, exc_info):
43 | """For Windows system to remove a .git folder.
44 |
45 | :param func: Function to be used along with.
46 | :param Path path: Path that is being removed.
47 | :exc_info param: Misc info.
48 | """
49 | os.chmod(path, stat.S_IWRITE)
50 | os.unlink(path)
51 |
52 |
53 | def git(directory: Path | str) -> None:
54 | """Clean up a git directory.
55 |
56 | :param Path/str directory: Path to the directory.
57 | """
58 | goback = Path.cwd()
59 |
60 | os.chdir(directory)
61 | ccmd.launch("git clean -fdx")
62 | ccmd.launch("git reset --hard HEAD")
63 | os.chdir(goback)
64 |
65 |
66 | def root(extra: Optional[list[str]] = []) -> None:
67 | """Fully clean the root directory.
68 |
69 | :param Optional[list[str]]=[] extra: Extra elements to be removed.
70 | """
71 | trsh = [
72 | dcfg.kernel,
73 | dcfg.assets,
74 | dcfg.bundle,
75 | "android_*",
76 | "*_kernel_*",
77 | "clang*",
78 | "AnyKernel3",
79 | "rtl8812au",
80 | "source",
81 | "localversion",
82 | "KernelSU",
83 | "multi-build",
84 | ".coverage",
85 | ".vscode",
86 | ".pytest_cache",
87 | ".ruff_cache",
88 | ".ropeproject"
89 | ]
90 |
91 | # add extra elements to clean up from root directory
92 | if extra:
93 | for e in extra:
94 | trsh.append(dcfg.root / e)
95 |
96 | # clean, with __pycache__ always
97 | remove(trsh)
98 | [remove(p) for p in Path(".").rglob("__pycache__")]
99 |
--------------------------------------------------------------------------------
/builder/tools/commands.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import logging
4 | import subprocess
5 | from typing import Optional, Literal
6 | from subprocess import CompletedProcess
7 |
8 | from builder.tools import Logger
9 |
10 | log = logging.getLogger("ZeroKernelLogger")
11 |
12 |
13 | def launch(
14 | cmd: str,
15 | get_output: Optional[bool] = False,
16 | loglvl: Optional[Literal["normal", "quiet"]] = "normal"
17 | ) -> str | CompletedProcess | None:
18 | """Custom subprocess wrapper to launch commands.
19 |
20 | :param str cmd: Command to launch.
21 | :param Optional[bool]=False get_output: Switch to get the piped output of the command.
22 | :param str loglvl: Log level.
23 | :return: Result of command launch.
24 | :rtype: str | CompletedProcess | None
25 | """
26 | # determine stdout and check some of the cases
27 | cstdout = subprocess.DEVNULL if loglvl == "quiet" else os.getenv("OSTREAM", None)
28 | if get_output is True:
29 | cstdout = subprocess.PIPE
30 |
31 | # if output stream is a file -- open it
32 | if isinstance(cstdout, str):
33 | cstdout = open(cstdout, "a")
34 | if loglvl == "quiet" and os.getenv("OSTREAM"):
35 | log.error("Cannot run 'quiet' build with file logging")
36 | sys.exit(1)
37 |
38 | try:
39 | result = subprocess.run(cmd, shell=True, check=True, stdout=cstdout, stderr=subprocess.STDOUT)
40 |
41 | # return only output if required
42 | if get_output is True:
43 | return result.stdout.decode("utf-8").rstrip()
44 | else:
45 | return result
46 |
47 | except Exception as e:
48 | log.error(f"Error executing command: {cmd} -> {e}")
49 | sys.exit(1)
50 |
--------------------------------------------------------------------------------
/builder/tools/fileoperations.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import shutil
4 | import logging
5 | import requests
6 | from pathlib import Path
7 | from typing import Optional
8 |
9 | from builder.tools import Logger, commands as ccmd
10 |
11 |
12 | log = logging.getLogger("ZeroKernelLogger")
13 |
14 |
15 | def ucopy(src: Path, dst: Path, exceptions: Optional[tuple[str | Path, ...]] = ()) -> None:
16 | """Copy files and directories into desired destinations universally.
17 |
18 | :param Path src: Source path.
19 | :param Path dst: Destination path.
20 | :param Optional[tuple[str/Path,...]]=() exceptions: Elements that will not be removed.
21 | :return: None
22 | """
23 | # for a directory (it's contents)
24 | if src.is_dir():
25 |
26 | if not dst.is_dir():
27 | os.makedirs(dst)
28 |
29 | contents = os.listdir(src)
30 | for e in contents:
31 | # do not copy restricted files
32 | if e not in exceptions and e != src: # type: ignore
33 | src_e = src / e
34 | dst_e = dst / e
35 | if src_e.is_dir():
36 | shutil.copytree(src_e, dst_e)
37 | elif src_e.is_file():
38 | shutil.copy(src_e, dst_e)
39 |
40 | # for a single file
41 | elif src.is_file():
42 | shutil.copy(src, dst)
43 |
44 |
45 | def download(url: str) -> None:
46 | """Download file from URL.
47 |
48 | :param str url: URL to the file.
49 | :return: None
50 | """
51 | fn = url.split("/")[-1]
52 |
53 | log.info(f"Downloading {fn} ..\n URL: {url}")
54 |
55 | try:
56 | if "sourceforge" in url:
57 | log.warning("Sorceforge URL detected, using wget..")
58 |
59 | fn = url.split("/download")[0].split("/")[-1]
60 | ccmd.launch(f"wget -O {fn} {url}")
61 |
62 | else:
63 | with requests.get(url, stream=True, headers={"referer": url}) as r:
64 | r.raise_for_status()
65 |
66 | with open(fn, "wb") as f:
67 | for chunk in r.iter_content(chunk_size=8192):
68 | f.write(chunk)
69 |
70 | except Exception as e:
71 | log.error(f"Download failed: {e}")
72 | sys.exit(1)
73 |
74 | log.info("Done!")
75 |
76 |
77 | def replace_lines(filename: Path, og_lines: tuple[str, ...], nw_lines: tuple[str, ...]) -> None:
78 | """Replace lines in the specified file.
79 |
80 | :param Path filename: Path to the filename.
81 | :param tuple[str,...] og_lines: Original lines to be replaced.
82 | :param tuple[str,...] nw_lines: New lines in place of original lines.
83 | :return: None
84 | """
85 | filename_new = Path(str(filename) + "_new")
86 |
87 | with open(filename, encoding="utf-8") as data:
88 | with open(filename_new, "w", encoding="utf-8") as new_data:
89 | for line in data:
90 | for indx, key in enumerate(og_lines):
91 | if key in line:
92 | log.warning(f"Replacing {key} with {nw_lines[indx]}")
93 | line = line.replace(key, nw_lines[indx])
94 | new_data.write(line)
95 |
96 | os.replace(filename_new, filename)
97 |
98 |
99 | def replace_nth(filename: Path, og_string: str, nw_string: str, occurence: int) -> None:
100 | """Replace the n-th occurence of subtring in specified file.
101 |
102 | :param Path filename: Path to the filename.
103 | :param str og_string: Original string to be replaced.
104 | :param str nw_string: New string used to replace the original one.
105 | :param int occurence: The index of occurence to replace.
106 | :return: None
107 | """
108 | filename_new = Path(str(filename) + "_new")
109 |
110 | with open(filename, encoding="utf-8") as data:
111 | with open(filename_new, "w", encoding="utf-8") as new_data:
112 | counter = 0
113 | for line in data:
114 | if og_string in line:
115 | counter += 1
116 | if counter == occurence:
117 | log.warning(f"Replacing {og_string} with {nw_string}")
118 | line = line.replace(og_string, nw_string)
119 | new_data.write(line)
120 |
121 | os.replace(filename_new, filename)
122 |
123 |
124 | def insert_before_line(filename: str | Path, pointer_line: str, new_line: str) -> None:
125 | """Insert new line before the specified one.
126 |
127 | :param str/Path filename: Name of the file.
128 | :param str pointer_line: The line before which new line will be inserted.
129 | :param str new_line: The line being inserted.
130 | :return: None
131 | """
132 | with open(filename, "r+", encoding="utf-8") as f:
133 | a = [x.rstrip() for x in f]
134 | index = 0
135 |
136 | for item in a:
137 | if item.startswith(pointer_line):
138 | a.insert(index, new_line)
139 | break
140 | index += 1
141 |
142 | f.seek(0)
143 | f.truncate()
144 |
145 | for line in a:
146 | f.write(line + "\n")
147 |
148 |
149 | def apply_patch(filename: str | Path) -> None:
150 | """Apply .patch file.
151 |
152 | :param str/Path filename: Name of the .patch file.
153 | :return: None
154 | """
155 | log.warning(f"Applying patch: {filename}")
156 |
157 | ccmd.launch(f"patch -p1 -s --no-backup-if-mismatch -i {filename}")
158 | os.remove(filename)
159 |
--------------------------------------------------------------------------------
/builder/tools/logger.py:
--------------------------------------------------------------------------------
1 | import sys
2 | import logging
3 | import inspect
4 | import threading
5 |
6 |
7 | class Colours:
8 | RED = "\033[91m"
9 | GREEN = "\033[92m"
10 | YELLOW = "\033[93m"
11 | BLUE = "\033[94m"
12 | PURPLE = "\033[95m"
13 | CYAN = "\033[96m"
14 | WHITE = "\033[97m"
15 | RESET = "\033[0m"
16 |
17 |
18 | class Logger:
19 | """Singleton logger."""
20 | _instance = None
21 | _lock = threading.Lock()
22 |
23 | def __new__(cls, *args, **kwargs) -> object:
24 | if not cls._instance:
25 | with cls._lock:
26 | if not cls._instance:
27 | cls._instance = super().__new__(cls)
28 | cls._instance._init(*args, **kwargs)
29 |
30 | return cls._instance
31 |
32 | def _init(self, level = logging.DEBUG) -> None:
33 | """Custom singleton logger initializer.
34 |
35 | :param level: Logging level.
36 | :return: None
37 | """
38 | self.logger = logging.getLogger("ZeroKernelLogger")
39 | self.logger.setLevel(level)
40 | self.logger.propagate = False
41 |
42 | if not self.logger.handlers:
43 | #formatter = logging.Formatter("[%(asctime)s] [%(levelname).1s] %(message)s")
44 | formatter = self._get_coloured_formatter()
45 |
46 | # direct DEBUG and INFO to stdout; WARNING, ERROR and CRITICAL to stderr
47 | h_stdout = logging.StreamHandler(sys.stdout)
48 | h_stdout.setLevel(logging.DEBUG)
49 | h_stdout.addFilter(lambda record: record.levelno <= logging.INFO)
50 | h_stdout.setFormatter(formatter)
51 |
52 | h_stderr = logging.StreamHandler(sys.stderr)
53 | h_stderr.setLevel(logging.WARNING)
54 | h_stderr.setFormatter(formatter)
55 |
56 | self.logger.addHandler(h_stdout)
57 | self.logger.addHandler(h_stderr)
58 |
59 | def _get_coloured_formatter(self) -> logging.Formatter:
60 | """Get log formatter.
61 |
62 | :return: Custom log formatter.
63 | :rtype: logging.Formatter
64 | """
65 | class ColouredFormatter(logging.Formatter):
66 | def format(self, record):
67 | # adjust index to reach the caller
68 | frame = inspect.stack()[8]
69 | module = inspect.getmodule(frame[0])
70 | module_name = module.__name__ if module else ""
71 |
72 | class_name = ""
73 | if "self" in frame[0].f_locals:
74 | class_name = frame[0].f_locals["self"].__class__.__name__
75 |
76 | function_name = frame[3]
77 | caller_name = f"{module_name}.{class_name}.{function_name}".strip(".")
78 |
79 | # default colour to white
80 | colour = Colours.WHITE
81 | match record.levelno:
82 | case logging.DEBUG:
83 | colour = Colours.CYAN
84 | case logging.INFO:
85 | colour = Colours.GREEN
86 | case logging.WARNING:
87 | colour = Colours.YELLOW
88 | case logging.ERROR:
89 | colour = Colours.RED
90 | case logging.CRITICAL:
91 | colour = Colours.PURPLE
92 |
93 | record.msg = f"{colour}{record.msg}{Colours.RESET}"
94 | record.name = caller_name
95 |
96 | return super().format(record)
97 |
98 | return ColouredFormatter("[%(asctime)s] [%(levelname).1s] %(message)s")
99 |
100 | def set_level(self, level) -> None:
101 | """Set logging level.
102 |
103 | :param level: Logging level.
104 | :return: None
105 | """
106 | self.logger.setLevel(level)
107 |
108 | def get_logger(self) -> logging.Logger:
109 | """Get logger.
110 |
111 | :return: Logger.
112 | :rtype: logging.Logger
113 | """
114 | return self.logger
115 |
--------------------------------------------------------------------------------
/builder/utils/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/seppzer0/zero_kernel/1c6df84cb5d91b44e6ff1d09f266d64c005e92f3/builder/utils/__init__.py
--------------------------------------------------------------------------------
/builder/utils/bridge.py:
--------------------------------------------------------------------------------
1 | """
2 | Bridge-connector to launch shift the build process from host to Docker/Podman container.
3 |
4 | Essentially it re-launches the same builder, but the one that is a copy inside the container.
5 | Complete chain is: builder in host -> this bridge -> builder in container.
6 | """
7 |
8 | import sys
9 | import logging
10 | import argparse
11 |
12 | from builder.tools import Logger
13 | from builder.managers import ResourceManager
14 | from builder.commands import KernelCommand, AssetsCommand, BundleCommand
15 |
16 |
17 | log = logging.getLogger("ZeroKernelLogger")
18 |
19 |
20 | def parse_args() -> argparse.Namespace:
21 | """Parse arguments.
22 |
23 | Arguments here are NOT mandatory because this script has dual use:
24 | 1) launch one of the commands: kernel, assets, bundle;
25 | 2) install shared tools from tools.json.
26 |
27 | Because of that, all of the arguments are technically optional.
28 | Making any of the arguments mandatory would not allow it to be dual-use.
29 |
30 | :return: Namespace of arguments.
31 | :rtype: argparse.Namespace
32 | """
33 | args = None if sys.argv[1:] else ["-h"]
34 |
35 | parser = argparse.ArgumentParser()
36 | parser.add_argument(
37 | "--command",
38 | help="select builder command",
39 | choices={"kernel", "assets", "bundle"}
40 | )
41 | parser.add_argument(
42 | "--codename",
43 | help="select device codename"
44 | )
45 | parser.add_argument(
46 | "--base",
47 | help="select a kernel base for the build"
48 | )
49 | parser.add_argument(
50 | "--lkv",
51 | help="select Linux kernel version"
52 | )
53 | parser.add_argument(
54 | "--chroot",
55 | help="select chroot type",
56 | choices={"full", "minimal"}
57 | )
58 | parser.add_argument(
59 | "--package-type",
60 | dest="package_type",
61 | help="select bundle packaging type",
62 | choices={"conan", "slim", "full"}
63 | )
64 | parser.add_argument(
65 | "--clean-kernel",
66 | dest="clean_kernel",
67 | help="clean kernel directory",
68 | action="store_true"
69 | )
70 | parser.add_argument(
71 | "--clean-assets",
72 | dest="clean_assets",
73 | help="clean assets directory",
74 | action="store_true"
75 | )
76 | parser.add_argument(
77 | "--rom-only",
78 | dest="rom_only",
79 | help="download only ROM for an asset",
80 | action="store_true"
81 | )
82 | parser.add_argument(
83 | "--ksu",
84 | help="add KernelSU support",
85 | action="store_true"
86 | )
87 | parser.add_argument(
88 | "--defconfig",
89 | dest="defconfig",
90 | help="specify path to custom defconfig",
91 | )
92 | parser.add_argument(
93 | "--shared",
94 | help="only setup the shared tools in the environment",
95 | action="store_true"
96 | )
97 |
98 | return parser.parse_args(args)
99 |
100 |
101 | def main(args: argparse.Namespace) -> None:
102 | match args.command:
103 |
104 | case "kernel":
105 | kc = KernelCommand(
106 | codename = args.codename,
107 | base = args.base,
108 | lkv = args.lkv,
109 | clean_kernel = args.clean_kernel,
110 | ksu = args.ksu,
111 | defconfig = args.defconfig,
112 | )
113 | kc.execute()
114 |
115 | case "assets":
116 | ac = AssetsCommand(
117 | codename = args.codename,
118 | base = args.base,
119 | chroot = args.chroot,
120 | clean_assets = args.clean_assets,
121 | rom_only = args.rom_only,
122 | ksu = args.ksu,
123 | )
124 | ac.execute()
125 |
126 | case "bundle":
127 | bc = BundleCommand(
128 | codename = args.codename,
129 | base = args.base,
130 | lkv = args.lkv,
131 | package_type = args.package_type,
132 | ksu = args.ksu,
133 | defconfig = args.defconfig,
134 | )
135 | bc.execute()
136 |
137 | case _:
138 | # if no command was selected, then shared tools are (supposed to be) installed
139 | if args.shared:
140 | rm = ResourceManager()
141 | rm.read_data()
142 | rm.generate_paths()
143 | rm.download()
144 |
145 | else:
146 | # technically this part of code cannot be reached and is just an extra precaution
147 | log.error("Invalid argument set specified, please review")
148 | sys.exit(1)
149 |
150 |
151 | if __name__ == "__main__":
152 | main(parse_args())
153 |
--------------------------------------------------------------------------------
/conanfile.py:
--------------------------------------------------------------------------------
1 | from conans import ConanFile
2 |
3 |
4 | class ZeroKernelConan(ConanFile):
5 | name = "zero"
6 | author = "seppzer0"
7 | url = "https://gitlab.com/api/v4/projects/40803264/packages/conan"
8 | description = "An advanced Android kernel builder with Kali NetHunter support."
9 | topics = ("zero_kernel", "kali-nethunter", "nethunter")
10 | settings = None
11 | options = {
12 | "base": {"los", "pa", "x", "aosp"},
13 | "chroot": {"minimal", "full"},
14 | "codename": {"dumpling", "cheeseburger"}
15 | }
16 |
17 | def export_sources(self):
18 | self.copy("*", src="source", dst=".")
19 |
20 | def build(self):
21 | shared_args = "--build-env=local --base={} --codename={} --chroot={}".format(
22 | self.options.base,
23 | self.options.codename,
24 | self.options.chroot
25 | )
26 | cmd = "python3 builder kernel {0} &&"\
27 | "python3 builder assets {0} --clean"\
28 | .format(shared_args)
29 | print(f"[cmd] {cmd}")
30 | self.run(cmd)
31 |
32 | def package(self):
33 | # package built kernel with collected assets
34 | self.copy("*.zip", src="kernel", dst="kernel", keep_path=False)
35 | self.copy("*", src="assets", dst="assets")
36 |
--------------------------------------------------------------------------------
/docs/FAQ.md:
--------------------------------------------------------------------------------
1 | # FAQ
2 |
3 | This page contains answers to popular questions in relation to this kernel.
4 |
5 | ## Q: How to TURN ON monitor mode on internal Wi-Fi card?
6 |
7 | **A:** There are two options to switch internal Wi-Fi card to monitor mode:
8 |
9 | - in Kali chroot environment, launch `airmon-ng start wlan0`;
10 | - in NetHunter app, navigate to the `Custom Commands` menu and launch the `Start wlan0 in monitor mode`.
11 |
12 | Be aware that while in monitor mode, you won't be able to connect to a Wi-Fi network like you'd normally do in an Android device.
13 |
14 | ## Q: How to TURN OFF monitor mode on internal Wi-Fi card?
15 |
16 | **A:** Similarly, depending on which approach you chose to turn on the monitor mode, there are two options:
17 |
18 | - in Kali chroot environment -> `airmon-ng stop wlan0`;
19 | - in NetHunter app -> `Custom Commands` -> `Stop wlan0 in monitor mode`.
20 |
21 | ## Q: Why is there an unused wlan1 interface?
22 |
23 | **A:** Because it's a ~~bug~~ feature of Android 13.
24 |
25 | Initially, when launching `airmon-ng` in Kali chroot environment without any of the interfaces in monitor mode and no external adapters plugged in, you will see two wlan interfaces: `wlan0` and `wlan1`.
26 |
27 | This is most likely caused by the [Wi-Fi STA/STA Concurrency](https://source.android.com/docs/core/connect/wifi-sta-sta-concurrency) mechanism. Disabling this seems dangerous, so a workaround is required.
28 |
29 | Switching `wlan0` to monitor mode disables `wlan1` completely. However, when restoring `wlan0` to "normal" mode, `wlan1` appears back.
30 |
31 | ## Q: How to TURN ON and OFF monitor mode on external Wi-Fi card?
32 |
33 | **A:** For an external card, you would have to use `airmon-ng start ` and `airmon-ng stop ` commands.
34 |
35 | ## Q: How do I switch from standard partition ROM to retrofit dynamic partition ROM and vice versa?
36 |
37 | **A:** Refer to these [instructions](https://gist.github.com/nkeor/d71b7884ee951de669b0d4baeacc58ba).
38 |
--------------------------------------------------------------------------------
/docs/FLASHING.md:
--------------------------------------------------------------------------------
1 | # Kernel Flashing Instructions
2 |
3 | For this kernel to work properly, there are some steps that need to be done.
4 |
5 | Please also note that the instructions provided are made for a clean installation.
6 |
7 | *As for now*, dirty installations (==with data preservation) are not tested nor researched.
8 |
9 | Also keep in mind that after executing all of the documented steps, **your device will have disabled DM-Verity and Force Encrypt, which can be seen as (and actually is) a security risk.**
10 |
11 | ## **0. Backup**
12 |
13 | Before doing anything to your device, **make a backup**.
14 |
15 | It is highly recommended to do a NANDroid backup via TWRP.
16 |
17 | Backup should be made with removed lockscreen passwords/fingerprints/PINs. Otherwise there might be some issues during the flashing process.
18 |
19 | ## **1. Download assets**
20 |
21 | Listed below files are required:
22 |
23 | - compiled kernel, obviously;
24 | - ROM;
25 | - Magisk or KernelSU;
26 | - TWRP, the special [build](https://sourceforge.net/projects/op5-5t/files/Android-12/TWRP/twrp-3.7.0_12-5-dyn-cheeseburger_dumpling.img/download) by faoliveira78 (supports operations with encrypted and dynamic partitions);
27 | - DM-Verity and Force Encrypt disabler;
28 | - Kali NetHunter + Kali NetHunter Terminal apps;
29 | - Kali NetHunter Chroot (you can do this later, but it would be easier to download this beforehand);
30 | - ~~`nhpatch.sh` script from this repo (fixes NetHunter permissions for Android 12+)~~ with recent NetHunter app versions, `nhpatch.sh` usage is no longer required.
31 |
32 | Currently, all of the mentioned assets can be collected via the `assets` subcommand (use `full` option).
33 |
34 | ## **2. Flashing**
35 |
36 | **Note:** Once again, this instruction is for clean installation only, meaning that you will lose all your data.
37 |
38 | The installation process is done in the following order:
39 |
40 | ### **2.0 Some checks**
41 |
42 | Before doing anything, please ensure that you have:
43 |
44 | - an unlocked bootloader;
45 | - an installed TWRP recovery;
46 | - a working NANDroid backup of your device.
47 |
48 | ### **2.1 In TWRP**
49 |
50 | - wipe your phone via `Wipe -> Advanced Wipe` menu, check all the shown boxes;
51 | - wipe your device again via `Wipe -> Format Data` menu (this will remove any encryption that is present on your device);
52 | - reboot into TWRP via `Reboot -> Recovery`;
53 | - if using a Retrofit Dynamic Partitions ROM such as ParanoidAndoid -> untoggle `Unmount System before installing a ZIP` in the Settings;
54 | - flash the ROM;
55 | - flash the kernel;
56 | - **if using Magisk** --> flash Magisk root manager (you must change the `.apk` extension into `.zip` for this);
57 | - flash DM-Verity and Force Encrypt disabler zip;
58 | - reboot into system via `Reboot -> System` .
59 |
60 | ### **2.2 In OS**
61 |
62 | #### For Magisk users
63 |
64 | - install Magisk apk, open it and do what the pop-up says (finish root installation, which will automatically reboot your device; if you don't see the pop-up, close the Magisk app and open it again);
65 | - once booted back into OS, open Magisk app again and proceed with finishing the installation (when prompted with "Additional Setup", select the default `Patch vbmeta in boot image` in `Options` and `Direct install` in `Method` submenus);
66 | - install NetHunter + NetHunter Terminal apps;
67 | - open NetHunter app (if seeing a Busybox-related error, press "OK" and re-open the app);
68 | - navigate to the `Kali Chroot Manager` submenu and install the chroot (if you downloaded it beforehand, use the "restore" option);
69 | - make sure that your NetHunter and NetHunter Terminal apps are properly configured to see the installed chroot directory (by default it may be `/data/local/nhsystem/kalifs`; if you see it anywhere, change it to `/data/local/nhsystem/kali-arm64`);
70 | - in NetHunter Terminal app open `Kali` shell (if it opens properly, then congratulations, you have a working Kali NetHunter on your device).
71 |
72 | #### For KernelSU users
73 |
74 | - install KernelSU Manager app, open it and verify that the `Superuser` tab works properly (should show the `Shell` item);
75 | - install NetHunter and NetHunter Terminal apps, but do not open them yet;
76 | - open KernelSU Manager app, grant SU permissions to both NetHunter and NetHunter Terminal apps via `Superuser` tab;
77 | - open the NetHunter app (if seeing a Busybox-related error, press "OK" and re-open the app);
78 | - navigate to the `Kali Chroot Manager` submenu and install the chroot (if you downloaded it beforehand, use the "restore" option);
79 | - make sure that your NetHunter and NetHunter Terminal apps are properly configured to see the installed chroot directory (by default it may be `/data/local/nhsystem/kalifs`; if you see it anywhere, change it to `/data/local/nhsystem/kali-arm64`);
80 | - in NetHunter Terminal app open `Kali` shell (if it opens properly, then congratulations, you have a working Kali NetHunter on your device).
81 |
82 | #### For x_kernel-based kernel + ParanoidAndroid users
83 |
84 | This is a small side-note for using x_kernel-based build with ParanoidAndroid ROM. When booting into OS, you will see a message that `There is an internal problem with this device. Please call manufacturer.`. This warning is essentially similar to the unlocked bootloader message and is completely harmless. Press "OK" and proceed.
85 |
--------------------------------------------------------------------------------
/docs/TODO.md:
--------------------------------------------------------------------------------
1 | # TODO
2 |
3 | ## Kernel features
4 |
5 | ### Done
6 |
7 | - [x] add monitor mode support for internal Wi-Fi chipset (with Wi-Fi packet injection);
8 | - [x] add KernelSU support.
9 | - [x] add DM-Verity and Force Encrypt disabler.
10 |
11 | ### Left
12 |
13 | - [ ] add DriveDroid(-like) support.
14 |
15 | ## Build process features
16 |
17 | ### Done
18 |
19 | - [x] add an asset downloader (ROM, Magisk, etc.);
20 | - [x] add CI/CD pipelines;
21 | - [x] add option to save logs to a file;
22 | - [x] add option to build in Docker container;
23 | - [x] add multidevice support, with appropriate manifest;
24 | - [x] improve log level setting throughout all stages of the build;
25 | - [x] improve file/directory cleaning mechanism in a form of a dedicated module;
26 | - [x] improve Clang download mechanism;
27 | - [x] add build counter mechanism for CI/CD pipelines;
28 | - [x] add codename specific elements to final kernel zip;
29 | - [x] add a simpler and slimer bundle creator (kernel+ROM);
30 | - [x] add return types to functions;
31 | - [x] add requirements.txt for pip;
32 | - [x] add proper OS detection for preventing local builds in unsupported systems;
33 | - [x] add static analysis for the wrapper;
34 | - [x] add a single-point manifest with main info on the tool;
35 | - [x] improve documentation;
36 | - [x] apply OOP paradigm;
37 | - [x] switch to uv for dependency management;
38 | - [x] switch to `pathlib`;
39 | - [x] add a FAQ page;
40 | - [x] refactor Docker/Podman command formation;
41 | - [x] fix Podman usage (.dockerignore);
42 | - [x] add a separate method for multiline patching in files;
43 | - [x] add a dataclass for wrapper's arguments;
44 | - [x] add PA ROM support;
45 | - [x] decompose `run()` method in `ContainerEngine`;
46 | - [x] skip building Docker/Podman image if it's already present in local cache;
47 | - [x] for containerized build, download the contents of manifests during image build;
48 | - [x] add a new argument responsible for Linux kernel version selection;
49 | - [x] add 4.14 Linux kernel version builds;
50 | - [x] decompose `run` methods into separate methods as much as possible;
51 | - [x] switch to pydantic;
52 | - [x] add type checks with pyright;
53 | - [x] add unit tests with coverage checks;
54 | - [x] switch to `__enter__` and `__exit__` Python's magic methods for container engines.
55 |
56 | ### Left
57 |
58 | - [ ] add published Conan package validator;
59 | - [ ] create a commit-based lockfile system for reproducible kernel builds;
60 | - [ ] dedicate kernel source patchers as separate modules (LineageOS, AOSP, AOSPA etc);
61 | - [ ] make kernel building and assets collection processes asynchronous when launching the `bundle` option;
62 | - [ ] add a GitHub workflow for checking PRs;
63 | - [ ] add system app debloater;
64 | - [ ] move device-specific modifications into appropriate folder with custom Modificator (sub)classes;
65 | - [ ] switch to `exec` commands for Docker and Podman instead of a single `run` command;
66 | - [ ] replace `ccmd.launch()` to just `launch()` (or any other name);
67 | - [ ] embed newlines usage (from both sides) into `messages` functions as arguments;
68 | - [ ] add tests on cases of custom wrapper edge case exits (kernel build on Windows, ROM-only asset collection for a ROM-universal kernel etc.);
69 | - [ ] break down (or create inheritance from) KernelBuilder into LineageOsKernelBuilder, ParanoidAndroidKernelBuilder, XKernelBuilder etc;
70 | - [ ] investigate project restructuring to avoid circular import;
71 | - [ ] consider creating a separate "errors" subpackage for all errors;
72 | - [ ] add GKI kernels support;
73 | - [ ] use logging facility for logs.
74 |
--------------------------------------------------------------------------------
/docs/architecture/clients/classes.puml:
--------------------------------------------------------------------------------
1 | @startuml classes
2 | set namespaceSeparator none
3 | class "GithubApiClient" as clients.github.GithubApiClient {
4 | direct_url
5 | endpoint
6 | file_filter : Optional[str]
7 | project : str
8 | run() -> str | None
9 | }
10 | class "LineageOsApiClient" as clients.los.LineageOsApiClient {
11 | endpoint : str
12 | json_key : str
13 | rom_name : str
14 | }
15 | class "ParanoidAndroidApiClient" as clients.pa.ParanoidAndroidApiClient {
16 | endpoint : str
17 | json_key : str
18 | rom_name : str
19 | map_codename() -> str
20 | }
21 | class "RomApiClient" as clients.rom_api.RomApiClient {
22 | codename : str
23 | endpoint : str
24 | json_key : str
25 | rom_name : str
26 | rom_only : bool
27 | map_codename() -> str
28 | run() -> str
29 | }
30 | @enduml
31 |
--------------------------------------------------------------------------------
/docs/architecture/clients/packages.puml:
--------------------------------------------------------------------------------
1 | @startuml packages
2 | set namespaceSeparator none
3 | package "clients" as clients {
4 | }
5 | package "clients.github" as clients.github {
6 | }
7 | package "clients.los" as clients.los {
8 | }
9 | package "clients.pa" as clients.pa {
10 | }
11 | package "clients.rom_api" as clients.rom_api {
12 | }
13 | clients --> clients.github
14 | clients --> clients.los
15 | clients --> clients.pa
16 | @enduml
17 |
--------------------------------------------------------------------------------
/docs/architecture/commands/classes.puml:
--------------------------------------------------------------------------------
1 | @startuml classes
2 | set namespaceSeparator none
3 | class "AssetsCommand" as commands.assets.AssetsCommand {
4 | base : str
5 | chroot : Literal['full', 'minimal']
6 | clean_assets : bool
7 | codename : str
8 | ksu : bool
9 | rom_only : bool
10 | execute() -> None
11 | }
12 | class "BundleCommand" as commands.bundle.BundleCommand {
13 | base : str
14 | codename : str
15 | defconfig : Optional[Path]
16 | ksu : bool
17 | lkv : str
18 | package_type : str
19 | build_kernel(rom_name: str, clean_only: bool) -> None
20 | collect_assets(rom_name: str, chroot: Literal['full', 'minimal']) -> None
21 | conan_options(json_file: str) -> dict
22 | conan_package(options: tuple[str, ...], reference: str) -> None
23 | conan_sources() -> None
24 | conan_upload(reference: str) -> None
25 | execute() -> None
26 | }
27 | class "KernelCommand" as commands.kernel.KernelCommand {
28 | base : str
29 | clean_kernel : bool
30 | codename : str
31 | defconfig : Optional[Path]
32 | ksu : bool
33 | lkv : str
34 | execute() -> None
35 | }
36 | @enduml
37 |
--------------------------------------------------------------------------------
/docs/architecture/commands/packages.puml:
--------------------------------------------------------------------------------
1 | @startuml packages
2 | set namespaceSeparator none
3 | package "commands" as commands {
4 | }
5 | package "commands.assets" as commands.assets {
6 | }
7 | package "commands.bundle" as commands.bundle {
8 | }
9 | package "commands.kernel" as commands.kernel {
10 | }
11 | commands --> commands.assets
12 | commands --> commands.bundle
13 | commands --> commands.kernel
14 | @enduml
15 |
--------------------------------------------------------------------------------
/docs/architecture/configs/classes.puml:
--------------------------------------------------------------------------------
1 | @startuml classes
2 | set namespaceSeparator none
3 | class "ArgumentConfig" as configs.argument.ArgumentConfig {
4 | base : str
5 | benv : Literal['docker', 'podman', 'local']
6 | chroot : Optional[str]
7 | clean_assets : Optional[bool]
8 | clean_image : Optional[bool]
9 | clean_kernel : Optional[bool]
10 | codename : str
11 | command : Literal['kernel', 'assets', 'bundle']
12 | conan_upload : Optional[bool]
13 | defconfig : Optional[Path]
14 | ksu : Optional[bool]
15 | lkv : Optional[str]
16 | package_type : Optional[str]
17 | rom_only : Optional[bool]
18 | check_settings() -> None
19 | }
20 | class "DirectoryConfig" as configs.directory.DirectoryConfig {
21 | assets : Path
22 | bundle : Path
23 | kernel : Path
24 | root : Path
25 | }
26 | @enduml
27 |
--------------------------------------------------------------------------------
/docs/architecture/configs/packages.puml:
--------------------------------------------------------------------------------
1 | @startuml packages
2 | set namespaceSeparator none
3 | package "configs" as configs {
4 | }
5 | package "configs.argument" as configs.argument {
6 | }
7 | package "configs.directory" as configs.directory {
8 | }
9 | configs --> configs.argument
10 | configs --> configs.directory
11 | @enduml
12 |
--------------------------------------------------------------------------------
/docs/architecture/core/classes.puml:
--------------------------------------------------------------------------------
1 | @startuml classes
2 | set namespaceSeparator none
3 | class "AssetsCollector" as core.assets_collector.AssetsCollector {
4 | assets
5 | base : str
6 | chroot : Literal['full', 'minimal']
7 | clean_assets : bool
8 | codename : str
9 | ksu : bool
10 | rom_collector_dto
11 | rom_only : bool
12 | run() -> None
13 | }
14 | class "KernelBuilder" as core.kernel_builder.KernelBuilder {
15 | base : str
16 | clean_kernel : bool
17 | codename : str
18 | defconfig : Optional[Path]
19 | ksu : bool
20 | lkv : str
21 | rmanager
22 | build() -> None
23 | clean_build() -> None
24 | create_zip() -> None
25 | patch_all() -> None
26 | patch_anykernel3() -> None
27 | patch_ioctl() -> None
28 | patch_kernel() -> None
29 | patch_ksu() -> None
30 | patch_qcacld() -> None
31 | patch_rtl8812au() -> None
32 | patch_rtl8812au_source_mod_v5642() -> None
33 | patch_strict_prototypes() -> None
34 | run() -> None
35 | update_defconfig() -> None
36 | write_localversion() -> None
37 | }
38 | @enduml
39 |
--------------------------------------------------------------------------------
/docs/architecture/core/packages.puml:
--------------------------------------------------------------------------------
1 | @startuml packages
2 | set namespaceSeparator none
3 | package "core" as core {
4 | }
5 | package "core.assets_collector" as core.assets_collector {
6 | }
7 | package "core.kernel_builder" as core.kernel_builder {
8 | }
9 | core --> core.assets_collector
10 | core --> core.kernel_builder
11 | @enduml
12 |
--------------------------------------------------------------------------------
/docs/architecture/engines/classes.puml:
--------------------------------------------------------------------------------
1 | @startuml classes
2 | set namespaceSeparator none
3 | class "GenericContainerEngine" as engines.generic_container.GenericContainerEngine {
4 | base : str
5 | benv : Literal['docker', 'podman']
6 | builder_cmd
7 | chroot : Optional[Literal['full', 'minimal']]
8 | clean_assets : Optional[bool]
9 | clean_image : Optional[bool]
10 | clean_kernel : Optional[bool]
11 | codename : str
12 | command : Literal['kernel', 'assets', 'bundle']
13 | conan_upload : Optional[bool]
14 | container_options
15 | defconfig : Optional[Path]
16 | dir_bundle_conan
17 | get_container_cmd
18 | ksu : Optional[bool]
19 | lkv : Optional[str]
20 | package_type : Optional[str]
21 | rom_only : Optional[bool]
22 | build_image() -> str | None | CompletedProcess
23 | check_cache() -> bool
24 | create_dirs() -> None
25 | }
26 | @enduml
27 |
--------------------------------------------------------------------------------
/docs/architecture/engines/packages.puml:
--------------------------------------------------------------------------------
1 | @startuml packages
2 | set namespaceSeparator none
3 | package "engines" as engines {
4 | }
5 | package "engines.generic_container" as engines.generic_container {
6 | }
7 | engines --> engines.generic_container
8 | @enduml
9 |
--------------------------------------------------------------------------------
/docs/architecture/interfaces/classes.puml:
--------------------------------------------------------------------------------
1 | @startuml classes
2 | set namespaceSeparator none
3 | interface "IAssetsCollector" as interfaces.modules.IAssetsCollector {
4 | assets
5 | rom_collector_dto
6 | {abstract}run() -> None
7 | }
8 | interface "IBundleCommand" as interfaces.commands.IBundleCommand {
9 | {abstract}build_kernel(rom_name: str, clean_only: bool) -> None
10 | {abstract}collect_assets(rom_name: str, chroot: Literal['full', 'minimal']) -> None
11 | {abstract}conan_options(json_file: str) -> dict
12 | {abstract}conan_package(options: tuple[str, ...], reference: str) -> None
13 | {abstract}conan_sources() -> None
14 | {abstract}conan_upload(reference: str) -> None
15 | }
16 | interface "ICommand" as interfaces.commands.ICommand {
17 | {abstract}execute() -> None
18 | }
19 | interface "IGenericContainerEngine" as interfaces.engines.IGenericContainerEngine {
20 | builder_cmd
21 | container_options
22 | dir_bundle_conan
23 | get_container_cmd
24 | {abstract}build_image() -> str | None | CompletedProcess
25 | {abstract}check_cache() -> bool
26 | {abstract}create_dirs() -> None
27 | }
28 | interface "IKernelBuilder" as interfaces.modules.IKernelBuilder {
29 | {abstract}build() -> None
30 | {abstract}clean_build() -> None
31 | {abstract}create_zip() -> None
32 | {abstract}patch_all() -> None
33 | {abstract}patch_anykernel3() -> None
34 | {abstract}patch_ioctl() -> None
35 | {abstract}patch_kernel() -> None
36 | {abstract}patch_ksu() -> None
37 | {abstract}patch_qcacld() -> None
38 | {abstract}patch_rtl8812au() -> None
39 | {abstract}patch_rtl8812au_source_mod_v5642() -> None
40 | {abstract}patch_strict_prototypes() -> None
41 | {abstract}run() -> None
42 | {abstract}write_localversion() -> None
43 | }
44 | interface "IResourceManager" as interfaces.managers.IResourceManager {
45 | {abstract}download() -> None
46 | {abstract}export_path() -> None
47 | {abstract}generate_paths() -> None
48 | {abstract}read_data() -> None
49 | }
50 | interface "IRomApiClient" as interfaces.clients.IRomApiClient {
51 | {abstract}map_codename() -> str
52 | {abstract}run() -> str
53 | }
54 | @enduml
55 |
--------------------------------------------------------------------------------
/docs/architecture/interfaces/packages.puml:
--------------------------------------------------------------------------------
1 | @startuml packages
2 | set namespaceSeparator none
3 | package "interfaces" as interfaces {
4 | }
5 | package "interfaces.clients" as interfaces.clients {
6 | }
7 | package "interfaces.commands" as interfaces.commands {
8 | }
9 | package "interfaces.engines" as interfaces.engines {
10 | }
11 | package "interfaces.managers" as interfaces.managers {
12 | }
13 | package "interfaces.modules" as interfaces.modules {
14 | }
15 | interfaces --> interfaces.clients
16 | interfaces --> interfaces.commands
17 | interfaces --> interfaces.engines
18 | interfaces --> interfaces.managers
19 | interfaces --> interfaces.modules
20 | @enduml
21 |
--------------------------------------------------------------------------------
/docs/architecture/managers/classes.puml:
--------------------------------------------------------------------------------
1 | @startuml classes
2 | set namespaceSeparator none
3 | class "ResourceManager" as managers.resource.ResourceManager {
4 | base : Optional[str]
5 | codename : Optional[str]
6 | lkv : Optional[str]
7 | paths : dict[str, Path]
8 | download() -> None
9 | export_path() -> None
10 | generate_paths() -> None
11 | read_data() -> None
12 | }
13 | @enduml
14 |
--------------------------------------------------------------------------------
/docs/architecture/managers/packages.puml:
--------------------------------------------------------------------------------
1 | @startuml packages
2 | set namespaceSeparator none
3 | package "managers" as managers {
4 | }
5 | package "managers.resource" as managers.resource {
6 | }
7 | managers --> managers.resource
8 | @enduml
9 |
--------------------------------------------------------------------------------
/docs/architecture/tools/classes.puml:
--------------------------------------------------------------------------------
1 | @startuml classes
2 | set namespaceSeparator none
3 | @enduml
4 |
--------------------------------------------------------------------------------
/docs/architecture/tools/packages.puml:
--------------------------------------------------------------------------------
1 | @startuml packages
2 | set namespaceSeparator none
3 | package "tools" as tools {
4 | }
5 | package "tools.cleaning" as tools.cleaning {
6 | }
7 | package "tools.commands" as tools.commands {
8 | }
9 | package "tools.fileoperations" as tools.fileoperations {
10 | }
11 | package "tools.messages" as tools.messages {
12 | }
13 | @enduml
14 |
--------------------------------------------------------------------------------
/docs/architecture/utils/classes.puml:
--------------------------------------------------------------------------------
1 | @startuml classes
2 | set namespaceSeparator none
3 | @enduml
4 |
--------------------------------------------------------------------------------
/docs/architecture/utils/packages.puml:
--------------------------------------------------------------------------------
1 | @startuml packages
2 | set namespaceSeparator none
3 | package "utils" as utils {
4 | }
5 | package "utils.bridge" as utils.bridge {
6 | }
7 | @enduml
8 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "zero-kernel"
3 | version = "0.6.1"
4 | description = "Advanced Android kernel builder with Kali NetHunter support."
5 | authors = [{name = "seppzer0"}]
6 | readme = "README.md"
7 | requires-python = ">=3.12"
8 | dependencies = [
9 | "argparse",
10 | "conan ~=1.6",
11 | "requests ~=2.31",
12 | "pathlib ~=1.0",
13 | "typing ~=3.7",
14 | "pydantic ~=2.6",
15 | ]
16 |
17 | [project.urls]
18 | Repository = "https://github.com/seppzer0/zero_kernel"
19 | Documentation = "https://github.com/seppzer0/zero_kernel/blob/main/README.md"
20 |
21 | [project.scripts]
22 | buildz = "zero_builder:main"
23 |
24 | [build-system]
25 | requires = ["hatchling"]
26 | build-backend = "hatchling.build"
27 |
28 | [tool.hatch.build.targets.wheel]
29 | packages = ["builder"]
30 |
31 | [tool.uv]
32 | environments = [
33 | "sys_platform == 'darwin'",
34 | "sys_platform == 'linux'",
35 | #"implementation_name == 'cpython'",
36 | ]
37 | dev-dependencies = [
38 | "bandit ~=1.7",
39 | "pytest ~=8.0",
40 | "pytest-cov ~=4.1",
41 | "pyright ~=1.1",
42 | "pylint ~=3.1",
43 | "ruff ~=0.7",
44 | "pre-commit ~=4.2",
45 | "pip",
46 | ]
47 |
48 | [[tool.uv.index]]
49 | name = "pypi"
50 | default = true
51 | url = "https://pypi.org/simple"
52 |
53 | [tool.coverage.run]
54 | source = [
55 | "builder/clients",
56 | "builder/commands",
57 | "builder/configs",
58 | "builder/engines",
59 | "builder/managers"
60 | ]
61 |
62 | [tool.coverage.report]
63 | show_missing = true
64 |
65 | [tool.pyright]
66 | include = ["builder"]
67 | pythonVersion = "3.12"
68 | venvPath = "."
69 | venv = ".venv"
70 |
71 | [tool.ruff]
72 | include = ["builder/**/*.py"]
73 | indent-width = 4
74 | line-length = 125
75 | exclude = [
76 | ".git",
77 | ".pytest_cache",
78 | ".venv"
79 | ]
80 | output-format = "full"
81 |
82 | [tool.ruff.lint]
83 | select = ["E", "Q", "D", "E501"]
84 | ignore = ["D1", "D200", "D212"]
85 | task-tags = ["TODO", "NOTE"]
86 |
87 | [tool.ruff.lint.per-file-ignores]
88 | "__init__.py" = ["F401"]
89 |
90 | [tool.ruff.lint.pydocstyle]
91 | convention = "google"
92 |
93 | [tool.ruff.lint.flake8-quotes]
94 | docstring-quotes = "double"
95 | inline-quotes = "double"
96 |
97 | [tool.pytest.ini_options]
98 | addopts = "--import-mode=importlib -vv"
99 | testpaths = ["tests"]
100 |
--------------------------------------------------------------------------------
/requirement-uv.txt:
--------------------------------------------------------------------------------
1 | uv==0.7.9
2 |
--------------------------------------------------------------------------------
/scripts/install_hooks.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import logging
4 | import textwrap
5 | from pathlib import Path
6 |
7 |
8 | logging.basicConfig(
9 | format="[%(asctime)s] [%(levelname).1s] %(message)s",
10 | datefmt="%H:%M:%S",
11 | level=logging.INFO,
12 | stream=sys.stdout,
13 | )
14 |
15 |
16 | class HookInstaller:
17 | """Manager of git hooks for the repository."""
18 |
19 | def __init__(self) -> None:
20 | self.__path = Path(__file__).absolute().parents[1] / ".git" / "hooks" / "pre-commit"
21 |
22 | @property
23 | def hook(self) -> str:
24 | """Contents of hook to be installed.
25 |
26 | :return: Contents of a git hook.
27 | :rtype: str
28 | """
29 | hook = textwrap.dedent(
30 | """
31 | #!/usr/bin/env bash
32 |
33 | ARGS=(hook-impl --config=.pre-commit-config.yaml --hook-type=pre-commit)
34 | HERE="$(cd "$(dirname "$0")" && pwd)"
35 | ARGS+=(--hook-dir "$HERE" -- "$@")
36 |
37 | exec uv run -m pre_commit "${ARGS[@]}"
38 | """
39 | )
40 | # strip only leading whitespace
41 | return hook.lstrip()
42 |
43 | def install(self) -> None:
44 | """Write the hook to the specified file.
45 |
46 | :return: None
47 | """
48 | with open(self.__path, "w") as f:
49 | f.write(self.hook)
50 | os.chmod(self.__path, 0o755)
51 |
52 |
53 | def main() -> None:
54 | logging.info("Installing git hooks...")
55 | hi = HookInstaller()
56 | hi.install()
57 | logging.info("Done! Hooks installed successfully.")
58 |
59 |
60 | if __name__ == "__main__":
61 | main()
62 |
--------------------------------------------------------------------------------
/scripts/install_rtw_drivers.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 |
6 | function main() {
7 | mkdir -p ~/repos
8 | cd ~/repos
9 | git clone https://github.com/lwfinger/rtw88
10 | cd rtw88
11 | make
12 | make install
13 | make install_fw
14 | }
15 |
16 | main
17 |
--------------------------------------------------------------------------------
/scripts/multi_build.py:
--------------------------------------------------------------------------------
1 | """
2 | Wrapper-script to launch the builder for multiple setting combinations.
3 | """
4 |
5 | import os
6 | import shutil
7 | import argparse
8 | import subprocess
9 | from pathlib import Path
10 |
11 |
12 | def parse_args() -> argparse.Namespace:
13 | """Parse arguments.
14 |
15 | :return: Namespace of arguments.
16 | :rtype: argparse.Namespace
17 | """
18 | parser = argparse.ArgumentParser()
19 | parser.add_argument(
20 | "--env",
21 | choices={"docker", "podman", "local"},
22 | default="docker"
23 | )
24 |
25 | return parser.parse_args()
26 |
27 |
28 | def rmove(src: Path, dst: Path) -> None:
29 | """Recursively move files from one directory to another.
30 |
31 | :param Path src: Source path.
32 | :param Path dst: Destination path.
33 | :return: None
34 | """
35 | # for a directory
36 | if src.is_dir():
37 |
38 | if not dst.is_dir():
39 | os.makedirs(dst)
40 |
41 | contents = os.listdir(src)
42 | for e in contents:
43 | # do not copy restricted files
44 | if e != src:
45 | src_e = src / e
46 | dst_e = dst / e
47 | shutil.move(src_e, dst_e)
48 |
49 | # for a single file
50 | elif src.is_file():
51 | shutil.move(src, dst)
52 |
53 |
54 | def main(args: argparse.Namespace) -> None:
55 | rootpath = Path(__file__).absolute().parents[1]
56 | argsets = (
57 | {
58 | "command": "bundle",
59 | "rom": "los",
60 | "codename": "dumpling",
61 | "lkv": "4.4",
62 | "ksu": False
63 | },
64 | {
65 | "command": "kernel",
66 | "rom": "los",
67 | "codename": "dumpling",
68 | "lkv": "4.4",
69 | "ksu": True
70 | },
71 | {
72 | "command": "kernel",
73 | "rom": "x",
74 | "codename": "dumpling",
75 | "lkv": "4.4",
76 | "ksu": False
77 | },
78 | {
79 | "command": "kernel",
80 | "rom": "x",
81 | "codename": "dumpling",
82 | "lkv": "4.4",
83 | "ksu": True
84 | },
85 | {
86 | "command": "kernel",
87 | "rom": "x",
88 | "codename": "dumpling",
89 | "lkv": "4.14",
90 | "ksu": False
91 | },
92 | {
93 | "command": "assets",
94 | "rom": "los",
95 | "codename": "cheeseburger",
96 | "ksu": True
97 | },
98 | )
99 |
100 | os.chdir(rootpath)
101 | dir_shared = rootpath / "multi-build"
102 | shutil.rmtree(dir_shared, ignore_errors=True)
103 | os.makedirs(dir_shared)
104 |
105 | for count, argset in enumerate(argsets, 1):
106 | # define some of the values individually
107 | benv = f"--build-env {args.env}"
108 | base = f'--base {argset["rom"]}'
109 | codename = f'--codename {argset["codename"]}'
110 | lkv = f'--lkv {argset["lkv"]}' if argset["command"] in ("kernel", "bundle") else ""
111 | ksu = "--ksu" if argset["ksu"] else ""
112 | size = "--package-type slim" if argset["command"] == "bundle" else ""
113 | extra = "--chroot minimal --rom-only --clean" if argset["command"] == "assets" else ""
114 |
115 | # if the build is last, make it automatically remove the Docker/Podman image from runner
116 | clean_image = "--clean-image" if count == len(argsets) and args.env in ("docker", "podman") else ""
117 |
118 | # form and launch the command
119 | cmd = f"python3 builder {argset['command']} {benv} {base} {codename} {lkv} {size} {ksu} {clean_image} {extra}"
120 | print(f"[CMD]: {cmd}")
121 | subprocess.run(cmd.strip(), shell=True, check=True)
122 |
123 | # define directory to copy artifacts to
124 | out = ""
125 | match argset["command"]:
126 | case "bundle":
127 | out = "bundle"
128 | case "kernel":
129 | out = "kernel"
130 | case "assets":
131 | out = "assets"
132 | # convert into full path
133 | out = Path(rootpath, out)
134 | rmove(out, dir_shared)
135 |
136 |
137 | if __name__ == "__main__":
138 | main(parse_args())
139 |
--------------------------------------------------------------------------------
/tests/unit/builder/core/test_kernel_builder.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | from pathlib import Path
3 |
4 | from builder.core import KernelBuilder
5 | from builder.managers import ResourceManager
6 |
7 |
8 | @pytest.mark.parametrize(
9 | "config, expected_defconfig",
10 | (
11 | (
12 | {"codename": "dumpling", "base": "los", "lkv": "4.4", "clean_kernel": False, "ksu": False},
13 | Path("lineage_oneplus5_defconfig")
14 | ),
15 | (
16 | {"codename": "cheeseburger", "base": "pa", "lkv": "4.14", "clean_kernel": False, "ksu": True},
17 | Path("vendor", "paranoid_defconfig")
18 | ),
19 | (
20 | {"codename": "dumpling", "base": "x", "lkv": "4.4", "clean_kernel": True, "ksu": True},
21 | Path("oneplus5_defconfig")
22 | ),
23 | (
24 | {"codename": "guacamoleb", "base": "los", "lkv": "4.14", "clean_kernel": True, "ksu": False},
25 | Path("lineage_sm8150_defconfig")
26 | ),
27 | )
28 | )
29 | def test__defconfig__check(config: dict[str, str], expected_defconfig: Path) -> None:
30 | """Test defconfig path definition."""
31 | t = KernelBuilder(**config, rmanager=ResourceManager())
32 | res_actual = t._defconfig
33 | res_expected = expected_defconfig
34 | assert res_actual == res_expected
35 |
--------------------------------------------------------------------------------
/tests/unit/builder/tools/test_commands.py:
--------------------------------------------------------------------------------
1 | import pytest
2 |
3 | from builder.tools import commands as ccmd
4 |
5 |
6 | def test__launch__invalid_command(capfd) -> None:
7 | """Test an invalid command execution handling."""
8 | cmd = "some_invalid_command"
9 | with pytest.raises(SystemExit):
10 | ccmd.launch(cmd)
11 |
--------------------------------------------------------------------------------
/tests/unit/builder/tools/test_messages.py:
--------------------------------------------------------------------------------
1 | import pytest
2 | import logging
3 |
4 | from builder.tools import Logger
5 |
6 |
7 | log = logging.getLogger("ZeroKernelLogger")
8 |
9 |
10 | #def test__message_info__validate(capfd) -> None:
11 | # """Check "info" message construction."""
12 | # m = "This is a test message."
13 | # expected_result = f"[I] {m}"
14 | # log.error(m)
15 | # out, err = capfd.readouterr()
16 | # assert expected_result in out.rstrip()
17 | #
18 | #
19 | #def test__message_error__validate(capfd) -> None:
20 | # """Check "error" message construction."""
21 | # m = "This is a test message."
22 | # expected_result = f"[E] {m}"
23 | # log.error(m)
24 | # out, err = capfd.readouterr()
25 | # assert expected_result in out.rstrip()
26 | #
27 | #
28 | #def test__message_warning__validate(capfd) -> None:
29 | # """Check "warning" message construction."""
30 | # m = "This is a test message."
31 | # expected_result = f"[W] {m}"
32 | # # system exit with code 0 is still a SystemExit
33 | # with pytest.raises(SystemExit):
34 | # log.warning(m)
35 | # out, err = capfd.readouterr()
36 | # assert expected_result in out.rstrip()
37 | #
38 | #
39 | #def test__message_debug__validate(capfd) -> None:
40 | # """Check "debug" message construction."""
41 | # m = "This is a test message."
42 | # expected_result = f"[D] {m}"
43 | # log.debug(m)
44 | # out, err = capfd.readouterr()
45 | # assert out.rstrip() == expected_result
46 |
--------------------------------------------------------------------------------