├── .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 | --------------------------------------------------------------------------------